diff -ur --new-file old/atm/.kernel new/atm/.kernel
--- old/atm/.kernel	Fri Jul 19 15:46:26 1996
+++ new/atm/.kernel	Fri Jul 19 17:42:11 1996
@@ -1,2 +1,2 @@
 # this file is used to control automated generation of differences
-2.0.0
+2.0
diff -ur --new-file old/atm/BUGS new/atm/BUGS
--- old/atm/BUGS	Wed Jul 17 18:54:05 1996
+++ new/atm/BUGS	Wed Jul 31 11:24:48 1996
@@ -1,9 +1,10 @@
-Known bugs and restrictions in version 0.14
+Known bugs and restrictions in version 0.15
 ===========================================
 
 IP over ATM
 -----------
 
+ - atmarpd seems to leak memory
  - kernel crashes badly if (remote) ARP server is set to local machine
  - clients don't always refresh their entry at ARP server (fixed ?)
  - ARP timers are too short and should be different for client and server
@@ -21,6 +22,7 @@
 Signaling
 ---------
 
+ - atmsigd appears to leak memory
  - never initiates a RESTART procedure
  - doesn't implement T322 (i.e. STATUS ENQUIRY is not retried)
  - atmsigd sometimes terminates ungracefully when encountering errors in
diff -ur --new-file old/atm/CHANGES new/atm/CHANGES
--- old/atm/CHANGES	Fri Jul 19 15:21:51 1996
+++ new/atm/CHANGES	Wed Jul 31 19:25:05 1996
@@ -1,3 +1,88 @@
+Version 0.14 to 0.15 (31-JUL-1996)
+====================
+
+Bug fixes
+---------
+
+ - zatm: fixed traffic shaper settings for UBR (with a little help from Joern
+   Wohlrab)
+ - atmsigd compilation didn't use the same UNI version configuration as qgen
+ - field "class" in struct atm_traform broke compilation of ATM applications
+   written in C++, so it has been renamed to "traffic_class" (reported by
+   Furquan Ansari)
+ - AAL parameters IE wasn't included in CONNECT, which violated RFC1755
+   (discovered by Robert Olsson)
+ - zatm: removed broken sanity check for in-sequence skb delivery on RX
+ - clip didn't have clip_open function, so SIOCSIFFLAGS on the interface failed
+   with ENODEV
+ - single-copy compiles again
+ - zatm: driver doesn't pretend any longer to support single-copy (but
+   single-copy will come back later)
+ - QOS IE was sent with the wrong coding standard when using UNI 3.1 (with help
+   from Fraz Ahmad)
+ - zatm: forgot to remove some debugging code (around ZATM_TUNE)
+ - qlib.c didn't zero the length array, leading to (rare) "not enough space"
+   errors from QMSG (actually, the "break" mechanism seems to be flaky - will
+   have to check)
+ - fixed Arequipa race condition when the upper layer protocol and signaling
+   decided to close the SVC at the same time
+ - alloc_tx used to return NULL in out of memory conditions, thereby possibly
+   hanging atm_sendmsg
+
+New features
+------------
+
+ - ttcp_atm now also accepts names with -p
+ - (finally !) added send/receive buffer limits and cleaned up the use of
+   vcc->[rt]x_{inuse,quota}
+ - added mkdiff script for automatic generation of diffs
+ - Arequipa now works for PVCs too (but arequipad is now required for any
+   use of Arequipa, not only for incoming connections)
+
+Other changes
+-------------
+
+ - atm/atm-<version>.patch is now called atm/atm.patch (to make the diffs more
+   useful)
+ - the maximum SDU size is now checked in net/atm/common.c:adjust_tp
+ - text2atm/atm2text now accepts/generates E.164 addresses with a leading +
+   sign, as required by ANS (atm95-1532)
+ - internal signaling protocol: added message as_error to un-overload as_close
+ - internal signaling protocol: split as_establish into as_connect and
+   as_accept
+ - internal signaling prococol: as_close.reply no longer contains positive
+   numbers
+ - internal signaling prococol: as_bind now also carries the AAL type
+ - atmsigd: now also checks the AAL type in as_bind messages
+ - kernel signaling: a few minor changes
+ - STANDARDS (i.e. UNI version) configuration option has been moved from
+   atm/qgen/Makefile to atm/Rules.make
+ - atmsigd's startup message now gives more useful indication of which UNI
+   version(s) it supports
+ - signaling no longer uses sa[sp]_[rt]xtp, so ...
+ - sa[sp]_[rt]xtp is gone
+ - removed atm/sigd/svc.c from the distribution. We now have many other tools
+   for testing SVCs, so it's superfluous.
+ - atmarpd: revalidation is now less frequent (use -DFREQUENT_REVAL for
+   the faster timeout)
+ - ioctl CLIP_PVC now returns the number of the new interface and clip prints
+   its name to standard output (like atmarp -c)
+ - clip now issues the CLIP_PVC ioctl after binding, which avoids wasting
+   interface numbers on failed setup attempts
+ - atmsigd -d now sets the debug level for qgen and SSCOP to DIAG_INFO, and
+   doesn't set q_dump
+ - zatm: added protection against I > M
+ - atm_peek_aal5 now only aligns to page boundaries for pdu_size >= PAGE_SIZE
+ - eni driver now spits out CRC error messages at most every other second
+ - zatm driver now repeats RX error messages at most every other second
+ - ttcp_atm now sleeps for a second after setting up an SVC, because the
+   switch seems to lag behind (need to examine this further)
+ - moved SAAL from sigd/ to saal/ (for sharing with UNI 4.0 signaling)
+ - Arequipa sockets are now closed via arequipad instead of via atmsigd (new
+   ioctls AREQUIPA_CTRL and AREQUIPA_CLS3RD)
+ - various minor changes
+
+
 Version 0.13 to 0.14 (19-JUL-1996)
 ====================
 
diff -ur --new-file old/atm/Makefile new/atm/Makefile
--- old/atm/Makefile	Wed Jun  5 19:37:32 1996
+++ new/atm/Makefile	Wed Jul 31 14:37:38 1996
@@ -1,7 +1,7 @@
-# "qgen" _must_ appear before "sigd"
+# "qgen" and "saal" _must_ appear before "sigd"
 # "lib" must appear before anything else
 
-DIRS=lib maint ip test debug qgen sigd arpd ilmid led aqd
+DIRS=lib maint ip test debug qgen saal sigd arpd ilmid led aqd
 
 all:
 		for n in $(DIRS); do $(MAKE) -C $$n || exit; done
diff -ur --new-file old/atm/README new/atm/README
--- old/atm/README	Fri Jul 19 14:22:25 1996
+++ new/atm/README	Tue Jul 30 19:15:22 1996
@@ -1,4 +1,4 @@
-ATM on Linux, release 0.14 (pre-alpha) by Werner Almesberger, EPFL LRC
+ATM on Linux, release 0.15 (pre-alpha) by Werner Almesberger, EPFL LRC
 ====================================== werner.almesberger@lrc.di.epfl.ch
 
 This is experimental software. There are known major bugs and certainly
diff -ur --new-file old/atm/Rules.make new/atm/Rules.make
--- old/atm/Rules.make	Fri Jul 19 15:09:39 1996
+++ new/atm/Rules.make	Tue Jul 30 21:36:22 1996
@@ -1,3 +1,13 @@
+# Default is UNI 3.0, foor good reasons, see below.
+#
+STANDARDS=-DUNI30
+#
+# Note: some UNI 3.0 switches will show really strange behaviour if confronted
+#	with using 3.1 signaling, so be sure to test your network *very*
+#	carefully before permanently configuring machines to use UNI 3.1.
+#
+# STANDARDS=-DUNI31 -DALLOW_UNI30
+
 CC=cc
 CFLAGS_NOWARN=-g -DVERSION=\"`cat ../VERSION`\" $(INCLUDES) -I../lib
 CFLAGS_NOOPT=$(CFLAGS_NOWARN) -Wall -Wshadow -Wpointer-arith -Wcast-align \
diff -ur --new-file old/atm/USAGE new/atm/USAGE
--- old/atm/USAGE	Fri Jul 19 15:43:40 1996
+++ new/atm/USAGE	Wed Jul 31 18:58:08 1996
@@ -1,4 +1,4 @@
-Usage instructions  -  ATM on Linux, release 0.14 (pre-alpha)
+Usage instructions  -  ATM on Linux, release 0.15 (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.14.tar.gz  
+    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.15.tar.gz  
   - the Linux kernel, version 2.0, e.g. from  
     ftp://ftp.cs.helsinki.fi/pub/Software/Linux/Kernel/v2.0/linux-2.0.tar.gz  
   - Perl, version 4 or 5 
@@ -31,7 +31,7 @@
 all the files listed above there. Then extract the ATM on Linux 
 distribution:
 
-tar xfz atm-0.14.tar.gz
+tar xfz atm-0.15.tar.gz
 
 and the kernel source:
 
@@ -40,14 +40,14 @@
 Finally, you can extract the ATM-related patches:
 
 cd linux
-patch -s -p1 <../atm/atm-0.14.patch
+patch -s -p1 <../atm/atm.patch
 
 The distribution installs into the following directories:
 
   atm/  Documentation in ASCII format, kernel patch, top-level Makefile, 
     and distribution scripts 
-  atm/sigd/  UNI 3.0 and UNI 3.1 signaling demon: atmsigd, test program: 
-    svc 
+  atm/sigd/  UNI 3.0 and UNI 3.1 signaling demon: atmsigd 
+  atm/saal/  Signaling AAL library (SSCOP, SSCF, and SAAL) 
   atm/qgen/  Q.2931-style message handling 
   atm/ilmid/  ILMI address registration demon: ilmid 
   atm/maint/  ATM maintenance programs: atmaddr, atmdiag, atmdump, atmtcp, 
@@ -84,9 +84,9 @@
   Enable extended debugging (CONFIG_ATM_TNETA1570_DEBUG)
 
 The "MMU hacks" add single-copy support for raw AAL5 on adapters whose 
-driver supports this (currently only the ENI155p). It's generally a good 
-idea to enable extended debugging, although this might slow down the 
-drivers a bit.
+driver supports this (currently only the ENI155p). Extended debugging 
+should only be enabled when tracing specific problems, because it slows 
+down the drivers, and it may introduce new race conditions.
 
 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 .
@@ -139,9 +139,10 @@
 make
 make install
 
-make install will install executables in the directory /usr/local/bin. 
-Libraries and header files are installed in /usr/lib and /usr/include, 
-respectively. Man pages are installed in /usr/local/man.
+make install will install executables in the directory /usr/local/bin and 
+/usr/local/sbin, respectively. Libraries and header files are installed in 
+/usr/lib and /usr/include, respectively. Man pages are installed in 
+/usr/local/man.
 
 
 Device setup
@@ -345,9 +346,9 @@
 with this package. The original is on  ftp://ftp.sgi.com/sgi/src/ttcp . The 
 following options have been added:
 
-   -a   use native ATM instead of UDP/TCP. (The address must be in the 
+   -a   use native ATM instead of UDP/TCP. The address must be in the 
     format  [<itf>.]<vpi>.<vci>  for PVCs, or a valid ATM end system 
-    address for SVCs.) 
+    address for SVCs. 
    -P <num>   use a CBR connection with a peak cell rate of <num> cells per 
     second. Default is to use UBR. 
    -C   disable (UDP) checksums 
@@ -456,9 +457,13 @@
 If atmsigd is killed, all system calls requiring interaction with it will 
 return with an error and set errno to EUNATCH.
 
-Note that atmsigd provides only very limited functionality and not all 
-messages conform to UNI 3.1 yet. Signaling is known to work for Classical 
-IP over ATM with a Fore ASX-200 switch.
+Note that atmsigd provides only very limited functionality. Signaling is 
+known to work for Classical IP over ATM with a Fore ASX-200 switch.
+
+By default, atmsigd is configured to conform to UNI 3.0. It can be compiled 
+for UNI 3.1 by changing the  STANDARDS=  line at the beginning of 
+atm/Rules.make, and running  make clean; make  in atm/qgen and atm/sigd (in 
+this order).
 
 Note that atmsigd is configured to be paranoid. If it detects unusual 
 problems, it frequently terminates. This will (obviously) change in the 
@@ -467,21 +472,16 @@
 atmsigd also looks for a configuration file atmsigd.conf in the current 
 directory.
 
-The svc program is used to test SVCs:
-
-  svc <atm_address>  
-
-attempts to set up a call to the specified destination (BHLI and BLLI are 
-omitted, so most end systems will refuse such calls).
-
-  svc  
-
-listens for incoming calls (without BHLI nor BLLI) and accepts them.
-
 
 ILMI demon
 ----------
 
+ILMI provides a mechanism for automatic address configuration. If there is 
+no switch or if the switch doesn't support ILMI, the ATM addresses must be 
+configured manually (see section "Manual address configuration"). Note that 
+the ILMI demon should not be used on interfaces where addresses are 
+manually configured.
+
 The ILMI demon is started as follows:
 
   # ilmid [-b] [-d] [-l <logfile>] [-x] [<itf>]  
@@ -511,8 +511,8 @@
 ----------------------------
 
 If your switch doesn't support ILMI, you have to set the ATM address 
-manually on the switch and on the PC(s). Under Linux, use the atmaddr 
-command to set the address:
+manually on the switch and on the PC(s). On the Linux side, make sure that 
+ilmid doesn't run, then use the atmaddr command to set the address:
 
   # atmaddr -a [<itf>] <atm_address>  
 
@@ -777,8 +777,7 @@
 Arequipa (Application REQUested IP over ATM, [6]) is an extension to IP 
 over ATM that adds support for using direct ATM connections (with QOS 
 parameters, etc.) to carry traffic between INET sockets. On a system that 
-should accept incoming Arequipa connections, the Arequipa demon has to be 
-started:
+should use Arequipa connections, the Arequipa demon has to be started:
 
   # arequipad [-b] [-d] [-n]  
 
@@ -789,6 +788,9 @@
 
 The status of Arequipa connections is shown in /proc/atm/arequipa.
 
+If atmsigd is not running, all attempts to use Arequipa fail and the ioctls 
+set errno to EUNATCH.
+
 The following example illustrates the use of Arequipa with ttcp_atm. A 
 machine "a" sends data to a machine "b", which is also registered with that 
 name in the DNS. Furthermore, "b" has an entry with the name "b-atm" in the 
@@ -799,6 +801,7 @@
 
 then run on "a":
 
+# arequipad -b
 # ttcp_atm -t -s -Q b-atm b
 
 
diff -ur --new-file old/atm/VERSION new/atm/VERSION
--- old/atm/VERSION	Fri Jul 19 14:21:12 1996
+++ new/atm/VERSION	Tue Jul 30 18:02:35 1996
@@ -1 +1 @@
-0.14
+0.15
diff -ur --new-file old/atm/aqd/io.c new/atm/aqd/io.c
--- old/atm/aqd/io.c	Fri Jun  7 13:12:55 1996
+++ new/atm/aqd/io.c	Wed Jul 31 16:31:44 1996
@@ -28,7 +28,7 @@
 
 extern int pretty;
 
-static int incoming;
+static int kernel,incoming;
 
 
 void open_all(void)
@@ -37,6 +37,10 @@
     struct atm_blli blli;
     struct atm_qos qos;
 
+    if ((kernel = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0)
+	diag(COMPONENT,DIAG_FATAL,"socket: %s",strerror(errno));
+    if (ioctl(kernel,AREQUIPA_CTRL,0) < 0)
+	diag(COMPONENT,DIAG_FATAL,"ioctl AREQUIPA_CTRL: %s",strerror(errno));
     if ((incoming = socket(PF_ATMSVC,SOCK_DGRAM,ATM_AAL5)) < 0)
 	diag(COMPONENT,DIAG_FATAL,"socket: %s",strerror(errno));
     memset(&qos,0,sizeof(qos));
@@ -62,10 +66,30 @@
 
 void close_all(void)
 {
+    (void) close(kernel);
     (void) close(incoming);
 }
 
 
+static void recv_kernel(void)
+{
+    struct atm_vcc *vcc;
+    int size;
+
+    size = read(kernel,&vcc,sizeof(vcc));
+    if (size < 0) {
+	diag(COMPONENT,DIAG_ERROR,"read kernel: %s",strerror(errno));
+	return;
+    }
+    if (size != sizeof(vcc))
+	diag(COMPONENT,DIAG_FATAL,"kernel message has bad size (%d != %d)",
+	  size,sizeof(vcc));
+    diag(COMPONENT,DIAG_DEBUG,"Closing socket 0x%08lx",(unsigned long) vcc);
+    if (ioctl(kernel,AREQUIPA_CLS3RD,(unsigned long) vcc) < 0)
+	diag(COMPONENT,DIAG_ERROR,"ioctl AREQUIPA_CLS3RD: %s",strerror(errno));
+}
+
+
 static void accept_new(void)
 {
     char buffer[MAX_ATM_ADDR_LEN+1];
@@ -81,9 +105,11 @@
     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);
+
     /*
      * Connection acceptance policy functions go here.
      */
+
     if (ioctl(fd,AREQUIPA_INCOMING,0) < 0)
         diag(COMPONENT,DIAG_ERROR,"ioctl AREQUIPA_INCOMING: %s",
 	  strerror(errno));
@@ -98,12 +124,16 @@
 
     while (1) {
 	FD_ZERO(&fds);
+	FD_SET(kernel,&fds);
 	FD_SET(incoming,&fds);
-	ret = select(incoming+1,&fds,NULL,NULL,NULL);
+	ret = select((kernel > incoming ? kernel : incoming)+1,&fds,NULL,NULL,
+	  NULL);
 	if (ret < 0) {
 	    if (errno != EINTR) perror("select");
+	    continue;
 	}
-	else if (FD_ISSET(incoming,&fds)) accept_new();
-		/* why else would we get here ? */
+	if (FD_ISSET(kernel,&fds)) recv_kernel();
+	if (FD_ISSET(incoming,&fds)) accept_new();
+	
     }
 }
diff -ur --new-file old/atm/arpd/arp.c new/atm/arpd/arp.c
--- old/atm/arpd/arp.c	Thu Jul 18 20:15:23 1996
+++ new/atm/arpd/arp.c	Mon Jul 29 13:40:22 1996
@@ -33,8 +33,15 @@
 #define SEC	 1000000
 #define T_REPLY	    (1*SEC)	/* 1 sec */
 #define R_REPLY	    5		/* 5 retries */
-#define T_REVAL	    (30*SEC)	/* 30 sec - make much larger (if srv ...) @@@ */
-#define R_REVAL	    0		/* no retries */
+#ifdef FREQUENT_REVAL
+#define T_CREVAL    (30*SEC)	/* 30 sec, client */
+#define T_SREVAL    (35*SEC)	/* 35 sec, server */
+#else
+#define T_CREVAL   (600*SEC)	/* 10 min, client (RFC1577: <= 15 min) */
+#define T_SREVAL  (1500*SEC)	/* 25 min, server (RFC1577: >= 20 min) */
+#endif
+#define R_CREVAL    0		/* no retries */
+#define R_SREVAL    0		/* no retries */
 #define T_RETRY	    (1*SEC)	/* 1 sec */
 #define R_RETRY	    25000	/* enough try keep trying during a long
 				   weekend */
@@ -470,7 +477,8 @@
     if (entry->state != as_valid && !entry->vccs && itf->arp_srv)
 	connect_me(entry);
     if ((entry->flags & ATF_ARPSRV) || !(entry->flags & ATF_PERM))
-	START_TIMER(entry,REVAL);
+	if (entry->itf->arp_srv) START_TIMER(entry,CREVAL);
+	else START_TIMER(entry,SREVAL);
     entry->state = as_valid;
 }
 
@@ -691,7 +699,7 @@
     vcc->connecting = 0;
     vcc->fd = fd;
     vcc->entry = entry;
-    if (!(flags & ATF_PERM)) START_TIMER(entry,REVAL);
+    if (!(flags & ATF_PERM)) START_TIMER(entry,CREVAL);
     Q_INSERT_HEAD(entry->vccs,vcc);
     Q_INSERT_HEAD(itf->table,entry);
     return 0;
@@ -712,7 +720,9 @@
     *entry->addr = *addr;
     entry->qos = *qos;
     entry->flags = flags;
-    if (!(flags & ATF_PERM) || (flags & ATF_ARPSRV)) START_TIMER(entry,REVAL);
+    if (!(flags & ATF_PERM) || (flags & ATF_ARPSRV))
+	if (itf->arp_srv) START_TIMER(entry,CREVAL);
+	else START_TIMER(entry,SREVAL);
     entry->itf = itf;
     Q_INSERT_HEAD(itf->table,entry);
     if (flags & ATF_ARPSRV) {
diff -ur --new-file old/atm/atm-0.14.patch new/atm/atm-0.14.patch
--- old/atm/atm-0.14.patch	Fri Jul 19 15:46:43 1996
+++ new/atm/atm-0.14.patch	Thu Jan  1 01:00:00 1970
@@ -1,16033 +0,0 @@
---- ref/Makefile	Fri Jun  7 10:55:00 1996
-+++ work/Makefile	Mon Jun 10 17:41:25 1996
-@@ -126,6 +126,10 @@
- 
- DRIVERS := $(DRIVERS) drivers/net/net.a
- 
-+ifdef CONFIG_ATM
-+DRIVERS := $(DRIVERS) drivers/atm/atm.a
-+endif
-+
- ifdef CONFIG_CD_NO_IDESCSI
- DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
- endif
-@@ -288,6 +292,7 @@
- 	if [ -f SCSI_MODULES  ]; then inst_mod SCSI_MODULES  scsi;  fi; \
- 	if [ -f FS_MODULES    ]; then inst_mod FS_MODULES    fs;    fi; \
- 	if [ -f CDROM_MODULES ]; then inst_mod CDROM_MODULES cdrom; fi; \
-+	if [ -f ATM_MODULES   ]; then inst_mod ATM_MODULES atm;     fi; \
- 	\
- 	ls *.o > .allmods; \
- 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 .allmods - > .misc; \
---- ref/Documentation/Configure.help	Fri Jun  7 15:28:46 1996
-+++ work/Documentation/Configure.help	Thu Jul 18 10:29:16 1996
-@@ -1164,6 +1164,101 @@
-   you can read some network related routing information from that
-   file. Everything you write to that file will be discarded.
- 
-+Asynchronous Transfer Mode (ATM)
-+CONFIG_ATM
-+  You are likely to want to enable this.
-+
-+Enable single-copy
-+CONFIG_MMU_HACKS
-+  Single-copy avoids intermediate buffering of AAL5 PDUs when using the
-+  native ATM API. Enabling single-copy may lead to significant throughput
-+  improvements. Note that single-copy probably still has bugs, so
-+  enabling it may de-stabilize the system.
-+
-+Extended debugging for single-copy
-+CONFIG_MMU_HACKS_DEBUG
-+  Extended debugging records various events and displays that list when
-+  an inconsistency is detected. This mechanism is faster than generally
-+  using printks, but still has some impact on performance. Enable this
-+  if you suspect problems with single-copy.
-+
-+Classical IP over ATM with ATMARP
-+CONFIG_ATM_ATMARP
-+  Classical IP over ATM for PVCs and SVCs, supporting InARP and ATMARP.
-+  This interface is younger than the old "CLIP" interface (next item)
-+  and it may therefore still have some bugs. In the future, it will
-+  replace the old interface entirely.
-+
-+Classical IP over ATM for PVCs (obsolete)
-+CONFIG_ATM_CLIP
-+  Classical IP over ATM for PVCs only and without InARP. This interface
-+  is obsolete and will be removed in later versions of ATM on Linux, as
-+  soon as ATMARP is fully mature.
-+
-+Application REQUested IP over ATM
-+CONFIG_AREQUIPA
-+  Arequipa is an experimental mechanism to create short-cut ATM SVCs for
-+  IP traffic. See
-+  ftp://lrcftp.epfl.ch/pub/arequipa/draft-almesberger-arequipa-01.txt
-+  for details.
-+
-+LANE support
-+CONFIG_ATM_LANE
-+  Lan Emulation emulates services of existing LANs across an ATM network.
-+  Client in Linux currently supports only one virtual LAN, and it has
-+  not been tested in environment which uses other than DIX Ethernet-
-+  data frames, i.e. Appletalk, IPX support has not been tested.
-+  Specification for LANE can be found from ATM Forum's specification
-+  LAN Emulation Over ATM - Version 1.0.
-+
-+ATM over TCP
-+CONFIG_ATM_TCP
-+  ATM over TCP driver. Useful for doing some simple experiments with the
-+  native ATM socket API, but not for many other things. You don't need
-+  this is you have a "real" ATM card.
-+
-+Efficient Networks ENI155P
-+CONFIG_ATM_ENI
-+  Driver for the Efficient Networks ENI155p series 155 Mbps ATM adapters.
-+  Both, the C and S variants (512kB and 2MB ob-board RAM, respectively),
-+  and the FPGA and the ASIC Tonga versions of the board are supported.
-+  The driver works with MMF (-MF) and UTP-5 (-U5) adapters.
-+
-+Enable extended debugging
-+CONFIG_ATM_ENI_DEBUG
-+  Extended debugging records various events and displays that list when
-+  an inconsistency is detected. This mechanism is faster than generally
-+  using printks, but still has some impact on performance. Note that
-+  extended debugging may create certain race conditions itself. Enable
-+  this ONLY if you suspect problems with the driver.
-+
-+ZeitNet ZN1221/ZN1225
-+CONFIG_ATM_ZATM
-+  Driver for the ZeitNet ZN1221 (MMF) and ZN1225 (UTP-5) 155 Mbps ATM
-+  adapters.
-+
-+Enable extended debugging
-+CONFIG_ATM_ZATM_DEBUG
-+  Extended debugging records various events and displays that list when
-+  an inconsistency is detected. This mechanism is faster than generally
-+  using printks, but still has some impact on performance. Note that
-+  extended debugging may create certain race conditions itself. Enable
-+  this ONLY if you suspect problems with the driver.
-+
-+Rolfs TI TNETA1570
-+CONFIG_ATM_TNETA1570
-+  Driver for the TNETA1570 155 Mbps ATM adapter, both developed by Rolf
-+  Fiedler at TU Chemnitz, see also
-+  ftp://ftp.infotech.tu-chemnitz.de/pub/linux-atm
-+
-+Enable extended debugging
-+CONFIG_ATM_TNETA1570_DEBUG
-+  Extended debugging records various events and displays that list when
-+  an inconsistency is detected. This mechanism is faster than generally
-+  using printks, but still has some impact on performance. Note that
-+  extended debugging may create certain race conditions itself. Enable
-+  this ONLY if you suspect problems with the driver.
-+
- SCSI support?
- CONFIG_SCSI
-   If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or
---- ref/kernel/ksyms.c	Wed May 29 15:42:27 1996
-+++ work/kernel/ksyms.c	Mon Jun 10 17:43:08 1996
-@@ -62,6 +62,9 @@
- #ifdef __SMP__
- #include <linux/smp.h>
- #endif
-+#ifdef CONFIG_ATM
-+#include <linux/atmdev.h>
-+#endif
- 
- extern char *get_options(char *str, int *ints);
- extern void set_device_ro(int dev,int flag);
-@@ -76,6 +79,10 @@
- 
- extern void hard_reset_now(void);
- 
-+#ifdef CONFIG_ATM_TCP
-+extern int (*atmtcp_attach_hook)(struct socket *sock);
-+#endif
-+
- struct symbol_table symbol_table = {
- #include <linux/symtab_begin.h>
- #ifdef MODVERSIONS
-@@ -348,7 +355,16 @@
- #ifdef CONFIG_BLK_DEV_MD
- 	X(disk_name),	/* for md.c */
- #endif
-- 	
-+ 
-+#ifdef CONFIG_ATM
-+	X(atm_dev_register),
-+	X(atm_dev_deregister),
-+	X(atm_find_ci),
-+#endif
-+#ifdef CONFIG_ATM_TCP
-+	X(atmtcp_attach_hook),
-+#endif
-+
- 	/* binfmt_aout */
- 	X(get_write_access),
- 	X(put_write_access),
---- ref/arch/i386/config.in	Mon May 13 06:17:23 1996
-+++ work/arch/i386/config.in	Mon Jun 10 17:36:00 1996
-@@ -72,6 +72,10 @@
-   endmenu
- fi
- 
-+if [ "$CONFIG_ATM" = "y" ]; then
-+  source drivers/atm/Config.in
-+fi
-+
- mainmenu_option next_comment
- comment 'ISDN subsystem'
- 
---- ref/arch/sparc/config.in	Thu Apr 25 12:22:05 1996
-+++ work/arch/sparc/config.in	Thu Jul 11 13:50:26 1996
-@@ -100,6 +100,12 @@
- 	endmenu
- fi
- 
-+if [ "$CONFIG_ATM" = "y" ]; then
-+	if [ "$CONFIG_INET" = "y" ]; then
-+		tristate 'ATM over TCP' CONFIG_ATM_TCP n
-+	fi
-+fi
-+
- source fs/Config.in
- 
- mainmenu_option next_comment
---- ref/drivers/Makefile	Mon Apr 22 09:59:39 1996
-+++ work/drivers/Makefile	Mon Jun 10 17:44:29 1996
-@@ -9,7 +9,7 @@
- 
- SUB_DIRS     := block char net #streams
- MOD_SUB_DIRS := $(SUB_DIRS)
--ALL_SUB_DIRS := $(SUB_DIRS) pci sbus scsi sound cdrom isdn
-+ALL_SUB_DIRS := $(SUB_DIRS) atm pci sbus scsi sound cdrom isdn
- 
- ifdef CONFIG_PCI
- SUB_DIRS += pci
-@@ -50,6 +50,11 @@
-   ifeq ($(CONFIG_ISDN),m)
-   MOD_SUB_DIRS += isdn
-   endif
-+endif
-+
-+ifdef CONFIG_ATM
-+SUB_DIRS += atm
-+MOD_SUB_DIRS += atm
- endif
- 
- ifeq ($(CONFIG_AP1000),y)
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/Config.in	Wed Jul 17 21:59:55 1996
-@@ -0,0 +1,21 @@
-+#
-+# ATM device configuration
-+#
-+comment 'ATM drivers'
-+if [ "$CONFIG_INET" = "y" ]; then
-+  tristate 'ATM over TCP' CONFIG_ATM_TCP n
-+fi
-+if [ "$CONFIG_PCI" = "y" ]; then
-+  tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI y
-+  if [ ! "$CONFIG_ATM_ENI" = "n" ]; then
-+    bool '  Enable extended debugging' CONFIG_ATM_ENI_DEBUG n
-+  fi
-+  tristate 'ZeitNet ZN1221/ZN1225' CONFIG_ATM_ZATM y
-+  if [ ! "$CONFIG_ATM_ZATM" = "n" ]; then
-+    bool '  Enable extended debugging' CONFIG_ATM_ZATM_DEBUG n
-+  fi
-+  bool 'Rolfs TI TNETA1570' CONFIG_ATM_TNETA1570 y
-+  if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then
-+    bool '  Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n
-+  fi
-+fi
---- ref/net/Config.in	Wed Jun  5 13:42:27 1996
-+++ work/net/Config.in	Wed Jul 17 19:33:18 1996
-@@ -27,4 +27,17 @@
- if [ "$CONFIG_NETLINK" = "y" ]; then
-   bool 'Routing messages' CONFIG_RTNETLINK
- fi
-+bool 'Asynchronous Transfer Mode (ATM)' CONFIG_ATM y
-+if [ "$CONFIG_ATM" = "y" ]; then
-+  bool '  Enable single-copy' CONFIG_MMU_HACKS n
-+  if [ "$CONFIG_MMU_HACKS" = "y" ]; then
-+    bool '    Extended debugging for single-copy' CONFIG_MMU_HACKS_DEBUG y
-+  fi
-+  if [ "$CONFIG_INET" = "y" ]; then
-+    bool '  Classical IP over ATM with ATMARP' CONFIG_ATM_ATMARP y
-+    bool '  Classical IP over ATM for PVCs (obsolete)' CONFIG_ATM_CLIP n
-+    bool '  Application REQUested IP over ATM' CONFIG_AREQUIPA y
-+  fi
-+  bool '  LANE support' CONFIG_ATM_LANE y
-+fi
- endmenu
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/Makefile	Thu Jul 18 20:51:29 1996
-@@ -0,0 +1,38 @@
-+# File: drivers/atm/Makefile
-+#
-+# Makefile for the Linux network (ATM) device drivers.
-+#
-+
-+L_TARGET := atm.a
-+L_OBJS	 := atmdev_init.o
-+M_OBJS	 :=
-+
-+include ../../.config
-+
-+ifeq ($(CONFIG_ATM_ENI),y)
-+L_OBJS += eni.o suni.o
-+endif
-+
-+ifeq ($(CONFIG_ATM_ZATM),y)
-+L_OBJS += zatm.o uPD98402.o
-+endif
-+
-+ifeq ($(CONFIG_ATM_TNETA1570),y)
-+L_OBJS += tneta1570.o suni.o
-+endif
-+
-+ifeq ($(CONFIG_ATM_FORE200),y)
-+L_OBJS += fore200.o
-+endif
-+
-+ifeq ($(CONFIG_ATM_TCP),y)
-+L_OBJS += atmtcp.o
-+else
-+  ifeq ($(CONFIG_ATM_TCP),m)
-+  M_OBJS += atmtcp.o
-+  endif
-+endif
-+
-+EXTRA_CFLAGS=-g
-+
-+include $(TOPDIR)/Rules.make
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/atmdev_init.c	Thu Jul 18 20:50:30 1996
-@@ -0,0 +1,47 @@
-+/* drivers/atm/atmdev_init.c - ATM device driver initialization */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#include <linux/config.h>
-+
-+
-+#ifdef CONFIG_ATM_ENI
-+extern int eni_detect(void);
-+#endif
-+#ifdef CONFIG_ATM_ZATM
-+extern int zatm_detect(void);
-+#endif
-+#ifdef CONFIG_ATM_TNETA1570
-+extern int tneta1570_detect(void);
-+#endif
-+#ifdef CONFIG_ATM_FORE200
-+extern int fore200_detect(void);
-+#endif
-+#ifdef CONFIG_ATM_TCP
-+extern int atmtcp_init(void);
-+#endif
-+
-+
-+int atmdev_init(void)
-+{
-+	int devs;
-+
-+	devs = 0;
-+#ifdef CONFIG_ATM_ENI
-+	devs += eni_detect();
-+#endif
-+#ifdef CONFIG_ATM_ZATM
-+	devs += zatm_detect();
-+#endif
-+#ifdef CONFIG_ATM_TNETA1570
-+	devs += tneta1570_detect();
-+#endif
-+#ifdef CONFIG_ATM_FORE200
-+	devs += fore200_detect();
-+#endif
-+#ifdef CONFIG_ATM_TCP
-+	devs += atmtcp_init();
-+#endif
-+	return devs;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/atmtcp.c	Thu Jul  4 17:05:57 1996
-@@ -0,0 +1,316 @@
-+/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+/* Various enhancements 1996 by Ronald A. McCormick */
-+
-+
-+#include <linux/module.h>
-+#include <linux/kernel.h>		/* printk */
-+#include <linux/mm.h>			/* verify_area */
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/errno.h>
-+#include <linux/skbuff.h>
-+#include <linux/in.h>
-+#include <linux/sched.h>		/* xtime */
-+#include <asm/segment.h>		/* set_fs, get_fs, ... */
-+#include <asm/byteorder.h>		/* ntohs, htons */
-+
-+
-+#define DEV_LABEL	"atmtcp"
-+
-+
-+#define MAX_VPI_BITS	8
-+#define MAX_VCI_BITS	16
-+
-+
-+#define PRIV(c) ((struct private *) ((c)->dev_data))
-+
-+
-+extern int (*atmtcp_attach_hook)(struct socket *sock);
-+
-+
-+/* BIG BUG: we can never get rid of an ATMTCP interface @@@ */
-+
-+
-+/*
-+ *  correct compilation of this is implementation-dependent !
-+ */
-+
-+struct atmtcp_hdr {
-+	unsigned short	vpi;		
-+	unsigned short	vci;
-+	unsigned short	length;		/* ... of data part */
-+};
-+
-+struct private {
-+	struct atm_dev	*dev;		/* device back pointer */
-+	struct socket	*sock;		/* file descriptor */
-+};
-+
-+
-+static int atmtcp_open(struct atm_vcc *vcc,short vpi,int vci)
-+{
-+	int error;
-+
-+	error = atm_find_ci(vcc,&vpi,&vci);
-+	if (error) return error;
-+	vcc->vpi = vpi;
-+	vcc->vci = vci;
-+	if (vpi == ATM_VPI_UNSPEC  || vci == ATM_VCI_UNSPEC) return 0;
-+#ifdef DEBUG
-+	printk(KERN_DEBUG DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,
-+	    vcc->vpi,vcc->vci);
-+#endif
-+	vcc->flags |= ATM_VF_ADDR | ATM_VF_READY;
-+	return 0;
-+}
-+
-+
-+int atmtcp_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
-+{
-+	if (cmd == ATM_SETCIRANGE) {
-+		struct atm_cirange ci;
-+		struct atm_vcc *walk;
-+
-+		memcpy_fromfs(&ci,(void *) arg,sizeof(struct atm_cirange));
-+		if (ci.vpi_bits == ATM_CI_MAX) ci.vpi_bits = MAX_VPI_BITS;
-+		if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS;
-+		if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 ||
-+		    ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0)
-+			return -EINVAL;
-+/*
-+ * should all this checking be done in net/atm ?
-+ */
-+		for (walk = dev->vccs; walk; walk = walk->next)
-+			if ((walk->vpi >> ci.vpi_bits) ||
-+			    (walk->vci >> ci.vci_bits)) return -EBUSY;
-+		dev->ci_range = ci;
-+		return 0;
-+	}
-+	return -EINVAL;
-+}
-+
-+
-+
-+static int do_recv(struct socket *sock,unsigned char *ubuf,int size,
-+    int nonblock)
-+{
-+	struct iovec iov;
-+	struct msghdr msg;
-+
-+	iov.iov_base = ubuf;
-+	iov.iov_len = size;
-+
-+	msg.msg_name = NULL;
-+	msg.msg_namelen = 0;
-+	msg.msg_control = NULL;
-+	msg.msg_iov = &iov;
-+	msg.msg_iovlen = 1;
-+
-+	return sock->ops->recvmsg(sock,&msg,size,nonblock,0,NULL);
-+}
-+
-+
-+static int do_send(struct socket *sock,const void *buff,int len,int nonblock)
-+{
-+	struct iovec iov;
-+	struct msghdr msg;
-+
-+	iov.iov_base = (void *)buff;
-+	iov.iov_len = len;
-+
-+	msg.msg_name = NULL;
-+	msg.msg_namelen = 0;
-+	msg.msg_control = NULL;
-+	msg.msg_iov = &iov;
-+	msg.msg_iovlen = 1;
-+
-+	return sock->ops->sendmsg(sock,&msg,len,nonblock,0);
-+}
-+
-+
-+static int atmtcp_setsockopt(struct atm_vcc *vcc,int level,int optname,
-+    char *optval,int optlen)
-+{
-+	return -EINVAL;
-+}
-+
-+
-+static int atmtcp_send(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	struct atmtcp_hdr hdr;
-+	unsigned long old_fs;
-+	int len,size;
-+	void *pos;
-+
-+	if (!PRIV(vcc->dev)->sock) {
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		return -EINVAL;
-+	}
-+	hdr.vpi = htons(vcc->vpi);
-+	hdr.vci = htons(vcc->vci);
-+	old_fs = get_fs();
-+	set_fs(get_ds());
-+	hdr.length = htons(skb->len);
-+	size = do_send(PRIV(vcc->dev)->sock,(char *) &hdr,sizeof(hdr),0);
-+	if (size <= 0) {
-+		printk(KERN_WARNING "atmtcp_send: PDU lost; errno = %d\n",
-+		    -size);
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		set_fs(old_fs);
-+		vcc->stats->tx_err++;
-+		return size;
-+	}
-+	pos = skb->data;
-+	len = skb->len;
-+	while (len) {
-+		size = do_send(PRIV(vcc->dev)->sock,pos,len,0);
-+		if (size < 0 && size != -EAGAIN) {
-+			printk(KERN_ERR "atmtcp_send: link failure; errno = "
-+			    "%d\n",-size);
-+			dev_kfree_skb(skb,FREE_WRITE);
-+			set_fs(old_fs);
-+			vcc->stats->tx_err++;
-+			return size;
-+		}
-+		if (size > 0) {
-+			len -= size;
-+			pos += size;
-+		}
-+	}
-+	dev_kfree_skb(skb,FREE_WRITE);
-+	set_fs(old_fs);
-+	vcc->stats->tx++;
-+	return 0;
-+}
-+
-+
-+static void atmtcp_poll(struct atm_vcc *vcc,int nonblock)
-+{
-+	struct atm_dev *dev;
-+	struct atm_vcc *walk;
-+	struct sk_buff *skb;
-+	struct atmtcp_hdr hdr;
-+	unsigned long old_fs;
-+	int size,left;
-+	void *pos;
-+
-+	if (!(dev = vcc->dev) || !PRIV(dev)->sock) return;
-+	if (skb_peek(&vcc->recvq)) nonblock = 1;
-+	old_fs = get_fs();
-+	set_fs(get_ds());
-+	while ((size = do_recv(PRIV(dev)->sock,(char*) &hdr,sizeof(hdr),
-+	    nonblock) == sizeof(hdr))) {
-+		if (!(skb = vcc->peek(vcc,ntohs(hdr.length),NULL))) {
-+			printk(KERN_WARNING "atmtcp_poll: peek reject (%d)\n",
-+			    ntohs(hdr.length));
-+			break;
-+		}
-+		skb->atm.timestamp = xtime;
-+		skb->len = ntohs(hdr.length);
-+		skb->free = 1;
-+		skb->atm.iovcnt = 0;
-+		pos = skb->data;
-+		for (left = ntohs(hdr.length); left; left -= size) {
-+			size = do_recv(PRIV(dev)->sock,pos,left,0);
-+			if (size == -EAGAIN) size = 0;
-+			if (size < 0) {
-+				printk(KERN_ERR "atmtcp_poll: bad read: %d\n",
-+				    size);
-+				vcc->stats->rx_err++;
-+				break;
-+			}
-+			pos += size;
-+		}
-+		for (walk = dev->vccs; walk; walk = walk->next)
-+			if (walk->vpi == ntohs(hdr.vpi) && walk->vci ==
-+			    ntohs(hdr.vci)) break;
-+		if (walk) {
-+			skb_queue_tail(&walk->recvq,skb);
-+			wake_up(&walk->sleep);
-+			nonblock = 1;
-+			vcc->stats->rx++;
-+		}
-+		else {
-+			printk(KERN_ERR "atmtcp_poll: bad label %d.%d at itf "
-+			    "%d\n",ntohs(hdr.vpi),ntohs(hdr.vci),dev->number);
-+			kfree_skb(skb,FREE_READ);
-+			vcc->stats->rx_err++;
-+		}
-+	}
-+	set_fs(old_fs);
-+	if (size > 0 && size != sizeof(hdr)) {
-+		printk(KERN_ERR "atmtcp_poll: bad header (%d)\n",size);
-+		vcc->stats->rx_err++;
-+	}
-+}
-+
-+
-+static struct atmdev_ops ops = {
-+	atmtcp_open,
-+	NULL,		/* no close */
-+	atmtcp_ioctl,
-+	NULL,		/* no getsockopt */
-+	atmtcp_setsockopt,
-+	atmtcp_send,
-+	NULL,		/* no direct writes */
-+	atmtcp_poll,
-+	NULL,		/* no send_oam */
-+	NULL,		/* no phy_put */
-+	NULL,		/* no phy_get */
-+	NULL		/* no feedback */
-+};
-+
-+
-+int atmtcp_attach(struct socket *sock)
-+{
-+	struct private *dsc;
-+
-+	if (!suser()) return -EPERM;
-+	dsc = kmalloc(sizeof(struct private),GFP_KERNEL);
-+	if (!dsc) return -ENOMEM;
-+	dsc->dev = atm_dev_register(DEV_LABEL,&ops,0);
-+	if (!dsc->dev) {
-+		kfree(dsc);
-+		return -EBUSY;
-+	}
-+	dsc->dev->dev_data = dsc;
-+	dsc->dev->ci_range.vpi_bits = MAX_VPI_BITS;
-+	dsc->dev->ci_range.vci_bits = MAX_VCI_BITS;
-+	sock->inode->i_count++;
-+	dsc->sock = sock;
-+	printk(KERN_NOTICE DEV_LABEL "(itf %d): ready\n",dsc->dev->number);
-+	MOD_INC_USE_COUNT;
-+	return dsc->dev->number;
-+}
-+
-+
-+int atmtcp_init(void)
-+{
-+	printk(KERN_NOTICE DEV_LABEL ": ready (dynamic device creation)\n");
-+	atmtcp_attach_hook = atmtcp_attach;
-+	return 1;
-+}
-+
-+
-+#ifdef MODULE
-+
-+int init_module(void)
-+{
-+	(void) atmtcp_init();
-+	return 0;
-+}
-+
-+
-+void cleanup_module(void)
-+{
-+	/*
-+	 * We currently have no means to detach ATM devices. That'll follow
-+	 * a bit later. (Needs careful usage tracking and open/close
-+	 * interlocking.)
-+	 */
-+	atmtcp_attach_hook = NULL;
-+}
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/eni.c	Thu Jul 18 22:07:12 1996
-@@ -0,0 +1,1944 @@
-+/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/mm.h>
-+#include <linux/bios32.h>
-+#include <linux/pci.h>
-+#include <linux/errno.h>
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+#include <linux/skbuff.h>
-+#include <linux/time.h>
-+#include <linux/sched.h> /* for xtime */
-+#include <linux/delay.h>
-+#include <linux/uio.h>
-+#include <asm/system.h>
-+#include <asm/string.h>
-+#include <asm/byteorder.h>
-+
-+#include "tonga.h"
-+#include "midway.h"
-+#include "suni.h"
-+#include "eni.h"
-+
-+
-+/*
-+ * TODO:
-+ *
-+ * Show stoppers
-+ *  none
-+ *
-+ * Major
-+ *  - test ASIC Tonga support
-+ *
-+ * Minor
-+ *  - OAM support
-+ *  - fix bugs listed below
-+ */
-+
-+/*
-+ * KNOWN BUGS:
-+ *
-+ * - may run into JK-JK bug and deadlock
-+ * - should allocate UBR channel first
-+ * - ATM DD interface doesn't support double connect(2) yet
-+ * - buffer space allocation algorithm is stupid
-+ *   (RX: should be maxSDU+maxdelay*rate
-+ *    TX: should be maxSDU+min(maxSDU,maxdelay*rate) )
-+ * - adapter memory may fragment badly
-+ * - ATM DD interface doesn't know about CLP
-+ * - doesn't support OAM cells
-+ * - buddy implementation is hyper-inefficient
-+ * - eni_put_free may hang if not putting memory fragments that _complete_
-+ *   2^n block
-+ * - keeps IRQ even if initialization fails
-+ */
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+#ifndef CONFIG_ATM_ENI_DEBUG
-+
-+
-+#define NULLCHECK(x)
-+
-+#define EVENT(s,a,b)
-+
-+
-+static void event_dump(void)
-+{
-+}
-+
-+
-+#else
-+
-+
-+/* 
-+ * NULL pointer checking
-+ */
-+
-+#define NULLCHECK(x) \
-+  if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x))
-+
-+/*
-+ * Very extensive activity logging. Greatly improves bug detection speed but
-+ * costs a few Mbps if enabled.
-+ */
-+
-+#define EV 64
-+
-+static const char *ev[EV];
-+static unsigned long ev_a[EV],ev_b[EV];
-+static int ec = 0;
-+
-+
-+static void EVENT(const char *s,unsigned long a,unsigned long b)
-+{
-+	ev[ec] = s; 
-+	ev_a[ec] = a;
-+	ev_b[ec] = b;
-+	ec = (ec+1) % EV;
-+}
-+
-+
-+static void event_dump(void)
-+{
-+	int n,i;
-+
-+	for (n = 0; n < EV; n++) {
-+		i = (ec+n) % EV;
-+		printk(KERN_NOTICE);
-+		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
-+	}
-+}
-+
-+
-+#endif /* CONFIG_ATM_ENI_DEBUG */
-+
-+
-+/*
-+ * NExx   must not be equal at end
-+ * EExx   may be equal at end
-+ * xxPJOK verify validity of pointer jumps
-+ * xxPMOK operating on a circular buffer of "c" words
-+ */
-+
-+#define NEPJOK(a0,a1,b) \
-+    ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1))
-+#define EEPJOK(a0,a1,b) \
-+    ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1))
-+#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b)
-+#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b)
-+
-+
-+static int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0,
-+  backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0,
-+  putting = 0;
-+
-+static struct atm_dev *eni_boards = NULL;
-+
-+
-+/*-------------------------------- utilities --------------------------------*/
-+
-+
-+static void dump_mem(struct eni_dev *eni_dev)
-+{
-+	int i;
-+
-+	for (i = 0; i < eni_dev->free_len; i++)
-+		printk(KERN_DEBUG "  %d: 0x%lx %d\n",i,
-+		    eni_dev->free_list[i].start,
-+		    1 << eni_dev->free_list[i].order);
-+}
-+
-+
-+static void dump(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+
-+	int i;
-+
-+	eni_dev = ENI_DEV(dev);
-+	printk(KERN_NOTICE "Free memory\n");
-+	dump_mem(eni_dev);
-+	printk(KERN_NOTICE "TX buffers\n");
-+	for (i = 0; i < NR_CHAN; i++)
-+		if (eni_dev->tx[i].send)
-+			printk(KERN_NOTICE "  TX %d @ 0x%08lx: %ld\n",i,
-+			    (unsigned long) eni_dev->tx[i].send,
-+			    eni_dev->tx[i].words*4);
-+	printk(KERN_NOTICE "RX buffers\n");
-+	for (i = 0; i < 1024; i++)
-+		if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx)
-+			printk(KERN_NOTICE "  RX %d @ 0x%08lx: %ld\n",i,
-+			    (unsigned long) ENI_VCC(eni_dev->rx_map[i])->recv,
-+			    ENI_VCC(eni_dev->rx_map[i])->words*4);
-+	printk(KERN_NOTICE "----\n");
-+}
-+
-+
-+static void eni_put_free(struct eni_dev *eni_dev,unsigned long start,
-+    unsigned long size)
-+{
-+	struct eni_free *list;
-+	int len,order;
-+
-+	DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size);
-+	start += eni_dev->base_diff;
-+	list = eni_dev->free_list;
-+	len = eni_dev->free_len;
-+	while (size) {
-+		if (len >= eni_dev->free_list_size) {
-+			printk(KERN_CRIT "eni_put_free overflow (0x%lx,%ld)\n",
-+			    start,size);
-+			break;
-+		}
-+		for (order = 0; !((start | size) & (1 << order)); order++);
-+		if (MID_MIN_BUF_SIZE > (1 << order)) {
-+			printk(KERN_CRIT "eni_put_free: order %d too small\n",
-+			    order);
-+			break;
-+		}
-+		list[len].start = start;
-+		list[len].order = order;
-+		len++;
-+		start += 1 << order;
-+		size -= 1 << order;
-+	}
-+	eni_dev->free_len = len;
-+	/*dump_mem(eni_dev);*/
-+}
-+
-+
-+static unsigned long eni_alloc_mem(struct eni_dev *eni_dev,unsigned long *size)
-+{
-+	struct eni_free *list;
-+	unsigned long start;
-+	int len,i,order,best_order,index;
-+
-+	list = eni_dev->free_list;
-+	len = eni_dev->free_len;
-+	if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE;
-+	if (*size > MID_MAX_BUF_SIZE) return 0;
-+	for (order = 0; (1 << order) < *size; order++);
-+	DPRINTK("trying: %ld->%d\n",*size,order);
-+	best_order = 65; /* we don't have more than 2^64 of anything ... */
-+	index = 0; /* silence GCC */
-+	for (i = 0; i < len; i++)
-+		if (list[i].order == order) {
-+			best_order = order;
-+			index = i;
-+			break;
-+		}
-+		else if (best_order > list[i].order && list[i].order > order) {
-+				best_order = list[i].order;
-+				index = i;
-+			}
-+	if (best_order == 65) return 0;
-+	start = list[index].start-eni_dev->base_diff;
-+	list[index] = list[--len];
-+	eni_dev->free_len = len;
-+	*size = 1 << order;
-+	eni_put_free(eni_dev,start+*size,(1 << best_order)-*size);
-+	DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start);
-+	for (i = (*size >> 2)-1; i >= 0; i--) /* never leak data */
-+		((unsigned long *) start)[i] = 0;
-+	/*dump_mem(eni_dev);*/
-+	return start;
-+}
-+
-+
-+static void eni_free_mem(struct eni_dev *eni_dev,unsigned long start,
-+    unsigned long size)
-+{
-+	struct eni_free *list;
-+	int len,i,order;
-+
-+	start += eni_dev->base_diff;
-+	list = eni_dev->free_list;
-+	len = eni_dev->free_len;
-+	for (order = -1; size; order++) size >>= 1;
-+	DPRINTK("eni_free_mem: 0x%lx+0x%lx (order %d)\n",start,size,order);
-+	for (i = 0; i < len; i++)
-+		if (list[i].start == (start^(1 << order)) &&
-+		    list[i].order == order) {
-+			DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i,
-+			    list[i].start,start,1 << order,list[i].order,order);
-+			list[i] = list[--len];
-+			start &= ~(1 << order);
-+			order++;
-+			i = -1;
-+			continue;
-+		}
-+	if (len >= eni_dev->free_list_size) {
-+		printk(KERN_ALERT "eni_free_mem overflow (0x%lx,%d)\n",start,
-+		    order);
-+		return;
-+	}
-+	list[len].start = start;
-+	list[len].order = order;
-+	eni_dev->free_len = len+1;
-+	/*dump_mem(eni_dev);*/
-+}
-+
-+
-+/*----------------------------------- RX ------------------------------------*/
-+
-+
-+#define ENI_VCC_NOS ((struct atm_vcc *) 1)
-+
-+
-+static __u32 fetch(struct atm_vcc *vcc,int i)
-+{
-+	return ntohl(ENI_VCC(vcc)->recv[(ENI_VCC(vcc)->descr+i+1) &
-+	    (ENI_VCC(vcc)->words-1)]);		           /* ^ skip descr */
-+}
-+
-+
-+static void rx_ident_err(struct atm_vcc *vcc)
-+{
-+	struct atm_dev *dev;
-+	struct eni_vcc *eni_vcc;
-+
-+	dev = vcc->dev;
-+	/* immediately halt adapter */
-+	ENI_DEV(dev)->reg[MID_MC_S] &= ~(MID_DMA_ENABLE | MID_TX_ENABLE |
-+	  MID_RX_ENABLE);
-+	/* dump useful information */
-+	eni_vcc = ENI_VCC(vcc);
-+	printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident "
-+	    "mismatch\n",dev->number);
-+	printk(KERN_ALERT "  VCI %d, rxing %d, words %ld\n",vcc->vci,
-+	    eni_vcc->rxing,eni_vcc->words);
-+	printk(KERN_ALERT "  host descr 0x%08lx, rx pos 0x%08lx, descr value "
-+	    "0x%08lx\n",eni_vcc->descr,eni_vcc->rx_pos,
-+	    eni_vcc->recv[eni_vcc->descr]);
-+	printk(KERN_ALERT "  last 0x%08lx, servicing %d\n",
-+	    (unsigned long) eni_vcc->last,eni_vcc->servicing);
-+	EVENT("---dump ends here---\n",0,0);
-+	printk(KERN_NOTICE "---recent events---\n");
-+	event_dump();
-+	ENI_DEV(dev)->fast = NULL; /* really stop it */
-+	ENI_DEV(dev)->slow = NULL;
-+	skb_queue_head_init(&ENI_DEV(dev)->rx_queue);
-+}
-+
-+
-+static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb,
-+    unsigned long skip,unsigned long size,unsigned long eff)
-+{
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	unsigned long dma_rd,dma_wr;
-+	unsigned long dma[RX_DMA_BUF*2];
-+	unsigned long paddr,here;
-+	int i,j;
-+
-+	eni_dev = ENI_DEV(vcc->dev);
-+	eni_vcc = ENI_VCC(vcc);
-+	paddr = 0; /* GCC, shut up */
-+	if (skb) {
-+		paddr = (unsigned long) skb->data;
-+		if (paddr & 3)
-+			printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has "
-+			    "mis-aligned RX data (0x%lx)\n",vcc->dev->number,
-+			    vcc->vci,paddr);
-+		skb->atm.size = size+skip;
-+		    /* PDU plus descriptor */
-+		skb->atm.vcc = vcc;
-+	}
-+	j = 0;
-+	if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */
-+		here = (eni_vcc->descr+skip) & (eni_vcc->words-1);
-+		dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci
-+		    << MID_DMA_VCI_SHIFT) | MID_DT_JK;
-+		j++;
-+	}
-+	here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1);
-+	if (!eff) size += skip;
-+	else {
-+		unsigned long words;
-+
-+		if (!size) {
-+			DPRINTK("strange things happen ...\n");
-+			EVENT("strange things happen ... (skip=%ld,eff=%ld)\n",
-+			    size,eff);
-+		}
-+		words = eff;
-+		if (paddr & 15) {
-+			unsigned long init;
-+
-+			init = 4-((paddr & 15) >> 2);
-+			if (init > words) init = words;
-+			dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
-+			    (vcc->vci << MID_DMA_VCI_SHIFT);
-+			dma[j++] = paddr;
-+			paddr += init << 2;
-+			words -= init;
-+		}
-+		if (words & ~7) {
-+#if 0 /* works with *some* PCI chipsets ... */
-+			dma[j++] = MID_DT_8W | ((words >> 3) <<
-+			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
-+			    MID_DMA_VCI_SHIFT);
-+			dma[j++] = paddr;
-+			paddr += (words & ~7) << 2;
-+			words &= 7;
-+#else
-+			dma[j++] = MID_DT_4W | ((words >> 2) <<
-+			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
-+			    MID_DMA_VCI_SHIFT);
-+			dma[j++] = paddr;
-+			paddr += (words & ~3) << 2;
-+			words &= 3;
-+#endif
-+		}
-+		if (words) {
-+			dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT)
-+			    | (vcc->vci << MID_DMA_VCI_SHIFT);
-+			dma[j++] = paddr;
-+		}
-+	}
-+	if (size != eff) {
-+		dma[j++] = (here << MID_DMA_COUNT_SHIFT) |
-+		    (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK;
-+		j++;
-+	}
-+	if (!j || j > 2*RX_DMA_BUF) {
-+		printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n");
-+		if (skb) kfree_skb(skb,FREE_READ);
-+		return -1;
-+	}
-+	dma[j-2] |= MID_DMA_END;
-+	j = j >> 1;
-+	dma_wr = eni_dev->reg[MID_DMA_WR_RX];
-+	dma_rd = eni_dev->reg[MID_DMA_RD_RX];
-+	if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */
-+		printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n",
-+		    vcc->dev->number);
-+		if (skb) kfree_skb(skb,FREE_READ);
-+		return -1;
-+	}
-+        for (i = 0; i < j; i++) {
-+		eni_dev->rx_dma[dma_wr*2] = dma[i*2];
-+		eni_dev->rx_dma[dma_wr*2+1] = dma[i*2+1];
-+		dma_wr = (dma_wr+1) & (NR_DMA_RX-1);
-+        }
-+	if (skb) {
-+		skb->atm.pos = eni_vcc->descr+size+1;
-+		skb_queue_tail(&eni_dev->rx_queue,skb);
-+eni_vcc->last = skb;
-+rx_enqueued++;
-+	}
-+	eni_vcc->descr = here;
-+	eni_dev->reg[MID_DMA_WR_RX] = dma_wr;
-+	return 0;
-+}
-+
-+
-+static void discard(struct atm_vcc *vcc,unsigned long size)
-+{
-+	struct eni_vcc *eni_vcc;
-+
-+	eni_vcc = ENI_VCC(vcc);
-+	EVENT("discard (size=%ld)\n",size,0);
-+	while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0);
-+	    /* could do a full fallback, but that might be more expensive */
-+	if (eni_vcc->rxing) eni_vcc->last->atm.pos += size+1;
-+	else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1);
-+}
-+
-+
-+/*
-+ * TODO: should check whether direct copies (without DMA setup, dequeuing on
-+ * interrupt, etc.) aren't much faster for AAL0
-+ */
-+
-+static int rx_aal0(struct atm_vcc *vcc)
-+{
-+	struct eni_vcc *eni_vcc;
-+	unsigned long descr;
-+	unsigned long length;
-+	struct sk_buff *skb;
-+
-+	DPRINTK(">rx_aal0\n");
-+	eni_vcc = ENI_VCC(vcc);
-+	descr = eni_vcc->recv[eni_vcc->descr];
-+	if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
-+		rx_ident_err(vcc);
-+		return 1;
-+	}
-+	if (descr & MID_RED_T) {
-+		DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
-+		    vcc->dev->number);
-+		length = 0;
-+		vcc->stats->rx_err++;
-+	}
-+	else {
-+		length = ATM_CELL_SIZE-1; /* no HEC */
-+	}
-+	if (!length) skb = NULL;
-+	else skb = vcc->peek(vcc,length,fetch);
-+	if (!skb) {
-+		discard(vcc,length >> 2);
-+		return 0;
-+	}
-+	skb->free = 1; /* not very useful ... */
-+	skb->len = length;
-+	skb->atm.timestamp = eni_vcc->timestamp;
-+	DPRINTK("got len %ld\n",length);
-+	if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1;
-+	eni_vcc->rxing++;
-+	return 0;
-+}
-+
-+
-+static int rx_aal5(struct atm_vcc *vcc)
-+{
-+	struct eni_vcc *eni_vcc;
-+	unsigned long descr;
-+	unsigned long size,eff,length;
-+	struct sk_buff *skb;
-+
-+	EVENT("rx_aal5\n",0,0);
-+	DPRINTK(">rx_aal5\n");
-+	eni_vcc = ENI_VCC(vcc);
-+	descr = eni_vcc->recv[eni_vcc->descr];
-+	if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
-+		rx_ident_err(vcc);
-+		return 1;
-+	}
-+	if (descr & (MID_RED_T | MID_RED_CRC_ERR)) {
-+		if (descr & MID_RED_T) {
-+			EVENT("empty cell (descr=0x%08lx)\n",descr,0);
-+			DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
-+			    vcc->dev->number);
-+			size = 0;
-+		}
-+		else {
-+			printk(KERN_WARNING DEV_LABEL "(itf %d): discarding "
-+			    "PDU with CRC error\n",vcc->dev->number);
-+			size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
-+			EVENT("CRC error (descr=0x%08lx,size=%ld)\n",descr,
-+			    size);
-+		}
-+		eff = length = 0;
-+		vcc->stats->rx_err++;
-+	}
-+	else {
-+		size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
-+		DPRINTK("size=%ld\n",size);
-+		length = eni_vcc->recv[(eni_vcc->descr+size-1) &
-+		    (eni_vcc->words-1)] & 0xffff; /* -trailer(2)+header(1) */
-+		if (length && length <= (size << 2)-8 && length <=
-+		  ATM_MAX_AAL5_PDU) eff = (length+3) >> 2;
-+		else {				 /* ^ trailer length (8) */
-+			EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr,
-+			    length);
-+			printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU "
-+			    "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n",
-+			    vcc->dev->number,vcc->vci,length,size << 2,descr);
-+#if 0
-+printk("0x%lx[0x%lx] (0x%lx,%ld) size %ld len %ld\n",
-+  (unsigned long) eni_vcc->recv,eni_vcc->descr,(unsigned long) eni_vcc,
-+  eni_vcc->words,size << 2,length);
-+#endif
-+			length = eff = 0;
-+			vcc->stats->rx_err++;
-+		}
-+	}
-+	if (!eff) skb = NULL;
-+	else {
-+		skb = vcc->peek(vcc,eff << 2,fetch);
-+		if (!skb) {
-+			EVENT("peek reject (eff << 2=%ld)\n",eff << 2,0);
-+			DPRINTK(DEV_LABEL "(itf %d): peek reject for %ld "
-+			    "bytes\n",vcc->dev->number,eff << 2);
-+		}
-+	}
-+	if (!skb) {
-+		discard(vcc,size);
-+		return 0;
-+	}
-+	skb->free = 1; /* not very useful ... */
-+	skb->len = length;
-+	DPRINTK("got len %ld\n",length);
-+	if (do_rx_dma(vcc,skb,1,size,eff)) return 1;
-+	eni_vcc->rxing++;
-+	return 0;
-+}
-+
-+
-+static inline int rx_vcc(struct atm_vcc *vcc)
-+{
-+	volatile unsigned long *vci_dsc;
-+	struct eni_vcc *eni_vcc;
-+	unsigned long tmp;
-+
-+	eni_vcc = ENI_VCC(vcc);
-+	vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*4;
-+	EVENT("rx_vcc(1)\n",0,0);
-+	while (eni_vcc->descr != (tmp = (vci_dsc[1] & MID_VCI_DESCR) >>
-+	    MID_VCI_DESCR_SHIFT)) {
-+		EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n",
-+		    eni_vcc->descr,tmp);
-+		DPRINTK("CB_DESCR %ld REG_DESCR %ld\n",ENI_VCC(vcc)->descr,
-+		    ((vci_dsc[1] & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT));
-+		if (ENI_VCC(vcc)->rx(vcc)) return 1;
-+	}
-+	/* clear IN_SERVICE flag */
-+	vci_dsc[0] &= ~MID_VCI_IN_SERVICE;
-+	/*
-+	 * If new data has arrived between evaluating the while condition and
-+	 * clearing IN_SERVICE, we wouldn't be notified until additional data
-+	 * follows. So we have to loop again to be sure.
-+	 */
-+	EVENT("rx_vcc(3)\n",0,0);
-+	while (ENI_VCC(vcc)->descr != (tmp = (vci_dsc[1] & MID_VCI_DESCR) >>
-+	    MID_VCI_DESCR_SHIFT)) {
-+		EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n",
-+		    eni_vcc->descr,tmp);
-+		DPRINTK("CB_DESCR %ld REG_DESCR %ld\n",ENI_VCC(vcc)->descr,
-+		    ((vci_dsc[1] & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT));
-+		if (ENI_VCC(vcc)->rx(vcc)) return 1;
-+	}
-+	return 0;
-+}
-+
-+
-+static void poll_rx(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+	struct atm_vcc *curr;
-+
-+	eni_dev = ENI_DEV(dev);
-+	while ((curr = eni_dev->fast)) {
-+		EVENT("poll_tx.fast\n",0,0);
-+		if (rx_vcc(curr)) return;
-+		eni_dev->fast = ENI_VCC(curr)->next;
-+		ENI_VCC(curr)->next = ENI_VCC_NOS;
-+		ENI_VCC(curr)->servicing--;
-+	}
-+	while ((curr = eni_dev->slow)) {
-+		EVENT("poll_tx.slow\n",0,0);
-+		if (rx_vcc(curr)) return;
-+		eni_dev->slow = ENI_VCC(curr)->next;
-+		ENI_VCC(curr)->next = ENI_VCC_NOS;
-+		ENI_VCC(curr)->servicing--;
-+	}
-+}
-+
-+
-+static void get_service(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+	struct atm_vcc *vcc;
-+	unsigned long vci;
-+
-+	DPRINTK(">get_service\n");
-+	eni_dev = ENI_DEV(dev);
-+	while (eni_dev->reg[MID_SERV_WRITE] != eni_dev->serv_read) {
-+		vci = eni_dev->service[eni_dev->serv_read];
-+		eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1);
-+		vcc = eni_dev->rx_map[vci & 1023];
-+		if (!vcc) {
-+			printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not "
-+			    "found\n",dev->number,vci);
-+			continue; /* nasty but we try to go on anyway */
-+			/* @@@ nope, doesn't work */
-+		}
-+		EVENT("getting from service\n",0,0);
-+		if (ENI_VCC(vcc)->next != ENI_VCC_NOS) {
-+			EVENT("double service\n",0,0);
-+			DPRINTK("Grr, servicing VCC %ld twice\n",vci);
-+			continue;
-+		}
-+		ENI_VCC(vcc)->timestamp = xtime;
-+		ENI_VCC(vcc)->next = NULL;
-+		if (vcc->rxtp.class == ATM_CBR) {
-+			if (eni_dev->fast)
-+				ENI_VCC(eni_dev->last_fast)->next = vcc;
-+			else eni_dev->fast = vcc;
-+			eni_dev->last_fast = vcc;
-+		}
-+		else {
-+			if (eni_dev->slow)
-+				ENI_VCC(eni_dev->last_slow)->next = vcc;
-+			else eni_dev->slow = vcc;
-+			eni_dev->last_slow = vcc;
-+		}
-+putting++;
-+		ENI_VCC(vcc)->servicing++;
-+	}
-+}
-+
-+
-+static void dequeue_rx(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	struct atm_vcc *vcc;
-+	struct sk_buff *skb;
-+	volatile unsigned long *vci_dsc;
-+	int first;
-+
-+	eni_dev = ENI_DEV(dev);
-+	first = 1;
-+	while (1) {
-+		skb = skb_dequeue(&eni_dev->rx_queue);
-+		if (!skb) {
-+			if (first) {
-+				DPRINTK(DEV_LABEL "(itf %d): RX but not "
-+				    "rxing\n",dev->number);
-+				EVENT("nothing to dequeue\n",0,0);
-+			}
-+			break;
-+		}
-+		EVENT("dequeued (size=%d,pos=0x%lx)\n",skb->atm.size,
-+		    skb->atm.pos);
-+/*printk("DMA@0x%X\n",eni_dev->reg[MID_DMA_ADDR]);*/
-+rx_dequeued++;
-+		vcc = skb->atm.vcc;
-+		eni_vcc = ENI_VCC(vcc);
-+		first = 0;
-+		vci_dsc = eni_dev->vci+(vcc->vci << 2);
-+		if (!EEPMOK(eni_vcc->rx_pos,skb->atm.size,(vci_dsc[1] &
-+		    MID_VCI_READ) >> MID_VCI_READ_SHIFT,eni_vcc->words)) {
-+			EVENT("requeuing\n",0,0);
-+			skb_queue_head(&eni_dev->rx_queue,skb);
-+			break;
-+		}
-+		eni_vcc->rxing--;
-+		eni_vcc->rx_pos = skb->atm.pos & (eni_vcc->words-1);
-+		if (!skb->len) kfree_skb(skb,FREE_READ);
-+		else {
-+			EVENT("pushing (len=%d)\n",skb->len,0);
-+			if (vcc->aal == ATM_AAL0)
-+				*(unsigned long *) skb->data =
-+				    ntohl(*(unsigned long *) skb->data);
-+			vcc->push(vcc,skb);
-+			pushed++;
-+		}
-+		vcc->stats->rx++;
-+	}
-+	wake_up(&eni_dev->rx_wait);
-+}
-+
-+
-+static int open_rx_first(struct atm_vcc *vcc)
-+{
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	unsigned long size;
-+
-+	DPRINTK("open_rx_first\n");
-+	eni_dev = ENI_DEV(vcc->dev);
-+	eni_vcc = ENI_VCC(vcc);
-+	eni_vcc->rx = NULL;
-+	if (vcc->rxtp.class == ATM_NONE) return 0;
-+	size = vcc->rxtp.max_sdu*3; /* @@@ improve this */
-+	eni_vcc->recv = (volatile unsigned long *) eni_alloc_mem(eni_dev,&size);
-+	DPRINTK("rx at 0x%lx\n",(unsigned long) eni_vcc->recv);
-+	eni_vcc->words = size >> 2;
-+	if (!eni_vcc->recv) return -ENOBUFS;
-+	eni_vcc->rx = vcc->aal == ATM_AAL5 ? rx_aal5 : rx_aal0;
-+	eni_vcc->descr = 0;
-+	eni_vcc->rx_pos = 0;
-+	eni_vcc->rxing = 0;
-+	eni_vcc->servicing = 0;
-+	eni_vcc->next = ENI_VCC_NOS;
-+	return 0;
-+}
-+
-+
-+static int open_rx_second(struct atm_vcc *vcc)
-+{
-+	volatile unsigned long *here;
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	unsigned long size;
-+	int order;
-+
-+	DPRINTK("open_rx_second\n");
-+	eni_dev = ENI_DEV(vcc->dev);
-+	eni_vcc = ENI_VCC(vcc);
-+	if (!eni_vcc->rx) return 0;
-+	/* set up VCI descriptor */
-+	here = eni_dev->vci+(vcc->vci << 2);
-+	DPRINTK("loc 0x%x\n",eni_vcc->recv-eni_dev->ram);
-+	size = eni_vcc->words >> 8;
-+	for (order = -1; size; order++) size >>= 1;
-+	here[1] = 0; /* descr, read = 0 */
-+	here[2] = 0; /* write, state, count = 0 */
-+	if (eni_dev->rx_map[vcc->vci])
-+		printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already "
-+		    "in use\n",vcc->dev->number,vcc->vci);
-+	eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */
-+	here[0] = ((vcc->aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) <<
-+	    MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE |
-+	    (((eni_vcc->recv-eni_dev->ram) >> MID_LOC_SKIP) <<
-+	    MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT);
-+	return 0;
-+}
-+
-+
-+static void close_rx(struct atm_vcc *vcc)
-+{
-+	volatile unsigned long *here;
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	unsigned long flags,tmp;
-+
-+	eni_vcc = ENI_VCC(vcc);
-+	if (!eni_vcc->rx) return;
-+	eni_dev = ENI_DEV(vcc->dev);
-+	if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
-+		here = eni_dev->vci+(vcc->vci << 2);
-+		/* block receiver */
-+		*here = (*here & ~MID_VCI_MODE) | (MID_MODE_TRASH <<
-+		    MID_VCI_MODE_SHIFT);
-+		/* wait for receiver to become idle */
-+		udelay(27);
-+		/* discard pending cell */
-+		*here &= ~MID_VCI_IN_SERVICE;
-+		/* don't accept any new ones */
-+		eni_dev->rx_map[vcc->vci] = NULL;
-+		/* wait for RX queue to drain */
-+		DPRINTK("eni_close: waiting for RX ...\n");
-+		EVENT("RX closing\n",0,0);
-+		save_flags(flags);
-+		cli();
-+		while (eni_vcc->rxing || eni_vcc->servicing) {
-+			EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing,
-+			    eni_vcc->servicing);
-+			printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing,
-+			    eni_vcc->rxing);
-+			sleep_on(&eni_dev->rx_wait);
-+		}
-+		while (eni_vcc->rx_pos != (tmp = eni_dev->vci[vcc->vci*4+1] &
-+		    MID_VCI_READ) >> MID_VCI_READ_SHIFT) {
-+			EVENT("drain discard (host 0x%lx, nic 0x%lx)\n",
-+			    eni_vcc->rx_pos,tmp);
-+			printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%lx\n",
-+			    eni_vcc->rx_pos,tmp);
-+			sleep_on(&eni_dev->rx_wait);
-+		}
-+		restore_flags(flags);
-+	}
-+	eni_free_mem(eni_dev,(unsigned long) eni_vcc->recv,
-+	    eni_vcc->words << 2);
-+	eni_vcc->rx = NULL;
-+}
-+
-+
-+static int start_rx(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+
-+	eni_dev = ENI_DEV(dev);
-+	eni_dev->rx_map = (struct atm_vcc **) get_free_page(GFP_KERNEL);
-+	if (!eni_dev->rx_map) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
-+		    dev->number);
-+		free_page((unsigned long) eni_dev->free_list);
-+		return -ENOMEM;
-+	}
-+	memset(eni_dev->rx_map,0,PAGE_SIZE);
-+	eni_dev->fast = eni_dev->last_fast = NULL;
-+	eni_dev->slow = eni_dev->last_slow = NULL;
-+	eni_dev->rx_wait = NULL;
-+	skb_queue_head_init(&eni_dev->rx_queue);
-+	eni_dev->serv_read = eni_dev->reg[MID_SERV_WRITE];
-+	eni_dev->reg[MID_DMA_WR_RX] = 0;
-+	return 0;
-+}
-+
-+
-+/*----------------------------------- TX ------------------------------------*/
-+
-+
-+enum enq_res { enq_ok,enq_next,enq_jam };
-+
-+
-+static inline void put_dma(int chan,unsigned long *dma,int *j,
-+    unsigned long paddr,unsigned long size)
-+{
-+	unsigned long init;
-+
-+	DPRINTK("put_dma: 0x%lx+0x%lx\n",paddr,size);
-+	EVENT("put_dma: 0x%lx+0x%lx\n",paddr,size);
-+	if (paddr & 3)
-+		printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr);
-+#if 0 /* don't complain - all cases that pass are handled somewhere else */
-+	if (size & 3)
-+		printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size);
-+#endif
-+	size = (size+3) >> 2;
-+/* fixme: extra DMA descr when misaligned short PDU @@@ */
-+	if (paddr & 31) {
-+		init = 8-((paddr & 31) >> 2);
-+		if (init > size) init = size;
-+		dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
-+		    (chan << MID_DMA_CHAN_SHIFT);
-+		dma[(*j)++] = paddr;
-+		paddr += init << 2;
-+		size -= init;
-+	}
-+	if (size & ~7) {
-+		dma[(*j)++] = MID_DT_8W | ((size >> 3) << MID_DMA_COUNT_SHIFT)
-+		    | (chan << MID_DMA_CHAN_SHIFT);
-+		dma[(*j)++] = paddr;
-+		paddr += (size & ~7) << 2;
-+		size &= 7;
-+	}
-+	if (size) {
-+		dma[(*j)++] = MID_DT_WORD | (size << MID_DMA_COUNT_SHIFT) |
-+		    (chan << MID_DMA_CHAN_SHIFT);
-+		dma[(*j)++] = paddr;
-+	}
-+}
-+
-+
-+static enum enq_res do_tx(struct sk_buff *skb)
-+{
-+	struct atm_vcc *vcc;
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	struct eni_tx *tx;
-+	unsigned long dma_rd,dma_wr;
-+	unsigned long dma[TX_DMA_BUF*2];
-+	unsigned long size; /* in words */
-+	int aal5,dma_size,i,j;
-+
-+	DPRINTK(">do_tx\n");
-+	NULLCHECK(skb);
-+	EVENT("do_tx: skb=0x%lx, %d bytes\n",(unsigned long) skb,skb->len);
-+	vcc = skb->atm.vcc;
-+	NULLCHECK(vcc);
-+	eni_dev = ENI_DEV(vcc->dev);
-+	NULLCHECK(eni_dev);
-+	eni_vcc = ENI_VCC(vcc);
-+	tx = eni_vcc->tx;
-+	NULLCHECK(tx);
-+	if ((unsigned long) skb->data & 3)
-+		printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned "
-+		    "TX data\n",vcc->dev->number,vcc->vci);
-+	/*
-+	 * Potential future IP speedup: make hard_header big enough to put
-+	 * segmentation descriptor directly into PDU. Saves: 4 slave writes,
-+	 * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-)
-+	 */
-+
-+	/* check space in buffer */
-+	if (!(aal5 = vcc->aal == ATM_AAL5))
-+		size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE;
-+			/* cell without HEC plus segmentation header (includes
-+			   four-byte cell header) */
-+	else {
-+		size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1;
-+			/* add AAL5 trailer */
-+		size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE;
-+						/* add segmentation header */
-+	}
-+	if (!NEPMOK(tx->tx_pos,size+TX_GAP,
-+	    eni_dev->reg[MID_TX_RDPTR(tx->index)],tx->words)) {
-+	    /* leave some space for "too close" */
-+		DPRINTK(DEV_LABEL "(itf %d): TX full (size %ld)\n",
-+		    vcc->dev->number,size);
-+		return enq_next;
-+	}
-+	/* check DMA */
-+	dma_wr = eni_dev->reg[MID_DMA_WR_TX];
-+	dma_rd = eni_dev->reg[MID_DMA_RD_TX];
-+	dma_size = 2; /* JK for descriptor and final fill */
-+DPRINTK("iovcnt = %d\n",skb->atm.iovcnt);
-+	if (!skb->atm.iovcnt) dma_size += 3;
-+	else dma_size += 3*skb->atm.iovcnt;
-+	if (dma_size > TX_DMA_BUF) {
-+		printk(KERN_WARNING DEV_LABEL "(itf %d): needs %d DMA entries "
-+		    "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF);
-+	}
-+	DPRINTK("dma_wr is %ld, tx_pos is %ld\n",dma_wr,tx->tx_pos);
-+	if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) <
-+	     dma_size) {
-+		printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n",
-+		    vcc->dev->number);
-+		return enq_jam;
-+	}
-+	/* prepare DMA queue entries */
-+	j = 0;
-+	dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) <<
-+	     MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
-+	     MID_DT_JK;
-+	j++;
-+	if (!skb->atm.iovcnt)
-+		if (aal5)
-+			put_dma(tx->index,dma,&j,(unsigned long) skb->data,
-+			    skb->len);
-+		else put_dma(tx->index,dma,&j,(unsigned long) skb->data+4,
-+			    skb->len-4);
-+	else {
-+DPRINTK("doing direct send\n");
-+		for (i = 0; i < skb->atm.iovcnt; i++)
-+			put_dma(tx->index,dma,&j,(unsigned long)
-+			    ((struct iovec *) skb->data)[i].iov_base,
-+			    ((struct iovec *) skb->data)[i].iov_len);
-+	}
-+	/* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */
-+	dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) <<
-+	     MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
-+	     MID_DMA_END | MID_DT_JK;
-+	j++;
-+	DPRINTK("DMA at end: %d\n",j);
-+	/* store frame */
-+	tx->send[tx->tx_pos] = (MID_SEG_TX_ID << MID_SEG_ID_SHIFT) |
-+	    (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) |
-+	    (tx->resolution << MID_SEG_RATE_SHIFT) |
-+	    (size/(ATM_CELL_PAYLOAD/4));
-+/*printk("dsc = 0x%08lx\n",tx->send[tx->tx_pos]);*/
-+	tx->send[(tx->tx_pos+1) & (tx->words-1)] = (vcc->vci <<
-+	    MID_SEG_VCI_SHIFT) | (aal5 ? 0 : (skb->data[3] & 0xf));
-+	DPRINTK("size: %ld, len:%ld\n",size,skb->len);
-+	if (aal5)
-+		tx->send[(tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1)] =
-+		    skb->len;
-+	j = j >> 1;
-+	for (i = 0; i < j; i++) {
-+		eni_dev->tx_dma[dma_wr*2] = dma[i*2];
-+		eni_dev->tx_dma[dma_wr*2+1] = dma[i*2+1];
-+		dma_wr = (dma_wr+1) & (NR_DMA_TX-1);
-+	}
-+	skb->atm.pos = tx->tx_pos;
-+	skb->atm.size = size;
-+	ENI_VCC(vcc)->txing += size;
-+	tx->tx_pos = (tx->tx_pos+size) & (tx->words-1);
-+	DPRINTK("dma_wr set to %ld, tx_pos is now %ld\n",dma_wr,tx->tx_pos);
-+	eni_dev->reg[MID_DMA_WR_TX] = dma_wr;
-+	skb_queue_tail(&eni_dev->tx_queue,skb);
-+queued++;
-+	return enq_ok;
-+}
-+
-+
-+static void poll_tx(struct atm_dev *dev)
-+{
-+	struct eni_tx *tx;
-+	struct sk_buff *skb;
-+	enum enq_res res;
-+	int i;
-+
-+	DPRINTK(">poll_tx\n");
-+	for (i = NR_CHAN-1; i >= 0; i--) {
-+		tx = &ENI_DEV(dev)->tx[i];
-+		if (tx->send)
-+			while ((skb = skb_dequeue(&tx->backlog))) {
-+				res = do_tx(skb);
-+				if (res != enq_ok) {
-+					DPRINTK("re-queuing TX PDU\n");
-+					skb_queue_head(&tx->backlog,skb);
-+requeued++;
-+					if (res == enq_jam) return;
-+					else break;
-+				}
-+			}
-+	}
-+}
-+
-+
-+static void dequeue_tx(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+	struct atm_vcc *vcc;
-+	struct sk_buff *skb;
-+	struct eni_tx *tx;
-+
-+	NULLCHECK(dev);
-+	eni_dev = ENI_DEV(dev);
-+	NULLCHECK(eni_dev);
-+	while ((skb = skb_dequeue(&eni_dev->tx_queue))) {
-+		vcc = skb->atm.vcc;
-+		NULLCHECK(vcc);
-+		tx = ENI_VCC(vcc)->tx;
-+		NULLCHECK(ENI_VCC(vcc)->tx);
-+		DPRINTK("dequeue_tx: next 0x%lx curr 0x%lx\n",skb->atm.pos,
-+		    eni_dev->reg[MID_TX_DESCRSTART(tx->index)]);
-+		if (ENI_VCC(vcc)->txing < tx->words && skb->atm.pos ==
-+		    eni_dev->reg[MID_TX_DESCRSTART(tx->index)]) {
-+			skb_queue_head(&eni_dev->tx_queue,skb);
-+			break;
-+		}
-+		ENI_VCC(vcc)->txing -= skb->atm.size;
-+		if (vcc->pop) vcc->pop(vcc,skb);
-+		else dev_kfree_skb(skb,FREE_WRITE);
-+		vcc->stats->tx++;
-+		wake_up(&eni_dev->tx_wait);
-+dma_complete++;
-+	}
-+}
-+
-+
-+static int alloc_tx(struct eni_dev *eni_dev,int *pcr,int min,int max,int ubr)
-+{
-+	static const int pre_div[] = { 4,16,128,2048 };
-+	    /* 2^(((x+2)^2-(x+2))/2+1) */
-+	int i,pre,res;
-+
-+	DPRINTK("in pcr: %d/%d%s\n",min,max,ubr ? " (ubr)" : "");
-+	for (i = !ubr; i < NR_CHAN; i++)
-+		if (!eni_dev->tx[i].send) break;
-+	if (i == NR_CHAN) return -EAGAIN;
-+	if (min)
-+		if (min == ATM_MAX_PCR) pre = res = 0;
-+		else {
-+			int div;
-+
-+			for (pre = 0; pre < 3; pre++)
-+				if (TS_CLOCK/pre_div[pre]/64 <= min) break;
-+			div = pre_div[pre]*min;
-+			DPRINTK("min div %d\n",div);
-+			res = TS_CLOCK/div-1;
-+		}
-+	else {
-+		int div;
-+
-+		if (max > eni_dev->tx_bw && !ubr) max = eni_dev->tx_bw;
-+		for (pre = 3; pre >= 0; pre--)
-+			if (TS_CLOCK/pre_div[pre]/64 > max) break;
-+		if (pre < 3) pre++; /* else fail later */
-+		div = pre_div[pre]*max;
-+		DPRINTK("max div %d\n",div);
-+		res = (TS_CLOCK+div-1)/div-1;
-+	}
-+	if (res < 0) res = 0;
-+	if (res > MID_SEG_MAX_RATE) res = MID_SEG_MAX_RATE;
-+	eni_dev->tx[i].prescaler = pre;
-+	eni_dev->tx[i].resolution = res;
-+	eni_dev->tx[i].pcr = *pcr = TS_CLOCK/pre_div[pre]/(res+1);
-+	DPRINTK("out pcr: %d (%d:%d) <%d,%d>\n",*pcr,pre,res,min,max);
-+	DPRINTK("got chan %d\n",i);
-+	if ((min && *pcr < min) || (max && *pcr > max)) return -EINVAL;
-+	if (*pcr > eni_dev->tx_bw && !ubr) return -EAGAIN;
-+	return i;
-+}
-+
-+
-+static int open_tx_first(struct atm_vcc *vcc)
-+{
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	unsigned long size,mem;
-+	int tx_ind,pcr,order;
-+
-+	eni_dev = ENI_DEV(vcc->dev);
-+	eni_vcc = ENI_VCC(vcc);
-+	eni_vcc->tx = NULL;
-+	if (vcc->txtp.class == ATM_NONE) return 0;
-+	eni_vcc->txing = 0;
-+	if (vcc->txtp.class != ATM_UBR)
-+		size = vcc->txtp.max_sdu*3; /* @@@ improve */
-+	else {
-+		if (eni_dev->ubr) {
-+			eni_vcc->tx = eni_dev->ubr;
-+			return 0;
-+		}
-+		size = UBR_BUFFER;
-+		vcc->txtp.min_pcr = ATM_MAX_PCR;
-+		vcc->txtp.max_pcr = 0;
-+	}
-+	DPRINTK("get_tx\n");
-+	mem = eni_alloc_mem(eni_dev,&size);
-+	if (!mem) return -ENOBUFS;
-+	if ((tx_ind = alloc_tx(eni_dev,&pcr,vcc->txtp.min_pcr,
-+	    vcc->txtp.max_pcr,vcc->txtp.class == ATM_UBR)) < 0) {
-+		eni_free_mem(eni_dev,mem,size);
-+		return tx_ind;
-+	}
-+	if (vcc->txtp.class == ATM_UBR) eni_dev->ubr = &eni_dev->tx[tx_ind];
-+	else eni_dev->tx_bw -= pcr;
-+	vcc->txtp.min_pcr = vcc->txtp.max_pcr = pcr;
-+	eni_dev->tx[tx_ind].send = (volatile unsigned long *) mem;
-+	eni_dev->tx[tx_ind].words = size >> 2;
-+	skb_queue_head_init(&eni_dev->tx[tx_ind].backlog);
-+	size >>= 10;
-+	for (order = -1; size; order++) size >>= 1;
-+	eni_dev->reg[MID_TX_PLACE(tx_ind)] = (order << MID_SIZE_SHIFT) |
-+	    ((eni_dev->tx[tx_ind].send-eni_dev->ram) >> MID_LOC_SKIP);
-+	eni_dev->tx[tx_ind].tx_pos = eni_dev->reg[MID_TX_DESCRSTART(tx_ind)] &
-+	    MID_DESCR_START;
-+/*printk("mp 0x%lx tp 0x%lx\n",(unsigned long) eni_dev->tx[tx_ind].send,
-+  eni_dev->tx[tx_ind].tx_pos);*/
-+	eni_vcc->tx = &eni_dev->tx[tx_ind];
-+	return 0;
-+}
-+
-+
-+static int open_tx_second(struct atm_vcc *vcc)
-+{
-+	return 0; /* nothing to do */
-+}
-+
-+
-+static void close_tx(struct atm_vcc *vcc)
-+{
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	unsigned long flags;
-+
-+	eni_vcc = ENI_VCC(vcc);
-+	if (!eni_vcc->tx) return;
-+	eni_dev = ENI_DEV(vcc->dev);
-+	/* wait for TX queue to drain */
-+	DPRINTK("eni_close: waiting for TX ...\n");
-+	save_flags(flags);
-+	cli();
-+	while (skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing) {
-+		DPRINTK("%d TX left\n",eni_vcc->txing);
-+		sleep_on(&eni_dev->tx_wait);
-+	}
-+	restore_flags(flags);
-+#if 0
-+	if (skb_peek(&eni_vcc->tx->backlog))
-+		printk(KERN_CRIT DEV_LABEL "SKBs in BACKLOG !!!\n");
-+#endif
-+	if (eni_vcc->tx != eni_dev->ubr) {
-+		eni_free_mem(eni_dev,(unsigned long) eni_vcc->tx->send,
-+		    eni_vcc->tx->words << 2);
-+		eni_vcc->tx->send = NULL;
-+		eni_dev->tx_bw += eni_vcc->tx->pcr;
-+	}
-+	eni_vcc->tx = NULL;
-+}
-+
-+
-+static int start_tx(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+	int i;
-+
-+	eni_dev = ENI_DEV(dev);
-+	eni_dev->lost = 0;
-+	eni_dev->tx_bw = ATM_OC3_PCR;
-+	eni_dev->tx_wait = NULL;
-+	eni_dev->ubr = NULL;
-+	skb_queue_head_init(&eni_dev->tx_queue);
-+	eni_dev->reg[MID_DMA_WR_TX] = 0;
-+	for (i = 0; i < NR_CHAN; i++) {
-+		eni_dev->tx[i].send = NULL;
-+		eni_dev->tx[i].index = i;
-+	}
-+	return 0;
-+}
-+
-+
-+/*--------------------------------- common ----------------------------------*/
-+
-+
-+static void foo(void)
-+{
-+printk(KERN_INFO
-+  "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n"
-+  "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n",
-+  tx_complete,dma_complete,queued,requeued,submitted,backlogged,
-+  rx_enqueued,rx_dequeued,putting,pushed);
-+if (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost);
-+}
-+
-+static void misc_int(struct atm_dev *dev,unsigned long reason)
-+{
-+	struct eni_dev *eni_dev;
-+
-+	DPRINTK(">misc_int\n");
-+	eni_dev = ENI_DEV(dev);
-+	if (reason & MID_STAT_OVFL) {
-+		EVENT("stat overflow\n",0,0);
-+		eni_dev->lost += eni_dev->reg[MID_STAT] & MID_OVFL_TRASH;
-+	}
-+	if (reason & MID_SUNI_INT) {
-+		EVENT("SUNI int\n",0,0);
-+		dev->phy->interrupt(dev);
-+		foo();
-+	}
-+	if (reason & MID_TX_IDENT_MISM) {
-+		printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident "
-+		    "mismatch\n",dev->number);
-+		EVENT("---dump ends here---\n",0,0);
-+		printk(KERN_NOTICE "---recent events---\n");
-+		event_dump();
-+	}
-+	if (reason & MID_TX_DMA_OVFL) {
-+		printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA "
-+		    "overflow\n",dev->number);
-+		EVENT("---dump ends here---\n",0,0);
-+		printk(KERN_NOTICE "---recent events---\n");
-+		event_dump();
-+	}
-+}
-+
-+
-+static void eni_int(int irq,void *dev_id,struct pt_regs *regs)
-+{
-+	struct atm_dev *dev;
-+	struct eni_dev *eni_dev;
-+	unsigned long reason;
-+
-+	DPRINTK(">eni_int\n");
-+	dev = dev_id;
-+	eni_dev = ENI_DEV(dev);
-+	while ((reason = eni_dev->reg[MID_ISA])) {
-+		DPRINTK(DEV_LABEL ": int 0x%lx\n",reason);
-+		if (reason & MID_RX_DMA_COMPLETE) {
-+			EVENT("INT: RX DMA complete, starting dequeue_rx\n",
-+			    0,0);
-+			dequeue_rx(dev);
-+			EVENT("dequeue_rx done, starting poll_rx\n",0,0);
-+			poll_rx(dev);
-+			EVENT("poll_rx done\n",0,0);
-+			/* poll_tx ? */
-+		}
-+		if (reason & MID_SERVICE) {
-+			EVENT("INT: service, starting get_service\n",0,0);
-+			get_service(dev);
-+			EVENT("get_service done, starting poll_rx\n",0,0);
-+			poll_rx(dev);
-+			EVENT("poll_rx done\n",0,0);
-+		}
-+ 		if (reason & MID_TX_DMA_COMPLETE) {
-+			EVENT("INT: TX DMA COMPLETE\n",0,0);
-+			dequeue_tx(dev);
-+		}
-+		if (reason & MID_TX_COMPLETE) {
-+			EVENT("INT: TX COMPLETE\n",0,0);
-+tx_complete++;
-+			wake_up(&eni_dev->tx_wait);
-+			poll_tx(dev);
-+			/* poll_rx ? */
-+		}
-+		if (reason & (MID_STAT_OVFL | MID_SUNI_INT | MID_TX_IDENT_MISM
-+		    | MID_TX_DMA_OVFL)) {
-+			EVENT("misc interrupt\n",0,0);
-+			misc_int(dev,reason);
-+		}
-+	}
-+}
-+
-+
-+/*--------------------------------- entries ---------------------------------*/
-+
-+
-+static const char *media_name[] = {
-+    "MMF", "SMF", "MMF", "03?", /*  0- 3 */
-+    "UTP", "05?", "06?", "07?", /*  4- 7 */
-+    "TAXI","09?", "10?", "11?", /*  8-11 */
-+    "12?", "13?", "14?", "15?", /* 12-15 */
-+    "MMF", "SMF", "18?", "19?", /* 16-19 */
-+    "UTP", "21?", "22?", "23?", /* 20-23 */
-+    "24?", "25?", "26?", "27?", /* 24-27 */
-+    "28?", "29?", "30?", "31?"  /* 28-31 */
-+};
-+
-+
-+#define SET_SEPROM \
-+  ({ if (!error && !pci_error) { \
-+    pci_error = pcibios_write_config_byte(eni_dev->bus,eni_dev->dev_fn, \
-+      PCI_TONGA_CTRL,tonga); \
-+    udelay(10); /* 10 usecs */ \
-+  } })
-+#define GET_SEPROM \
-+  ({ if (!error && !pci_error) { \
-+    pci_error = pcibios_read_config_byte(eni_dev->bus,eni_dev->dev_fn, \
-+      PCI_TONGA_CTRL,&tonga); \
-+    udelay(10); /* 10 usecs */ \
-+  } })
-+
-+
-+static int get_esi_asic(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+	unsigned char tonga;
-+	int error,failed,pci_error;
-+	int address,i,j;
-+
-+	eni_dev = ENI_DEV(dev);
-+	error = pci_error = 0;
-+	tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK;
-+	SET_SEPROM;
-+	for (i = 0; i < ESI_LEN && !error && !pci_error; i++) {
-+		/* start operation */
-+		tonga |= SEPROM_DATA;
-+		SET_SEPROM;
-+		tonga |= SEPROM_CLK;
-+		SET_SEPROM;
-+		tonga &= ~SEPROM_DATA;
-+		SET_SEPROM;
-+		tonga &= ~SEPROM_CLK;
-+		SET_SEPROM;
-+		/* send address */
-+		address = ((i+SEPROM_ESI_BASE) << 1)+1;
-+		for (j = 7; j >= 0; j--) {
-+			tonga = (address >> j) & 1 ? tonga | SEPROM_DATA :
-+			    tonga & ~SEPROM_DATA;
-+			SET_SEPROM;
-+			tonga |= SEPROM_CLK;
-+			SET_SEPROM;
-+			tonga &= ~SEPROM_CLK;
-+			SET_SEPROM;
-+		}
-+		/* get ack */
-+		tonga |= SEPROM_DATA;
-+		SET_SEPROM;
-+		tonga |= SEPROM_CLK;
-+		SET_SEPROM;
-+		GET_SEPROM;
-+		failed = tonga & SEPROM_DATA;
-+		tonga &= ~SEPROM_CLK;
-+		SET_SEPROM;
-+		tonga |= SEPROM_DATA;
-+		SET_SEPROM;
-+		if (failed) error = -EIO;
-+		else {
-+			dev->esi[i] = 0;
-+			for (j = 7; j >= 0; j--) {
-+				dev->esi[i] <<= 1;
-+				tonga |= SEPROM_DATA;
-+				SET_SEPROM;
-+				tonga |= SEPROM_CLK;
-+				SET_SEPROM;
-+				GET_SEPROM;
-+				if (tonga & SEPROM_DATA) dev->esi[i] |= 1;
-+				tonga &= ~SEPROM_CLK;
-+				SET_SEPROM;
-+				tonga |= SEPROM_DATA;
-+				SET_SEPROM;
-+			}
-+			/* get ack */
-+			tonga |= SEPROM_DATA;
-+			SET_SEPROM;
-+			tonga |= SEPROM_CLK;
-+			SET_SEPROM;
-+			GET_SEPROM;
-+			if (!(tonga & SEPROM_DATA)) error = -EIO;
-+			tonga &= ~SEPROM_CLK;
-+			SET_SEPROM;
-+			tonga |= SEPROM_DATA;
-+			SET_SEPROM;
-+		}
-+		/* stop operation */
-+		tonga &= ~SEPROM_DATA;
-+		SET_SEPROM;
-+		tonga |= SEPROM_CLK;
-+		SET_SEPROM;
-+		tonga |= SEPROM_DATA;
-+		SET_SEPROM;
-+	}
-+	if (pci_error) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI (%s)\n",
-+		    dev->number,pcibios_strerror(pci_error));
-+		error = -EIO;
-+	}
-+	return error;
-+}
-+
-+
-+#undef SET_SEPROM
-+#undef GET_SEPROM
-+
-+
-+static int get_esi_fpga(struct atm_dev *dev,unsigned long base)
-+{
-+	struct eni_dev *eni_dev;
-+	struct midway_eprom *eprom;
-+	int i;
-+
-+	eprom = (struct midway_eprom *) (base+EPROM_SIZE-sizeof(struct
-+	    midway_eprom));
-+	eni_dev = ENI_DEV(dev);
-+	for (i = 0; i < ESI_LEN; i++)
-+		dev->esi[i] = eprom->mac[(i & ~3) | (3-(i & 3))];
-+	return 0;
-+}
-+
-+
-+static int eni_init(struct atm_dev *dev)
-+{
-+	struct midway_eprom *eprom;
-+	struct eni_dev *eni_dev;
-+	unsigned int real_base,base;
-+	unsigned short command;
-+	unsigned char revision;
-+	int error,i,last;
-+
-+	DPRINTK(">eni_init\n");
-+	dev->ci_range.vpi_bits = 0;
-+	dev->ci_range.vci_bits = NR_VCI_LD;
-+	eni_dev = ENI_DEV(dev);
-+	if ((error = pcibios_read_config_word(eni_dev->bus,eni_dev->dev_fn,
-+	    PCI_COMMAND,&command)) || (error = pcibios_read_config_dword(
-+	    eni_dev->bus,eni_dev->dev_fn,PCI_BASE_ADDRESS_0,&real_base)) ||
-+	    (error = pcibios_read_config_byte(eni_dev->bus,eni_dev->dev_fn,
-+	    PCI_INTERRUPT_LINE,&eni_dev->irq)) || (error =
-+	    pcibios_read_config_byte(eni_dev->bus,eni_dev->dev_fn,
-+	    PCI_REVISION_ID,&revision))) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): init error %s\n",
-+		    dev->number,pcibios_strerror(error));
-+		return -EINVAL;
-+	}
-+	real_base &= MEM_VALID; /* strip flags */
-+	if ((error = pcibios_write_config_word(eni_dev->bus,eni_dev->dev_fn,
-+	    PCI_COMMAND,PCI_COMMAND_MEMORY | (eni_dev->asic ?
-+	    PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory (%s)"
-+		    "\n",dev->number,pcibios_strerror(error));
-+		return error;
-+	}
-+	printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",
-+	    dev->number,revision,real_base,eni_dev->irq);
-+	if (!(base = (unsigned long) vremap(real_base,MAP_MAX_SIZE))) {
-+		printk("\n");
-+		printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page "
-+		    "mapping\n",dev->number);
-+		return error;
-+	}
-+	eni_dev->base_diff = real_base-base;
-+	/* id may not be present in ASIC Tonga boards - check this @@@ */
-+	if (!eni_dev->asic) {
-+		eprom = (struct midway_eprom *) (base+EPROM_SIZE-sizeof(struct
-+		    midway_eprom));
-+		if (eprom->magic != ENI155_MAGIC) {
-+			printk("\n");
-+			printk(KERN_ERR KERN_ERR DEV_LABEL "(itf %d): bad "
-+			    "magic - expected 0x%X, got 0x%lX\n",dev->number,
-+			    ENI155_MAGIC,eprom->magic);
-+			return -EINVAL;
-+		}
-+	}
-+	eni_dev->phy = (volatile unsigned long *) (base+PHY_BASE);
-+	eni_dev->reg = (volatile unsigned long *) (base+REG_BASE);
-+	eni_dev->ram = (volatile unsigned long *) (base+RAM_BASE);
-+	last = (MAP_MAX_SIZE-RAM_BASE)/4;
-+	for (i = last-RAM_INCREMENT; i >= 0;
-+	    i -= RAM_INCREMENT) {
-+		eni_dev->ram[i] = 0x55555555;
-+		if (eni_dev->ram[i] != 0x55555555) last = i;
-+		else {
-+			eni_dev->ram[i] = 0xAAAAAAAA;
-+			if (eni_dev->ram[i] != 0xAAAAAAAA) last = i;
-+			else eni_dev->ram[i] = i;
-+		}
-+	}
-+	for (i = 0; i < last; i += RAM_INCREMENT)
-+		if (eni_dev->ram[i] != i) break;
-+	eni_dev->mem = i << 2;
-+	memset((void *) eni_dev->ram,0,eni_dev->mem);
-+	/* TODO: should shrink allocation now */
-+	printk("mem=%dkB (",eni_dev->mem >> 10);
-+	/* TODO: check for non-SUNI, check for TAXI ? */
-+	if (!(eni_dev->reg[MID_RES_ID_MCON] & 0x200) != !eni_dev->asic) {
-+		printk(")\n");
-+		printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%0lx\n",
-+		    dev->number,eni_dev->reg[MID_RES_ID_MCON]);
-+		return -EINVAL;
-+	}
-+	error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base);
-+	if (error) return error;
-+	for (i = 0; i < ESI_LEN; i++)
-+		printk("%s%02X",i ? "-" : "",dev->esi[i]);
-+	printk(")\n");
-+	printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number,
-+	    eni_dev->reg[MID_RES_ID_MCON] & 0x200 ? "ASIC" : "FPGA",
-+	    media_name[eni_dev->reg[MID_RES_ID_MCON] & DAUGTHER_ID]);
-+	return suni_init(dev);
-+}
-+
-+
-+static int eni_start(struct atm_dev *dev)
-+{
-+	struct eni_dev *eni_dev;
-+	volatile unsigned long *buf;
-+	unsigned long buffer_mem;
-+	int error;
-+
-+	DPRINTK(">eni_start\n");
-+	eni_dev = ENI_DEV(dev);
-+	if (request_irq(eni_dev->irq,&eni_int,0,DEV_LABEL,dev)) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
-+		    dev->number,eni_dev->irq);
-+		return -EAGAIN;
-+	}
-+	/* @@@ should release IRQ on error */
-+	if ((error = pcibios_write_config_word(eni_dev->bus,eni_dev->dev_fn,
-+	    PCI_COMMAND,PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
-+	    (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+"
-+		    "master (%s)\n",dev->number,pcibios_strerror(error));
-+		return error;
-+	}
-+	if ((error = pcibios_write_config_byte(eni_dev->bus,eni_dev->dev_fn,
-+	    PCI_TONGA_CTRL,END_SWAP_DMA))) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap "
-+		    "(%s)\n",dev->number,pcibios_strerror(error));
-+		return error;
-+	}
-+	/* determine addresses of internal tables */
-+	eni_dev->vci = eni_dev->ram;
-+	eni_dev->rx_dma = eni_dev->ram+NR_VCI*4;
-+	eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*2;
-+	eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*2;
-+	buf = eni_dev->service+NR_SERVICE;
-+	DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n",
-+	    (unsigned long) eni_dev->vci,(unsigned long) eni_dev->rx_dma,
-+	    (unsigned long) eni_dev->tx_dma,(unsigned long) eni_dev->service,
-+	    buf);
-+	/* initialize memory management */
-+	buffer_mem = eni_dev->mem-((unsigned long) buf-
-+	    (unsigned long) eni_dev->ram);
-+	eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2;
-+	eni_dev->free_list = (struct eni_free *) kmalloc(
-+	    sizeof(struct eni_free)*(eni_dev->free_list_size+1),GFP_KERNEL);
-+	if (!eni_dev->free_list) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
-+		    dev->number);
-+		return -ENOMEM;
-+	}
-+	eni_dev->free_len = 0;
-+	eni_put_free(eni_dev,(unsigned long) buf,buffer_mem);
-+	memset((void *) eni_dev->vci,0,16*NR_VCI); /* clear VCI table */
-+	/*
-+	 * byte_addr  free (k)
-+	 * 0x00000000     512  VCI table
-+	 * 0x00004000	  496  RX DMA
-+	 * 0x00005000	  492  TX DMA
-+	 * 0x00006000	  488  service list
-+	 * 0x00007000	  484  buffers
-+	 * 0x00080000	    0  end (512kB)
-+	 */
-+	eni_dev->reg[MID_IE] = 0xffffffff;
-+#if 0
-+MID_TX_COMPLETE_0 | MID_TX_DMA_OVFL |
-+	    MID_TX_IDENT_MISM | MID_RX_DMA_COMPLETE | MID_TX_DMA_COMPLETE |
-+	    MID_SERVICE | MID_SUNI_INT | MID_STAT_OVFL; /* enable interrupts */
-+#endif
-+	error = start_tx(dev);
-+	if (error) return error;
-+	error = start_rx(dev);
-+	if (error) return error;
-+	error = dev->phy->start(dev);
-+	if (error) return error;
-+	eni_dev->reg[MID_MC_S] |= (1 << MID_INT_SEL_SHIFT) |
-+	    MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE;
-+	    /* Tonga uses SBus INTReq1 */
-+	(void) eni_dev->reg[MID_ISA]; /* clear Midway interrupts */
-+	return 0;
-+}
-+
-+
-+static void eni_close(struct atm_vcc *vcc)
-+{
-+	DPRINTK(">eni_close\n");
-+	if (!ENI_VCC(vcc)) return;
-+	vcc->flags &= ~ATM_VF_READY;
-+	close_rx(vcc);
-+	close_tx(vcc);
-+	DPRINTK("eni_close: done waiting\n");
-+	/* deallocate memory */
-+	kfree(ENI_VCC(vcc));
-+	ENI_VCC(vcc) = NULL;
-+	vcc->flags &= ~ATM_VF_ADDR;
-+	/*foo();*/
-+}
-+
-+
-+static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci)
-+{
-+	struct atm_vcc *walk;
-+
-+	if (*vpi == ATM_VPI_ANY) *vpi = 0;
-+	if (*vci == ATM_VCI_ANY) {
-+		for (*vci = ATM_NOT_RSV_VCI; *vci < NR_VCI; (*vci)++) {
-+			if (vcc->rxtp.class != ATM_NONE &&
-+			    ENI_DEV(vcc->dev)->rx_map[*vci])
-+				continue;
-+			if (vcc->txtp.class != ATM_NONE) {
-+				for (walk = vcc->dev->vccs; walk;
-+				    walk = walk->next)
-+					if ((walk->flags & ATM_VF_ADDR) &&
-+					    walk->vci == *vci &&
-+					    walk->txtp.class != ATM_NONE)
-+						break;
-+				if (walk) continue;
-+			}
-+			break;
-+		}
-+		return *vci == NR_VCI ? -EADDRINUSE : 0;
-+	}
-+	if (*vci == ATM_VCI_UNSPEC) return 0;
-+	if (vcc->rxtp.class != ATM_NONE && ENI_DEV(vcc->dev)->rx_map[*vci])
-+		return -EADDRINUSE;
-+	if (vcc->txtp.class == ATM_NONE) return 0;
-+	for (walk = vcc->dev->vccs; walk; walk = walk->next)
-+		if ((walk->flags & ATM_VF_ADDR) && walk->vci == *vci &&
-+		    walk->txtp.class != ATM_NONE)
-+			return -EADDRINUSE;
-+	return 0;
-+}
-+
-+
-+static int eni_open(struct atm_vcc *vcc,short vpi,int vci)
-+{
-+	struct eni_dev *eni_dev;
-+	struct eni_vcc *eni_vcc;
-+	int error;
-+
-+	DPRINTK(">eni_open\n");
-+	EVENT("eni_open\n",0,0);
-+	if (!(vcc->flags & ATM_VF_PARTIAL)) ENI_VCC(vcc) = NULL;
-+	eni_dev = ENI_DEV(vcc->dev);
-+#if 1 /* set to 0 to test atm_find_ci (get_ci usually is faster) */
-+	error = get_ci(vcc,&vpi,&vci);
-+	if (error) return error;
-+#else
-+	error = atm_find_ci(vcc,&vpi,&vci);
-+	if (error) return error;
-+#endif
-+	vcc->vpi = vpi;
-+	vcc->vci = vci;
-+	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
-+		vcc->flags |= ATM_VF_ADDR;
-+	if (vcc->aal != ATM_AAL0 && vcc->aal != ATM_AAL5) return -EINVAL;
-+	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
-+	    vcc->vci);
-+	if (!(vcc->flags & ATM_VF_PARTIAL)) {
-+		eni_vcc = kmalloc(sizeof(struct eni_vcc),GFP_KERNEL);
-+		if (!eni_vcc) return -ENOMEM;
-+		ENI_VCC(vcc) = eni_vcc;
-+		eni_vcc->tx = NULL; /* for eni_close after open_rx */
-+		if ((error = open_rx_first(vcc))) {
-+			eni_close(vcc);
-+			return error;
-+		}
-+		if ((error = open_tx_first(vcc))) {
-+			eni_close(vcc);
-+			return error;
-+		}
-+	}
-+	if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
-+	if ((error = open_rx_second(vcc))) {
-+		eni_close(vcc);
-+		return error;
-+	}
-+	if ((error = open_tx_second(vcc))) {
-+		eni_close(vcc);
-+		return error;
-+	}
-+	vcc->flags |= ATM_VF_READY;
-+	/* should power down SUNI while !ref_count @@@ */
-+	return 0;
-+}
-+
-+
-+static int eni_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
-+{
-+	if (cmd == ENI_MEMDUMP) {
-+		dump(dev);
-+		return 0;
-+	}
-+	if (cmd == ATM_SETCIRANGE) {
-+		struct atm_cirange ci;
-+
-+		memcpy_fromfs(&ci,(void *) arg,sizeof(struct atm_cirange));
-+		if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) &&
-+		    (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX))
-+		    return 0;
-+		return -EINVAL;
-+	}
-+	if (!dev->phy->ioctl) return -EINVAL;
-+	return dev->phy->ioctl(dev,cmd,arg);
-+}
-+
-+
-+static int eni_getsockopt(struct atm_vcc *vcc,int level,int optname,
-+    char *optval,int *optlen)
-+{
-+#ifdef CONFIG_MMU_HACKS
-+
-+static const struct atm_buffconst bctx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
-+static const struct atm_buffconst bcrx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
-+
-+#else
-+
-+static const struct atm_buffconst bctx = { 4,0,4,0,0,0 };
-+static const struct atm_buffconst bcrx = { 4,0,4,0,0,0 };
-+
-+#endif
-+	int error;
-+
-+	if (level == SOL_AAL && (optname == SO_BCTXOPT ||
-+	    optname == SO_BCRXOPT)) {
-+		if (get_fs_long(optlen) < sizeof(struct atm_buffconst))
-+			return -EINVAL;
-+		put_fs_long(sizeof(struct atm_buffconst),optlen);
-+		error = verify_area(VERIFY_WRITE,optval,
-+		    sizeof(struct atm_buffconst));
-+		if (error) return error;
-+		memcpy_tofs(optval,optname == SO_BCTXOPT ? &bctx : &bcrx,
-+		    sizeof(struct atm_buffconst));
-+		return 0;
-+	}
-+	return -EINVAL;
-+}
-+
-+
-+static int eni_setsockopt(struct atm_vcc *vcc,int level,int optname,
-+    char *optval,int optlen)
-+{
-+	return -EINVAL;
-+}
-+
-+
-+static int eni_send(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	unsigned long flags;
-+
-+	DPRINTK(">eni_send\n");
-+	if (!ENI_VCC(vcc)->tx) {
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		return -EINVAL;
-+	}
-+	if (!skb) {
-+		printk(KERN_CRIT "!skb in eni_send ?\n");
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		return -EINVAL;
-+	}
-+	if (vcc->aal == ATM_AAL0) {
-+		if (skb->len != ATM_CELL_SIZE-1) {
-+			dev_kfree_skb(skb,FREE_WRITE);
-+			return -EINVAL;
-+		}
-+		*(unsigned long *) skb->data = htonl(*(unsigned long *)
-+		    skb->data);
-+	}
-+submitted++;
-+	skb->atm.vcc = vcc;
-+	save_flags(flags);
-+	cli(); /* brute force */
-+	if (skb_peek(&ENI_VCC(vcc)->tx->backlog) || do_tx(skb)) {
-+		skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb);
-+		backlogged++;
-+	}
-+	restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+static int eni_sg_send(struct atm_vcc *vcc,unsigned long start,
-+    unsigned long size)
-+{
-+	return vcc->aal == ATM_AAL5 && !((start | size) & 3);
-+		/* don't tolerate misalignment */
-+}
-+
-+
-+static void eni_phy_put(struct atm_dev *dev,unsigned char value,
-+    unsigned long addr)
-+{
-+	ENI_DEV(dev)->phy[addr] = value;
-+}
-+
-+
-+
-+static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr)
-+{
-+	volatile unsigned tmp; /* force 32 bit access */
-+
-+	tmp = ENI_DEV(dev)->phy[addr];
-+	return tmp;
-+}
-+
-+
-+static const struct atmdev_ops ops = {
-+	eni_open,
-+	eni_close,
-+	eni_ioctl,
-+	eni_getsockopt,
-+	eni_setsockopt,
-+	eni_send,
-+	eni_sg_send,
-+	NULL,			/* no poll */
-+	NULL,			/* no send_oam */
-+	eni_phy_put,
-+	eni_phy_get,
-+	NULL			/* no feedback */
-+};
-+
-+
-+int eni_detect(void)
-+{
-+	struct atm_dev *dev;
-+	struct eni_dev *eni_dev;
-+	int devs,type,index;
-+
-+	if (!pcibios_present()) {
-+		printk(KERN_ERR DEV_LABEL " driver but no PCI BIOS ?\n");
-+		return 0;
-+	}
-+	eni_dev = (struct eni_dev *) kmalloc(sizeof(struct eni_dev),
-+	    GFP_KERNEL);
-+	if (!eni_dev) return -ENOMEM;
-+	devs = 0;
-+	for (type = 0; type < 2; type++) {
-+		index = 0;
-+		while (!pcibios_find_device(PCI_VENDOR_ID_EF,type ?
-+		    PCI_DEVICE_ID_EF_ATM_ASIC : PCI_DEVICE_ID_EF_ATM_FPGA,
-+		    index,&eni_dev->bus,&eni_dev->dev_fn)) {
-+			dev = atm_dev_register(DEV_LABEL,&ops,0);
-+			if (!dev) break;
-+			ENI_DEV(dev) = eni_dev;
-+			eni_dev->asic = type;
-+			if (eni_init(dev) || eni_start(dev)) {
-+				atm_dev_deregister(dev);
-+				break;
-+			}
-+			eni_dev->more = eni_boards;
-+			eni_boards = dev;
-+			index++;
-+			devs++;
-+			eni_dev = (struct eni_dev *) kmalloc(sizeof(struct
-+			    eni_dev),GFP_KERNEL);
-+			if (!eni_dev) break;
-+		}
-+	}
-+	return devs;
-+}
-+
-+
-+#ifdef MODULE
-+
-+int init_module(void)
-+{
-+	if (!eni_detect()) {
-+		printk(KERN_ERROR DEV_LABEL ": no adapter found\n");
-+		return -ENXIO;
-+	}
-+	MOD_INC_USE_COUNT;
-+	return 0;
-+}
-+
-+
-+void cleanup_module(void)
-+{
-+	/*
-+	 * Well, there's no way to get rid of the driver yet, so we don't
-+	 * have to clean up, right ? :-)
-+	 */
-+}
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/eni.h	Fri Jul 19 15:08:56 1996
-@@ -0,0 +1,113 @@
-+/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+ 
-+#ifndef DRIVER_ATM_ENI_H
-+#define DRIVER_ATM_ENI_H
-+
-+#include <linux/atmioc.h>
-+
-+#define ENI_MEMDUMP	_IOW('a',ATMIOC_SARPRV,struct atmif_sioc)
-+						/* printk memory map */
-+
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+#include <linux/skbuff.h>
-+#include <linux/time.h>
-+
-+#include "midway.h"
-+
-+
-+#define KERNEL_OFFSET	0xC0000000	/* kernel 0x0 is at phys 0xC0000000 */
-+#define DEV_LABEL	"eni"
-+
-+#define UBR_BUFFER	(128*1024)	/* UBR buffer size */
-+
-+#define RX_DMA_BUF	 5		/* burst and skip a few things */
-+#define TX_DMA_BUF	60		/* should be enough for 64 kB */
-+
-+
-+struct eni_free {
-+	unsigned long start;		/* counting in bytes */
-+	int order;
-+};
-+
-+struct eni_tx {
-+	volatile unsigned long *send;	/* base, NULL if unused */
-+	int prescaler;			/* shaping prescaler */
-+	int resolution;			/* shaping divider */
-+	unsigned long tx_pos;		/* current TX write position */
-+	unsigned long words;		/* size of TX queue */
-+	int index;			/* TX channel number */
-+	int pcr;			/* peak cell rate */
-+	struct sk_buff_head backlog;	/* queue of waiting TX buffers */
-+};
-+
-+struct eni_vcc {
-+	int (*rx)(struct atm_vcc *vcc);	/* RX function, NULL if none */
-+	volatile unsigned long *recv;	/* receive buffer */
-+	unsigned long words;		/* its size in words */
-+	unsigned long descr;		/* next descriptor (RX) */
-+	unsigned long rx_pos;		/* current RX descriptor pos */
-+	struct eni_tx *tx;		/* TXer, NULL if none */
-+	int rxing;			/* number of pending PDUs */
-+	int servicing;			/* number of waiting VCs (0 or 1) */
-+	int txing;			/* number of pending TX cells/PDUs */
-+	struct timeval timestamp;	/* for RX timing */
-+	struct atm_vcc *next;		/* next pending RX */
-+	struct sk_buff *last;		/* last PDU being DMAed (used to carry
-+					   discard information) */
-+};
-+
-+struct eni_dev {
-+	/*-------------------------------- base pointers into Midway address
-+					   space */
-+	volatile unsigned long *phy;	/* PHY interface chip registers */
-+	volatile unsigned long *reg;	/* register base */
-+	volatile unsigned long *ram;	/* RAM base */
-+	volatile unsigned long *vci;	/* VCI table */
-+	volatile unsigned long *rx_dma;	/* RX DMA queue */
-+	volatile unsigned long *tx_dma;	/* TX DMA queue */
-+	volatile unsigned long *service;/* service list */
-+	/*-------------------------------- TX part */
-+	struct eni_tx tx[NR_CHAN];	/* TX channels */
-+	struct eni_tx *ubr;		/* UBR channel */
-+	struct sk_buff_head tx_queue;	/* PDUs currently being TX DMAed*/
-+	struct wait_queue *tx_wait;	/* for close */
-+	int tx_bw;			/* remaining bandwidth */
-+	/*-------------------------------- RX part */
-+	unsigned long serv_read;	/* host service read index */
-+	struct atm_vcc *fast,*last_fast;/* queues of VCCs with pending PDUs */
-+	struct atm_vcc *slow,*last_slow;
-+	struct atm_vcc **rx_map;	/* for fast lookups */
-+	struct sk_buff_head rx_queue;	/* PDUs currently being RX-DMAed */
-+	struct wait_queue *rx_wait;	/* for close */
-+	/*-------------------------------- statistics */
-+	unsigned long lost;		/* number of lost cells (RX) */
-+	/*-------------------------------- memory management */
-+	unsigned long base_diff;	/* virtual-real base address */
-+	int free_len;			/* free list length */
-+	struct eni_free *free_list;	/* free list */
-+	int free_list_size;		/* maximum size of free list */
-+	/*-------------------------------- ENI links */
-+	struct atm_dev *more;		/* other ENI devices */
-+	/*-------------------------------- general information */
-+	int mem;			/* RAM on board (in bytes) */
-+	int asic;			/* PCI interface type, 0 for FPGA */
-+	unsigned char irq;		/* IRQ */
-+	unsigned char bus;		/* PCI stuff */
-+	unsigned char dev_fn;
-+};
-+
-+
-+#define ENI_DEV(d) ((struct eni_dev *) (d)->dev_data)
-+#define ENI_VCC(d) ((struct eni_vcc *) (d)->dev_data)
-+
-+#endif /* __KERNEL__ */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/midway.h	Mon Jun 10 17:36:06 1996
-@@ -0,0 +1,265 @@
-+/* drivers/atm/midway.h - Efficient Networks Midway (SAR) description */
-+ 
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef DRIVERS_ATM_MIDWAY_H
-+#define DRIVERS_ATM_MIDWAY_H
-+
-+
-+#define NR_VCI		1024		/* number of VCIs */
-+#define NR_VCI_LD	10		/* log2(NR_VCI) */
-+#define NR_DMA_RX	512		/* RX DMA queue entries */
-+#define NR_DMA_TX	512		/* TX DMA queue entries */
-+#define NR_SERVICE	NR_VCI		/* service list size */
-+#define NR_CHAN		8		/* number of TX channels */
-+#define TS_CLOCK	25000000	/* traffic shaper clock (cell/sec) */
-+
-+#define MAP_MAX_SIZE	0x00400000	/* memory window for max config */
-+#define EPROM_SIZE	0x00010000
-+#define	MEM_VALID	0xffc00000	/* mask base address with this */
-+#define PHY_BASE	0x00020000	/* offset of PHY register are */
-+#define REG_BASE	0x00040000	/* offset of Midway register area */
-+#define RAM_BASE	0x00200000	/* offset of RAM area */
-+#define RAM_INCREMENT	0x00008000	/* probe for RAM every 128kB (32 kw) */
-+
-+#define MID_VCI_BASE	RAM_BASE
-+#define MID_DMA_RX_BASE	(MID_VCI_BASE+NR_VCI*16)
-+#define MID_DMA_TX_BASE	(MID_DMA_RX_BASE+NR_DMA_RX*8)
-+#define MID_SERVICE_BASE (MID_DMA_TX_BASE+NR_DMA_TX*8)
-+#define MID_FREE_BASE	(MID_SERVICE_BASE+NR_SERVICE*4)
-+
-+#define MAC_LEN 6 /* atm.h */
-+
-+#define MID_MIN_BUF_SIZE (1024)		/*   1 kB is minimum */
-+#define MID_MAX_BUF_SIZE (128*1024)	/* 128 kB is maximum */
-+
-+#define RX_DESCR_SIZE	1		/* RX PDU descr is 1 longword */
-+#define TX_DESCR_SIZE	2		/* TX PDU descr is 2 longwords */
-+#define AAL5_TRAILER	(ATM_AAL5_TRAILER/4) /* AAL5 trailer is 2 longwords */
-+
-+#define TX_GAP		8		/* TX buffer gap (words) */
-+
-+/*
-+ * Midway Reset/ID
-+ *
-+ * All values read-only. Writing to this register resets Midway chip.
-+ */
-+
-+#define MID_RES_ID_MCON	0x00		/* Midway Reset/ID */
-+
-+#define MID_ID		0xf0000000	/* Midway version */
-+#define MID_SHIFT	24
-+#define MID_MOTHER_ID	0x00000700	/* mother board id */
-+#define MID_MOTHER_SHIFT 8
-+#define MID_CON_TI	0x00000080	/* 0: normal ctrl; 1: SABRE */
-+#define MID_CON_SUNI	0x00000040	/* 0: UTOPIA; 1: SUNI */
-+#define MID_CON_V6	0x00000020	/* 0: non-pipel UTOPIA (required iff
-+					   !CON_SUNI; 1: UTOPIA */
-+#define DAUGTHER_ID	0x0000001f	/* daugther board id */
-+
-+/*
-+ * Interrupt Status Acknowledge, Interrupt Status & Interrupt Enable
-+ */
-+
-+#define MID_ISA		0x01		/* Interrupt Status Acknowledge */
-+#define MID_IS		0x02		/* Interrupt Status */
-+#define MID_IE		0x03		/* Interrupt Enable */
-+
-+#define MID_TX_COMPLETE_7 0x00010000	/* channel N completed a PDU */
-+#define MID_TX_COMPLETE_6 0x00008000	/* transmission */
-+#define MID_TX_COMPLETE_5 0x00004000
-+#define MID_TX_COMPLETE_4 0x00002000
-+#define MID_TX_COMPLETE_3 0x00001000
-+#define MID_TX_COMPLETE_2 0x00000800
-+#define MID_TX_COMPLETE_1 0x00000400
-+#define MID_TX_COMPLETE_0 0x00000200
-+#define MID_TX_COMPLETE	0x0001fe00	/* any TX */
-+#define MID_TX_DMA_OVFL	0x00000100	/* DMA to adapter overflow */
-+#define MID_TX_IDENT_MISM 0x00000080	/* TX: ident mismatch => halted */
-+#define MID_DMA_LERR_ACK 0x00000040	/* LERR - SBus ? */
-+#define MID_DMA_ERR_ACK	0x00000020	/* DMA error */
-+#define	MID_RX_DMA_COMPLETE 0x00000010	/* DMA to host done */
-+#define MID_TX_DMA_COMPLETE 0x00000008	/* DMA from host done */
-+#define MID_SERVICE	0x00000004	/* something in service list */
-+#define MID_SUNI_INT	0x00000002	/* interrupt from SUNI */
-+#define MID_STAT_OVFL	0x00000001	/* statistics overflow */
-+
-+/*
-+ * Master Control/Status
-+ */
-+
-+#define MID_MC_S	0x04
-+
-+#define MID_INT_SELECT	0x000001C0	/* Interrupt level (000: off) */
-+#define MID_INT_SEL_SHIFT 6
-+#define	MID_TX_LOCK_MODE 0x00000020	/* 0: streaming; 1: TX ovfl->lock */
-+#define MID_DMA_ENABLE	0x00000010	/* R: 0: disable; 1: enable
-+					   W: 0: no change; 1: enable */
-+#define MID_TX_ENABLE	0x00000008	/* R: 0: TX disabled; 1: enabled
-+					   W: 0: no change; 1: enable */
-+#define MID_RX_ENABLE	0x00000004	/* like TX */
-+#define MID_WAIT_1MS	0x00000002	/* R: 0: timer not running; 1: running
-+					   W: 0: no change; 1: no interrupts
-+							       for 1 ms */
-+#define MID_WAIT_500US	0x00000001	/* like WAIT_1MS, but 0.5 ms */
-+
-+/*
-+ * Statistics
-+ *
-+ * Cleared when reading.
-+ */
-+
-+#define MID_STAT		0x05
-+
-+#define MID_VCI_TRASH	0xFFFF0000	/* trashed cells because of VCI mode */
-+#define MID_VCI_TRASH_SHIFT 16
-+#define MID_OVFL_TRASH	0x0000FFFF	/* trashed cells because of overflow */
-+
-+/*
-+ * Address registers
-+ */
-+
-+#define MID_SERV_WRITE	0x06	/* free pos in service area (R, 10 bits) */
-+#define MID_DMA_ADDR	0x07	/* virtual DMA address (R, 32 bits) */
-+#define MID_DMA_WR_RX	0x08	/* (RW, 9 bits) */
-+#define MID_DMA_RD_RX	0x09
-+#define MID_DMA_WR_TX	0x0A
-+#define MID_DMA_RD_TX	0x0B
-+
-+/*
-+ * Transmit Place Registers (0x10+4*channel)
-+ */
-+
-+#define MID_TX_PLACE(c)	(0x10+4*(c))
-+
-+#define MID_SIZE	0x00003800	/* size, N*256 x 32 bit */
-+#define MID_SIZE_SHIFT	11
-+#define MID_LOCATION	0x000007FF	/* location in adapter memory (word) */
-+
-+#define MID_LOC_SKIP	8		/* 8 bits of location are always zero
-+					   (applies to all uses of location) */
-+
-+/*
-+ * Transmit ReadPtr Registers (0x11+4*channel)
-+ */
-+
-+#define MID_TX_RDPTR(c)	(0x11+4*(c))
-+
-+#define MID_READ_PTR	0x00007FFF	/* next word for PHY */
-+
-+/*
-+ * Transmit DescrStart Registers (0x12+4*channel)
-+ */
-+
-+#define MID_TX_DESCRSTART(c) (0x12+4*(c))
-+
-+#define MID_DESCR_START	0x00007FFF	/* seg buffer being DMAed */
-+
-+#define ENI155_MAGIC	0xa54b872d
-+
-+struct midway_eprom {
-+	unsigned char mac[MAC_LEN],inv_mac[MAC_LEN];
-+	unsigned char pad[36];
-+	unsigned long serial,inv_serial;
-+	unsigned long magic,inv_magic;
-+};
-+
-+
-+/*
-+ * VCI table entry
-+ */
-+
-+#define MID_VCI_IN_SERVICE	0x00000001	/* set if VCI is currently in
-+						   service list */
-+#define MID_VCI_SIZE		0x00038000	/* reassembly buffer size,
-+						   2*<size> kB */
-+#define MID_VCI_SIZE_SHIFT	15
-+#define MID_VCI_LOCATION	0x1ffc0000	/* buffer location */
-+#define MID_VCI_LOCATION_SHIFT	18
-+#define MID_VCI_PTI_MODE	0x20000000	/* 0: trash, 1: preserve */
-+#define MID_VCI_MODE		0xc0000000
-+#define MID_VCI_MODE_SHIFT	30
-+#define MID_VCI_READ		0x00007fff
-+#define MID_VCI_READ_SHIFT	0
-+#define MID_VCI_DESCR		0x7fff0000
-+#define MID_VCI_DESCR_SHIFT	16
-+#define MID_VCI_COUNT		0x000007ff
-+#define MID_VCI_COUNT_SHIFT	0
-+#define MID_VCI_STATE		0x0000c000
-+#define MID_VCI_STATE_SHIFT	14
-+#define MID_VCI_WRITE		0x7fff0000
-+#define MID_VCI_WRITE_SHIFT	16
-+
-+#define MID_MODE_TRASH	0
-+#define MID_MODE_RAW	1
-+#define MID_MODE_AAL5	2
-+
-+/*
-+ * Reassembly buffer descriptor
-+ */
-+
-+#define MID_RED_COUNT		0x000007ff
-+#define MID_RED_CRC_ERR		0x00000800
-+#define MID_RED_T		0x00001000
-+#define MID_RED_CE		0x00010000
-+#define MID_RED_CLP		0x01000000
-+#define MID_RED_IDEN		0xfe000000
-+#define MID_RED_SHIFT		25
-+
-+#define MID_RED_RX_ID		0x1b		/* constant identifier */
-+
-+/*
-+ * Segmentation buffer descriptor
-+ */
-+
-+#define MID_SEG_COUNT		MID_RED_COUNT
-+#define MID_SEG_RATE		0x01f80000
-+#define MID_SEG_RATE_SHIFT	19
-+#define MID_SEG_PR		0x06000000
-+#define MID_SEG_PR_SHIFT	25
-+#define MID_SEG_AAL5		0x08000000
-+#define MID_SEG_ID		0xf0000000
-+#define MID_SEG_ID_SHIFT	28
-+#define MID_SEG_MAX_RATE	63
-+
-+#define MID_SEG_CLP		0x00000001
-+#define MID_SEG_PTI		0x0000000e
-+#define MID_SEG_PTI_SHIFT	1
-+#define MID_SEG_VCI		0x00003ff0
-+#define MID_SEG_VCI_SHIFT	4
-+
-+#define MID_SEG_TX_ID		0xb		/* constant identifier */
-+
-+/*
-+ * DMA entry
-+ */
-+
-+#define MID_DMA_COUNT		0xffff0000
-+#define MID_DMA_COUNT_SHIFT	16
-+#define MID_DMA_END		0x00000020
-+#define MID_DMA_TYPE		0x0000000f
-+
-+#define MID_DT_JK	0x3
-+#define MID_DT_WORD	0x0
-+#define MID_DT_2W	0x7
-+#define MID_DT_4W	0x4
-+#define MID_DT_8W	0x5
-+#define MID_DT_16W	0x6
-+#define MID_DT_2WM	0xf
-+#define MID_DT_4WM	0xc
-+#define MID_DT_8WM	0xd
-+#define MID_DT_16WM	0xe
-+
-+/* only for RX*/
-+#define MID_DMA_VCI		0x0000ffc0
-+#define	MID_DMA_VCI_SHIFT	6
-+
-+/* only for TX */
-+#define MID_DMA_CHAN		0x000001c0
-+#define MID_DMA_CHAN_SHIFT	6
-+
-+#define MID_DT_BYTE	0x1
-+#define MID_DT_HWORD	0x2
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/suni.c	Mon Jun 10 17:36:06 1996
-@@ -0,0 +1,271 @@
-+/* drivers/atm/suni.c - PMC SUNI (PHY) driver */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/sched.h>
-+#include <linux/kernel.h>
-+#include <linux/mm.h>
-+#include <linux/errno.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+#include <linux/delay.h>
-+#include <linux/timer.h>
-+#include <asm/system.h>
-+#include <asm/param.h>
-+#include <asm/segment.h>
-+
-+#include "suni.h"
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+struct suni_priv {
-+	struct sonet_stats sonet_stats; /* link diagnostics */
-+	unsigned char loop_mode;        /* loopback mode */
-+	struct atm_dev *dev;		/* device back-pointer */
-+	struct suni_priv *next;		/* next SUNI */
-+};
-+
-+
-+#define PRIV(dev) ((struct suni_priv *) dev->phy_data)
-+
-+#define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg)
-+#define GET(reg) dev->ops->phy_get(dev,SUNI_##reg)
-+#define REG_CHANGE(mask,shift,value,reg) \
-+  PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg)
-+
-+
-+static struct timer_list poll_timer = { NULL, NULL, 0L, 0L, NULL };
-+static int start_timer = 1;
-+static struct suni_priv *sunis = NULL;
-+
-+
-+static void suni_hz(unsigned long dummy)
-+{
-+	struct suni_priv *walk;
-+	struct atm_dev *dev;
-+	struct sonet_stats *stats;
-+
-+	for (walk = sunis; walk; walk = walk->next) {
-+		dev = walk->dev;
-+		stats = &walk->sonet_stats;
-+		PUT(0,MRI); /* latch counters */
-+		udelay(1);
-+		stats->section_bip += (GET(RSOP_SBL) & 0xff) |
-+		    ((GET(RSOP_SBM) & 0xff) << 8);
-+		if (stats->section_bip < 0) stats->section_bip = LONG_MAX;
-+		stats->line_bip += (GET(RLOP_LBL) & 0xff) |
-+		    ((GET(RLOP_LB) & 0xff) << 8) |
-+		    ((GET(RLOP_LBM) & 0xff) << 16);
-+		if (stats->line_bip < 0) stats->line_bip = LONG_MAX;
-+		stats->path_bip += (GET(RPOP_PBL) & 0xff) |
-+		    ((GET(RPOP_PBM) & 0xff) << 8);
-+		if (stats->path_bip < 0) stats->path_bip = LONG_MAX;
-+		stats->line_febe += (GET(RLOP_LFL) & 0xff) |
-+		    ((GET(RLOP_LF) & 0xff) << 8) |
-+		    ((GET(RLOP_LFM) & 0xff) << 16);
-+		if (stats->line_febe < 0) stats->line_febe = LONG_MAX;
-+		stats->path_febe += (GET(RPOP_PFL) & 0xff) |
-+		    ((GET(RPOP_PFM) & 0xff) << 8);
-+		if (stats->path_febe < 0) stats->path_febe = LONG_MAX;
-+		stats->corr_hcs += GET(RACP_CHEC) & 0xff;
-+		if (stats->corr_hcs < 0) stats->corr_hcs = LONG_MAX;
-+		stats->uncorr_hcs += GET(RACP_UHEC) & 0xff;
-+		if (stats->uncorr_hcs < 0) stats->uncorr_hcs = LONG_MAX;
-+		stats->rx_cells += (GET(RACP_RCCL) & 0xff) |
-+		    ((GET(RACP_RCC) & 0xff) << 8) |
-+		    ((GET(RACP_RCCM) & 0xff) << 16);
-+		if (stats->rx_cells < 0) stats->rx_cells = LONG_MAX;
-+		stats->tx_cells += (GET(TACP_TCCL) & 0xff) |
-+		    ((GET(TACP_TCC) & 0xff) << 8) |
-+		    ((GET(TACP_TCCM) & 0xff) << 16);
-+		if (stats->tx_cells < 0) stats->tx_cells = LONG_MAX;
-+	}
-+	if (!start_timer) {
-+		del_timer(&poll_timer);
-+		poll_timer.expires = jiffies+HZ;
-+		add_timer(&poll_timer);
-+	}
-+}
-+
-+
-+static int fetch_stats(struct atm_dev *dev,struct sonet_stats *arg,int zero)
-+{
-+	unsigned long flags;
-+
-+	save_flags(flags);
-+	cli();
-+	if (arg)
-+		memcpy_tofs(arg,&PRIV(dev)->sonet_stats,
-+		    sizeof(struct sonet_stats));
-+	if (zero)
-+		memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
-+	restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+#define HANDLE_FLAG(flag,reg,bit) \
-+  if (todo & flag) { \
-+    if (set) PUT(GET(reg) | bit,reg); \
-+    else PUT(GET(reg) & ~bit,reg); \
-+    todo &= ~flag; \
-+  }
-+
-+
-+static int change_diag(struct atm_dev *dev,unsigned long arg,int set)
-+{
-+	int todo;
-+
-+	todo = get_fs_long(arg);
-+	HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8);
-+	HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP);
-+	HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3);
-+	HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF);
-+	HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS);
-+	HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS);
-+	HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS);
-+	HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS);
-+	put_fs_long(todo,arg);
-+	return 0;
-+}
-+
-+
-+#undef HANDLE_FLAG
-+
-+
-+static int get_diag(struct atm_dev *dev,unsigned long arg)
-+{
-+	int set;
-+
-+	set = 0;
-+	if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP;
-+	if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP;
-+	if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP;
-+	/* SONET_INS_FRAME is one-shot only */
-+	if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS;
-+	if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS;
-+	if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS;
-+	if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS;
-+	put_fs_long(set,arg);
-+	return 0;
-+}
-+
-+
-+static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
-+{
-+	int error;
-+
-+	switch (cmd) {
-+		case SONET_GETSTATZ:
-+		case SONET_GETSTAT:
-+			return fetch_stats(dev,(struct sonet_stats *) arg,
-+			    cmd == SONET_GETSTATZ);
-+		case SONET_SETDIAG:
-+			return change_diag(dev,arg,1);
-+		case SONET_CLRDIAG:
-+			return change_diag(dev,arg,0);
-+		case SONET_GETDIAG:
-+			return get_diag(dev,arg);
-+		case SONET_SETFRAMING:
-+			if (!suser()) return -EPERM;
-+			if (arg != SONET_FRAME_SONET) return -EINVAL;
-+			return 0;
-+		case SONET_GETFRAMING:
-+			put_fs_long(SONET_FRAME_SONET,arg);
-+			return 0;
-+		case SONET_GETFRSENSE:
-+			return -EINVAL;
-+		case SUNI_SETLOOP:
-+			if (!suser()) return -EPERM;
-+			if (arg > SUNI_LM_LOOP) return -EINVAL;
-+			PUT((GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE)) |
-+			    (arg == SUNI_LM_DIAG ? SUNI_MCT_DLE : 0) |
-+			    (arg == SUNI_LM_LOOP ? SUNI_MCT_LLE : 0),MCT);
-+			PRIV(dev)->loop_mode = arg;
-+			return 0;
-+		case SUNI_GETLOOP:
-+			error = verify_area(VERIFY_WRITE,(void *) arg,
-+			    sizeof(int));
-+			if (error) return error;
-+			put_fs_long(PRIV(dev)->loop_mode,arg);
-+			return 0;
-+		default:
-+			return -EINVAL;
-+	}
-+}
-+
-+
-+static void suni_int(struct atm_dev *dev)
-+{
-+	printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number,
-+	    GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ?  "lost" : "detected again");
-+}
-+
-+
-+static int suni_start(struct atm_dev *dev)
-+{
-+	unsigned long flags;
-+
-+	if (!(PRIV(dev) = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
-+		return -ENOMEM;
-+	PRIV(dev)->dev = dev;
-+	save_flags(flags);
-+	cli();
-+	PRIV(dev)->next = sunis;
-+	sunis = PRIV(dev);
-+	restore_flags(flags);
-+	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
-+	PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
-+		/* interrupt on loss of signal */
-+	(void) GET(RSOP_SIS); /* clear SUNI interrupts */
-+	PRIV(dev)->loop_mode = SUNI_LM_NONE;
-+	suni_hz(0); /* clear SUNI counters */
-+	(void) fetch_stats(dev,NULL,1); /* clear kernel counters */
-+	cli();
-+	if (!start_timer) restore_flags(flags);
-+	else {
-+		start_timer = 0;
-+		restore_flags(flags);
-+		/*init_timer(&poll_timer);*/
-+		poll_timer.expires = jiffies+HZ;
-+		poll_timer.function = suni_hz;
-+#if 0
-+printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.prev,
-+    (unsigned long) poll_timer.next);
-+#endif
-+		add_timer(&poll_timer);
-+	}
-+	return 0;
-+}
-+
-+
-+static const struct atmphy_ops suni_ops = {
-+	suni_start,
-+	suni_ioctl,
-+	suni_int
-+};
-+
-+
-+int suni_init(struct atm_dev *dev)
-+{
-+	unsigned char mri;
-+
-+	mri = GET(MRI); /* reset SUNI */
-+	PUT(mri | SUNI_MRI_RESET,MRI);
-+	PUT(mri,MRI);
-+	PUT(0,MT); /* disable all tests */
-+	REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET,
-+	    TPOP_APM); /* use SONET */
-+	REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
-+	    TACP_IUCHP); /* idle cells */
-+	PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
-+	dev->phy = &suni_ops;
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/suni.h	Fri Jul 19 15:08:56 1996
-@@ -0,0 +1,219 @@
-+/* drivers/atm/suni.h - PMC SUNI (PHY) declarations */
-+ 
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef DRIVER_ATM_SUNI_H
-+#define DRIVER_ATM_SUNI_H
-+
-+#include <linux/atmdev.h>
-+#include <linux/atmioc.h>
-+
-+
-+/* SUNI registers */
-+
-+#define SUNI_MRI		0x00	/* Master Reset and Identity / Load
-+					   Meter */
-+#define SUNI_MC			0x01	/* Master Configuration */
-+#define SUNI_MIS		0x02	/* Master Interrupt Status */
-+			  /* no 0x03 */
-+#define SUNI_MCM		0x04	/* Master Clock Monitor */
-+#define SUNI_MCT		0x05	/* Master Control */
-+#define SUNI_CSCS		0x06	/* Clock Synthesis Control and Status */
-+#define SUNI_CRCS		0x07	/* Clock Recovery Control and Status */
-+			     /* 0x08-0x0F reserved */
-+#define SUNI_RSOP_CIE		0x10	/* RSOP Control/Interrupt Enable */
-+#define SUNI_RSOP_SIS		0x11	/* RSOP Status/Interrupt Status */
-+#define SUNI_RSOP_SBL		0x12	/* RSOP Section BIP-8 LSB */
-+#define SUNI_RSOP_SBM		0x13	/* RSOP Section BIP-8 MSB */
-+#define SUNI_TSOP_CTRL		0x14	/* TSOP Control */
-+#define SUNI_TSOP_DIAG		0x15	/* TSOP Diagnostic */
-+			     /* 0x16-0x17 reserved */
-+#define SUNI_RLOP_CS		0x18	/* RLOP Control/Status */
-+#define SUNI_RLOP_IES		0x19	/* RLOP Interrupt Enable/Status */
-+#define SUNI_RLOP_LBL		0x1A	/* RLOP Line BIP-8/24 LSB */
-+#define SUNI_RLOP_LB		0x1B	/* RLOP Line BIP-8/24 */
-+#define SUNI_RLOP_LBM		0x1C	/* RLOP Line BIP-8/24 MSB */
-+#define SUNI_RLOP_LFL		0x1D	/* RLOP Line FEBE LSB */
-+#define SUNI_RLOP_LF 		0x1E	/* RLOP Line FEBE */
-+#define SUNI_RLOP_LFM		0x1F	/* RLOP Line FEBE MSB */
-+#define SUNI_TLOP_CTRL		0x20	/* TLOP Control */
-+#define SUNI_TLOP_DIAG		0x21	/* TLOP Diagnostic */
-+			     /* 0x22-0x2F reserved */
-+#define SUNI_RPOP_SC		0x30	/* RPOP Status/Control */
-+#define SUNI_RPOP_IS		0x31	/* RPOP Interrupt Status */
-+			     /* 0x32 reserved */
-+#define SUNI_RPOP_IE		0x33	/* RPOP Interrupt Enable */
-+			     /* 0x34-0x36 reserved */
-+#define SUNI_RPOP_PSL		0x37	/* RPOP Path Signal Label */
-+#define SUNI_RPOP_PBL		0x38	/* RPOP Path BIP-8 LSB */
-+#define SUNI_RPOP_PBM		0x39	/* RPOP Path BIP-8 MSB */
-+#define SUNI_RPOP_PFL		0x3A	/* RPOP Path FEBE LSB */
-+#define SUNI_RPOP_PFM		0x3B	/* RPOP Path FEBE MSB */
-+			     /* 0x3C reserved */
-+#define SUNI_RPOP_PBC		0x3D	/* RPOP Path BIP-8 Configuration */
-+			     /* 0x3E-0x3F reserved */
-+#define SUNI_TPOP_CD		0x40	/* TPOP Control/Diagnostic */
-+#define SUNI_TPOP_PC		0x41	/* TPOP Pointer Control */
-+			     /* 0x42-0x44 reserved */
-+#define SUNI_TPOP_APL		0x45	/* TPOP Arbitrary Pointer LSB */
-+#define SUNI_TPOP_APM		0x46	/* TPOP Arbitrary Pointer MSB */
-+			     /* 0x47 reserved */
-+#define SUNI_TPOP_PSL		0x48	/* TPOP Path Signal Label */
-+#define SUNI_TPOP_PS		0x49	/* TPOP Path Status */
-+			     /* 0x4A-0x4F reserved */
-+#define SUNI_RACP_CS		0x50	/* RACP Control/Status */
-+#define SUNI_RACP_IES		0x51	/* RACP Interrupt Enable/Status */
-+#define SUNI_RACP_MHP		0x52	/* RACP Match Header Pattern */
-+#define SUNI_RACP_MHM		0x53	/* RACP Match Header Mask */
-+#define SUNI_RACP_CHEC		0x54	/* RACP Correctable HCS Error Count */
-+#define SUNI_RACP_UHEC		0x55	/* RACP Uncorrectable HCS Err Count */
-+#define SUNI_RACP_RCCL		0x56	/* RACP Receive Cell Counter LSB */
-+#define SUNI_RACP_RCC		0x57	/* RACP Receive Cell Counter */
-+#define SUNI_RACP_RCCM		0x58	/* RACP Receive Cell Counter MSB */
-+#define SUNI_RACP_CFG		0x59	/* RACP Configuration */
-+			     /* 0x5A-0x5F reserved */
-+#define SUNI_TACP_CS		0x60	/* TACP Control/Status */
-+#define SUNI_TACP_IUCHP		0x61	/* TACP Idle/Unassigned Cell Hdr Pat */
-+#define SUNI_TACP_IUCPOP	0x62	/* TACP Idle/Unassigned Cell Payload
-+					   Octet Pattern */
-+#define SUNI_TACP_FIFO		0x63	/* TACP FIFO Configuration */
-+#define SUNI_TACP_TCCL		0x64	/* TACP Transmit Cell Counter LSB */
-+#define SUNI_TACP_TCC		0x65	/* TACP Transmit Cell Counter */
-+#define SUNI_TACP_TCCM		0x66	/* TACP Transmit Cell Counter MSB */
-+#define SUNI_TACP_CFG		0x67	/* TACP Configuration */
-+			     /* 0x68-0x7F reserved */
-+#define	SUNI_MT			0x80	/* Master Test */
-+			     /* 0x81-0xFF reserved */
-+
-+/* SUNI register values */
-+
-+
-+/* MRI is reg 0 */
-+#define SUNI_MRI_ID		0x0f	/* R, SUNI revision number */
-+#define SUNI_MRI_ID_SHIFT 	0
-+#define SUNI_MRI_TYPE		0x70	/* R, SUNI type (lite is 011) */
-+#define SUNI_MRI_TYPE_SHIFT 	4
-+#define SUNI_MRI_RESET		0x80	/* RW, reset & power down chip
-+					   0: normal operation
-+					   1: reset & low power */
-+/* MCT is reg 5 */
-+#define SUNI_MCT_LOOPT		0x01	/* RW, timing source, 0: from
-+					   TRCLK+/- */
-+#define SUNI_MCT_DLE		0x02	/* RW, diagnostic loopback */
-+#define SUNI_MCT_LLE		0x04	/* RW, line loopback */
-+#define SUNI_MCT_FIXPTR		0x20	/* RW, disable transmit payload pointer
-+					   adjustments
-+					   0: payload ptr controlled by TPOP
-+					      ptr control reg
-+					   1: payload pointer fixed at 522 */
-+#define SUNI_MCT_LCDV		0x40	/* R, loss of cell delineation */
-+#define SUNI_MCT_LCDE		0x80	/* RW, loss of cell delineation
-+					   interrupt (1: on) */
-+/* RSOP_CIE is reg 0x10 */
-+#define SUNI_RSOP_CIE_OOFE	0x01	/* RW, enable interrupt on frame alarm
-+					   state change */
-+#define SUNI_RSOP_CIE_LOFE	0x02	/* RW, enable interrupt on loss of
-+					   frame state change */
-+#define SUNI_RSOP_CIE_LOSE	0x04	/* RW, enable interrupt on loss of
-+					   signal state change */
-+#define SUNI_RSOP_CIE_BIPEE	0x08	/* RW, enable interrupt on section
-+					   BIP-8 error (B1) */
-+#define SUNI_RSOP_CIE_FOOF	0x20	/* W, force RSOP out of frame at next
-+					   boundary */
-+#define SUNI_RSOP_CIE_DDS	0x40	/* RW, disable scrambling */
-+
-+/* RSOP_SIS is reg 0x11 */
-+#define SUNI_RSOP_SIS_OOFV	0x01	/* R, out of frame */
-+#define SUNI_RSOP_SIS_LOFV	0x02	/* R, loss of frame */
-+#define SUNI_RSOP_SIS_LOSV	0x04	/* R, loss of signal */
-+#define SUNI_RSOP_SIS_OOFI	0x08	/* R, out of frame interrupt */
-+#define SUNI_RSOP_SIS_LOFI	0x10	/* R, loss of frame interrupt */
-+#define SUNI_RSOP_SIS_LOSI	0x20	/* R, loss of signal interrupt */
-+#define SUNI_RSOP_SIS_BIPEI	0x40	/* R, section BIP-8 interrupt */
-+
-+/* TSOP_CTRL is reg 0x14 */
-+#define SUNI_TSOP_CTRL_LAIS	0x01	/* insert alarm indication signal */
-+#define SUNI_TSOP_CTRL_DS	0x40	/* disable scrambling */
-+
-+/* TSOP_DIAG is reg 0x15 */
-+#define SUNI_TSOP_DIAG_DFP	0x01	/* insert single bit error cont. */
-+#define SUNI_TSOP_DIAG_DBIP8	0x02	/* insert section BIP err (cont) */
-+#define SUNI_TSOP_DIAG_DLOS	0x04	/* set line to zero (loss of signal) */
-+
-+/* TLOP_DIAG is reg 0x21 */
-+#define SUNI_TLOP_DIAG_DBIP	0x01	/* insert line BIP err (continuously) */
-+
-+/* TPOP_DIAG is reg 0x40 */
-+#define SUNI_TPOP_DIAG_PAIS	0x01	/* insert STS path alarm ind (cont) */
-+#define SUNI_TPOP_DIAG_DB3	0x02	/* insert path BIP err (continuously) */
-+
-+/* TPOP_APM is reg 0x46 */
-+#define SUNI_TPOP_APM_APTR	0x03	/* RW, arbitrary pointer, upper 2
-+					   bits */
-+#define SUNI_TPOP_APM_APTR_SHIFT 0
-+#define SUNI_TPOP_APM_S		0x0c	/* RW, "unused" bits of payload
-+					   pointer */
-+#define SUNI_TPOP_APM_S_SHIFT	2
-+#define SUNI_TPOP_APM_NDF	0xf0	 /* RW, NDF bits */
-+#define SUNI_TPOP_APM_NDF_SHIFT	4
-+
-+#define SUNI_TPOP_S_SONET	0	/* set S bits to 00 */
-+#define SUNI_TPOP_S_SDH		2	/* set S bits to 10 */
-+
-+/* RACP_IES is reg 0x51 */
-+#define SUNI_RACP_IES_FOVRI	0x02	/* R, FIFO overrun */
-+#define SUNI_RACP_IES_UHCSI	0x04	/* R, uncorrectable HCS error */
-+#define SUNI_RACP_IES_CHCSI	0x08	/* R, correctable HCS error */
-+#define SUNI_RACP_IES_OOCDI	0x10	/* R, change of cell delineation
-+					   state */
-+#define SUNI_RACP_IES_FIFOE	0x20	/* RW, enable FIFO overrun interrupt */
-+#define SUNI_RACP_IES_HCSE	0x40	/* RW, enable HCS error interrupt */
-+#define SUNI_RACP_IES_OOCDE	0x80	/* RW, enable cell delineation state
-+					   change interrupt */
-+
-+/* TACP_CS is reg 0x60 */
-+#define SUNI_TACP_CS_FIFORST	0x01	/* RW, reset transmit FIFO (sticky) */
-+#define SUNI_TACP_CS_DSCR	0x02	/* RW, disable payload scrambling */
-+#define SUNI_TACP_CS_HCAADD	0x04	/* RW, add coset polynomial to HCS */
-+#define SUNI_TACP_CS_DHCS	0x10	/* RW, insert HCS errors */
-+#define SUNI_TACP_CS_FOVRI	0x20	/* R, FIFO overrun */
-+#define SUNI_TACP_CS_TSOCI	0x40	/* R, TSOC input high */
-+#define SUNI_TACP_CS_FIFOE	0x80	/* RW, enable FIFO overrun interrupt */
-+
-+/* TACP_IUCHP is reg 0x61 */
-+#define SUNI_TACP_IUCHP_CLP	0x01	/* RW, 8th bit of 4th octet of i/u
-+					   pattern */
-+#define SUNI_TACP_IUCHP_PTI	0x0e	/* RW, 5th-7th bits of 4th octet of i/u
-+					   pattern */
-+#define SUNI_TACP_IUCHP_PTI_SHIFT 1
-+#define SUNI_TACP_IUCHP_GFC	0xf0	/* RW, 1st-4th bits of 1st octet of i/u
-+					   pattern */
-+#define SUNI_TACP_IUCHP_GFC_SHIFT 4
-+
-+/* MT is reg 0x80 */
-+#define SUNI_MT_HIZIO		0x01	/* RW, all but data bus & MP interface
-+					   tri-state */
-+#define SUNI_MT_HIZDATA		0x02	/* W, also tri-state data bus */
-+#define SUNI_MT_IOTST		0x04	/* RW, enable test mode */
-+#define SUNI_MT_DBCTRL		0x08	/* W, control data bus by CSB pin */
-+#define SUNI_MT_PMCTST		0x10	/* W, PMC test mode */
-+
-+
-+#define SUNI_IDLE_PATTERN       0x6a    /* idle pattern */
-+
-+/* ioctls */
-+
-+#define SUNI_GETLOOP	_IOR('a',ATMIOC_PHYPRV,int)	/* get loopback mode */
-+#define SUNI_SETLOOP	_IO('a',ATMIOC_PHYPRV+1)	/* set loopback mode */
-+
-+#define SUNI_LM_NONE	0	/* no loopback */
-+#define SUNI_LM_DIAG	1	/* diagnostic (i.e. loop TX to RX) */
-+#define SUNI_LM_LOOP	2	/* line (i.e. loop RX to TX) */
-+
-+
-+#ifdef __KERNEL__
-+int suni_init(struct atm_dev *dev);
-+#endif
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/tonga.h	Mon Jun 10 17:36:07 1996
-@@ -0,0 +1,20 @@
-+/* drivers/atm/tonga.h - Efficient Networks Tonga (PCI bridge) declarations */
-+ 
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef DRIVER_ATM_TONGA_H
-+#define DRIVER_ATM_TONGA_H
-+
-+#define PCI_TONGA_CTRL	0x60	/* control register */
-+
-+#define END_SWAP_DMA	0x80	/* endian swap on DMA */
-+#define END_SWAP_BYTE	0x40	/* endian swap on slave byte accesses */
-+#define END_SWAP_WORD	0x20	/* endian swap on slave word accesses */
-+#define SEPROM_MAGIC	0x0c	/* obscure required pattern (ASIC only) */
-+#define SEPROM_DATA	0x02	/* serial EEPROM data (ASIC only) */
-+#define SEPROM_CLK	0x01	/* serial EEPROM clock (ASIC only) */
-+
-+#define SEPROM_ESI_BASE	64	/* start of ESI in serial EEPROM */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/uPD98401.h	Mon Jun 10 17:36:08 1996
-@@ -0,0 +1,292 @@
-+/* drivers/atm/uPD98401.h - NEC uPD98401 (SAR) declarations */
-+ 
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef DRIVERS_ATM_uPD98401_H
-+#define DRIVERS_ATM_uPD98401_H
-+
-+
-+#define MAX_CRAM_SIZE	(1 << 18)	/* 2^18 words */
-+#define RAM_INCREMENT	1024		/* check in 4 kB increments */
-+
-+#define uPD98401_PORTS	0x24		/* probably more ? */
-+
-+
-+/*
-+ * Commands
-+ */
-+
-+#define uPD98401_OPEN_CHAN	0x20000000 /* open channel */
-+#define uPD98401_CHAN_ADDR	0x0003fff8 /*	channel address */
-+#define uPD98401_CHAN_ADDR_SHIFT 3
-+#define uPD98401_CLOSE_CHAN	0x24000000 /* close channel */
-+#define uPD98401_CHAN_RT	0x02000000 /*	RX/TX (0 TX, 1 RX) */
-+#define uPD98401_DEACT_CHAN	0x28000000 /* deactivate channel */
-+#define uPD98401_TX_READY	0x30000000 /* TX ready */
-+#define uPD98401_ADD_BAT	0x34000000 /* add batches */
-+#define uPD98401_POOL		0x000f0000 /* pool number */
-+#define uPD98401_POOL_SHIFT	16
-+#define uPD98401_POOL_NUMBAT	0x0000ffff /* number of batches */
-+#define uPD98401_NOP		0x3f000000 /* NOP */
-+#define uPD98401_IND_ACC	0x00000000 /* Indirect Access */
-+#define uPD98401_IA_RW		0x10000000 /*	Read/Write (0 W, 1 R) */
-+#define uPD98401_IA_B3		0x08000000 /*	Byte select, 1 enable */
-+#define uPD98401_IA_B2		0x04000000
-+#define uPD98401_IA_B1		0x02000000
-+#define uPD98401_IA_B0		0x01000000
-+#define uPD98401_IA_BALL	0x0f000000 /*   whole longword */
-+#define uPD98401_IA_TGT		0x000c0000 /*	Target */
-+#define uPD98401_IA_TGT_SHIFT	18
-+#define uPD98401_IA_TGT_CM	0	   /*	- Control Memory */
-+#define uPD98401_IA_TGT_SAR	1	   /*	- uPD98401 registers */
-+#define uPD98401_IA_TGT_PHY	3	   /*   - PHY device */
-+#define uPD98401_IA_ADDR	0x0003ffff
-+
-+/*
-+ * Command Register Status
-+ */
-+
-+#define uPD98401_BUSY		0x80000000 /* SAR is busy */
-+#define uPD98401_LOCKED		0x40000000 /* SAR is locked by other CPU */
-+
-+/*
-+ * Indications
-+ */
-+
-+/* Normal (AAL5) Receive Indication */
-+#define uPD98401_AAL5_UINFO	0xffff0000 /* user-supplied information */
-+#define uPD98401_AAL5_UINFO_SHIFT 16
-+#define uPD98401_AAL5_SIZE	0x0000ffff /* PDU size (in _CELLS_ !!) */
-+#define uPD98401_AAL5_CHAN	0x7fff0000 /* Channel number */
-+#define uPD98401_AAL5_CHAN_SHIFT	16
-+#define uPD98401_AAL5_ERR	0x00008000 /* Error indication */
-+#define uPD98401_AAL5_CI	0x00004000 /* Congestion Indication */
-+#define uPD98401_AAL5_CLP	0x00002000 /* CLP (>= 1 cell had CLP=1) */
-+#define uPD98401_AAL5_ES	0x00000f00 /* Error Status */
-+#define uPD98401_AAL5_ES_SHIFT	8
-+#define uPD98401_AAL5_ES_NONE	0	   /*	No error */
-+#define uPD98401_AAL5_ES_FREE	1	   /*	Receiver free buf underflow */
-+#define uPD98401_AAL5_ES_FIFO	2	   /*	Receiver FIFO overrun */
-+#define uPD98401_AAL5_ES_TOOBIG	3	   /*	Maximum length violation */
-+#define uPD98401_AAL5_ES_CRC	4	   /*	CRC error */
-+#define uPD98401_AAL5_ES_ABORT	5	   /*	User abort */
-+#define uPD98401_AAL5_ES_LENGTH	6	   /*   Length violation */
-+#define uPD98401_AAL5_ES_T1	7	   /*	T1 error (timeout) */
-+#define uPD98401_AAL5_ES_DEACT	8	   /*	Deactivated with DEACT_CHAN */
-+#define uPD98401_AAL5_POOL	0x0000001f /* Free buffer pool number */
-+
-+/* Raw Cell Indication */
-+#define uPD98401_RAW_UINFO	uPD98401_AAL5_UINFO
-+#define uPD98401_RAW_UINFO_SHIFT uPD98401_AAL5_UINFO_SHIFT
-+#define uPD98401_RAW_HEC	0x000000ff /* HEC */
-+#define uPD98401_RAW_CHAN	uPD98401_AAL5_CHAN
-+#define uPD98401_RAW_CHAN_SHIFT uPD98401_AAL5_CHAN_SHIFT
-+
-+/* Transmit Indication */
-+#define uPD98401_TXI_CONN	0x7fff0000 /* Connection Number */
-+#define uPD98401_TXI_CONN_SHIFT	16
-+#define uPD98401_TXI_ACTIVE	0x00008000 /* Channel remains active */
-+#define uPD98401_TXI_PQP	0x00007fff /* Packet Queue Pointer */
-+
-+/*
-+ * Directly Addressable Registers
-+ */
-+
-+#define uPD98401_GMR	0x00	/* General Mode Register */
-+#define uPD98401_GSR	0x01	/* General Status Register */
-+#define uPD98401_IMR	0x02	/* Interrupt Mask Register */
-+#define uPD98401_RQU	0x03	/* Receive Queue Underrun */
-+#define uPD98401_RQA	0x04	/* Receive Queue Alert */
-+#define uPD98401_ADDR	0x05	/* Last Burst Address */
-+#define uPD98401_VER	0x06	/* Version Number */
-+#define uPD98401_SWR	0x07	/* Software Reset */
-+#define uPD98401_CMR	0x08	/* Command Register */
-+#define uPD98401_CMR_L	0x09	/* Command Register and Lock/Unlock */
-+#define uPD98401_CER	0x0a	/* Command Extension Register */
-+#define uPD98401_CER_L	0x0b	/* Command Ext Reg and Lock/Unlock */
-+
-+#define uPD98401_MSH(n) (0x10+(n))	/* Mailbox n Start Address High */
-+#define uPD98401_MSL(n) (0x14+(n))	/* Mailbox n Start Address High */
-+#define uPD98401_MBA(n) (0x18+(n))	/* Mailbox n Bottom Address */
-+#define uPD98401_MTA(n) (0x1c+(n))	/* Mailbox n Tail Address */
-+#define uPD98401_MWA(n) (0x20+(n))	/* Mailbox n Write Address */
-+
-+/* GMR is at 0x00 */
-+#define uPD98401_GMR_ONE	0x80000000 /* Must be set to one */
-+#define uPD98401_GMR_SLM	0x40000000 /* Address mode (0 word, 1 byte) */
-+#define uPD98401_GMR_CPE	0x00008000 /* Control Memory Parity Enable */
-+#define uPD98401_GMR_LP		0x00004000 /* Loopback */
-+#define uPD98401_GMR_WA		0x00002000 /* Early Bus Write Abort/RDY */
-+#define uPD98401_GMR_RA		0x00001000 /* Early Read Abort/RDY */
-+#define uPD98401_GMR_SZ		0x00000f00 /* Burst Size Enable */
-+#define uPD98401_BURST16	0x00000800 /*	16-word burst */
-+#define uPD98401_BURST8		0x00000400 /*	 8-word burst */
-+#define uPD98401_BURST4		0x00000200 /*	 4-word burst */
-+#define uPD98401_BURST2		0x00000100 /*	 2-word burst */
-+#define uPD98401_GMR_AD		0x00000080 /* Address (burst resolution) Disable */
-+#define uPD98401_GMR_BO		0x00000040 /* Byte Order (0 little, 1 big) */
-+#define uPD98401_GMR_PM		0x00000020 /* Bus Parity Mode (0 byte, 1 word)*/
-+#define uPD98401_GMR_PC		0x00000010 /* Bus Parity Control (0even,1odd) */
-+#define uPD98401_GMR_BPE	0x00000008 /* Bus Parity Enable */
-+#define uPD98401_GMR_DR		0x00000004 /* Receive Drop Mode (0drop,1don't)*/
-+#define uPD98401_GMR_SE		0x00000002 /* Shapers Enable */
-+#define uPD98401_GMR_RE		0x00000001 /* Receiver Enable */
-+
-+/* GSR is at 0x01, IMR is at 0x02 */
-+#define uPD98401_INT_PI		0x80000000 /* PHY interrupt */
-+#define uPD98401_INT_RQA	0x40000000 /* Receive Queue Alert */
-+#define uPD98401_INT_RQU	0x20000000 /* Receive Queue Underrun */
-+#define uPD98401_INT_RD		0x10000000 /* Receiver Deactivated */
-+#define uPD98401_INT_SPE	0x08000000 /* System Parity Error */
-+#define uPD98401_INT_CPE	0x04000000 /* Control Memory Parity Error */
-+#define uPD98401_INT_SBE	0x02000000 /* System Bus Error */
-+#define uPD98401_INT_IND	0x01000000 /* Initialization Done */
-+#define uPD98401_INT_RCR	0x0000ff00 /* Raw Cell Received */
-+#define uPD98401_INT_RCR_SHIFT	8
-+#define uPD98401_INT_MF		0x000000f0 /* Mailbox Full */
-+#define uPD98401_INT_MF_SHIFT	4
-+#define uPD98401_INT_MM		0x0000000f /* Mailbox Modified */
-+
-+/* VER is at 0x06 */
-+#define uPD98401_MAJOR		0x0000ff00 /* Major revision */
-+#define uPD98401_MAJOR_SHIFT	8
-+#define uPD98401_MINOR		0x000000ff /* Minor revision */
-+
-+/*
-+ * Indirectly Addressable Registers
-+ */
-+
-+#define uPD98401_IM(n)	(0x40000+(n))	/* Scheduler n I and M */
-+#define uPD98401_X(n)	(0x40010+(n))	/* Scheduler n X */
-+#define uPD98401_Y(n)	(0x40020+(n))	/* Scheduler n Y */
-+#define uPD98401_PC(n)	(0x40030+(n))	/* Scheduler n P, C, p and c */
-+#define uPD98401_PS(n)	(0x40040+(n))	/* Scheduler n priority and status */
-+
-+/* IM contents */
-+#define uPD98401_IM_I		0xff000000 /* I */
-+#define uPD98401_IM_I_SHIFT	24
-+#define uPD98401_IM_M		0x00ffffff /* M */
-+
-+/* PC contents */
-+#define uPD98401_PC_P		0xff000000 /* P */
-+#define uPD98401_PC_P_SHIFT	24
-+#define uPD98401_PC_C		0x00ff0000 /* C */
-+#define uPD98401_PC_C_SHIFT	16
-+#define uPD98401_PC_p		0x0000ff00 /* p */
-+#define uPD98401_PC_p_SHIFT	8
-+#define uPD98401_PC_c		0x000000ff /* c */
-+
-+/* PS contents */
-+#define uPD98401_PS_PRIO	0xf0	/* Priority level (0 high, 15 low) */
-+#define uPD98401_PS_PRIO_SHIFT	4
-+#define uPD98401_PS_S		0x08	/* Scan - must be 0 (internal) */
-+#define uPD98401_PS_R		0x04	/* Round Robin (internal) */
-+#define uPD98401_PS_A		0x02	/* Active (internal) */
-+#define uPD98401_PS_E		0x01	/* Enabled */
-+
-+#define uPD98401_TOS	0x40100	/* Top of Stack Control Memory Address */
-+#define uPD98401_SMA	0x40200	/* Shapers Control Memory Start Address */
-+#define uPD98401_PMA	0x40201	/* Receive Pool Control Memory Start Address */
-+#define uPD98401_T1R	0x40300	/* T1 Register */
-+#define uPD98401_VRR	0x40301	/* VPI/VCI Reduction Register/Recv. Shutdown */
-+#define uPD98401_TSR	0x40302	/* Time-Stamp Register */
-+
-+/* VRR is at 0x40301 */
-+#define uPD98401_VRR_SDM	0x80000000 /* Shutdown Mode */
-+#define uPD98401_VRR_SHIFT	0x000f0000 /* VPI/VCI Shift */
-+#define uPD98401_VRR_SHIFT_SHIFT 16
-+#define uPD98401_VRR_MASK	0x0000ffff /* VPI/VCI mask */
-+
-+/*
-+ * TX packet descriptor
-+ */
-+
-+#define uPD98401_TXPD_SIZE	16	   /* descriptor size (in bytes) */
-+
-+#define uPD98401_TXPD_V		0x80000000 /* Valid bit */
-+#define uPD98401_TXPD_DP	0x40000000 /* Descriptor (1) or Pointer (0) */
-+#define uPD98401_TXPD_SM	0x20000000 /* Single (1) or Multiple (0) */
-+#define uPD98401_TXPD_CLPM	0x18000000 /* CLP mode */
-+#define uPD98401_CLPM_0		0	   /*	00 CLP = 0 */
-+#define uPD98401_CLPM_1		3	   /*	11 CLP = 1 */
-+#define uPD98401_CLPM_LAST	1	   /*	01 CLP unless last cell */
-+#define uPD98401_TXPD_CLPM_SHIFT 27
-+#define uPD98401_TXPD_PTI	0x07000000 /* PTI pattern */
-+#define uPD98401_TXPD_PTI_SHIFT	24
-+#define uPD98401_TXPD_GFC	0x00f00000 /* GFC pattern */
-+#define uPD98401_TXPD_GFC_SHIFT	20
-+#define uPD98401_TXPD_C10	0x00040000 /* insert CRC-10 */
-+#define uPD98401_TXPD_AAL5	0x00020000 /* AAL5 processing */
-+#define uPD98401_TXPD_MB	0x00010000 /* TX mailbox number */
-+#define uPD98401_TXPD_UU	0x0000ff00 /* CPCS-UU */
-+#define uPD98401_TXPD_UU_SHIFT	8
-+#define uPD98401_TXPD_CPI	0x000000ff /* CPI */
-+
-+/*
-+ * TX buffer descriptor
-+ */
-+
-+#define uPD98401_TXBD_SIZE	8	   /* descriptor size (in bytes) */
-+
-+#define uPD98401_TXBD_LAST	0x80000000 /* last buffer in packet */
-+
-+/*
-+ * TX VC table
-+ */
-+
-+/* 1st word has the same structure as in a TX packet descriptor */
-+#define uPD98401_TXVC_L		0x80000000 /* last buffer */
-+#define uPD98401_TXVC_SHP	0x0f000000 /* shaper number */
-+#define uPD98401_TXVC_SHP_SHIFT	24
-+#define uPD98401_TXVC_VPI	0x00ff0000 /* VPI */
-+#define uPD98401_TXVC_VPI_SHIFT	16
-+#define uPD98401_TXVC_VCI	0x0000ffff /* VCI */
-+#define uPD98401_TXVC_QRP	6	   /* Queue Read Pointer is in word 6 */
-+
-+/*
-+ * RX free buffer pools descriptor
-+ */
-+
-+#define uPD98401_RXFP_ALERT	0x70000000 /* low water mark */
-+#define uPD98401_RXFP_ALERT_SHIFT 28
-+#define uPD98401_RXFP_BFSZ	0x0f000000 /* buffer size, 64*2^n */
-+#define uPD98401_RXFP_BFSZ_SHIFT 24
-+#define uPD98401_RXFP_BTSZ	0x00ff0000 /* batch size, n+1 */
-+#define uPD98401_RXFP_BTSZ_SHIFT 16
-+#define uPD98401_RXFP_REMAIN	0x0000ffff /* remaining batches in pool */
-+
-+/*
-+ * RX VC table
-+ */
-+
-+#define uPD98401_RXVC_BTSZ	0xff000000 /* remaining free buffers in batch */
-+#define uPD98401_RXVC_BTSZ_SHIFT 24
-+#define uPD98401_RXVC_MB	0x00200000 /* RX mailbox number */
-+#define uPD98401_RXVC_POOL	0x001f0000 /* free buffer pool number */
-+#define uPD98401_RXVC_POOL_SHIFT 16
-+#define uPD98401_RXVC_UINFO	0x0000ffff /* user-supplied information */
-+#define uPD98401_RXVC_T1	0xffff0000 /* T1 timestamp */
-+#define uPD98401_RXVC_T1_SHIFT	16
-+#define uPD98401_RXVC_PR	0x00008000 /* Packet Reception, 1 if busy */
-+#define uPD98401_RXVC_DR	0x00004000 /* FIFO Drop */
-+#define uPD98401_RXVC_OD	0x00001000 /* Drop OAM cells */
-+#define uPD98401_RXVC_AR	0x00000800 /* AAL5 or raw cell; 1 if AAL5 */
-+#define uPD98401_RXVC_MAXSEG	0x000007ff /* max number of segments per PDU */
-+#define uPD98401_RXVC_REM	0xfffe0000 /* remaining words in curr buffer */
-+#define uPD98401_RXVC_REM_SHIFT	17
-+#define uPD98401_RXVC_CLP	0x00010000 /* CLP received */
-+#define uPD98401_RXVC_BFA	0x00008000 /* Buffer Assigned */
-+#define uPD98401_RXVC_BTA	0x00004000 /* Batch Assigned */
-+#define uPD98401_RXVC_CI	0x00002000 /* Congestion Indication */
-+#define uPD98401_RXVC_DD	0x00001000 /* Dropping incoming cells */
-+#define uPD98401_RXVC_DP	0x00000800 /* like PR ? */
-+#define uPD98401_RXVC_CURSEG	0x000007ff /* Current Segment count */
-+
-+/*
-+ * RX lookup table
-+ */
-+
-+#define uPD98401_RXLT_ENBL	0x8000	   /* Enable */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/uPD98402.h	Mon Jun 10 17:36:08 1996
-@@ -0,0 +1,106 @@
-+/* drivers/atm/uPD98402.h - NEC uPD98402 (PHY) declarations */
-+ 
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef DRIVERS_ATM_uPD98402_H
-+#define DRIVERS_ATM_uPD98402_H
-+
-+/*
-+ * Registers
-+ */
-+
-+#define uPD98402_CMR		0x00	/* Command Register */
-+#define uPD98402_MDR		0x01	/* Mode Register */
-+#define uPD98402_PICR		0x02	/* PHY Interrupt Cause Register */
-+#define uPD98402_PIMR		0x03	/* PHY Interrupt Mask Register */
-+#define uPD98402_ACR		0x04	/* Alarm Cause Register */
-+#define uPD98402_ACMR		0x05	/* Alarm Cause Mask Register */
-+#define uPD98402_PCR		0x06	/* Performance Cause Register */
-+#define uPD98402_PCMR		0x07	/* Performance Cause Mask Register */
-+#define uPD98402_IACM		0x08	/* Internal Alarm Cause Mask Register */
-+#define uPD98402_B1ECT		0x09	/* B1 Error Count Register */
-+#define uPD98402_B2ECT		0x0a	/* B2 Error Count Register */
-+#define uPD98402_B3ECT		0x0b	/* B3 Error Count Regster */
-+#define uPD98402_PFECB		0x0c	/* Path FEBE Count Register */
-+#define uPD98402_LECCT		0x0d	/* Line FEBE Count Register */
-+#define uPD98402_HECCT		0x0e	/* HEC Error Count Register */
-+#define uPD98402_FJCT		0x0f	/* Frequence Justification Count Reg */
-+#define uPD98402_PCOCR		0x10	/* Perf. Counter Overflow Cause Reg */
-+#define uPD98402_PCOMR		0x11	/* Perf. Counter Overflow Mask Reg */
-+#define uPD98402_C11T		0x20	/* C11T Data Register */
-+#define uPD98402_C12T		0x21	/* C12T Data Register */
-+#define uPD98402_C13T		0x22	/* C13T Data Register */
-+#define uPD98402_F1T		0x23	/* F1T Data Register */
-+#define uPD98402_K2T		0x25	/* K2T Data Register */
-+#define uPD98402_C2T		0x26	/* C2T Data Register */
-+#define uPD98402_F2T		0x27	/* F2T Data Register */
-+#define uPD98402_C11R		0x30	/* C11T Data Register */
-+#define uPD98402_C12R		0x31	/* C12T Data Register */
-+#define uPD98402_C13R		0x32	/* C13T Data Register */
-+#define uPD98402_F1R		0x33	/* F1T Data Register */
-+#define uPD98402_K2R		0x35	/* K2T Data Register */
-+#define uPD98402_C2R		0x36	/* C2T Data Register */
-+#define uPD98402_F2R		0x37	/* F2T Data Register */
-+
-+/* CMR is at 0x00 */
-+#define uPD98402_CMR_PFRF	0x01	/* Send path FERF */
-+#define uPD98402_CMR_LFRF	0x02	/* Send line FERF */
-+#define uPD98402_CMR_PAIS	0x04	/* Send path AIS */
-+#define uPD98402_CMR_LAIS	0x08	/* Send line AIS */
-+
-+/* MDR is at 0x01 */
-+#define uPD98402_MDR_ALP	0x01	/* ATM layer loopback */
-+#define uPD98402_MDR_TPLP	0x02	/* PMD loopback, to host */
-+#define uPD98402_MDR_RPLP	0x04	/* PMD loopback, to network */
-+#define uPD98402_MDR_SS0	0x08	/* SS0 */
-+#define uPD98402_MDR_SS1	0x10	/* SS1 */
-+#define uPD98402_MDR_SS_MASK	0x18	/* mask */
-+#define uPD98402_MDR_SS_SHIFT	3	/* shift */
-+#define uPD98402_MDR_HEC	0x20	/* disable HEC inbound processing */
-+#define uPD98402_MDR_FSR	0x40	/* disable frame scrambler */
-+#define uPD98402_MDR_CSR	0x80	/* disable cell scrambler */
-+
-+/* PICR is at 0x02, PIMR is at 0x03 */
-+#define uPD98402_INT_PFM	0x01	/* performance counter has changed */
-+#define uPD98402_INT_ALM	0x02	/* line fault */
-+#define uPD98402_INT_RFO	0x04	/* receive FIFO overflow */
-+#define uPD98402_INT_PCO	0x08	/* performance counter overflow */
-+#define uPD98402_INT_OTD	0x20	/* OTD has occurred */
-+#define uPD98402_INT_LOS	0x40	/* Loss Of Signal */
-+#define uPD98402_INT_LOF	0x80	/* Loss Of Frame */
-+
-+/* ACR is as 0x04, ACMR is at 0x05 */
-+#define uPD98402_ALM_PFRF	0x01	/* path FERF */
-+#define uPD98402_ALM_LFRF	0x02	/* line FERF */
-+#define uPD98402_ALM_PAIS	0x04	/* path AIS */
-+#define uPD98402_ALM_LAIS	0x08	/* line AIS */
-+#define uPD98402_ALM_LOD	0x10	/* loss of delineation */
-+#define uPD98402_ALM_LOP	0x20	/* loss of pointer */
-+#define uPD98402_ALM_OOF	0x40	/* out of frame */
-+
-+/* PCR is at 0x06, PCMR is at 0x07 */
-+#define uPD98402_PFM_PFEB	0x01	/* path FEBE */
-+#define uPD98402_PFM_LFEB	0x02	/* line FEBE */
-+#define uPD98402_PFM_B3E	0x04	/* B3 error */
-+#define uPD98402_PFM_B2E	0x08	/* B2 error */
-+#define uPD98402_PFM_B1E	0x10	/* B1 error */
-+#define uPD98402_PFM_FJ		0x20	/* frequency justification */
-+
-+/* IACM is at 0x08 */
-+#define uPD98402_IACM_PFRF	0x01	/* don't generate path FERF */
-+#define uPD98402_IACM_LFRF	0x02	/* don't generate line FERF */
-+
-+/* PCOCR is at 0x010, PCOMR is at 0x11 */
-+#define uPD98402_PCO_B1EC	0x01	/* B1ECT overflow */
-+#define uPD98402_PCO_B2EC	0x02	/* B2ECT overflow */
-+#define uPD98402_PCO_B3EC	0x04	/* B3ECT overflow */
-+#define uPD98402_PCO_PFBC	0x08	/* PFEBC overflow */
-+#define uPD98402_PCO_LFBC	0x10	/* LFEVC overflow */
-+#define uPD98402_PCO_HECC	0x20	/* HECCT overflow */
-+#define uPD98402_PCO_FJC	0x40	/* FJCT overflow */
-+
-+
-+int uPD98402_init(struct atm_dev *dev);
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/uPD98402.c	Mon Jun 10 17:36:09 1996
-@@ -0,0 +1,201 @@
-+/* drivers/atm/uPD98402.c - NEC uPD98402 (PHY) declarations */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#include <linux/sched.h> /* for jiffies */
-+#include <linux/mm.h>
-+#include <linux/errno.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+#include <asm/segment.h>
-+
-+#include "uPD98402.h"
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+struct uPD98402_priv {
-+	struct sonet_stats sonet_stats;	/* link diagnostics */
-+	unsigned char framing;		/* SONET/SDH framing */
-+};
-+
-+
-+#define PRIV(dev) ((struct uPD98402_priv *) dev->phy_data)
-+
-+#define PUT(val,reg) dev->ops->phy_put(dev,val,uPD98402_##reg)
-+#define GET(reg) dev->ops->phy_get(dev,uPD98402_##reg)
-+
-+
-+static int fetch_stats(struct atm_dev *dev,struct sonet_stats *arg,int zero)
-+{
-+	unsigned long flags;
-+ 
-+	save_flags(flags);
-+	cli();
-+	PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT);
-+	if (arg)
-+		memcpy_tofs(arg,&PRIV(dev)->sonet_stats,
-+		    sizeof(struct sonet_stats));
-+	if (zero) {
-+		memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
-+		PRIV(dev)->sonet_stats.corr_hcs = -1;
-+		PRIV(dev)->sonet_stats.tx_cells = -1;
-+		PRIV(dev)->sonet_stats.rx_cells = -1;
-+	}
-+	restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+static int set_framing(struct atm_dev *dev,unsigned char framing)
-+{
-+	static const unsigned char sonet[] = { 1,2,3,0 };
-+	static const unsigned char sdh[] = { 1,0,0,2 };
-+	const char *set;
-+	unsigned long flags;
-+ 
-+	switch (framing) {
-+		case SONET_FRAME_SONET:
-+			set = sonet;
-+			break;
-+		case SONET_FRAME_SDH:
-+			set = sdh;
-+			break;
-+		default:
-+			return -EINVAL;
-+	}
-+	save_flags(flags);
-+	cli();
-+	PUT(set[0],C11T);
-+	PUT(set[1],C12T);
-+	PUT(set[2],C13T);
-+	PUT((GET(MDR) & ~uPD98402_MDR_SS_MASK) | (set[3] <<
-+	    uPD98402_MDR_SS_SHIFT),MDR);
-+	restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+static int get_sense(struct atm_dev *dev,unsigned long arg)
-+{
-+	unsigned long flags;
-+ 
-+	save_flags(flags);
-+	cli();
-+	put_fs_byte(GET(C11R),arg);
-+	put_fs_byte(GET(C12R),arg+1);
-+	put_fs_byte(GET(C13R),arg+2);
-+	put_fs_byte(0xff,arg+3);
-+	put_fs_byte(0xff,arg+4);
-+	put_fs_byte(0xff,arg+5);
-+	restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+static int uPD98402_ioctl(struct atm_dev *dev,unsigned int cmd,
-+    unsigned long arg)
-+{
-+	switch (cmd) {
-+
-+		case SONET_GETSTATZ:
-+                case SONET_GETSTAT:
-+			return fetch_stats(dev,(struct sonet_stats *) arg,
-+			    cmd == SONET_GETSTATZ);
-+		case SONET_SETFRAMING:
-+			return set_framing(dev,arg);
-+		case SONET_GETFRAMING:
-+			put_fs_long(PRIV(dev)->framing,arg);
-+			return 0;
-+		case SONET_GETFRSENSE:
-+			return get_sense(dev,arg);
-+		default:
-+		    return -EINVAL;
-+	}
-+}
-+
-+
-+static void stat_event(struct atm_dev *dev)
-+{
-+	unsigned char events;
-+
-+	events = GET(PCR);
-+	if (events & uPD98402_PFM_PFEB)
-+		if ((PRIV(dev)->sonet_stats.path_febe += GET(PFECB)) < 0)
-+			PRIV(dev)->sonet_stats.path_febe = LONG_MAX;
-+	if (events & uPD98402_PFM_LFEB)
-+		if ((PRIV(dev)->sonet_stats.line_febe += GET(LECCT)) < 0)
-+			PRIV(dev)->sonet_stats.line_febe = LONG_MAX;
-+	if (events & uPD98402_PFM_B3E)
-+		if ((PRIV(dev)->sonet_stats.path_bip += GET(B3ECT)) < 0)
-+			PRIV(dev)->sonet_stats.path_bip = LONG_MAX;
-+	if (events & uPD98402_PFM_B2E)
-+		if ((PRIV(dev)->sonet_stats.line_bip += GET(B2ECT)) < 0)
-+			PRIV(dev)->sonet_stats.line_bip = LONG_MAX;
-+	if (events & uPD98402_PFM_B1E)
-+		if ((PRIV(dev)->sonet_stats.section_bip += GET(B1ECT)) < 0)
-+			PRIV(dev)->sonet_stats.section_bip = LONG_MAX;
-+}
-+
-+
-+static void uPD98402_int(struct atm_dev *dev)
-+{
-+	static unsigned long silence = 0;
-+	unsigned char reason;
-+
-+	while ((reason = GET(PICR))) {
-+		if (reason & uPD98402_INT_LOS)
-+			printk(KERN_NOTICE "%s(itf %d): signal lost\n",
-+			    dev->type,dev->number);
-+		if (reason & uPD98402_INT_PFM) stat_event(dev);
-+		if (reason & uPD98402_INT_PCO) {
-+			(void) GET(PCOCR); /* clear interrupt cause */
-+			PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT);
-+		}
-+		if ((reason & uPD98402_INT_RFO) && jiffies > silence) {
-+			printk(KERN_WARNING "%s(itf %d): uPD98402 receive "
-+			    "FIFO overflow\n",dev->type,dev->number);
-+			silence = jiffies+HZ/2;
-+		}
-+	}
-+}
-+
-+
-+static int uPD98402_start(struct atm_dev *dev)
-+{
-+DPRINTK("phy_start\n");
-+	if (!(PRIV(dev) = kmalloc(sizeof(struct uPD98402_priv),GFP_KERNEL)))
-+		return -ENOMEM;
-+	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
-+	(void) GET(PCR); /* clear performance events */
-+	PUT(uPD98402_PFM_FJ,PCMR); /* ignore frequency adj */
-+	(void) GET(PCOCR); /* clear overflows */
-+	PUT(~uPD98402_PCO_HECC,PCOMR);
-+	(void) GET(PICR); /* clear interrupts */
-+	PUT(~(uPD98402_INT_PFM | uPD98402_INT_ALM | uPD98402_INT_RFO |
-+	  uPD98402_INT_LOS),PIMR); /* enable them */
-+	(void) fetch_stats(dev,NULL,1); /* clear kernel counters */
-+	return 0;
-+}
-+
-+
-+
-+static const struct atmphy_ops uPD98402_ops = {
-+	uPD98402_start,
-+	uPD98402_ioctl,	/* no ioctl yet */
-+	uPD98402_int
-+};
-+
-+
-+int uPD98402_init(struct atm_dev *dev)
-+{
-+DPRINTK("phy_init\n");
-+	dev->phy = &uPD98402_ops;
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/zatm.c	Wed Jul 10 21:02:11 1996
-@@ -0,0 +1,1665 @@
-+/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/module.h>
-+#include <linux/sched.h>
-+#include <linux/kernel.h>
-+#include <linux/mm.h>
-+#include <linux/bios32.h>
-+#include <linux/pci.h>
-+#include <linux/errno.h>
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+#include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-+#include <linux/delay.h>
-+#include <linux/ioport.h> /* for request_region */
-+#include <linux/uio.h>
-+#include <asm/byteorder.h>
-+#include <asm/system.h>
-+#include <asm/string.h>
-+#include <asm/io.h>
-+
-+#include "uPD98401.h"
-+#include "uPD98402.h"
-+#include "zeprom.h"
-+#include "zatm.h"
-+
-+
-+/*
-+ * TODO:
-+ *
-+ * Minor features
-+ *  - support 64 kB SDUs (will have to use multibuffer batches then :-( )
-+ *  - proper use of CDV, credit = max(1,CDVT*PCR)
-+ *  - AAL0
-+ *  - better receive timestamps
-+ *  - OAM
-+ */
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+#ifndef CONFIG_ATM_ZATM_DEBUG
-+
-+
-+#define NULLCHECK(x)
-+
-+#define EVENT(s,a,b)
-+
-+
-+static void event_dump(void)
-+{
-+}
-+
-+
-+#else
-+
-+
-+/* 
-+ * NULL pointer checking
-+ */
-+
-+#define NULLCHECK(x) \
-+  if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x))
-+
-+/*
-+ * Very extensive activity logging. Greatly improves bug detection speed but
-+ * costs a few Mbps if enabled.
-+ */
-+
-+#define EV 64
-+
-+static const char *ev[EV];
-+static unsigned long ev_a[EV],ev_b[EV];
-+static int ec = 0;
-+
-+
-+static void EVENT(const char *s,unsigned long a,unsigned long b)
-+{
-+	ev[ec] = s; 
-+	ev_a[ec] = a;
-+	ev_b[ec] = b;
-+	ec = (ec+1) % EV;
-+}
-+
-+
-+static void event_dump(void)
-+{
-+	int n,i;
-+
-+	printk(KERN_NOTICE "----- event dump follows -----\n");
-+	for (n = 0; n < EV; n++) {
-+		i = (ec+n) % EV;
-+		printk(KERN_NOTICE);
-+		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
-+	}
-+	printk(KERN_NOTICE "----- event dump ends here -----\n");
-+}
-+
-+
-+#endif /* CONFIG_ATM_ZATM_DEBUG */
-+
-+
-+#define RING_BUSY	1	/* indication from do_tx that PDU has to be
-+				   backlogged */
-+
-+static struct atm_dev *zatm_boards = NULL;
-+static unsigned long dummy[2] = {0,0};
-+
-+
-+#define zin_n(r) inl_p(zatm_dev->base+r*4)
-+#define zin(r) inl_p(zatm_dev->base+uPD98401_##r*4)
-+#define zout(v,r) outl_p(v,zatm_dev->base+uPD98401_##r*4)
-+#define zwait while (zin(CMR) & uPD98401_BUSY)
-+
-+/* RX0, RX1, TX0, TX1 */
-+static const int mbx_entries[NR_MBX] = { 1024,1024,1024,1024 };
-+static const int mbx_esize[NR_MBX] = { 16,16,4,4 }; /* entry size in bytes */
-+
-+#define MBX_SIZE(i) (mbx_entries[i]*mbx_esize[i])
-+
-+
-+/*-------------------------------- utilities --------------------------------*/
-+
-+
-+static void zpokel(struct zatm_dev *zatm_dev,unsigned long value,
-+    unsigned long addr)
-+{
-+	zwait;
-+	zout(value,CER);
-+	zout(uPD98401_IND_ACC | uPD98401_IA_BALL |
-+	    (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
-+}
-+
-+
-+static unsigned long zpeekl(struct zatm_dev *zatm_dev,unsigned long addr)
-+{
-+	zwait;
-+	zout(uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |
-+	  (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
-+	zwait;
-+	return zin(CER);
-+}
-+
-+
-+/*------------------------------- free lists --------------------------------*/
-+
-+
-+#define HEAD_SIZE (3*sizeof(void *))
-+
-+
-+static void refill_pool(struct atm_dev *dev,int pool)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct sk_buff *skb,*first;
-+	unsigned long flags;
-+	int align,offset,free,count,size;
-+
-+	EVENT("refill_pool\n",0,0);
-+	zatm_dev = ZATM_DEV(dev);
-+	size = (64 << (pool <= ZATM_AAL5_POOL_BASE ? 0 :
-+	    pool-ZATM_AAL5_POOL_BASE))+HEAD_SIZE;
-+	if (size < PAGE_SIZE) {
-+		align = 32; /* for 32 byte alignment */
-+		offset = HEAD_SIZE;
-+	}
-+	else {
-+		align = 4096;
-+		offset = zatm_dev->pool_info[pool].offset+HEAD_SIZE;
-+	}
-+	size += align;
-+	save_flags(flags);
-+	cli();
-+	free = zpeekl(zatm_dev,zatm_dev->pool_base+2*pool) &
-+	    uPD98401_RXFP_REMAIN;
-+	restore_flags(flags);
-+	if (free >= zatm_dev->pool_info[pool].low_water) return;
-+	EVENT("starting ... POOL: 0x%08lx, 0x%08lx\n",
-+	    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
-+	    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
-+	EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
-+	count = 0;
-+	first = NULL;
-+	while (free < zatm_dev->pool_info[pool].high_water) {
-+		skb = alloc_skb(size,GFP_ATOMIC);
-+		if (!skb) {
-+			printk(KERN_WARNING DEV_LABEL "(Itf %d): got no new "
-+			    "skb (%d) with %d free\n",dev->number,size,free);
-+			break;
-+		}
-+		if (!first) first = skb;
-+		count++;
-+		/*
-+		 * 2nd skb head structure:
-+		 *   [0] pointer to buffer (for SAR)
-+		 *   [1] buffer descr link pointer (for SAR)
-+		 *   [2] back pointer to skb (for poll_rx)
-+		 *   [3] data
-+		 *   ...
-+		 */
-+		skb->free = 1;
-+		skb->data = (void *) ((((unsigned long) skb->data+align+
-+		    offset-1) & ~(align-1))-offset);
-+		((void **) skb->data)[0] = skb->data+HEAD_SIZE;
-+		((void **) skb->data)[1] = NULL;
-+		((void **) skb->data)[2] = skb;
-+		EVENT("enq skb 0x%08lx/0x%08lx\n",(unsigned long) skb,
-+		    (unsigned long) skb->data);
-+		cli();
-+		if (zatm_dev->last_free[pool])
-+			((void **) zatm_dev->last_free[pool]->data)[-2] =
-+			    skb->data;
-+		zatm_dev->last_free[pool] = skb;
-+		skb->data += HEAD_SIZE;
-+		skb_queue_tail(&zatm_dev->pool[pool],skb);
-+		restore_flags(flags);
-+		free++;
-+	}
-+	if (first) {
-+		cli();
-+		zwait;
-+		zout((unsigned long) first->data-HEAD_SIZE,CER);
-+		zout(uPD98401_ADD_BAT | (pool << uPD98401_POOL_SHIFT) | count,
-+		    CMR);
-+		restore_flags(flags);
-+		EVENT ("POOL: 0x%08lx, 0x%08lx\n",
-+		    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
-+		    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
-+		EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
-+	}
-+}
-+
-+
-+static void drain_free(struct atm_dev *dev,int pool)
-+{
-+	struct sk_buff *skb;
-+
-+	while ((skb = skb_dequeue(&ZATM_DEV(dev)->pool[pool])))
-+		kfree_skb(skb,FREE_READ);
-+}
-+
-+
-+static int pool_index(int max_pdu)
-+{
-+	int i;
-+
-+	max_pdu += ATM_CELL_PAYLOAD-1;
-+	if (max_pdu > 65536) return -1;
-+	for (i = 0; (64 << i) < max_pdu; i++);
-+	return i+ZATM_AAL5_POOL_BASE;
-+}
-+
-+
-+/* use_pool isn't reentrant */
-+
-+
-+static void use_pool(struct atm_dev *dev,int pool)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned long flags;
-+	int size;
-+
-+	zatm_dev = ZATM_DEV(dev);
-+	if (!(zatm_dev->pool_info[pool].ref_count++)) {
-+		skb_queue_head_init(&zatm_dev->pool[pool]);
-+		size = pool-ZATM_AAL5_POOL_BASE;
-+		if (size < 0) size = 0; /* 64B... */
-+		else if (size > 10) size = 10; /* ... 64kB */
-+		save_flags(flags);
-+		cli();
-+		zpokel(zatm_dev,((zatm_dev->pool_info[pool].low_water/4) <<
-+		    uPD98401_RXFP_ALERT_SHIFT) |
-+		    (1 << uPD98401_RXFP_BTSZ_SHIFT) |
-+		    (size << uPD98401_RXFP_BFSZ_SHIFT),
-+		    zatm_dev->pool_base+pool*2);
-+		zpokel(zatm_dev,(unsigned long) dummy,zatm_dev->pool_base+
-+		    pool*2+1);
-+		restore_flags(flags);
-+		zatm_dev->last_free[pool] = NULL;
-+		refill_pool(dev,pool);
-+	}
-+	DPRINTK("pool %d: %d\n",pool,zatm_dev->pool_info[pool].ref_count);
-+}
-+
-+
-+static void unuse_pool(struct atm_dev *dev,int pool)
-+{
-+	if (!(--ZATM_DEV(dev)->pool_info[pool].ref_count))
-+		drain_free(dev,pool);
-+}
-+
-+
-+static void zatm_feedback(struct atm_vcc *vcc,struct sk_buff *skb,
-+    unsigned long start,unsigned long dest,int len)
-+{
-+	struct zatm_pool_info *pool;
-+	unsigned long offset,flags;
-+
-+	DPRINTK("start 0x%08lx dest 0x%08lx len %d\n",start,dest,len);
-+	if (len < PAGE_SIZE) return;
-+	pool = &ZATM_DEV(vcc->dev)->pool_info[ZATM_VCC(vcc)->pool];
-+	offset = (dest-start) & (PAGE_SIZE-1);
-+	save_flags(flags);
-+	cli();
-+	if (!offset || pool->offset == offset) {
-+		pool->next_cnt = 0;
-+		return;
-+	}
-+	if (offset != pool->next_off) {
-+		pool->next_off = offset;
-+		pool->next_cnt = 0;
-+		return;
-+	}
-+	if (++pool->next_cnt >= pool->next_thres) {
-+		pool->offset = pool->next_off;
-+		pool->next_cnt = 0;
-+	}
-+	restore_flags(flags);
-+}
-+
-+
-+/*----------------------------------- RX ------------------------------------*/
-+
-+
-+#if 0
-+static void exception(struct atm_vcc *vcc)
-+{
-+   static int count = 0;
-+   struct zatm_dev *zatm_dev = ZATM_DEV(vcc->dev);
-+   struct zatm_vcc *zatm_vcc = ZATM_VCC(vcc);
-+   unsigned long *qrp;
-+   int i;
-+
-+   if (count++ > 2) return;
-+   for (i = 0; i < 8; i++)
-+	printk("TX%d: 0x%08lx\n",i,
-+	  zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+i));
-+   for (i = 0; i < 5; i++)
-+	printk("SH%d: 0x%08lx\n",i,
-+	  zpeekl(zatm_dev,uPD98401_IM(zatm_vcc->shaper)+16*i));
-+   qrp = (unsigned long *) zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
-+     uPD98401_TXVC_QRP);
-+   printk("qrp=0x%08lx\n",(unsigned long) qrp);
-+   for (i = 0; i < 4; i++) printk("QRP[%d]: 0x%08lx",i,qrp[i]);
-+}
-+#endif
-+
-+
-+static const char *err_txt[] = {
-+	"No error",
-+	"RX buf underflow",
-+	"RX FIFO overrun",
-+	"Maximum len violation",
-+	"CRC error",
-+	"User abort",
-+	"Length violation",
-+	"T1 error",
-+	"Deactivated",
-+	"???",
-+	"???",
-+	"???",
-+	"???",
-+	"???",
-+	"???",
-+	"???"
-+};
-+
-+
-+static void poll_rx(struct atm_dev *dev,int mbx)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned long pos,x;
-+	int error;
-+
-+	EVENT("poll_rx\n",0,0);
-+	zatm_dev = ZATM_DEV(dev);
-+	pos = (zatm_dev->mbx_start[mbx] & ~0xffff) | zin(MTA(mbx));
-+	while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
-+		unsigned long *here;
-+		struct sk_buff *skb,*expect;
-+		struct atm_vcc *vcc;
-+		int cells,size,chan;
-+
-+		EVENT("MBX: host 0x%lx, nic 0x%lx\n",pos,x);
-+		here = (unsigned long *) pos;
-+		if (((pos += 16) & 0xffff) == zatm_dev->mbx_end[mbx])
-+			pos = zatm_dev->mbx_start[mbx];
-+		cells = here[0] & uPD98401_AAL5_SIZE;
-+#if 0
-+{
-+unsigned long *x;
-+printk("RX IND: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",here[0],here[1],here[2],
-+  here[3]);
-+		printk("POOL: 0x%08lx, 0x%08lx\n",zpeekl(zatm_dev,
-+		      zatm_dev->pool_base),
-+		      zpeekl(zatm_dev,zatm_dev->pool_base+1));
-+		x = (unsigned long *) here[2];
-+		printk("[0..3] = 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n",
-+		    x[0],x[1],x[2],x[3]);
-+}
-+#endif
-+		error = 0;
-+		if (here[3] & uPD98401_AAL5_ERR) {
-+			error = (here[3] & uPD98401_AAL5_ES) >>
-+			    uPD98401_AAL5_ES_SHIFT;
-+			if (error == uPD98401_AAL5_ES_DEACT ||
-+			    error == uPD98401_AAL5_ES_FREE) continue;
-+		}
-+EVENT("error code 0x%x/0x%x\n",(here[3] & uPD98401_AAL5_ES) >>
-+  uPD98401_AAL5_ES_SHIFT,error);
-+		skb = (struct sk_buff *) ((void **) here[2])[2];
-+		skb->atm.timestamp = xtime;
-+#if 0
-+printk("[-3..0] 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",((unsigned *) skb->data)[-3],
-+  ((unsigned *) skb->data)[-2],((unsigned *) skb->data)[-1],
-+  ((unsigned *) skb->data)[0]);
-+#endif
-+		EVENT("skb 0x%08lx, here 0x%08lx\n",(unsigned long) skb,
-+		    (unsigned long) here);
-+#if 0
-+printk("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
-+#endif
-+		size = error ? 0 : ntohs(((unsigned short *) skb->data)[cells*
-+		    ATM_CELL_PAYLOAD/sizeof(unsigned short)-3]);
-+		EVENT("got skb 0x%lx, size %d\n",(unsigned long) skb,size);
-+		chan = (here[3] & uPD98401_AAL5_CHAN) >>
-+		    uPD98401_AAL5_CHAN_SHIFT;
-+		if (chan < zatm_dev->chans && zatm_dev->rx_map[chan]) {
-+			vcc = zatm_dev->rx_map[chan];
-+			if (skb == zatm_dev->last_free[ZATM_VCC(vcc)->pool])
-+				zatm_dev->last_free[ZATM_VCC(vcc)->pool] = NULL;
-+			expect = skb_dequeue(&zatm_dev->pool[ZATM_VCC(vcc)->
-+                            pool]);
-+			if (skb != expect) {
-+				printk(KERN_CRIT DEV_LABEL "(itf %d): skb "
-+				    "mismatch - got 0x%08lx, expected "
-+				    "0x%08lx\n",dev->number,
-+				    (unsigned long) skb,(unsigned long) expect);
-+				size = 0;
-+				vcc = NULL;
-+				event_dump();
-+			}
-+		}
-+		else {
-+			printk(KERN_ERR DEV_LABEL "(itf %d): RX indication "
-+			    "for non-existing channel\n",dev->number);
-+			size = 0;
-+			vcc = NULL;
-+			event_dump();
-+		}
-+		if (error) {
-+			printk(KERN_WARNING DEV_LABEL "(itf %d): chan %d "
-+			    "error %s\n",dev->number,chan,err_txt[error]);
-+			size = 0;
-+		}
-+		if (size && (size > cells*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER ||
-+		    size <= (cells-1)*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER)) {
-+			printk(KERN_ERR DEV_LABEL "(itf %d): size %d with %d "
-+			    "cells\n",dev->number,size,cells);
-+			size = 0;
-+			event_dump();
-+		}
-+		if (size > ATM_MAX_AAL5_PDU) {
-+			printk(KERN_ERR DEV_LABEL "(itf %d): size too big "
-+			    "(%d)\n",dev->number,size);
-+			size = 0;
-+			event_dump();
-+		}
-+		if (!size) {
-+			kfree_skb(skb,FREE_READ);
-+			if (vcc) vcc->stats->rx_err++;
-+		}
-+		else {
-+			skb->len = size;
-+			skb->atm.vcc = vcc;
-+			vcc->push(skb->atm.vcc,skb);
-+			vcc->stats->rx++;
-+		}
-+#if 0
-+if (vcc && size == 2) exception(vcc);
-+#endif
-+	}
-+	zout(pos & 0xffff,MTA(mbx));
-+#if 0 /* probably a stupid idea */
-+	refill_pool(dev,zatm_vcc->pool);
-+		/* maybe this saves us a few interrupts */
-+#endif
-+}
-+
-+
-+static int open_rx_first(struct atm_vcc *vcc)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	unsigned long flags;
-+	unsigned short chan;
-+	int cells;
-+
-+	DPRINTK("open_rx_first (0x%x)\n",inb_p(0xc053));
-+	zatm_dev = ZATM_DEV(vcc->dev);
-+	zatm_vcc = ZATM_VCC(vcc);
-+	zatm_vcc->rx_chan = 0;
-+	if (vcc->rxtp.class == ATM_NONE) return 0;
-+	if (vcc->aal == ATM_AAL5) {
-+		if (vcc->rxtp.max_sdu > 65464) vcc->rxtp.max_sdu = 65464;
-+			/* fix this - we may want to receive 64kB SDUs
-+			   later */
-+		cells = (vcc->rxtp.max_sdu+ATM_AAL5_TRAILER+ATM_CELL_PAYLOAD-1)/
-+		    ATM_CELL_PAYLOAD;
-+		zatm_vcc->pool = pool_index(cells*ATM_CELL_PAYLOAD);
-+	}
-+	else {
-+		cells = 1;
-+		zatm_vcc->pool = ZATM_AAL0_POOL;
-+	}
-+	if (zatm_vcc->pool < 0) return -EMSGSIZE;
-+	save_flags(flags);
-+	cli();
-+	zwait;
-+	zout(uPD98401_OPEN_CHAN,CMR);
-+	zwait;
-+	DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
-+	chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
-+	restore_flags(flags);
-+	DPRINTK("chan is %d\n",chan);
-+	if (!chan) return -EAGAIN;
-+	use_pool(vcc->dev,zatm_vcc->pool);
-+	DPRINTK("pool %d\n",zatm_vcc->pool);
-+	/* set up VC descriptor */
-+	cli();
-+	zpokel(zatm_dev,zatm_vcc->pool << uPD98401_RXVC_POOL_SHIFT,
-+	    chan*VC_SIZE/4);
-+	zpokel(zatm_dev,uPD98401_RXVC_OD | (vcc->aal == ATM_AAL5 ?
-+	    uPD98401_RXVC_AR : 0) | cells,chan*VC_SIZE/4+1);
-+	zpokel(zatm_dev,0,chan*VC_SIZE/4+2);
-+	zatm_vcc->rx_chan = chan;
-+	zatm_dev->rx_map[chan] = vcc;
-+	restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+static int open_rx_second(struct atm_vcc *vcc)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	unsigned long flags;
-+	int pos,shift;
-+
-+	DPRINTK("open_rx_second (0x%x)\n",inb_p(0xc053));
-+	zatm_dev = ZATM_DEV(vcc->dev);
-+	zatm_vcc = ZATM_VCC(vcc);
-+	if (!zatm_vcc->rx_chan) return 0;
-+	save_flags(flags);
-+	cli();
-+	/* should also handle VPI @@@ */
-+	pos = vcc->vci >> 1;
-+	shift = (1-(vcc->vci & 1)) << 4;
-+	zpokel(zatm_dev,(zpeekl(zatm_dev,pos) & ~(0xffff << shift)) |
-+	    ((zatm_vcc->rx_chan | uPD98401_RXLT_ENBL) << shift),pos);
-+	restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+static void close_rx(struct atm_vcc *vcc)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	unsigned long flags;
-+	int pos,shift;
-+
-+	zatm_vcc = ZATM_VCC(vcc);
-+	zatm_dev = ZATM_DEV(vcc->dev);
-+	if (!zatm_vcc->rx_chan) return;
-+DPRINTK("close_rx\n");
-+	/* disable receiver */
-+	save_flags(flags);
-+	if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
-+		cli();
-+		pos = vcc->vci >> 1;
-+		shift = (1-(vcc->vci & 1)) << 4;
-+		zpokel(zatm_dev,zpeekl(zatm_dev,pos) & ~(0xffff << shift),pos);
-+		restore_flags(flags);
-+		udelay(48000/zatm_dev->khz+1);
-+	}
-+	cli();
-+	zwait;
-+	zout(uPD98401_DEACT_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
-+	    uPD98401_CHAN_ADDR_SHIFT),CMR);
-+	zwait;
-+	zout(uPD98401_CLOSE_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
-+	    uPD98401_CHAN_ADDR_SHIFT),CMR);
-+	zwait;
-+	if (!(zin(CMR) & uPD98401_CHAN_ADDR))
-+		printk(KERN_CRIT DEV_LABEL "(itf %d): can't close RX channel "
-+		    "%d\n",vcc->dev->number,zatm_vcc->rx_chan);
-+	restore_flags(flags);
-+	zatm_dev->rx_map[zatm_vcc->rx_chan] = NULL;
-+	zatm_vcc->rx_chan = 0;
-+	unuse_pool(vcc->dev,zatm_vcc->pool);
-+}
-+
-+
-+static int start_rx(struct atm_dev *dev)
-+{
-+	struct zatm_dev *zatm_dev;
-+	int size,i;
-+
-+DPRINTK("start_rx\n");
-+	zatm_dev = ZATM_DEV(dev);
-+	size = sizeof(struct atm_vcc *)*zatm_dev->chans;
-+	zatm_dev->rx_map = (struct atm_vcc **) kmalloc(size,GFP_KERNEL);
-+	if (!zatm_dev->rx_map) return -ENOMEM;
-+	memset(zatm_dev->rx_map,0,size);
-+	/* set VPI/VCI split (use all VCIs and give what's left to VPIs) */
-+	zpokel(zatm_dev,(1 << dev->ci_range.vci_bits)-1,uPD98401_VRR);
-+	/* prepare free buffer pools */
-+	for (i = 0; i <= ZATM_LAST_POOL; i++) {
-+		zatm_dev->pool_info[i].ref_count = 0;
-+		zatm_dev->pool_info[i].rqa_count = 0;
-+		zatm_dev->pool_info[i].rqu_count = 0;
-+		zatm_dev->pool_info[i].low_water = LOW_MARK;
-+		zatm_dev->pool_info[i].high_water = HIGH_MARK;
-+		zatm_dev->pool_info[i].offset = 0;
-+		zatm_dev->pool_info[i].next_off = 0;
-+		zatm_dev->pool_info[i].next_cnt = 0;
-+		zatm_dev->pool_info[i].next_thres = OFF_CNG_THRES;
-+	}
-+	return 0;
-+}
-+
-+
-+/*----------------------------------- TX ------------------------------------*/
-+
-+
-+static int do_tx(struct sk_buff *skb)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	unsigned long *dsc;
-+	unsigned long flags;
-+
-+	EVENT("do_tx\n",0,0);
-+	DPRINTK("sending skb %p\n",skb);
-+	zatm_dev = ZATM_DEV(skb->atm.vcc->dev);
-+	zatm_vcc = ZATM_VCC(skb->atm.vcc);
-+	EVENT("iovcnt=%d\n",skb->atm.iovcnt,0);
-+	save_flags(flags);
-+	cli();
-+	if (!skb->atm.iovcnt) {
-+		if (zatm_vcc->txing == RING_ENTRIES-1) {
-+			restore_flags(flags);
-+			return RING_BUSY;
-+		}
-+		zatm_vcc->txing++;
-+		dsc = zatm_vcc->ring+zatm_vcc->ring_curr;
-+		zatm_vcc->ring_curr = (zatm_vcc->ring_curr+RING_WORDS) &
-+		    (RING_ENTRIES*RING_WORDS-1);
-+		dsc[1] = 0;
-+		dsc[2] = skb->len;
-+		dsc[3] = (unsigned long) skb->data;
-+		mb();
-+		dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM
-+		    | (skb->atm.vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0);
-+		EVENT("dsc (0x%08lx)\n",(unsigned long) dsc,0);
-+	}
-+	else {
-+printk("NONONONOO!!!!\n");
-+#if 0
-+		unsigned long *put;
-+		int i;
-+
-+		dsc = (unsigned long *) kmalloc(uPD98401_TXPD_SIZE*2+
-+		    uPD98401_TXBD_SIZE*skb->atm.iovcnt,GFP_ATOMIC);
-+		if (!dsc) {
-+			dev_kfree_skb(skb,FREE_WRITE);
-+			return -EAGAIN;
-+		}
-+		/* @@@ should check alignment */
-+		put = dsc+8;
-+		dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP |
-+		    (skb->atm.vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0);
-+		dsc[1] = 0;
-+		dsc[2] = skb->atm.iovcnt*uPD98401_TXBD_SIZE;
-+		dsc[3] = (unsigned long) put;
-+		for (i = 0; i < skb->atm.iovcnt; i++) {
-+			*put++ = ((struct iovec *) skb->data)[i].iov_len;
-+			*put++ = (unsigned long) ((struct iovec *)
-+			    skb->data)[i].iov_base;
-+		}
-+		put[-2] |= uPD98401_TXBD_LAST;
-+#endif
-+	}
-+	skb->atm.pos = (unsigned long) dsc;
-+	skb_queue_tail(&zatm_vcc->tx_queue,skb);
-+	DPRINTK("QRP=0x%08lx\n",zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
-+	  uPD98401_TXVC_QRP));
-+	zwait;
-+	zout(uPD98401_TX_READY | (zatm_vcc->tx_chan <<
-+	    uPD98401_CHAN_ADDR_SHIFT),CMR);
-+	restore_flags(flags);
-+	EVENT("done\n",0,0);
-+	return 0;
-+}
-+
-+
-+static inline void dequeue_tx(struct atm_vcc *vcc)
-+{
-+	struct zatm_vcc *zatm_vcc;
-+	struct sk_buff *skb;
-+
-+	EVENT("dequeue_tx\n",0,0);
-+	zatm_vcc = ZATM_VCC(vcc);
-+	skb = skb_dequeue(&zatm_vcc->tx_queue);
-+	if (!skb) {
-+		printk(KERN_CRIT DEV_LABEL "(itf %d): dequeue_tx but not "
-+		    "txing\n",vcc->dev->number);
-+		return;
-+	}
-+if (*(unsigned long *) skb->atm.pos != (uPD98401_TXPD_V | uPD98401_TXPD_DP |
-+  uPD98401_TXPD_SM | uPD98401_TXPD_AAL5)) printk("@#*$!!!!  (%08lx)\n",
-+  *(unsigned long *) skb->atm.pos);
-+	*(unsigned long *) skb->atm.pos = 0; /* mark as invalid */
-+	zatm_vcc->txing--;
-+	if (vcc->pop) vcc->pop(vcc,skb);
-+	else dev_kfree_skb(skb,FREE_WRITE);
-+	while ((skb = skb_dequeue(&zatm_vcc->backlog)))
-+		if (do_tx(skb) == RING_BUSY) {
-+			skb_queue_head(&zatm_vcc->backlog,skb);
-+			break;
-+		}
-+	vcc->stats->tx++;
-+	wake_up(&zatm_vcc->tx_wait);
-+}
-+
-+
-+static void poll_tx(struct atm_dev *dev,int mbx)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned long pos,x;
-+
-+	EVENT("poll_tx\n",0,0);
-+	zatm_dev = ZATM_DEV(dev);
-+	pos = (zatm_dev->mbx_start[mbx] & ~0xffff) | zin(MTA(mbx));
-+	while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
-+		int chan;
-+
-+#if 1
-+  unsigned long data,*addr;
-+
-+		EVENT("MBX: host 0x%lx, nic 0x%lx\n",pos,x);
-+		addr = (unsigned long *) pos;
-+		data = *addr;
-+		chan = (data & uPD98401_TXI_CONN) >> uPD98401_TXI_CONN_SHIFT;
-+		EVENT("addr = 0x%08lx, data = 0x%08lx,",(unsigned long) addr,
-+		    data);
-+		EVENT("chan = %d\n",chan,0);
-+#else
-+NO !
-+		chan = (zatm_dev->mbx_start[mbx][pos >> 2] & uPD98401_TXI_CONN)
-+		>> uPD98401_TXI_CONN_SHIFT;
-+#endif
-+		if (chan < zatm_dev->chans && zatm_dev->tx_map[chan])
-+			dequeue_tx(zatm_dev->tx_map[chan]);
-+		else {
-+			printk(KERN_CRIT DEV_LABEL "(itf %d): TX indication "
-+			    "for non-existing channel %d\n",dev->number,chan);
-+			event_dump();
-+		}
-+		if (((pos += 4) & 0xffff) == zatm_dev->mbx_end[mbx])
-+			pos = zatm_dev->mbx_start[mbx];
-+	}
-+	zout(pos & 0xffff,MTA(mbx));
-+}
-+
-+
-+static int alloc_shaper(struct atm_dev *dev,int *pcr,int min,int max,int ubr)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned long flags;
-+	unsigned long i,m,c;
-+	int shaper;
-+
-+	DPRINTK("alloc_shaper (min = %d, max = %d)\n",min,max);
-+	zatm_dev = ZATM_DEV(dev);
-+	if (!zatm_dev->free_shapers) return -EAGAIN;
-+	for (shaper = 0; !((zatm_dev->free_shapers >> shaper) & 1); shaper++);
-+	zatm_dev->free_shapers &= ~1 << shaper;
-+	if (ubr) {
-+		i = c = 5;
-+		m = 1;
-+		zatm_dev->ubr_ref_cnt++;
-+		zatm_dev->ubr = shaper;
-+	}
-+	else {
-+		if (min) {
-+			if (min <= 255) {
-+				i = min;
-+				m = ATM_OC3_PCR;
-+			}
-+			else {
-+				i = 255;
-+				m = ATM_OC3_PCR*255/min;
-+			}
-+		}
-+		else {
-+			if (max > zatm_dev->tx_bw) max = zatm_dev->tx_bw;
-+			if (max <= 255) {
-+				i = max;
-+				m = ATM_OC3_PCR;
-+			}
-+			else {
-+				i = 255;
-+				m = (ATM_OC3_PCR*255+max-1)/max;
-+			}
-+		}
-+		*pcr = i*ATM_OC3_PCR/m;
-+		c = 20; /* @@@ should use max_cdv ! */
-+		if ((min && *pcr < min) || (max && *pcr > max)) return -EINVAL;
-+		if (zatm_dev->tx_bw < *pcr) return -EAGAIN;
-+		zatm_dev->tx_bw -= *pcr;
-+	}
-+	save_flags(flags);
-+	cli();
-+	DPRINTK("i = %d, m = %d, PCR = %d\n",i,m,*pcr);
-+	zpokel(zatm_dev,(i << uPD98401_IM_I_SHIFT) | m,uPD98401_IM(shaper));
-+	zpokel(zatm_dev,c << uPD98401_PC_C_SHIFT,uPD98401_PC(shaper));
-+	zpokel(zatm_dev,0,uPD98401_X(shaper));
-+	zpokel(zatm_dev,0,uPD98401_Y(shaper));
-+	zpokel(zatm_dev,uPD98401_PS_E,uPD98401_PS(shaper));
-+	restore_flags(flags);
-+	return shaper;
-+}
-+
-+
-+static void dealloc_shaper(struct atm_dev *dev,int shaper)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned long flags;
-+
-+	zatm_dev = ZATM_DEV(dev);
-+	if (shaper == zatm_dev->ubr) {
-+		if (--zatm_dev->ubr_ref_cnt) return;
-+		zatm_dev->ubr = -1;
-+	}
-+	save_flags(flags);
-+	cli();
-+	zpokel(zatm_dev,zpeekl(zatm_dev,uPD98401_PS(shaper)) & ~uPD98401_PS_E,
-+	    uPD98401_PS(shaper));
-+	restore_flags(flags);
-+	zatm_dev->free_shapers |= 1 << shaper;
-+}
-+
-+
-+static void close_tx(struct atm_vcc *vcc)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	unsigned long flags;
-+	int chan;
-+struct sk_buff *skb;
-+int once = 1;
-+
-+	zatm_vcc = ZATM_VCC(vcc);
-+	zatm_dev = ZATM_DEV(vcc->dev);
-+	chan = zatm_vcc->tx_chan;
-+	if (!chan) return;
-+	DPRINTK("close_tx\n");
-+	save_flags(flags);
-+	cli();
-+	while (skb_peek(&zatm_vcc->backlog)) {
-+if (once) {
-+printk("waiting for backlog to drain ...\n");
-+event_dump();
-+once = 0;
-+}
-+		sleep_on(&zatm_vcc->tx_wait);
-+	}
-+once = 1;
-+	while ((skb = skb_peek(&zatm_vcc->tx_queue))) {
-+if (once) {
-+printk("waiting for TX queue to drain ... %p\n",skb);
-+event_dump();
-+once = 0;
-+}
-+		DPRINTK("waiting for TX queue to drain ... %p\n",skb);
-+		sleep_on(&zatm_vcc->tx_wait);
-+	}
-+#if 0
-+	zwait;
-+	zout(uPD98401_DEACT_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
-+#endif
-+	zwait;
-+	zout(uPD98401_CLOSE_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
-+	zwait;
-+	if (!(zin(CMR) & uPD98401_CHAN_ADDR))
-+		printk(KERN_CRIT DEV_LABEL "(itf %d): can't close TX channel "
-+		    "%d\n",vcc->dev->number,chan);
-+	restore_flags(flags);
-+	zatm_vcc->tx_chan = 0;
-+	zatm_dev->tx_map[chan] = NULL;
-+	if (zatm_vcc->shaper != zatm_dev->ubr) {
-+		zatm_dev->tx_bw += vcc->txtp.min_pcr;
-+		dealloc_shaper(vcc->dev,zatm_vcc->shaper);
-+	}
-+	if (zatm_vcc->ring) kfree(zatm_vcc->ring);
-+}
-+
-+
-+static int open_tx_first(struct atm_vcc *vcc)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	unsigned long flags,*loop;
-+	unsigned short chan;
-+	int pcr;
-+
-+	DPRINTK("open_tx_first\n");
-+	zatm_dev = ZATM_DEV(vcc->dev);
-+	zatm_vcc = ZATM_VCC(vcc);
-+	zatm_vcc->tx_chan = 0;
-+	if (vcc->txtp.class == ATM_NONE) return 0;
-+	save_flags(flags);
-+	cli();
-+	zwait;
-+	zout(uPD98401_OPEN_CHAN,CMR);
-+	zwait;
-+	DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
-+	chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
-+	restore_flags(flags);
-+	DPRINTK("chan is %d\n",chan);
-+	if (!chan) return -EAGAIN;
-+	if (vcc->txtp.class == ATM_UBR && zatm_dev->ubr != -1)
-+		zatm_vcc->shaper = zatm_dev->ubr;
-+	else {
-+		if (vcc->txtp.class == ATM_UBR)
-+			vcc->txtp.max_sdu = ATM_MAX_AAL5_PDU;
-+		if ((zatm_vcc->shaper = alloc_shaper(vcc->dev,&pcr,
-+		    vcc->txtp.min_pcr,vcc->txtp.max_pcr,
-+		    vcc->txtp.class == ATM_UBR)) < 0) {
-+			close_tx(vcc);
-+			return zatm_vcc->shaper;
-+		}
-+		vcc->txtp.min_pcr = vcc->txtp.max_pcr = pcr;
-+	}
-+	zatm_vcc->tx_chan = chan;
-+	skb_queue_head_init(&zatm_vcc->tx_queue);
-+	zatm_vcc->tx_wait = NULL;
-+	/* initialize ring */
-+	zatm_vcc->ring = kmalloc(RING_SIZE,GFP_KERNEL);
-+	if (!zatm_vcc->ring) return -ENOMEM;
-+	memset(zatm_vcc->ring,0,RING_SIZE);
-+	loop = zatm_vcc->ring+RING_ENTRIES*RING_WORDS;
-+	loop[0] = uPD98401_TXPD_V;
-+	loop[1] = loop[2] = 0;
-+	loop[3] = (unsigned long) zatm_vcc->ring;
-+	zatm_vcc->ring_curr = 0;
-+	zatm_vcc->txing = 0;
-+	skb_queue_head_init(&zatm_vcc->backlog);
-+	zpokel(zatm_dev,(unsigned long) zatm_vcc->ring,
-+	    chan*VC_SIZE/4+uPD98401_TXVC_QRP);
-+	return 0;
-+}
-+
-+
-+static int open_tx_second(struct atm_vcc *vcc)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	unsigned long flags;
-+
-+	DPRINTK("open_tx_second\n");
-+	zatm_dev = ZATM_DEV(vcc->dev);
-+	zatm_vcc = ZATM_VCC(vcc);
-+	if (!zatm_vcc->tx_chan) return 0;
-+	save_flags(flags);
-+	/* set up VC descriptor */
-+	cli();
-+	zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4);
-+	zpokel(zatm_dev,uPD98401_TXVC_L | (zatm_vcc->shaper <<
-+	    uPD98401_TXVC_SHP_SHIFT) | (vcc->vpi << uPD98401_TXVC_VPI_SHIFT) |
-+	    vcc->vci,zatm_vcc->tx_chan*VC_SIZE/4+1);
-+	zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4+2);
-+	restore_flags(flags);
-+	zatm_dev->tx_map[zatm_vcc->tx_chan] = vcc;
-+	return 0;
-+}
-+
-+
-+static int start_tx(struct atm_dev *dev)
-+{
-+	struct zatm_dev *zatm_dev;
-+	int i;
-+
-+	DPRINTK("start_tx\n");
-+	zatm_dev = ZATM_DEV(dev);
-+	zatm_dev->tx_map = (struct atm_vcc **) kmalloc(sizeof(struct atm_vcc *)*
-+	    zatm_dev->chans,GFP_KERNEL);
-+	if (!zatm_dev->tx_map) return -ENOMEM;
-+	zatm_dev->tx_bw = ATM_OC3_PCR;
-+	zatm_dev->free_shapers = (1 << NR_SHAPERS)-1;
-+	zatm_dev->ubr = -1;
-+	zatm_dev->ubr_ref_cnt = 0;
-+	/* initialize shapers */
-+	for (i = 0; i < NR_SHAPERS; i++) zpokel(zatm_dev,0,uPD98401_PS(i));
-+	return 0;
-+}
-+
-+
-+/*------------------------------- interrupts --------------------------------*/
-+
-+
-+static void zatm_int(int irq,void *dev_id,struct pt_regs *regs)
-+{
-+	struct atm_dev *dev;
-+	struct zatm_dev *zatm_dev;
-+	unsigned long reason;
-+
-+	dev = dev_id;
-+	zatm_dev = ZATM_DEV(dev);
-+	while ((reason = zin(GSR))) {
-+		EVENT("reason 0x%lx\n",reason,0);
-+		if (reason & uPD98401_INT_PI) {
-+			EVENT("PHY int\n",0,0);
-+			dev->phy->interrupt(dev);
-+		}
-+		if (reason & uPD98401_INT_RQA) {
-+			unsigned long pools;
-+			int i;
-+
-+			pools = zin(RQA);
-+			EVENT("RQA (0x%08lx)\n",pools,0);
-+			for (i = 0; pools; i++) {
-+				if (pools & 1) {
-+					refill_pool(dev,i);
-+					zatm_dev->pool_info[i].rqa_count++;
-+				}
-+				pools >>= 1;
-+			}
-+		}
-+		if (reason & uPD98401_INT_RQU) {
-+			unsigned long pools;
-+			int i;
-+			pools = zin(RQU);
-+			printk(KERN_WARNING DEV_LABEL "(itf %d): RQU 0x%08lx\n",
-+			    dev->number,pools);
-+			event_dump();
-+			for (i = 0; pools; i++) {
-+				if (pools & 1) {
-+					refill_pool(dev,i);
-+					zatm_dev->pool_info[i].rqu_count++;
-+				}
-+				pools >>= 1;
-+			}
-+		}
-+		/* don't handle RD */
-+		if (reason & uPD98401_INT_SPE)
-+			printk(KERN_ALERT DEV_LABEL "(itf %d): system parity "
-+			    "error at 0x%08x\n",dev->number,zin(ADDR));
-+		if (reason & uPD98401_INT_CPE)
-+			printk(KERN_ALERT DEV_LABEL "(itf %d): control memory "
-+			    "parity error at 0x%08x\n",dev->number,zin(ADDR));
-+		if (reason & uPD98401_INT_SBE) {
-+			printk(KERN_ALERT DEV_LABEL "(itf %d): system bus "
-+			    "error at 0x%08x\n",dev->number,zin(ADDR));
-+			event_dump();
-+		}
-+		/* don't handle IND */
-+		if (reason & uPD98401_INT_MF) {
-+			printk(KERN_CRIT DEV_LABEL "(itf %d): mailbox full "
-+			    "(0x%lx)\n",dev->number,(reason & uPD98401_INT_MF)
-+			    >> uPD98401_INT_MF_SHIFT);
-+			event_dump();
-+			    /* @@@ should try to recover */
-+		}
-+		if (reason & uPD98401_INT_MM) {
-+			if (reason & 1) poll_rx(dev,0);
-+			if (reason & 2) poll_rx(dev,1);
-+			if (reason & 4) poll_tx(dev,2);
-+			if (reason & 8) poll_tx(dev,3);
-+		}
-+		/* @@@ handle RCRn */
-+	}
-+}
-+
-+
-+/*----------------------------- (E)EPROM access -----------------------------*/
-+
-+
-+static void eprom_set(struct zatm_dev *zatm_dev,unsigned long value,
-+    unsigned short cmd)
-+{
-+	int error;
-+
-+	if ((error = pcibios_write_config_dword(zatm_dev->bus,zatm_dev->dev_fn,
-+	    cmd,value)))
-+		printk(KERN_ERR DEV_LABEL ": PCI write failed (%s)\n",
-+		    pcibios_strerror(error));
-+}
-+
-+
-+static unsigned long eprom_get(struct zatm_dev *zatm_dev,unsigned short cmd)
-+{
-+	unsigned int value;
-+	int error;
-+
-+	if ((error = pcibios_read_config_dword(zatm_dev->bus,zatm_dev->dev_fn,
-+	    cmd,&value)))
-+		printk(KERN_ERR DEV_LABEL ": PCI read failed (%s)\n",
-+		    pcibios_strerror(error));
-+	return value;
-+}
-+
-+
-+static void eprom_put_bits(struct zatm_dev *zatm_dev,unsigned long data,
-+    int bits,unsigned short cmd)
-+{
-+	unsigned long value;
-+	int i;
-+
-+	for (i = bits-1; i >= 0; i--) {
-+		value = ZEPROM_CS | (((data >> i) & 1) ? ZEPROM_DI : 0);
-+		eprom_set(zatm_dev,value,cmd);
-+		eprom_set(zatm_dev,value | ZEPROM_SK,cmd);
-+		eprom_set(zatm_dev,value,cmd);
-+	}
-+}
-+
-+
-+static void eprom_get_byte(struct zatm_dev *zatm_dev,unsigned char *byte,
-+    unsigned short cmd)
-+{
-+	int i;
-+
-+	*byte = 0;
-+	for (i = 8; i; i--) {
-+		eprom_set(zatm_dev,ZEPROM_CS,cmd);
-+		eprom_set(zatm_dev,ZEPROM_CS | ZEPROM_SK,cmd);
-+		*byte <<= 1;
-+		if (eprom_get(zatm_dev,cmd) & ZEPROM_DO) *byte |= 1;
-+		eprom_set(zatm_dev,ZEPROM_CS,cmd);
-+	}
-+}
-+
-+
-+static unsigned char eprom_try_esi(struct atm_dev *dev,unsigned short cmd,
-+    int offset,int swap)
-+{
-+	unsigned char buf[ZEPROM_SIZE];
-+	struct zatm_dev *zatm_dev;
-+	int i;
-+
-+	zatm_dev = ZATM_DEV(dev);
-+	for (i = 0; i < ZEPROM_SIZE; i += 2) {
-+		eprom_set(zatm_dev,ZEPROM_CS,cmd); /* select EPROM */
-+		eprom_put_bits(zatm_dev,ZEPROM_CMD_READ,ZEPROM_CMD_LEN,cmd);
-+		eprom_put_bits(zatm_dev,i >> 1,ZEPROM_ADDR_LEN,cmd);
-+		eprom_get_byte(zatm_dev,buf+i+swap,cmd);
-+		eprom_get_byte(zatm_dev,buf+i+1-swap,cmd);
-+		eprom_set(zatm_dev,0,cmd); /* deselect EPROM */
-+	}
-+	memcpy(dev->esi,buf+offset,ESI_LEN);
-+	return memcmp(dev->esi,"\0\0\0\0\0",ESI_LEN); /* assumes ESI_LEN == 6 */
-+}
-+
-+
-+static void eprom_get_esi(struct atm_dev *dev)
-+{
-+	if (eprom_try_esi(dev,ZEPROM_V1_REG,ZEPROM_V1_ESI_OFF,1)) return;
-+	(void) eprom_try_esi(dev,ZEPROM_V2_REG,ZEPROM_V2_ESI_OFF,0);
-+}
-+
-+
-+/*--------------------------------- entries ---------------------------------*/
-+
-+
-+static int zatm_init(struct atm_dev *dev)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned short command;
-+	unsigned char revision;
-+	int error,i,last;
-+	unsigned long t0,t1,t2;
-+
-+	DPRINTK(">zatm_init\n");
-+	zatm_dev = ZATM_DEV(dev);
-+	if ((error = pcibios_read_config_word(zatm_dev->bus,zatm_dev->dev_fn,
-+	    PCI_COMMAND,&command)) || (error = pcibios_read_config_dword(
-+	    zatm_dev->bus,zatm_dev->dev_fn,PCI_BASE_ADDRESS_0,&zatm_dev->base))
-+	    || (error = pcibios_read_config_byte(zatm_dev->bus,zatm_dev->dev_fn,
-+	    PCI_INTERRUPT_LINE,&zatm_dev->irq)) || (error =
-+	    pcibios_read_config_byte(zatm_dev->bus,zatm_dev->dev_fn,
-+	    PCI_REVISION_ID,&revision))) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): init error %s\n",
-+		    dev->number,pcibios_strerror(error));
-+		return -EINVAL;
-+	}
-+	zatm_dev->base &= PCI_BASE_ADDRESS_IO_MASK;
-+	if ((error = pcibios_write_config_word(zatm_dev->bus,zatm_dev->dev_fn,
-+	    PCI_COMMAND,command | PCI_COMMAND_IO | PCI_COMMAND_MASTER))) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable IO (%s)\n",
-+		    dev->number,pcibios_strerror(error));
-+		return error;
-+	}
-+	eprom_get_esi(dev);
-+	printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",
-+	    dev->number,revision,zatm_dev->base,zatm_dev->irq);
-+	/* reset uPD98401 */
-+	zout(0,SWR);
-+	while (!(zin(GSR) & uPD98401_INT_IND));
-+	zout(uPD98401_GMR_ONE /*uPD98401_BURST4*/,GMR);
-+	last = MAX_CRAM_SIZE;
-+	for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
-+		zpokel(zatm_dev,0x55555555,i);
-+		if (zpeekl(zatm_dev,i) != 0x55555555) last = i;
-+		else {
-+			zpokel(zatm_dev,0xAAAAAAAA,i);
-+			if (zpeekl(zatm_dev,i) != 0xAAAAAAAA) last = i;
-+			else zpokel(zatm_dev,i,i);
-+		}
-+	}
-+	for (i = 0; i < last; i += RAM_INCREMENT)
-+		if (zpeekl(zatm_dev,i) != i) break;
-+	zatm_dev->mem = i << 2;
-+	while (i) zpokel(zatm_dev,0,--i);
-+	/* reset again to rebuild memory pointers */
-+	zout(0,SWR);
-+	while (!(zin(GSR) & uPD98401_INT_IND));
-+	zout(uPD98401_GMR_ONE | uPD98401_BURST8 | uPD98401_BURST4 |
-+	    uPD98401_BURST2 | uPD98401_GMR_PM | uPD98401_GMR_DR,GMR);
-+	/* TODO: should shrink allocation now */
-+	printk("mem=%dkB,%s (",zatm_dev->mem >> 10,zatm_dev->copper ? "UTP" :
-+	    "MMF");
-+	for (i = 0; i < ESI_LEN; i++)
-+		printk("%02X%s",dev->esi[i],i == ESI_LEN-1 ? ")\n" : "-");
-+	do {
-+		t0 = zpeekl(zatm_dev,uPD98401_TSR);
-+		udelay(10);
-+		t1 = zpeekl(zatm_dev,uPD98401_TSR);
-+		udelay(1010);
-+		t2 = zpeekl(zatm_dev,uPD98401_TSR);
-+	}
-+	while (t0 > t1 || t1 > t2);
-+	zatm_dev->khz = t2-2*t1+t0;
-+	printk(KERN_NOTICE DEV_LABEL "(itf %d): uPD98401 %d.%d at %d.%03d "
-+	    "MHz\n",dev->number,
-+	    (zin(VER) & uPD98401_MAJOR) >> uPD98401_MAJOR_SHIFT,
-+            zin(VER) & uPD98401_MINOR,zatm_dev->khz/1000,zatm_dev->khz % 1000);
-+	return uPD98402_init(dev);
-+}
-+
-+
-+static int zatm_start(struct atm_dev *dev)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned long curr;
-+	int pools,vccs,rx;
-+	int error,i,ld;
-+
-+	DPRINTK("zatm_start\n");
-+	zatm_dev = ZATM_DEV(dev);
-+	if (request_irq(zatm_dev->irq,&zatm_int,0,DEV_LABEL,dev)) {
-+		printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
-+		    dev->number,zatm_dev->irq);
-+		return -EAGAIN;
-+	}
-+	request_region(zatm_dev->base,uPD98401_PORTS,DEV_LABEL);
-+	/* define memory regions */
-+	pools = NR_POOLS;
-+	if (NR_SHAPERS*SHAPER_SIZE > pools*POOL_SIZE)
-+		pools = NR_SHAPERS*SHAPER_SIZE/POOL_SIZE;
-+	vccs = (zatm_dev->mem-NR_SHAPERS*SHAPER_SIZE-pools*POOL_SIZE)/
-+	    (2*VC_SIZE+RX_SIZE);
-+	ld = -1;
-+	for (rx = 1; rx < vccs; rx <<= 1) ld++;
-+	dev->ci_range.vpi_bits = 0; /* @@@ no VPI for now */
-+	dev->ci_range.vci_bits = ld;
-+	zatm_dev->chans = vccs; /* ??? */
-+	curr = rx*RX_SIZE/4;
-+	DPRINTK("RX pool 0x%08lx\n",curr);
-+	zpokel(zatm_dev,curr,uPD98401_PMA); /* receive pool */
-+	zatm_dev->pool_base = curr;
-+	curr += pools*POOL_SIZE/4;
-+	DPRINTK("Shapers 0x%08lx\n",curr);
-+	zpokel(zatm_dev,curr,uPD98401_SMA); /* shapers */
-+	curr += NR_SHAPERS*SHAPER_SIZE/4;
-+	DPRINTK("Free    0x%08lx\n",curr);
-+	zpokel(zatm_dev,curr,uPD98401_TOS); /* free pool */
-+	printk(KERN_INFO DEV_LABEL "(itf %d): %d shapers, %d pools, %d RX, "
-+	    "%ld VCs\n",dev->number,NR_SHAPERS,pools,rx,
-+	    (zatm_dev->mem-curr*4)/VC_SIZE);
-+	/* create mailboxes */
-+	for (i = 0; i < NR_MBX; i++) zatm_dev->mbx_start[i] = 0;
-+	for (i = 0; i < NR_MBX; i++)
-+		if (mbx_entries[i]) {
-+			unsigned long here;
-+
-+			here = (unsigned long) kmalloc(2*MBX_SIZE(i),
-+			    GFP_KERNEL);
-+			if (!here) return -ENOMEM;
-+			if ((here^(here+MBX_SIZE(i))) & ~0xffff) /* paranoia */
-+				here = (here & ~0xffff)+0x10000;
-+			DPRINTK("mbx@0x%08lx-0x%08lx\n",here,here+MBX_SIZE(i));
-+			zatm_dev->mbx_start[i] = here;
-+			zatm_dev->mbx_end[i] = (here+MBX_SIZE(i)) & 0xffff;
-+			zout(here >> 16,MSH(i));
-+			zout(here,MSL(i));
-+			zout((here+MBX_SIZE(i)) & 0xffff,MBA(i));
-+			zout(here & 0xffff,MTA(i));
-+			zout(here & 0xffff,MWA(i));
-+		}
-+	error = start_tx(dev);
-+	if (error) return error;
-+	error = start_rx(dev);
-+	if (error) return error;
-+	error = dev->phy->start(dev);
-+	if (error) return error;
-+	zout(0xffffffff,IMR); /* enable interrupts */
-+	/* enable TX & RX */
-+	zout(zin(GMR) | uPD98401_GMR_SE | uPD98401_GMR_RE,GMR);
-+	return 0;
-+}
-+
-+
-+static void zatm_close(struct atm_vcc *vcc)
-+{
-+        DPRINTK(">zatm_close\n");
-+        if (!ZATM_VCC(vcc)) return;
-+	vcc->flags &= ~ATM_VF_READY;
-+        close_rx(vcc);
-+EVENT("close_tx\n",0,0);
-+        close_tx(vcc);
-+        DPRINTK("zatm_close: done waiting\n");
-+        /* deallocate memory */
-+        kfree(ZATM_VCC(vcc));
-+        ZATM_VCC(vcc) = NULL;
-+	vcc->flags &= ~ATM_VF_ADDR;
-+}
-+
-+
-+static int zatm_open(struct atm_vcc *vcc,short vpi,int vci)
-+{
-+	struct zatm_dev *zatm_dev;
-+	struct zatm_vcc *zatm_vcc;
-+	int error;
-+
-+	DPRINTK(">zatm_open\n");
-+	zatm_dev = ZATM_DEV(vcc->dev);
-+	if (!(vcc->flags & ATM_VF_PARTIAL)) ZATM_VCC(vcc) = NULL;
-+	error = atm_find_ci(vcc,&vpi,&vci);
-+	if (error) return error;
-+	vcc->vpi = vpi;
-+	vcc->vci = vci;
-+	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
-+		vcc->flags |= ATM_VF_ADDR;
-+	if (vcc->aal != ATM_AAL5) return -EINVAL; /* @@@ AAL0 */
-+	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
-+	    vcc->vci);
-+	if (!(vcc->flags & ATM_VF_PARTIAL)) {
-+		zatm_vcc = kmalloc(sizeof(struct zatm_vcc),GFP_KERNEL);
-+		if (!zatm_vcc) {
-+			vcc->flags &= ~ATM_VF_ADDR;
-+			return -ENOMEM;
-+		}
-+		ZATM_VCC(vcc) = zatm_vcc;
-+		ZATM_VCC(vcc)->tx_chan = 0; /* for zatm_close after open_rx */
-+		if ((error = open_rx_first(vcc))) {
-+	                zatm_close(vcc);
-+	                return error;
-+	        }
-+		if ((error = open_tx_first(vcc))) {
-+			zatm_close(vcc);
-+			return error;
-+	        }
-+	}
-+	if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
-+	if ((error = open_rx_second(vcc))) {
-+		zatm_close(vcc);
-+		return error;
-+        }
-+	if ((error = open_tx_second(vcc))) {
-+		zatm_close(vcc);
-+		return error;
-+        }
-+	vcc->flags |= ATM_VF_READY;
-+        return 0;
-+}
-+
-+
-+static int zatm_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
-+{
-+	struct zatm_dev *zatm_dev;
-+	unsigned long flags;
-+	int error;
-+
-+	zatm_dev = ZATM_DEV(dev);
-+printk(KERN_CRIT "got here!\n");
-+	switch (cmd) {
-+case ZATM_TUNE:
-+{
-+	unsigned long old;
-+
-+	old = zin(GMR);
-+printk(KERN_INFO "got 0x%08lx\n",old);
-+	zout((old & ~0xf00) | (arg & 0xf00),GMR);
-+	return old;
-+
-+}
-+
-+		case ZATM_GETPOOLZ:
-+			if (!suser()) return -EPERM;
-+			/* fall through */
-+		case ZATM_GETPOOL:
-+			{
-+				int pool;
-+
-+				error = verify_area(VERIFY_WRITE,(void *) arg,
-+				    sizeof(struct zatm_pool_req));
-+				if (error) return error;
-+				error = verify_area(VERIFY_READ,(void *) arg,
-+				    sizeof(struct zatm_pool_req));
-+							/* paranoia ? */
-+				if (error) return error;
-+				pool = get_fs_long(&((struct zatm_pool_req *)
-+				    arg)->pool_num);
-+				if (pool < 0 || pool > ZATM_LAST_POOL)
-+					return -EINVAL;
-+				save_flags(flags);
-+				cli();
-+				memcpy_tofs(&((struct zatm_pool_req *) arg)->
-+				    info,&zatm_dev->pool_info[pool],
-+				    sizeof(struct zatm_pool_info));
-+				if (cmd == ZATM_GETPOOLZ) {
-+					zatm_dev->pool_info[pool].rqa_count = 0;
-+					zatm_dev->pool_info[pool].rqu_count = 0;
-+				}
-+				restore_flags(flags);
-+				return 0;
-+			}
-+		case ZATM_SETPOOL:
-+			{
-+				int pool,low,high,next_thres;
-+
-+				if (!suser()) return -EPERM;
-+				error = verify_area(VERIFY_READ,(void *) arg,
-+				    sizeof(struct zatm_pool_req));
-+				if (error) return error;
-+				pool = get_fs_long(&((struct zatm_pool_req *)
-+				    arg)->pool_num);
-+				if (pool < 0 || pool > ZATM_LAST_POOL)
-+					return -EINVAL;
-+				low = get_fs_long(&((struct zatm_pool_req *)
-+				    arg)->info.low_water);
-+				high = get_fs_long(&((struct zatm_pool_req *)
-+				    arg)->info.high_water);
-+				next_thres = get_fs_long(
-+				    &((struct zatm_pool_req *) arg)->info.
-+				    next_thres);
-+				if (!low)
-+					low = zatm_dev->pool_info[pool].
-+					    low_water;
-+				if (!high)
-+					high = zatm_dev->pool_info[pool].
-+					    high_water;
-+				if (!next_thres)
-+					next_thres = zatm_dev->pool_info[pool].
-+					    next_thres;
-+				if (low >= high || low < 0) return -EINVAL;
-+				save_flags(flags);
-+				cli();
-+				zatm_dev->pool_info[pool].low_water = low;
-+				zatm_dev->pool_info[pool].high_water = high;
-+				zatm_dev->pool_info[pool].next_thres =
-+				    next_thres;
-+				restore_flags(flags);
-+				return 0;
-+			}
-+		default:
-+        		if (!dev->phy->ioctl) return -EINVAL;
-+		        return dev->phy->ioctl(dev,cmd,arg);
-+	}
-+}
-+
-+
-+static int zatm_getsockopt(struct atm_vcc *vcc,int level,int optname,
-+    char *optval,int *optlen)
-+{
-+#ifdef CONFIG_MMU_HACKS
-+
-+static const struct atm_buffconst bctx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
-+static const struct atm_buffconst bcrx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
-+
-+#else
-+
-+static const struct atm_buffconst bctx = { 4,0,4,0,0,0 };
-+static const struct atm_buffconst bcrx = { 4,0,4,0,0,0 };
-+
-+#endif
-+	int error;
-+
-+	if (level == SOL_AAL && (optname == SO_BCTXOPT ||
-+	    optname == SO_BCRXOPT)) {
-+		if (get_fs_long(optlen) < sizeof(struct atm_buffconst))
-+			return -EINVAL;
-+		put_fs_long(sizeof(struct atm_buffconst),optlen);
-+		error = verify_area(VERIFY_WRITE,optval,
-+		    sizeof(struct atm_buffconst));
-+		if (error) return error;
-+		memcpy_tofs(optval,optname == SO_BCTXOPT ? &bctx : &bcrx,
-+		    sizeof(struct atm_buffconst));
-+		return 0;
-+	}
-+	return -EINVAL;
-+}
-+
-+
-+static int zatm_setsockopt(struct atm_vcc *vcc,int level,int optname,
-+    char *optval,int optlen)
-+{
-+	return -EINVAL;
-+}
-+
-+
-+static int zatm_sg_send(struct atm_vcc *vcc,unsigned long start,
-+    unsigned long size)
-+{
-+	return vcc->aal == ATM_AAL5;
-+	   /* @@@ should check size and maybe alignment*/
-+}
-+
-+
-+static int zatm_send(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	int error;
-+
-+	EVENT(">zatm_send 0x%08x\n",(unsigned long) skb,0);
-+	if (!ZATM_VCC(vcc)->tx_chan || !(vcc->flags & ATM_VF_READY)) {
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		return -EINVAL;
-+	}
-+	if (!skb) {
-+		printk(KERN_CRIT "!skb in zatm_send ?\n");
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		return -EINVAL;
-+	}
-+	skb->atm.vcc = vcc;
-+	error = do_tx(skb);
-+	if (error != RING_BUSY) return error;
-+	skb_queue_tail(&ZATM_VCC(vcc)->backlog,skb);
-+	return 0;
-+}
-+
-+
-+static void zatm_phy_put(struct atm_dev *dev,unsigned char value,
-+    unsigned long addr)
-+{
-+	struct zatm_dev *zatm_dev;
-+
-+	zatm_dev = ZATM_DEV(dev);
-+	zwait;
-+	zout(value,CER);
-+	zout(uPD98401_IND_ACC | uPD98401_IA_B0 |
-+	    (uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
-+}
-+
-+
-+static unsigned char zatm_phy_get(struct atm_dev *dev,unsigned long addr)
-+{
-+	struct zatm_dev *zatm_dev;
-+
-+	zatm_dev = ZATM_DEV(dev);
-+	zwait;
-+	zout(uPD98401_IND_ACC | uPD98401_IA_B0 | uPD98401_IA_RW |
-+	  (uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
-+	zwait;
-+	return zin(CER) & 0xff;
-+}
-+
-+
-+static const struct atmdev_ops ops = {
-+	zatm_open,
-+	zatm_close,
-+	zatm_ioctl,
-+	zatm_getsockopt,
-+	zatm_setsockopt,
-+	zatm_send,
-+	zatm_sg_send,
-+	NULL,			/* no poll */
-+	NULL,			/* no send_oam */
-+	zatm_phy_put,
-+	zatm_phy_get,
-+	zatm_feedback
-+};
-+
-+
-+int zatm_detect(void)
-+{
-+	struct atm_dev *dev;
-+	struct zatm_dev *zatm_dev;
-+	int devs,type,index;
-+
-+	if (!pcibios_present()) {
-+		printk(KERN_ERR DEV_LABEL " driver but no PCI BIOS ?\n");
-+		return 0;
-+	}
-+	zatm_dev = (struct zatm_dev *) kmalloc(sizeof(struct zatm_dev),
-+	    GFP_KERNEL);
-+	if (!zatm_dev) return -ENOMEM;
-+	devs = 0;
-+	for (type = 0; type < 2; type++) {
-+		index = 0;
-+		while (!pcibios_find_device(PCI_VENDOR_ID_ZEITNET,type ?
-+		    PCI_DEVICE_ID_ZEITNET_1225 : PCI_DEVICE_ID_ZEITNET_1221,
-+		    index,&zatm_dev->bus,&zatm_dev->dev_fn)) {
-+			dev = atm_dev_register(DEV_LABEL,&ops,0);
-+			if (!dev) break;
-+			ZATM_DEV(dev) = zatm_dev;
-+			zatm_dev->copper = type;
-+			if (zatm_init(dev) || zatm_start(dev)) {
-+				atm_dev_deregister(dev);
-+				break;
-+			}
-+			zatm_dev->more = zatm_boards;
-+			zatm_boards = dev;
-+			index++;
-+			devs++;
-+			zatm_dev = (struct zatm_dev *) kmalloc(sizeof(struct
-+			    zatm_dev),GFP_KERNEL);
-+			if (!zatm_dev) break;
-+		}
-+	}
-+	return devs;
-+}
-+
-+
-+#ifdef MODULE
-+ 
-+int init_module(void)
-+{
-+	if (!zatm_detect()) {
-+		printk(KERN_ERROR DEV_LABEL ": no adapter found\n");
-+		return -ENXIO;
-+	}
-+	MOD_INC_USE_COUNT;
-+	return 0;
-+}
-+ 
-+ 
-+void cleanup_module(void)
-+{
-+	/*
-+	 * Well, there's no way to get rid of the driver yet, so we don't
-+	 * have to clean up, right ? :-)
-+	 */
-+}
-+ 
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/zatm.h	Fri Jul 19 15:09:24 1996
-@@ -0,0 +1,128 @@
-+/* drivers/atm/zatm.h - ZeitNet ZN122x device driver declarations */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef DRIVER_ATM_ZATM_H
-+#define DRIVER_ATM_ZATM_H
-+
-+#include <linux/atmioc.h>
-+
-+#define ZATM_GETPOOL	_IOW('a',ATMIOC_SARPRV+1,struct atmif_sioc)
-+						/* get pool statistics */
-+#define ZATM_GETPOOLZ	_IOW('a',ATMIOC_SARPRV+2,struct atmif_sioc)
-+						/* get statistics and zero */
-+#define ZATM_SETPOOL	_IOW('a',ATMIOC_SARPRV+3,struct atmif_sioc)
-+						/* set pool parameters */
-+#define ZATM_TUNE _IOW('a',ATMIOC_SARPRV+4,struct atmif_sioc)
-+
-+struct zatm_pool_info {
-+	int ref_count;			/* free buffer pool usage counters */
-+	int low_water,high_water;	/* refill parameters */
-+	int rqa_count,rqu_count;	/* queue condition counters */
-+	int offset,next_off;		/* alignment optimizations: offset */
-+	int next_cnt,next_thres;	/* repetition counter and threshold */
-+};
-+
-+struct zatm_pool_req {
-+	int pool_num;			/* pool number */
-+	struct zatm_pool_info info;	/* actual information */
-+};
-+
-+
-+#define ZATM_OAM_POOL		0	/* free buffer pool for OAM cells */
-+#define ZATM_AAL0_POOL		1	/* free buffer pool for AAL0 cells */
-+#define ZATM_AAL5_POOL_BASE	2	/* first AAL5 free buffer pool */
-+#define ZATM_LAST_POOL	ZATM_AAL5_POOL_BASE+10 /* max. 64 kB */
-+
-+
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/skbuff.h>
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+
-+
-+#define DEV_LABEL	"zatm"
-+
-+#define MAX_AAL5_PDU	10240	/* allocate for AAL5 PDUs of this size */
-+#define MAX_RX_SIZE_LD	14	/* ceil(log2((MAX_AAL5_PDU+47)/48)) */
-+
-+#define LOW_MARK	12	/* start adding new buffers if less than 12 */
-+#define HIGH_MARK	30	/* stop adding buffers after reaching 30 */
-+#define OFF_CNG_THRES	5	/* threshold for offset changes */
-+
-+#define RX_SIZE		2	/* RX lookup entry size (in bytes) */
-+#define NR_POOLS	32	/* number of free buffer pointers */
-+#define POOL_SIZE	8	/* buffer entry size (in bytes) */
-+#define NR_SHAPERS	16	/* number of shapers */
-+#define SHAPER_SIZE	4	/* shaper entry size (in bytes) */
-+#define VC_SIZE		32	/* VC dsc (TX or RX) size (in bytes) */
-+
-+#define RING_ENTRIES	32	/* ring entries (without back pointer) */
-+#define RING_WORDS	4	/* ring element size */
-+#define RING_SIZE	(sizeof(unsigned long)*(RING_ENTRIES+1)*RING_WORDS)
-+
-+#define NR_MBX		4	/* four mailboxes */
-+#define MBX_RX_0	0	/* mailbox indices */
-+#define MBX_RX_1	1
-+#define MBX_TX_0	2
-+#define MBX_TX_1	3
-+
-+
-+struct zatm_vcc {
-+	/*-------------------------------- RX part */
-+	int rx_chan;			/* RX channel, 0 if none */
-+	int pool;			/* free buffer pool */
-+	/*-------------------------------- TX part */
-+	int tx_chan;			/* TX channel, 0 if none */
-+	int shaper;			/* shaper, <0 if none */
-+	struct sk_buff_head tx_queue;	/* list of buffers in transit */
-+	struct wait_queue *tx_wait;	/* for close */
-+	unsigned long *ring;		/* transmit ring */
-+	int ring_curr;			/* current write position */
-+	int txing;			/* number of transmits in progress */
-+	struct sk_buff_head backlog;	/* list of buffers waiting for ring */
-+};
-+
-+struct zatm_dev {
-+	/*-------------------------------- TX part */
-+	int tx_bw;			/* remaining bandwidth */
-+	unsigned long free_shapers;	/* bit set */
-+	int ubr;			/* UBR shaper; -1 if none */
-+	int ubr_ref_cnt;		/* number of VCs using UBR shaper */
-+	/*-------------------------------- RX part */
-+	int pool_ref[NR_POOLS];		/* free buffer pool usage counters */
-+	volatile struct sk_buff *last_free[NR_POOLS];
-+					/* last entry in respective pool */
-+	struct sk_buff_head pool[NR_POOLS];/* free buffer pools */
-+	struct zatm_pool_info pool_info[NR_POOLS]; /* pool information */
-+	/*-------------------------------- maps */
-+	struct atm_vcc **tx_map;	/* TX VCCs */
-+	struct atm_vcc **rx_map;	/* RX VCCs */
-+	int chans;			/* map size, must be 2^n */
-+	/*-------------------------------- mailboxes */
-+	unsigned long mbx_start[NR_MBX];/* start addresses */
-+	unsigned short mbx_end[NR_MBX];	/* end offset (in bytes) */
-+	/*-------------------------------- other pointers */
-+	unsigned long pool_base;	/* Free buffer pool dsc (word addr) */
-+	/*-------------------------------- ZATM links */
-+	struct atm_dev *more;		/* other ZATM devices */
-+	/*-------------------------------- general information */
-+	int mem;			/* RAM on board (in bytes) */
-+	int khz;			/* timer clock */
-+	int copper;			/* PHY type */
-+	unsigned char irq;		/* IRQ */
-+	unsigned int base;		/* IO base address */
-+	unsigned char bus;		/* PCI stuff */
-+	unsigned char dev_fn;
-+};
-+
-+
-+#define ZATM_DEV(d) ((struct zatm_dev *) (d)->dev_data)
-+#define ZATM_VCC(d) ((struct zatm_vcc *) (d)->dev_data)
-+
-+#endif __KERNEL__
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/zeprom.h	Wed Jul 10 20:16:37 1996
-@@ -0,0 +1,34 @@
-+/* drivers/atm/zeprom.h - ZeitNet ZN122x EEPROM (NM93C46) declarations */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef DRIVER_ATM_ZEPROM_H
-+#define DRIVER_ATM_ZEPROM_H
-+
-+/* Different versions use different control registers */
-+
-+#define ZEPROM_V1_REG	PCI_VENDOR_ID	/* PCI register */
-+#define ZEPROM_V2_REG	0x40
-+
-+/* Bits in contol register */
-+
-+#define ZEPROM_SK	0x80000000	/* strobe (probably on raising edge) */
-+#define ZEPROM_CS	0x40000000	/* Chip Select */
-+#define ZEPROM_DI	0x20000000	/* Data Input */
-+#define ZEPROM_DO	0x10000000	/* Data Output */
-+
-+#define ZEPROM_SIZE	32		/* 32 bytes */
-+#define ZEPROM_V1_ESI_OFF 24		/* ESI offset in EEPROM (V1) */
-+#define ZEPROM_V2_ESI_OFF 4		/* ESI offset in EEPROM (V2) */
-+
-+#define ZEPROM_CMD_LEN	3		/* commands are three bits */
-+#define ZEPROM_ADDR_LEN	6		/* addresses are six bits */
-+
-+/* Commands (3 bits) */
-+
-+#define ZEPROM_CMD_READ	6
-+
-+/* No other commands are needed. */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/tneta1570.h	Mon Jun 10 17:36:11 1996
-@@ -0,0 +1,310 @@
-+/* drivers/atm/tneta1570.h - TI TNETA1570 (SAR) declarations */
-+ 
-+/* Written 1996 by Rolf Fiedler
-+ * based on the atm drivers by Werner Almesberger, EPFL LRC 
-+ */
-+
-+#ifndef DRIVERS_ATM_TNETA1570_H
-+#define DRIVERS_ATM_TNETA1570_H
-+
-+#include <linux/atmioc.h>
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+#include <linux/skbuff.h>
-+#include <linux/time.h>
-+
-+#define NR_VCI     256
-+#define NR_VCI_LD  8
-+#define NR_VPI     32
-+#define NR_VPI_LD  5
-+#define MAX_VCI    255
-+#define MAX_VPI    255
-+
-+/* this is better put in pci.h, but I want to keep my driver independent */
-+#define MEM_VALID 0xfffffff0
-+#ifndef PCI_VENDOR_ID_TI
-+#define PCI_VENDOR_ID_TI 0x104c
-+#endif
-+#ifndef PCI_DEVICE_ID_TI_TNETA1570
-+#define PCI_DEVICE_ID_TI_TNETA1570 0xa001
-+#endif
-+
-+#define KERNEL_OFFSET    0xc0000000    /* kernel 0 is at linear 0xc0000000 */
-+#define DEV_LABEL	 "tneta1570"
-+#define RAM_INCREMENT	1024		/* check in 4 kB increments */
-+
-+/*---------------------------------------------------------*/
-+
-+/* transmit DMA state table, in control memory */
-+struct tneta_tx_dma_state_table{
-+     unsigned int control;
-+     unsigned int current_buffer_ptr;
-+     unsigned int atm_header;
-+     unsigned int dma_state_flag;          /* only initialized value */
-+     unsigned int next_buffer_ptr;
-+     unsigned int start_of_buffer_address;
-+     unsigned int partial_AAL5_tx_CRC;
-+     unsigned int AAL5_control_length_field;
-+};
-+
-+/* receive DMA state table, in control memory */
-+#define RX_DMA_CONTROL_AAL5 ((1<<29)|(1<<26))
-+#define RX_DMA_CONTROL_AAL0 ((1<<29)|(1<<27))
-+#define RX_TIME_OUT         (0xfff<<12)
-+#define DMA_ON              (1<<31)          
-+#define DMA_FILTER          (1<<30)
-+struct tneta_rx_dma_state_table{
-+     unsigned int control;             /* needs initialization */
-+     unsigned int current_buffer_ptr;
-+     unsigned int sob_ptr;
-+     unsigned int EOP;
-+     unsigned int sop_ptr;
-+     unsigned int AAL0_cells;          /* needs initialization if AAL0 */
-+     unsigned int dma_on_idx;          /* needs initialization */
-+     unsigned int rx_timeout;          /* needs initialization */
-+};
-+
-+#define MAX_AAL5_PDU	9600	        /* max size for AAL5 PDUs buffers */
-+#define MAX_AAL5_CELLS  200
-+#define MAX_AAL0_PDU    48
-+#define MAX_AAL0_CELLS  1
-+#define AAL0_BUFS       32
-+#define AAL5_BUFS       16
-+#define FBR_AAL0_32     ((2<<16) | (1<<10) | (0))
-+#define FBR_AAL5_16     ((MAX_AAL5_CELLS<<16) | (0<<10) | (0))
-+#define RX_HDR          20
-+#define MAX_FBR_ENTRIES 256
-+struct tneta_rx_fbrptr {
-+        unsigned int * buf_ptr;
-+        unsigned int buf_size;
-+};
-+
-+#define AAL5_IND    (1<<26)
-+struct tneta_rx_compl {
-+     unsigned int atm_header;
-+     unsigned int error;
-+     unsigned int buf_ptr;
-+     unsigned int trailer;
-+     unsigned int control;             /* needs initialization */
-+     unsigned int res1;
-+     unsigned int res2;
-+     unsigned int res3;
-+};
-+
-+/* device dependent vcc data */
-+#define TX_SEG_RING_SIZE 256
-+
-+struct tneta1570_vcc {
-+	/*-------------------------------- RX part */
-+        int (*rx)(struct atm_vcc *vcc);
-+        int rxing;                        /* pending cells */
-+        struct wait_queue * rx_wait;      /* for close */
-+        int dma_channel;                  /* for close */
-+        int fbr_idx;
-+        /*-------------------------------- TX part */
-+        struct wait_queue * tx_wait;      /* for close */
-+        int txing;                        /* cells to be tx'd */
-+        int scheduler_idx;                /* index in scheduler table */
-+        unsigned int * seg_ring;          /* size aligned 1K */
-+        unsigned int * seg_ring_mptr;     /* word aligned */
-+        int seg_ring_idx;
-+   
-+        struct atm_vcc *next;             /* next pending rx */
-+        struct sk_buff *last;             /* last PDU */
-+};
-+
-+
-+/* device dependent data */
-+#define TXCMPLR_SZ_IRQ 256   /* size aligned */
-+#define TXCMPLR_SZ_NOI 128   /* size aligned */
-+#define RXCMPLR_SZ_IRQ 128   /* size aligned */
-+#define RXCMPLR_SZ_NOI 64    /* size aligned */
-+#define SAR_REG_WORD(dev, x) (dev->reg[x])
-+#define SAR_REG_SHORT(dev, x) (((unsigned short *)dev->reg)[x])
-+#define MAX_SCHED_ENTRIES 1022
-+#define MAX_TXDMA_ENTRIES 1022
-+#define TX_CMPL_R_IRQ(dev)  (dev->txcmplringptr_irq[dev->txcmpl_ring_idx_irq])
-+#define TX_CMPL_R_NOI(dev)  (dev->txcmplringptr_noi[dev->txcmpl_ring_idx_noi])
-+#define RX_CMPL_R_IRQ(dev)  (dev->rxcmplringptr_irq[dev->rxcmpl_ring_idx_irq])
-+#define RX_CMPL_R_NOI(dev)  (dev->rxcmplringptr_noi[dev->rxcmpl_ring_idx_noi])
-+
-+struct tneta1570_dev {
-+	/*-------------------------------- TX part */
-+        int txcmpl_ring_idx_noi, txcmpl_ring_idx_irq;
-+        unsigned int *txcmplringptr_noi, *txcmplringptr_irq;
-+        unsigned int *txcmpl_ring;
-+
-+        /*-------------------------------- RX part */
-+        int rxcmpl_ring_idx_noi, rxcmpl_ring_idx_irq;
-+        struct tneta_rx_compl *rxcmplringptr_noi, *rxcmplringptr_irq;
-+        struct tneta_rx_compl *rxcmpl_ring;
-+
-+        /*-------------------------------- maps */
-+        int oam_fbr_idx;
-+   
-+        /*-------------------------------- stats */
-+        unsigned int lost;              /* lost rx cells */
-+        /*-------------------------------- other pointers */
-+
-+	/*-------------------------------- TNETA links */
-+	struct atm_dev *more;		/* other TNETA devices */
-+	/*-------------------------------- general information */
-+   
-+        volatile unsigned long *ram;           /* base of phy device */
-+        volatile unsigned long *scheduler;     /* base of scheduler table */
-+        volatile unsigned int *reg;           /* base of sar regs device */
-+        volatile struct tneta_rx_fbrptr *free_buf_ptr;  /* free buffer pointers */
-+        volatile unsigned long *rx_vpi_vci;    /* rx vpi vci table */
-+        volatile struct tneta_tx_dma_state_table *tx_dma_state;  /* tx dma state table */
-+        volatile struct tneta_rx_dma_state_table *rx_dma_state;  /* rx dma state table */
-+        volatile unsigned long *phy;           /* base of phy device */
-+   
-+	int mem;			/* RAM on board (in bytes) */
-+	void * base;		        /* board base address */
-+        unsigned long base_diff;        /* virtual - phy base address */
-+        unsigned int pci_map_size;      /* pci map size of board */     
-+        unsigned char irq;		/* IRQ */
-+	unsigned char bus;		/* PCI stuff */
-+	unsigned char dev_fn;
-+};
-+
-+
-+#define TNETA1570_DEV(d) ((struct tneta1570_dev *) (d)->dev_data)
-+#define TNETA1570_VCC(d) ((struct tneta1570_vcc *) (d)->dev_data)
-+
-+/*---------------------------------------------------------*/
-+
-+/*
-+ * Directly Addressable Registers, memory mapped
-+ */
-+#define TNETA_REG_BASE_OFFSET   0x3200   /* offset PCI base to registers */
-+#define TNETA_CONFIG         0   /* configuration register */
-+#define TNETA_STATUS         1   /* status register */
-+#define TNETA_INT_MASK       2   /* interrupt mask register */ 
-+#define TNETA_S_RAT          6   /* 3L  RAT cycle # rx DMA state table */
-+#define TNETA_S_RGT          7   /* 3H global reass. timer */
-+#define TNETA_RXUNKNOWN      4   /* rx unknown register */
-+#define TNETA_S_TXCOMPLSIZEI 10  /* 5L TX compl. ring size W/ interrupt */
-+#define TNETA_S_TXCOMPLSIZE  11  /* 5H TX compl. ring size W/O interrupt */
-+#define TNETA_S_RXCOMPLSIZEI 12  /* 6L RX completion ring size W/ interrupt */ 
-+#define TNETA_S_RXCOMPLSIZE  13  /* 6H RX completion ring size W/O interrupt */ 
-+#define TNETA_S_FIFO_FREE    14  /* 7L FIFO free-buffer-size */
-+#define TNETA_S_TXSEGSIZE    15  /* 7H Segmentation ring size */
-+#define TNETA_S_AAL_DISCARD  16  /* 8L discarded AAL5 rx cell counter */
-+#define TNETA_S_HEC_ERR      17  /* 8H HEC error counter */
-+#define TNETA_UNKNOWN_P      9   /* Unknown Protocols RX # */
-+#define TNETA_CELL_RXC       10  /* ATM Cells RX'd # */
-+#define TNETA_CELL_TXC       11  /* ATM Cells TX'd # */
-+#define TNETA_S_TXM_RXM      24  /* 12L TX FIFO & RX FIFO max occupancy */ 
-+#define TNETA_S_VCIM         25  /* 12H VCI mask */ 
-+#define TNETA_S_SCHEDSIZE    26  /* 13L scheduler-table-size register */
-+#define TNETA_RESET          14  /* software reset register */
-+
-+#define TNETA_TXCOMPLNOI     128 /* TX completion ring W/O interrupt pointer */
-+#define TNETA_TXCOMPLIRQ     129 /* TX completion ring W/  interrupt pointer */
-+#define TNETA_RXCOMPLNOI     130 /* RX completion ring W/O interrupt pointer */
-+#define TNETA_RXCOMPLIRQ     131 /* RX completion ring W/  interrupt pointer */
-+
-+/* configuration register bits */
-+#define TNETA_R0_UNI       0x2000
-+#define TNETA_R0_MAX_RETRY (0xf << 9)  /* 1111 max retry master */
-+#define TNETA_R0_LOOP      0x0100      /* set to loop-back (reset!, no enable) */
-+#define TNETA_R0_TX_HECERR 0x0080      /* force HEC error */
-+#define TNETA_R0_SMALL_MAP 0x0040
-+#define TNETA_R0_TX_ENABLE 0x0020
-+#define TNETA_R0_RX_ENABLE 0x0010
-+#define TNETA_R0_ENDIAN    (0x0 << 3)  /* 00 little endian */
-+#define TNETA_R0_PERBUFFER 0x002
-+#define TNETA_R0_RAT_ENABL 0x001       /* enable reass. aging timer */
-+#define TNETA_R0_STANDARD_MODE (TNETA_R0_UNI)
-+                             
-+/* status register bits */
-+#define TNETA_R1_PCI_MODE  0x400        /* 32/64 bit */
-+#define TNETA_R1_RX_FREEZE 0x200        /* rx ring overflow */
-+#define TNETA_R1_TX_FREEZE 0x100        /* tx ring overflow */
-+#define TNETA_R1_CP_RX     0x080        /* packet reassembly completed */
-+#define TNETA_R1_RX_IRR    0x040        /* rx-unknown written */
-+#define TNETA_R1_HEC_OVER  0x020        /* HEC error counter overflow */
-+#define TNETA_R1_UP_OVER   0x010        /* unknown proto counter overflow */
-+#define TNETA_R1_APD_OVER  0x008        /* AAL5 PDU discard counter overflow */
-+#define TNETA_R1_ACR_OVER  0x004        /* ATM cell rxd counter overflow */
-+#define TNETA_R1_ACT_OVER  0x002        /* ATM cell txd counter overflow */
-+#define TNETA_R1_CP_TX     0x001        /* packet segmentation completed */
-+#define TNETA_R2_STANDARD_INTS (TNETA_R1_RX_FREEZE | \
-+                                TNETA_R1_TX_FREEZE | \
-+                                TNETA_R1_CP_RX | \
-+                                TNETA_R1_CP_TX)
-+/* control memory map offsets */
-+#define TNETA_SCHED_TABLE             0x0
-+#define TNETA_FREE_BUFFER_POINTERS    0x3800
-+#define TNETA_RX_VPIVCI_DMA_POINTERS  0x4000
-+#define TNETA_TX_DMA_STATE_TABLE      0x8000
-+#define TNETA_RX_DMA_STATE_TABLE      0x10000
-+#define TNETA_SUNI_OFFSET             (0x20000 << 2)
-+
-+/* reserved control memory area */
-+#define RESERVED_LL                   0xc00   /* lower limit */
-+#define RESERVED_UL                   0xe00   /* upper limit */
-+
-+#define TNETA_SUNI_RDREQ              0x00100
-+#define TNETA_SUNI_RD_D_AV            (0x10)
-+
-+#define OWN (0x1 << 31)        /* own bit, set to 1 if owned by tneta1570 */
-+#define SEG_PTR(x) (0x3fffff00 & ((unsigned int)x >> 2))
-+#define BUF_PTR(x) (((unsigned int)x >> 2) & 0x3fffffff)
-+                   /* pointer to tx data buffer header, aligned to 4 byte */
-+
-+/* tx data buffer header */
-+struct tx_buffer_descriptor{
-+     unsigned int control;
-+     unsigned int next_buffer;
-+     unsigned int atm_header;
-+     unsigned int AAL5_control;
-+};
-+
-+/* control word layout */
-+#define TNETA_TXDBH_CTRL_RDY        (0x1 << 31)
-+#define TNETA_TXDBH_CTRL_SOP        (0x1 << 30)
-+#define TNETA_TXDBH_CTRL_EOP        (0x1 << 29)
-+#define TNETA_TXDBH_CTRL_ABORT      (0x1 << 28)
-+#define TNETA_TXDBH_CTRL_AAL0_PTI   (0x0 << 26)  
-+#define TNETA_TXDBH_CTRL_AAL5       (0x1 << 26)  
-+#define TNETA_TXDBH_CTRL_AAL0_NOPTI (0x2 << 26)  
-+#define TNETA_TXDBH_CTRL_INT_NOINT  (0x1 << 25)
-+#define TNETA_TXDBH_CTRL_BUF_OFFSET (0xff << 16)
-+#define TNETA_TXDBH_CTRL_BYTE_COUNT (0xffff << 0)
-+
-+#define AAL5_PDU_INT (TNETA_TXDBH_CTRL_RDY | \
-+                      TNETA_TXDBH_CTRL_SOP | \
-+                      TNETA_TXDBH_CTRL_EOP | \
-+                      TNETA_TXDBH_CTRL_AAL5 | \
-+                      TNETA_TXDBH_CTRL_INT_NOINT)
-+
-+#define AAL0_PDU_INT (TNETA_TXDBH_CTRL_RDY | \
-+                      TNETA_TXDBH_CTRL_SOP | \
-+                      TNETA_TXDBH_CTRL_EOP | \
-+                      TNETA_TXDBH_CTRL_AAL0_PTI)
-+
-+/* ATM header */
-+#define TNETA_TXDBH_ATMH_GFC        (0xff << 24)
-+#define TNETA_TXDBH_ATMH_VPI        (0xff << 16)
-+#define TNETA_TXDBH_ATMH_VCI        (0xffff << 4)
-+#define TNETA_TXDBH_ATMH_PTI        (0x7 << 1)
-+#define TNETA_TXDBH_ATMH_CLP        (0x1 << 0)					
-+	
-+/* AAL5 control */
-+#define TNETA_TXDBH_AAL5_CPCS_UU    (0xff << 24)
-+#define TNETA_TXDBH_AAL5_CPI        (0xffff << 16)
-+#define TNETA_TXDBH_AAL5_USER       (0xffff << 0)
-+
-+/* TX completion rings */
-+#define TNETA_TXCR_OWN         (0x1 << 31)
-+#define TNETA_TXCR_ABORT       (0x1 << 30)
-+#define TNETA_TXCR_BUFADDR     (0x3fffffff << 0)
-+
-+#endif __KERNEL__
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/tneta1570.c	Mon Jun 10 17:36:12 1996
-@@ -0,0 +1,1479 @@
-+/* drivers/atm/tneta1570.c - ti tneta1570 atm driver */
-+ 
-+/* Written 1996 by Rolf Fiedler (rolf.fiedler@infotech.tu-chemnitz.de)
-+ * based on the atm drivers by Werner Almesberger, EPFL LRC
-+ */
-+
-+#include <linux/config.h> /* for extended debugging options */
-+
-+#include <linux/kernel.h>
-+#include <linux/mm.h>
-+#include <linux/bios32.h>
-+#include <linux/pci.h>
-+#include <linux/errno.h>
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/sonet.h>
-+#include <linux/skbuff.h>
-+#include <linux/time.h>
-+#include <linux/sched.h> /* for xtime */
-+#include <linux/delay.h>
-+#include <linux/uio.h>
-+#include <asm/system.h>
-+#include <asm/string.h>
-+#include <asm/byteorder.h>
-+
-+#include "tneta1570.h"
-+#include "suni.h"
-+
-+#define SLOW_DOWN_FACTOR 8
-+
-+/*
-+ * KNOWN BUGS:
-+ *
-+ * doesn't work a bit
-+ */
-+
-+inline void write_sched_tab(struct tneta1570_dev * dev, int i, unsigned int v);
-+inline int read_sched_tab(struct tneta1570_dev * dev, int i);
-+static void dequeue_OAM(struct atm_dev *dev);   /* dequeue OAM cells */
-+static void dequeue_AAL0(struct atm_dev *dev);  /* dequeue AAL0 cells */
-+static void dequeue_AAL5(struct atm_dev *dev);  /* dequeue AAL5 cells */
-+static int alloc_dma(struct atm_vcc *vcc);
-+
-+#if 0
-+#define DPRINTK printk
-+#else
-+#define DPRINTK (void) 
-+#endif
-+
-+#ifndef CONFIG_ATM_TNETA1570_DEBUG
-+
-+
-+#define NULLCHECK(x)
-+
-+#define EVENT(s,a,b)
-+
-+
-+static inline void event_dump(void)
-+{
-+}
-+
-+
-+#else
-+
-+
-+/* 
-+ * NULL pointer checking
-+ */
-+
-+#define NULLCHECK(x) \
-+  if ((unsigned long) (x) < 0x30) printk(#x "==0x%x\n", (int) (x))
-+
-+/*
-+ * Very extensive activity logging. Greatly improves bug detection speed but
-+ * costs a few Mbps if enabled.
-+ */
-+
-+#define EV 64
-+
-+static const char *ev[EV];
-+static unsigned long ev_a[EV],ev_b[EV];
-+static int ec = 0;
-+
-+static void EVENT(const char *s,unsigned long a,unsigned long b)
-+{
-+	ev[ec] = s; 
-+	ev_a[ec] = a;
-+	ev_b[ec] = b;
-+	ec = (ec+1) % EV;
-+}
-+
-+
-+static void event_dump(void)
-+{
-+	int n,i;
-+
-+	for (n = 0; n < EV; n++) {
-+		i = (ec+n) % EV;
-+		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
-+	}
-+}
-+
-+
-+#endif /* CONFIG_ATM_TNETA1570_DEBUG */
-+
-+
-+static int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0,
-+  backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0,
-+  putting = 0;
-+
-+static struct atm_dev *tneta1570_boards = NULL;
-+
-+
-+/*-------------------------------- utilities --------------------------------*/
-+
-+
-+static void dump_mem(struct tneta1570_dev *tneta1570_dev)
-+{
-+}
-+
-+
-+static void dump(struct atm_dev *dev)
-+{
-+    struct tneta1570_dev *tneta1570_dev;
-+
-+    tneta1570_dev = TNETA1570_DEV(dev);
-+    printk("\nFree memory\n");
-+    dump_mem(tneta1570_dev);
-+    printk("TX buffers\n");
-+
-+    printk("RX buffers\n");
-+
-+    printk("----\n");
-+}
-+
-+/* --------------------------memory allocation-------------------------------*/
-+
-+
-+/*
-+ * allocate a skbuff with enough room for the SDU header
-+ */
-+static struct sk_buff *tneta1570_alloc_tx(struct atm_vcc *vcc, unsigned int size)
-+{
-+        struct sk_buff *ptr;
-+   
-+        ptr = alloc_skb(size + 48, GFP_KERNEL);
-+        if(ptr == NULL) return NULL;
-+        skb_reserve(ptr, 48);        /* room for push */
-+        return ptr;
-+}
-+
-+
-+
-+/*----------------------------------- RX ------------------------------------*/
-+
-+/*
-+ * alloc_dma
-+ * find entry in dma state table for this vpi/vci
-+ * if vpi already open increase valid vci range
-+ * else open vpi, set vci range to vci
-+ * return index to dma channel, return 0 on error
-+ */
-+static int alloc_dma(struct atm_vcc *vcc)
-+{
-+        unsigned char unsorted[MAX_VPI], sorted[MAX_VPI];
-+        unsigned int x;
-+        int i;
-+        struct tneta1570_dev *tneta1570_dev;
-+
-+        EVENT(">alloc_rx_dma\n",0,0);
-+   
-+        tneta1570_dev = TNETA1570_DEV(vcc->dev);
-+         
-+        x = tneta1570_dev->rx_vpi_vci[vcc->vpi];
-+    
-+        if(x != 0) {
-+	        x = 0x7fff & (x >> 16);
-+	        return  x + vcc->vci - 0x800;  /* idx = value - offset */
-+	} else {
-+                /* create a new vpi entry */
-+	        /* first, find dma channel range */
-+                for(i=0; i<MAX_VPI+1; i++) {
-+	                if(tneta1570_dev->rx_vpi_vci[i] & OWN) {
-+                                x = tneta1570_dev->rx_vpi_vci[i];
-+			        unsorted[i] = 0x7f & (x >> 24);
-+		        } else {
-+		                unsorted[i] = 0;
-+			}
-+		        sorted[i] = 0;
-+		}
-+                for(i=0; i<MAX_VPI+1; i++) {
-+                        if(unsorted[i] != 0)
-+		                sorted[unsorted[i]-8] = 1;
-+		}
-+                for(i=0; i<MAX_VPI+1; i++) {
-+                        if(unsorted[i] == 0)
-+		                break;
-+		}
-+                if(i==MAX_VPI+1) return 0;
-+                tneta1570_dev->rx_vpi_vci[vcc->vpi] = OWN | ((i+8)<<24) | (MAX_VCI);
-+	        return 256*i + vcc->vci;
-+	}
-+}
-+
-+
-+/*
-+ * let the protocol have a look at the buffer
-+ */
-+static unsigned long tneta1570_fetch(struct atm_vcc *vcc, int i)
-+{
-+  return 0; /* not implemented yet */
-+}
-+
-+/*
-+ * interrupt driven dequeue for rx buffer
-+ * -> for each and every post in the completion ring do
-+ *    increment completion ring ptr (modulo RXCMPLR_SIZE_IRQ)
-+ *    get skb address from buffer address
-+ *    get free-buffer ring address from tneta1570_vcc
-+ *    prepare skb with header information
-+ *    vcc->push skb
-+ *    alloc (ATOMIC) new skb for this vci, put in free-buffer ring
-+ *    increment free-buffer ring index (modulo FBR_SIZE)
-+ */
-+static void dequeue_rx(struct atm_dev * dev)
-+{
-+        struct tneta1570_dev * tneta1570_dev;
-+
-+        EVENT(">dequeue_rx\n",0,0);
-+        NULLCHECK(dev);
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+	NULLCHECK(tneta1570_dev);
-+
-+        /* find here completion ring entries */
-+        EVENT(">completion ring post: %08x, %d\n",
-+ 	        (unsigned int)&RX_CMPL_R_IRQ(tneta1570_dev).atm_header,
-+	        tneta1570_dev->rxcmpl_ring_idx_irq); 
-+
-+        while(!(RX_CMPL_R_IRQ(tneta1570_dev).control & OWN)) { 
-+                if((RX_CMPL_R_IRQ(tneta1570_dev).control & 0xff) == 0) {
-+	                dequeue_OAM(dev);
-+                } else { 
-+		        if((RX_CMPL_R_IRQ(tneta1570_dev).error & AAL5_IND)) {
-+	                        dequeue_AAL5(dev);
-+                        } else {
-+	                        dequeue_AAL0(dev);
-+
-+			}
-+		}
-+                RX_CMPL_R_IRQ(tneta1570_dev).control = OWN;
-+                tneta1570_dev->rxcmpl_ring_idx_irq++;
-+                tneta1570_dev->rxcmpl_ring_idx_irq %= RXCMPLR_SZ_IRQ;
-+	}
-+        return ;
-+}
-+
-+static void dequeue_OAM(struct atm_dev *dev)  /* dequeue AAL0 cells */
-+{
-+        struct tneta1570_dev * tneta1570_dev;
-+        struct sk_buff *skb, *new_skb;
-+        unsigned int *p, *fbr;
-+
-+        EVENT(">dequeue_rx_OAM\n",0,0);
-+        NULLCHECK(dev);
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+	NULLCHECK(tneta1570_dev);
-+
-+/*        if(vcc->push_oam) printk(" PushOAM is present.\n"); */
-+         
-+        p = (unsigned int *)(RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr << 2);
-+        skb = (struct sk_buff *)(*(p-1));  /* get skb */
-+	      
-+        /* get new buffer before dequeueing old
-+         */
-+        new_skb = alloc_skb(MAX_AAL0_PDU+RX_HDR, GFP_ATOMIC);
-+        if(new_skb == NULL) {
-+        /* add just received buffer to free list, drop pdu :-( */
-+                fbr = tneta1570_dev->free_buf_ptr[0].buf_ptr;
-+                fbr[tneta1570_dev->oam_fbr_idx] = OWN | ((int)p >> 2);
-+                tneta1570_dev->oam_fbr_idx++;
-+                tneta1570_dev->oam_fbr_idx %= AAL0_BUFS;
-+                return;
-+	}
-+	   
-+        /* add newly allocated buffer to free list */
-+        fbr = tneta1570_dev->free_buf_ptr[0].buf_ptr;
-+        fbr[tneta1570_dev->oam_fbr_idx] = OWN | ((unsigned int)(new_skb->data+4) >> 2);
-+	*(struct sk_buff **)(new_skb->data) = new_skb;
-+        tneta1570_dev->oam_fbr_idx++;
-+        tneta1570_dev->oam_fbr_idx %= AAL0_BUFS;
-+	   
-+        /* push received buffer to protocol */
-+		   
-+	skb->data[16] = skb->data[12];
-+	skb->data[17] = skb->data[13];
-+	skb->data[18] = skb->data[14];
-+	skb->data[19] = skb->data[15];
-+	skb->data += 4*4;  /* skip header */
-+	skb->len  = 52;
-+        /* should check for multiple buffers @@@@ */
-+	skb->tail = skb->data + skb->len;
-+		   
-+        if (/* vcc->push */ 1) {
-+	        dev_kfree_skb(skb, GFP_ATOMIC);
-+                printk(" Don't know how to call push !\n");
-+	        /* vcc->push(vcc,skb); */
-+	} else {
-+	        dev_kfree_skb(skb, GFP_ATOMIC);
-+	        printk(DEV_LABEL "(itf %d) No push in protocol.\n", 
-+		       dev->number); 
-+	}
-+}
-+
-+
-+static void dequeue_AAL0(struct atm_dev *dev)  /* dequeue AAL0 cells */
-+{
-+        struct tneta1570_dev * tneta1570_dev;
-+        struct tneta1570_vcc * tneta1570_vcc;
-+   	struct atm_vcc *vcc;
-+	struct sk_buff *skb, *new_skb;
-+        unsigned int *p, v, *fbr;
-+   
-+        EVENT(">dequeue_rx_AAL0\n",0,0);
-+        NULLCHECK(dev);
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+	NULLCHECK(tneta1570_dev);
-+   
-+        p = (unsigned int *)(RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr << 2);
-+        skb = (struct sk_buff *)(*(p-1));  /* get skb */
-+	      
-+        vcc = skb->atm.vcc;
-+        NULLCHECK(vcc);
-+        tneta1570_vcc = TNETA1570_VCC(vcc);
-+        NULLCHECK(tneta1570_vcc);   
-+        /* get new buffer before dequeueing old
-+         * if error in peek drop pdu
-+         */
-+        new_skb = vcc->peek(vcc, MAX_AAL0_PDU+RX_HDR, NULL);
-+        if(new_skb == NULL) {
-+        /* add just received buffer to free list, drop pdu :-( */
-+                v = RX_CMPL_R_IRQ(tneta1570_dev).control;
-+                fbr = tneta1570_dev->free_buf_ptr[0xff & v].buf_ptr;
-+                fbr[tneta1570_vcc->fbr_idx] = OWN | ((int)p >> 2);
-+                tneta1570_vcc->fbr_idx++;
-+                tneta1570_vcc->fbr_idx %= AAL0_BUFS;
-+                return;
-+	}
-+	   
-+        /* add newly allocated buffer to free list */
-+                       
-+	v = RX_CMPL_R_IRQ(tneta1570_dev).control;
-+        fbr = tneta1570_dev->free_buf_ptr[0xff & v].buf_ptr;
-+        fbr[tneta1570_vcc->fbr_idx] = OWN | ((unsigned int)(new_skb->data+4) >> 2);
-+	*(struct sk_buff **)(new_skb->data) = new_skb;
-+        new_skb->atm.vcc = vcc;      /* link vcc info */
-+        tneta1570_vcc->fbr_idx++;
-+        tneta1570_vcc->fbr_idx %= AAL0_BUFS;
-+	   
-+        /* push received buffer to protocol */
-+		   
-+	skb->data[16] = skb->data[12];
-+	skb->data[17] = skb->data[13];
-+	skb->data[18] = skb->data[14];
-+	skb->data[19] = skb->data[15];
-+	skb->data += 4*4;  /* skip header */
-+	skb->len  = 52;
-+        /* should check for multiple buffers @@@@ */
-+	skb->tail = skb->data + skb->len;
-+        skb->free = 1;   /* set free flag, is there need for it? */
-+		   
-+        if (vcc->push) {
-+                vcc->push(vcc,skb);
-+	} else {
-+	        printk(DEV_LABEL "(itf %d) No push in protocol.\n", 
-+		       dev->number); 
-+	}
-+        /* update statistics */
-+        vcc->stats->rx++;
-+rx_dequeued++;
-+}
-+  
-+   
-+   
-+static void dequeue_AAL5(struct atm_dev *dev)   /* dequeue AAL5 PDU */
-+{
-+        struct tneta1570_dev *tneta1570_dev;
-+        struct tneta1570_vcc * tneta1570_vcc;
-+        struct atm_vcc *vcc;
-+	struct sk_buff *skb, *new_skb;
-+        unsigned int *p, v, *fbr;
-+   
-+        EVENT(">dequeue_rx_AAL5\n",0,0);
-+        NULLCHECK(dev);
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+	NULLCHECK(tneta1570_dev);
-+   
-+        p = (unsigned int *)(RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr << 2);
-+        skb = (struct sk_buff *)(*(p-1));  /* get skb */
-+
-+        DPRINTK(" p %08x, skb %08x, head %08x, err %08x, sop %08x, trailer %08x, idx %08x",
-+	         p, skb, 
-+	         RX_CMPL_R_IRQ(tneta1570_dev).atm_header,
-+	         RX_CMPL_R_IRQ(tneta1570_dev).error, 
-+	         RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr,
-+	         RX_CMPL_R_IRQ(tneta1570_dev).trailer,
-+	         RX_CMPL_R_IRQ(tneta1570_dev).control);
-+   
-+        vcc = skb->atm.vcc;
-+        DPRINTK(" vcc %08x", vcc);
-+   
-+        NULLCHECK(vcc);
-+        tneta1570_vcc = TNETA1570_VCC(vcc);
-+        NULLCHECK(tneta1570_vcc);   
-+        /* get new buffer before dequeueing old
-+         * if error in peek drop pdu
-+         */
-+        new_skb = vcc->peek(vcc, MAX_AAL5_PDU+RX_HDR, NULL);
-+        if(new_skb == NULL) {
-+        /* add just received buffer to free list :-( */
-+                v = RX_CMPL_R_IRQ(tneta1570_dev).control;
-+	        fbr = tneta1570_dev->free_buf_ptr[0xff & v].buf_ptr;
-+	        fbr[tneta1570_vcc->fbr_idx] = OWN | ((int)p >> 2);
-+	        tneta1570_vcc->fbr_idx++;
-+	        tneta1570_vcc->fbr_idx %= AAL5_BUFS;
-+	        return;
-+	}
-+        /* add newly allocated buffer to free list */
-+                        
-+	v = RX_CMPL_R_IRQ(tneta1570_dev).control;
-+        fbr = tneta1570_dev->free_buf_ptr[v].buf_ptr;
-+        fbr[tneta1570_vcc->fbr_idx] = OWN | ((unsigned int)(new_skb->data+4) >> 2);
-+	*(struct sk_buff **)(new_skb->data) = new_skb;
-+        new_skb->atm.vcc = vcc;   /* link context info */
-+        tneta1570_vcc->fbr_idx++;
-+        tneta1570_vcc->fbr_idx %= AAL5_BUFS;
-+	   
-+        /* push received buffer to protocol */
-+	skb->data += 5*4;  /* skip header */
-+	skb->len  = RX_CMPL_R_IRQ(tneta1570_dev).trailer & 0xffff;
-+        /* should check for multiple buffers @@@@ */
-+	skb->tail = skb->data + skb->len;
-+        skb->free = 1;   /* set free flag, is there need for it? */
-+   
-+        if (vcc->push) {
-+                vcc->push(vcc,skb);
-+	} else {
-+	        printk(DEV_LABEL "(itf %d) No push in protocol.\n", 
-+	               dev->number); 
-+	}
-+        /* update statistics */
-+        vcc->stats->rx++;
-+rx_dequeued++;
-+}
-+
-+   
-+/*
-+ * --- bring rx up ---
-+ * init vpi/vci table - well, already done by tneta1570_init
-+ * kmalloc completion rings 
-+ * write ptrs to completion rings into sar regs
-+ * init completion rings to all OWN
-+ * write completion ring related data to device structure, set index to 0
-+ * init dma channels 0, 1, 2 for reception of OAM cells
-+ */
-+static int start_rx(struct atm_dev *dev)
-+{
-+        int i;
-+        unsigned int x;
-+        unsigned int *ptr;
-+        struct sk_buff *buf;
-+        struct tneta1570_dev *tneta1570_dev;
-+
-+        EVENT(">start_rx\n",0,0);
-+        tneta1570_dev = TNETA1570_DEV(dev);
-+   	tneta1570_dev->lost = 0;
-+   
-+        /* init rx completion rings */
-+        tneta1570_dev->rxcmpl_ring =
-+                       kmalloc(2*RXCMPLR_SZ_IRQ*sizeof(struct tneta_rx_compl),
-+			       GFP_KERNEL);
-+        DPRINTK("RX_CMPL_R->%08x", tneta1570_dev->rxcmpl_ring);
-+        if(!tneta1570_dev->rxcmpl_ring) {
-+	        printk(DEV_LABEL "(itf %d) malloc on rx start failed.\n", dev->number);
-+	        return -ENOMEM;
-+	}
-+        /* align completion-ring with irq to its size */
-+        x = (unsigned int)(&tneta1570_dev->rxcmpl_ring[RXCMPLR_SZ_IRQ]);
-+        tneta1570_dev->rxcmplringptr_irq = (struct tneta_rx_compl *)
-+                     (x & (~(RXCMPLR_SZ_IRQ*sizeof(struct tneta_rx_compl) - 1)));
-+        /* the rest is for the completion ring w/o irq */   
-+        if((tneta1570_dev->rxcmplringptr_irq - RXCMPLR_SZ_NOI) 
-+	   > tneta1570_dev->rxcmpl_ring) {
-+                tneta1570_dev->rxcmplringptr_noi = 
-+	                tneta1570_dev->rxcmplringptr_irq - RXCMPLR_SZ_NOI;
-+        } else {
-+                tneta1570_dev->rxcmplringptr_noi = 
-+	                tneta1570_dev->rxcmplringptr_irq + RXCMPLR_SZ_IRQ;
-+        }
-+        EVENT(">init rx completion ring irq\n",0,0);
-+        for(i=0; i<RXCMPLR_SZ_IRQ; i++)
-+                tneta1570_dev->rxcmplringptr_irq[i].control = OWN;
-+        EVENT(">init tx completion ring noi\n",0,0);
-+        for(i=0; i<RXCMPLR_SZ_NOI; i++)
-+                tneta1570_dev->rxcmplringptr_noi[i].control = OWN;
-+   
-+        tneta1570_dev->rxcmpl_ring_idx_noi = 0;
-+        tneta1570_dev->rxcmpl_ring_idx_irq = 0;
-+
-+        SAR_REG_SHORT(tneta1570_dev, TNETA_S_RXCOMPLSIZEI) = RXCMPLR_SZ_IRQ-1; 
-+        SAR_REG_SHORT(tneta1570_dev, TNETA_S_RXCOMPLSIZE)  = RXCMPLR_SZ_NOI-1; 
-+        SAR_REG_WORD(tneta1570_dev, TNETA_RXCOMPLNOI) = (unsigned int)tneta1570_dev->rxcmplringptr_noi;
-+        SAR_REG_WORD(tneta1570_dev, TNETA_RXCOMPLIRQ) = (unsigned int)tneta1570_dev->rxcmplringptr_irq;
-+
-+        /* init dma 0,1,2 for oam */
-+   
-+        /* alloc memory for oam fbr & buffers */
-+        ptr = kmalloc(AAL0_BUFS*sizeof(struct tneta_rx_fbrptr), GFP_KERNEL);
-+        if(ptr == NULL) printk(DEV_LABEL "PANIC on start rx \n");
-+        for(i=0; i<AAL0_BUFS; i++) {
-+	        buf = alloc_skb(MAX_AAL0_PDU+RX_HDR, GFP_KERNEL);
-+                if(buf == NULL) printk(DEV_LABEL "PANIC on start rx \n");
-+                *(struct sk_buff **)(buf->data) = buf;  /* link */
-+                ptr[i] = OWN | ((unsigned int)(buf->data + 4) >> 2);
-+	}
-+
-+        tneta1570_dev->oam_fbr_idx = 0;
-+        /* oam cells use fbr 0 */
-+        tneta1570_dev->free_buf_ptr[0].buf_ptr = ptr;
-+        tneta1570_dev->free_buf_ptr[0].buf_size = FBR_AAL0_32;
-+   
-+        tneta1570_dev->rx_dma_state[0].control = RX_DMA_CONTROL_AAL0;
-+        tneta1570_dev->rx_dma_state[0].AAL0_cells = MAX_AAL0_CELLS;
-+        tneta1570_dev->rx_dma_state[0].rx_timeout = RX_TIME_OUT;
-+        tneta1570_dev->rx_dma_state[0].dma_on_idx = DMA_ON + 0; 
-+        tneta1570_dev->rx_dma_state[1].control = RX_DMA_CONTROL_AAL0;
-+        tneta1570_dev->rx_dma_state[1].AAL0_cells = MAX_AAL0_CELLS;
-+        tneta1570_dev->rx_dma_state[1].rx_timeout = RX_TIME_OUT;
-+        tneta1570_dev->rx_dma_state[1].dma_on_idx = DMA_ON + 0; 
-+        tneta1570_dev->rx_dma_state[2].control = RX_DMA_CONTROL_AAL0;
-+        tneta1570_dev->rx_dma_state[2].AAL0_cells = MAX_AAL0_CELLS;
-+        tneta1570_dev->rx_dma_state[2].rx_timeout = RX_TIME_OUT;
-+        tneta1570_dev->rx_dma_state[2].dma_on_idx = DMA_ON + 0; 
-+
-+        SAR_REG_WORD(tneta1570_dev, TNETA_CONFIG) |= TNETA_R0_RX_ENABLE;
-+
-+        return 0;
-+}
-+
-+/* 
-+ * open vpi/vci for rx
-+ * alloc 16 words for free-buffer ring
-+ * alloc 16 skbs, size: if AAL5
-+ *                         IP-MTU + a little something (64K SDUs need multiple bufs)
-+ *                      else
-+ *                         48 + a little something
-+ * enter pointers to skbs in free-buffer ring
-+ * find unused free-buffer ring-pointer table entry
-+ * put pointer to free-buffer ring in ring-pointer table & tneta1570_vcc
-+ * check if vpi has already alloc'd a dma table range
-+ *       if not, alloc range in dma table for vpi (size is 256*8 words)
-+ * if AAL5 -> set dma state table to AAL5
-+ * else use counter terminated AAL0 (cell count 1)
-+ * prepare dma state table entry for this vpi/vci (may turn rx for this vci on)
-+ * if range < vci -> set range to vci (max) (this definitely turns it on)
-+ */
-+static int open_rx(struct atm_vcc *vcc)
-+{
-+        int i, dma_idx, fbr_idx;
-+        struct sk_buff *skb;
-+        unsigned int *ptr;
-+        struct tneta1570_dev *tneta1570_dev;
-+        struct tneta1570_vcc *tneta1570_vcc;
-+
-+        EVENT(">open_rx\n",0,0);
-+   
-+        tneta1570_dev = TNETA1570_DEV(vcc->dev);
-+        tneta1570_vcc = TNETA1570_VCC(vcc);
-+
-+   	tneta1570_vcc->rx_wait = NULL;
-+
-+	tneta1570_vcc->dma_channel = alloc_dma(vcc);
-+        if(!tneta1570_vcc->dma_channel) return -1;
-+
-+        dma_idx = tneta1570_vcc->dma_channel;
-+   
-+        for(fbr_idx = 0; fbr_idx<MAX_FBR_ENTRIES; fbr_idx++)
-+	        if(tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr == 0)
-+	                break;
-+        if(fbr_idx == MAX_FBR_ENTRIES) return -1;
-+	   
-+        if(vcc->aal == ATM_AAL0) {
-+                /* alloc memory for fbr & buffers */
-+                ptr = kmalloc(AAL0_BUFS*sizeof(struct tneta_rx_fbrptr),
-+		              GFP_KERNEL);
-+                if(ptr == NULL) printk(DEV_LABEL "PANIC on open rx \n");
-+                for(i=0; i<AAL0_BUFS; i++) {
-+	                skb = vcc->peek(vcc, MAX_AAL0_PDU+RX_HDR, NULL);
-+                        if(skb == NULL) printk(DEV_LABEL "PANIC on open rx \n");
-+		        *(struct sk_buff **)(skb->data) = skb; /* link */
-+		        skb->atm.vcc = vcc; /* link vcc info */
-+                        ptr[i] = OWN | ((unsigned int)(skb->data+4) >> 2);
-+	        }
-+
-+   	        tneta1570_vcc->fbr_idx = 0;
-+
-+	        tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr = ptr;
-+                tneta1570_dev->free_buf_ptr[fbr_idx].buf_size = FBR_AAL0_32;
-+   
-+                tneta1570_dev->rx_dma_state[dma_idx].control = RX_DMA_CONTROL_AAL0;
-+                tneta1570_dev->rx_dma_state[dma_idx].AAL0_cells = MAX_AAL0_CELLS;
-+                tneta1570_dev->rx_dma_state[dma_idx].rx_timeout = RX_TIME_OUT;
-+                tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx = DMA_ON + fbr_idx;
-+	   
-+	} else if(vcc->aal == ATM_AAL5) {
-+                /* alloc memory for fbr & buffers */
-+                ptr = kmalloc(AAL5_BUFS*sizeof(struct tneta_rx_fbrptr),
-+		              GFP_KERNEL);
-+                if(ptr == NULL) printk(DEV_LABEL "PANIC on open rx \n");
-+                for(i=0; i<AAL5_BUFS; i++) {
-+	                skb = vcc->peek(vcc, MAX_AAL5_PDU+RX_HDR, NULL);
-+                        if(skb == NULL) printk(DEV_LABEL "PANIC on open rx \n");
-+		        *(struct sk_buff **)(skb->data) = skb; /* link */
-+		        skb->atm.vcc = vcc;  /* link vcc info */
-+                        ptr[i] = OWN | ((unsigned int)(skb->data+4) >> 2);
-+	        }
-+
-+	        tneta1570_vcc->fbr_idx = 0;
-+	   
-+	        tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr = ptr;
-+                tneta1570_dev->free_buf_ptr[fbr_idx].buf_size = FBR_AAL5_16;
-+   
-+                tneta1570_dev->rx_dma_state[dma_idx].control = RX_DMA_CONTROL_AAL5;
-+                tneta1570_dev->rx_dma_state[dma_idx].rx_timeout = RX_TIME_OUT;
-+                tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx = DMA_ON + fbr_idx;
-+	   
-+	} else return -1;
-+        return 0;
-+}
-+
-+/* 
-+ * close vpi/vci for rx
-+ * 
-+ * get free-buffer ring table address
-+ * clear dma state table entry (rx for vci off)
-+ * get free-buffer ring address
-+ * wait for rx to drain
-+ * free all remaining skbs (the ones with own enabled, e.g. all)
-+ * free free-buffer ring
-+ * set free-buffer-ring ptr to 0 to indicate 'freeness'
-+ * check if vpi has another vci enabled
-+ * -> yes:    if other_vci > this_vci -> do nothing
-+ *            else                    -> set max vci to highest other vci
-+ * -> no: set vci in vpi/vci table to 0
-+ */
-+static void close_rx(struct atm_vcc *vcc)
-+{
-+        int i, fbr_idx, dma_idx, x;
-+        struct sk_buff *skb;
-+        unsigned int *buf, *ptr;
-+        struct tneta1570_dev *tneta1570_dev;
-+        struct tneta1570_vcc *tneta1570_vcc;
-+
-+        EVENT(">close_rx\n",0,0);
-+   
-+        tneta1570_dev = TNETA1570_DEV(vcc->dev);
-+        tneta1570_vcc = TNETA1570_VCC(vcc);
-+
-+        dma_idx = tneta1570_vcc->dma_channel;
-+        fbr_idx = tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx & 0xff;
-+        /* wait for EOP */
-+        while(tneta1570_dev->rx_dma_state[dma_idx].control & OWN);
-+        tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx = 0; /* off */
-+        /* mark as empty */
-+        ptr = tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr;
-+        tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr = 0;  /* mark fbr free */
-+        x = tneta1570_dev->free_buf_ptr[fbr_idx].buf_size;
-+        x = x >> 10;  /* ring size */
-+        x &= 0x3f; 
-+        x++;
-+        x = 16 * x;
-+        for(i=0; i<x; i++) {
-+	        if(ptr[i] && OWN) {
-+	                buf = (unsigned int *)(ptr[i] << 2);
-+	                skb = (struct sk_buff *)(*(buf-1));
-+	                skb->free = 1; /* ???? */
-+                        kfree_skb(skb, FREE_READ);
-+		}
-+        }
-+        kfree(ptr);
-+
-+        /* if last vci on this vpi is closed, close vpi */
-+
-+}
-+
-+
-+/*----------------------------------- TX ------------------------------------*/
-+
-+/* 
-+ * -- bring tx up --
-+ * init scheduler table to all 0s
-+ * kmalloc tx completion rings
-+ * write ptrs to completion rings into sar regs
-+ * init completion rings to all 0x80000000s
-+ * write completion ring related data to device structure, set index to 0
-+ * enable tx in sar
-+ */
-+static int start_tx(struct atm_dev *dev)
-+{
-+        int i;
-+        unsigned int x, *seg_ring, *seg_ring_mptr;
-+        struct tneta1570_dev *tneta1570_dev;
-+
-+        EVENT(">start_tx\n",0,0);
-+   
-+        tneta1570_dev = TNETA1570_DEV(dev);
-+     
-+        /* init tx completion rings */
-+        tneta1570_dev->txcmpl_ring=kmalloc(2*TXCMPLR_SZ_IRQ*4, GFP_KERNEL);
-+        if(!tneta1570_dev->txcmpl_ring) {
-+	        printk(DEV_LABEL "(itf %d) malloc on tx start failed.\n", dev->number);
-+	        return -ENOMEM;
-+	}
-+        DPRINTK("TX_CMPL_R->%08x", tneta1570_dev->txcmpl_ring);
-+        /* align completion-ring with irq to its size */
-+        x = (unsigned int)(&tneta1570_dev->txcmpl_ring[TXCMPLR_SZ_IRQ]);
-+        tneta1570_dev->txcmplringptr_irq = (unsigned int *)(x & (~(TXCMPLR_SZ_IRQ*4 - 1)));
-+        /* the rest is for the completion ring w/o irq */   
-+        if((tneta1570_dev->txcmplringptr_irq - TXCMPLR_SZ_NOI) > tneta1570_dev->txcmpl_ring) {
-+                tneta1570_dev->txcmplringptr_noi = tneta1570_dev->txcmplringptr_irq - TXCMPLR_SZ_NOI;
-+        } else {
-+                tneta1570_dev->txcmplringptr_noi = tneta1570_dev->txcmplringptr_irq + TXCMPLR_SZ_IRQ;
-+        }
-+        EVENT(">init tx completion ring irq\n",0,0);
-+        for(i=0; i<TXCMPLR_SZ_IRQ; i++)
-+                tneta1570_dev->txcmplringptr_irq[i] = OWN;
-+        EVENT(">init tx completion ring noi\n",0,0);
-+        for(i=0; i<TXCMPLR_SZ_NOI; i++)
-+                tneta1570_dev->txcmplringptr_noi[i] = OWN;
-+   
-+        tneta1570_dev->txcmpl_ring_idx_noi = 0;
-+        tneta1570_dev->txcmpl_ring_idx_irq = 0;
-+
-+        /* dma state reread bug fix - allocate dma 1 */
-+        seg_ring_mptr=kmalloc(2*TX_SEG_RING_SIZE*4, GFP_KERNEL);
-+        if(!seg_ring_mptr) {
-+	        printk(DEV_LABEL "(itf %d) malloc on tx open failed.\n",
-+		       dev->number);
-+	        return -ENOMEM;
-+	}
-+        x = (unsigned int)(seg_ring_mptr + TX_SEG_RING_SIZE);
-+        seg_ring = (unsigned int *)(x & ~(TX_SEG_RING_SIZE * 4 - 1));
-+
-+        for(i=0; i<TX_SEG_RING_SIZE; i++) 
-+                seg_ring[i]=0;            /* turn off - high cost :-( */
-+
-+        write_sched_tab(tneta1570_dev, 0, 1);
-+        tneta1570_dev->tx_dma_state[1].dma_state_flag = (OWN | SEG_PTR(seg_ring));
-+
-+        /* init registers */ 
-+        SAR_REG_SHORT(tneta1570_dev, TNETA_S_TXCOMPLSIZEI) = TXCMPLR_SZ_IRQ - 1; 
-+        SAR_REG_SHORT(tneta1570_dev, TNETA_S_TXCOMPLSIZE)  = TXCMPLR_SZ_NOI - 1; 
-+        SAR_REG_SHORT(tneta1570_dev, TNETA_S_TXSEGSIZE)  = TX_SEG_RING_SIZE - 1; 
-+        SAR_REG_SHORT(tneta1570_dev, TNETA_S_SCHEDSIZE) = 2 + SLOW_DOWN_FACTOR;
-+   
-+        SAR_REG_WORD(tneta1570_dev, TNETA_TXCOMPLNOI) = (unsigned int)tneta1570_dev->txcmplringptr_noi;
-+        SAR_REG_WORD(tneta1570_dev, TNETA_TXCOMPLIRQ) = (unsigned int)tneta1570_dev->txcmplringptr_irq;
-+
-+        SAR_REG_WORD(tneta1570_dev, TNETA_CONFIG) |= TNETA_R0_TX_ENABLE;        
-+
-+        return 0;
-+}
-+ 
-+/*
-+ * -- open vpi/vci --
-+ * find free entry in scheduler table
-+ * init tx_dma_state table for this vpi/vci
-+ * kmalloc tx_seg_ring, init with all 0s
-+ * add entry to scheduler table
-+ */
-+static int open_tx(struct atm_vcc *vcc)
-+{
-+        int scheduler_idx, i;
-+        unsigned int x;
-+        struct tneta1570_dev *tneta1570_dev;
-+        struct tneta1570_vcc *tneta1570_vcc;
-+
-+        EVENT(">open_tx\n",0,0);
-+   
-+        tneta1570_dev = TNETA1570_DEV(vcc->dev);
-+        tneta1570_vcc = TNETA1570_VCC(vcc);
-+
-+   	tneta1570_vcc->tx_wait = NULL;
-+
-+        if (vcc->txtp.class == ATM_NONE) return 0;
-+
-+        tneta1570_vcc->seg_ring_mptr=kmalloc(2*TX_SEG_RING_SIZE*4, GFP_KERNEL);
-+        if(!tneta1570_vcc->seg_ring_mptr) {
-+	        printk(DEV_LABEL "(itf %d) malloc on tx open failed.\n",
-+		       vcc->dev->number);
-+	        return -ENOMEM;
-+	}
-+        /* align seg-ring to its size */
-+        x = (unsigned int)(tneta1570_vcc->seg_ring_mptr + TX_SEG_RING_SIZE);
-+        tneta1570_vcc->seg_ring = (unsigned int *)(x & ~(TX_SEG_RING_SIZE * 4 - 1));
-+
-+        EVENT("> seg ring is at phy %x\n", (unsigned int)tneta1570_vcc->seg_ring,0);
-+        for(i=0; i<TX_SEG_RING_SIZE; i++) 
-+                tneta1570_vcc->seg_ring[i]=0;
-+        tneta1570_vcc->seg_ring_idx = 0;
-+
-+        /* find free scheduler table entry */
-+        scheduler_idx=0;
-+        while(read_sched_tab(tneta1570_dev, scheduler_idx) != 0) {
-+                scheduler_idx++;
-+                if(scheduler_idx >= MAX_SCHED_ENTRIES) {
-+		        printk(DEV_LABEL "(itf %d) tx scheduler full on open.\n", 
-+			       vcc->dev->number);
-+                        return -ENOMEM;
-+	        }
-+	}
-+
-+        if((scheduler_idx < SLOW_DOWN_FACTOR) && 
-+	   (scheduler_idx + SLOW_DOWN_FACTOR < MAX_SCHED_ENTRIES))
-+                SAR_REG_SHORT(tneta1570_dev, TNETA_S_SCHEDSIZE) =
-+                                             scheduler_idx + SLOW_DOWN_FACTOR;
-+        else SAR_REG_SHORT(tneta1570_dev, TNETA_S_SCHEDSIZE) =scheduler_idx;
-+
-+        tneta1570_vcc->scheduler_idx = scheduler_idx;
-+        write_sched_tab(tneta1570_dev, scheduler_idx, scheduler_idx+1); /* 1 in entry 0 */
-+   
-+        tneta1570_dev->tx_dma_state[scheduler_idx+1].dma_state_flag = 
-+             (OWN | SEG_PTR(tneta1570_vcc->seg_ring));
-+
-+        tneta1570_vcc->txing = 0;  /* init txing flag */
-+
-+        return 0;
-+}
-+
-+/* 
-+ * -- close vpi/vci --
-+ * remove scheduler table entry
-+ * free tx_seg_ring
-+ */
-+static void close_tx(struct atm_vcc *vcc)
-+{
-+        struct tneta1570_dev *tneta1570_dev;
-+        struct tneta1570_vcc *tneta1570_vcc;
-+
-+        EVENT(">close_tx\n",0,0);
-+   
-+        tneta1570_dev = TNETA1570_DEV(vcc->dev);
-+        tneta1570_vcc = TNETA1570_VCC(vcc);
-+
-+        while(tneta1570_vcc->txing) {
-+	     sleep_on(&tneta1570_vcc->tx_wait);  /* wait for tx to drain */
-+	}
-+  
-+        /* remove ptr to segmentation ring */
-+        
-+        tneta1570_dev->tx_dma_state[tneta1570_vcc->scheduler_idx+1].dma_state_flag = 0;
-+        write_sched_tab(tneta1570_dev, tneta1570_vcc->scheduler_idx, 0); 
-+   
-+        kfree(tneta1570_vcc->seg_ring_mptr);    
-+}
-+
-+/* 
-+ * -- send skb --
-+ * prepare buffer with header data 
-+ * set entry in segmentation ring for vpi/vci and increment index
-+ * smile ;-)
-+ */
-+static int do_tx(struct sk_buff * skb)
-+{
-+        struct atm_vcc *vcc;
-+        struct tneta1570_dev *tneta1570_dev;
-+        struct tneta1570_vcc *tneta1570_vcc;
-+        unsigned int seg_ring_entry, i;
-+        unsigned int *buffer;
-+   
-+        NULLCHECK(skb);
-+        EVENT(">do_tx: skb=0x%lx, %d bytes\n",(unsigned long) skb,skb->len);
-+        vcc = skb->atm.vcc;
-+        NULLCHECK(vcc);
-+        tneta1570_dev = TNETA1570_DEV(vcc->dev);
-+        NULLCHECK(tneta1570_dev);
-+        tneta1570_vcc = TNETA1570_VCC(vcc);
-+        if ((unsigned long) skb->data & 2) {
-+                printk(DEV_LABEL "(itf %d): VCI %d has mis-aligned TX data\n",
-+                    vcc->dev->number,vcc->vci);
-+		dev_kfree_skb(skb,FREE_WRITE);
-+	        return -EINVAL;
-+	}
-+        /* ping seems to be a problem here, its not using my alloc function */
-+        /* prepare buffer descriptor */
-+        if((skb->data - skb->head) < 24) {
-+                DPRINTK(DEV_LABEL "(itf %d): skbuff push impossible (%d)\n",
-+                    vcc->dev->number, skb->data - skb->head);
-+                /* copy the data if push not possible */
-+                if(!(buffer=kmalloc(skb->len + 24, GFP_ATOMIC))) {
-+                        printk(DEV_LABEL "(itf %d): malloc on send failed.\n",
-+                               vcc->dev->number);
-+			dev_kfree_skb(skb,FREE_WRITE);
-+                        return -ENOMEM;
-+	        }
-+                EVENT(">tx mem alloc'd at %08x\n", (unsigned int)buffer,0);
-+                NULLCHECK(skb->data);
-+                if(vcc->aal == ATM_AAL0) {
-+                        buffer[3] = 0;
-+                        buffer[4] = ((unsigned int *)skb->data)[0];  /* copy atm header */
-+                        buffer[5] = 0;
-+                        for(i=0; i<skb->len/4+1; i++)
-+		                buffer[6+i] = ((unsigned int *)skb->data)[1+i];
-+	                buffer[2] = AAL0_PDU_INT | (0xffff & skb->len); /* offset 0 */
-+	        } else {
-+                        buffer[3] = 0;
-+                        buffer[4] = (vcc->vpi << 20) | (vcc->vci << 4);  /* prepare atm header */
-+                        buffer[5] = 0;
-+                        for(i=0; i<skb->len/4+1; i++)
-+		                buffer[6+i] = ((unsigned int *)skb->data)[i];
-+	                buffer[2] = AAL5_PDU_INT | (0xffff & skb->len);
-+                }
-+                buffer[1] = (unsigned int)skb;
-+                buffer[0] = 0; /* push size */
-+                EVENT(">data copied\n",0,0);
-+	} else { /* push skb and put header in front of sdu */
-+                if(vcc->aal == ATM_AAL0) { /* sdu contains header */
-+                        buffer = (unsigned int *)skb_push(skb, 20);  /* make room for 1+4 words header */
-+                        buffer[3] = 0;
-+                        buffer[4] = buffer[5];  /* copy atm header */
-+                        buffer[5] = 0;
-+	                buffer[2] = AAL0_PDU_INT | (0xffff & (skb->len-20)); /* offset 0 */
-+                        buffer[0] = 20; /* push size */
-+	        } else {
-+                        buffer = (unsigned int *)skb_push(skb, 24);  /* make room for 1+4 words header */
-+                        buffer[3] = 0;
-+                        buffer[4] = (vcc->vpi << 20) | (vcc->vci << 4);  /* prepare atm header */
-+                        buffer[5] = 0;
-+	                buffer[2] = AAL5_PDU_INT | (0xffff & (skb->len-24));
-+                        buffer[0] = 24; /* push size */
-+                }
-+                buffer[1] = (unsigned int)skb;  /* store skb ptr for dequeue */
-+	}
-+
-+        seg_ring_entry = ((unsigned int)(&buffer[2]) >> 2) | OWN;
-+        tneta1570_vcc->seg_ring[tneta1570_vcc->seg_ring_idx] = seg_ring_entry;
-+        DPRINTK(">zippered up");
-+        DPRINTK(">sched %d,"
-+ 		">dma %08x,"
-+ 		">sridx %d >seg_r %08x (%08x),"
-+ 		">buffer %08x, (%08x),"
-+		"atm header %08x\n",
-+ 	        read_sched_tab(tneta1570_dev, tneta1570_vcc->scheduler_idx),
-+ 		tneta1570_dev->tx_dma_state[tneta1570_vcc->scheduler_idx+1].dma_state_flag,
-+ 	        tneta1570_vcc->seg_ring_idx,
-+ 	        tneta1570_vcc->seg_ring, 
-+ 		tneta1570_vcc->seg_ring[tneta1570_vcc->scheduler_idx],
-+ 	        &buffer[2], buffer[2], buffer[4]);
-+	 
-+        /* index to seg.ring entry for next SDU */
-+        tneta1570_vcc->seg_ring_idx++;
-+        tneta1570_vcc->seg_ring_idx %= TX_SEG_RING_SIZE;
-+
-+        tneta1570_vcc->txing++;
-+        return 0;
-+}
-+
-+/*
-+ * -- dequeue tx buffer on tx complete interrupt --
-+ * check completion ring
-+ * while valid entry 
-+ *     dequeue skb (needs a bit of backtracking)
-+ *     increment completion ring index
-+ */
-+static void dequeue_tx(struct atm_dev * dev)
-+{
-+	struct tneta1570_dev *tneta1570_dev;
-+	struct atm_vcc *vcc;
-+	struct sk_buff *skb;
-+        unsigned int * p, v;
-+
-+        EVENT(">dequeue_tx\n",0,0);
-+   
-+        NULLCHECK(dev);
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+	NULLCHECK(tneta1570_dev);
-+        
-+        /* find here completion ring entries */
-+        EVENT(">completion ring post: %08x, %d\n", TX_CMPL_R_IRQ(tneta1570_dev),
-+		tneta1570_dev->txcmpl_ring_idx_irq); 
-+        while(!(TX_CMPL_R_IRQ(tneta1570_dev) & OWN)) { 
-+	        p = (unsigned int *)(TX_CMPL_R_IRQ(tneta1570_dev) << 2);
-+	        TX_CMPL_R_IRQ(tneta1570_dev) = OWN;
-+	   	tneta1570_dev->txcmpl_ring_idx_irq++;
-+	        tneta1570_dev->txcmpl_ring_idx_irq %= TXCMPLR_SZ_IRQ;
-+	        skb = (struct sk_buff *)(*(p-1));  /* get skb */
-+                v = *(p-2);    /* get skb push size */
-+
-+                if(v==0) {     /* free copy area */
-+		        kfree(p-2);
-+                } else {       /* correct skb */
-+                        skb_pull(skb, v);
-+		}
-+	      
-+	        vcc = skb->atm.vcc;
-+                NULLCHECK(vcc);
-+	        if (vcc->pop) vcc->pop(vcc,skb);
-+		else dev_kfree_skb(skb,FREE_WRITE);
-+
-+	        vcc->stats->tx++;
-+	        TNETA1570_VCC(vcc)->txing--;
-+	        wake_up(&(TNETA1570_VCC(vcc)->tx_wait));
-+dma_complete++;
-+	};
-+}
-+
-+
-+
-+/*--------------------------------- common ----------------------------------*/
-+
-+
-+static void foo(void)
-+{
-+printk("tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n"
-+  "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n",
-+  tx_complete,dma_complete,queued,requeued,submitted,backlogged,
-+  rx_enqueued,rx_dequeued,putting,pushed);
-+printk("loss: %d\n",TNETA1570_DEV(tneta1570_boards)->lost);
-+}
-+
-+
-+static void tneta1570_int(int irq,void *dev_id,struct pt_regs *regs)
-+{
-+	struct atm_dev *dev;
-+	struct tneta1570_dev *tneta1570_dev;
-+	unsigned long reason;
-+
-+	EVENT(">tneta_int\n",0,0);
-+	dev = dev_id;
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+	while ( (reason = 0x3ff & SAR_REG_WORD(tneta1570_dev, TNETA_STATUS)) ) {
-+		DPRINTK(DEV_LABEL ": int 0x%08x\n",reason);
-+		if (reason & TNETA_R1_RX_FREEZE) {
-+			EVENT("INT: RX Freeze - RX ring overflow\n",0,0);
-+		}	/* ? */
-+		if (reason & TNETA_R1_TX_FREEZE) {
-+			EVENT("INT: TX Freeze - TX ring overflow\n",0,0);
-+		        dequeue_tx(dev);
-+		}	/* ? */
-+		if (reason & TNETA_R1_CP_RX) {
-+			EVENT("INT: RX Complete - RX buffer completed\n",0,0);
-+                        dequeue_rx(dev);
-+		}	/* ? */
-+	        if (reason & TNETA_R1_CP_TX) {
-+			EVENT("INT: TX Complete - TX Buffer completed\n",0,0);
-+		        dequeue_tx(dev);
-+			/* pop buffers which have been completed */
-+		}
-+	}
-+}
-+
-+/* 
-+ * perform SAR software reset 
-+ */
-+static int reset_sar(struct tneta1570_dev * tneta1570_dev)
-+{
-+   int i;
-+   unsigned int pci_config[64];
-+   
-+   for(i=0; i<64; i++)
-+      if(pcibios_read_config_dword(tneta1570_dev->bus,
-+				   tneta1570_dev->dev_fn,
-+				   i*4,
-+				   &pci_config[i])
-+	    !=PCIBIOS_SUCCESSFUL) return -1;
-+
-+   SAR_REG_WORD(tneta1570_dev, TNETA_RESET) = 0;  /* reset sar */
-+
-+   for(i=0; i<64; i++)
-+      if(pcibios_write_config_dword(tneta1570_dev->bus,
-+				    tneta1570_dev->dev_fn,
-+				    i*4,
-+				    pci_config[i])
-+	    !=PCIBIOS_SUCCESSFUL) return -1;
-+   return 0;
-+   
-+}
-+
-+
-+inline void write_sched_tab(struct tneta1570_dev * dev, int i, unsigned int v)
-+{ 
-+        if(1 & i)
-+                dev->scheduler[i>>1] = (dev->scheduler[i>>1] & 0xffff) | (v << 16);
-+        else
-+                dev->scheduler[i>>1] = (dev->scheduler[i>>1] & 0xffff0000) | (v & 0xffff);
-+}
-+
-+inline int read_sched_tab(struct tneta1570_dev * dev, int i)
-+{ 
-+        if(1 & i)
-+                return (dev->scheduler[i>>1] >> 16);
-+        else
-+                return (0xffff & dev->scheduler[i>>1]);
-+}
-+
-+/*--------------------------------- entries ---------------------------------*/
-+
-+
-+static int tneta1570_init(struct atm_dev *dev)
-+{
-+	struct tneta1570_dev *tneta1570_dev;
-+	unsigned int real_base,base;
-+	unsigned short command;
-+	unsigned char revision;
-+	int error,i,last;
-+
-+	EVENT(">tneta1570_init\n",0,0);
-+
-+        dev->ci_range.vpi_bits = NR_VPI_LD;
-+	dev->ci_range.vci_bits = NR_VCI_LD;
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+
-+        if ((error = pcibios_read_config_word(tneta1570_dev->bus,
-+			    tneta1570_dev->dev_fn, PCI_COMMAND,&command)) 
-+	    || (error = pcibios_read_config_dword(tneta1570_dev->bus,
-+	                    tneta1570_dev->dev_fn,PCI_BASE_ADDRESS_0,&real_base)) 
-+	    || (error = pcibios_read_config_byte(tneta1570_dev->bus,
-+			    tneta1570_dev->dev_fn, PCI_INTERRUPT_LINE,&tneta1570_dev->irq)) 
-+	    || (error = pcibios_read_config_byte(tneta1570_dev->bus,
-+			    tneta1570_dev->dev_fn, PCI_REVISION_ID,&revision))) {
-+ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
-+		    pcibios_strerror(error));
-+		return -EINVAL;
-+	}
-+
-+        /* find mapping size of board */
-+        if(pcibios_write_config_dword(tneta1570_dev->bus,
-+				      tneta1570_dev->dev_fn,
-+				      PCI_BASE_ADDRESS_0,
-+				      0xffffffff)!=PCIBIOS_SUCCESSFUL)
-+        {
-+ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
-+		    pcibios_strerror(error));
-+		return -EINVAL;
-+        }
-+
-+        if(pcibios_read_config_dword(tneta1570_dev->bus,
-+			             tneta1570_dev->dev_fn,
-+				     PCI_BASE_ADDRESS_0,
-+				     &(tneta1570_dev->pci_map_size))!=PCIBIOS_SUCCESSFUL)
-+        { 
-+ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
-+		    pcibios_strerror(error));
-+		return -EINVAL;
-+        }
-+        tneta1570_dev->pci_map_size=~tneta1570_dev->pci_map_size+1;
-+   
-+        if(pcibios_write_config_dword(tneta1570_dev->bus,
-+				      tneta1570_dev->dev_fn,
-+				      PCI_BASE_ADDRESS_0,
-+				      real_base)!=PCIBIOS_SUCCESSFUL)
-+        { 
-+ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
-+		    pcibios_strerror(error));
-+		return -EINVAL;
-+	}
-+   
-+   
-+        real_base &= MEM_VALID; /* strip flags */
-+	if ((error = pcibios_write_config_word(tneta1570_dev->bus,
-+			                       tneta1570_dev->dev_fn,
-+					       PCI_COMMAND,
-+					       PCI_COMMAND_MEMORY)))
-+        {
-+		printk(DEV_LABEL "(itf %d): can't enable memory (%s)\n",
-+		    dev->number,pcibios_strerror(error));
-+		return error;
-+	}
-+	printk(DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",dev->number,
-+	    revision,real_base,tneta1570_dev->irq);
-+	if (!(base = (unsigned long) vremap(real_base,tneta1570_dev->pci_map_size))) {
-+		printk(DEV_LABEL "(itf %d): can't set up page mapping\n",
-+		    dev->number);
-+		return error;
-+	}
-+	tneta1570_dev->base_diff = real_base-base;
-+	tneta1570_dev->reg = (volatile unsigned int *) (base+TNETA_REG_BASE_OFFSET);
-+	tneta1570_dev->scheduler = (volatile unsigned long *) (base+TNETA_SCHED_TABLE);
-+	tneta1570_dev->ram = (volatile unsigned long *) (base+TNETA_SCHED_TABLE);
-+        tneta1570_dev->free_buf_ptr = (volatile struct tneta_rx_fbrptr *) (base+TNETA_FREE_BUFFER_POINTERS);
-+	tneta1570_dev->rx_vpi_vci = (volatile unsigned long *) (base+TNETA_RX_VPIVCI_DMA_POINTERS);
-+	tneta1570_dev->tx_dma_state = (volatile struct tneta_tx_dma_state_table *) (base+TNETA_TX_DMA_STATE_TABLE);
-+	tneta1570_dev->rx_dma_state = (volatile struct tneta_rx_dma_state_table *) (base+TNETA_RX_DMA_STATE_TABLE);
-+	tneta1570_dev->phy = (volatile unsigned long *) (base+TNETA_SUNI_OFFSET);
-+
-+        tneta1570_dev->txcmpl_ring_idx_noi = 0;
-+        tneta1570_dev->txcmpl_ring_idx_irq = 0;
-+        tneta1570_dev->rxcmpl_ring_idx_noi = 0;
-+        tneta1570_dev->rxcmpl_ring_idx_irq = 0;
-+
-+        /* test board memory, find amount of memory installed */ 
-+        last = (TNETA_SUNI_OFFSET)/4;  /* max up to phy ctrl */
-+	for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
-+	        if(i>RESERVED_UL || i<RESERVED_LL) {
-+	   	        tneta1570_dev->ram[i] = 0x55555555;
-+		        if (tneta1570_dev->ram[i] != 0x55555555) last = i;
-+		        else {
-+			        tneta1570_dev->ram[i] = 0xAAAAAAAA;
-+			        if (tneta1570_dev->ram[i] != 0xAAAAAAAA) last = i;
-+			        else tneta1570_dev->ram[i] = i;
-+		        }
-+	        }
-+	}
-+	for (i = 0; i < last; i += RAM_INCREMENT)
-+	        if(i>RESERVED_UL || i<RESERVED_LL)
-+   	                if (tneta1570_dev->ram[i] != i) break;
-+
-+        tneta1570_dev->mem = i << 2; /* byte count */
-+        /* init control memory with all 0s (including regs) */
-+        last = i;
-+        /* bring sar up */
-+        for(i=0; i<last; i++)
-+	        if(i>RESERVED_UL || i<RESERVED_LL)
-+                        tneta1570_dev->ram[i] = 0; 
-+
-+	printk("mem=%dkB\n",tneta1570_dev->mem >> 10);
-+
-+        /* reset SAR */
-+        reset_sar(tneta1570_dev);
-+
-+        for(i=0; i<last; i++)
-+	        if(i>RESERVED_UL || i<RESERVED_LL)
-+                        tneta1570_dev->ram[i] = 0; 
-+        /* TODO: check for non-SUNI, check for TAXI ? */
-+	return suni_init(dev);
-+}
-+
-+
-+static int tneta1570_start(struct atm_dev *dev)
-+{
-+	struct tneta1570_dev *tneta1570_dev;
-+	int error;
-+
-+	EVENT(">tneta1570_start\n",0,0);
-+	tneta1570_dev = TNETA1570_DEV(dev);
-+	if (request_irq(tneta1570_dev->irq,&tneta1570_int,0,DEV_LABEL,dev)) {
-+		printk(DEV_LABEL "(itf %d): IRQ%d is already in use\n",
-+		    dev->number,tneta1570_dev->irq);
-+		return -EAGAIN;
-+	}
-+	/* @@@ should release IRQ on error */
-+
-+        if ((error = pcibios_write_config_word(tneta1570_dev->bus,
-+					       tneta1570_dev->dev_fn,
-+	                                       PCI_COMMAND,
-+					       PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)))
-+        {
-+		printk(DEV_LABEL "(itf %d): can't enable memory+master (%s)\n",
-+		    dev->number,pcibios_strerror(error));
-+		return error;
-+	}
-+
-+        /* bring suni up */
-+	error = dev->phy->start(dev);
-+	if (error) return error;
-+   
-+        SAR_REG_WORD(tneta1570_dev, TNETA_CONFIG) = TNETA_R0_STANDARD_MODE;
-+        SAR_REG_WORD(tneta1570_dev, TNETA_INT_MASK) = TNETA_R2_STANDARD_INTS;
-+        
-+        error = start_tx(dev);
-+	if (error) return error;
-+	error = start_rx(dev);
-+	if (error) return error;
-+
-+        return 0;
-+}
-+
-+
-+
-+static void tneta1570_close(struct atm_vcc *vcc)
-+{
-+	EVENT(">tneta1570_close\n",0,0);
-+	if (!TNETA1570_VCC(vcc)) return;
-+	vcc->flags &= ~ATM_VF_READY;
-+	close_rx(vcc);
-+	close_tx(vcc);
-+	EVENT("tneta1570_close: done waiting\n",0,0);
-+	/* deallocate memory */
-+	kfree(TNETA1570_VCC(vcc));
-+	TNETA1570_VCC(vcc) = NULL;
-+	vcc->flags &= ~ATM_VF_ADDR;
-+	/*foo();*/
-+}
-+
-+
-+/* trying to find a connection identifier */
-+/* 
-+ * not implemented yet
-+ */
-+static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci)
-+{
-+	return 0;
-+}
-+
-+
-+static int tneta1570_open(struct atm_vcc *vcc,short vpi,int vci)
-+{
-+	struct tneta1570_dev *tneta1570_dev;
-+	struct tneta1570_vcc *tneta1570_vcc;
-+	int error;
-+
-+	EVENT(">tneta1570_open\n",0,0);
-+	TNETA1570_VCC(vcc) = NULL;
-+        vcc->alloc_tx = tneta1570_alloc_tx; /* set my skb_alloc function */
-+   
-+        tneta1570_dev = TNETA1570_DEV(vcc->dev);
-+#if 0   /* set to 0 to test atm_find_ci (get_ci usually is faster) */
-+	error = get_ci(vcc,&vpi,&vci); /* get connection id */
-+	if (error) return error;
-+#else
-+	error = atm_find_ci(vcc,&vpi,&vci);  /* get connection id */
-+	if (error) return error;
-+#endif
-+	vcc->vpi = vpi;
-+	vcc->vci = vci;
-+	if (vcc->aal != ATM_AAL0 && vcc->aal != ATM_AAL5) return -EINVAL;
-+	tneta1570_vcc = kmalloc(sizeof(struct tneta1570_vcc),GFP_KERNEL);
-+	if (!tneta1570_vcc) return -ENOMEM;
-+	TNETA1570_VCC(vcc) = tneta1570_vcc;
-+	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
-+	    vcc->vci);
-+	vcc->flags |= ATM_VF_ADDR; /* set flag */
-+	if ((error = open_rx(vcc))) {    /* open rx */
-+	        tneta1570_close(vcc);    /* close on error */
-+		return error;
-+	}
-+	if ((error = open_tx(vcc))) {    /* open tx */
-+		tneta1570_close(vcc);
-+		return error;
-+	}
-+	vcc->flags |= ATM_VF_READY;  /* set IF ready */
-+	/* should power down SUNI while !ref_count @@@ */
-+	return 0;
-+}
-+
-+
-+static int tneta1570_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
-+{
-+	if (cmd == 12345678) dump(dev);
-+	if (!dev->phy->ioctl) return -EINVAL;
-+	return dev->phy->ioctl(dev,cmd,arg);
-+}
-+
-+
-+static int tneta1570_getsockopt(struct atm_vcc *vcc,int level,int optname,
-+    char *optval,int *optlen)
-+{
-+	return -EINVAL;
-+}
-+
-+
-+static int tneta1570_setsockopt(struct atm_vcc *vcc,int level,int optname,
-+    char *optval,int optlen)
-+{
-+	return -EINVAL;
-+}
-+
-+/*
-+ * -- send a PDU (AAL5 or AAL0)
-+ */
-+static int tneta1570_send(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	EVENT(">tneta1570_send\n",0,0);
-+	if (!skb) {
-+		printk("!skb in tneta1570_send ?\n");
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		return -EINVAL;
-+	}
-+	if (vcc->aal == ATM_AAL0) {
-+		if (skb->len != ATM_CELL_SIZE-1) {
-+			dev_kfree_skb(skb,FREE_WRITE);
-+			return -EINVAL;
-+		}
-+     	        *(unsigned long *) skb->data = htonl(*(unsigned long *)
-+		                                       skb->data);
-+	                /* correct byte order of an AAL0 header */
-+	}
-+   
-+submitted++;
-+	skb->atm.vcc = vcc;
-+
-+        return do_tx(skb);
-+}
-+
-+/* Scatter/Gather send, build vector and send from user space
-+ * not yet implemented
-+ */
-+static int tneta1570_sg_send(struct atm_vcc *vcc,unsigned long start,
-+    unsigned long size)
-+{
-+	return vcc->aal == ATM_AAL5 && !((start | size) & 3);
-+		/* don't tolerate misalignment */
-+}
-+
-+
-+static void tneta1570_phy_put(struct atm_dev *dev,unsigned char value,
-+    unsigned long addr)
-+{
-+	TNETA1570_DEV(dev)->phy[addr] = value;
-+}
-+
-+
-+
-+static unsigned char tneta1570_phy_get(struct atm_dev *dev,unsigned long addr)
-+{
-+	volatile unsigned tmp; /* force 32 bit access */
-+
-+        /* set address */
-+	TNETA1570_DEV(dev)->phy[addr+TNETA_SUNI_RDREQ]=0;
-+        /* read the bugger */
-+        tmp=TNETA1570_DEV(dev)->phy[addr];
-+
-+	return (unsigned char)(0xff & tmp);
-+}
-+
-+
-+static struct atmdev_ops ops = {
-+	tneta1570_open,
-+	tneta1570_close,
-+	tneta1570_ioctl,
-+	tneta1570_getsockopt,
-+	tneta1570_setsockopt,
-+	tneta1570_send,
-+	NULL,                   /* no tneta1570_sg_send */
-+	NULL,			/* no poll */
-+	NULL,			/* no send_oam ???? */
-+	tneta1570_phy_put,
-+	tneta1570_phy_get,
-+	NULL			/* no feedback */
-+};
-+
-+
-+int tneta1570_detect(void)
-+{
-+	struct atm_dev *dev;
-+	struct tneta1570_dev *tneta1570_dev;
-+	int index;
-+
-+	if (!pcibios_present()) {
-+		printk(DEV_LABEL " driver but no PCI BIOS ?\n");
-+		return 0;
-+	}
-+	tneta1570_dev = (struct tneta1570_dev *) kmalloc(sizeof(struct tneta1570_dev),
-+	    GFP_KERNEL);
-+	if (!tneta1570_dev) return -ENOMEM;
-+	index = 0;
-+	while (!pcibios_find_device(PCI_VENDOR_ID_TI,
-+	                            PCI_DEVICE_ID_TI_TNETA1570,
-+				    index,
-+	                            &tneta1570_dev->bus,
-+				    &tneta1570_dev->dev_fn))
-+        {
-+		dev = atm_dev_register(DEV_LABEL,&ops,0);
-+		if (!dev) break;
-+		TNETA1570_DEV(dev) = tneta1570_dev;
-+	   
-+	        if (tneta1570_init(dev) || tneta1570_start(dev)) {
-+			atm_dev_deregister(dev);
-+			break;
-+		}
-+		tneta1570_dev->more = tneta1570_boards;
-+		tneta1570_boards = dev;
-+		index++;
-+		tneta1570_dev = (struct tneta1570_dev *) kmalloc(sizeof(struct tneta1570_dev),
-+		    GFP_KERNEL);
-+		if (!tneta1570_dev) break;
-+                
-+	}
-+	return index;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/drivers/atm/fore200.c	Thu Jul 18 20:59:50 1996
-@@ -0,0 +1,26 @@
-+/* drivers/atm/fore200.c - This is just a test, not a real driver */
-+ 
-+/* Written 1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#include <linux/string.h>
-+#include <linux/kernel.h>
-+#include <asm/sbus.h>
-+
-+
-+int fore200_detect(void)
-+{
-+	struct linux_sbus *bus;
-+	struct linux_sbus_device *sdev = 0;
-+
-+	for_each_sbus(bus) {
-+		for_each_sbusdev(sdev, bus) {
-+			if (strcmp(sdev->prom_name,"FORE,sba-200") == 0) {
-+				printk(KERN_NOTICE "fore200: found an SBA-200 "
-+				    "in slot %x,offset=%08lx,irq=%d",
-+				    sdev->slot,sdev->offset,sdev->irqs->pri);
-+			}
-+		}
-+	}
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atm.h	Wed Jul 17 19:41:04 1996
-@@ -0,0 +1,228 @@
-+/* atm.h - general ATM declarations */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef _LINUX_ATM_H
-+#define _LINUX_ATM_H
-+
-+/*
-+ * BEGIN_xx and END_xx markers are used for automatic generation of
-+ * documentation. Do not change them.
-+ */
-+
-+#include <linux/atmsap.h>
-+#include <linux/atmioc.h>
-+
-+
-+/* general ATM constants */
-+#define ATM_CELL_SIZE		    53	/* ATM cell size incl. header */
-+#define ATM_CELL_PAYLOAD	    48	/* ATM payload size */
-+#define ATM_AAL0_SDU		    52	/* AAL0 SDU size */
-+#define ATM_MAX_AAL34_PDU	 65535	/* maximum AAL3/4 PDU payload */
-+#define ATM_AAL5_TRAILER	     8	/* AAL5 trailer size */
-+#define ATM_MAX_AAL5_PDU	 65535	/* maximum AAL5 PDU payload */
-+#define ATM_MAX_CDV		  9999	/* maximum (default) CDV */
-+#define ATM_NOT_RSV_VCI		    32	/* first non-reserved VCI value */
-+
-+#define ATM_MAX_VPI		   255	/* maximum VPI at the UNI */
-+#define ATM_MAX_VPI_NNI		  4096	/* maximum VPI at the NNI */
-+#define ATM_MAX_VCI		 65535	/* maximum VCI */
-+
-+/*
-+ * The following items should be added to sys/socket.h aka linux/socket.h
-+ */
-+
-+/* address families */
-+#define AF_ATMPVC	6		/* ATM PVCs */
-+#define AF_ATMSVC	7		/* ATM SVCs */
-+
-+/* "protcol" values for the socket system call */
-+#define ATM_AAL0	0		/* "raw" ATM cells */
-+#define ATM_AAL1	1		/* AAL1 (CBR) */
-+#define ATM_AAL2	2		/* AAL2 (VBR) */
-+#define ATM_AAL34	3		/* AAL3/4 (data) */
-+#define ATM_AAL5	5		/* AAL5 (data) */
-+#define ATM_SAAL	12		/* signaling AAL */
-+
-+/*
-+ * UGLY - there should only be one protocol family (PF_ATM) for both
-+ * address families. A quick glance at some kernel internals seems to
-+ * suggest to me that doing it the "right" way might involve some
-+ * swimming against the stream ...
-+ */
-+
-+/* protocol families */
-+#define PF_ATMPVC	AF_ATMPVC
-+#define PF_ATMSVC	AF_ATMSVC
-+
-+/* layers for getsockopt/setsockopt */
-+#define SOL_ATM		2
-+#define SOL_AAL		3
-+
-+/*
-+ * ATM layer (values used for flags must be 2^n, non-flag values should use
-+ * the remaining values)
-+ */
-+
-+#define SO_SETCLP	1		/* set CLP bit value - TODO */
-+#define SO_CIRANGE	3		/* connection identifier range;
-+					   socket must be bound or connected */
-+#define SO_ATMQOS	5		/* Quality of Service setting */
-+
-+/* AAL layer */
-+#define SO_AALTYPE	0		/* AAL type, get only */
-+
-+/* socket layer */
-+#define SO_BCTXOPT	16		/* not ATM specific - should go */
-+#define SO_BCRXOPT	17		/*   somewhere else */
-+
-+
-+/* for SO_BCTXOPT and SO_BCRXOPT */
-+
-+struct atm_buffconst {
-+	unsigned long	buf_fac;	/* buffer alignment factor */
-+	unsigned long	buf_off;	/* buffer alignment offset */
-+	unsigned long	size_fac;	/* buffer size factor */
-+	unsigned long	size_off;	/* buffer size offset */
-+	unsigned long	min_size;	/* minimum size */
-+	unsigned long	max_size;	/* maximum size, 0 = unlimited */
-+};
-+
-+
-+/* ATM cell header (for AAL0) */
-+
-+/* BEGIN_CH */
-+#define ATM_HDR_GFC_MASK	0xf0000000
-+#define ATM_HDR_GFC_SHIFT	28
-+#define ATM_HDR_VPI_MASK	0x0ff00000
-+#define ATM_HDR_VPI_SHIFT	20
-+#define ATM_HDR_VCI_MASK	0x000ffff0
-+#define ATM_HDR_VCI_SHIFT	4
-+#define ATM_HDR_PTI_MASK	0x0000000e
-+#define ATM_HDR_PTI_SHIFT	1
-+#define ATM_HDR_CLP		0x00000001
-+/* END_CH */
-+
-+
-+/* PTI codings */
-+
-+/* BEGIN_PTI */
-+#define ATM_PTI_US0	0  /* user data cell, congestion not exp, SDU-type 0 */
-+#define ATM_PTI_US1	1  /* user data cell, congestion not exp, SDU-type 1 */
-+#define ATM_PTI_UCES0	2  /* user data cell, cong. experienced, SDU-type 0 */
-+#define ATM_PTI_UCES1	3  /* user data cell, cong. experienced, SDU-type 1 */
-+#define ATM_PTI_SEGF5	4  /* segment OAM F5 flow related cell */
-+#define ATM_PTI_E2EF5	5  /* end-to-end OAM F5 flow related cell */
-+#define ATM_PTI_RSV_RM	6  /* reserved for traffic control/resource mgmt */
-+#define ATM_PTI_RSV	7  /* reserved */
-+/* END_PTI */
-+
-+
-+/*
-+ * The following items should stay in linux/atm.h, which should be linked to
-+ * netatm/atm.h
-+ */
-+
-+/* Traffic description */
-+
-+#define ATM_NONE	0		/* no traffic */
-+#define ATM_UBR		1
-+#define ATM_CBR		2
-+#define ATM_VBR		3
-+#define ATM_ABR		4
-+#define ATM_ANYCLASS	5		/* compatible with everything */
-+
-+#define ATM_MAX_PCR	-1		/* maximum available PCR */
-+
-+struct atm_trafprm {
-+	unsigned char	class;		/* traffic class (ATM_UBR, ...) */
-+	int		max_pcr;	/* maximum PCR in cells per second */
-+	int		min_pcr;	/* minimum PCR in cells per second */
-+	int		max_cdv;	/* maximum CDV in microseconds */
-+	int		max_sdu;	/* maximum SDU in bytes */
-+};
-+
-+struct atm_qos {
-+	struct atm_trafprm txtp;	/* parameters in TX direction */
-+	struct atm_trafprm rxtp;	/* parameters in RX direction */
-+};
-+
-+/* PVC addressing */
-+
-+#define ATM_ITF_ANY	-1		/* "magic" PVC address values */
-+#define ATM_VPI_ANY	-1
-+#define ATM_VCI_ANY	-1
-+#define ATM_VPI_UNSPEC	-2
-+#define ATM_VCI_UNSPEC	-2
-+
-+
-+struct sockaddr_atmpvc {
-+	unsigned short 	sap_family;	/* address family, AF_ATMPVC  */
-+	struct {			/* PVC address */
-+		short	itf;		/* ATM interface */
-+		short	vpi;		/* VPI (only 8 bits at UNI) */
-+		int	vci;		/* VCI (only 16 bits at UNI) */
-+	}		sap_addr;	/* PVC address */
-+	struct atm_trafprm sap_txtp;	/* TX traffic parameters */
-+	struct atm_trafprm sap_rxtp;	/* RX traffic parameters */
-+};
-+
-+/* SVC addressing */
-+
-+#define	ATM_ESA_LEN	20		/* ATM End System Address length */
-+#define ATM_E164_LEN	12		/* maximum E.164 number length */
-+
-+#define ATM_MAX_BLLI	16		/* maximum number of BLLI elements */
-+
-+#define ATM_AFI_DCC	0x39		/* DCC ATM Format */
-+#define ATM_AFI_ICD	0x47		/* ICD ATM Format */
-+#define ATM_AFI_E164	0x45		/* E.164 ATM Format */
-+
-+struct sockaddr_atmsvc {
-+    unsigned short 	sas_family;	/* address family, AF_ATMSVC */
-+    struct {				/* SVC address */
-+        unsigned char	prv[ATM_ESA_LEN];/* private ATM address */
-+        char		pub[ATM_E164_LEN+1]; /* public address (E.164) */
-+    					/* unused addresses must be bzero'ed */
-+        struct atm_blli	*blli;		/* local SAP, low-layer information */
-+        struct atm_bhli	bhli;		/* local SAP, high-layer information */
-+    }			sas_addr;	/* SVC address */
-+    struct atm_trafprm	sas_txtp;	/* TX traffic parameters */
-+    struct atm_trafprm	sas_rxtp;	/* RX traffic parameters */
-+};
-+
-+
-+/*
-+ * Some stuff for linux/sockios.h
-+ */
-+
-+struct atmif_sioc {
-+    int number;
-+    int length;
-+    void *arg;
-+};
-+
-+
-+#define SIOCSIFATMTCP	_IO('a',ATMIOC_ITF)	/* set ATMTCP mode */
-+
-+/*
-+ * The following ioctls are obsolete and will be removed in a later release
-+ */
-+
-+#define SIOCGIFATMADDR	_IOR('a',ATMIOC_ITF+1,struct atmif_sioc)
-+					/* get local ATM address */
-+#define SIOCSIFATMADDR	_IOR('a',ATMIOC_ITF+2,struct atmif_sioc)
-+					/* set local ATM address */
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/net.h>	/* struct net_proto */
-+
-+
-+void atmpvc_proto_init(struct net_proto *pro);
-+void atmsvc_proto_init(struct net_proto *pro);
-+
-+#endif /* __KERNEL__ */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atmclip.h	Fri Jul 19 15:10:11 1996
-@@ -0,0 +1,37 @@
-+/* atmclip.h - Classical IP over ATM */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef LINUX_ATMCLIP_H
-+#define LINUX_ATMCLIP_H
-+
-+#include <linux/sockios.h>
-+#include <linux/atmioc.h>
-+
-+
-+#define CLIP_NULENCAP	SIOCDEVPRIVATE		/* set NULL encapsulation */
-+#define CLIP_LLCENCAP	(SIOCDEVPRIVATE+1)	/* set LLC/SNAP encap. */
-+
-+
-+#define RFC1483LLC_LEN	8		/* LLC+OUI+PID = 8 */
-+#define RFC1626_MTU	9180		/* RFC1626 default MTU */
-+
-+#define CLIP_DEFAULT_IDLETIMER 1200	/* 20 minutes, see RFC1755 */
-+#define CLIP_CHECK_INTERVAL	 10	/* check every ten seconds */
-+
-+#define SIOCMKCLIP	_IO('a',ATMIOC_CLIP)	/* create IP interface */
-+#define CLIP_PVC	_IO('a',ATMIOC_CLIP+4)	/* IP itf becomes CLIP itf */
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/skbuff.h>
-+#include <linux/atmdev.h>
-+#include <linux/atm.h>
-+
-+
-+int atm_init_clip(struct atm_vcc *vcc);
-+
-+#endif /* __KERNEL__ */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atmdev.h	Fri Jul 19 15:07:27 1996
-@@ -0,0 +1,242 @@
-+/* atmdev.h - ATM device driver declarations */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef LINUX_ATMDEV_H
-+#define LINUX_ATMDEV_H
-+
-+
-+#include <linux/config.h>
-+#include <linux/uio.h>
-+#include <linux/atmioc.h>
-+
-+
-+#define MAX_ATM_ITF	10	/* for now, should be lowered */
-+#define MAX_ATM_VCC	128	/* for now, should be dynamic */
-+
-+#define ESI_LEN		6
-+
-+#define ATM_OC3_PCR	(155520000/270*260/8/53)
-+			/* OC3 link rate:  155520000 bps
-+			   SONET overhead: /270*260 (9 section, 1 path)
-+			   bits per cell:  /8/53
-+			   max cell rate:  353207.547 cells/sec */
-+
-+#define ATM_SD(s)	((struct atm_vcc *) ((s)->data))
-+
-+
-+struct atm_aal_stats {
-+	long		tx,tx_err;	/* TX okay and errors */
-+	long		rx,rx_err;	/* RX okay and errors */
-+	long		rx_drop;	/* RX out of memory */
-+};
-+
-+
-+struct atm_dev_stats {
-+	struct atm_aal_stats aal0;
-+	struct atm_aal_stats aal34;
-+	struct atm_aal_stats aal5;
-+};
-+
-+
-+#define ATM_GETNAMES	_IOW('a',ATMIOC_ITF+3,struct atm_iobuf)
-+					/* get interface names (numbers) */
-+#define ATM_GETTYPE	_IOW('a',ATMIOC_ITF+4,struct atmif_sioc)
-+					/* get interface type name */
-+#define ATM_GETESI	_IOW('a',ATMIOC_ITF+5,struct atmif_sioc)
-+					/* get interface ESI */
-+#define ATM_GETADDR	_IOW('a',ATMIOC_ITF+6,struct atmif_sioc)
-+					/* get itf's local ATM addr. list */
-+#define ATM_RSTADDR	_IOW('a',ATMIOC_ITF+7,struct atmif_sioc)
-+					/* reset itf's ATM address list */
-+#define ATM_ADDADDR	_IOW('a',ATMIOC_ITF+8,struct atmif_sioc)
-+					/* add a local ATM address */
-+#define ATM_DELADDR	_IOW('a',ATMIOC_ITF+9,struct atmif_sioc)
-+					/* remove a local ATM address */
-+#define ATM_GETCIRANGE	_IOW('a',ATMIOC_ITF+10,struct atmif_sioc)
-+					/* get connection identifier range */
-+#define ATM_SETCIRANGE	_IOW('a',ATMIOC_ITF+11,struct atmif_sioc)
-+					/* set connection identifier range */
-+#define ATM_GETSTAT	_IOW('a',ATMIOC_SARCOM+0,struct atmif_sioc)
-+					/* get AAL layer statistics */
-+#define ATM_GETSTATZ	_IOW('a',ATMIOC_SARCOM+1,struct atmif_sioc)
-+					/* get AAL layer statistics and zero */
-+
-+/* for ATM_GETTYPE */
-+#define ATM_ITFTYP_LEN	8	/* maximum length of interface type name */
-+
-+
-+
-+struct atm_iobuf {
-+	int length;
-+	void *buffer;
-+};
-+
-+/* for ATM_GETCIRANGE / ATM_SETCIRANGE */
-+
-+#define ATM_CI_MAX      -1              /* use maximum range of VPI/VCI */
-+ 
-+struct atm_cirange {
-+	char	vpi_bits;		/* 1..8, ATM_CI_MAX (-1) for maximum */
-+	char	vci_bits;		/* 1..16, ATM_CI_MAX (-1) for maximum */
-+};
-+
-+
-+#define ATM_BACKLOG_DEFAULT 32 /* if we get more, we're likely to time out
-+				  anyway */
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/sched.h> /* struct wait_queue */
-+#include <linux/time.h> /* struct timeval */
-+#include <linux/net.h>
-+#include <linux/skbuff.h> /* struct sk_buff */
-+#include <linux/atm.h>
-+
-+#ifdef CONFIG_AREQUIPA
-+#include <net/sock.h> /* for struct sock */
-+#endif
-+
-+
-+#define ATM_VF_ADDR	1	/* Address is in use. Set by anybody, cleared
-+				   by device driver. */
-+#define ATM_VF_READY	2	/* VC is ready to transfer data. Set by device
-+				   driver, cleared by anybody. */
-+#define ATM_VF_PARTIAL	4	/* resources are bound to PVC (partial PVC
-+				   setup), controlled by socket layer */
-+#define ATM_VF_BOUND	4	/* local SAP is set, controlled by SVC socket
-+				   layer */
-+#define ATM_VF_REGIS	8	/* registered with demon, controlled by SVC
-+				   socket layer */
-+#define ATM_VF_RELEASED 16	/* demon has indicated/requested release,
-+				   controlled by SVC socket layer */
-+#define ATM_VF_HASQOS	32	/* QOS parameters have been set using the
-+				   new API */
-+#define ATM_VF_LISTEN	64	/* socket is used for listening */
-+#define ATM_VF_META	128	/* SVC socket isn't used for normal data
-+				   traffic and doesn't depend on signaling
-+				   to be available */
-+
-+struct atm_vcc {
-+	unsigned short	flags;		/* VCC flags (ATM_VF_*) */
-+	unsigned char	family;		/* address family; 0 if unused */
-+	unsigned char	aal;		/* ATM Adaption Layer */
-+	short		vpi;		/* VPI and VCI (types must be equal */
-+					/* with sockaddr) */
-+	int 		vci;
-+	unsigned long	aal_options;	/* AAL layer options */
-+	unsigned long	atm_options;	/* ATM layer options */
-+	struct atm_dev	*dev;		/* device back pointer */
-+	struct atm_trafprm txtp,rxtp;	/* traffic parameters */
-+	struct atm_qos	qos;		/* QOS - only valid if ATM_VF_HASQOS */
-+	unsigned long	tx_quota,rx_quota; /* buffer quotas */
-+	unsigned long	tx_inuse,rx_inuse; /* buffer space in use */
-+	void (*push)(struct atm_vcc *vcc,struct sk_buff *skb);
-+	void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */
-+	struct sk_buff *(*peek)(struct atm_vcc *vcc,unsigned long pdu_size,
-+	    __u32 (*fetch)(struct atm_vcc *vcc,int i));
-+					/* super-efficient xfers; note that */
-+					/* PDU_SIZE may be rounded */
-+	struct sk_buff *(*alloc_tx)(struct atm_vcc *vcc,unsigned int size);
-+					/* TX allocation routine - can be */
-+					/* modified by protocol or by driver.*/
-+					/* NOTE: this interface will change */
-+	int (*push_oam)(struct atm_vcc *vcc,void *cell);
-+	void		*dev_data;	/* per-device data */
-+	void		*proto_data;	/* per-protocol data */
-+	struct timeval	timestamp;	/* AAL timestamps */
-+	struct sk_buff_head recvq;	/* receive queue */
-+	struct atm_aal_stats *stats;	/* pointer to AAL stats group */
-+	struct wait_queue *sleep;	/* if socket is busy */
-+	struct atm_vcc	*prev,*next;
-+	/* SVC part --- may move later */
-+	short		itf;		/* interface number */
-+	struct sockaddr_atmsvc local;
-+	struct sockaddr_atmsvc remote;
-+	void (*callback)(struct atm_vcc *vcc);
-+	struct sk_buff_head listenq;
-+	int		backlog_quota;	/* number of connection requests we */
-+					/* can still accept */
-+	int		reply;
-+	void		*user_back;	/* user backlink - not touched */
-+#ifdef CONFIG_AREQUIPA
-+	struct sock	*upper;		/* our "master" */
-+	struct socket	*sock;		/* back pointer to our own socket */
-+#endif
-+};
-+
-+
-+struct atm_dev_addr {
-+	struct sockaddr_atmsvc addr;	/* ATM address */
-+	struct atm_dev_addr *next;	/* next address */
-+};
-+
-+
-+struct atm_dev {
-+	const struct atmdev_ops *ops;	/* device operations; NULL if unused */
-+	const struct atmphy_ops *phy;	/* PHY operations, may be undefined */
-+					/* (NULL) */
-+	const char	*type;		/* device type name */
-+	int		number;		/* device index */
-+	struct atm_vcc	*vccs;		/* VCC table (or NULL) */
-+	struct atm_vcc	*last;		/* last VCC (or undefined) */
-+	void		*dev_data;	/* per-device data */
-+	void		*phy_data;	/* private PHY date */
-+	unsigned long	flags;		/* device flags, TBD */
-+	struct atm_dev_addr *local;	/* local ATM addresses */
-+	unsigned char	esi[ESI_LEN];	/* ESI ("MAC" addr) */
-+	struct atm_cirange ci_range;	/* VPI/VCI range */
-+	struct atm_dev_stats stats;	/* statistics */
-+/* */ int sending;
-+};
-+
-+
-+/*
-+ * ioctl, getsockopt, setsockopt, sg_send, and poll are optional and can
-+ * be set to NULL.
-+ */
-+
-+#define ATM_OF_IMMED	1	/* Attempt immediate delivery */
-+#define ATM_OF_INRATE	2	/* Attempt in-rate delivery */
-+
-+struct atmdev_ops { /* only send is required */
-+	int (*open)(struct atm_vcc *vcc,short vpi,int vci);
-+	void (*close)(struct atm_vcc *vcc);
-+	int (*ioctl)(struct atm_dev *dev,unsigned int cmd,unsigned long arg);
-+	int (*getsockopt)(struct atm_vcc *vcc,int level,int optname,
-+	    char *optval,int *optlen);
-+	int (*setsockopt)(struct atm_vcc *vcc,int level,int optname,
-+	    char *optval,int optlen);
-+	int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
-+	int (*sg_send)(struct atm_vcc *vcc,unsigned long start,
-+	    unsigned long size);
-+#if 0 /* keep the current hack for now */
-+	int (*send_iovec)(struct atm_vcc *vcc,struct iovec *iov,int size,
-+	    void (*discard)(struct atm_vcc *vcc,void *user),void *user);
-+#endif
-+	void (*poll)(struct atm_vcc *vcc,int nonblock);
-+	int (*send_oam)(struct atm_vcc *vcc,void *cell,int flags);
-+	void (*phy_put)(struct atm_dev *dev,unsigned char value,
-+	    unsigned long addr);
-+	unsigned char (*phy_get)(struct atm_dev *dev,unsigned long addr);
-+	void (*feedback)(struct atm_vcc *vcc,struct sk_buff *skb,
-+	    unsigned long start,unsigned long dest,int len);
-+};
-+
-+
-+struct atmphy_ops {
-+	int (*start)(struct atm_dev *dev);
-+	int (*ioctl)(struct atm_dev *dev,unsigned int cmd,unsigned long arg);
-+	void (*interrupt)(struct atm_dev *dev);
-+};
-+
-+
-+struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
-+    unsigned long flags);
-+void atm_dev_deregister(struct atm_dev *dev);
-+int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci);
-+
-+#endif /* __KERNEL__ */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atmsap.h	Mon Jun 10 17:36:14 1996
-@@ -0,0 +1,129 @@
-+/* atmsap.h - ATM Service Access Point addressing definitions */
-+
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef _LINUX_ATMSAP_H
-+#define _LINUX_ATMSAP_H
-+
-+/*
-+ * BEGIN_xx and END_xx markers are used for automatic generation of
-+ * documentation. Do not change them.
-+ */
-+
-+
-+/*
-+ * Layer 2 protocol identifiers
-+ */
-+
-+/* BEGIN_L2 */
-+#define ATM_L2_NONE	0	/* L2 not specified */
-+#define ATM_L2_ISO1745  0x01	/* Basic mode ISO 1745 */
-+#define ATM_L2_Q291	0x02	/* ITU-T Q.291 */
-+#define ATM_L2_X25_LL	0x06	/* ITU-T X.25, link layer */
-+#define ATM_L2_X25_ML	0x07	/* ITU-T X.25, multilink */
-+#define ATM_L2_LAPB	0x08	/* Extended LAPB, half-duplex */
-+#define ATM_L2_HDLC_ARM	0x09	/* HDLC ARM (ISO 4335) */
-+#define ATM_L2_HDLC_NRM	0x0a	/* HDLC NRM (ISO 4335) */
-+#define ATM_L2_HDLC_ABM	0x0b	/* HDLC ABM (ISO 4335) */
-+#define ATM_L2_ISO8802	0x0c	/* LAN LLC (ISO 8802/2) */
-+#define ATM_L2_X75	0x0d	/* ITU-T X.75, SLP */
-+#define ATM_L2_Q922	0x0e	/* ITU-T Q.922 */
-+#define ATM_L2_USER	0x10	/* user-specified */
-+#define ATM_L2_ISO7776	0x11	/* ISO 7776 DTE-DTE */
-+/* END_L2 */
-+
-+
-+/*
-+ * Layer 3 protocol identifiers
-+ */
-+
-+/* BEGIN_L3 */
-+#define ATM_L3_NONE	0	/* L3 not specified */
-+#define ATM_L3_X25	0x06	/* ITU-T X.25, packet layer */
-+#define ATM_L3_ISO8208	0x07	/* ISO/IEC 8208 */
-+#define ATM_L3_X223	0x08	/* ITU-T X.223/ISO 8878 */
-+#define ATM_L3_ISO8473	0x09	/* ISO/IEC 8473 */
-+#define ATM_L3_T70	0x0a	/* ITU-T T.70 minimum network layer */
-+#define ATM_L3_TR9577	0x0b	/* ISO/IEC TR 9577 */
-+#define ATM_L3_USER	0x10	/* user-specified */
-+/* END_L3 */
-+
-+
-+/*
-+ * High layer identifiers
-+ */
-+
-+/* BEGIN_HL */
-+#define ATM_HL_NONE	0	/* HL not specified */
-+#define ATM_HL_ISO	0x01	/* ISO */
-+#define ATM_HL_USER	0x02	/* user-specific */
-+#if defined(UNI30) || defined(ALLOW_UNI30)
-+#define ATM_HL_HLP	0x03	/* high layer profile - UNI 3.0 only */
-+#endif
-+#define ATM_HL_VENDOR	0x04	/* vendor-specific application identifier */
-+/* END_HL */
-+
-+
-+/*
-+ * ITU-T coded mode of operation
-+ */
-+
-+/* BEGIN_IMD */
-+#define ATM_IMD_NONE	 0	/* mode not specified */
-+#define ATM_IMD_NORMAL	 1	/* normal mode of operation */
-+#define ATM_IMD_EXTENDED 2	/* extended mode of operation */
-+/* END_IMD */
-+
-+/*
-+ * Selected ISO/IEC TR 9577 Network Layer Protocol Identifiers (NLPID)
-+ */
-+
-+#define NLPID_IEEE802_1_SNAP	0x80	/* IEEE 802.1 SNAP */
-+
-+/*
-+ * SAP structures
-+ */
-+
-+#define ATM_MAX_HLI	7	/* maximum high-layer information length */
-+
-+
-+struct atm_blli {
-+    unsigned char l2_proto;	/* layer 2 protocol */
-+    union {
-+	struct {
-+	    unsigned char mode;	/* mode of operation (ATM_IMD_xxx), 0 if */
-+				/* absent */
-+	    unsigned char window; /* window size (k), 1-127 (0 to omit) */
-+	} itu;			/* ITU-T encoding */
-+	unsigned char user;	/* user-specified l2 information */
-+    } l2;
-+    unsigned char l3_proto;	/* layer 3 protocol */
-+    union {
-+	struct {
-+	    unsigned char mode;	/* mode of operation (ATM_IMD_xxx), 0 if */
-+				/* absent */
-+	    unsigned char def_size; /* default packet size (log2), 4-12 (0 to */
-+				    /* omit) */
-+	    unsigned char window;/* packet window size, 1-127 (0 to omit) */
-+	} itu;			/* ITU-T ecoding */
-+	unsigned char user;	/* user specified l3 information */
-+	struct {		/* if l3_proto = ATM_L3_TR9577 */
-+	    unsigned char ipi;	/* initial protocol id */
-+	    unsigned char snap[5];/* IEEE 802.1 SNAP identifier */
-+				  /* (only if ipi == NLPID_IEEE802_1_SNAP) */
-+	} tr9577;
-+    } l3;
-+    struct atm_blli *next;	/* next BLLI or NULL (undefined when used in */
-+				/* atmsvc_msg) */
-+};
-+
-+
-+struct atm_bhli {
-+    unsigned char hl_type;	/* high layer information type */
-+    unsigned char hl_length;	/* length (only if hl_type == ATM_HL_USER || */
-+				/* hl_type == ATM_HL_ISO) */
-+    unsigned char hl_info[ATM_MAX_HLI];/* high layer information */
-+};
-+
-+#endif
---- ref/include/linux/skbuff.h	Sun Jun  9 11:23:42 1996
-+++ work/include/linux/skbuff.h	Wed Jul 17 19:41:04 1996
-@@ -112,6 +112,16 @@
- 	unsigned char 	*end;			/* End pointer					*/
- 	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
- 	__u16		redirport;		/* Redirect port				*/
-+#ifdef CONFIG_ATM
-+	struct {
-+		int		size;		/* PDU size (adapter too) */
-+		unsigned long	pos;		/* adapter data */
-+		struct atm_vcc	*vcc;		/* ATM VCC */
-+		int		iovcnt;		/* 0 for "normal" operation */
-+		struct timeval	timestamp;	/* timestamp or 0 */
-+	} atm;
-+#endif
-+
- };
- 
- #ifdef CONFIG_SKB_LARGE
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/sonet.h	Mon Jun 10 17:36:15 1996
-@@ -0,0 +1,52 @@
-+/* sonet.h - SONET/SHD physical layer control */
-+ 
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef LINUX_SONET_H
-+#define LINUX_SONET_H
-+
-+struct sonet_stats {
-+	long section_bip;		/* section parity errors (B1) */
-+	long line_bip;			/* line parity errors (B2) */
-+	long path_bip;			/* path parity errors (B3) */
-+	long line_febe;			/* line parity errors at remote */
-+	long path_febe;			/* path parity errors at remote */
-+	long corr_hcs;			/* correctable header errors */
-+	long uncorr_hcs;		/* uncorrectable header errors */
-+	long tx_cells;			/* cells sent */
-+	long rx_cells;			/* cells received */
-+};
-+
-+#define SONET_GETSTAT	_IOR('a',ATMIOC_PHYTYP,struct sonet_stats)
-+					/* get statistics */
-+#define SONET_GETSTATZ	_IOR('a',ATMIOC_PHYTYP+1,struct sonet_stats)
-+					/* ... and zero counters */
-+#define SONET_SETDIAG	_IOWR('a',ATMIOC_PHYTYP+2,int)
-+					/* set error insertion */
-+#define SONET_CLRDIAG	_IOWR('a',ATMIOC_PHYTYP+3,int)
-+					/* clear error insertion */
-+#define SONET_GETDIAG	_IOR('a',ATMIOC_PHYTYP+4,int)
-+					/* query error insertion */
-+#define SONET_SETFRAMING _IO('a',ATMIOC_PHYTYP+5)
-+					/* set framing mode (SONET/SDH) */
-+#define SONET_GETFRAMING _IOR('a',ATMIOC_PHYTYP+6,int)
-+					/* get framing mode */
-+#define SONET_GETFRSENSE _IOR('a',ATMIOC_PHYTYP+7, \
-+  unsigned char[SONET_FRSENSE_SIZE])	/* get framing sense information */
-+
-+#define SONET_INS_SBIP	  1		/* section BIP */
-+#define SONET_INS_LBIP	  2		/* line BIP */
-+#define SONET_INS_PBIP	  4		/* path BIP */
-+#define SONET_INS_FRAME	  8		/* out of frame */
-+#define SONET_INS_LOS	 16		/* set line to zero */
-+#define SONET_INS_LAIS	 32		/* line alarm indication signal */
-+#define SONET_INS_PAIS	 64		/* path alarm indication signal */
-+#define SONET_INS_HCS	128		/* insert HCS error */
-+
-+#define SONET_FRAME_SONET 0		/* SONET STS-3 framing */
-+#define SONET_FRAME_SDH   1		/* SDH STM-1 framing */
-+
-+#define SONET_FRSENSE_SIZE 6		/* C1[3],H1[3] (0xff for unknown) */
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atmsvc.h	Fri Jul 19 14:04:50 1996
-@@ -0,0 +1,76 @@
-+/* atmsvc.h - ATM signaling kernel-demon interface definitions */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef _LINUX_ATMSVC_H
-+#define _LINUX_ATMSVC_H
-+
-+#include <linux/atm.h>
-+#include <linux/atmioc.h>
-+
-+
-+#define ATMSIGD_CTRL _IO('a',ATMIOC_SPECIAL)
-+				/* become ATM signaling demon control socket */
-+
-+enum atmsvc_msg_type { as_catch_null,as_bind,as_establish,as_listen,as_okay,
-+  as_indicate,as_close,as_itf_notify };
-+
-+struct atmsvc_msg {
-+	enum atmsvc_msg_type type;
-+	unsigned long vcc;
-+	unsigned long listen_vcc;	/* indicate */
-+	int reply;			/* for okay and close:
-+					 *   < 0: error before active
-+					 *        (sigd has discarded ctx)
-+					 *   ==0: success
-+				         *   > 0: error when active (still need
-+					 *        to close)
-+					 */
-+	unsigned char aal;		/* AAL */
-+	struct sockaddr_atmpvc pvc;	/* indicate, okay (connect) */
-+	struct sockaddr_atmsvc local;	/* local SVC address */
-+	struct sockaddr_atmsvc svc;	/* MUST BE BEFORE "blli", SVC address */
-+	struct atm_blli blli[1];	/* MUST BE LAST */
-+};
-+
-+/*
-+ * Message contents:
-+ *
-+ * Message   Dir  vcc listen_vcc reply aal    pvc       --svc--    local
-+ *                                         addr *xtp addr sap *xtp addr
-+ * bind	     K->D  X      -        -    -   -    -    X    X   -    -
-+ * - okay    D->K  X      -        -    -   -    -    X    X   -    -
-+ * establish K->D  X      -        -    X   -    -    X    X   X    X
-+ * - okay    D->K  X      -        -    -   X    X    -    -   -    X
-+ * listen    K->D  X      -        -    X   -    -    X    X   X    -
-+ * - okay    D->K  X      -        -    -   -    -    -    -   -    -
-+ * indicate  D->K  -      X        -    -   X    2    X    X   X    -
-+ * accept1   K->D  X      X        -    -   -    -    -    -   -    -
-+ * - okay    D->K  X      -        -    -   -    -    -    -   -    X
-+ * close     K->D  X      -        -    -   -    -    -    -   -    -
-+ * close     D->K  X      -        X    -   -    -    -    -   -    -
-+ *
-+ *  1  (establish)
-+ *  2  same value as in svc.*xtp
-+ *
-+ * Note: we may have to get rid of the PVC part in indicate messages later,
-+ *       so it's a good idea not to depend on it too heavily.
-+ */
-+
-+/*
-+ * In an as_itf_update message, only the fields type and pvc.sas_addr.itf are
-+ * valid.
-+ */
-+
-+/*
-+ * Some policy stuff for atmsigd and for net/atm/svc.c. Both have to agree on
-+ * what PCR is used to request bandwidth from the device driver. net/atm/svc.c
-+ * tries to do better than that, but only if there's no routing decision (i.e.
-+ * if signaling only uses one ATM interface).
-+ */
-+
-+#define SELECT_TOP_PCR(tp) ((tp).max_pcr && (tp).max_pcr != ATM_MAX_PCR ? \
-+  (tp).max_pcr : (tp).min_pcr)
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atmioc.h	Mon Jun 10 17:36:16 1996
-@@ -0,0 +1,32 @@
-+/* atmioc.h - ranges for ATM-related ioctl numbers */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef _LINUX_ATMIOC_H
-+#define _LINUX_ATMIOC_H
-+
-+#include <asm/ioctl.h>
-+		/* everybody including atmioc.h will also need _IO{,R,W,WR} */
-+
-+#define ATMIOC_PHYCOM	  0x00 /* PHY device common ioctls, globally unique */
-+#define ATMIOC_PHYCOM_END 0x0f
-+#define ATMIOC_PHYTYP	  0x10 /* PHY dev type ioctls, unique per PHY type */
-+#define ATMIOC_PHYTYP_END 0x2f
-+#define ATMIOC_PHYPRV	  0x30 /* PHY dev private ioctls, unique per driver */
-+#define ATMIOC_PHYPRV_END 0x4f
-+#define ATMIOC_SARCOM	  0x50 /* SAR device common ioctls, globally unique */
-+#define ATMIOC_SARCOM_END 0x50
-+#define ATMIOC_SARPRV	  0x60 /* SAR dev private ioctls, unique per driver */
-+#define ATMIOC_SARPRV_END 0x7f
-+#define ATMIOC_ITF	  0x80 /* Interface ioctls, globally unique */
-+#define ATMIOC_ITF_END	  0x8f
-+/* 0x90-0xbf: Reserved for future use */
-+#define ATMIOC_AREQUIPA	  0xc0 /* Application requested IP over ATM, glob. u. */
-+#define ATMIOC_LANE	  0xd0 /* LAN Emulation, globally unique */
-+#define	ATMIOC_CLIP	  0xe0 /* Classical IP over ATM control, globally u. */
-+#define	ATMIOC_CLIP_END	  0xef
-+#define	ATMIOC_SPECIAL	  0xf0 /* Special-purpose controls, globally unique */
-+#define	ATMIOC_SPECIAL_END 0xff
-+
-+#endif
---- ref/net/Makefile	Thu May 16 15:35:55 1996
-+++ work/net/Makefile	Mon Jun 10 17:50:26 1996
-@@ -8,12 +8,17 @@
- # Note 2! The CFLAGS definition is now in the main makefile...
- 
- MOD_SUB_DIRS := ipv4
--ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipx unix appletalk netrom #decnet
-+ALL_SUB_DIRS := 802 atm ax25 bridge core ethernet ipv4 ipx unix appletalk \
-+		netrom #decnet
- SUB_DIRS     := core ethernet unix
- MOD_LIST_NAME := NET_MISC_MODULES
- 
- ifeq ($(CONFIG_NET),y)
- SUB_DIRS += 802
-+endif
-+
-+ifeq ($(CONFIG_ATM),y)
-+SUB_DIRS += atm
- endif
- 
- ifeq ($(CONFIG_INET),y)
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/Makefile	Wed Jul 17 19:31:55 1996
-@@ -0,0 +1,54 @@
-+#
-+# Makefile for the ATM Protocol Families.
-+#
-+# Note! Dependencies are done automagically by 'make dep', which also
-+# removes any old dependencies. DON'T put your own dependencies here
-+# unless it's something special (ie not a .c file).
-+#
-+# Note 2! The CFLAGS definition is now in the main makefile...
-+
-+include ../../.config
-+
-+CFLAGS += -g
-+
-+ifeq ($(CONFIG_ATM),y)
-+
-+OBJS	= common.o dev.o mmuio.o pvc.o raw.o signaling.o static.o svc.o
-+
-+ifeq ($(CONFIG_ATM_ATMARP),y)
-+OBJS += atmarp.o
-+NEED_IPCOM = ipcommon.o
-+endif
-+
-+ifeq ($(CONFIG_ATM_CLIP),y)
-+OBJS += clip.o
-+NEED_IPCOM = ipcommon.o
-+endif
-+
-+ifeq ($(CONFIG_AREQUIPA),y)
-+OBJS += arequipa.o
-+NEED_IPCOM = ipcommon.o
-+endif
-+
-+OBJS += $(NEED_IPCOM)
-+
-+ifeq ($(CONFIG_PROC_FS),y)
-+OBJS += proc.o
-+endif
-+
-+ifeq ($(CONFIG_ATM_LANE),y)
-+OBJS += lec.o lec_arpc.o
-+endif
-+
-+atm.o:	$(OBJS)
-+	$(LD) -r -o atm.o $(OBJS)
-+
-+else
-+
-+atm.o:
-+	$(AR) rcs atm.o
-+
-+endif
-+
-+
-+include $(TOPDIR)/Rules.make
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/clip.c	Wed Jul 17 18:25:56 1996
-@@ -0,0 +1,113 @@
-+/* net/atm/clip.c - Classical IP over ATM */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/string.h>
-+#include <linux/kernel.h>
-+#include <linux/errno.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <linux/skbuff.h>
-+#include <linux/errno.h>
-+#include <linux/skbuff.h>
-+#include <linux/in.h>
-+#include <linux/mmuio.h>
-+#include <linux/atmdev.h>
-+#include <linux/atmclip.h>
-+#include <net/sock.h>
-+#include <netinet/in.h>
-+
-+#include "protocols.h"
-+#include "common.h"
-+#include "ipcommon.h"
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+
-+static int pvc_num = 0;
-+
-+
-+/*static*/ void atm_push_clip(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	DPRINTK("clip push\n");
-+	if (!skb) {
-+		printk(KERN_ALERT "ARGL!\n");
-+		unregister_netdev((struct device *) vcc->proto_data);
-+		vcc->proto_data = NULL;
-+		return;
-+	}
-+	skb->dev = vcc->proto_data;
-+	ipcom_push(skb);
-+}
-+
-+
-+static int clip_xmit(struct sk_buff *skb,struct device *dev)
-+{
-+	DPRINTK("clip xmit\n");
-+	if (!CLIP(dev)->vcc) return -ENOTCONN; /* @@@ discard skb ? */
-+	ipcom_xmit(dev,CLIP(dev)->vcc,skb);
-+	CLIP(dev)->stats.tx_packets++;
-+	return 0;
-+}
-+
-+
-+#if 0
-+static int clip_sg_xmit(struct sk_buff *skb,struct device *dev)
-+{
-+/*
-+struct iovec *iov;
-+int i;
-+
-+iov = (struct iovec *) skb->data;
-+for (i = 0; i < skb->atm.iovcnt; i++)
-+  printk("[%d] 0x%lx + %d\n",i,(unsigned long) iov[i].iov_base,
-+    iov[i].iov_len);
-+
-+	printk("clip *DIRECT* xmit (iovcnt: %d)\n",skb->atm.iovcnt);
-+*/
-+	CLIP(dev)->vcc->dev->ops->send(CLIP(dev)->vcc,skb);
-+	CLIP(dev)->stats.tx_packets++;
-+	return 0;
-+}
-+#endif
-+
-+
-+static int clip_init(struct device *dev)
-+{
-+	ipcom_init(dev,clip_xmit,0);
-+	return 0;
-+}
-+
-+
-+
-+int atm_init_clip(struct atm_vcc *vcc)
-+{
-+	struct device *dev;
-+
-+	DPRINTK("atm_init_clip\n");
-+	if (!suser()) return -EPERM;
-+	vcc->aal = ATM_AAL5;
-+	vcc->push = atm_push_clip;
-+	vcc->peek = atm_peek_clip;
-+	vcc->pop = atm_pop_clip;
-+	vcc->push_oam = NULL;
-+	vcc->proto_data = dev = kmalloc(sizeof(struct device)+
-+	    sizeof(struct clip_priv),GFP_KERNEL);
-+	if (!vcc->proto_data) return -ENOMEM;
-+	memset(dev,0,sizeof(struct device)+sizeof(struct clip_priv));
-+	dev->name = CLIP(dev)->name;
-+	sprintf(dev->name,"atm%d",pvc_num++);
-+	dev->init = clip_init;
-+	CLIP(dev)->vcc = vcc;
-+	if (register_netdev(dev)) return -EIO;
-+	DPRINTK("registered %s,0x%lx\n",dev->name,(unsigned long) vcc);
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/common.c	Fri Jul 19 15:10:47 1996
-@@ -0,0 +1,871 @@
-+/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/net.h>		/* struct socket, struct net_proto, struct
-+				   proto_ops */
-+#include <linux/atm.h>		/* ATM stuff */
-+#include <linux/atmdev.h>
-+#include <linux/atmclip.h>	/* CLIP_*ENCAP */
-+#include <linux/atmarp.h>	/* manifest constants */
-+#include <linux/sonet.h>	/* for ioctls */
-+#include <linux/socket.h>	/* SOL_SOCKET */
-+#include <linux/errno.h>	/* error codes */
-+#include <linux/kernel.h>	/* suser */
-+#include <linux/mm.h>		/* verify_area */
-+#include <linux/sched.h>
-+#include <linux/time.h>		/* struct timeval */
-+#include <linux/skbuff.h>
-+#include <linux/mmuio.h>
-+#include <linux/uio.h>
-+
-+#ifdef CONFIG_AREQUIPA
-+#include <linux/arequipa.h>
-+#endif
-+
-+#ifdef CONFIG_ATM_LANE
-+#include <linux/atmlec.h>
-+#include "lec.h"
-+#include "lec_arpc.h"
-+#endif
-+
-+#include "static.h"		/* atm_find_dev */
-+#include "common.h"		/* prototypes */
-+#include "protocols.h"		/* atm_init_<transport> */
-+#include "tunable.h"		/* tunable parameters */
-+#include "atmarp.h"		/* for clip_create */
-+#include "signaling.h"		/* for WAITING and sigd_attach */
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size)
-+{
-+	return alloc_skb(size,GFP_KERNEL);
-+}
-+
-+
-+int atm_create(struct socket *sock,int protocol)
-+{
-+	struct atm_vcc *vcc;
-+	int error;
-+
-+	ATM_SD(sock) = NULL;
-+	if (sock->type == SOCK_STREAM) return -EINVAL;
-+	if (!(vcc = alloc_atm_vcc())) return -ENOMEM;
-+#ifdef CONFIG_AREQUIPA
-+	vcc->upper = NULL;
-+	vcc->sock = sock;
-+#endif
-+	vcc->flags = 0;
-+	vcc->dev = NULL;
-+	vcc->family = sock->ops->family;
-+	vcc->alloc_tx = alloc_tx;
-+	vcc->callback = NULL;
-+	memset(&vcc->local,0,sizeof(struct sockaddr_atmsvc));
-+	memset(&vcc->remote,0,sizeof(struct sockaddr_atmsvc));
-+	vcc->tx_quota = ATM_RXBQ_DEF*1024;
-+	vcc->rx_quota = ATM_TXBQ_DEF*1024;
-+	vcc->tx_inuse = vcc->rx_inuse = 0;
-+	switch (protocol) {
-+		/*
-+		 * Note: ATM_AAL0 also covers the "0 for default" case,
-+		 *	 although some people may not like that default ;-)
-+		 */
-+		case ATM_AAL0:
-+			error = atm_init_aal0(vcc);
-+			break;
-+		case ATM_AAL34:
-+			error = atm_init_aal34(vcc);
-+			break;
-+		case ATM_AAL5:
-+			error = atm_init_aal5(vcc);
-+			break;
-+		default:
-+			error = -EPROTOTYPE;
-+	}
-+	if (error) {
-+		free_atm_vcc(vcc);
-+		return error;
-+	}
-+	vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
-+	vcc->atm_options = vcc->aal_options = 0;
-+	vcc->timestamp.tv_sec = vcc->timestamp.tv_usec = 0;
-+	vcc->sleep = NULL;
-+	skb_queue_head_init(&vcc->recvq);
-+	skb_queue_head_init(&vcc->listenq);
-+	ATM_SD(sock) = vcc;
-+	return 0;
-+}
-+
-+
-+int atm_release_vcc(struct atm_vcc *vcc,int free_vcc)
-+{
-+	struct sk_buff *skb;
-+
-+	vcc->flags &= ~ATM_VF_READY;
-+	if (vcc->dev) {
-+		if (vcc->dev->ops->close) vcc->dev->ops->close(vcc);
-+		if (vcc->push) vcc->push(vcc,NULL); /* atmarpd has no push */
-+		while ((skb = skb_dequeue(&vcc->recvq)))
-+			kfree_skb(skb,FREE_READ);
-+		if (vcc->prev) vcc->prev->next = vcc->next;
-+		else vcc->dev->vccs = vcc->next;
-+		if (vcc->next) vcc->next->prev = vcc->prev;
-+		else vcc->dev->last = vcc->prev;
-+	}
-+	if (free_vcc) free_atm_vcc(vcc);
-+	return 0;
-+}
-+
-+
-+extern void atm_push_clip(struct atm_vcc *vcc,struct sk_buff *skb);
-+
-+
-+int atm_release(struct socket *sock,struct socket *peer)
-+{
-+	struct atm_vcc *vcc;
-+
-+	vcc = ATM_SD(sock);
-+	if (!vcc
-+#ifdef CONFIG_ATM_CLIP
-+	    || (vcc->push == atm_push_clip && (vcc->vpi || vcc->vci))
-+#endif
-+	    )
-+		return 0; /* ugly */
-+	return atm_release_vcc(vcc,1);
-+}
-+
-+
-+static int adjust_tp(struct atm_trafprm *tp,unsigned char aal)
-+{
-+	if (!tp->class) return 0;
-+	if (tp->class != ATM_UBR && !tp->min_pcr && !tp->max_pcr)
-+		return -EINVAL;
-+	if (!tp->max_sdu)
-+		switch (aal) {
-+			case ATM_AAL0:
-+				tp->max_sdu = ATM_CELL_SIZE-1;
-+				break;
-+			case ATM_AAL34:
-+				tp->max_sdu = ATM_MAX_AAL34_PDU;
-+				break;
-+			default:
-+				printk(KERN_WARNING "ATM: AAL problems ... "
-+				    "(%d)\n",aal);
-+			case ATM_AAL5:
-+				tp->max_sdu = ATM_MAX_AAL5_PDU;
-+		}
-+	if (!tp->max_cdv) tp->max_cdv = ATM_MAX_CDV;
-+	return 0;
-+}
-+
-+
-+static int check_ci(struct atm_vcc *vcc,short vpi,int vci)
-+{
-+	struct atm_vcc *walk;
-+
-+	for (walk = vcc->dev->vccs; walk; walk = walk->next)
-+		if ((walk->flags & ATM_VF_ADDR) && walk->vpi == vpi &&
-+		    walk->vci == vci && ((walk->txtp.class != ATM_NONE &&
-+		    vcc->txtp.class != ATM_NONE) || (walk->rxtp.class !=
-+		    ATM_NONE && vcc->rxtp.class != ATM_NONE)))
-+			return -EADDRINUSE;
-+		/* allow VCCs with same VPI/VCI iff they don't collide on
-+		   TX/RX (but we may refuse such sharing for other reasons,
-+		   e.g. if protocol requires to have both channels) */
-+	return 0;
-+}
-+
-+
-+int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci)
-+{
-+	static short p = 0; /* poor man's per-device cache */
-+	static int c = 0;
-+	short old_p;
-+	int old_c;
-+
-+	if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY)
-+		return check_ci(vcc,*vpi,*vci);
-+	/* last scan may have left values out of bounds for current device */
-+	if (*vpi != ATM_VPI_ANY) p = *vpi;
-+	else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
-+	if (*vci != ATM_VCI_ANY) c = *vci;
-+	else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits)
-+			c = ATM_NOT_RSV_VCI;
-+	old_p = p;
-+	old_c = c;
-+	do {
-+		if (!check_ci(vcc,p,c)) {
-+			*vpi = p;
-+			*vci = c;
-+			return 0;
-+		}
-+		if (*vci == ATM_VCI_ANY) {
-+			c++;
-+			if (c >= 1 << vcc->dev->ci_range.vci_bits)
-+				c = ATM_NOT_RSV_VCI;
-+		}
-+		if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) &&
-+		    *vpi == ATM_VPI_ANY) {
-+			p++;
-+			if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
-+		}
-+	}
-+	while (old_p != p || old_c != c);
-+	return -EADDRINUSE;
-+}
-+
-+
-+static int atm_do_connect(struct atm_vcc *vcc,int itf,int vpi,int vci)
-+{
-+	volatile struct atm_dev *dev; /* need volatile for sequence */
-+	int error;
-+
-+	if (itf >= MAX_ATM_ITF) return -EINVAL;
-+	dev = &atm_dev[itf];
-+	if (!dev->ops) return -ENODEV;
-+	if ((vpi != ATM_VPI_UNSPEC && vpi >> dev->ci_range.vpi_bits) ||
-+	    (vci != ATM_VCI_UNSPEC && vci >> dev->ci_range.vci_bits))
-+		return -EINVAL;
-+	if (vci > 0 && vci < ATM_NOT_RSV_VCI && !suser()) return -EPERM;
-+	switch (vcc->aal) {
-+		case ATM_AAL0:
-+			vcc->stats = &((struct atm_dev *) dev)->stats.aal0;
-+			break;
-+		case ATM_AAL34:
-+			vcc->stats = &((struct atm_dev *) dev)->stats.aal34;
-+			break;
-+		case ATM_AAL5:
-+			vcc->stats = &((struct atm_dev *) dev)->stats.aal5;
-+			break;
-+		default:
-+			printk(KERN_WARNING "%s(itf %d): unsupported AAL "
-+			    "(%d)\n",vcc->dev->type,vcc->dev->number,vcc->aal);
-+			return -EINVAL;
-+	}
-+	error = adjust_tp(&vcc->txtp,vcc->aal);
-+	if (!error) error = adjust_tp(&vcc->rxtp,vcc->aal);
-+	if (error) return error;
-+	vcc->dev = (struct atm_dev *) dev;
-+	DPRINTK("VCC %d.%d, AAL %d\n",vpi,vci,vcc->aal);
-+	DPRINTK("  TX: %d, PCR %d..%d, SDU %d\n",vcc->txtp.class,
-+	    vcc->txtp.min_pcr,vcc->txtp.max_pcr,vcc->txtp.max_sdu);
-+	DPRINTK("  RX: %d, PCR %d..%d, SDU %d\n",vcc->rxtp.class,
-+	    vcc->rxtp.min_pcr,vcc->rxtp.max_pcr,vcc->rxtp.max_sdu);
-+	if (dev->ops->open) {
-+		error = dev->ops->open(vcc,vpi,vci);
-+		if (error) {
-+			vcc->dev = NULL;
-+			return error;
-+		}
-+	}
-+	vcc->prev = dev->last;
-+	vcc->next = NULL;
-+	if (!dev->vccs) dev->vccs = vcc;
-+	else dev->last->next = vcc;
-+	dev->last = vcc;
-+	return 0;
-+}
-+
-+
-+int atm_connect(struct socket *sock,int itf,short vpi,int vci)
-+{
-+	struct atm_vcc *vcc;
-+	int error;
-+
-+	DPRINTK("atm_connect (vpi %d, vci %d)\n",vpi,vci);
-+	if (sock->state == SS_CONNECTED) return -EISCONN;
-+	if (sock->state != SS_UNCONNECTED) return -EINVAL;
-+	if (!(vpi || vci)) return -EINVAL;
-+	vcc = ATM_SD(sock);
-+	if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC)
-+		vcc->flags &= ~ATM_VF_PARTIAL;
-+	else if (vcc->flags & ATM_VF_PARTIAL) return -EINVAL;
-+	printk("atm_connect (TX: cl %d,bw %d-%d,sdu %d; RX: cl %d,bw %d-%d,"
-+	    "sdu %d)\n",vcc->txtp.class,vcc->txtp.min_pcr,vcc->txtp.max_pcr,
-+	    vcc->txtp.max_sdu,vcc->rxtp.class,vcc->rxtp.min_pcr,
-+	    vcc->rxtp.max_pcr,vcc->rxtp.max_sdu);
-+	if (vcc->txtp.class == ATM_ANYCLASS || vcc->rxtp.class == ATM_ANYCLASS)
-+		return -EINVAL;
-+	if (itf != ATM_ITF_ANY) error = atm_do_connect(vcc,itf,vpi,vci);
-+	else {
-+		int i;
-+
-+		error = -ENODEV;
-+		for (i = 0; i < MAX_ATM_ITF; i++)
-+			if (atm_dev[i].ops)
-+				if (!(error = atm_do_connect(vcc,i,vpi,vci)))
-+					break;
-+	}
-+	if (error) return error;
-+	if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
-+		vcc->flags |= ATM_VF_PARTIAL;
-+	else sock->state = SS_CONNECTED;
-+	return 0;
-+}
-+
-+
-+int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len,
-+    int nonblock,int flags,int *addr_len)
-+{
-+	struct atm_vcc *vcc;
-+	struct sk_buff *skb;
-+	unsigned long cpu_flags;
-+	int eff_len;
-+
-+	void *buff;
-+	int size;
-+
-+	if (sock->state != SS_CONNECTED || flags || m->msg_name)
-+		return -EINVAL;
-+	if (m->msg_iovlen != 1) return -ENOSYS; /* fix this later @@@ */
-+	buff = m->msg_iov->iov_base;
-+	size = m->msg_iov->iov_len;
-+	/* verify_area is done by net/socket.c */
-+	vcc = ATM_SD(sock);
-+	if (vcc->dev->ops->poll && (vcc->flags & ATM_VF_READY))
-+		while (1) {
-+			vcc->dev->ops->poll(vcc,nonblock);
-+			if (current->signal & ~current->blocked)
-+				return -ERESTARTSYS;
-+			if (skb_peek(&vcc->recvq)) break;
-+			if (nonblock) return -EAGAIN;
-+		}
-+	save_flags(cpu_flags);
-+	cli();
-+	while (!(skb = skb_dequeue(&vcc->recvq))) {
-+		if (vcc->flags & ATM_VF_RELEASED) return vcc->reply;
-+		if (!(vcc->flags & ATM_VF_READY)) return 0;
-+		if (nonblock) {
-+			restore_flags(cpu_flags);
-+			return -EAGAIN;
-+		}
-+		interruptible_sleep_on(&vcc->sleep);
-+		if (current->signal & ~current->blocked) {
-+			restore_flags(cpu_flags);
-+			return -ERESTARTSYS;
-+		}
-+	}
-+	restore_flags(cpu_flags);
-+	vcc->timestamp = skb->atm.timestamp;
-+	eff_len = skb->len > size ? size : skb->len;
-+	if (vcc->dev->ops->feedback)
-+		vcc->dev->ops->feedback(vcc,skb,(unsigned long) skb->data,
-+		    (unsigned long) buff,eff_len);
-+#ifdef CONFIG_MMU_HACKS
-+	mmucp_tofs((unsigned long) buff,eff_len,skb,(unsigned long) skb->data);
-+#else
-+#ifdef DUMP_PACKETS
-+        if (vcc->vci==5 || vcc->vci >32) {
-+                char buf[300];
-+                int i;
-+                
-+                for(i=0;i<99 && i < skb->len;i++) {
-+                        sprintf(buf+i*3,"%2.2x ",
-+                                0xff&skb->data[i]);
-+                }
-+                printk("recv %d:%s\n",vcc->vci,buf);
-+        }          
-+#endif      
-+	memcpy_tofs(buff,skb->data,eff_len);
-+	kfree_skb(skb,FREE_READ);
-+#endif
-+	return eff_len;
-+}
-+
-+
-+int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len,
-+  int nonblock,int flags)
-+{
-+	struct atm_vcc *vcc;
-+	struct sk_buff *skb;
-+	int eff;
-+
-+	const void *buff;
-+	int size;
-+
-+	if (sock->state != SS_CONNECTED || flags || m->msg_name)
-+		return -EINVAL;
-+	if (m->msg_iovlen != 1) return -ENOSYS; /* fix this later @@@ */
-+	buff = m->msg_iov->iov_base;
-+	size = m->msg_iov->iov_len;
-+	vcc = ATM_SD(sock);
-+	if (vcc->flags & ATM_VF_RELEASED) return vcc->reply;
-+	if (!(vcc->flags & ATM_VF_READY)) return -EPIPE;
-+	if (!size) return 0;
-+	/* verify_area is done by net/socket.c */
-+#ifdef CONFIG_MMU_HACKS
-+	if (vcc->dev->ops->sg_send && vcc->dev->ops->sg_send(vcc,
-+	    (unsigned long) buff,size)) {
-+		int res,max_iov;
-+
-+		max_iov = 2+size/PAGE_SIZE;
-+		/*
-+		 * Doesn't use alloc_tx yet - this will change later. @@@
-+		 */
-+		while (!(skb = alloc_skb(sizeof(struct iovec)*max_iov,
-+		    GFP_KERNEL))) {
-+			if (nonblock) return -EAGAIN;
-+			interruptible_sleep_on(&vcc->sleep);
-+			if (current->signal & ~current->blocked)
-+				return -ERESTARTSYS;
-+		}
-+		skb->free = 1;
-+		skb->len = size;
-+		res = lock_user((unsigned long) buff,size,max_iov,
-+		    (struct iovec *) skb->data);
-+		if (res < 0) {
-+			kfree_skb(skb,FREE_WRITE);
-+			if (res != -EAGAIN) return res;
-+		}
-+		else {
-+			DPRINTK("res is %d\n",res);
-+			skb->atm.iovcnt = res;
-+			skb_device_lock(skb);
-+			vcc->dev->ops->send(vcc,skb);
-+			/* FIXME: security: may send up to 3 "garbage" bytes */
-+			return size;
-+		}
-+	}
-+#endif
-+	eff = (size+3) & ~3; /* align to word boundary */
-+	while (size+vcc->tx_inuse+ATM_PDU_OVHD > vcc->tx_quota ||
-+	    !(skb = vcc->alloc_tx(vcc,eff))) {
-+		if (nonblock) return -EAGAIN;
-+		interruptible_sleep_on(&vcc->sleep);
-+		if (current->signal & ~current->blocked)
-+			return -ERESTARTSYS;
-+		if (vcc->flags & ATM_VF_RELEASED) return vcc->reply;
-+		if (!(vcc->flags & ATM_VF_READY)) return -EPIPE;
-+	}
-+	skb->free = 1;
-+	skb->len = size;
-+	skb->atm.iovcnt = 0;
-+	memcpy_fromfs(skb->data,buff,size);
-+	if (eff != size) memset(skb->data+size,0,eff-size);
-+#if 0 /* experimental */
-+	vcc->dev->sending = 1;
-+#endif
-+	skb_device_lock(skb);
-+#ifdef DUMP_PACKETS
-+        if (vcc->vci==5 || vcc->vci >32) {
-+                char buf[300];
-+                int i;
-+                
-+                for(i=0;i<99 && i < skb->len;i++) {
-+                        sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
-+                }
-+                printk("send %d:%s\n",vcc->vci,buf);
-+        }
-+#endif
-+	vcc->dev->ops->send(vcc,skb);
-+#if 0 /* experimental */
-+	while (vcc->dev->sending) sleep_on(&vcc->sleep);
-+#endif
-+	return size;
-+}
-+
-+
-+int atm_select(struct socket *sock,int sel_type,select_table *wait)
-+{
-+	struct atm_vcc *vcc;
-+
-+	vcc = ATM_SD(sock);
-+	switch(sel_type) {
-+		case SEL_IN:
-+			if (vcc->dev && vcc->dev->ops->poll)
-+				vcc->dev->ops->poll(ATM_SD(sock),1);
-+			if (sock->state == SS_CONNECTING) break;
-+			if (!skb_peek(&vcc->recvq) &&
-+			    !skb_peek(&vcc->listenq) &&
-+			    !(vcc->flags & ATM_VF_RELEASED)) break;
-+			if (vcc->dev && vcc->dev->ops->poll) return 0;
-+			return 1;
-+		case SEL_OUT:
-+			if (sock->state == SS_CONNECTING) break;
-+			if (vcc->txtp.class == ATM_NONE) return 1;
-+			if (vcc->txtp.max_sdu+vcc->tx_inuse+ATM_PDU_OVHD >
-+			  vcc->tx_quota) break;
-+			return 1;
-+		case SEL_EX:
-+			if (sock->state != SS_CONNECTING) return 0;
-+			if (vcc->reply == WAITING) break;
-+			return 1;
-+	}
-+	select_wait(&vcc->sleep,wait);
-+	return 0;
-+}
-+
-+
-+
-+static int fetch_stats(struct atm_dev *dev,struct atm_dev_stats *arg,int zero)
-+{
-+	unsigned long flags;
-+ 
-+	save_flags(flags);
-+	cli();
-+	if (arg)
-+		memcpy_tofs(arg,&dev->stats,sizeof(struct atm_dev_stats));
-+        if (zero)
-+                memset(&dev->stats,0,sizeof(struct atm_dev_stats));
-+        restore_flags(flags);
-+	return 0;
-+}
-+
-+
-+extern int clip_ioctl(void *dev,void *rq,int cmd); /* @@@ lie and cheat ... */
-+
-+
-+#ifdef CONFIG_AREQUIPA
-+
-+/* @@@ stolen from net/socket.c - should be in a common header file */
-+
-+
-+struct socket *sockfd_lookup(int fd)
-+{
-+	struct file *file;
-+	struct inode *inode;
-+
-+	if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd])) 
-+		return NULL;
-+	inode = file->f_inode;
-+	if (!inode || !inode->i_sock) return NULL;
-+	return &inode->u.socket_i;
-+}
-+
-+#endif
-+
-+
-+int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
-+{
-+	struct atm_dev *dev;
-+	unsigned long eff_arg;
-+	int rsize,wsize,len;
-+	int error;
-+
-+	rsize = wsize = 0;
-+	switch (cmd) {
-+		case ATM_GETNAMES:
-+			{
-+				struct atm_iobuf *buf;
-+
-+				error = verify_area(VERIFY_READ,(void *) arg,
-+				    sizeof(struct atm_iobuf));
-+				if (error) return error;
-+				buf = (struct atm_iobuf *) arg;
-+				error = verify_area(VERIFY_WRITE,
-+				    (void *) get_fs_long(&buf->buffer),
-+				    get_fs_long(&buf->length));
-+				if (error) return error;
-+				error = verify_area(VERIFY_WRITE,
-+				    (void *) &buf->length,sizeof(int));
-+				if (error) return error;
-+				return atm_dev_list((void *) get_fs_long(&buf->
-+				    buffer),&buf->length);
-+			}
-+		case SIOCGSTAMP: /* borrowed from IP */
-+			if (!ATM_SD(sock)->timestamp.tv_sec) return -ENOENT;
-+			error = verify_area(VERIFY_WRITE,(void *) arg,
-+			    sizeof(struct timeval));
-+			if (error) return error;
-+			memcpy_tofs((void *) arg,&ATM_SD(sock)->timestamp,
-+			    sizeof(struct timeval));
-+			return 0;
-+		case ATMSIGD_CTRL:
-+			if (!suser()) return -EPERM;
-+			error = sigd_attach(ATM_SD(sock));
-+			if (!error) sock->state = SS_CONNECTED;
-+			return error;
-+#ifdef CONFIG_ATM_CLIP
-+		case CLIP_PVC:
-+			if (!suser()) return -EPERM;
-+			return atm_init_clip(ATM_SD(sock));
-+		case CLIP_NULENCAP:
-+		case CLIP_LLCENCAP:
-+			if (!suser()) return -EPERM;
-+			return clip_ioctl(ATM_SD(sock)->proto_data,NULL,cmd);
-+				/* rather crude hack ... */
-+#endif
-+#ifdef CONFIG_ATM_ATMARP
-+		case SIOCMKCLIP:
-+			if (!suser()) return -EPERM;
-+			return clip_create(arg);
-+		case ATMARPD_CTRL:
-+			if (!suser()) return -EPERM;
-+			error = atm_init_atmarp(ATM_SD(sock));
-+			if (!error) sock->state = SS_CONNECTED;
-+			return error;
-+		case ATMARP_MKIP:
-+			if (!suser()) return -EPERM;
-+			return atmarp_mkip(ATM_SD(sock),arg);
-+		case ATMARP_SETENTRY:
-+			if (!suser()) return -EPERM;
-+			return atmarp_setentry(ATM_SD(sock),arg);
-+		case ATMARP_ENCAP:
-+			if (!suser()) return -EPERM;
-+			return atmarp_encap(ATM_SD(sock),arg);
-+#endif
-+#ifdef CONFIG_AREQUIPA
-+		case AREQUIPA_PRESET:
-+			{
-+				struct socket *upper;
-+
-+				if (!(upper = sockfd_lookup(arg)))
-+					return -ENOTSOCK;
-+				if (upper->ops->family != PF_INET)
-+					return -EPROTOTYPE;
-+				return arequipa_preset(sock,
-+				    (struct sock *) upper->data);
-+			}
-+		case AREQUIPA_INCOMING:
-+			return arequipa_incoming(sock);
-+#endif
-+#ifdef CONFIG_ATM_LANE
-+                case ATMLEC_CTRL:
-+                        if (!suser()) return -EPERM;
-+                        error = lecd_attach(ATM_SD(sock));
-+                        if (!error) sock->state = SS_CONNECTED;
-+                        return error;
-+                case ATMLEC_MCAST:
-+                        if (!suser()) return -EPERM;
-+                        return lec_mcast_attach(ATM_SD(sock));
-+                case ATMLEC_DATA:
-+                        if (!suser()) return -EPERM;
-+                        return lec_vcc_attach(ATM_SD(sock), (void*)arg);
-+#endif
-+		case ATM_GETTYPE:
-+			wsize = -1; /* special - don't check length */
-+			break;
-+		case ATM_GETESI:
-+			wsize = ESI_LEN;
-+			break;
-+		case ATM_GETSTATZ:
-+			if (!suser()) return -EPERM;
-+			/* fall through */
-+		case ATM_GETSTAT:
-+			wsize = sizeof(struct atm_dev_stats);
-+			break;
-+		case ATM_GETCIRANGE:
-+			wsize = sizeof(struct atm_cirange);
-+			break;
-+		case ATM_SETCIRANGE:
-+			if (!suser()) return -EPERM;
-+			rsize = sizeof(struct atm_cirange);
-+			break;
-+		case SONET_GETSTATZ:
-+			if (!suser()) return -EPERM;
-+			/* fall through */
-+		case SONET_GETSTAT:
-+			wsize = sizeof(struct sonet_stats);
-+			break;
-+		case SONET_GETDIAG:
-+			wsize = sizeof(int);
-+			break;
-+		case SONET_SETDIAG:
-+		case SONET_CLRDIAG:
-+			if (!suser()) return -EPERM;
-+			rsize = sizeof(int);
-+			break;
-+		case SONET_SETFRAMING:
-+			rsize = sizeof(int);
-+			break;
-+		case SONET_GETFRAMING:
-+			wsize = sizeof(int);
-+			break;
-+		case SONET_GETFRSENSE:
-+			wsize = SONET_FRSENSE_SIZE;
-+			break;
-+		default:
-+			wsize = -1; /* just in case ... */
-+	}
-+	error = verify_area(VERIFY_READ,(void *) arg,
-+	    sizeof(struct atmif_sioc));
-+	if (error) return error;
-+	if (wsize) {
-+		error = verify_area(VERIFY_WRITE,(void *) arg,
-+		    sizeof(struct atmif_sioc));
-+		if (error) return error;
-+	}
-+	if (!(dev = atm_find_dev(get_fs_long(&((struct atmif_sioc *) arg)->
-+	    number)))) return -ENODEV;
-+	len = get_fs_long(&((struct atmif_sioc *) arg)->length);
-+	eff_arg = get_fs_long(&((struct atmif_sioc *) arg)->arg);
-+	if (!eff_arg && (rsize || wsize)) return -EINVAL;
-+	if (rsize > 0) {
-+		if (len != rsize) return -EINVAL;
-+		error = verify_area(VERIFY_READ,(void *) eff_arg,len);
-+		if (error) return error;
-+	}
-+	if (wsize > 0) {
-+		if (len != wsize) return -EINVAL;
-+		error = verify_area(VERIFY_WRITE,(void *) eff_arg,len);
-+		if (error) return error;
-+		put_fs_long(wsize,&((struct atmif_sioc *) arg)->length);
-+	}
-+	switch (cmd) {
-+		case ATM_GETTYPE:
-+			{
-+				int length;
-+
-+				length = strlen(dev->type);
-+				if (len <length+1) return -EINVAL;
-+				error = verify_area(VERIFY_WRITE,
-+				    (void *) eff_arg,length+1);
-+				if (error) return error;
-+				memcpy_tofs((void *) eff_arg,dev->type,
-+				    length+1);
-+				put_fs_long(length,
-+				    &((struct atmif_sioc *) arg)->length);
-+				return length;
-+			}
-+		case ATM_GETESI:
-+			memcpy_tofs((void *) eff_arg,dev->esi,ESI_LEN);
-+			return wsize;
-+		case ATM_GETSTATZ:
-+		case ATM_GETSTAT:
-+			error = fetch_stats(dev,(void *) eff_arg,
-+			    cmd == ATM_GETSTATZ);
-+			return error < 0 ? error : wsize;
-+		case ATM_GETCIRANGE:
-+			memcpy_tofs((void *) eff_arg,&dev->ci_range,
-+			    sizeof(struct atm_cirange));
-+			return wsize;
-+		default:
-+			if (!dev->ops->ioctl) return -EINVAL;
-+			error = dev->ops->ioctl(dev,cmd,eff_arg);
-+			if (error >= 0) 
-+				put_fs_long(len,
-+				    &((struct atmif_sioc *) arg)->length);
-+			return error;
-+	}
-+}
-+
-+
-+int atm_setsockopt(struct socket *sock,int level,int optname,
-+    char *optval,int optlen)
-+{
-+	struct atm_vcc *vcc;
-+	int error;
-+
-+	error = verify_area(VERIFY_READ,optval,optlen);
-+	if (error) return -EINVAL;
-+	vcc = ATM_SD(sock);
-+	if (level == SOL_SOCKET) {
-+		unsigned long value;
-+
-+		switch (optname) {
-+			case SO_SNDBUF:
-+				if (optlen != sizeof(value)) return -EINVAL;
-+				value = get_fs_long(optval);
-+				if (!value) value = ATM_TXBQ_DEF;
-+				if (value > ATM_TXBQ_MAX) value = ATM_TXBQ_MAX;
-+				vcc->tx_quota = value*1024;
-+				return 0;
-+			case SO_RCVBUF:
-+				if (optlen != sizeof(value)) return -EINVAL;
-+				value = get_fs_long(optval);
-+				if (!value) value = ATM_RXBQ_DEF;
-+				if (value > ATM_RXBQ_MAX) value = ATM_RXBQ_MAX;
-+				vcc->rx_quota = value*1024;
-+				return 0;
-+			default:
-+				return -EINVAL;
-+		}
-+	}
-+	if (level == SOL_ATM) {
-+		switch (optname) {
-+			case SO_ATMQOS:
-+				if (optlen != sizeof(struct atm_qos))
-+					return -EINVAL;
-+				if (vcc->flags & ATM_VF_HASQOS) return -EINVAL;
-+				memcpy_fromfs(&vcc->qos,optval,optlen);
-+				vcc->flags |= ATM_VF_HASQOS;
-+				return 0;
-+			default:
-+				break;
-+		}
-+	}
-+	if (!vcc->dev || !vcc->dev->ops->setsockopt) return -EINVAL;
-+	return vcc->dev->ops->setsockopt(vcc,level,optname,optval,optlen);
-+}
-+
-+
-+static int putsockopt(void *data,int len,char *optval,int *optlen)
-+{
-+	int error;
-+
-+	if (get_fs_long(optlen) < len) return -EINVAL;
-+	put_fs_long(len,optlen);
-+	error = verify_area(VERIFY_WRITE,optval,len);
-+	if (error) return error;
-+	memcpy_tofs(optval,data,len);
-+	return 0;
-+}
-+
-+
-+int atm_getsockopt(struct socket *sock,int level,int optname,
-+    char *optval,int *optlen)
-+{
-+	struct atm_vcc *vcc;
-+	int error;
-+
-+	error = verify_area(VERIFY_READ,optlen,sizeof(*optlen)); /* paranoia? */
-+	if (error) return error;
-+	error = verify_area(VERIFY_WRITE,optlen,sizeof(*optlen));
-+	if (error) return error;
-+	vcc = ATM_SD(sock);
-+	switch (level) {
-+		case SOL_SOCKET:
-+			switch (optname) {
-+				case SO_SNDBUF:
-+					return putsockopt(&vcc->tx_quota,
-+					    sizeof(unsigned long),optval,
-+					    optlen);
-+				case SO_RCVBUF:
-+					return putsockopt(&vcc->rx_quota,
-+					    sizeof(unsigned long),optval,
-+					    optlen);
-+				case SO_BCTXOPT:
-+				case SO_BCRXOPT:
-+					level = SOL_AAL; /* cheat */
-+					break;
-+				default:
-+					return -EINVAL;
-+			}
-+			/* fall through */
-+		case SOL_AAL:
-+			switch (optname) {
-+				case SO_AALTYPE:
-+					return putsockopt(&vcc->aal,
-+					    sizeof(unsigned char),optval,
-+					    optlen);
-+				default:
-+					break;
-+			}
-+			break;
-+		case SOL_ATM:
-+			switch (optname) {
-+				case SO_ATMQOS:
-+					if (!(vcc->flags & ATM_VF_HASQOS))
-+						return -EINVAL;
-+					return putsockopt(&vcc->qos,
-+					    sizeof(struct atm_qos),optval,
-+					    optlen);
-+				default:
-+					break;
-+			}
-+			/* fall through */
-+		default:
-+			break;
-+	}
-+	if (!vcc->dev || !vcc->dev->ops->getsockopt) return -EINVAL;
-+	return vcc->dev->ops->getsockopt(vcc,level,optname,optval,optlen);
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/common.h	Wed Jul 17 19:58:02 1996
-@@ -0,0 +1,32 @@
-+/* net/atm/common.h - ATM sockets (common part for PVC and SVC) */
-+ 
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef NET_ATM_COMMON_H
-+#define NET_ATM_COMMON_H
-+
-+#include <linux/net.h>
-+
-+int atm_create(struct socket *sock,int protocol);
-+int atm_release(struct socket *sock,struct socket *peer);
-+int atm_connect(struct socket *sock,int itf,short vpi,int vci);
-+int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len,
-+    int nonblock,int flags,int *addr_len);
-+int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len,
-+  int nonblock,int flags);
-+int atm_select(struct socket *sock,int sel_type,select_table *wait);
-+int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg);
-+int atm_setsockopt(struct socket *sock,int level,int optname,char *optval,
-+    int optlen);
-+int atm_getsockopt(struct socket *sock,int level,int optname,char *optval,
-+    int *optlen);
-+
-+int atm_release_vcc(struct atm_vcc *vcc,int free_vcc);
-+
-+/* SVC */
-+
-+int copy_svc_addr(struct sockaddr_atmsvc *to,struct sockaddr_atmsvc *from);
-+void svc_callback(struct atm_vcc *vcc);
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/dev.c	Mon Jun 10 17:36:19 1996
-@@ -0,0 +1,38 @@
-+/* net/atm/dev.c - ATM device registeration */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/atmdev.h>
-+#include <linux/kernel.h>
-+#include <linux/uio.h>
-+
-+#include "static.h"
-+
-+
-+struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
-+    unsigned long flags)
-+{
-+	volatile struct atm_dev *dev; /* need volatile to ensure sequence */
-+
-+	dev = alloc_atm_dev(type);
-+	if (!dev) {
-+		printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
-+		    type);
-+		return NULL;
-+	}
-+	dev->number = 0;
-+	while (atm_find_dev(dev->number)) dev->number++;
-+	dev->vccs = dev->last = NULL;
-+	dev->dev_data = NULL;
-+	dev->ops = ops;
-+	dev->flags = flags;
-+	memset((void *) &dev->stats,0,sizeof(struct atm_dev_stats));
-+	return (struct atm_dev *) dev;
-+}
-+
-+
-+void atm_dev_deregister(struct atm_dev *dev)
-+{
-+	free_atm_dev(dev);
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/pvc.c	Thu Jul 18 21:05:13 1996
-@@ -0,0 +1,172 @@
-+/* net/atm/pvc.c - ATM PVC sockets */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/net.h>		/* struct socket, struct net_proto,
-+				   struct proto_ops */
-+#include <linux/atm.h>		/* ATM stuff */
-+#include <linux/atmdev.h>	/* ATM devices */
-+#include <linux/atmclip.h>	/* Classical IP over ATM */
-+#include <linux/errno.h>	/* error codes */
-+#include <linux/kernel.h>	/* printk */
-+#include <linux/skbuff.h>
-+#ifdef CONFIG_AREQUIPA
-+#include <linux/arequipa.h>
-+#endif
-+
-+#include "static.h"		/* static data ... */
-+#include "common.h"		/* common for PVCs and SVCs */
-+
-+#ifndef NULL
-+#define NULL 0
-+#endif
-+
-+
-+static int pvc_dup(struct socket *newsock,struct socket *oldsock)
-+{
-+	return -EOPNOTSUPP;
-+}
-+
-+
-+static int pvc_shutdown(struct socket *sock,int how)
-+{
-+	return -EOPNOTSUPP;
-+}
-+
-+
-+static int pvc_bind(struct socket *sock,struct sockaddr *sockaddr,
-+    int sockaddr_len)
-+{
-+	struct sockaddr_atmpvc *addr;
-+	struct atm_vcc *vcc;
-+	/*int error;*/
-+
-+	if (sockaddr_len != sizeof(struct sockaddr_atmpvc)) return -EINVAL;
-+	addr = (struct sockaddr_atmpvc *) sockaddr;
-+	if (addr->sap_family != AF_ATMPVC) return -EAFNOSUPPORT;
-+	vcc = ATM_SD(sock);
-+	if (vcc->flags & ATM_VF_PARTIAL) {
-+		if (vcc->vpi != ATM_VPI_UNSPEC) addr->sap_addr.vpi = vcc->vpi;
-+		if (vcc->vci != ATM_VCI_UNSPEC) addr->sap_addr.vci = vcc->vci;
-+	}
-+	else if (vcc->flags & ATM_VF_HASQOS) {
-+			vcc->txtp = vcc->qos.txtp;
-+			vcc->rxtp = vcc->qos.rxtp;
-+		}
-+		else {
-+			vcc->txtp = addr->sap_txtp;
-+			vcc->rxtp = addr->sap_rxtp;
-+		}
-+	return atm_connect(sock,addr->sap_addr.itf,addr->sap_addr.vpi,
-+	    addr->sap_addr.vci);
-+}
-+
-+
-+static int pvc_connect(struct socket *sock,struct sockaddr *sockaddr,
-+    int sockaddr_len,int flags)
-+{
-+	return pvc_bind(sock,sockaddr,sockaddr_len);
-+}
-+
-+
-+static int pvc_listen(struct socket *sock,int backlog)
-+{
-+	return -EOPNOTSUPP;
-+}
-+
-+
-+static int pvc_accept(struct socket *sock,struct socket *newsock,int flags)
-+{
-+	return -EOPNOTSUPP;
-+}
-+
-+
-+static int pvc_getname(struct socket *sock,struct sockaddr *sockaddr,
-+    int *sockaddr_len,int peer)
-+{
-+	struct sockaddr_atmpvc *addr;
-+	struct atm_vcc *vcc;
-+
-+#if 0 /* add some sanity checks later ... @@@ */
-+	if (sock->state != SS_CONNECTED) return -EINVAL;
-+#endif
-+	if (*sockaddr_len < sizeof(struct sockaddr_atmpvc)) return -EINVAL;
-+        *sockaddr_len = sizeof(struct sockaddr_atmpvc);
-+	addr = (struct sockaddr_atmpvc *) sockaddr;
-+	vcc = ATM_SD(sock);
-+	addr->sap_family = AF_ATMPVC;
-+	addr->sap_addr.itf = vcc->dev->number;
-+	addr->sap_addr.vpi = vcc->vpi;
-+	addr->sap_addr.vci = vcc->vci;
-+	memcpy(&addr->sap_txtp,&vcc->txtp,sizeof(struct atm_trafprm));
-+	memcpy(&addr->sap_rxtp,&vcc->rxtp,sizeof(struct atm_trafprm));
-+	return 0;
-+}
-+
-+
-+static int pvc_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
-+{
-+	/* put PVC-specific code here */
-+	return atm_ioctl(sock,cmd,arg);
-+}
-+
-+
-+static int pvc_setsockopt(struct socket *sock,int level,int optname,
-+    char *optval,int optlen)
-+{
-+	/* put PVC-specific code here */
-+	return atm_setsockopt(sock,level,optname,optval,optlen);
-+}
-+
-+
-+static int pvc_getsockopt(struct socket *sock,int level,int optname,
-+    char *optval,int *optlen)
-+{
-+	/* put PVC-specific code here */
-+	return atm_getsockopt(sock,level,optname,optval,optlen);
-+}
-+
-+
-+static struct proto_ops pvc_proto_ops = {
-+	PF_ATMPVC,
-+	atm_create,
-+	pvc_dup,
-+	atm_release,
-+	pvc_bind,
-+	pvc_connect,
-+	NULL,			/* no socketpair */
-+	pvc_accept,
-+	pvc_getname,
-+	atm_select,
-+	pvc_ioctl,
-+	pvc_listen,
-+	pvc_shutdown,
-+	pvc_setsockopt,
-+	pvc_getsockopt,
-+	NULL,			/* no fcntl */
-+	atm_sendmsg,
-+	atm_recvmsg
-+};
-+
-+
-+/*
-+ *	Initialize the ATM PVC protocol family
-+ */
-+
-+
-+extern int atmdev_init(void);
-+
-+
-+void atmpvc_proto_init(struct net_proto *pro)
-+{
-+	atm_static_init();
-+	if (sock_register(pvc_proto_ops.family,&pvc_proto_ops) < 0) {
-+		printk(KERN_ERR "ATMPVC: can't register");
-+		return;
-+	}
-+#ifdef CONFIG_AREQUIPA
-+	(void) atm_init_arequipa();
-+#endif
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/static.c	Fri Jul 19 15:21:05 1996
-@@ -0,0 +1,114 @@
-+/* net/atm/static.c - Staticly allocated resources */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/ctype.h>
-+#include <linux/string.h>
-+#include <linux/atmdev.h>
-+#include <asm/segment.h> /* for get_fs_long and put_fs_long */
-+
-+#include "static.h"
-+
-+
-+#ifndef NULL
-+#define NULL 0
-+#endif
-+
-+
-+struct atm_dev atm_dev[MAX_ATM_ITF];
-+struct atm_vcc atm_vcc[MAX_ATM_VCC];
-+
-+
-+void atm_static_init(void)
-+{
-+	int i;
-+
-+	for (i = 0; i < MAX_ATM_ITF; i++) atm_dev[i].ops = NULL;
-+	for (i = 0; i < MAX_ATM_VCC; i++) atm_vcc[i].family = 0;
-+}
-+
-+
-+struct atm_dev *alloc_atm_dev(const char *type)
-+{
-+	static int curr_dev = 0;
-+	int last_dev;
-+
-+	last_dev = curr_dev;
-+	do {
-+		if (!atm_dev[curr_dev].ops) {
-+			memset(&atm_dev[curr_dev],0,sizeof(struct atm_dev));
-+			atm_dev[curr_dev].type = type;
-+			return atm_dev+curr_dev;
-+		}
-+		if (++curr_dev == MAX_ATM_ITF) curr_dev = 0;
-+	}
-+	while (last_dev != curr_dev);
-+	return NULL;
-+}
-+
-+
-+struct atm_vcc *alloc_atm_vcc(void)
-+{
-+	static int curr_vcc = 0;
-+	int last_vcc;
-+
-+	last_vcc = curr_vcc;
-+	do {
-+		if (!atm_vcc[curr_vcc].family) return atm_vcc+curr_vcc;
-+		if (++curr_vcc == MAX_ATM_VCC) curr_vcc = 0;
-+	}
-+	while (last_vcc != curr_vcc);
-+	return NULL;
-+}
-+
-+
-+void free_atm_dev(struct atm_dev *dev)
-+{
-+	dev->ops = NULL;
-+}
-+
-+
-+void free_atm_vcc(struct atm_vcc *vcc)
-+{
-+	vcc->family = 0;
-+}
-+
-+
-+struct atm_dev *atm_find_dev(int number)
-+{
-+	int i;
-+
-+	for (i = 0; i < MAX_ATM_ITF; i++)
-+		if (atm_dev[i].ops && atm_dev[i].number == number)
-+			return &atm_dev[i];
-+	return NULL;
-+}
-+
-+
-+int atm_dev_list(int *buffer,int *u_length)
-+{
-+	int length,left,i;
-+
-+	length = get_fs_long(u_length);
-+	left = length/sizeof(int);
-+	if (!left) return -EINVAL;
-+	for (i = 0; i < MAX_ATM_ITF; i++)
-+		if (atm_dev[i].ops) {
-+			if (!left) break;
-+			put_fs_long(atm_dev[i].number,buffer);
-+			buffer++;
-+			left--;
-+		}
-+	put_fs_long(length-(length/sizeof(int)-left)*sizeof(int),u_length);
-+	return length/sizeof(int)-left;
-+}
-+
-+
-+void for_all_vccs(void (*fn)(struct atm_vcc *vcc))
-+{
-+	int i;
-+
-+	for (i = 0; i < MAX_ATM_VCC; i++)
-+		if (atm_vcc[i].family) fn(atm_vcc+i);
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/static.h	Fri Jul 19 15:10:11 1996
-@@ -0,0 +1,29 @@
-+/* net/atm/static.h - Staticly allocated resources */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef NET_ATM_STATIC_H
-+#define NET_ATM_STATIC_H
-+
-+#include <linux/atmdev.h>
-+
-+
-+/*
-+ * This is evil ...
-+ */
-+
-+extern struct atm_dev atm_dev[MAX_ATM_ITF];
-+extern struct atm_vcc atm_vcc[MAX_ATM_VCC];
-+
-+
-+void atm_static_init(void);
-+struct atm_dev *alloc_atm_dev(const char *type);
-+struct atm_vcc *alloc_atm_vcc(void);
-+void free_atm_dev(struct atm_dev *dev);
-+void free_atm_vcc(struct atm_vcc *vcc);
-+struct atm_dev *atm_find_dev(int number);
-+int atm_dev_list(int *buffer,int *u_length);
-+void for_all_vccs(void (*fn)(struct atm_vcc *vcc));
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/svc.c	Thu Jul  4 19:50:34 1996
-@@ -0,0 +1,625 @@
-+/* net/atm/svc.c - ATM SVC sockets */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/string.h>
-+#include <linux/net.h>		/* struct socket, struct net_proto,
-+				   struct proto_ops */
-+#include <linux/errno.h>	/* error codes */
-+#include <linux/kernel.h>	/* printk */
-+#include <linux/skbuff.h>
-+#include <linux/wait.h>
-+#include <linux/sched.h>	/* jiffies and HZ */
-+#include <linux/fcntl.h>	/* O_NONBLOCK */
-+#include <linux/atm.h>		/* ATM stuff */
-+#include <linux/atmsap.h>
-+#include <linux/atmsvc.h>
-+#include <linux/atmdev.h>
-+
-+#include "static.h"
-+#include "common.h"		/* common for PVCs and SVCs */
-+#include "signaling.h"
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+/*
-+ * Note: since all this is still nicely synchronized with the signaling demon,
-+ *       there's no need to protect sleep loops with clis. If signaling is
-+ *       moved into the kernel, that would change.
-+ */
-+
-+
-+void svc_callback(struct atm_vcc *vcc)
-+{
-+	wake_up(&vcc->sleep);
-+}
-+
-+
-+static int svc_create(struct socket *sock,int protocol)
-+{
-+	int error;
-+
-+	error = atm_create(sock,protocol);
-+	if (error) return error;
-+	ATM_SD(sock)->callback = svc_callback;
-+	return 0;
-+}
-+
-+
-+static int svc_dup(struct socket *newsock,struct socket *oldsock)
-+{
-+	return svc_create(newsock,ATM_SD(oldsock)->aal);
-+}
-+
-+
-+static int svc_shutdown(struct socket *sock,int how)
-+{
-+	return -EOPNOTSUPP;
-+}
-+
-+
-+static void svc_disconnect(struct atm_vcc *vcc)
-+{
-+	struct sk_buff *skb;
-+
-+	DPRINTK("svc_disconnect\n");
-+	if (vcc->flags & ATM_VF_REGIS) {
-+		sigd_enq(vcc,as_close,NULL,NULL,NULL);
-+		while (!(vcc->flags & ATM_VF_RELEASED) && sigd)
-+		    sleep_on(&vcc->sleep);
-+	}
-+	while ((skb = skb_dequeue(&vcc->listenq))) {
-+		vcc->flags &= ~ATM_VF_RELEASED;
-+		DPRINTK("LISTEN REL\n");
-+		sigd_enq(NULL,as_close,vcc,NULL,NULL); /* reject */
-+		while (!(vcc->flags & ATM_VF_RELEASED) && sigd)
-+		    sleep_on(&vcc->sleep);
-+		dev_kfree_skb(skb,FREE_WRITE);
-+	}
-+	vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_RELEASED); /* may retry later */
-+}
-+
-+
-+static int svc_release(struct socket *sock,struct socket *peer)
-+{
-+	DPRINTK("svc_release\n");
-+	if (!ATM_SD(sock)) return 0;
-+	ATM_SD(sock)->flags &= ~ATM_VF_READY;
-+	atm_release_vcc(ATM_SD(sock),0);
-+	svc_disconnect(ATM_SD(sock));
-+	    /* VCC pointer is used as a reference, so we must not free it
-+	       (thereby subjecting it to re-use) before all pending connections
-+	        are closed */
-+	free_atm_vcc(ATM_SD(sock));
-+	return 0;
-+}
-+
-+
-+int copy_svc_addr(struct sockaddr_atmsvc *to,struct sockaddr_atmsvc *from)
-+{
-+	struct atm_blli *walk,**link,*next;
-+	int error,bllis;
-+
-+	*to = *from;
-+	link = &to->sas_addr.blli;
-+	error = bllis = 0;
-+	for (walk = from->sas_addr.blli; walk; walk = (struct atm_blli *)
-+	    get_fs_long((unsigned long) &walk->next)) {
-+		if (++bllis > ATM_MAX_BLLI) {
-+			error = -E2BIG;
-+			break;
-+		}
-+		*link = kmalloc(sizeof(struct atm_blli),GFP_KERNEL);
-+		if (!*link) {
-+			error = -ENOMEM;
-+			break;
-+		}
-+		error = verify_area(VERIFY_READ,walk,sizeof(struct atm_blli));
-+		    /* kmalloc may swap out user pages */
-+		if (error) break;
-+		memcpy_fromfs(*link,walk,sizeof(struct atm_blli));
-+		link = &(*link)->next;
-+	}
-+	*link = NULL;
-+	if (!error) return 0;
-+	for (walk = to->sas_addr.blli; walk; walk = next) {
-+		next = walk->next;
-+		kfree(walk);
-+	}
-+	return error;
-+}
-+
-+
-+static int svc_bind(struct socket *sock,struct sockaddr *sockaddr,
-+    int sockaddr_len)
-+{
-+	struct sockaddr_atmsvc *addr;
-+	struct atm_vcc *vcc;
-+	int error;
-+
-+	if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) return -EINVAL;
-+	if (sock->state == SS_CONNECTED) return -EISCONN;
-+	if (sock->state != SS_UNCONNECTED) return -EINVAL;
-+	addr = (struct sockaddr_atmsvc *) sockaddr;
-+	if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
-+	vcc = ATM_SD(sock);
-+	vcc->flags &= ~ATM_VF_BOUND; /* failing rebind will kill old binding */
-+	/* @@@ check memory (de)allocation on rebind */
-+	if (vcc->flags & ATM_VF_HASQOS) {
-+		addr->sas_txtp = vcc->qos.txtp;
-+		addr->sas_rxtp = vcc->qos.rxtp;
-+	}
-+	error = copy_svc_addr(&vcc->local,addr);
-+	if (error) return error;
-+	vcc->reply = WAITING;
-+	sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local);
-+	while (vcc->reply == WAITING && sigd) sleep_on(&vcc->sleep);
-+	vcc->flags &= ~ATM_VF_REGIS; /* doesn't count */
-+	if (!sigd) return -EUNATCH; /* good code ? @@@ */
-+        if (!vcc->reply) vcc->flags |= ATM_VF_BOUND;
-+	return vcc->reply;
-+}
-+
-+
-+static int svc_connect(struct socket *sock,struct sockaddr *sockaddr,
-+    int sockaddr_len,int flags)
-+{
-+	struct sockaddr_atmsvc *addr;
-+	struct atm_vcc *vcc;
-+	int error;
-+
-+	DPRINTK("svc_connect\n");
-+	if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) return -EINVAL;
-+	if (sock->state == SS_CONNECTED) return -EISCONN;
-+	vcc = ATM_SD(sock);
-+	if (sock->state == SS_CONNECTING) {
-+		if (vcc->reply == WAITING) return -EALREADY;
-+		sock->state = SS_UNCONNECTED;
-+		if (vcc->reply) return vcc->reply;
-+	}
-+	else {
-+		if (sock->state != SS_UNCONNECTED) return -EINVAL;
-+		addr = (struct sockaddr_atmsvc *) sockaddr;
-+		if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
-+		if (vcc->flags & ATM_VF_HASQOS) {
-+			addr->sas_txtp = vcc->qos.txtp;
-+			addr->sas_rxtp = vcc->qos.rxtp;
-+		}
-+		if (addr->sas_txtp.class == ATM_ANYCLASS ||
-+		    addr->sas_rxtp.class == ATM_ANYCLASS) return -EINVAL;
-+		if (!addr->sas_txtp.class && !addr->sas_rxtp.class)
-+			return -EINVAL;
-+		error = copy_svc_addr(&vcc->remote,addr);
-+		if (error) return error;
-+		vcc->reply = WAITING;
-+		sigd_enq(vcc,as_establish,NULL,NULL,&vcc->remote);
-+		if (flags & O_NONBLOCK) {
-+			sock->state = SS_CONNECTING;
-+			return -EINPROGRESS;
-+		}
-+		while (vcc->reply == WAITING && sigd) {
-+			interruptible_sleep_on(&vcc->sleep);
-+			if (current->signal & ~current->blocked) {
-+				DPRINTK("*ABORT*\n");
-+				/*
-+				 * This is tricky:
-+				 *   Kernel -------close------> Demon
-+				 *   Kernel <-----close(0)----- Demon
-+			         * or
-+				 *   Kernel -------close------> Demon
-+				 *   Kernel <--close(error)---- Demon
-+				 * or
-+				 *   Kernel -------close------> Demon
-+				 *   Kernel <------okay-------- Demon
-+				 *   Kernel <-----close(0)----- Demon
-+				 */
-+				vcc->flags &= ~ATM_VF_RELEASED;
-+				sigd_enq(vcc,as_close,NULL,NULL,NULL);
-+				while (vcc->reply == WAITING && sigd)
-+					sleep_on(&vcc->sleep);
-+				if (!vcc->reply)
-+					while (!(vcc->flags & ATM_VF_RELEASED)
-+					    && sigd) sleep_on(&vcc->sleep);
-+				vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_RELEASED);
-+				    /* we're gone now but may connect later */
-+				return -EINTR;
-+			}
-+		}
-+		if (!sigd) return -EUNATCH;
-+		if (vcc->reply) return vcc->reply;
-+	}
-+/*
-+ * Not supported yet
-+ *
-+ * #ifndef CONFIG_SINGLE_SIGITF
-+ */
-+	vcc->txtp.max_pcr = SELECT_TOP_PCR(vcc->txtp);
-+	vcc->txtp.min_pcr = 0;
-+/*
-+ * #endif
-+ */
-+	if (!(error = atm_connect(sock,vcc->itf,vcc->vpi,vcc->vci)))
-+		sock->state = SS_CONNECTED;
-+	else (void) svc_disconnect(ATM_SD(sock));
-+	return error;
-+}
-+
-+
-+static int svc_listen(struct socket *sock,int backlog)
-+{
-+	struct atm_vcc *vcc;
-+
-+	DPRINTK("svc_listen\n");
-+	/* let server handle listen on unbound sockets */
-+	vcc = ATM_SD(sock);
-+	vcc->reply = WAITING;
-+	sigd_enq(vcc,as_listen,NULL,NULL,&vcc->local);
-+	while (vcc->reply == WAITING && sigd) sleep_on(&vcc->sleep);
-+	if (!sigd) return -EUNATCH;
-+	vcc->flags |= ATM_VF_LISTEN;
-+	vcc->backlog_quota = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT;
-+	return vcc->reply;
-+}
-+
-+
-+static int svc_accept(struct socket *sock,struct socket *newsock,int flags)
-+{
-+	struct sk_buff *skb;
-+	struct atmsvc_msg *msg;
-+	struct atm_vcc *old_vcc,*new_vcc;
-+	int error;
-+
-+	DPRINTK("svc_accept\n");
-+	old_vcc = ATM_SD(sock);
-+	new_vcc = ATM_SD(newsock);
-+	while (1) {
-+		while (!(skb = skb_dequeue(&old_vcc->listenq)) && sigd) {
-+			if (flags & O_NONBLOCK) return 0; /* not -EAGAIN ? */
-+			interruptible_sleep_on(&old_vcc->sleep);
-+			if (current->signal & ~current->blocked)
-+				return -ERESTARTSYS;
-+		}
-+		/* should try to atm_connect now and possibly send a close on
-+		   error */
-+		/* wait should be short, so we ignore the non-blocking flag */
-+		new_vcc->reply = WAITING;
-+		sigd_enq(new_vcc,as_establish,old_vcc,NULL,NULL);
-+		while (new_vcc->reply == WAITING && sigd)
-+			sleep_on(&new_vcc->sleep);
-+		if (!sigd) return -EUNATCH;
-+		old_vcc->backlog_quota++;
-+		if (!new_vcc->reply) break;
-+		dev_kfree_skb(skb,FREE_WRITE);
-+	}
-+	if (!sigd) return -EUNATCH;
-+	msg = (struct atmsvc_msg *) skb->data;
-+	new_vcc->txtp = msg->pvc.sap_txtp;
-+	new_vcc->rxtp = msg->pvc.sap_rxtp;
-+	new_vcc->remote = msg->svc;
-+	/* copy BLLI @@@ */
-+	new_vcc->remote.sas_addr.blli = NULL;
-+	error = atm_connect(newsock,msg->pvc.sap_addr.itf,msg->pvc.sap_addr.vpi,
-+	    msg->pvc.sap_addr.vci);
-+	dev_kfree_skb(skb,FREE_WRITE);
-+	if (error) {
-+		(void) svc_disconnect(ATM_SD(newsock));
-+		/* @@@ or should we try additional connections just until
-+		   we'd block ? (then we MUST return) */
-+		return error == -EAGAIN ? -EBUSY : error;
-+	}
-+	newsock->state = SS_CONNECTED;
-+	return 0;
-+}
-+
-+
-+static int svc_getname(struct socket *sock,struct sockaddr *sockaddr,
-+    int *sockaddr_len,int peer)
-+{
-+	struct sockaddr_atmsvc *addr;
-+
-+	/* this will be fun ... we have: txtp/rxtp, public and private
-+	   address, bhli and possibly a lot of bllis. Now address buffers
-+	   are static ... argl */
-+	/* The solution: use pointers to link bllis */
-+	/* naturally, we still need some place to put all those nasty
-+	   little bllis ... */
-+#if 0
-+	/* sigh ... */
-+	if (*sockaddr_len < sizeof(struct sockaddr_atmsvc)) return -EINVAL;
-+#endif
-+	*sockaddr_len = sizeof(struct sockaddr_atmsvc);
-+	addr = (struct sockaddr_atmsvc *) sockaddr;
-+	memcpy(addr,peer ? &ATM_SD(sock)->remote : &ATM_SD(sock)->local,
-+	    sizeof(struct sockaddr_atmsvc));
-+	addr->sas_addr.blli = NULL; /* @@@ no - copy it */
-+	memcpy(&addr->sas_txtp,&ATM_SD(sock)->txtp,sizeof(struct atm_trafprm));
-+	memcpy(&addr->sas_rxtp,&ATM_SD(sock)->rxtp,sizeof(struct atm_trafprm));
-+	return 0;
-+}
-+
-+
-+static int check_addr(struct sockaddr_atmsvc *addr)
-+{
-+	int i;
-+
-+	if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
-+	if (!*addr->sas_addr.prv)
-+		if (!*addr->sas_addr.pub) return -EINVAL;
-+		else return 0;
-+	for (i = 1; i < ATM_E164_LEN+1; i++)
-+		if (!addr->sas_addr.prv[i]) return 0;
-+	return -EINVAL;
-+}
-+
-+
-+static int identical(struct sockaddr_atmsvc *a,struct sockaddr_atmsvc *b)
-+{
-+	if (*a->sas_addr.prv)
-+		if (memcmp(a->sas_addr.prv,b->sas_addr.prv,ATM_ESA_LEN))
-+			return 0;
-+	if (!*a->sas_addr.pub) return !*b->sas_addr.pub;
-+	if (!*b->sas_addr.pub) return 0;
-+	return !strcmp(a->sas_addr.pub,b->sas_addr.pub);
-+}
-+
-+
-+/*
-+ * Avoid modification of any list of local interfaces while reading it
-+ * (which may involve page faults and therefore rescheduling)
-+ */
-+
-+
-+static volatile int local_lock = 0;
-+static struct wait_queue *local_wait = NULL;
-+
-+
-+static void lock_local(void)
-+{
-+	while (local_lock) sleep_on(&local_wait);
-+	local_lock = 1;
-+}
-+
-+
-+static void unlock_local(void)
-+{
-+	local_lock = 0;
-+	wake_up(&local_wait);
-+}
-+
-+
-+static void notify_sigd(struct atm_dev *dev)
-+{
-+	struct sockaddr_atmpvc pvc;
-+
-+	pvc.sap_addr.itf = dev->number;
-+	sigd_enq(NULL,as_itf_notify,NULL,&pvc,NULL);
-+}
-+
-+
-+static void reset_addr(struct atm_dev *dev)
-+{
-+	struct atm_dev_addr *this;
-+
-+	lock_local();
-+	while (dev->local) {
-+		this = dev->local;
-+		dev->local = this->next;
-+		kfree(this);
-+	}
-+	unlock_local();
-+	notify_sigd(dev);
-+}
-+
-+
-+static int add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
-+{
-+	struct atm_dev_addr **walk;
-+
-+	lock_local();
-+	for (walk = &dev->local; *walk; walk = &(*walk)->next)
-+		if (identical(&(*walk)->addr,addr)) {
-+			unlock_local();
-+			return -EEXIST;
-+		}
-+	*walk = kmalloc(sizeof(struct atm_dev_addr),GFP_KERNEL);
-+	if (!*walk) {
-+		unlock_local();
-+		return -ENOMEM;
-+	}
-+	(*walk)->addr = *addr;
-+	(*walk)->next = NULL;
-+	unlock_local();
-+	notify_sigd(dev);
-+	return 0;
-+}
-+
-+
-+static int del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
-+{
-+	struct atm_dev_addr **walk,*this;
-+
-+	lock_local();
-+	for (walk = &dev->local; *walk; walk = &(*walk)->next)
-+		if (identical(&(*walk)->addr,addr)) break;
-+	if (!*walk) {
-+		unlock_local();
-+		return -ENOENT;
-+	}
-+	this = *walk;
-+	*walk = this->next;
-+	kfree(this);
-+	unlock_local();
-+	notify_sigd(dev);
-+	return 0;
-+}
-+
-+
-+static int get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *buf,int size)
-+{
-+	struct atm_dev_addr *walk;
-+	int total;
-+
-+	lock_local();
-+	total = 0;
-+	for (walk = dev->local; walk; walk = walk->next) {
-+		total += sizeof(struct sockaddr_atmsvc);
-+		if (total > size) {
-+			unlock_local();
-+			return -E2BIG;
-+		}
-+		memcpy_tofs(buf,&walk->addr,sizeof(struct sockaddr_atmsvc));
-+		buf++;
-+	}
-+	unlock_local();
-+	return total;
-+}
-+
-+
-+static int svc_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
-+{
-+	struct atm_dev *dev;
-+	void *buf;
-+	int rsize,wsize,len;
-+	int error;
-+
-+	rsize = wsize = 0;
-+	switch (cmd) {
-+		case SIOCGIFATMADDR:
-+			wsize = sizeof(struct sockaddr_atmsvc);
-+			break;
-+		case SIOCSIFATMADDR:
-+		case ATM_ADDADDR:
-+		case ATM_DELADDR:
-+			rsize = sizeof(struct sockaddr_atmsvc);
-+		case ATM_RSTADDR:
-+			if (!suser()) return -EPERM;
-+			break;
-+		case ATM_GETADDR:
-+			wsize = -1;
-+			break;
-+		default:
-+			return atm_ioctl(sock,cmd,arg);
-+	}
-+	error = verify_area(VERIFY_READ,(void *) arg,
-+	    sizeof(struct atmif_sioc));
-+	if (error) return error;
-+	if (wsize) {
-+		error = verify_area(VERIFY_WRITE,(void *) arg,
-+		    sizeof(struct atmif_sioc));
-+		if (error) return error;
-+	}
-+	if (!(dev = atm_find_dev(get_fs_long(&((struct atmif_sioc *) arg)->
-+	    number)))) return -ENODEV;
-+	len = get_fs_long(&((struct atmif_sioc *) arg)->length);
-+	buf = (void *) get_fs_long(&((struct atmif_sioc *) arg)->arg);
-+	if (cmd == SIOCSIFATMADDR && !buf) rsize = 0; /* @@@ */
-+	if (!buf && (rsize || wsize)) return -EINVAL;
-+	if (rsize > 0) {
-+		if (len != rsize) return -EINVAL;
-+		error = verify_area(VERIFY_READ,buf,len);
-+		if (error) return error;
-+	}
-+	if (wsize > 0) {
-+		if (len != wsize) return -EINVAL;
-+		error = verify_area(VERIFY_WRITE,buf,len);
-+		if (error) return error;
-+		put_fs_long(wsize,&((struct atmif_sioc *) arg)->length);
-+	}
-+	switch (cmd) {
-+		case SIOCGIFATMADDR:
-+			{
-+				struct sockaddr_atmsvc addr;
-+
-+				memset(&addr,0,sizeof(addr));
-+				memcpy_tofs((struct sockaddr_atmsvc *) buf,
-+				    dev->local ? &dev->local->addr : &addr,
-+				    sizeof(struct sockaddr_atmsvc));
-+				return wsize;
-+			}
-+		case ATM_RSTADDR:
-+			reset_addr(dev);
-+			return 0;
-+		case SIOCSIFATMADDR: /* OBSOLETE - REMOVE WHEN ILMID UPDATED */
-+			reset_addr(dev);
-+			if (!buf) return 0;
-+			cmd = ATM_ADDADDR;
-+			/* fall through */
-+		case ATM_ADDADDR:
-+		case ATM_DELADDR:
-+			{
-+				struct sockaddr_atmsvc addr;
-+
-+				memcpy_fromfs(&addr,buf,sizeof(addr));
-+				error = check_addr(&addr);
-+				if (error) return error;
-+				if (cmd == ATM_ADDADDR)
-+					return add_addr(dev,&addr);
-+				else return del_addr(dev,&addr);
-+			}
-+		case ATM_GETADDR:
-+			error = get_addr(dev,buf,len);
-+			if (error < 0) return error;
-+			put_fs_long(error,
-+				&((struct atmif_sioc *) arg)->length);
-+			return error;
-+	}
-+	return -EINVAL; /* actually, we're in trouble if we end up here ... */
-+}
-+
-+
-+static int svc_setsockopt(struct socket *sock,int level,int optname,
-+    char *optval,int optlen)
-+{
-+	/* stuff for SVCs */
-+	return atm_setsockopt(sock,level,optname,optval,optlen);
-+}
-+
-+
-+static int svc_getsockopt(struct socket *sock,int level,int optname,
-+    char *optval,int *optlen)
-+{
-+	/* stuff for SVCs */
-+	return atm_getsockopt(sock,level,optname,optval,optlen);
-+}
-+
-+
-+static struct proto_ops svc_proto_ops = {
-+	PF_ATMSVC,
-+	svc_create,
-+	svc_dup,
-+	svc_release,
-+	svc_bind,
-+	svc_connect,
-+	NULL,			/* no socketpair */
-+	svc_accept,
-+	svc_getname,
-+	atm_select,
-+	svc_ioctl,
-+	svc_listen,
-+	svc_shutdown,
-+	svc_setsockopt,
-+	svc_getsockopt,
-+	NULL,			/* no fcntl */
-+	atm_sendmsg,
-+	atm_recvmsg
-+};
-+
-+
-+/*
-+ *	Initialize the ATM SVC protocol family
-+ */
-+
-+void atmsvc_proto_init(struct net_proto *pro)
-+{
-+	if (sock_register(svc_proto_ops.family,&svc_proto_ops) < 0) {
-+		printk(KERN_ERR "ATMSVC: can't register");
-+		return;
-+	}
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/signaling.c	Fri Jul 12 17:05:22 1996
-@@ -0,0 +1,238 @@
-+/* net/atm/signaling.c - ATM signaling */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/errno.h>	/* error codes */
-+#include <linux/kernel.h>	/* printk, suser */
-+#include <linux/skbuff.h>
-+#include <linux/wait.h>
-+#include <linux/sched.h>	/* jiffies and HZ */
-+#include <linux/atm.h>		/* ATM stuff */
-+#include <linux/atmsap.h>
-+#include <linux/atmsvc.h>
-+#include <linux/atmdev.h>
-+
-+#include "static.h"
-+#include "signaling.h"
-+
-+
-+#undef WAIT_FOR_DEMON		/* #define this if system calls on SVC sockets
-+				   should block until the demon runs.
-+				   Danger: may cause nasty hangs if the demon
-+				   crashes. */
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+struct atm_vcc *sigd = NULL;
-+static struct wait_queue *sigd_sleep = NULL;
-+
-+
-+static int sigd_send(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	struct atmsvc_msg *msg;
-+
-+	msg = (struct atmsvc_msg *) skb->data;
-+	DPRINTK("sigd_send %d (0x%lx)\n",(int) msg->type,msg->vcc);
-+	vcc = (struct atm_vcc *) msg->vcc;
-+	if (!vcc) vcc = (struct atm_vcc *) msg->listen_vcc;
-+	switch (msg->type) {
-+		case as_okay:
-+			vcc->reply = msg->reply;
-+			if (!*vcc->local.sas_addr.prv &&
-+			    !*vcc->local.sas_addr.pub) {
-+				vcc->local.sas_family = AF_ATMSVC;
-+				memcpy(vcc->local.sas_addr.prv,
-+				    msg->local.sas_addr.prv,ATM_ESA_LEN);
-+				memcpy(vcc->local.sas_addr.pub,
-+				    msg->local.sas_addr.pub,ATM_E164_LEN+1);
-+			}
-+			if (vcc->vpi || vcc->vci) break;
-+			vcc->itf = msg->pvc.sap_addr.itf;
-+			vcc->vpi = msg->pvc.sap_addr.vpi;
-+			vcc->vci = msg->pvc.sap_addr.vci;
-+			vcc->txtp = msg->pvc.sap_txtp;
-+			vcc->rxtp = msg->pvc.sap_rxtp;
-+			break;
-+		case as_indicate:
-+			DPRINTK("as_indicate!!!\n");
-+			if (!vcc->backlog_quota) {
-+				sigd_enq(0,as_close,(struct atm_vcc *)
-+				    msg->listen_vcc,NULL,NULL);
-+				return 0;
-+			}
-+			vcc->backlog_quota--;
-+			skb_queue_tail(&vcc->listenq,skb);
-+			if (vcc->callback) {
-+				DPRINTK("waking vcc->sleep 0x%lx\n",
-+				    (unsigned long) &vcc->sleep);
-+				vcc->callback(vcc);
-+			}
-+			return 0;
-+		case as_close:
-+			if (msg->reply < 0) {
-+				vcc->flags &= ~ATM_VF_REGIS;
-+				vcc->reply = msg->reply;
-+			}
-+			else {
-+				vcc->flags |= ATM_VF_RELEASED;
-+				vcc->reply = -msg->reply;
-+			}
-+			vcc->flags &= ~ATM_VF_READY;
-+			break;
-+		default:
-+			printk(KERN_ALERT "sigd_send: bad message type %d\n",
-+			    (int) msg->type);
-+			return -EINVAL;
-+	}
-+	if (vcc->callback) vcc->callback(vcc);
-+	dev_kfree_skb(skb,FREE_WRITE);
-+	return 0;
-+}
-+
-+
-+static void _sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
-+    const struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
-+    const struct sockaddr_atmsvc *svc,int atomic)
-+{
-+	struct sk_buff *skb;
-+	struct atmsvc_msg *msg;
-+	struct atm_blli *walk;
-+	int size,i;
-+#ifdef WAIT_FOR_DEMON
-+	static unsigned long silence = 0;
-+#endif
-+
-+	DPRINTK("sigd_enq %d (0x%lx)\n",(int) type,(unsigned long) vcc);
-+	size = sizeof(struct atmsvc_msg)-sizeof(struct atm_blli);
-+	if (svc)
-+		for (walk = svc->sas_addr.blli; walk; walk = walk->next)
-+			size += sizeof(struct atm_blli);
-+	if (!atomic) while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule();
-+	else if (!(skb = alloc_skb(size,GFP_ATOMIC))) {
-+			printk(KERN_ALERT "sigd_enq: no memory for message\n");
-+			return;
-+		}
-+	skb->free = 1;
-+	skb->len = size;
-+	msg = (struct atmsvc_msg *) skb->data;
-+	msg->type = type;
-+	msg->vcc = (unsigned long) vcc;
-+	msg->listen_vcc = (unsigned long) listen_vcc;
-+	msg->aal = vcc ? vcc->aal : 0;
-+	if (!svc) msg->svc.sas_family = 0;
-+	else {
-+		msg->svc = *svc;
-+		i = 0;
-+		for (walk = svc->sas_addr.blli; walk; walk = walk->next)
-+			msg->blli[i++] = *walk;
-+	}
-+	if (vcc) msg->local = vcc->local;
-+	if (!pvc) memset(&msg->pvc,0,sizeof(msg->pvc));
-+	else msg->pvc = *pvc;
-+	while (!sigd) {
-+#ifdef WAIT_FOR_DEMON
-+/*
-+ * Don't ever enable this as Arequipa depends on being able to send a
-+ * message to the signaling demon from an interrupt.
-+ */
-+		if (silence < jiffies) {
-+			printk(KERN_INFO "atmsvc: waiting for signaling demon "
-+			    "...\n");
-+			silence = jiffies+30*HZ;
-+		}
-+		sleep_on(&sigd_sleep);
-+#else
-+		printk(KERN_WARNING "atmsvc: no signaling demon\n");
-+		kfree_skb(skb,FREE_READ);
-+		return;
-+#endif
-+	}
-+	skb_queue_tail(&sigd->recvq,skb);
-+	wake_up(&sigd->sleep);
-+	if (vcc) vcc->flags |= ATM_VF_REGIS;
-+}
-+
-+
-+void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
-+    const struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
-+    const struct sockaddr_atmsvc *svc)
-+{
-+	_sigd_enq(vcc,type,listen_vcc,pvc,svc,0);
-+}
-+
-+
-+void sigd_enq_atomic(struct atm_vcc *vcc,enum atmsvc_msg_type type,
-+    const struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
-+    const struct sockaddr_atmsvc *svc)
-+{
-+	_sigd_enq(vcc,type,listen_vcc,pvc,svc,1);
-+}
-+
-+
-+static void trigger(struct atm_vcc *vcc)
-+{
-+	if (vcc->family == PF_ATMSVC && !(vcc->flags & ATM_VF_META)) {
-+		vcc->flags |= ATM_VF_RELEASED;
-+		vcc->reply = -EUNATCH;
-+		wake_up(&vcc->sleep);
-+	}
-+}
-+
-+
-+static void sigd_close(struct atm_vcc *vcc)
-+{
-+	struct sk_buff *skb;
-+
-+	DPRINTK("sigd_close\n");
-+	sigd = NULL;
-+	if (skb_peek(&vcc->recvq))
-+		printk(KERN_ERR "sigd_close: closing with requests pending\n");
-+	while ((skb = skb_dequeue(&vcc->recvq))) kfree_skb(skb,FREE_READ);
-+	for_all_vccs(trigger);
-+}
-+
-+
-+static struct atmdev_ops sigd_dev_ops = {
-+	NULL,		/* no open */
-+	sigd_close,	/* close */
-+	NULL,		/* no ioctl */
-+	NULL,		/* no getsockopt */
-+	NULL,		/* no setsockopt */
-+	sigd_send,	/* send */
-+	NULL,		/* no sg_send */
-+	NULL,		/* no poll */
-+	NULL,		/* no phy_put */
-+	NULL,		/* no phy_get */
-+	NULL		/* no feedback */
-+};
-+
-+
-+static struct atm_dev sigd_dev = {
-+	&sigd_dev_ops,
-+	NULL,		/* no PHY */
-+    	"sig",		/* type */
-+	999,		/* dummy device number */
-+	NULL,NULL,	/* pretend not to have any VCCs */
-+	NULL,NULL,	/* no data */
-+	0,		/* no flags */
-+	NULL,		/* no local address */
-+	{ 0 }		/* no ESI, no statistics */
-+};
-+
-+
-+int sigd_attach(struct atm_vcc *vcc)
-+{
-+	if (sigd) return -EADDRINUSE;
-+	DPRINTK("sigd_attach\n");
-+	sigd = vcc;
-+	vcc->dev = &sigd_dev;
-+	vcc->flags |= ATM_VF_READY | ATM_VF_META;
-+	wake_up(&sigd_sleep);
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/signaling.h	Fri Jul 19 15:10:11 1996
-@@ -0,0 +1,28 @@
-+/* net/atm/signaling.h - ATM signaling */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef NET_ATM_SIGNALING_H
-+#define NET_ATM_SIGNALING_H
-+
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/atmsvc.h>
-+
-+
-+#define WAITING 1 /* for reply: 0: no error, < 0: error, ... */
-+
-+
-+extern struct atm_vcc *sigd; /* needed in svc_release */
-+
-+
-+void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
-+    const struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
-+    const struct sockaddr_atmsvc *svc);
-+void sigd_enq_atomic(struct atm_vcc *vcc,enum atmsvc_msg_type type,
-+    const struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
-+    const struct sockaddr_atmsvc *svc);
-+int sigd_attach(struct atm_vcc *vcc);
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/atmarp.c	Wed Jul 17 18:01:27 1996
-@@ -0,0 +1,603 @@
-+/* atmarp.c - RFC1577 ATM ARP */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#include <linux/string.h>
-+#include <linux/errno.h>
-+#include <linux/kernel.h> /* for UINT_MAX */
-+#include <linux/netdevice.h>
-+#include <linux/skbuff.h>
-+#include <linux/wait.h>
-+#include <linux/timer.h>
-+#include <linux/if_arp.h> /* for some manifest constants */
-+#include <linux/notifier.h>
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/atmclip.h>
-+#include <linux/atmarp.h>
-+#include <linux/ip.h> /* for net/route.h */
-+#include <linux/in.h> /* for struct sockaddr_in */
-+#include <net/route.h> /* for struct rtable and ip_rt_route */
-+#include <asm/param.h> /* for HZ */
-+#include <asm/byteorder.h> /* for htons etc. */
-+
-+
-+/*
-+ * WARNING: This code will eventually become full ATMARP support as defined in
-+ *	    RFC1577 (actually, the plan is to move on quickly to RFC1577+), but
-+ *	    right now it's certainly full of bugs and severe design errors.
-+ *	    Don't use it as a reference for anything.
-+ */
-+
-+
-+#include "common.h"
-+#include "protocols.h" /* for atm_push_raw */
-+#include "ipcommon.h"
-+#include "atmarp.h"
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+/*
-+ * Relation between address/subddress and private/public address:
-+ *
-+ * Caller 	Called			Address		Subaddress
-+ * -----------  ----------------------- --------------	----------
-+ * private	same private net	private		-
-+ * public-only	private via public	public (E.164)	private
-+ * private net	public UNI		public (NSAP)	-
-+ *
-+ * See also: ATM Forum UNI 3.0 (or 3.1), Annex A
-+ */
-+
-+
-+struct device *clip_devs = NULL;
-+struct atm_vcc *atmarpd = NULL;
-+static struct wait_queue *atmarpd_sleep = NULL;
-+static struct timer_list idle_timer = { NULL, NULL, 0L, 0L, NULL };
-+static int start_timer = 1;
-+
-+
-+#define WAITING 1 /* see also signaling.h */
-+
-+
-+static int atmarpd_send(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	struct atmarp_ctrl *ctrl;
-+ 
-+        ctrl = (struct atmarp_ctrl *) skb->data;
-+	if (ctrl->magic != ATMARP_CTRL_MAGIC) {
-+		printk(KERN_ALERT "atmarpd_send: bad magic 0x%x\n",
-+		    ctrl->magic);
-+		return -EPROTO;
-+	}
-+	if (ctrl->type != act_complete) {
-+		printk(KERN_ALERT "atmarpd_send: bad type 0x%x\n",ctrl->type);
-+		return -EPROTO;
-+	}
-+	if (!ctrl->reply) {
-+		printk(KERN_ALERT "atmarpd_send: no reply\n");
-+		return -EPROTO;
-+	}
-+	*ctrl->reply = ctrl->arg;
-+	wake_up(&atmarpd_sleep);
-+	dev_kfree_skb(skb,FREE_WRITE);
-+	return 0;
-+}
-+
-+
-+static int send_demon(enum atmarp_ctrl_type type,int itf,unsigned long arg,
-+    void *data,int length)
-+{
-+	struct atmarp_ctrl *ctrl;
-+	struct sk_buff *skb;
-+	int size,need_reply;
-+	volatile int reply;
-+
-+	DPRINTK("send_demon(%d)\n",type);
-+	if (!atmarpd) return -EUNATCH;
-+	size = sizeof(struct atmarp_ctrl)+(data ? length : 0);
-+	skb = alloc_skb(size,GFP_ATOMIC);
-+	if (!skb) return -ENOMEM;
-+	skb->free = 1;
-+	skb->len = size;
-+	need_reply = type == act_ioctl || type == act_create;
-+	ctrl = (struct atmarp_ctrl *) skb->data;
-+	ctrl->magic = ATMARP_CTRL_MAGIC;
-+	ctrl->type = type;
-+	ctrl->reply = need_reply ? &reply : NULL;
-+	ctrl->itf_num = itf;
-+	ctrl->arg = arg;
-+	if (data) memcpy(ctrl->data,data,length);
-+	reply = WAITING;
-+	skb_queue_tail(&atmarpd->recvq,skb);
-+	wake_up(&atmarpd->sleep);
-+	if (!need_reply) return 0;
-+	while (reply == WAITING && atmarpd) sleep_on(&atmarpd_sleep);
-+	return atmarpd ? reply : -EUNATCH;
-+}
-+
-+
-+static void remove(struct atmarp_entry *entry) /* @@@ ugly ! */
-+{
-+	struct atmarp_entry **walk;
-+
-+	for (walk = &PRIV(entry->dev)->table; *walk; walk = &(*walk)->next)
-+		if (*walk == entry) {
-+			*walk = entry->next;
-+			return;
-+		}
-+	printk(KERN_CRIT "ATMARP: remove failed (0x%08lx)\n",
-+	    (unsigned long) entry);
-+}
-+
-+
-+static inline void time_out_entry(struct atmarp_entry **entry)
-+{
-+	struct atmarp_entry *next;
-+
-+	DPRINTK("VC TIMED OUT\n");
-+	if ((*entry)->vcc) {
-+		(*entry)->vcc->flags |= ATM_VF_RELEASED;
-+		(*entry)->vcc->flags &= ~ATM_VF_READY;
-+	}
-+	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;
-+	}
-+}
-+
-+
-+static void idle_timer_check(unsigned long dummy)
-+{
-+	struct device *itf;
-+	struct atmarp_entry **entry;
-+	unsigned long expire;
-+
-+	idle_timer.expires = UINT_MAX;
-+	for (itf = clip_devs; itf; itf = PRIV(itf)->next)
-+		for (entry = &PRIV(itf)->table; *entry;
-+		    entry = &(*entry)->next)
-+			if ((*entry)->idle_timeout) {
-+				expire = (*entry)->last_use+(*entry)->
-+				    idle_timeout;
-+				if (expire < jiffies) {
-+					time_out_entry(entry);
-+					if (!*entry) break;
-+				}
-+				else if (expire < idle_timer.expires)
-+						idle_timer.expires = expire;
-+			}
-+	if (idle_timer.expires < jiffies+CLIP_CHECK_INTERVAL*HZ)
-+		idle_timer.expires = jiffies+CLIP_CHECK_INTERVAL*HZ;
-+	del_timer(&idle_timer);
-+	add_timer(&idle_timer);
-+}
-+
-+
-+static void atm_push_ip(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+#if 0
-+	DPRINTK("clip push\n");
-+#endif
-+	if (!skb) {
-+		DPRINTK("removing AE\n");
-+		AE(vcc)->old_push(vcc,NULL);
-+		if (AE(vcc)->ip) remove(AE(vcc));
-+		kfree(AE(vcc));
-+		return;
-+	}
-+	AE(vcc)->last_use = jiffies;
-+	skb->dev = AE(vcc)->dev;
-+	skb->mac.raw = skb->data;
-+	if (!skb->dev->hard_header_len) skb->protocol = htons(ETH_P_IP);
-+	else if (skb->len < RFC1483LLC_LEN || memcmp(skb->data,llc_oui,
-+		    sizeof(llc_oui))) skb->protocol = 0;
-+			/* probably wrong encap ... */
-+		else {
-+			skb->protocol = ((unsigned short *) skb->data)[3];
-+			skb_pull(skb,RFC1483LLC_LEN);
-+			if (vcc && skb->protocol == htons(ETH_P_ARP)) {
-+				PRIV(skb->dev)->stats.rx_packets++;
-+				atm_push_raw(vcc,skb);
-+				return;
-+			}
-+		}
-+	PRIV(skb->dev)->stats.rx_packets++;
-+	netif_rx(skb);
-+}
-+
-+
-+static struct atmarp_entry *new_entry(int timeout)
-+{
-+	struct atmarp_entry *entry;
-+
-+	entry = kmalloc(sizeof(struct atmarp_entry),GFP_ATOMIC);
-+	if (!entry) return NULL;
-+	entry->ip = 0;
-+	entry->vcc = NULL;
-+	entry->encap = 1;
-+	entry->dev = clip_devs;
-+	entry->old_push = NULL;
-+	entry->last_use = jiffies;
-+	entry->idle_timeout = timeout*HZ;
-+	entry->queued = NULL;
-+	entry->next = NULL;
-+	return entry;
-+}
-+
-+
-+static void attach_entry(struct atm_vcc *vcc,struct atmarp_entry *entry)
-+{
-+	AE(vcc) = entry;
-+	entry->old_push = vcc->push;
-+	vcc->push = atm_push_ip;
-+	entry->vcc = vcc;
-+}
-+
-+
-+static int clip_hard_header(struct sk_buff *skb,struct device *dev,
-+    unsigned short type,void *daddr,void *saddr,unsigned len)
-+{
-+	void *here;
-+
-+	here = skb_push(skb,dev->hard_header_len);
-+	memcpy(here,llc_oui,sizeof(llc_oui));
-+	((unsigned short *) here)[3] = htons(type);
-+	return -RFC1483LLC_LEN;
-+}
-+
-+
-+static int clip_rebuild_header(void *buff,struct device *dev,unsigned long dst,
-+    struct sk_buff *skb)
-+{
-+#if 0
-+	void *here;
-+
-+	here = skb->data; /*skb_push(skb,dev->hard_header_len);*/
-+	memcpy(here,llc_oui,sizeof(llc_oui));
-+	((unsigned short *) here)[3] = htons(ETH_P_IP);
-+#endif
-+	return 0;
-+}
-+
-+
-+static int clip_xmit(struct sk_buff *skb,struct device *dev)
-+{
-+	struct atmarp_entry *entry;
-+
-+#if 0
-+  int i;
-+	DPRINTK("new clip_xmit (0x%x)\n",skb->raddr);
-+/*(int *) 0xffff0000 = 42;*/
-+for (i = 0; i < skb->len; i++) printk("%02X ",skb->data[i]);
-+printk("\n");
-+#endif
-+	for (entry = PRIV(dev)->table; entry; entry = entry->next)
-+		if (entry->ip == skb->raddr) break;
-+	if (!entry) {
-+		DPRINTK("no entry - queuing\n");
-+		send_demon(act_need,PRIV(dev)->number,skb->raddr,NULL,0);
-+		entry = new_entry(ATMARP_RETRY_DELAY);
-+		entry->queued = skb;
-+		entry->ip = skb->raddr;
-+		entry->next = PRIV(dev)->table;
-+		PRIV(dev)->table = entry;
-+		idle_timer_check(0);
-+		return 0;
-+	}
-+	if (!entry->vcc || !(entry->vcc->flags & ATM_VF_READY)) {
-+		DPRINTK("not found - discarding\n");
-+		dev_kfree_skb(skb,FREE_WRITE);
-+		return 0;
-+		/* Should return -EHOSTUNREACH, but then it will retry
-+		   forever, so we just discard the packet. */
-+	}
-+	if (!entry->encap) skb_push(skb,RFC1483LLC_LEN);
-+		/* assumes that we'll never attempt to xmit the same packet
-+		   twice @@@ */
-+	else {
-+		memcpy((void *) skb->data,llc_oui,sizeof(llc_oui));
-+		((unsigned short *) skb->data)[3] = htons(ETH_P_IP);
-+	}
-+	skb->atm.iovcnt = 0;
-+	AE(entry->vcc)->last_use = jiffies;
-+	entry->vcc->dev->ops->send(entry->vcc,skb);
-+	PRIV(dev)->stats.tx_packets++;
-+	return 0;
-+}
-+
-+
-+static struct enet_statistics *atm_clip_get_stats(struct device *dev)
-+{
-+	return &PRIV(dev)->stats;
-+}
-+
-+
-+int atmarp_mkip(struct atm_vcc *vcc,int timeout)
-+{
-+	struct atmarp_entry *entry;
-+
-+	DPRINTK("MKIP\n");
-+	entry = new_entry(timeout);
-+	if (!entry) return -ENOMEM;
-+	attach_entry(vcc,entry);
-+	idle_timer_check(0);
-+	return 0;
-+}
-+
-+
-+int atmarp_setentry(struct atm_vcc *vcc,unsigned long ip)
-+{
-+	struct atmarp_entry **walk,**next,*succ;
-+	struct rtable *route;
-+	struct sk_buff *queued;
-+
-+	DPRINTK("SETENTRY 0x%lx\n",ip);
-+	if (vcc->push != atm_push_ip) {
-+		printk(KERN_WARNING "atmarp_setentry: VCC has no ARP entry\n");
-+		return -EBADF;
-+	}
-+	if (!ip) {
-+		if (!AE(vcc)->ip) {
-+			printk(KERN_ERR "hiding hidden ATMARP entry\n");
-+			return 0;
-+		}
-+		DPRINTK("setentry: remove\n");
-+		remove(AE(vcc));
-+		AE(vcc)->ip = 0;
-+		return 0;
-+	}
-+	DPRINTK("setentry: route\n");
-+	route = ip_rt_route(ip,1);
-+	if (!route) return -EHOSTUNREACH;
-+	AE(vcc)->dev = route->rt_dev;
-+	if (AE(vcc)->ip) {
-+		DPRINTK("setentry: update\n");
-+		DPRINTK("(updating)\n");
-+		AE(vcc)->ip = ip;
-+		return 0;
-+	}
-+	DPRINTK("setentry: add\n");
-+	queued = NULL;
-+	for (walk = &PRIV(AE(vcc)->dev)->table; *walk; walk = next) {
-+		next = &(*walk)->next;
-+		if ((*walk)->ip == ip) {
-+			if ((*walk)->vcc) continue;
-+				/* more than one VC to dest */
-+			if ((*walk)->queued) {
-+				DPRINTK("setentry: flushing\n");
-+				if (queued)
-+					printk(KERN_CRIT "atmarp: bad news - "
-+					    "more than one skb queued\n");
-+				queued = (*walk)->queued;
-+			}
-+			succ = (*walk)->next;
-+			kfree(*walk);
-+			*walk = succ;
-+			next = walk;
-+			continue;
-+		}
-+	}
-+	DPRINTK("(adding)\n");
-+	AE(vcc)->ip = ip;
-+	AE(vcc)->next =  PRIV(AE(vcc)->dev)->table;
-+	PRIV(AE(vcc)->dev)->table = AE(vcc);
-+	if (queued) clip_xmit(queued,route->rt_dev);
-+	return 0;
-+}
-+
-+
-+int atmarp_encap(struct atm_vcc *vcc,int mode)
-+{
-+	AE(vcc)->encap = mode;
-+	return 0;
-+}
-+
-+
-+static int atmarp_ioctl(struct device *dev,unsigned int cmd,void *arg)
-+{
-+	struct atmarpreq req;
-+	__u32 *ip;
-+	int error;
-+
-+	DPRINTK("atmarp_ioctl\n");
-+	error = verify_area(VERIFY_READ,arg,sizeof(struct atmarpreq));
-+	if (error) return error;
-+	memcpy_fromfs(&req,arg,sizeof(struct atmarpreq));
-+	if (req.arp_pa.sa_family != AF_INET) return -EPFNOSUPPORT;
-+	ip = &((struct sockaddr_in *) &req.arp_pa)->sin_addr.s_addr;
-+	if (!(*ip & ~dev->pa_mask) && !(req.arp_flags & ATF_ARPSRV))
-+		return -EINVAL;
-+	switch (cmd) {
-+		case SIOCSARP:
-+		case SIOCDARP:
-+		case SIOCGARP:
-+			return send_demon(act_ioctl,PRIV(dev)->number,cmd,&req,
-+			    sizeof(struct atmarpreq));
-+			/* @@@ get will need special treatment */
-+		default:
-+			return -EINVAL;
-+	}
-+	return 0;
-+}
-+
-+
-+static int clip_open(struct device *dev)
-+{
-+	DPRINTK("clip_open called\n");
-+	return 0;
-+}
-+
-+
-+static int clip_stop(struct device *dev)
-+{
-+	DPRINTK("clip_stop\n");
-+	/* @@@ just kill it on error ? */
-+	return 0;
-+}
-+
-+
-+static int clip_init(struct device *dev)
-+{
-+	DPRINTK("init %s\n",dev->name);
-+	dev->hard_start_xmit = clip_xmit;
-+	/* sg_xmit ... */
-+	dev->open = clip_open;
-+	dev->stop = clip_stop;
-+	ether_setup(dev);
-+	dev->tbusy = 0; /* @@@ check */
-+	dev->hard_header = clip_hard_header;
-+	dev->do_ioctl = NULL;
-+	dev->ip_arp = atmarp_ioctl;
-+	dev->rebuild_header = clip_rebuild_header;
-+	dev->get_stats = atm_clip_get_stats;
-+	dev->hard_header_len = RFC1483LLC_LEN;
-+	dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
-+	dev->flags |= IFF_NOARP; /* we do our own ARP ... */
-+	dev->mtu = RFC1626_MTU;
-+	return send_demon(act_create,PRIV(dev)->number,0,NULL,0);
-+}
-+
-+
-+int clip_create(int number) /* remove by downing */
-+{
-+	struct device *dev,*walk;
-+
-+	if (number != -1) {
-+		for (walk = clip_devs; walk; walk = PRIV(walk)->next)
-+			if (PRIV(walk)->number == number) return -EEXIST;
-+	}
-+	else {
-+		number = 0;
-+		for (walk = clip_devs; walk; walk = PRIV(walk)->next)
-+			if (PRIV(walk)->number >= number)
-+				number = PRIV(walk)->number+1;
-+	}
-+	dev = kmalloc(sizeof(struct device)+sizeof(struct atmarp_priv),
-+	    GFP_KERNEL);
-+	if (!dev) return -ENOMEM;
-+	memset(dev,0,sizeof(struct device)+sizeof(struct atmarp_priv));
-+	dev->name = PRIV(dev)->name;
-+	sprintf(dev->name,"atm%d",number);
-+	dev->init = clip_init;
-+	if (register_netdev(dev)) return -EIO; /* free dev ? */
-+	PRIV(dev)->number = number;
-+	PRIV(dev)->table = NULL;
-+	PRIV(dev)->next = clip_devs;
-+	clip_devs = dev;
-+	DPRINTK("registered (net:%s)\n",dev->name);
-+	return number;
-+}
-+
-+
-+static int clip_device_event(struct notifier_block *this,unsigned long event,
-+    void *dev)
-+{
-+	/* ignore non-CLIP devices */
-+	if (((struct device *) dev)->init != clip_init) return NOTIFY_DONE;
-+	switch (event) {
-+		case NETDEV_UP:
-+			(void) send_demon(act_up,PRIV(dev)->number,0,NULL,0);
-+			break;
-+		case NETDEV_DOWN:
-+			DPRINTK("clip_device_event NETDEV_DOWN\n");
-+			(void) send_demon(act_down,PRIV(dev)->number,0,NULL,0);
-+			break;
-+		case NETDEV_REBOOT:
-+			/* ignore */
-+			break;
-+		default:
-+			printk(KERN_ERR "clip_device_event: unknown event "
-+			    "%ld\n",event);
-+			break;
-+	}
-+	return NOTIFY_DONE;
-+}
-+
-+
-+static struct notifier_block clip_dev_notifier = {
-+	clip_device_event,
-+	NULL,
-+	0
-+};
-+
-+
-+static void atmarpd_close(struct atm_vcc *vcc)
-+{
-+        struct sk_buff *skb;
-+
-+        DPRINTK("atmarpd_close\n");
-+        atmarpd = NULL; /* assumed to be atomic */
-+	barrier();
-+	unregister_netdevice_notifier(&clip_dev_notifier);
-+	wake_up(&atmarpd_sleep);
-+        if (skb_peek(&vcc->recvq))
-+                printk(KERN_ERR "atmarpd_close: closing with requests "
-+		    "pending\n");
-+        while ((skb = skb_dequeue(&vcc->recvq))) kfree_skb(skb,FREE_READ);
-+	DPRINTK("(done)\n");
-+}
-+
-+
-+static struct atmdev_ops atmarpd_dev_ops = {
-+	NULL,		/* no open */
-+	atmarpd_close,	/* close */
-+	NULL,		/* no ioctl */
-+	NULL,		/* no getsockopt */
-+	NULL,		/* no setsockopt */
-+	atmarpd_send,	/* no send */
-+	NULL,		/* no sg_send */
-+	NULL,		/* no poll */
-+	NULL,		/* no phy_put */
-+	NULL,		/* no phy_get */
-+	NULL		/* no feedback */
-+};
-+
-+static struct atm_dev atmarpd_dev = {
-+	&atmarpd_dev_ops,
-+	NULL,		/* no PHY */
-+	"arpd",		/* type */
-+	999,		/* dummy device number */
-+	NULL,NULL,	/* pretend not to have any VCCs */
-+	NULL,NULL,	/* no data */
-+	0,		/* no flags */
-+	NULL,		/* no local address */
-+	{ 0 }		/* no ESI, no statistics */
-+};
-+
-+
-+int atm_init_atmarp(struct atm_vcc *vcc)
-+{
-+	if (atmarpd) return -EADDRINUSE;
-+	if (start_timer) {
-+		start_timer = 0;
-+		idle_timer.expires = jiffies+CLIP_CHECK_INTERVAL*HZ;
-+		idle_timer.function = idle_timer_check;
-+		add_timer(&idle_timer);
-+	}
-+	atmarpd = vcc;
-+	vcc->flags |= ATM_VF_READY | ATM_VF_META;
-+	    /* allow replies and avoid getting closed if signaling dies */
-+	vcc->dev = &atmarpd_dev;
-+	vcc->aal = ATM_AAL5; /* lie */
-+	vcc->push = NULL;
-+	vcc->peek = NULL; /* crash */
-+	vcc->pop = NULL; /* crash */
-+	vcc->push_oam = NULL; /* crash */
-+	register_netdevice_notifier(&clip_dev_notifier);
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/atmarp.h	Fri Jul 19 15:10:11 1996
-@@ -0,0 +1,54 @@
-+/* net/atm/atmarp.h - RFC1577 ATM ARP */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+ 
-+#ifndef NET_ATM_ATMARP_H
-+#define NET_ATM_ATMARP_H
-+
-+#include <linux/netdevice.h>
-+#include <linux/skbuff.h>
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/atmarp.h>
-+
-+
-+#define PRIV(dev) ((struct atmarp_priv *) ((struct device *) (dev)+1))
-+#define AE(vcc) ((struct atmarp_entry *) (vcc->user_back))
-+
-+
-+struct atmarp_entry {
-+	unsigned long	ip;		/* IP address, 0 if none */
-+	struct atm_vcc	*vcc;		/* active VCC */
-+	unsigned char encap;		/* 0: NULL, 1: LLC/SNAP */
-+	struct device	*dev;		/* device back pointer */
-+	void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb);
-+					/* keep old push fn for detaching */
-+	unsigned long last_use;		/* last send or receive operation */
-+	unsigned long idle_timeout;	/* keep open idle for so many jiffies*/
-+	struct sk_buff *queued;		/* queue one skb when resolving */
-+	struct atmarp_entry *next;	/* ugly linked list ... */
-+};
-+
-+/* Entry is PVC iff vcc && vcc->family == AF_ATMPVC */
-+ 
-+
-+struct atmarp_priv {
-+	struct atmarp_entry *table;	/* ARP table */
-+	char name[8];			/* interface name */
-+	int number;			/* for convenience ... */
-+	struct enet_statistics stats;
-+	struct device *next;		/* next CLIP interface */
-+};
-+
-+
-+extern struct device *clip_devs;
-+extern struct atm_vcc *atmarpd; /* ugly */
-+
-+
-+int clip_create(int number);
-+int atmarp_mkip(struct atm_vcc *vcc,int timeout);
-+int atmarp_setentry(struct atm_vcc *vcc,unsigned long ip);
-+int atmarp_encap(struct atm_vcc *vcc,int mode);
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/ipcommon.h	Wed Jul 17 18:27:22 1996
-@@ -0,0 +1,65 @@
-+/* net/atm/ipcommon.h - Common items for all ways of doing IP over ATM */
-+
-+/* Written 1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef NET_ATM_IPCOMMON_H
-+#define NET_ATM_IPCOMMON_H
-+
-+
-+#include <linux/string.h>
-+#include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-+#include <linux/atmdev.h>
-+#include <linux/atmclip.h>
-+
-+
-+extern const unsigned char llc_oui[6];
-+
-+
-+#define CLIP(dev) ((struct clip_priv *) ((struct device *) (dev)+1))
-+
-+
-+struct clip_priv {
-+	char name[8];
-+	struct atm_vcc *vcc;
-+	struct enet_statistics stats;
-+};
-+
-+
-+static inline void ipcom_push(struct sk_buff *skb)
-+{
-+	skb->mac.raw = skb->data;
-+	if (!skb->dev->hard_header_len) skb->protocol = htons(ETH_P_IP);
-+	else if (skb->len < RFC1483LLC_LEN || memcmp(skb->data,llc_oui,
-+		    sizeof(llc_oui))) skb->protocol = 0;
-+			/* probably wrong encap ... */
-+		else {
-+			skb->protocol = ((unsigned short *) skb->data)[3];
-+			skb_pull(skb,RFC1483LLC_LEN);
-+		}
-+	CLIP(skb->dev)->stats.rx_packets++;
-+	netif_rx(skb);
-+}
-+
-+
-+static inline void ipcom_xmit(struct device *dev,struct atm_vcc *vcc,
-+    struct sk_buff *skb)
-+{
-+	if (dev->hard_header_len) {
-+		memcpy(skb->data,llc_oui,sizeof(llc_oui));
-+		((unsigned short *) skb->data)[3] = htons(ETH_P_IP); /* hack */
-+	}
-+	skb->atm.iovcnt = 0;
-+	vcc->dev->ops->send(vcc,skb);
-+}
-+
-+
-+struct sk_buff *atm_peek_clip(struct atm_vcc *vcc,unsigned long pdu_size,
-+    __u32 (*fetch)(struct atm_vcc *vcc,int i));
-+void atm_pop_clip(struct atm_vcc *vcc,struct sk_buff *skb);
-+void ipcom_init(struct device *dev,
-+    int (*hard_start_xmit)(struct sk_buff *skb,struct device *dev),
-+    unsigned short extra_flags);
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/ipcommon.c	Thu Jul 18 11:58:30 1996
-@@ -0,0 +1,173 @@
-+/* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */
-+
-+/* Written 1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/string.h>
-+#include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-+#include <linux/in.h>
-+#include <linux/atmdev.h>
-+#include <linux/atmclip.h>
-+
-+#include "common.h"
-+#include "ipcommon.h"
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+const unsigned char llc_oui[] = {
-+	0xaa,	/* DSAP: non-ISO */
-+	0xaa,	/* SSAP: non-ISO */
-+	0x03,	/* Ctrl: Unnumbered Information Command PDU */
-+	0x00,	/* OUI: EtherType */
-+	0x00,
-+	0x00 };
-+
-+
-+static int clip_hard_header(struct sk_buff *skb,struct device *dev,
-+    unsigned short type,void *daddr,void *saddr,unsigned len)
-+{
-+	void *here;
-+
-+	if (!dev->hard_header_len) return 0; /* just in case ... */
-+	here = skb_push(skb,dev->hard_header_len);
-+	memcpy(here,llc_oui,sizeof(llc_oui));
-+	((unsigned short *) here)[3] = htons(type);
-+	return -RFC1483LLC_LEN;
-+}
-+
-+
-+static int clip_rebuild_header(void *buff,struct device *dev,unsigned long dst,
-+    struct sk_buff *skb)
-+{
-+	/* no ARP with this type of IP over ATM */
-+	return 0;
-+}
-+
-+
-+struct sk_buff *atm_peek_clip(struct atm_vcc *vcc,unsigned long pdu_size,
-+    __u32 (*fetch)(struct atm_vcc *vcc,int i))
-+{
-+        struct sk_buff *skb;
-+	int header_size;
-+
-+	/* TODO: check against buffer quota (should even check against upper
-+		 layer protocol socket quota) */
-+	header_size = 0;
-+	if (fetch && pdu_size > 28) { /* > IP+UDP header */
-+		unsigned long type;
-+
-+/* doesn't work yet */
-+type = ntohl(fetch(vcc,2));
-+/*printk("type is 0x%08lx\n",type);*/
-+		type = ntohl(fetch(vcc,2)) & 0xff0000;
-+		if (type == IPPROTO_UDP << 8) header_size = 28; /* bytes */
-+		else if (type == IPPROTO_TCP << 8) header_size = 40; /* bytes */
-+	}
-+	if (!header_size) skb = alloc_skb((pdu_size+3) & ~3,GFP_ATOMIC);
-+	else {
-+		skb = alloc_skb(((pdu_size+3) & ~3)+PAGE_SIZE+header_size-1,
-+		    GFP_ATOMIC);
-+		if (skb) {
-+			DPRINTK("data before: 0x%lx\n",
-+			    (unsigned long) skb->data);
-+		        skb->data = (unsigned char *) (((unsigned long) skb->
-+			    data+header_size+PAGE_SIZE-1) & ~(PAGE_SIZE-1))-
-+			    header_size;
-+			DPRINTK("data after: 0x%lx\n",
-+			    (unsigned long) skb->data);
-+		}
-+	}
-+	if (!skb) {
-+		CLIP(vcc->proto_data)->stats.rx_dropped++;
-+		vcc->stats->rx_drop++;
-+	}
-+        return skb;
-+}
-+ 
-+ 
-+void atm_pop_clip(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+#ifdef CONFIG_MMU_HACKS
-+	if (skb->atm.iovcnt)
-+		unlock_user(skb->atm.iovcnt-1,(struct iovec *) skb->data+1);
-+#endif
-+/*printk("popping (r:%d,w:%d)\n",skb->sk->rmem_alloc,skb->sk->wmem_alloc);*/
-+	dev_kfree_skb(skb,FREE_WRITE);
-+}
-+
-+
-+
-+static struct enet_statistics *atm_clip_get_stats(struct device *dev)
-+{
-+	return &CLIP(dev)->stats;
-+}
-+
-+
-+/*@@@static*/ int clip_ioctl(struct device *dev,struct ifreq *rq,int cmd)
-+{
-+	if (!suser()) return -EPERM;
-+	switch (cmd) {
-+		case CLIP_NULENCAP:
-+#if 0
-+			dev->type = ARPHDR_ATMNULL;
-+#endif
-+			dev->hard_header_len = 0;
-+			return 0;
-+		case CLIP_LLCENCAP:
-+#if 0
-+			dev->type = ARPHDR_ATMLLC;
-+#endif
-+			dev->hard_header_len = RFC1483LLC_LEN;
-+			return 0;
-+		default:
-+			return -EOPNOTSUPP;
-+	}
-+}
-+
-+
-+static int clip_stop(struct device *dev)
-+{
-+	DPRINTK("DOWN! (%s,0x%lx)\n",dev->name,(unsigned long) CLIP(dev)->vcc);
-+	atm_release_vcc(CLIP(dev)->vcc,1);
-+	return 0;
-+}
-+
-+
-+void ipcom_init(struct device *dev,
-+    int (*hard_start_xmit)(struct sk_buff *skb,struct device *dev),
-+    unsigned short extra_flags)
-+{
-+	DPRINTK("ipcom_init\n");
-+	DPRINTK("configuring %s\n",dev->name);
-+	dev->hard_start_xmit = hard_start_xmit;
-+#if 0
-+#ifdef CONFIG_MMU_HACKS
-+	dev->sg_xmit = clip_sg_xmit;
-+#else
-+	dev->sg_xmit = NULL;
-+#endif
-+#endif
-+	dev->stop = clip_stop;
-+	ether_setup(dev);
-+	dev->tbusy = 0; /* @@@ check */
-+	dev->hard_header = clip_hard_header;
-+	dev->do_ioctl = clip_ioctl;
-+	dev->rebuild_header = clip_rebuild_header;
-+	dev->get_stats = atm_clip_get_stats;
-+	dev->hard_header_len = RFC1483LLC_LEN;
-+	dev->flags |= IFF_NOARP | IFF_POINTOPOINT | extra_flags;
-+#if 0
-+	dev->type = ARPHDR_ATMLLC;
-+#endif
-+	dev->mtu = RFC1626_MTU;
-+	memset(&CLIP(dev)->stats,0,sizeof(struct enet_statistics));
-+
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/arequipa.c	Wed Jul 17 18:32:16 1996
-@@ -0,0 +1,258 @@
-+/* net/atm/arequipa.c - Application requested IP over ATM */
-+ 
-+/* Written 1996 by Jean-Michel Pittet and Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/string.h>
-+#include <linux/kernel.h>
-+#include <linux/errno.h>
-+#include <linux/netdevice.h>
-+#include <linux/skbuff.h>
-+#include <linux/errno.h>
-+#include <linux/skbuff.h>
-+#include <linux/in.h>
-+#include <linux/mmuio.h>
-+#include <linux/atmdev.h>
-+#include <linux/atmclip.h>
-+#include <linux/arequipa.h>
-+#include <linux/route.h>
-+#include <net/sock.h>
-+#include <netinet/in.h>
-+#include <asm/system.h> /* cli and such */
-+
-+#include "protocols.h"
-+#include "signaling.h"  /* for indirect closing, see below */
-+#include "common.h"
-+#include "ipcommon.h"
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+struct device *arequipa_dev = NULL;
-+    /* must use a different null value if skb->dev can ever be NULL */
-+
-+struct rtable arequipa_rt = {
-+	NULL,		/* rt_next */
-+	0L,		/* rt_dst */
-+	0L,		/* rt_src */
-+	0L,		/* rt_gateway */
-+	2,		/* rt_refcnt */
-+	1,		/* rt_use */
-+	0,		/* rt_window */
-+	0L,		/* rt_lastuse */
-+	NULL,		/* rt_hh */
-+	NULL,		/* rt_dev */
-+	RTF_UP,		/* rt_flags */
-+	RFC1626_MTU,	/* rt_mtu */
-+	0,		/* rt_irtt */
-+	0		/* rt_tos */
-+};
-+
-+
-+/*
-+ * Closing is tricky. Since we may be in an interrupt when executing
-+ * arequipa_close, we can't just go and call close_fp. So what we do it instead
-+ * is to ask signaling nicely to close the SVC. Signaling does its job and then
-+ * comes back to close us (in arequipa_callback). Now we're in a process
-+ * context and can sleep, etc.
-+ */
-+
-+int arequipa_close(struct sock *upper)
-+{
-+	struct socket *lower;
-+	unsigned long flags;
-+
-+	DPRINTK("arequipa_close\n");
-+	if (!(lower = upper->arequipa)) return -ENOTCONN;
-+	save_flags(flags);
-+	cli();
-+	upper->arequipa = NULL;
-+	ATM_SD(lower)->upper = NULL;
-+	restore_flags(flags);
-+	sigd_enq_atomic(ATM_SD(lower),as_close,NULL,NULL,NULL);
-+	ATM_SD(lower)->flags &= ~ATM_VF_REGIS;
-+	return 0;
-+}
-+
-+
-+
-+static void arequipa_callback(struct atm_vcc *vcc)
-+{
-+	DPRINTK("arequipa_callback\n");
-+	svc_callback(vcc);
-+	if (!(vcc->flags & ATM_VF_RELEASED)) return;
-+	vcc->callback = svc_callback; /* paranoia ... */
-+	if (vcc->upper) {
-+		if (!vcc->upper->arequipa)
-+			printk("arequipa_callback: upper pretends not to "
-+			    "use Arequipa\n");
-+		ip_rt_put(vcc->upper->ip_route_cache);
-+		vcc->upper->ip_route_cache = NULL;
-+		vcc->upper->arequipa = NULL;
-+		vcc->callback = svc_callback;
-+	}
-+	close_fp(vcc->sock->file);
-+	return;
-+}
-+
-+
-+static int check_aq_vcc(struct socket *lower)
-+{
-+	if (lower->ops->family != PF_ATMSVC) return -EPROTOTYPE;
-+	    /* no PVCs, because we need to be closed by a 3rd party */
-+	if (lower->state != SS_CONNECTED) return -ENOTCONN;
-+	if (ATM_SD(lower)->aal != ATM_AAL5) return -EPROTONOSUPPORT;
-+	return 0;
-+}
-+
-+
-+/*static*/ void atm_push_arequipa(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	if (!skb) return; /* it's okay to close Arequipa VCs */
-+	/*DPRINTK("arequipa push(%ld)\n",skb->len);*/
-+	skb->dev = arequipa_dev;
-+	ipcom_push(skb);
-+}
-+
-+
-+static void make_aq_vcc(struct socket *lower)
-+{
-+	struct atm_vcc *vcc;
-+	unsigned long flags;
-+
-+	save_flags(flags);
-+	cli();
-+	vcc = ATM_SD(lower);
-+	vcc->pop = atm_pop_clip;
-+	vcc->callback = arequipa_callback;
-+	vcc->push = atm_push_arequipa;
-+	vcc->peek = atm_peek_clip;
-+	vcc->push_oam = NULL;
-+	restore_flags(flags);
-+	lower->file->f_count++;
-+}
-+
-+
-+int arequipa_attach(struct socket *lower,struct sock *upper)
-+{
-+	struct rtable *rt;
-+	int error;
-+
-+	if (upper->arequipa) {
-+		printk(KERN_WARNING "arequipa_attach: upper already uses "
-+		    "Arequipa\n");
-+		return -EISCONN;
-+	}
-+	error = check_aq_vcc(lower);
-+	if (error) return error;
-+	if (ATM_SD(lower)->upper) {
-+		printk(KERN_WARNING "arequipa_attach: lower is already "
-+		    "attached\n");
-+		return -EISCONN;
-+	}
-+	DPRINTK("arequipa_attach %p (i_count=%d,f_count=%d)\n",upper,
-+	  lower->inode->i_count,lower->file->f_count);
-+	upper->arequipa = lower;
-+	ATM_SD(lower)->upper = upper;
-+	rt = upper->ip_route_cache; /* revalidate cache */
-+	upper->ip_route_cache = NULL;
-+	set_rt_cache(upper,rt);
-+	return 0;
-+}
-+
-+
-+int arequipa_expect(struct sock *upper,int on)
-+{
-+	DPRINTK("arequipa_expect %d\n",on);
-+	if (on) {
-+		if (upper->aq_route) return 0;
-+		upper->aq_route = kmalloc(sizeof(struct rtable),GFP_KERNEL);
-+		return upper->aq_route ? 0 : -ENOMEM;
-+	}
-+	if (!upper->aq_route) return 0;
-+	if (upper->arequipa) return -EBUSY;
-+	kfree(upper->aq_route);
-+	upper->aq_route = NULL;
-+	return 0;
-+}
-+
-+
-+int arequipa_preset(struct socket *lower,struct sock *upper)
-+{
-+	int error;
-+
-+	error = arequipa_expect(upper,1);
-+	if (error) return error;
-+	error = arequipa_attach(lower,upper);
-+	if (!error) make_aq_vcc(lower);
-+	return error;
-+}
-+
-+
-+int arequipa_incoming(struct socket *lower)
-+{
-+	int error;
-+
-+	if (!suser()) return -EPERM;
-+	error = check_aq_vcc(lower);
-+	if (error) return error;
-+	ATM_SD(lower)->upper = NULL;
-+	make_aq_vcc(lower);
-+	DPRINTK("aq_incoming %d\n",lower->file->f_count);
-+	return 0;
-+}
-+
-+
-+static int arequipa_xmit(struct sk_buff *skb,struct device *dev)
-+{
-+	struct atm_vcc *vcc;
-+
-+	/*DPRINTK("arequipa xmit\n");*/
-+	if (!skb->sk || !skb->sk->arequipa ||
-+	    !ATM_SD(skb->sk->arequipa)) {
-+		printk("arequipa_xmit: discarding orphaned packets\n");
-+	        dev_kfree_skb(skb,FREE_WRITE);
-+		return 0;
-+	}
-+	vcc = ATM_SD(skb->sk->arequipa);
-+	if (!(vcc->flags & ATM_VF_READY)) {
-+		printk("arequipa_xmit: not ready\n");
-+	        dev_kfree_skb(skb,FREE_WRITE);
-+		return 0;
-+	}
-+	ipcom_xmit(arequipa_dev,vcc,skb);
-+	CLIP(arequipa_dev)->stats.tx_packets++;
-+	return 0;
-+}
-+
-+
-+static int arequipa_init(struct device *dev)
-+{
-+	ipcom_init(dev,arequipa_xmit,IFF_UP);
-+	dev->pa_addr = 0x01020304;
-+	dev->pa_mask = ~0L;
-+	dev->pa_alen = 4;
-+	return 0;
-+}
-+
-+
-+int atm_init_arequipa(void)
-+{
-+	DPRINTK("atm_init_arequipa\n");
-+	if (!suser()) return -EPERM;
-+	arequipa_dev = kmalloc(sizeof(struct device)+sizeof(struct clip_priv),
-+	    GFP_KERNEL);
-+	if (!arequipa_dev) return -ENOMEM;
-+	arequipa_rt.rt_dev = arequipa_dev;
-+	memset(arequipa_dev,0,sizeof(struct device)+sizeof(struct clip_priv));
-+	arequipa_dev->name = "arequipa";
-+	arequipa_dev->init = arequipa_init;
-+	arequipa_init(arequipa_dev);
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/tunable.h	Mon Jun 10 17:36:24 1996
-@@ -0,0 +1,24 @@
-+/* net/atm/tunable.h - Tunable parameters of ATM support */
-+
-+/* Written 1995 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef NET_ATM_TUNABLE_H
-+#define NET_ATM_TUNABLE_H
-+
-+#if 0
-+/* this is just a reminder - TTS is a device-specific parameter and shall be
-+   used inside device drivers only */
-+#define ATM_TTS		1000	/* worst-case time to service of device
-+				   drivers, in microseconds */
-+#endif
-+
-+#define ATM_RXBQ_DEF	64	/* default RX buffer quota, in kB */
-+#define ATM_TXBQ_DEF	64	/* default TX buffer quota, in kB */
-+#define ATM_RXBQ_MAX	1024	/* RX buffer quota limit, in kB */
-+#define ATM_TXBQ_MAX	1024	/* TX buffer quota limit, in kB */
-+
-+#define ATM_PDU_OVHD	0	/* number of bytes to charge against buffer
-+				   quota per PDU */
-+
-+#endif
---- ref/net/ipv4/af_inet.c	Fri Jun  7 09:14:29 1996
-+++ work/net/ipv4/af_inet.c	Mon Jun 10 17:51:57 1996
-@@ -105,6 +105,14 @@
- #include <linux/kerneld.h>
- #endif
- 
-+#ifdef CONFIG_ATM_TCP
-+#include <linux/atm.h>
-+int (*atmtcp_attach_hook)(struct socket *sock) = NULL;
-+#endif
-+#ifdef CONFIG_AREQUIPA
-+#include <linux/arequipa.h>
-+#endif
-+
- #define min(a,b)	((a)<(b)?(a):(b))
- 
- extern struct proto packet_prot;
-@@ -415,6 +423,10 @@
- 
- 	if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
- 	{
-+#ifdef CONFIG_AREQUIPA
-+		if (sk->arequipa) arequipa_close(sk);
-+		if (sk->aq_route) kfree_s(sk->aq_route,sizeof(struct rtable));
-+#endif
- 		if(sk->opt)
- 			kfree(sk->opt);
- 		ip_rt_put(sk->ip_route_cache);
-@@ -662,6 +674,10 @@
- 			sk_free(sk);
- 			return(-ESOCKTNOSUPPORT);
- 	}
-+#ifdef CONFIG_AREQUIPA
-+	sk->arequipa = NULL;
-+	sk->aq_route = NULL;
-+#endif
- 	sk->socket = sock;
- #ifdef CONFIG_TCP_NAGLE_OFF
- 	sk->nonagle = 1;
-@@ -1334,7 +1350,18 @@
- 				return((*dlci_ioctl_hook)(cmd, (void *) arg));
- #endif
- 			return -ENOPKG;
--
-+#ifdef CONFIG_ATM_TCP
-+		case SIOCSIFATMTCP:
-+			if (atmtcp_attach_hook)
-+				return atmtcp_attach_hook(sock);
-+			return -EINVAL;
-+#endif
-+#ifdef CONFIG_AREQUIPA
-+		case AREQUIPA_EXPECT:
-+			return arequipa_expect(sk,arg);
-+		case AREQUIPA_CLOSE:
-+			return arequipa_close(sk);
-+#endif
- 		default:
- 			if ((cmd >= SIOCDEVPRIVATE) &&
- 			   (cmd <= (SIOCDEVPRIVATE + 15)))
---- ref/net/protocols.c	Sat Mar 30 12:20:34 1996
-+++ work/net/protocols.c	Mon Jun 10 17:52:25 1996
-@@ -42,6 +42,10 @@
- #include <linux/trdevice.h>
- extern void rif_init(struct net_proto *);
- #endif
-+#ifdef CONFIG_ATM
-+#include <linux/atm.h>
-+#endif
-+
- /*
-  *	Protocol Table
-  */
-@@ -73,6 +77,10 @@
- #endif
- #ifdef CONFIG_ATALK
-   { "DDP",	atalk_proto_init },			/* Netatalk Appletalk driver	*/
-+#endif
-+#ifdef CONFIG_ATM
-+  { "ATMPVC",   atmpvc_proto_init },
-+  { "ATMSVC",   atmsvc_proto_init },
- #endif
-   { NULL,	NULL		}			/* End marker			*/
- };
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/mmuio.c	Mon Jun 10 18:21:54 1996
-@@ -0,0 +1,474 @@
-+/* net/atm/mmuio.c - MMU-supported high-speed I/O */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/mmuio.h>
-+
-+
-+#ifdef CONFIG_MMU_HACKS
-+
-+#define invalidate flush_tlb_all /* @@@ improve this */
-+
-+
-+#include <linux/kernel.h>
-+#include <linux/sched.h>
-+#include <linux/errno.h>
-+#include <linux/mm.h>
-+#include <linux/swap.h>
-+#include <linux/pagemap.h>
-+#include <linux/uio.h>
-+#include <asm/page.h>
-+#include <asm/pgtable.h>
-+#include <asm/segment.h>
-+
-+#include <asm/bitops.h>
-+
-+#include <linux/skbuff.h>
-+
-+#include <linux/netdevice.h> /* needed to include net/sock.h */
-+#include <net/sock.h>
-+
-+
-+/* #define MAX_SC_LOCKS 3 */
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+#ifndef CONFIG_MMU_HACKS_DEBUG
-+ 
-+#define EVENT(s,a,b,c) DPRINTK(s,a,b,c)
-+ 
-+static void event_dump(void) {}
-+
-+#else
-+ 
-+/*
-+ * Very extensive activity logging. Greatly improves bug detection speed but
-+ * costs a few Mbps if enabled.
-+ */
-+ 
-+#define EV 64
-+ 
-+static const char *ev[EV];
-+
-+static unsigned long ev_a[EV],ev_b[EV],ev_c[EV];
-+static int ec = 0;
-+ 
-+ 
-+static void EVENT(const char *s,unsigned long a,unsigned long b,unsigned long c)
-+{
-+	ev[ec] = s;
-+	ev_a[ec] = a;
-+	ev_b[ec] = b;
-+	ev_c[ec] = c;
-+	ec = (ec+1) % EV;
-+}
-+
-+
-+static void event_dump(void)
-+{
-+	int n,i;
-+ 
-+	printk(KERN_NOTICE "----- Event dump follows -----\n");
-+	for (n = 0; n < EV; n++) {
-+		i = (ec+n) % EV;
-+		printk(KERN_NOTICE);
-+		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i],ev_c[i]);
-+	}
-+	printk(KERN_NOTICE "----- Event dump ends here -----\n");
-+}
-+ 
-+ 
-+#endif /* CONFIG_MMU_HACKS_DEBUG */
-+
-+/*
-+ * Helper functions to walk through page tables. If CREATE is set, they add
-+ * new entries if needed to reach a given PTE. PTEs are never created. If
-+ * CREATE is not set, *PMD and *PTE might become NULL while passing unavailable
-+ * memory regions.
-+ */
-+
-+
-+static inline int mmu_resolve(unsigned long addr,pgd_t **pgd,pmd_t **pmd,
-+    pte_t **pte,int create)
-+{
-+	*pgd = pgd_offset(current->mm,addr);
-+	*pmd = create ? pmd_alloc(*pgd,addr) : pmd_offset(*pgd,addr);
-+	if (!*pmd) {
-+		if (create) invalidate();
-+		*pte = NULL;
-+		return -ENOMEM;
-+	}
-+	*pte = create ? pte_alloc(*pmd,addr) : pte_offset(*pmd,addr);
-+	if (*pte) return 0;
-+	if (create) invalidate();
-+	return -ENOMEM;
-+}
-+
-+
-+static inline int mmu_step(unsigned long addr,pgd_t **pgd,pmd_t **pmd,
-+    pte_t **pte,int create)
-+{
-+	if (addr & (PTRS_PER_PTE*PAGE_SIZE-1)) {
-+		if (*pte) (*pte)++;
-+	}
-+	else {
-+		if (addr & (PTRS_PER_PMD*PTRS_PER_PTE*PAGE_SIZE-1)) {
-+			if (*pmd) (*pmd)++;
-+		}
-+		else {
-+			(*pgd)++;
-+			*pmd = create ? pmd_alloc(*pgd,addr) :
-+			    pmd_offset(*pgd,addr);
-+			if (!*pmd) {
-+				if (create) invalidate();
-+				*pte = NULL;
-+				return -ENOMEM;
-+			}
-+		}
-+		*pte = create ? pte_alloc(*pmd,addr) : pte_offset(*pmd,addr);
-+		if (!*pte) {
-+			if (create) invalidate();
-+			return -ENOMEM;
-+		}
-+	}
-+	return 0;
-+}
-+
-+
-+/*
-+ * Removes a range of pages belonging to the current process. This helps to
-+ * avoid undesirable copying when COW or swapped-out pages are overwritten in
-+ * one sweep.
-+ */
-+
-+
-+void free_range(unsigned long start,unsigned long size)
-+{
-+	pgd_t *pgd;
-+	pmd_t *pmd;
-+	pte_t *pte;
-+	unsigned long end;
-+
-+	end = (start+size) & ~(PAGE_SIZE-1);
-+	start = (start+PAGE_SIZE-1) & ~(PAGE_SIZE-1);
-+	if (start <= end) return;
-+	(void) mmu_resolve(start,&pgd,&pmd,&pte,0);
-+	while (1) {
-+		if (pte && !pte_none(*pte)) {
-+			pte_t old_page;
-+
-+			old_page = *pte;
-+			pte_clear(pte);
-+			if (!pte_present(old_page))
-+				swap_free(pte_val(old_page));
-+			else {
-+				current->mm->rss--;
-+				free_page(pte_page(old_page));
-+			}
-+		}
-+		if ((start += PAGE_SIZE) >= end) break;
-+		(void) mmu_step(start,&pgd,&pmd,&pte,0);
-+	}
-+	invalidate();
-+}
-+
-+
-+/*
-+ * Copies data by mapping kernel pages into the current process. If the data is
-+ * mis-aligned or if no whole pages can be copied, ordinary memory-to-memory
-+ * copies are done.
-+ *
-+ * TODO: Speed improvement: if copying "almost" a page, don't copy from kernel
-+ * to user, but still map the kernel page and copy the user data instead.
-+ * This may also reduce the number of bad COW/swap activity.
-+ *
-+ */
-+
-+
-+struct page_descriptor {
-+        struct page_descriptor *next;
-+        struct block_header *firstfree;
-+        int order;
-+        int nfree;
-+};
-+
-+
-+/*
-+ * Since we always work on "big" buffers (>= one memory page), kmalloc's 
-+ * page sharing doesn't get in the way.
-+ */
-+
-+
-+extern volatile unsigned long net_skbcount;
-+
-+
-+static void free_around(struct sk_buff *skb,unsigned long start,
-+    unsigned long end)
-+{
-+	struct page_descriptor *dsc;
-+	unsigned long order,first,last;
-+
-+	net_skbcount--;
-+	/* FIXME: should also update kmalloc counters @@@ */
-+	dsc = (struct page_descriptor *) ((unsigned long) skb->head &
-+	    PAGE_MASK);
-+	order = dsc->order;
-+	order = order < 7 ? 0 : order-7;
-+	first = (unsigned long) dsc;
-+	last = first+(PAGE_SIZE << order);
-+	if (mem_map[MAP_NR(first)].count != 1) {
-+		printk(KERN_CRIT "free_around: mem_map[%ld].count is 0x%x\n",
-+		    MAP_NR(first),mem_map[MAP_NR(first)].count);
-+		event_dump();
-+		return;
-+	}
-+	while (first < last) {
-+		mem_map[MAP_NR(first)].count = 1;
-+		if (first < start || first >= end) free_page(first);
-+		first += PAGE_SIZE;
-+	}
-+}
-+
-+
-+/* fixme: what if reading into shared memory region ? */
-+void mmucp_tofs(unsigned long user,unsigned long size,struct sk_buff *skb,
-+    unsigned long kernel)
-+{
-+	unsigned long extra;
-+	pgd_t *pgd;
-+	pmd_t *pmd;
-+	pte_t *pte;
-+	int error;
-+	unsigned long hole_start;
-+
-+	if (size > skb->len) size = skb->len;
-+	EVENT("mmucp_tofs 0x%lx to 0x%lx+%ld\n",kernel,user,size);
-+	if (skb->prev || skb->next || skb->lock || skb->users > 1) {
-+		memcpy_tofs((void *) user,(void *) kernel,size);
-+		return;
-+	}
-+	if (((kernel^user) & (PAGE_SIZE-1)) || size < PAGE_SIZE) {
-+		EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,size);
-+		memcpy_tofs((void *) user,(void *) kernel,size);
-+		kfree_skb(skb,FREE_READ);
-+		return;
-+	}
-+	if ((extra = -user & (PAGE_SIZE-1))) {
-+		if ((size -= extra) < PAGE_SIZE) {
-+			EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,
-+			    size+extra);
-+			memcpy_tofs((void *) user,(void *) kernel,size+extra);
-+			kfree_skb(skb,FREE_READ);
-+			return;
-+		}
-+		EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,size);
-+		memcpy_tofs((void *) user,(void *) kernel,extra);
-+		user += extra;
-+		kernel += extra;
-+	}
-+	if ((error = mmu_resolve(user,&pgd,&pmd,&pte,1)) < 0) {
-+		invalidate();
-+		oom(current);
-+		return;
-+	}
-+	hole_start = kernel;
-+	while (1) {
-+		pte_t old_page;
-+
-+		if (mem_map[MAP_NR(pte_page(*pte))].count > 1) {
-+			EVENT("memcpy(0x%lx,0x%lx,PAGE_SIZE);\n",user,kernel,0);
-+			memcpy_tofs((void *) user,(void *) kernel,PAGE_SIZE);
-+		}
-+		else {
-+			old_page = *pte;
-+			pte_clear(pte);
-+			if (!pte_none(old_page))
-+				if (!pte_present(old_page))
-+					swap_free(pte_val(old_page));
-+				else {
-+					current->mm->rss--;
-+					free_page(pte_page(old_page));
-+				}
-+			mem_map[MAP_NR(kernel)].count = 1;
-+			/* Page is now owned only by user. */
-+			*pte = mk_pte(kernel,PAGE_SHARED);
-+			*pte = pte_mkdirty(*pte);
-+			EVENT("mapped 0x%lx at 0x%lx\n",kernel,user,0);
-+			current->mm->rss++;
-+		}
-+		user += PAGE_SIZE;
-+		kernel += PAGE_SIZE;
-+		if ((size -= PAGE_SIZE) < PAGE_SIZE) break;
-+		if ((error = mmu_step(user,&pgd,&pmd,&pte,1)) < 0) {
-+			kernel -= PAGE_SIZE; /* back off */
-+			size = 0;
-+			oom(current);
-+			break;
-+		}
-+	}
-+	if (size) {
-+		EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,size);
-+		memcpy_tofs((void *) user,(void *) kernel,size);
-+	}
-+	invalidate();
-+	/* use skb code for all the administrative overhead */
-+	skb->count++;
-+	kfree_skb(skb,FREE_READ);
-+	if (skb->count == 1) free_around(skb,hole_start,kernel);
-+	else {
-+		printk(KERN_CRIT "mmu_tofs: skb->count == %d\n",skb->count);
-+		event_dump();
-+	}
-+}
-+
-+
-+/*
-+ * Fault user pages (current process) in physical memory, lock them there,
-+ * and set them COW so that they stay around even if the user process tries
-+ * to scribble over them. The locked physical page ranges are recorded in the
-+ * scatter-gather vector IOV.
-+ *
-+ * FIXME: Should check user's physical memory size limit.
-+ */
-+
-+
-+int lock_user(unsigned long start,unsigned long size,int iov_max,
-+    struct iovec *iov)
-+{
-+	struct vm_area_struct *vma;
-+	unsigned long end,last,from,page;
-+	unsigned long flags;
-+	pgd_t *pgd;
-+	pmd_t *pmd;
-+	pte_t *pte;
-+	int iovcnt,error;
-+
-+	EVENT("lock_user: %ld@0x%lx\n",size,start,0);
-+	end = start+size;
-+	if (start >= end) return 0;
-+	for (vma = find_vma(current,start); vma; vma = vma->vm_next)
-+		if (vma->vm_flags & (VM_SHARED | VM_SHM)) return -EAGAIN;
-+		else if (vma->vm_end >= end) break;
-+	iovcnt = 0;
-+	if ((error = mmu_resolve(start,&pgd,&pmd,&pte,1)) < 0) return error;
-+	last = from = 0;
-+	while (1) {
-+		mem_map_t *map;
-+
-+		EVENT("<0x%lx|0x%lx|0x%lx>\n",(unsigned long) pgd,
-+		    (unsigned long) pmd,(unsigned long) pte);
-+		EVENT("0x%lx,0x%lx,%d\n",start,end,iovcnt);
-+		if (pte_none(*pte) || !pte_present(*pte)) {
-+			struct vm_area_struct *vma;
-+ 
-+			EVENT("handling missing page\n",0,0,0);
-+			if (!(vma = find_vma(current,start))) {
-+				printk(KERN_CRIT "lock_user: VMA (0x%lx) not "
-+				    "found",start);
-+				event_dump();
-+				return -EINVAL;
-+			}
-+			do_no_page(current,vma,start,vma->vm_flags & VM_WRITE);
-+		}
-+		page = pte_page(*pte);
-+		EVENT("got page 0x%lx\n",page,0,0);
-+		if (!page) {
-+			printk(KERN_ERR "lock_user: Gnorf, no page\n");
-+			event_dump();
-+			return -EINVAL;
-+		}
-+		map = mem_map+MAP_NR(page);
-+		if (map->reserved) {
-+			printk(KERN_ERR "lock_user: reserved\n");
-+			event_dump();
-+			return -EINVAL;
-+		}
-+		if (!map->locked && PageSingleCopy(map))
-+			printk(KERN_CRIT "strange, !l && u\n");
-+		save_flags(flags);
-+		cli();
-+		while (map->locked /* && SC = 0 or SC = max */) {
-+			EVENT("Waiting for page ...\n",0,0,0);
-+			wait_on_page(map);
-+		}
-+		if (!map->locked) {
-+			map->count++;
-+			map->locked = 1;
-+			*pte = pte_wrprotect(*pte);
-+		}
-+		set_bit(PG_single_copy,&map->flags);
-+		restore_flags(flags);
-+		if (!last) {
-+			from = page+(start & (PAGE_SIZE-1));
-+			start &= ~(PAGE_SIZE-1);
-+		}
-+		else if (page != last+PAGE_SIZE) {
-+				if (iovcnt >= iov_max) return -ENOSPC;
-+				iov->iov_base = (caddr_t) from;
-+				iov->iov_len = last+PAGE_SIZE-from;
-+				EVENT("putting ... last=0x%lx,from=0x%lx\n",
-+				    last,from,0);
-+				iovcnt++;
-+				iov++;
-+                                from = page;
-+			}
-+		last = page;
-+		if ((start += PAGE_SIZE) >= end) break;
-+		if ((error = mmu_step(start,&pgd,&pmd,&pte,1)) < 0)
-+			return error;
-+	}
-+	invalidate();
-+	if (iovcnt >= iov_max) return -ENOSPC;
-+	iov->iov_base = (caddr_t) from;
-+	iov->iov_len = last+(end & (PAGE_SIZE-1))-from;
-+	if (start == end) iov->iov_len += PAGE_SIZE;
-+/*
-+for (i = 0; i <= iovcnt; i++)
-+  printk("iov[%d].iov_base = 0x%lx\niov[%d].iov_len = 0x%lx\n",
-+i,(unsigned long) iov[i-iovcnt].iov_base,i,iov[i-iovcnt].iov_len);
-+*/
-+	return iovcnt+1;
-+}
-+
-+
-+/*
-+ * Release user pages locked with lock_user. wrprotect isn't cleared, so we'll
-+ * get a few extra protection faults (COW handling doesn't copy pages that are
-+ * not shared), but that shouldn't do any harm.
-+ */
-+
-+
-+void unlock_user(int iovcnt,struct iovec *iov)
-+{
-+	unsigned long walk,end;
-+ 
-+	while (iovcnt--) {
-+		end = (unsigned long) iov->iov_base+iov->iov_len;
-+		for (walk = (unsigned long) iov->iov_base & ~(PAGE_SIZE-1);
-+		    walk < end; walk += PAGE_SIZE) {
-+			mem_map_t *map;
-+
-+			map = mem_map+MAP_NR(walk);
-+			if (!clear_bit(PG_single_copy,&map->flags))
-+				printk(KERN_CRIT "unlock_user: returning "
-+				    "unlocked page\n");
-+			/* -- if last lock is gone */
-+			EVENT("returning page 0x%08lx\n",walk,0,0);
-+			map->locked = 0;
-+			wake_up(&map->wait);
-+			free_page(walk);
-+			/* -- endif */
-+		}
-+		iov++;
-+	}
-+}
-+
-+#endif
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/mmuio.h	Wed Jul 17 19:58:01 1996
-@@ -0,0 +1,25 @@
-+/* mmuio.h - MMU-supported high-speed I/O */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef _LINUX_MMUIO_H
-+#define _LINUX_MMUIO_H
-+
-+#include <linux/config.h>
-+
-+#ifdef CONFIG_MMU_HACKS
-+
-+#include <linux/uio.h>
-+#include <linux/skbuff.h>
-+
-+void free_range(unsigned long start,unsigned long size);
-+void mmucp_tofs(unsigned long user,unsigned long size,struct sk_buff *skb,
-+    unsigned long kernel);
-+int lock_user(unsigned long start,unsigned long size,int iov_max,
-+    struct iovec *iov);
-+void unlock_user(int iovcnt,struct iovec *iov);
-+
-+#endif
-+
-+#endif
---- ref/include/linux/mm.h	Sun Jun  9 11:23:32 1996
-+++ work/include/linux/mm.h	Wed Jul 17 19:40:54 1996
-@@ -142,6 +142,7 @@
- #define PG_decr_after		 5
- #define PG_swap_unlock_after	 6
- #define PG_DMA			 7
-+#define PG_single_copy		 8 /* page is locked for single copy */
- #define PG_reserved		31
- 
- /* Make it prettier to test the above... */
-@@ -154,6 +155,7 @@
- #define PageDecrAfter(page)	(test_bit(PG_decr_after, &(page)->flags))
- #define PageSwapUnlockAfter(page) (test_bit(PG_swap_unlock_after, &(page)->flags))
- #define PageDMA(page)		(test_bit(PG_DMA, &(page)->flags))
-+#define PageSingleCopy(page)	(test_bit(PG_single_copy, &(page)->flags))
- #define PageReserved(page)	(test_bit(PG_reserved, &(page)->flags))
- 
- /*
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/raw.c	Fri Jul 12 16:01:03 1996
-@@ -0,0 +1,128 @@
-+/* net/atm/raw.c - Raw AAL0 and AAL5 transports */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/sched.h>
-+#include <linux/atmdev.h>
-+#include <linux/kernel.h>
-+#include <linux/skbuff.h>
-+#include <linux/mm.h>
-+#include <linux/mmuio.h>
-+#include <linux/uio.h>
-+
-+#include "protocols.h"
-+#include "tunable.h"		/* tunable parameters */
-+
-+
-+#if 0
-+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
-+#else
-+#define DPRINTK(format,args...)
-+#endif
-+
-+
-+/*
-+ * SKB == NULL indicates that the link is being closed
-+ */
-+
-+void atm_push_raw(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+	if (skb) {
-+		skb_queue_tail(&vcc->recvq,skb);
-+		wake_up(&vcc->sleep);
-+	}
-+}
-+
-+
-+/*
-+ * A return value of NULL means to discard the PDU
-+ */
-+
-+
-+static struct sk_buff *atm_peek_aal0(struct atm_vcc *vcc,unsigned long pdu_size,
-+    __u32 (*fetch)(struct atm_vcc *vcc,int i))
-+{
-+	struct sk_buff *skb;
-+
-+	if (pdu_size+vcc->rx_inuse+ATM_PDU_OVHD <= vcc->rx_quota) {
-+		skb = alloc_skb(pdu_size,GFP_ATOMIC);
-+		if (skb) return skb;
-+	}
-+	vcc->stats->rx_drop++;
-+	return NULL;
-+}
-+
-+
-+/*
-+ * atm_peek_aal5 is currently also used for AAL3/4
-+ */
-+
-+
-+static struct sk_buff *atm_peek_aal5(struct atm_vcc *vcc,unsigned long pdu_size,
-+    __u32 (*fetch)(struct atm_vcc *vcc,int i))
-+{
-+	struct sk_buff *skb;
-+
-+	if (pdu_size+vcc->rx_inuse+ATM_PDU_OVHD <= vcc->rx_quota) {
-+		skb = alloc_skb(((pdu_size+3) & ~3)+PAGE_SIZE-1,GFP_ATOMIC);
-+		if (skb) {
-+			skb->data = (unsigned char *)
-+			    (((unsigned long) skb->data+PAGE_SIZE-1) &
-+			    ~(PAGE_SIZE-1));
-+DPRINTK("PEEK: data at 0x%lx\n",(unsigned long) skb->data);
-+			return skb;
-+		}
-+	}
-+	vcc->stats->rx_drop++;
-+	return NULL;
-+}
-+
-+
-+static void atm_pop_raw(struct atm_vcc *vcc,struct sk_buff *skb)
-+{
-+#ifdef CONFIG_MMU_HACKS
-+	if (skb->atm.iovcnt)
-+		unlock_user(skb->atm.iovcnt,(struct iovec *) skb->data);
-+#endif
-+	dev_kfree_skb(skb,FREE_WRITE);
-+#if 0 /* experimental */
-+if (vcc->dev->sending != 1) printk("sending == %d !!!\n",vcc->dev->sending);
-+	vcc->dev->sending--;
-+#endif
-+	wake_up(&vcc->sleep);
-+}
-+
-+
-+int atm_init_aal0(struct atm_vcc *vcc)
-+{
-+	vcc->aal = ATM_AAL0;
-+	vcc->push = atm_push_raw;
-+	vcc->peek = atm_peek_aal0;
-+	vcc->pop = atm_pop_raw;
-+	vcc->push_oam = NULL;
-+	return 0;
-+}
-+
-+
-+int atm_init_aal34(struct atm_vcc *vcc)
-+{
-+	vcc->aal = ATM_AAL34;
-+	vcc->push = atm_push_raw;
-+	vcc->peek = atm_peek_aal5; /* same procedure */
-+	vcc->pop = atm_pop_raw;
-+	vcc->push_oam = NULL;
-+	return 0;
-+}
-+
-+
-+int atm_init_aal5(struct atm_vcc *vcc)
-+{
-+	vcc->aal = ATM_AAL5;
-+	vcc->push = atm_push_raw;
-+	vcc->peek = atm_peek_aal5;
-+	vcc->pop = atm_pop_raw;
-+	vcc->push_oam = NULL;
-+	return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/protocols.h	Mon Jun 10 17:36:28 1996
-@@ -0,0 +1,17 @@
-+/* net/atm/protocols.h - ATM protocol handler entry points */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#ifndef NET_ATM_PROTOCOLS_H
-+#define NET_ATM_PROTOCOLS_H
-+
-+void atm_push_raw(struct atm_vcc *vcc,struct sk_buff *skb);
-+
-+int atm_init_aal0(struct atm_vcc *vcc);	/* "raw" AAL0 */
-+int atm_init_aal34(struct atm_vcc *vcc);/* "raw" AAL3/4 transport */
-+int atm_init_aal5(struct atm_vcc *vcc);	/* "raw" AAL5 transport */
-+int atm_init_clip(struct atm_vcc *vcc);	/* Classical IP over ATM (RFC1577) */
-+int atm_init_atmarp(struct atm_vcc *vcc);/* ATM ARP */
-+
-+#endif
---- ref/include/linux/netdevice.h	Sun Jun  9 11:25:36 1996
-+++ work/include/linux/netdevice.h	Wed Jul 17 19:41:04 1996
-@@ -182,6 +182,8 @@
-   int			  (*set_mac_address)(struct device *dev, void *addr);
- #define HAVE_PRIVATE_IOCTL
-   int			  (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);
-+  int			  (*ip_arp)(struct device *dev,unsigned int cmd,
-+				    void *arg);
- #define HAVE_SET_CONFIG
-   int			  (*set_config)(struct device *dev, struct ifmap *map);
- #define HAVE_HEADER_CACHE
-@@ -284,6 +286,8 @@
- /* These functions live elsewhere (drivers/net/net_init.c, but related) */
- 
- extern void		ether_setup(struct device *dev);
-+extern int		ether_arp(struct device *dev,unsigned int cmd,
-+				  void *arg);
- extern void		tr_setup(struct device *dev);
- extern int		ether_config(struct device *dev, struct ifmap *map);
- /* Support for loadable net-drivers */
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atmarp.h	Wed Jul 17 19:58:01 1996
-@@ -0,0 +1,98 @@
-+/* atmarp.h - ATM ARP protocol and kernel-demon interface definitions */
-+ 
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef _LINUX_ATMARP_H
-+#define _LINUX_ATMARP_H
-+
-+#include <linux/socket.h>
-+#include <linux/atm.h>
-+#include <linux/atmioc.h>
-+
-+
-+/* RFC 1577 ATM ARP header */
-+
-+struct atmarphdr {
-+	unsigned short	ar_hrd;	/* Hardware type */
-+	unsigned short	ar_pro;	/* Protocol type */
-+	unsigned char	ar_shtl;/* Type & length of source ATM number (q) */
-+	unsigned char	ar_sstl;/* Type & length of source ATM subaddress (r) */
-+	unsigned short	ar_op;	/* Operation code (request, reply, or NAK) */
-+	unsigned char	ar_spln;/* Length of source protocol address (s) */
-+	unsigned char	ar_thtl;/* Type & length of target ATM number (x) */
-+	unsigned char	ar_tstl;/* Type & length of target ATM subaddress (y) */
-+	unsigned char	ar_tpln;/* Length of target protocol address (z) */
-+	/* ar_sha, at_ssa, ar_spa, ar_tha, ar_tsa, ar_tpa */
-+	unsigned char data[1];
-+};
-+
-+#define	TL_LEN	0x3f	/* ATMARP Type/Length field structure */
-+#define	TL_E164	0x40
-+
-+
-+#define ATF_NULL	0x040	/* use NULL encapsulation */
-+#define ATF_ARPSRV	0x080	/* entry describes ARP server */
-+
-+
-+#define MAX_ATMARP_SIZE (sizeof(struct atmarphdr)-1+2*(ATM_E164_LEN+ \
-+			ATM_ESA_LEN+4))
-+
-+#define ATMARP_RETRY_DELAY 30		/* request next resolution or forget
-+					   NAK after 30 sec - should go into
-+					   atmclip.h */
-+
-+/* These really ought to go into include/linux/if_arp.h */
-+
-+/* ARP protocol HARDWARE identifiers. */
-+#define ARPHRD_ATM	19		/* ATM Forum			*/
-+
-+/* ARP protocol opcodes. */
-+#define ARPOP_InREQUEST	8		/* InARP request		*/
-+#define ARPOP_InREPLY	9		/* InARP reply			*/
-+#define ARPOP_NAK	10		/* (ATM)ARP NAK			*/
-+
-+
-+#define ATMARPD_CTRL	_IO('a',ATMIOC_CLIP+1)	/* become atmarpd ctrl sock */
-+#define ATMARP_MKIP	_IO('a',ATMIOC_CLIP+2)	/* attach socket to IP */
-+#define ATMARP_SETENTRY	_IO('a',ATMIOC_CLIP+3)	/* fill or hide ARP entry */
-+#define ATMARP_ENCAP	_IO('a',ATMIOC_CLIP+5)	/* change encapsulation */
-+
-+/* ATMARP ioctl request. */
-+struct atmarpreq {
-+	struct sockaddr		arp_pa;		/* protocol address */
-+	struct sockaddr_atmsvc	arp_ha;		/* PVC or SVC address */
-+	struct atm_qos		arp_qos;	/* requested QOS */
-+	int			arp_flags;	/* flags */
-+};
-+
-+
-+struct atmarp_arpsioc {
-+	struct sockaddr		pa;	/* protocol address */
-+	int			aa_len;	/* size of ATM address */
-+	struct sockaddr_atmsvc	aa;	/* SVC address */
-+};
-+
-+
-+#define ATMARP_CTRL_MAGIC	0xac	/* put this into the magic byte */
-+
-+enum atmarp_ctrl_type {
-+	act_invalid,		/* catch uninitialized structures */
-+	act_need,		/* need address resolution */
-+	act_create,		/* interface has been created */
-+	act_up,			/* interface is coming up */
-+	act_down,		/* interface is going down */
-+	act_ioctl,		/* ioctl follows */
-+	act_complete		/* demon indicates completion */
-+};
-+
-+struct atmarp_ctrl {
-+	unsigned char		magic;	/* constant */
-+	enum atmarp_ctrl_type	type;	/* message type */
-+	volatile int		*reply;	/* reply address, NULL is asynch */
-+	int			itf_num;/* interface number (if present) */
-+	unsigned long		arg;	/* argument, e.g. IP address */
-+	unsigned char		data[1];/*optional data */
-+};
-+
-+#endif
---- ref/net/ipv4/udp.c	Tue Jun  4 14:34:43 1996
-+++ work/net/ipv4/udp.c	Mon Jun 10 19:11:22 1996
-@@ -109,6 +109,11 @@
- #include <net/route.h>
- #include <net/checksum.h>
- 
-+#ifdef CONFIG_AREQUIPA
-+#include <linux/atmdev.h>
-+#include <linux/arequipa.h>
-+#endif
-+
- /*
-  *	Snmp MIB for the UDP layer
-  */
-@@ -616,7 +621,7 @@
- 	sk->dummy_th.dest = usin->sin_port;
- 	sk->state = TCP_ESTABLISHED;
- 	udp_cache_zap();
--	sk->ip_route_cache = rt;
-+	set_rt_cache(sk,rt);
- 	return(0);
- }
- 
-@@ -655,6 +660,12 @@
- 
- static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
- {
-+#ifdef CONFIG_AREQUIPA
-+	if (skb->dev == arequipa_dev && !sk->arequipa && sk->aq_route) {
-+		if (!sk->ip_route_cache) printk("WNG: no route\n");
-+		(void) arequipa_attach(skb->atm.vcc->sock,sk);
-+	}
-+#endif
- 	skb->sk = sk;
- 
- 	if (sk->users) {
---- ref/net/ipv4/tcp.c	Sat Jun  8 11:09:09 1996
-+++ work/net/ipv4/tcp.c	Wed Jul 17 19:39:31 1996
-@@ -1926,6 +1926,11 @@
- 	 *	Put in the IP header and routing stuff.
- 	 */
- 
-+#ifdef CONFIG_AREQUIPA
-+	ip_rt_put(sk->ip_route_cache);
-+	set_rt_cache(sk,ip_rt_route(sk->daddr,sk->localroute));
-+#endif
-+
- 	tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
- 		IPPROTO_TCP, sk->opt, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
- 	if (tmp < 0)
---- ref/include/linux/proc_fs.h	Sun Jun  9 11:23:42 1996
-+++ work/include/linux/proc_fs.h	Wed Jul 17 19:41:04 1996
-@@ -34,6 +34,7 @@
- 	PROC_KSYMS,
- 	PROC_DMA,	
- 	PROC_IOPORTS,
-+	PROC_ATM,
- #ifdef __SMP_PROF__
- 	PROC_SMP_PROF,
- #endif
-@@ -148,6 +149,16 @@
- #define PROC_DYNAMIC_FIRST 4096
- #define PROC_NDYNAMIC      4096
- 
-+enum atm_directory_inos {
-+	PROC_ATM_DEVICES = 384,
-+	PROC_ATM_ARP,
-+        PROC_ATM_LEC,
-+	PROC_ATM_SVC,
-+	PROC_ATM_PVC,
-+	PROC_ATM_AREQUIPA,
-+	PROC_ATM_LAST
-+};
-+
- #define PROC_SUPER_MAGIC 0x9fa0
- 
- /*
-@@ -187,6 +198,7 @@
- extern struct proc_dir_entry proc_root;
- extern struct proc_dir_entry proc_net;
- extern struct proc_dir_entry proc_scsi;
-+extern struct proc_dir_entry proc_atm;
- extern struct proc_dir_entry proc_sys;
- extern struct proc_dir_entry proc_pid;
- extern struct proc_dir_entry proc_pid_fd;
-@@ -275,5 +287,6 @@
- extern struct inode_operations proc_kmsg_inode_operations;
- extern struct inode_operations proc_link_inode_operations;
- extern struct inode_operations proc_fd_inode_operations;
-+extern struct inode_operations proc_atm_inode_operations;
- 
- #endif
---- ref/fs/proc/root.c	Tue Apr 30 12:09:45 1996
-+++ work/fs/proc/root.c	Mon Jun 10 17:36:31 1996
-@@ -15,6 +15,10 @@
- #include <linux/config.h>
- #include <asm/bitops.h>
- 
-+
-+extern void atm_proc_init(void);
-+
-+
- /*
-  * Offset of the first process in the /proc root directory..
-  */
-@@ -148,6 +152,16 @@
- 	NULL, NULL				/* parent, subdir */
- };
- 
-+struct proc_dir_entry proc_atm = {
-+	PROC_ATM, 3, "atm",
-+	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
-+	0, &proc_dir_inode_operations,
-+	NULL, NULL,
-+	NULL,
-+	&proc_root, NULL
-+};
-+
-+
- int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
- {
- 	dp->next = dir->subdir;
-@@ -296,6 +310,11 @@
- 	proc_register(&proc_root, &proc_net);
- 	proc_register(&proc_root, &proc_scsi);
- 	proc_register(&proc_root, &proc_sys_root);
-+
-+#ifdef CONFIG_ATM
-+	proc_register(&proc_root, &proc_atm);
-+	atm_proc_init();
-+#endif
- 
- #ifdef CONFIG_DEBUG_MALLOC
- 	proc_register(&proc_root, &(struct proc_dir_entry) {
---- ref/fs/proc/inode.c	Thu Apr 25 15:32:45 1996
-+++ work/fs/proc/inode.c	Mon Jun 10 17:36:32 1996
-@@ -4,6 +4,7 @@
-  *  Copyright (C) 1991, 1992  Linus Torvalds
-  */
- 
-+#include <linux/config.h>
- #include <linux/sched.h>
- #include <linux/proc_fs.h>
- #include <linux/kernel.h>
-@@ -161,6 +162,14 @@
- 		return;
- 	}
- 
-+#ifdef CONFIG_ATM
-+	if (ino >= PROC_ATM_DEVICES && ino < PROC_ATM_LAST) {
-+		inode->i_mode = S_IFREG | S_IRUGO;
-+		inode->i_op = &proc_atm_inode_operations;
-+		return;
-+	}
-+#endif
-+
- 	if (!pid) {
- 		switch (ino) {
- 			case PROC_KMSG:
-@@ -175,6 +184,13 @@
-                                 inode->i_nlink = 2;
-                                 inode->i_op = &proc_scsi_inode_operations;
-                                 break;
-+#ifdef CONFIG_ATM
-+			case PROC_ATM:
-+				inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
-+				inode->i_nlink = 2;
-+				inode->i_op = &proc_atm_inode_operations;
-+				break;
-+#endif
- 			case PROC_KCORE:
- 				inode->i_mode = S_IFREG | S_IRUSR;
- 				inode->i_op = &proc_kcore_inode_operations;
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/proc.c	Mon Jun 10 18:25:55 1996
-@@ -0,0 +1,479 @@
-+/* net/atm/proc.c - ATM /proc interface */
-+
-+/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
-+
-+
-+#include <linux/config.h>
-+#include <linux/string.h>
-+#include <linux/types.h>
-+#include <linux/mm.h>
-+#include <linux/fs.h>
-+#include <linux/stat.h>
-+#include <linux/proc_fs.h>
-+#include <linux/errno.h>
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/netdevice.h>
-+#include <linux/atmclip.h>
-+#include <linux/atmarp.h>
-+#include <linux/if_arp.h>
-+#include <asm/param.h> /* for HZ */
-+
-+#include "static.h" /* ugly */
-+#include "signaling.h" /* to get sigd - ugly too */
-+#include "atmarp.h"
-+
-+#ifdef CONFIG_ATM_LANE
-+#include "lec_arpc.h"
-+extern struct lec_arp_table *lec_arp_tables[LEC_ARP_TABLE_SIZE];
-+extern struct lec_arp_table *lec_arp_empty_ones;
-+extern struct lec_arp_table *lec_no_forward;
-+#endif
-+
-+#ifdef CONFIG_AREQUIPA
-+void atm_push_arequipa(struct atm_vcc *vcc,struct sk_buff *skb);
-+#endif
-+
-+/*extern void eni_proc(int i,char *buf); - not yet @@@ */
-+extern void atm_push_clip(struct atm_vcc *vcc,struct sk_buff *skb);
-+
-+
-+static int atm_header(ino_t ino,char *buf)
-+{
-+	switch (ino) {
-+		case PROC_ATM_DEVICES:
-+			sprintf(buf,"Itf Type    ESI/\"MAC\"addr "
-+			    "AAL(TX,err,RX,err,drop) ...\n");
-+			break;
-+		case PROC_ATM_ARP:
-+			sprintf(buf,"IPitf TypeEncp Idle IP address      "
-+			    "ATM address\n");
-+			break;
-+		case PROC_ATM_SVC:
-+			sprintf(buf,"Itf VPI VCI   State      Remote\n");
-+			break;
-+		case PROC_ATM_PVC:
-+			sprintf(buf,"Itf VPI VCI   AAL RX(PCR,Class) "
-+			    "TX(PCR,Class)\n");
-+			break;
-+#ifdef CONFIG_ATM_LANE
-+                case PROC_ATM_LEC:
-+                        sprintf(buf," MAC          ATM destination                          Status            Flags VPI/VCI Recv VPI/VCI\n");
-+                        break;
-+#endif
-+#ifdef CONFIG_AREQUIPA
-+		case PROC_ATM_AREQUIPA:
-+			sprintf(buf,"Itf VPI VCI   State    Socket\n");
-+			break;
-+#endif
-+		default:
-+			return -EINVAL;
-+	}
-+	return strlen(buf);
-+}
-+
-+
-+static void add_stats(char *buf,const char *aal,
-+  const struct atm_aal_stats *stats)
-+{
-+	sprintf(strchr(buf,0),"%s ( %ld %ld %ld %ld %ld )",aal,stats->tx,
-+	    stats->tx_err,stats->rx,stats->rx_err,stats->rx_drop);
-+}
-+
-+
-+static void dev_info(const struct atm_dev *dev,char *buf)
-+{
-+	int off,i;
-+
-+	off = sprintf(buf,"%3d %-8s",dev->number,dev->type);
-+	for (i = 0; i < ESI_LEN; i++)
-+		off += sprintf(buf+off,"%02x",dev->esi[i]);
-+	strcat(buf,"  ");
-+	add_stats(buf,"0",&dev->stats.aal0);
-+	strcat(buf,"  ");
-+	add_stats(buf,"5",&dev->stats.aal5);
-+	strcat(buf,"\n");
-+}
-+
-+
-+#ifdef CONFIG_ATM_ATMARP
-+
-+
-+static int svc_addr(char *buf,struct sockaddr_atmsvc *addr)
-+{
-+	static int code[] = { 1,2,10,6,1,0 };
-+	static int e164[] = { 1,8,4,6,1,0 };
-+	int *fields;
-+	int len,i,j,pos;
-+
-+	len = 0;
-+	if (*addr->sas_addr.pub) {
-+		strcpy(buf,addr->sas_addr.pub);
-+		len = strlen(addr->sas_addr.pub);
-+		buf += len;
-+		if (*addr->sas_addr.pub) {
-+			*buf += '+';
-+			len++;
-+		}
-+	}
-+	else if (!*addr->sas_addr.prv) {
-+			strcpy(buf,"(none)");
-+			return strlen(buf);
-+		}
-+	if (*addr->sas_addr.prv) {
-+		len += 44;
-+		pos = 0;
-+		fields = *addr->sas_addr.prv == ATM_AFI_E164 ? e164 : code;
-+		for (i = 0; fields[i]; i++) {
-+			for (j = fields[i]; j; j--) {
-+				sprintf(buf,"%02X",addr->sas_addr.prv[pos++]);
-+				buf += 2;
-+			}
-+			if (fields[i+1]) *buf++ = '.';
-+		}
-+	}
-+	return len;
-+}
-+
-+
-+static void atmarp_info(struct device *dev,struct atmarp_entry *entry,
-+    char *buf)
-+{
-+	unsigned char *ip;
-+	int svc,off,ip_len;
-+
-+	svc = !entry->vcc || entry->vcc->family == AF_ATMSVC;
-+	off = sprintf(buf,"%-6s%-4s%-4s%5ld ",dev->name,svc ? "SVC" : "PVC",
-+	    entry->encap ? "LLC" : "NULL",(jiffies-entry->last_use)/HZ);
-+	ip = (unsigned char *) &entry->ip;
-+	ip_len = sprintf(buf+off,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
-+	off += ip_len;
-+	while (ip_len++ < 16) buf[off++] = ' ';
-+	if (!entry->vcc) strcpy(buf+off,"(incomplete)\n");
-+	else if (!svc)
-+			sprintf(buf+off,"%d.%d.%d\n",entry->vcc->dev->number,
-+			    entry->vcc->vpi,entry->vcc->vci);
-+		else {
-+			off += svc_addr(buf+off,&entry->vcc->remote);
-+			strcpy(buf+off,"\n");
-+		}
-+}
-+
-+
-+#endif
-+
-+
-+static void pvc_info(struct atm_vcc *vcc,char *buf)
-+{
-+	static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
-+	static const char *aal_name[] = {
-+		"0",	"1",	"2",	"3/4",	/*  0- 3 */
-+		"???",	"5",	"???",	"???" };/*  4- 7 */
-+	int off;
-+
-+	off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
-+	    vcc->dev->number,vcc->vpi,vcc->vci,
-+	    vcc->aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
-+	    aal_name[vcc->aal],vcc->rxtp.min_pcr,class_name[vcc->rxtp.class],
-+	    vcc->txtp.min_pcr,class_name[vcc->txtp.class]);
-+#ifdef CONFIG_ATM_CLIP
-+	if (vcc->push == atm_push_clip) {
-+		struct device *dev;
-+
-+		dev = (struct device *) vcc->proto_data;
-+		off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",dev->name);
-+		if (dev->hard_header_len == RFC1483LLC_LEN)
-+		    off += sprintf(buf+off,"LLC/SNAP");
-+		else if (dev->hard_header_len == 0)
-+		    off += sprintf(buf+off,"None");
-+		else off += sprintf(buf+off,"Unknown");
-+	}
-+#endif
-+	strcpy(buf+off,"\n");
-+}
-+
-+
-+static const char *vcc_state(struct atm_vcc *vcc)
-+{
-+    if (vcc->flags & ATM_VF_READY) return "CONNECTED";
-+    if (vcc->flags & ATM_VF_RELEASED) return "CLOSING";
-+    if (vcc->flags & ATM_VF_LISTEN) return "LISTEN";
-+    if (vcc->flags & ATM_VF_REGIS) return "INUSE";
-+    if (vcc->flags & ATM_VF_BOUND) return "BOUND";
-+    return "IDLE";
-+}
-+
-+
-+static void svc_info(struct atm_vcc *vcc,char *buf)
-+{
-+	char *here;
-+	int i;
-+
-+	if (!vcc->dev) sprintf(buf,"Unassigned    ");
-+	else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,vcc->vci);
-+	here = strchr(buf,0);
-+	here += sprintf(here,"%-10s ",vcc == sigd ? "Signaling" :
-+#ifdef CONFIG_ATM_ATMARP
-+	    vcc == atmarpd ? "ATMARPctrl" :
-+#endif
-+	    vcc_state(vcc));
-+	here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
-+	    *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
-+	if (*vcc->remote.sas_addr.prv)
-+		for (i = 0; i < ATM_ESA_LEN; i++)
-+			here += sprintf(here,"%02x",
-+			    vcc->remote.sas_addr.prv[i]);
-+	strcat(here,"\n");
-+}
-+
-+
-+#ifdef CONFIG_AREQUIPA
-+
-+
-+static const char *arequipa_state(const struct atm_vcc *vcc)
-+{
-+	if (!(vcc->flags & ATM_VF_REGIS)) return "DOOMED";
-+	if (vcc->upper) return "ATTACHED";
-+	return "DANGLING";
-+}
-+
-+
-+static void arequipa_info(struct atm_vcc *vcc,char *buf)
-+{
-+	char *here;
-+ 
-+	if (!vcc->dev) sprintf(buf,"Unassigned    ");
-+	else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,vcc->vci);
-+        here = strchr(buf,0);
-+        here += sprintf(here,"%-8s ",arequipa_state(vcc));
-+	if (vcc->upper)
-+		here += sprintf(here,"%ld",vcc->upper->socket &&
-+		    SOCK_INODE(vcc->upper->socket) ?
-+		    SOCK_INODE(vcc->upper->socket)->i_ino : 0);
-+	strcat(here,"\n");
-+}
-+
-+
-+#endif
-+
-+
-+#ifdef CONFIG_ATM_LANE
-+static char*
-+lec_arp_get_status_string(unsigned char status)
-+{
-+  switch(status) {
-+  case ESI_UNKNOWN:
-+    return "ESI_UNKNOWN       ";
-+  case ESI_ARP_PENDING:
-+    return "ESI_ARP_PENDING   ";
-+  case ESI_VC_PENDING:
-+    return "ESI_VC_PENDING    ";
-+  case ESI_FLUSH_PENDING:
-+    return "ESI_FLUSH_PENDING ";
-+  case ESI_FORWARD_DIRECT:
-+    return "ESI_FORWARD_DIRECT";
-+  default:
-+    return "<Unknown>         ";
-+  }
-+}
-+
-+static void 
-+lec_info(struct lec_arp_table *entry, char *buf)
-+{
-+        int j, offset=0;
-+        
-+
-+        for(j=0;j<ETH_ALEN;j++) {
-+                offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
-+        }
-+        offset+=sprintf(buf+offset, " ");
-+        for(j=0;j<ATM_ESA_LEN;j++) {
-+                offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
-+        }
-+        offset+=sprintf(buf+offset, " %s %4.4x",
-+                        lec_arp_get_status_string(entry->status),
-+                        entry->flags&0xffff);
-+        if (entry->vcc) {
-+                offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi, 
-+                                entry->vcc->vci);
-+        }
-+        if (entry->recv_vcc) {
-+                offset+=sprintf(buf+offset, "     %3d %3d", 
-+                                entry->recv_vcc->vpi, entry->recv_vcc->vci);
-+        }
-+
-+        sprintf(buf+offset,"\n");
-+}
-+#endif
-+
-+
-+static int atm_info(ino_t ino,loff_t *pos,char *buf)
-+{
-+	switch (ino) {
-+		case PROC_ATM_DEVICES:
-+			while (*pos <= MAX_ATM_ITF)
-+				if (atm_dev[*pos-1].ops) break;
-+				else (*pos)++;
-+			if (*pos > MAX_ATM_ITF) return 0;
-+			dev_info(atm_dev+*pos-1,buf);
-+			break;
-+#ifdef CONFIG_ATM_ATMARP
-+		case PROC_ATM_ARP:
-+			{
-+				struct device *dev;
-+				struct atmarp_entry *entry;
-+				int count;
-+
-+				count = *pos;
-+				for (dev = clip_devs; dev;
-+				    dev = PRIV(dev)->next)
-+					for (entry = PRIV(dev)->table; entry;
-+					    entry = entry->next)
-+						if (!--count) {
-+							atmarp_info(dev,entry,
-+							    buf);
-+							return strlen(buf);
-+						}
-+				return 0;
-+			}
-+			return 0;
-+#endif
-+		case PROC_ATM_SVC:
-+			while (*pos <= MAX_ATM_VCC)
-+				if (atm_vcc[*pos-1].family == PF_ATMSVC) break;
-+				else (*pos)++;
-+			if (*pos > MAX_ATM_VCC) return 0;
-+			svc_info(atm_vcc+*pos-1,buf);
-+			break;
-+		case PROC_ATM_PVC:
-+			while (*pos <= MAX_ATM_VCC)
-+				if (atm_vcc[*pos-1].family == PF_ATMPVC &&
-+				    atm_vcc[*pos-1].dev) break;
-+				else (*pos)++;
-+			if (*pos > MAX_ATM_VCC) return 0;
-+			pvc_info(atm_vcc+*pos-1,buf);
-+			break;
-+#ifdef CONFIG_AREQUIPA
-+		case PROC_ATM_AREQUIPA:
-+			while (*pos <= MAX_ATM_VCC)
-+				if (atm_vcc[*pos-1].family == PF_ATMSVC &&
-+				    atm_vcc[*pos-1].push == atm_push_arequipa)
-+					break;
-+				else (*pos)++;
-+			if (*pos > MAX_ATM_VCC) return 0;
-+			arequipa_info(atm_vcc+*pos-1,buf);
-+			break;
-+#endif
-+#ifdef CONFIG_ATM_LANE
-+               case PROC_ATM_LEC:
-+                 {
-+                          struct lec_arp_table *entry;
-+                          int i, count;
-+                          
-+                          count = *pos;
-+                          for(i=0;i<LEC_ARP_TABLE_SIZE;i++)
-+                                  for(entry=lec_arp_tables[i];entry;
-+                                      entry=entry->next) {
-+                                          if (!--count) {
-+                                                  lec_info(entry, buf);
-+                                                  return strlen(buf);
-+                                          }
-+                                  }
-+                          for(entry=lec_arp_empty_ones;
-+                              entry; entry=entry->next) {
-+                                  if (!--count) {
-+                                          lec_info(entry, buf);
-+                                          return strlen(buf);
-+                                  }
-+                          }
-+                          for(entry=lec_no_forward;
-+                              entry; entry=entry->next) {
-+                                  if (!--count) {
-+                                          lec_info(entry, buf);
-+                                          return strlen(buf);
-+                                  }
-+                          }
-+                          return 0;
-+                 }
-+#endif
-+		default:
-+			return -EINVAL;
-+	}
-+	return strlen(buf);
-+}
-+
-+
-+static int proc_atm_read(struct inode *inode,struct file *file,char *buf,
-+    int count)
-+{
-+	unsigned long page;
-+	int length;
-+
-+	if (count < 0) return -EINVAL;
-+	page = get_free_page(GFP_KERNEL);
-+	if (!page) return -ENOMEM;
-+	if (file->f_pos)
-+		length = atm_info(inode->i_ino,&file->f_pos,(char *) page);
-+	else length = atm_header(inode->i_ino,(char *) page);
-+	if (length > count) length = -EINVAL;
-+	if (length >= 0) {
-+		memcpy_tofs(buf,(char *) page,length);
-+		file->f_pos++;
-+	}
-+	free_page(page);
-+	return length;
-+}
-+
-+
-+static struct file_operations proc_atm_operations = {
-+	NULL,			/* lseek */
-+	proc_atm_read,		/* read */
-+	NULL,			/* write */
-+	NULL,			/* readdir */
-+	NULL,			/* select */
-+	NULL,			/* ioctl */
-+	NULL,			/* mmap */
-+	NULL,			/* no special open code */
-+	NULL,			/* no special release */
-+	NULL			/* can't fsync */
-+};
-+
-+struct inode_operations proc_atm_inode_operations = {
-+	&proc_atm_operations,	/* default ATM directory file-ops */
-+	NULL,			/* create */
-+	NULL,			/* lookup */
-+	NULL,			/* link */
-+	NULL,			/* unlink */
-+	NULL,			/* symlink */
-+	NULL,			/* mkdir */
-+	NULL,			/* rmdir */
-+	NULL,			/* mknod */
-+	NULL,			/* rename */
-+	NULL,			/* readlink */
-+	NULL,			/* follow_link */
-+	NULL,			/* readpage */
-+	NULL,			/* writepage */
-+	NULL,			/* bmap */
-+	NULL,			/* truncate */
-+	NULL			/* permission */
-+};
-+
-+
-+#define FILE(ino,name,len) &proc_atm, \
-+    (&(struct proc_dir_entry) { ino, len, name, \
-+    S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_atm_inode_operations, NULL })
-+ 
-+ 
-+void atm_proc_init(void)
-+{
-+	proc_register(FILE(PROC_ATM_DEVICES,"devices",7));
-+	proc_register(FILE(PROC_ATM_ARP,"arp",3));
-+	proc_register(FILE(PROC_ATM_SVC,"svc",3));
-+	proc_register(FILE(PROC_ATM_PVC,"pvc",3));
-+#ifdef CONFIG_ATM_LANE
-+        proc_register(FILE(PROC_ATM_LEC,"lec",3));
-+#endif
-+#ifdef CONFIG_AREQUIPA
-+	proc_register(FILE(PROC_ATM_AREQUIPA,"arequipa",8));
-+#endif
-+}
---- ref/drivers/char/console.c	Wed May 15 08:06:55 1996
-+++ work/drivers/char/console.c	Mon Jun 10 17:36:33 1996
-@@ -1972,6 +1972,43 @@
- 	}
- }
- 
-+
-+static unsigned long tty_base = 0;
-+ 
-+ 
-+static void put_scon(char ch)
-+{
-+	unsigned long flags;
-+ 
-+	while (1) {
-+		while (!(inb_p(tty_base+5) & 0x20));
-+		save_flags(flags);
-+		cli();
-+		if (inb_p(tty_base+5) & 0x20) break;
-+		restore_flags(flags);
-+	}
-+	outb_p(ch,tty_base);
-+	restore_flags(flags);
-+}
-+
-+
-+static void print_scon(const char *str)
-+{
-+	if (!tty_base) return;
-+	while (*str) {
-+		if (*str == '\n') put_scon('\r');
-+		put_scon(*str++);
-+	}
-+}
-+ 
-+ 
-+void serial_console_setup(char *str,int *ints)
-+{
-+	if (ints[0] != 1 || ints[1] < 0 || ints[1] > 3) return;
-+	tty_base = ((unsigned short *) 0x400)[ints[1]];
-+}
-+
-+
- /*
-  *  unsigned long con_init(unsigned long);
-  *
-@@ -2090,6 +2127,11 @@
- 		display_desc, video_num_columns, video_num_lines,
- 		MIN_NR_CONSOLES, (MIN_NR_CONSOLES == 1) ? "" : "s",
- 	        MAX_NR_CONSOLES);
-+
-+	if (tty_base) {
-+		register_console(print_scon);
-+		return kmem_start;
-+	}
- 
- 	/*
- 	 * can't register TGA yet, because PCI bus probe has *not* taken
---- ref/init/main.c	Mon May 20 19:33:57 1996
-+++ work/init/main.c	Mon Jun 10 17:36:34 1996
-@@ -172,6 +172,8 @@
- #endif
- 
- 
-+void serial_console_setup(char *str,int *ints);
-+
- #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
- extern void ipc_init(void);
- #endif
-@@ -409,6 +411,7 @@
- #ifdef CONFIG_BAYCOM
- 	{ "baycom=", baycom_setup },
- #endif
-+	{ "scon=", serial_console_setup },
- 	{ 0, 0 }
- };
- 
---- ref/drivers/block/genhd.c	Sun May 19 12:29:22 1996
-+++ work/drivers/block/genhd.c	Thu Jul 18 21:05:07 1996
-@@ -52,6 +52,7 @@
- extern int blk_dev_init(void);
- extern int scsi_dev_init(void);
- extern int net_dev_init(void);
-+extern int atmdev_init(void); 
- 
- /*
-  * disk_name() is used by genhd.c and md.c.
-@@ -654,6 +655,9 @@
- #endif
- #ifdef CONFIG_INET
- 	net_dev_init();
-+#endif
-+#ifdef CONFIG_ATM
-+	(void) atmdev_init();
- #endif
- 	console_map_init();
- 
---- ref/drivers/net/net_init.c	Mon Mar 25 07:58:21 1996
-+++ work/drivers/net/net_init.c	Mon Jun 10 17:36:34 1996
-@@ -170,6 +170,7 @@
- 	dev->set_mac_address 	= eth_mac_addr;
- 	dev->header_cache_bind 	= eth_header_cache_bind;
- 	dev->header_cache_update= eth_header_cache_update;
-+	dev->ip_arp = ether_arp;
- 
- 	dev->type		= ARPHRD_ETHER;
- 	dev->hard_header_len 	= ETH_HLEN;
---- ref/drivers/net/eql.c	Mon May  6 11:26:08 1996
-+++ work/drivers/net/eql.c	Mon Jun 10 17:36:35 1996
-@@ -248,6 +248,7 @@
- 	dev->open		= eql_open;
- 	dev->stop		= eql_close;
- 	dev->do_ioctl		= eql_ioctl;
-+	dev->ip_arp		= ether_arp;
- 	dev->hard_start_xmit	= eql_slave_xmit;
- 	dev->get_stats		= eql_get_stats;
-   
---- ref/drivers/net/pi2.c	Fri Mar  1 06:50:45 1996
-+++ work/drivers/net/pi2.c	Mon Jun 10 17:36:37 1996
-@@ -1414,6 +1414,7 @@
-     dev->open = pi_open;
-     dev->stop = pi_close;
-     dev->do_ioctl = pi_ioctl;
-+    dev->ip_arp = ether_arp;
-     dev->hard_start_xmit = pi_send_packet;
-     dev->get_stats = pi_get_stats;
- 
---- ref/drivers/net/ppp.c	Wed May 29 06:32:38 1996
-+++ work/drivers/net/ppp.c	Mon Jun 10 17:36:38 1996
-@@ -399,6 +399,7 @@
- 	dev->stop	      = ppp_dev_close;
- 	dev->get_stats	      = ppp_dev_stats;
- 	dev->do_ioctl	      = ppp_dev_ioctl;
-+	dev->ip_arp	      = ether_arp;
- 	dev->addr_len	      = 0;
- 	dev->tx_queue_len     = 10;
- 	dev->type	      = ARPHRD_PPP;
---- ref/net/ipv4/arp.c	Wed Jun  5 13:42:27 1996
-+++ work/net/ipv4/arp.c	Mon Jun 10 17:36:38 1996
-@@ -2165,7 +2165,7 @@
-  *	Handle an ARP layer I/O control request.
-  */
- 
--int arp_ioctl(unsigned int cmd, void *arg)
-+static int do_arp_ioctl(unsigned int cmd, void *arg)
- {
- 	int err;
- 	struct arpreq r;
-@@ -2176,12 +2176,7 @@
- 	{
- 		case SIOCDARP:
- 		case SIOCSARP:
--			if (!suser())
--				return -EPERM;
- 		case SIOCGARP:
--			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
--			if (err)
--				return err;
- 			memcpy_fromfs(&r, arg, sizeof(struct arpreq));
- 			break;
- 		case OLD_SIOCDARP:
-@@ -2370,6 +2365,47 @@
- 
- 
- 
-+int ether_arp(struct device *dev,unsigned int cmd,void *arg)
-+{
-+	return do_arp_ioctl(cmd,arg);
-+}
-+
-+
-+int arp_ioctl(unsigned int cmd,void *arg)
-+{
-+	struct arpreq req;
-+	struct rtable *route;
-+	int error;
-+
-+	switch (cmd) {
-+		case SIOCSARP:
-+		case SIOCDARP:
-+			if (!suser()) return -EPERM;
-+			/* fall through */
-+		case SIOCGARP:
-+			error = verify_area(VERIFY_READ,arg,sizeof(req));
-+			if (error) return error;
-+			memcpy_fromfs(&req,arg,sizeof(req));
-+			if (req.arp_pa.sa_family != AF_INET)
-+				return -EPFNOSUPPORT;
-+			break;
-+		default:
-+			/* Here's the deal: unknown ioctls are passed, but
-+			   their request structure _must_ begin with a struct
-+			   sockaddr. */
-+			error = verify_area(VERIFY_READ,arg,
-+			    sizeof(struct sockaddr));
-+			if (error) return error;
-+			memcpy_fromfs(&req.arp_pa,arg,sizeof(struct sockaddr));
-+	}
-+	route = ip_rt_route(((struct sockaddr_in *) &req.arp_pa)->sin_addr.
-+	    s_addr,1);
-+	if (!route) return -EHOSTUNREACH;
-+	if (!route->rt_dev->ip_arp) return -EINVAL;
-+	return route->rt_dev->ip_arp(route->rt_dev,cmd,arg);
-+}
-+  
-+  
- /*
-  *	Called once on startup.
-  */
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/atmlec.h	Wed Jul 17 19:58:02 1996
-@@ -0,0 +1,58 @@
-+/*
-+ * 
-+ * ATM Lan Emulation Daemon vs. driver interface
-+ *
-+ * carnil@cs.tut.fi
-+ *
-+ */
-+
-+#ifndef _ATMLEC_H_
-+#define _ATMLEC_H_
-+
-+#include <linux/atmioc.h>
-+#include <linux/atm.h>
-+#include <linux/if_ether.h>
-+/* ATM lec daemon control socket */
-+#define ATMLEC_CTRL _IO('a',ATMIOC_LANE)
-+#define ATMLEC_DATA _IO('a',ATMIOC_LANE+1)
-+#define ATMLEC_MCAST _IO('a',ATMIOC_LANE+2)
-+
-+typedef enum { l_set_mac_addr, l_del_mac_addr, 
-+               l_flush_xmt, l_svc_setup, 
-+               l_add_permanent, l_addr_delete,
-+               l_topology_change, l_flush_complete,
-+               l_arp_update, l_reset, l_config,
-+               l_flush_tran_id, l_set_lecid,
-+               l_arp_xmt } atmlec_msg_type;
-+#define ATMLEC_MSG_TYPE_MAX l_arp_xmt
-+
-+struct atmlec_config_msg {
-+        unsigned int maximum_unknown_frame_count;
-+        unsigned long max_unknown_frame_time;
-+        unsigned short max_retry_count;
-+        unsigned long aging_time;
-+        unsigned long forward_delay_time;
-+        unsigned long arp_response_time;
-+        unsigned long flush_timeout;
-+        unsigned long path_switching_delay;
-+};
-+ 
-+struct atmlec_msg {
-+        atmlec_msg_type type;
-+        union {
-+                struct {
-+                        unsigned char mac_addr[ETH_ALEN];
-+                        unsigned char atm_addr[ATM_ESA_LEN];
-+                        unsigned long flag;/* Topology_change flag, 
-+                                              remoteflag, permanent flag,
-+                                              lecid, transaction id */
-+                } normal;
-+                struct atmlec_config_msg config;
-+        } content;
-+};
-+
-+struct atmlec_ioc {
-+        unsigned char atm_addr[ATM_ESA_LEN];
-+        unsigned char receive;    /* 1= receive vcc, 0 = send_vcc */
-+};
-+#endif /* _ATMLEC_H_ */
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/lec.c	Thu Jul 11 13:11:56 1996
-@@ -0,0 +1,588 @@
-+/*
-+ * lec.c: Lan Emulation driver 
-+ * Marko Kiiskila carnil@cs.tut.fi
-+ *
-+ */
-+
-+#include <linux/kernel.h>
-+
-+/* We are ethernet device */
-+#include <linux/if_ether.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <net/sock.h>
-+#include <linux/skbuff.h>
-+#include <asm/byteorder.h>
-+#include <net/arp.h>
-+#include <linux/proc_fs.h>
-+
-+/* And atm device */
-+#include <linux/atmdev.h>
-+#include <linux/atmlec.h>
-+
-+#include "lec.h"
-+#include "lec_arpc.h"
-+
-+#define DPRINTK(format,args...)
-+
-+struct atm_vcc *lecd = NULL;
-+
-+#define DUMP_PACKETS 0 /* 0 = None,
-+                       * 1 = 30 first bytes
-+                       * 2 = Whole packet
-+                       */
-+
-+static int lec_open(struct device *dev);
-+static int lec_send_packet(struct sk_buff *skb, struct device *dev);
-+static int lec_close(struct device *dev);
-+static struct enet_statistics *lec_get_stats(struct device *dev);
-+static int lec_init(struct device *dev);
-+
-+static char myname[] = "lec0";
-+
-+extern struct lec_arp_table *lec_arp_empty_ones;
-+
-+/* Have we register_netdev() yet? */
-+unsigned char registered = 0;
-+
-+static struct device dev_lec = {
-+        myname,
-+        0, 0, /* rmem_end, rmem_start */
-+        0, 0, /* mem_end, mem_start */
-+        0, 0, /* base_addr, irq */
-+        0, 0, /* start, interrupt */
-+        0, NULL, /*  tbusy, *next */
-+        lec_init /* Initialization func */
-+}; 
-+
-+struct lec_priv {
-+        struct enet_statistics stats;
-+        unsigned short lecid;      /* Lecid of this client */
-+};
-+
-+/*
-+ * Open/initialize the netdevice. This is called (in the current kernel)
-+ * sometime after booting when the 'ifconfig' program is run.
-+ *
-+ * This routine should set everything up anew at each open, even
-+ * registers that "should" only need to be set once at boot, so that
-+ * there is non-reboot way to recover if something goes wrong.
-+ */
-+
-+static int 
-+lec_open(struct device *dev)
-+{
-+        struct lec_priv *priv = (struct lec_priv *)dev->priv;
-+        
-+        dev->tbusy = 0;
-+        dev->start = 1;
-+        dev->interrupt = 1;
-+        if (priv)
-+                memset(&priv->stats,0,sizeof(struct enet_statistics));
-+        
-+        return 0;
-+}
-+
-+static int 
-+lec_send_packet(struct sk_buff *skb, struct device *dev)
-+{
-+        struct lec_priv *priv = (struct lec_priv *)dev->priv;
-+        struct lecdatahdr_8023 *lec_h;
-+        struct atm_vcc *send_vcc;
-+#if DUMP_PACKETS > 0
-+        char buf[300];
-+        int i=0;
-+#endif /* DUMP_PACKETS >0 */
-+        
-+        DPRINTK("Lec_send_packet called\n");  
-+        if (!lecd) {
-+                printk("LEC:No lecd attached\n");
-+                if (priv)
-+                        priv->stats.tx_errors++;
-+                return -EUNATCH;
-+        } 
-+        if (dev->tbusy) {
-+                /*
-+                 * If we get here, some higher level has decided we are broken.
-+                 * There should really be a "kick me" function call instead.
-+                 */
-+                printk(KERN_WARNING "%s: transmit timed out.\n", dev->name);
-+                dev->tbusy=0;
-+        }
-+        /*
-+         * If some higher layer thinks we've missed an tx-done interrupt
-+         * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
-+         * itself.
-+         */
-+        if (skb == NULL) {
-+                dev_tint(dev);
-+                return 0;
-+        }
-+        /*
-+         * Block a timer-based transmit from overlapping. This could better be
-+         * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
-+         */
-+
-+        if (set_bit(0, (void*)&dev->tbusy) != 0) {
-+                printk(KERN_WARNING "%s: Transmitter access conflict.\n", 
-+                       dev->name);
-+        } else {
-+                DPRINTK("skbuff head:%lx data:%lx tail:%lx end:%lx\n",
-+                        (long)skb->head, (long)skb->data, (long)skb->tail,
-+                        (long)skb->end);
-+                
-+                /* Put le header to place */
-+                lec_h = (struct lecdatahdr_8023*)skb->data;
-+                lec_h->le_header = htons(priv->lecid); 
-+                
-+#if DUMP_PACKETS > 0
-+                printk("LEC: send datalen:%ld lecid:%4.4x\n", 
-+                        skb->len, priv->lecid);
-+#if DUMP_PACKETS >= 2
-+                for(i=0;i<skb->len && i <99;i++) {
-+                        sprintf(buf+i*3,"%2.2x ",0xff&skb->data[i]);
-+                }
-+#elif DUMP_PACKETS >= 1
-+                for(i=0;i<skb->len && i < 30;i++) {
-+                        sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
-+                }
-+#endif /* DUMP_PACKETS >= 1 */
-+                if (i==skb->len)
-+                        printk("%s\n",buf);
-+                else
-+                        printk("%s...\n",buf);
-+#endif /* DUMP_PACKETS > 0 */
-+                /* Send to right vcc */
-+                send_vcc = lec_arp_resolve(lec_h->h_dest);
-+                if (!send_vcc || !(send_vcc->flags & ATM_VF_READY)) {    
-+                        if (priv)
-+                                priv->stats.tx_errors++;
-+                        DPRINTK("LEC:lec_send_packet: dropping ..\n");
-+                        dev_kfree_skb(skb, FREE_WRITE);
-+                        return 0;
-+                } else {
-+#if DUMP_PACKETS > 0                    
-+                        printk("LEC:sending to vpi:%d vci:%d\n",
-+                                send_vcc->vpi, send_vcc->vci);
-+#endif /* DUMP_PACKETS > 0 */
-+                        /* Minimum ethernet-frame size */
-+                        if (skb->len <62)
-+                                skb->len = 62;
-+                        skb->atm.iovcnt = 0;
-+                        send_vcc->dev->ops->send(send_vcc, skb);
-+                        if (priv)
-+                                priv->stats.tx_packets++;
-+                }
-+        }
-+        /* Should we wait for card's device driver to notify us? */
-+        dev->tbusy=0;
-+        
-+        return 0;
-+}
-+
-+/* The inverse routine to net_open(). */
-+static int 
-+lec_close(struct device *dev) 
-+{
-+        dev->tbusy = 1;
-+        dev->start = 0;
-+        return 0;
-+}
-+
-+/*
-+ * Get the current statistics.
-+ * This may be called with the card open or closed.
-+ */
-+static struct enet_statistics *
-+lec_get_stats(struct device *dev)
-+{
-+        struct lec_priv *priv = (struct lec_priv *)dev->priv;
-+        
-+        return (struct enet_statistics *)&priv->stats;
-+}
-+
-+int 
-+lec_hard_header(struct sk_buff *skb, struct device *dev, 
-+               unsigned short type, void *daddr, void *saddr, 
-+               unsigned len)
-+{
-+        struct lec_priv *priv = (struct lec_priv *)dev->priv;
-+        struct lecdatahdr_8023 *hdr = 
-+                (struct lecdatahdr_8023 *)skb_push(skb, LEC_HEADER_LEN); 
-+                
-+        /* Set lecid header */
-+        if (priv)
-+                hdr->le_header = htons(priv->lecid);
-+        else
-+                hdr->le_header = 0;
-+        
-+        /* Set the protocol type. */
-+        if(type!=ETH_P_802_3)
-+                hdr->h_type = htons(type);
-+        else
-+                hdr->h_type = htons(len);
-+        
-+        /* Source hw address */
-+        if (saddr)
-+                memcpy(hdr->h_source, saddr, dev->addr_len);
-+        else
-+                memcpy(hdr->h_source, dev->dev_addr, dev->addr_len);
-+        
-+        /* Destination addr */
-+        if (daddr) {
-+                memcpy(hdr->h_dest, daddr, dev->addr_len);
-+                hdr->le_header = 0;
-+                return dev->hard_header_len;
-+        }
-+        return -dev->hard_header_len;
-+}
-+
-+int 
-+lec_rebuild_header(void *buff, struct device *dev, unsigned long dst,
-+                  struct sk_buff *skb)
-+{
-+        struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023*)buff;
-+        
-+        /*
-+         *      Only ARP/IP is currently supported
-+         */
-+        if (hdr->h_type !=  htons(ETH_P_IP)) {
-+                printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",
-+                       dev->name,(int)hdr->h_type);
-+                memcpy(hdr->h_source, dev->dev_addr, dev->addr_len);
-+                return 0;
-+        }
-+        /*
-+         *      Try and get ARP to resolve the header.
-+         */
-+#ifdef CONFIG_INET       
-+        return arp_find(hdr->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
-+#else
-+        return 0;
-+#endif
-+}
-+
-+/*
-+ * Upper level calls this function to bind hardware header cache entry.
-+ * If the call is successful, then corresponding Address Resolution Protocol
-+ * (maybe, not ARP) takes responsibility for updating cache content.
-+ */
-+void 
-+lec_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
-+                     unsigned short htype, __u32 daddr)
-+{
-+        struct hh_cache *hh;
-+        
-+        if (htype != ETH_P_IP) {
-+                printk("eth_header_cache_bind: %04x cache is not implemented\n",
-+                       htype);
-+                return;
-+        }
-+        if (arp_bind_cache(hhp, dev, htype, daddr))
-+                return;
-+        if ((hh=*hhp) != NULL) {
-+                memcpy(hh->hh_data+8, dev->dev_addr, ETH_ALEN);
-+                hh->hh_data[14] = htype>>8;
-+                hh->hh_data[15] = htype&0xFF;
-+        }
-+}
-+
-+/*
-+ * Called by Address Resolution module to notify changes in address.
-+ */
-+void 
-+lec_header_cache_update(struct hh_cache *hh, struct device *dev, 
-+                       unsigned char *haddr)
-+{
-+        if (hh->hh_type != ETH_P_IP) {
-+                printk("lec_header_cache_update: %04x cache is not implemented\n", 
-+                       hh->hh_type);
-+                return;
-+        }
-+        memcpy(hh->hh_data+2, haddr, ETH_ALEN);
-+        hh->hh_uptodate = 1;
-+}
-+
-+static int 
-+lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
-+{
-+        struct lec_priv *priv = (struct lec_priv *)dev_lec.priv;
-+        struct atmlec_msg *mesg;
-+        int i;
-+        
-+        mesg = (struct atmlec_msg *)skb->data;
-+        switch(mesg->type) {
-+        case l_set_mac_addr:
-+                for (i=0;i<6;i++) {
-+                        dev_lec.dev_addr[i] = mesg->content.normal.mac_addr[i];
-+                }    
-+                break;
-+        case l_del_mac_addr:
-+                for(i=0;i<6;i++) {
-+                        dev_lec.dev_addr[i] = 0;
-+                }
-+                break;
-+        case l_add_permanent:
-+                lec_add_permanent(mesg->content.normal.mac_addr,
-+                                  mesg->content.normal.atm_addr);
-+                break;
-+        case l_addr_delete:
-+                lec_addr_delete(mesg->content.normal.atm_addr, 
-+                                mesg->content.normal.flag);
-+                break;
-+        case l_topology_change:
-+                lec_topology_flag_change_set(mesg->content.normal.flag);
-+                break;
-+        case l_flush_complete:
-+                lec_flush_complete(mesg->content.normal.atm_addr, 
-+                                   mesg->content.normal.flag);
-+                break;
-+        case l_arp_update:
-+                lec_arp_update(mesg->content.normal.mac_addr,
-+                               mesg->content.normal.atm_addr,
-+                               mesg->content.normal.flag);
-+                break;
-+        case l_reset:
-+                lec_reset();
-+                break;
-+        case l_config:
-+                lec_config(&mesg->content.config);
-+                break;
-+        case l_flush_tran_id:
-+                lec_set_flush_tran_id(mesg->content.normal.mac_addr,
-+                                      mesg->content.normal.flag);
-+                break;
-+        case l_set_lecid:
-+                if (priv)
-+                        priv->lecid = (unsigned short)(0xffff&mesg->content.normal.flag);
-+                break;
-+        default:
-+                printk("LEC: Unknown message type %d\n",mesg->type);
-+                dev_kfree_skb(skb, FREE_WRITE);
-+                return -EINVAL;
-+        }
-+        dev_kfree_skb(skb, FREE_WRITE);
-+        return 0;
-+}
-+
-+static void 
-+lec_atm_close(struct atm_vcc *vcc)
-+{
-+        struct sk_buff *skb;
-+
-+        lecd = NULL;
-+        /* Do something needful? */
-+
-+        dev_lec.tbusy = 1;
-+        dev_lec.start = 0;
-+
-+        lec_arp_destroy();
-+
-+#if 0
-+        unregister_netdev(&dev_lec);
-+        /* Should we do this? */
-+        if (priv) {
-+                kfree_s(priv, sizeof(struct lec_priv));
-+                dev_lec.priv = NULL;
-+        }
-+#endif
-+        if (skb_peek(&vcc->recvq))
-+		printk("LEC lec_atm_close: closing with messages pending\n");
-+        while ((skb = skb_dequeue(&vcc->recvq))) 
-+		dev_kfree_skb(skb,FREE_READ);
-+  
-+	printk("LEC: Shut down!\n");
-+}
-+
-+static struct atmdev_ops lecdev_ops = {
-+        NULL, /*open*/
-+        lec_atm_close, /*close*/
-+        NULL, /*ioctl*/
-+        NULL, /*getsockopt */
-+        NULL, /*setsockopt */
-+        lec_atm_send, /*send */
-+        NULL, /*sg_send */
-+        NULL, /*poll */
-+        NULL, /*phy_put*/
-+        NULL, /*phy_get*/
-+        NULL  /*feedback*/
-+};
-+
-+static struct atm_dev lecatm_dev = {
-+        &lecdev_ops,
-+        NULL,	    /*PHY*/
-+        "lec",	    /*type*/
-+        999,	    /*dummy device number*/
-+        NULL,NULL,  /*no VCCs*/
-+        NULL,NULL,  /*no data*/
-+        0,	    /*no flags*/
-+        NULL,	    /* no local address*/
-+        { 0 }	    /*no ESI, no statistics */
-+};
-+
-+int 
-+send_to_lecd(atmlec_msg_type type, unsigned char *mac_addr, 
-+	    unsigned char *atm_addr)
-+{
-+	struct sk_buff *skb;
-+	struct atmlec_msg *mesg;
-+  
-+	if (!lecd) {
-+		return -1;
-+	}
-+	skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
-+	if (!skb)
-+		return -1;
-+	skb->free = 1;
-+	skb->len = sizeof(struct atmlec_msg);
-+	mesg = (struct atmlec_msg *)skb->data;
-+	mesg->type = type;
-+	if (mac_addr)
-+		memcpy(&mesg->content.normal.mac_addr, mac_addr, ETH_ALEN);
-+	if (atm_addr)
-+		memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
-+
-+	skb_queue_tail(&lecd->recvq, skb);
-+        wake_up(&lecd->sleep);
-+        return 0;
-+}
-+
-+static int 
-+lec_init(struct device *dev)
-+{
-+        dev->priv = kmalloc(sizeof(struct lec_priv), GFP_KERNEL);
-+        if (dev->priv == NULL)
-+                return -ENOMEM;
-+
-+        memset(dev->priv,0,sizeof(struct lec_priv));
-+
-+        ether_setup(dev);
-+        dev->open = lec_open;
-+        dev->stop = lec_close;
-+        dev->hard_start_xmit = lec_send_packet;
-+        dev->hard_header_len = LEC_HEADER_LEN;
-+
-+        dev->hard_header = lec_hard_header;
-+        dev->rebuild_header = lec_rebuild_header;
-+        dev->header_cache_bind  = lec_header_cache_bind;
-+        dev->header_cache_update= lec_header_cache_update;
-+
-+        dev->get_stats = lec_get_stats;
-+        dev->set_multicast_list = NULL;
-+        dev->do_ioctl  = NULL;
-+        printk("LEC: Initialized!\n");
-+        return 0;
-+}
-+
-+static unsigned char lec_ctrl_magic[] = {
-+        0xff,
-+        0x00,
-+        0x01,
-+        0x01 };
-+
-+void 
-+lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
-+{
-+        struct lec_priv *priv = (struct lec_priv *)dev_lec.priv;
-+        struct lecdatahdr_8023 *hdr;
-+#if DUMP_PACKETS >0
-+        int i=0;
-+        char buf[300];
-+
-+        printk("LEC: lec_push vcc vpi:%d vci:%d\n",vcc->vpi, vcc->vci);
-+#endif
-+        if (!skb) {
-+                DPRINTK("LEC: null skb\n");
-+                lec_vcc_close(vcc);
-+                return;
-+        }
-+#if DUMP_PACKETS > 0
-+        printk("LEC: rcv datalen:%ld lecid:%4.4x\n", skb->len, priv->lecid);
-+#if DUMP_PACKETS >= 2
-+        for(i=0;i<skb->len && i <99;i++) {
-+                sprintf(buf+i*3,"%2.2x ",0xff&skb->data[i]);
-+        }
-+#elif DUMP_PACKETS >= 1
-+        for(i=0;i<skb->len && i < 30;i++) {
-+                sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
-+        }
-+#endif /* DUMP_PACKETS >= 1 */
-+        if (i==skb->len)
-+                printk("%s\n",buf);
-+        else
-+                printk("%s...\n",buf);
-+#endif /* DUMP_PACKETS > 0 */
-+        if (memcmp(skb->data, lec_ctrl_magic, 4) ==0) { /* Control frame, to daemon*/
-+                DPRINTK("LEC: To daemon\n");
-+                skb_queue_tail(&vcc->recvq, skb);
-+                wake_up(&vcc->sleep);
-+        } else { /* Data frame, queue to protocol handlers */
-+                hdr = (struct lecdatahdr_8023 *)skb->data;
-+                if (hdr->le_header == htons(priv->lecid) ||
-+                    !lecd) { /* Probably looping back, or if lecd is missing,
-+				lecd has gone down */
-+                        DPRINTK("Ignoring loopback frame...\n");
-+                        dev_kfree_skb(skb, FREE_READ);
-+                        return;
-+                }
-+                if (lec_arp_empty_ones) { /* FILTER DATA!!!! */
-+                        lec_arp_check_empties(vcc, skb);
-+                }
-+                skb->dev = &dev_lec;
-+                skb->mac.raw = (unsigned char *)skb->data;
-+                skb->mac.ethernet = (struct ethhdr *)skb_pull(skb, 2);     
-+                if (*hdr->h_dest&1) {
-+                        if (memcmp(hdr->h_dest,dev_lec.broadcast, ETH_ALEN)==0)
-+                                skb->pkt_type=PACKET_BROADCAST;
-+                        else
-+                                skb->pkt_type=PACKET_MULTICAST;
-+                } else if (dev_lec.flags&(IFF_PROMISC|IFF_ALLMULTI)) {
-+                        if (memcmp(hdr->h_dest,dev_lec.dev_addr, ETH_ALEN))
-+                                skb->pkt_type=PACKET_OTHERHOST;
-+                } 
-+                if (ntohs(hdr->h_type)>=1536)
-+                        skb->protocol = hdr->h_type;
-+                else 
-+                        skb->protocol = htons(ETH_P_802_2);
-+                skb->h.raw = skb_pull(skb, ETH_HLEN);
-+                netif_rx(skb);
-+                if (priv)
-+                        priv->stats.rx_packets++;
-+        }
-+}
-+
-+int 
-+lec_vcc_attach(struct atm_vcc *vcc, void *arg)
-+{
-+        struct atmlec_ioc ioc_data;
-+
-+        /* Lecd must be up in this case */
-+        memcpy_fromfs(&ioc_data, arg, sizeof(struct atmlec_ioc));
-+        lec_vcc_added(&ioc_data, vcc, vcc->push);
-+        vcc->push = lec_push;
-+        return 0;
-+}
-+
-+/* Initialize device. */
-+int 
-+lecd_attach(struct atm_vcc *vcc)
-+{  
-+        int result;
-+
-+        if (lecd) {
-+                printk("LEC: Lecd already contacted\n");
-+                return -EADDRINUSE;
-+        }
-+	
-+        lec_arp_init();
-+	if (!registered) {
-+		registered = 1;
-+		if ((result =register_netdev(&dev_lec)) != 0)    
-+			return result;
-+	}
-+        lecd = vcc;
-+        vcc->dev = &lecatm_dev;
-+        vcc->flags |= ATM_VF_READY;
-+
-+        return 0;
-+}
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/lec.h	Fri Jul 19 15:10:11 1996
-@@ -0,0 +1,53 @@
-+/*
-+ *
-+ * Lan Emulation client header file
-+ *
-+ * Marko Kiiskila carnil@cs.tut.fi
-+ *
-+ */
-+
-+#ifndef _LEC_H_
-+#define _LEC_H_
-+
-+#include <linux/atmdev.h>
-+#include <linux/if_ether.h>
-+
-+#define LEC_HEADER_LEN 16
-+
-+struct lecdatahdr_8023 {
-+  unsigned short le_header;
-+  unsigned char h_dest[ETH_ALEN];
-+  unsigned char h_source[ETH_ALEN];
-+  unsigned short h_type;
-+};
-+
-+struct lecdatahdr_8025 {
-+  unsigned short le_header;
-+  unsigned char ac_pad;
-+  unsigned char fc;
-+  unsigned char h_dst[ETH_ALEN];
-+  unsigned char h_source[ETH_ALEN];
-+};
-+
-+/*
-+ * ATM LAN Emulation supports both LLC & Dix Ethernet EtherType
-+ * frames. 
-+ * 1. Dix Ethernet EtherType frames encoded by placing EtherType
-+ *    field in h_type field. Data follows immediatelly after header.
-+ * 2. LLC Data frames whose total length, including LLC field and data,
-+ *    but not padding required to meet the minimum data frame length, 
-+ *    is less than 1536(0x0600) MUST be encoded by placing that length
-+ *    in the the h_type field. The LLC field follows header immediatelly.
-+ * 3. LLC data frames longer than this maximum MUST be encoded by placing
-+ *    the value 0 in the h_type field.
-+ *
-+ */
-+
-+int lecd_attach(struct atm_vcc *vcc);
-+int lec_vcc_attach(struct atm_vcc *vcc, void *arg);
-+int make_lec(struct atm_vcc *vcc);
-+int send_to_lecd(atmlec_msg_type type, unsigned char *mac_addr,
-+                unsigned char *atm_addr);
-+void lec_push(struct atm_vcc *vcc, struct sk_buff *skb);
-+#endif _LEC_H_
-+
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/lec_arpc.c	Thu Jul 11 13:11:57 1996
-@@ -0,0 +1,1090 @@
-+#include <linux/types.h>
-+#include <linux/sched.h>
-+#include <linux/timer.h>
-+#include <asm/param.h>
-+#include <asm/atomic.h>
-+#include <net/route.h>
-+
-+#include <linux/atmlec.h>
-+
-+#include "lec.h"
-+#include "lec_arpc.h"
-+
-+#define DPRINTK(format,args...)
-+#define DEBUG_ARP_TABLE 0
-+
-+struct lec_arp_table *lec_arp_tables[LEC_ARP_TABLE_SIZE] =
-+{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,  /* At least this gets */
-+  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; /* initialized */
-+
-+/* Used for storing VCC's that don't have a MAC address attached yet */
-+struct lec_arp_table *lec_arp_empty_ones = NULL;
-+
-+/* Used for storing VCC's (and forward packets from) which are to
-+   age out by not using them to forward packets. 
-+   This is because to some LE clients there will be 2 VCCs. Only
-+   one of them gets used. */
-+struct lec_arp_table *lec_no_forward = NULL;
-+
-+static atomic_t lec_arp_lock_var = 0;
-+
-+struct atm_vcc *mcast_vcc = NULL;
-+
-+#define LEC_ARP_REFRESH_INTERVAL (3*HZ)
-+
-+static void lec_arp_check_expire(unsigned long dumle);
-+static __inline__ void lec_arp_expire_arp(unsigned long dumb);
-+static void lec_clear_all(void);
-+void dump_arp_table(void);
-+
-+static struct timer_list lec_arp_timer = 
-+{ NULL,NULL, LEC_ARP_REFRESH_INTERVAL, 0L, &lec_arp_check_expire };
-+static unsigned char lec_freeze;
-+
-+/* C10 */
-+static unsigned int lec_maximum_unknown_frame_count = 1;
-+/* Within the period of time defined by this variable, the client will send 
-+   no more than C10 frames to BUS for a given unicast destination. (C11) */
-+static unsigned long lec_max_unknown_frame_time = (1*HZ);
-+/* If no traffic has been sent in this vcc for this period of time,
-+   vcc will be torn down (C12)*/
-+static unsigned long lec_vcc_timeout_period = (1200*HZ);
-+/* An LE Client MUST not retry an LE_ARP_REQUEST for a 
-+   given frame's LAN Destination more than maximum retry count times,
-+   after the first LEC_ARP_REQUEST (C13)*/
-+static unsigned short lec_max_retry_count = 1;
-+/* Max time the client will maintain an entry in its arp cache in
-+   absence of a verification of that relationship (C17)*/
-+static unsigned long lec_aging_time = (300*HZ);
-+/* Max time the client will maintain an entry in cache when
-+   topology change flag is true (C18) */
-+static unsigned long lec_forward_delay_time = (15*HZ);
-+/* Topology change flag  (C19)*/
-+static int lec_topology_change = 0;
-+/* Max time the client expects an LE_ARP_REQUEST/LE_ARP_RESPONSE
-+   cycle to take (C20)*/
-+static unsigned long lec_arp_response_time = (1*HZ);
-+/* Time limit ot wait to receive an LE_FLUSH_RESPONSE after the
-+   LE_FLUSH_REQUEST has been sent before taking recover action. (C21)*/
-+static unsigned long lec_flush_timeout = (4*HZ);
-+/* The time since sending a frame to the bus after which the
-+   LE Client may assume that the frame has been either discarded or
-+   delivered to the recipient (C22) */
-+static unsigned long lec_path_switching_delay = (6*HZ);
-+
-+/* 
-+ * Arp table funcs
-+ */
-+
-+#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE -1))
-+
-+static __inline__ void 
-+lec_arp_lock(void)
-+{
-+        atomic_inc(&lec_arp_lock_var);
-+}
-+
-+static __inline__ void 
-+lec_arp_unlock(void)
-+{
-+        atomic_dec(&lec_arp_lock_var);
-+}
-+
-+/*
-+ * Initialization of arp-cache
-+ */
-+void 
-+lec_arp_init(void)
-+{
-+        unsigned short i;
-+
-+        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                lec_arp_tables[i] = NULL;
-+        }
-+        lec_arp_timer.expires = jiffies+LEC_ARP_REFRESH_INTERVAL;
-+        lec_freeze = 0;
-+        add_timer(&lec_arp_timer);
-+}
-+
-+void
-+lec_arp_destroy(void)
-+{
-+        del_timer(&lec_arp_timer);  
-+        lec_freeze = 1;
-+        lec_clear_all();
-+}
-+
-+void
-+lec_arp_clear_vccs(struct lec_arp_table *entry)
-+{
-+        if (entry->vcc) {
-+                entry->vcc->push = entry->old_push;
-+                entry->vcc->flags |= ATM_VF_RELEASED;
-+                entry->vcc->flags &= ~ATM_VF_READY;
-+                entry->vcc = NULL;
-+        }
-+        if (entry->recv_vcc) {
-+                entry->recv_vcc->push = entry->old_recv_push;
-+                entry->recv_vcc->flags |= ATM_VF_RELEASED;
-+                entry->recv_vcc->flags &= ~ATM_VF_READY;
-+                entry->recv_vcc = NULL;
-+        }        
-+}
-+
-+/*
-+ * Insert entry to lec_arp_table
-+ */
-+static __inline__ void 
-+lec_arp_put(struct lec_arp_table *to_put)
-+{
-+        unsigned short place;
-+        unsigned long flags;
-+
-+        save_flags(flags);
-+        cli();
-+
-+        place = HASH(to_put->mac_addr[ETH_ALEN-1]);
-+        to_put->next = lec_arp_tables[place];
-+        lec_arp_tables[place] = to_put;
-+  
-+        restore_flags(flags);
-+        DPRINTK("LEC_ARP: Added entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
-+                0xff&to_put->mac_addr[0], 0xff&to_put->mac_addr[1],
-+                0xff&to_put->mac_addr[2], 0xff&to_put->mac_addr[3],
-+                0xff&to_put->mac_addr[4], 0xff&to_put->mac_addr[5]);
-+        dump_arp_table();
-+}
-+
-+/*
-+ * Remove entry from lec_arp_table
-+ */
-+static __inline__ int 
-+lec_arp_remove(struct lec_arp_table *to_remove)
-+{
-+        unsigned short place;
-+        struct lec_arp_table *tmp;
-+        unsigned long flags;
-+        int remove_vcc=1;
-+
-+        save_flags(flags);
-+        cli();
-+
-+        if (!to_remove) {
-+                restore_flags(flags);
-+                return -1;
-+        }
-+        place = HASH(to_remove->mac_addr[ETH_ALEN-1]);
-+        tmp = lec_arp_tables[place];
-+        if (tmp == to_remove) {
-+                lec_arp_tables[place] = tmp->next;
-+        } else {
-+                while(tmp && tmp->next != to_remove) {
-+                        tmp = tmp->next;
-+                }
-+                if (!tmp) {/* Entry was not found */
-+                        restore_flags(flags);
-+                        return -1;
-+                }
-+        }
-+        tmp->next = to_remove->next;
-+        del_timer(&to_remove->timer);
-+  
-+        /* If this is the only MAC connected to this VCC, also tear down
-+           the VCC */
-+        if (to_remove->status >= ESI_FLUSH_PENDING) {
-+                /*
-+                 * ESI_FLUSH_PENDING, ESI_FORWARD_DIRECT
-+                 */
-+                for(place=0;place<LEC_ARP_TABLE_SIZE;place++) {
-+                        for(tmp=lec_arp_tables[place];tmp!=NULL;tmp=tmp->next){
-+                                if (memcmp(tmp->atm_addr, to_remove->atm_addr,
-+                                           ATM_ESA_LEN)==0) {
-+                                        remove_vcc=0;
-+                                        break;
-+                                }
-+                        }
-+                }
-+                if (remove_vcc)
-+                        lec_arp_clear_vccs(to_remove);
-+        }
-+        restore_flags(flags);
-+
-+        DPRINTK("LEC_ARP: Removed entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
-+                0xff&to_remove->mac_addr[0], 0xff&to_remove->mac_addr[1],
-+                0xff&to_remove->mac_addr[2], 0xff&to_remove->mac_addr[3],
-+                0xff&to_remove->mac_addr[4], 0xff&to_remove->mac_addr[5]);
-+
-+        dump_arp_table();
-+        return 0;
-+}
-+
-+#if DEBUG_ARP_TABLE
-+static char*
-+get_status_string(unsigned char st)
-+{
-+        switch(st) {
-+        case ESI_UNKNOWN:
-+                return "ESI_UNKNOWN";
-+        case ESI_ARP_PENDING:
-+                return "ESI_ARP_PENDING";
-+        case ESI_VC_PENDING:
-+                return "ESI_VC_PENDING";
-+        case ESI_FLUSH_PENDING:
-+                return "ESI_FLUSH_PENDING";
-+        case ESI_FORWARD_DIRECT:
-+                return "ESI_FORWARD_DIRECT";
-+        default:
-+                return "<UNKNOWN>";
-+        }
-+}
-+#endif
-+
-+void
-+dump_arp_table(void)
-+{
-+#if DEBUG_ARP_TABLE
-+        int i,j, offset;
-+        struct lec_arp_table *rulla;
-+        char buf[512];
-+
-+        printk("Dump:\n");
-+        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                rulla = lec_arp_tables[i];
-+                offset = 0;
-+                offset += sprintf(buf,"%d: %p\n",i, rulla);
-+                while (rulla) {
-+                        offset += sprintf(buf+offset,"Mac:");
-+                        for(j=0;j<ETH_ALEN;j++) {
-+                                offset+=sprintf(buf+offset,
-+                                                "%2.2x ",
-+                                                rulla->mac_addr[j]&0xff);
-+                        }
-+                        offset +=sprintf(buf+offset,"Atm:");
-+                        for(j=0;j<ATM_ESA_LEN;j++) {
-+                                offset+=sprintf(buf+offset,
-+                                                "%2.2x ",
-+                                                rulla->atm_addr[j]&0xff);
-+                        }      
-+                        offset+=sprintf(buf+offset,
-+                                        "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
-+                                        rulla->vcc?rulla->vcc->vpi:0, 
-+                                        rulla->vcc?rulla->vcc->vci:0,
-+                                        rulla->recv_vcc?rulla->recv_vcc->vpi:0,
-+                                        rulla->recv_vcc?rulla->recv_vcc->vci:0,
-+                                        rulla->last_used,
-+                                        rulla->timestamp, rulla->no_tries);
-+                        offset+=sprintf(buf+offset,
-+                                        "Flags:%x, Packets_flooded:%x, Status: %s ",
-+                                        rulla->flags, rulla->packets_flooded, 
-+                                        get_status_string(rulla->status));
-+                        offset+=sprintf(buf+offset,"->%p\n",rulla->next);
-+                        rulla = rulla->next;
-+                }
-+                printk("%s",buf);
-+        }
-+        rulla = lec_arp_empty_ones;
-+        if (rulla)
-+                printk("Empty ones\n");  
-+        while(rulla) {
-+                offset=0;
-+                offset += sprintf(buf+offset,"Mac:");
-+                for(j=0;j<ETH_ALEN;j++) {
-+                        offset+=sprintf(buf+offset,"%2.2x ",
-+                                        rulla->mac_addr[j]&0xff);
-+                }
-+                offset +=sprintf(buf+offset,"Atm:");
-+                for(j=0;j<ATM_ESA_LEN;j++) {
-+                        offset+=sprintf(buf+offset,"%2.2x ",
-+                                        rulla->atm_addr[j]&0xff);
-+                }      
-+                offset+=sprintf(buf+offset,
-+                                "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
-+                                rulla->vcc?rulla->vcc->vpi:0, 
-+                                rulla->vcc?rulla->vcc->vci:0, 
-+                                rulla->recv_vcc?rulla->recv_vcc->vpi:0,
-+                                rulla->recv_vcc?rulla->recv_vcc->vci:0,
-+                                rulla->last_used, 
-+                                rulla->timestamp, rulla->no_tries);
-+                offset+=sprintf(buf+offset,
-+                                "Flags:%x, Packets_flooded:%x, Status: %s ",
-+                                rulla->flags, rulla->packets_flooded, 
-+                                get_status_string(rulla->status));
-+                offset+=sprintf(buf+offset,"->%lx\n",(long)rulla->next);
-+                rulla = rulla->next;
-+                printk("%s",buf);
-+        }
-+#endif
-+}
-+
-+
-+/* 
-+ * Find entry by mac_address
-+ */
-+static __inline__ struct lec_arp_table*
-+lec_arp_find(unsigned char *mac_addr)
-+{
-+        unsigned short place;
-+        struct lec_arp_table *to_return;
-+
-+        DPRINTK("LEC_ARP: lec_arp_find :%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
-+                mac_addr[0]&0xff, mac_addr[1]&0xff, mac_addr[2]&0xff, 
-+                mac_addr[3]&0xff, mac_addr[4]&0xff, mac_addr[5]&0xff);
-+        lec_arp_lock();
-+        place = HASH(mac_addr[ETH_ALEN-1]);
-+  
-+        to_return = lec_arp_tables[place];
-+        while(to_return) {
-+                if (memcmp(mac_addr, to_return->mac_addr, ETH_ALEN) == 0) {
-+                        to_return->last_used = jiffies;
-+                        lec_arp_unlock();
-+                        return to_return;
-+                }
-+                to_return = to_return->next;
-+        }
-+        lec_arp_unlock();
-+        return NULL;
-+}
-+
-+static struct lec_arp_table*
-+make_entry(unsigned char *mac_addr)
-+{
-+        struct lec_arp_table *to_return;
-+
-+        to_return=(struct lec_arp_table *)kmalloc(sizeof(struct lec_arp_table),
-+                                                  GFP_ATOMIC);
-+        if (!to_return) {
-+                printk("LEC: Arp entry kmalloc failed\n");
-+                return NULL;
-+        }
-+        memset(to_return,0,sizeof(struct lec_arp_table));
-+        memcpy(to_return->mac_addr, mac_addr, ETH_ALEN);
-+        to_return->timer.function = lec_arp_expire_arp;
-+        to_return->timer.data = (unsigned long)to_return;
-+        return to_return;
-+}
-+
-+/*
-+ *
-+ * Arp sent timer expired
-+ *
-+ */
-+static void
-+lec_arp_expire_arp(unsigned long data)
-+{
-+        struct lec_arp_table *entry;
-+
-+        entry = (struct lec_arp_table *)data;
-+        del_timer(&entry->timer);
-+
-+        if (entry->status == ESI_ARP_PENDING) {
-+                send_to_lecd(l_arp_xmt, entry->mac_addr, NULL);
-+                entry->no_tries++;
-+                if(entry->no_tries <= lec_max_retry_count) {
-+                        entry->timer.expires = jiffies + (1*HZ);
-+                        add_timer(&entry->timer);
-+                } else 
-+                        /* For safety */
-+                        entry->timer.next = entry->timer.prev = NULL;
-+        }
-+}
-+
-+/*
-+ *
-+ * Unknown/unused vcc expire, remove associated entry
-+ *
-+ */
-+static void
-+lec_arp_expire_vcc(unsigned long data)
-+{
-+        struct lec_arp_table *to_remove = (struct lec_arp_table*)data;
-+        struct lec_arp_table *entry = NULL;
-+
-+        del_timer(&to_remove->timer);
-+
-+        DPRINTK("LEC_ARP: lec_arp_expire_vcc vpi:%d vci:%d\n",
-+                to_remove->vcc?to_remove->vcc->vpi:0,
-+                to_remove->vcc?to_remove->vcc->vci:0);
-+        if (to_remove == lec_arp_empty_ones)
-+                lec_arp_empty_ones = to_remove->next;                
-+        else {
-+                entry = lec_arp_empty_ones;
-+                while (entry && entry->next != to_remove)
-+                        entry = entry->next;
-+                if (entry)
-+                        entry->next = to_remove->next;
-+        }
-+        if (!entry)
-+                if (to_remove == lec_no_forward) {
-+                        lec_no_forward = to_remove->next;
-+                } else {
-+                        entry = lec_no_forward;
-+                        while (entry && entry->next != to_remove)
-+                                entry = entry->next;
-+                        if (entry)
-+                                entry->next = to_remove->next;
-+                }
-+        lec_arp_clear_vccs(to_remove);
-+        kfree_s(to_remove, sizeof(struct lec_arp_table));
-+}
-+
-+/*
-+ * Expire entries.
-+ * 1. Re-set timer
-+ * 2. For each entry, delete entries that have aged past the age limit.
-+ * 3. For each entry, depending on the status of the entry, perform
-+ *    the following maintenance.
-+ *    a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the
-+ *       tick_count is above the max_unknown_frame_time, clear
-+ *       the tick_count to zero and clear the packets_flooded counter
-+ *       to zero. This supports the packet rate limit per address
-+ *       while flooding unknowns.
-+ *    b. If the status is ESI_FLUSH_PENDING and the tick_count is greater
-+ *       than or equal to the path_switching_delay, change the status
-+ *       to ESI_FORWARD_DIRECT. This causes the flush period to end
-+ *       regardless of the progress of the flush protocol.
-+ */
-+static void
-+lec_arp_check_expire(unsigned long dumle)
-+{
-+        struct lec_arp_table *entry, *next;
-+        unsigned long now;
-+        unsigned long time_to_check;
-+        int i;
-+
-+        del_timer(&lec_arp_timer);
-+
-+        DPRINTK("lec_arp_check_expire\n");
-+        if (!lec_arp_lock_var) {
-+                lec_arp_lock();
-+                now = jiffies;
-+                for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                        for(entry = lec_arp_tables[i];entry != NULL;) {
-+                                if ((entry->flags) & LEC_REMOTE_FLAG && 
-+                                    lec_topology_change)
-+                                        time_to_check = lec_forward_delay_time;
-+                                else
-+                                        time_to_check = lec_aging_time;
-+
-+                                if((now-entry->last_used > time_to_check) && 
-+                                   !(entry->flags & LEC_PERMANENT_FLAG)) {
-+                                        /* Remove entry */
-+                                        printk("LEC:Remove entry1\n");
-+                                        del_timer(&entry->timer);
-+                                        next = entry->next;      
-+                                        lec_arp_remove(entry);
-+                                        kfree_s(entry, 
-+                                                sizeof(struct lec_arp_table));
-+                                        entry = next;
-+                                } else {
-+                                        /* Something else */
-+                                        if ((entry->status == ESI_VC_PENDING ||
-+                                             entry->status == ESI_ARP_PENDING) 
-+                                            &&
-+                                            now-entry->timestamp >= 
-+                                            lec_max_unknown_frame_time) {
-+                                                entry->timestamp = jiffies;
-+                                                entry->packets_flooded = 0;
-+                                        }
-+                                        if (entry->status == ESI_FLUSH_PENDING 
-+                                            &&
-+                                            now-entry->timestamp >= 
-+                                            lec_path_switching_delay) {
-+                                                entry->last_used = jiffies;
-+                                                entry->status = 
-+                                                        ESI_FORWARD_DIRECT;
-+                                        }
-+                                        entry = entry->next;
-+                                }
-+                        }
-+                }
-+                lec_arp_unlock();
-+        }
-+        lec_arp_timer.expires = jiffies + LEC_ARP_REFRESH_INTERVAL;
-+        if (!lec_freeze)
-+                add_timer(&lec_arp_timer);
-+}
-+
-+/*
-+ * Remove all entries
-+ */
-+static void
-+lec_clear_all(void)
-+{
-+        struct lec_arp_table *entry, *next;
-+        unsigned long flags;
-+        int i;
-+        
-+        save_flags(flags);
-+        cli();
-+        
-+        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                for(entry =lec_arp_tables[i];entry != NULL; entry=next) {
-+                        next = entry->next;
-+                        lec_arp_remove(entry);
-+                        kfree_s(entry, sizeof(struct lec_arp_table));
-+                }
-+        }
-+        entry = lec_arp_empty_ones;
-+        while(entry) {
-+                next = entry->next;
-+                del_timer(&entry->timer);
-+                lec_arp_clear_vccs(entry);
-+                kfree_s(entry, sizeof(struct lec_arp_table));
-+                entry = next;
-+        }
-+        lec_arp_empty_ones = NULL;
-+        entry = lec_no_forward;
-+        while(entry) {
-+                next = entry->next;
-+                del_timer(&entry->timer);
-+                lec_arp_clear_vccs(entry);
-+                kfree_s(entry, sizeof(struct lec_arp_table));
-+                entry = next;
-+        }
-+        lec_no_forward = NULL;
-+        mcast_vcc = NULL; /* To make sure... */
-+        memset(lec_arp_tables, 0, 
-+               sizeof(struct lec_arp_table*)*LEC_ARP_TABLE_SIZE);
-+        restore_flags(flags);
-+}
-+
-+/* 
-+ * Remove all entries not marked permanent
-+ */
-+void
-+lec_reset(void)
-+{
-+        int i;
-+        struct lec_arp_table *entry, *next;
-+
-+        lec_arp_lock();
-+
-+        printk("lec_reset\n");
-+        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                for(entry=lec_arp_tables[i];entry != NULL; entry=next) {
-+                        next = entry->next;
-+                        if (!(entry->flags & LEC_PERMANENT_FLAG)) {
-+                                lec_arp_remove(entry);
-+                                kfree_s(entry, sizeof(struct lec_arp_table));
-+                        }
-+                }
-+        }
-+        lec_arp_unlock();
-+}
-+
-+/*
-+ * Try to find vcc where mac_address is attached.
-+ * 
-+ */
-+struct atm_vcc*
-+lec_arp_resolve(unsigned char *mac_to_find)
-+{
-+        struct lec_arp_table *entry;
-+
-+        if (mac_to_find[0]&0x01) {
-+                return mcast_vcc;
-+        }
-+        entry = lec_arp_find(mac_to_find);
-+  
-+        if (entry) {
-+                if (entry->status == ESI_FORWARD_DIRECT) {
-+                        /* Connection Ok */
-+                        return entry->vcc;
-+                }
-+                if (entry->status == ESI_UNKNOWN) {
-+                        /* We want arp-request to be sent */
-+                        entry->status = ESI_ARP_PENDING;
-+                        entry->timestamp = jiffies;
-+                        entry->no_tries = 1;
-+                        entry->packets_flooded=1;
-+                        send_to_lecd(l_arp_xmt, mac_to_find, NULL);
-+                        del_timer(&entry->timer);
-+                        entry->timer.expires = jiffies + (1*HZ);
-+                        entry->timer.function = lec_arp_expire_arp;
-+                        add_timer(&entry->timer);
-+                        return mcast_vcc;
-+                }
-+                /* Data direct VC not yet set up, check to see if the unknown
-+                   frame count is greater than the limit. If the limit has
-+                   not been reached, allow the caller to send packet to
-+                   BUS. */
-+                if (entry->packets_flooded < lec_maximum_unknown_frame_count) {
-+                        entry->packets_flooded++;
-+                        DPRINTK("LEC_ARP: Flooding..\n");
-+                        return mcast_vcc;
-+                }
-+                return NULL;
-+        } else {
-+                /* No matching entry was found */
-+                entry = make_entry(mac_to_find);
-+                DPRINTK("LEC_ARP: Making entry\n");
-+                if (!entry) {
-+                        return mcast_vcc;
-+                }
-+                entry->packets_flooded =1;
-+                entry->status = ESI_ARP_PENDING;
-+                entry->no_tries = 1;
-+                entry->last_used = entry->timestamp = jiffies;
-+                send_to_lecd(l_arp_xmt, mac_to_find, NULL);
-+
-+                entry->timer.expires = jiffies + (1*HZ);
-+                add_timer(&entry->timer);
-+                lec_arp_put(entry);
-+                return mcast_vcc;
-+        }
-+}
-+
-+/*
-+ * Add permanent entry to arp table.
-+ */
-+struct lec_arp_table*
-+lec_add_permanent(unsigned char *mac_addr, unsigned char *atmaddr)
-+{
-+        struct lec_arp_table *to_add;
-+
-+        to_add = make_entry(mac_addr);
-+        if (!to_add) {
-+                return NULL;
-+        }
-+        memcpy(to_add->atm_addr, atmaddr, ATM_ESA_LEN);
-+        to_add->flags |= LEC_PERMANENT_FLAG;
-+        to_add->last_used = jiffies;
-+        /* Connection forming must be started */
-+        send_to_lecd(l_svc_setup, NULL, atmaddr);
-+        lec_arp_lock();
-+        lec_arp_put(to_add);
-+        lec_arp_unlock();
-+        return to_add;
-+}
-+
-+int
-+lec_addr_delete(unsigned char *atm_addr, unsigned long permanent)
-+{
-+        struct lec_arp_table *entry, *next;
-+        int i;
-+
-+        lec_arp_lock();
-+        printk("lec_addr_delete\n");
-+        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                for(entry=lec_arp_tables[i];entry != NULL; entry=next) {
-+                        next = entry->next;
-+                        if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN)
-+                            && (permanent || 
-+                                !(entry->flags & LEC_PERMANENT_FLAG)))
-+                                lec_arp_remove(entry);
-+                        kfree_s(entry, sizeof(struct lec_arp_table));
-+                        lec_arp_unlock();
-+                        return 0;
-+                }
-+        }
-+        lec_arp_unlock();
-+        return -1;
-+}
-+
-+/*
-+ * Notifies:  Response to arp_request (atm_addr != NULL) 
-+ */
-+void
-+lec_arp_update(unsigned char *mac_addr,
-+              unsigned char *atm_addr,
-+              unsigned long remoteflag)
-+{
-+        struct lec_arp_table *entry, *tmp;
-+        int i;
-+
-+        lec_arp_lock();
-+
-+        if (lec_arp_empty_ones) {
-+                entry = lec_arp_empty_ones;
-+                if (!memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN)) {
-+                        lec_arp_empty_ones = entry->next;
-+                } else {
-+                        while(entry->next && memcmp(entry->next->atm_addr, 
-+                                                    atm_addr, ATM_ESA_LEN))
-+                                entry = entry->next;
-+                        if (entry->next) {
-+                                tmp = entry;
-+                                entry = entry->next;
-+                                tmp->next = entry->next;
-+                        } else
-+                                entry = NULL;
-+                        
-+                }
-+                if (entry) {
-+                        del_timer(&entry->timer);
-+                        tmp = lec_arp_find(mac_addr);
-+                        if (tmp) {
-+                                del_timer(&tmp->timer);
-+                                tmp->status = ESI_FORWARD_DIRECT;
-+                                memcpy(tmp->atm_addr, atm_addr, ATM_ESA_LEN);
-+                                tmp->vcc = entry->vcc;
-+                                tmp->old_push = entry->old_push;
-+                                kfree_s(entry, sizeof(struct lec_arp_table));
-+                                entry=tmp;
-+                        } else {
-+                                entry->status = ESI_FORWARD_DIRECT;
-+                                memcpy(entry->mac_addr, mac_addr, ETH_ALEN);
-+                                lec_arp_put(tmp);
-+                        }
-+                        if (remoteflag)
-+                                entry->flags|=LEC_REMOTE_FLAG;
-+                        else
-+                                entry->flags&=~LEC_REMOTE_FLAG;
-+                        lec_arp_unlock();
-+                        return;
-+                }
-+        }
-+        entry = lec_arp_find(mac_addr);
-+        if (!entry) {
-+                entry = make_entry(mac_addr);
-+                memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN);
-+                entry->status = ESI_UNKNOWN;
-+                lec_arp_put(entry);
-+                /* Temporary, changes before end of function */
-+        }
-+        memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN);
-+        del_timer(&entry->timer);
-+        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                for(tmp=lec_arp_tables[i];tmp;tmp=tmp->next) {
-+                        if (entry != tmp &&
-+                            !memcmp(tmp->atm_addr, atm_addr,
-+                                    ATM_ESA_LEN)) { 
-+                                /* Vcc to this host exists */
-+                                if (tmp->status > ESI_VC_PENDING) {
-+                                        /*
-+                                         * ESI_FLUSH_PENDING,
-+                                         * ESI_FORWARD_DIRECT
-+                                         */
-+                                        entry->vcc = tmp->vcc;
-+                                        entry->old_push=tmp->old_push;
-+                                }
-+                                entry->status=tmp->status;
-+                                break;
-+                        }
-+                }
-+        }
-+        if (remoteflag)
-+                entry->flags|=LEC_REMOTE_FLAG;
-+        else
-+                entry->flags&=~LEC_REMOTE_FLAG;
-+        if (entry->status == ESI_ARP_PENDING ||
-+            entry->status == ESI_UNKNOWN) {
-+                entry->status = ESI_VC_PENDING;
-+                send_to_lecd(l_svc_setup, NULL, atm_addr);
-+        }
-+        lec_arp_unlock();
-+}
-+
-+/*
-+ * Notifies: Vcc setup ready 
-+ */
-+void
-+lec_vcc_added(struct atmlec_ioc *ioc_data,
-+             struct atm_vcc *vcc,
-+             void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb))
-+{
-+        struct lec_arp_table *entry;
-+        int i, found_entry=0;
-+        unsigned char bus_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff,0xff};
-+
-+        lec_arp_lock();
-+        if (ioc_data->receive == 2) {
-+                /* Vcc for BUS distribute */
-+                DPRINTK("LEC_ARP: Attaching mcast distribute\n");
-+                entry = lec_arp_find(bus_mac);
-+                if (!entry) {
-+                        printk("LEC_ARP: Multicast entry not found!\n");
-+                        lec_arp_unlock();
-+                        return;
-+                }
-+                memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
-+                entry->recv_vcc = vcc;
-+                entry->old_recv_push = old_push;
-+                lec_arp_unlock();
-+                return;
-+        } else if (ioc_data->receive == 1) {
-+                /* Vcc which we don't want to make default vcc, attach it
-+                   anyway. */
-+                DPRINTK("LEC_ARP:Attaching data direct, not default :%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
-+                        ioc_data->atm_addr[0],ioc_data->atm_addr[1],
-+                        ioc_data->atm_addr[2],ioc_data->atm_addr[3],
-+                        ioc_data->atm_addr[4],ioc_data->atm_addr[5],
-+                        ioc_data->atm_addr[6],ioc_data->atm_addr[7],
-+                        ioc_data->atm_addr[8],ioc_data->atm_addr[9],
-+                        ioc_data->atm_addr[10],ioc_data->atm_addr[11],
-+                        ioc_data->atm_addr[12],ioc_data->atm_addr[13],
-+                        ioc_data->atm_addr[14],ioc_data->atm_addr[15],
-+                        ioc_data->atm_addr[16],ioc_data->atm_addr[17],
-+                        ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
-+                entry = make_entry(bus_mac);
-+                memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
-+                memset(entry->mac_addr, 0, ETH_ALEN);
-+                entry->recv_vcc = vcc;
-+                entry->old_recv_push = old_push;
-+                entry->status = ESI_UNKNOWN;
-+                entry->timer.expires = jiffies + lec_vcc_timeout_period;
-+                entry->timer.function = lec_arp_expire_vcc;
-+                entry->next = lec_no_forward;
-+                lec_no_forward = entry;
-+                lec_arp_unlock();
-+                return;
-+        }
-+        DPRINTK("LEC_ARP:Attaching data direct, default:%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
-+                        ioc_data->atm_addr[0],ioc_data->atm_addr[1],
-+                        ioc_data->atm_addr[2],ioc_data->atm_addr[3],
-+                        ioc_data->atm_addr[4],ioc_data->atm_addr[5],
-+                        ioc_data->atm_addr[6],ioc_data->atm_addr[7],
-+                        ioc_data->atm_addr[8],ioc_data->atm_addr[9],
-+                        ioc_data->atm_addr[10],ioc_data->atm_addr[11],
-+                        ioc_data->atm_addr[12],ioc_data->atm_addr[13],
-+                        ioc_data->atm_addr[14],ioc_data->atm_addr[15],
-+                        ioc_data->atm_addr[16],ioc_data->atm_addr[17],
-+                ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
-+        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                for (entry = lec_arp_tables[i];entry!=NULL;entry=entry->next) {
-+                        if (memcmp(ioc_data->atm_addr, entry->atm_addr, 
-+                                   ATM_ESA_LEN)==0) {
-+                                DPRINTK("LEC_ARP: Attaching data direct\n");
-+                                DPRINTK("Currently -> Vcc: %d, Rvcc:%d\n",
-+                                        entry->vcc?entry->vcc->vci:0,
-+                                        entry->recv_vcc?entry->recv_vcc:0);
-+                                found_entry=1;
-+                                del_timer(&entry->timer);
-+                                if (vcc) {
-+                                        lec_arp_clear_vccs(entry);
-+                                }
-+                                entry->vcc = vcc;
-+                                entry->old_push = old_push;
-+                                if (entry->status == ESI_VC_PENDING) {
-+                                        if (lec_maximum_unknown_frame_count==0)
-+                                                entry->status = 
-+                                                        ESI_FORWARD_DIRECT;
-+                                        else {
-+                                                entry->timestamp = jiffies;
-+                                                entry->status = 
-+                                                        ESI_FLUSH_PENDING;
-+                                                send_to_lecd(l_flush_xmt, 
-+                                                             entry->mac_addr, 
-+                                                             entry->atm_addr);
-+                                        }
-+                                } else {
-+                                        /* They were forming a connection
-+                                           to us, and we to them. Our
-+                                           ATM address is numerically lower
-+                                           than theirs, so we make connection
-+                                           we formed into default VCC (8.1.11).
-+                                           Connection they made gets torn
-+                                           down. This might confuse some
-+                                           clients. Can be changed if
-+                                           someone reports trouble... */
-+                                        ;
-+                                }
-+                        }
-+                }
-+        }
-+        if (found_entry) {
-+                lec_arp_unlock();
-+                dump_arp_table();
-+                return;
-+        }
-+        /* Not found, snatch address from first data packet that arrives from
-+           this vcc */
-+        entry = make_entry(bus_mac);
-+        entry->vcc = vcc;
-+        entry->old_push = old_push;
-+        memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
-+        memset(entry->mac_addr, 0, ETH_ALEN);
-+        entry->status = ESI_UNKNOWN;
-+        entry->next = lec_arp_empty_ones;
-+        lec_arp_empty_ones = entry;
-+        entry->timer.expires = jiffies + lec_vcc_timeout_period;
-+        entry->timer.function = lec_arp_expire_vcc;
-+        add_timer(&entry->timer);
-+        lec_arp_unlock();
-+}
-+
-+void
-+lec_flush_complete(unsigned char *atm_addr, unsigned long tran_id)
-+{
-+        struct lec_arp_table *entry;
-+        int i;
-+  
-+        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                for (entry=lec_arp_tables[i];entry!=NULL;entry=entry->next) {
-+                        if (memcmp(atm_addr, entry->atm_addr,ATM_ESA_LEN)==0 &&
-+                            entry->flush_tran_id == tran_id) {
-+                                entry->status = ESI_FORWARD_DIRECT;
-+                                return;
-+                        }
-+                }
-+        }
-+        printk("LEC_ARP: Flush_complete: entry not found : %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", 
-+               0xff&atm_addr[0],0xff&atm_addr[1],0xff&atm_addr[2],
-+               0xff&atm_addr[3],0xff&atm_addr[4],0xff&atm_addr[5],
-+               0xff&atm_addr[6],0xff&atm_addr[7],0xff&atm_addr[8],
-+               0xff&atm_addr[9],0xff&atm_addr[10],0xff&atm_addr[11],
-+               0xff&atm_addr[12],0xff&atm_addr[13],0xff&atm_addr[14],
-+               0xff&atm_addr[15],0xff&atm_addr[16],0xff&atm_addr[17],
-+               0xff&atm_addr[18],0xff&atm_addr[19]);
-+}
-+
-+void 
-+lec_topology_flag_change_set(unsigned long topology_change_flag)
-+{
-+        lec_topology_change = topology_change_flag;  
-+}
-+
-+void
-+lec_set_flush_tran_id(unsigned char *mac_addr, unsigned long tran_id)
-+{
-+        struct lec_arp_table *entry;
-+
-+        entry = lec_arp_find(mac_addr);
-+        if (!entry) {
-+                printk("LEC_ARP: Set_flush_tran_id: entry not found\n");
-+                return;
-+        }
-+        entry->flush_tran_id = tran_id;
-+}
-+
-+int 
-+lec_mcast_attach(struct atm_vcc *vcc)
-+{
-+        unsigned char mac_addr[] = {
-+                0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-+        struct lec_arp_table *to_add;
-+  
-+        lec_arp_lock();
-+        to_add = make_entry(mac_addr);
-+        if (!to_add)
-+                return -ENOMEM;
-+        to_add->status = ESI_FORWARD_DIRECT;
-+        to_add->flags |= LEC_PERMANENT_FLAG;
-+        to_add->vcc = vcc;  
-+        to_add->old_push = vcc->push;
-+        vcc->push = lec_push;
-+        mcast_vcc = vcc;
-+        lec_arp_put(to_add);
-+        lec_arp_unlock();
-+        return 0;
-+}
-+
-+void
-+lec_config(struct atmlec_config_msg *conf)
-+{
-+        lec_maximum_unknown_frame_count = conf->maximum_unknown_frame_count;
-+        lec_max_unknown_frame_time = (conf->max_unknown_frame_time*HZ);
-+        lec_max_retry_count = conf->max_retry_count;
-+        lec_aging_time = (conf->aging_time*HZ);
-+        lec_forward_delay_time = (conf->forward_delay_time*HZ);
-+        lec_arp_response_time = (conf->arp_response_time*HZ);
-+        lec_flush_timeout = (conf->flush_timeout*HZ);
-+        lec_path_switching_delay = (conf->path_switching_delay*HZ);
-+}
-+
-+void
-+lec_vcc_close(struct atm_vcc *vcc)
-+{
-+        struct lec_arp_table *entry, *next;
-+        int i;
-+
-+        DPRINTK("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n",vcc->vpi,vcc->vci);
-+        dump_arp_table();
-+        lec_arp_lock();
-+        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
-+                for(entry = lec_arp_tables[i];entry!=NULL; entry=next) {
-+                        next = entry->next;
-+                        if (vcc == entry->vcc) {
-+                                lec_arp_remove(entry);
-+                                kfree_s(entry, sizeof(struct lec_arp_table));
-+                                if (mcast_vcc == vcc) {
-+                                        mcast_vcc = NULL;
-+                                }
-+                        } else if (vcc == entry->recv_vcc) { 
-+                                /* Bus distribution closed */
-+                                mcast_vcc = NULL;
-+                                lec_arp_remove(entry);
-+                                lec_arp_unlock();
-+                                vcc->push(vcc, NULL);
-+                                return;
-+                        }
-+                }
-+        }
-+        entry=lec_arp_empty_ones;
-+        while(entry && entry->vcc==vcc) {
-+                lec_arp_clear_vccs(entry);
-+                lec_arp_empty_ones=entry->next;
-+                kfree_s(entry, sizeof(struct lec_arp_table));
-+                entry=lec_arp_empty_ones;
-+        }
-+        for(;entry!=NULL;entry=next) {
-+                next=entry->next;
-+                if (vcc == next->vcc) {
-+                        lec_arp_clear_vccs(next);
-+                        entry->next = next->next;
-+                        kfree_s(next, sizeof(struct lec_arp_table));
-+                }
-+        }
-+        entry=lec_no_forward;
-+        while(entry && entry->recv_vcc==vcc) {
-+                lec_arp_clear_vccs(entry);
-+                lec_no_forward=entry->next;
-+                kfree_s(entry, sizeof(struct lec_arp_table));
-+                entry=lec_no_forward;
-+        }
-+        for(;entry!=NULL;entry=next) {
-+                next=entry->next;
-+                if (vcc == next->recv_vcc) {
-+                        lec_arp_clear_vccs(next);
-+                        entry->next = next->next;
-+                        kfree_s(next, sizeof(struct lec_arp_table));
-+                }
-+        }
-+        lec_arp_unlock();
-+}
-+
-+void
-+lec_arp_check_empties(struct atm_vcc *vcc, struct sk_buff *skb)
-+{
-+        struct lec_arp_table *entry, *prev;
-+        struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data;
-+  
-+        lec_arp_lock();
-+        entry = lec_arp_empty_ones;
-+        if (vcc == entry->vcc) {
-+                del_timer(&entry->timer);
-+                memcpy(entry->mac_addr, hdr->h_source, ETH_ALEN);
-+                entry->status = ESI_FORWARD_DIRECT;
-+                lec_arp_empty_ones = entry->next;
-+                /* We might have got an entry */
-+                if ((prev=lec_arp_find(hdr->h_source))) {
-+                        lec_arp_remove(prev);
-+                        kfree_s(prev, sizeof(struct lec_arp_table));
-+                }
-+                lec_arp_put(entry);
-+                lec_arp_unlock();
-+                return;
-+        }
-+        prev = entry;
-+        entry = entry->next;
-+        while (entry && entry->vcc != vcc) {
-+                prev= entry;
-+                entry = entry->next;
-+        }
-+        if (!entry) {
-+                DPRINTK("LEC_ARP: Arp_check_empties: entry not found!\n");
-+                lec_arp_unlock();
-+                return;
-+        }
-+        del_timer(&entry->timer);
-+        memcpy(entry->mac_addr, hdr->h_source, ETH_ALEN);
-+        entry->status = ESI_FORWARD_DIRECT;
-+        prev->next = entry->next;
-+        if ((prev = lec_arp_find(hdr->h_source))) {
-+                lec_arp_remove(prev);
-+                kfree_s(prev, sizeof(struct lec_arp_table));
-+        }
-+        lec_arp_put(entry);
-+        lec_arp_unlock();  
-+}
-+
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/net/atm/lec_arpc.h	Fri Jul 19 15:10:11 1996
-@@ -0,0 +1,104 @@
-+/*
-+ * Lec arp cache
-+ * Marko Kiiskila carnil@cs.tut.fi
-+ *
-+ */
-+#ifndef _LEC_ARP_H
-+#define _LEC_ARP_H
-+#include <linux/atm.h>
-+#include <linux/atmdev.h>
-+#include <linux/if_ether.h>
-+#include <linux/atmlec.h>
-+
-+struct lec_arp_table {
-+        struct lec_arp_table *next;          /* Linked entry list */
-+        unsigned char atm_addr[ATM_ESA_LEN]; /* Atm address */
-+        unsigned char mac_addr[ETH_ALEN];    /* Mac address */
-+        struct atm_vcc *vcc;                 /* Vcc this entry is attached */
-+        struct atm_vcc *recv_vcc;            /* Vcc we receive data from */
-+        void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb); 
-+                                             /* Push that leads to daemon */
-+        void (*old_recv_push)(struct atm_vcc *vcc, struct sk_buff *skb);
-+                                             /* Push that leads to daemon */
-+        void (*old_close)(struct atm_vcc *vcc);
-+                                             /* We want to see when this
-+                                              * vcc gets closed */
-+        unsigned long last_used;             /* For expiry */
-+        unsigned long timestamp;             /* Used for various timestamping
-+                                              * things:
-+                                              * 1. FLUSH started 
-+                                              *    (status=ESI_FLUSH_PENDING)
-+                                              * 2. Counting to 
-+                                              *    max_unknown_frame_time
-+                                              *    (status=ESI_ARP_PENDING||
-+                                              *     status=ESI_VC_PENDING)
-+                                              */
-+        unsigned char no_tries;              /* No of times arp retry has been 
-+                                                tried */
-+        unsigned char status;                /* Status of this entry */
-+        unsigned short flags;                /* Flags for this entry */
-+        unsigned short packets_flooded;      /* Data packets flooded */
-+        unsigned long flush_tran_id;         /* Transaction id in flush protocol */
-+        struct timer_list timer;             /* Arping timer */
-+};
-+
-+/* Status fields */
-+#define ESI_UNKNOWN 0       /*
-+                             * Next packet sent to this mac address
-+                             * causes ARP-request to be sent 
-+                             */
-+#define ESI_ARP_PENDING 1   /*
-+                             * There is no ATM address associated with this
-+                             * 48-bit address.  The LE-ARP protocol is in
-+                             * progress.
-+                             */
-+#define ESI_VC_PENDING 2    /*
-+                             * There is a valid ATM address associated with 
-+                             * this 48-bit address but there is no VC set 
-+                             * up to that ATM address.  The signaling 
-+                             * protocol is in process.
-+                             */
-+#define ESI_FLUSH_PENDING 4 /*
-+                             * The LEC has been notified of the FLUSH_START
-+                             * status and it is assumed that the flush 
-+                             * protocol is in process.
-+                             */
-+#define ESI_FORWARD_DIRECT 5 /*
-+                              * Either the Path Switching Delay (C22) has 
-+                              * elapsed or the LEC has notified the Mapping 
-+                              * that the flush protocol has completed.  In 
-+                              * either case, it is safe to forward packets 
-+                              * to this address via the data direct VC.
-+                              */
-+
-+/* Flag values */
-+#define LEC_REMOTE_FLAG      0x0001
-+#define LEC_PERMANENT_FLAG   0x0002
-+
-+/* Hash table size */
-+#define LEC_ARP_TABLE_SIZE 16
-+
-+/* Protos */
-+void lec_reset(void);
-+void lec_arp_init(void);
-+int lec_mcast_attach(struct atm_vcc *vcc);
-+void lec_arp_destroy(void);
-+void lec_vcc_close(struct atm_vcc *vcc);
-+
-+struct atm_vcc *lec_arp_resolve(unsigned char *mac_to_addr);
-+void lec_vcc_added(struct atmlec_ioc *ioc_data, struct atm_vcc *vcc,
-+                  void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb));
-+void lec_arp_check_empties(struct atm_vcc *vcc, struct sk_buff *skb);
-+struct lec_arp_table *lec_add_permanent(unsigned char *mac_addr, 
-+                                       unsigned char *atmaddr);
-+int lec_addr_delete(unsigned char *mac_addr, unsigned long permanent);
-+void lec_topology_flag_change_set(unsigned long topology_change_flag);
-+void lec_flush_complete(unsigned char *atm_addr, unsigned long tran_id);
-+void lec_arp_update(unsigned char *mac_addr, unsigned char *atm_addr,
-+                   unsigned long remoteflag);
-+void lec_set_flush_tran_id(unsigned char *mac_addr, unsigned long tran_id);
-+void lec_config(struct atmlec_config_msg *conf);
-+void lec_arp_check(unsigned char *to_check);
-+void lec_proc_info(char *buf);
-+
-+#endif
---- ref/include/net/route.h	Sun Jun  9 11:28:48 1996
-+++ work/include/net/route.h	Wed Jul 17 19:56:48 1996
-@@ -26,6 +26,7 @@
- #define _ROUTE_H
- 
- #include <linux/config.h>
-+#include <net/sock.h>
- 
- /*
-  * 0 - no debugging messages
-@@ -158,11 +159,17 @@
- #endif
- #endif
- 
-+
- extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
- 						       __u32 daddr, int local)
- {
- 	struct rtable * rt = *rp;
- 
-+#ifdef CONFIG_AREQUIPA
-+	extern struct device *arequipa_dev;
-+
-+	if (rt && rt->rt_dev == arequipa_dev) return rt;
-+#endif
- 	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
- 	    || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
- 	{
-@@ -172,6 +179,35 @@
- 	}
- 	return rt;
- }	
-+
-+
-+extern __inline__ void set_rt_cache(struct sock *sk,struct rtable *rt)
-+{
-+#ifndef CONFIG_AREQUIPA
-+	sk->ip_route_cache = rt;
-+#else
-+	extern struct rtable arequipa_rt;
-+
-+	if (!sk->arequipa) {
-+		sk->ip_route_cache = rt;
-+		return;
-+	}
-+	if (!sk->aq_route) {
-+		printk(KERN_CRIT "set_rt_cache: !sk->aq_route\n");
-+		sk->ip_route_cache = rt;
-+		return;
-+	}
-+#if 0 /* only needed if preloading the route cache for Arequipa */
-+        if (sk->ip_route_cache) ip_rt_put(sk->ip_route_cache);
-+#endif
-+        sk->ip_route_cache = NULL;
-+        if (!rt) return;
-+	*sk->aq_route = arequipa_rt;
-+        sk->ip_route_cache = sk->aq_route;
-+	sk->aq_route->rt_src = rt->rt_src;
-+	ip_rt_put(rt);
-+#endif
-+}
- 
- 
- #endif	/* _ROUTE_H */
---- ref/include/net/sock.h	Sun Jun  9 11:25:41 1996
-+++ work/include/net/sock.h	Wed Jul 17 19:41:04 1996
-@@ -301,7 +301,13 @@
- 	int			ip_mc_loop;		/* Loopback */
- 	char			ip_mc_name[MAX_ADDR_LEN];/* Multicast device name */
- 	struct ip_mc_socklist	*ip_mc_list;		/* Group array */
--#endif  
-+#endif
-+#ifdef CONFIG_AREQUIPA
-+	struct socket		*arequipa;   /* dedicated link layer socket,
-+						NULL if not connected */
-+	struct rtable		*aq_route;   /* buffer for fake route, NULL
-+						if neither present nor expect */
-+#endif
- 
- /*
-  *	This part is used for the timeout functions (timer.c). 
---- /dev/null	Fri Jul 19 15:27:12 1996
-+++ work/include/linux/arequipa.h	Wed Jul 17 19:58:01 1996
-@@ -0,0 +1,39 @@
-+/* arequipa.h - Arequipa interface definitions */
-+ 
-+/* Written 1996 by Jean-Michel Pittet and Werner Almesberger, EPFL LRC */
-+ 
-+
-+#ifndef _LINUX_AREQUIPA_H
-+#define _LINUX_AREQUIPA_H
-+
-+#include <linux/atmioc.h>
-+
-+
-+#define AREQUIPA_PRESET		_IO('a',ATMIOC_AREQUIPA)
-+#define AREQUIPA_INCOMING	_IO('a',ATMIOC_AREQUIPA+1)
-+#define AREQUIPA_EXPECT		_IO('a',ATMIOC_AREQUIPA+2)
-+#define AREQUIPA_CLOSE		_IO('a',ATMIOC_AREQUIPA+3)
-+
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/net.h>
-+#include <linux/netdevice.h>
-+#include <net/sock.h>
-+#include <net/route.h>
-+
-+
-+/* extern struct rtable arequipa_rt; - not needed */
-+extern struct device *arequipa_dev;
-+
-+int atm_init_arequipa(void);
-+int arequipa_attach(struct socket *lower,struct sock *upper);
-+
-+int arequipa_preset(struct socket *lower,struct sock *upper);
-+int arequipa_expect(struct sock *upper,int on);
-+int arequipa_incoming(struct socket *lower);
-+int arequipa_close(struct sock *upper);
-+
-+#endif /* __KERNEL__ */
-+
-+#endif
---- ref/net/socket.c	Thu Jun  6 20:22:25 1996
-+++ work/net/socket.c	Mon Jun 10 18:30:51 1996
-@@ -778,7 +778,7 @@
- 		sock_release(newsock);
- 		return(-EINVAL);
- 	}
--	sock->file=current->files->fd[fd];
-+	newsock->file=current->files->fd[fd];
- 	
- 	if (upeer_sockaddr)
- 	{
---- ref/net/ipv4/tcp_input.c	Sun Jun  9 10:39:09 1996
-+++ work/net/ipv4/tcp_input.c	Mon Jun 10 17:36:44 1996
-@@ -33,6 +33,11 @@
- #include <linux/config.h>
- #include <net/tcp.h>
- 
-+#ifdef CONFIG_AREQUIPA
-+#include <linux/atmdev.h>
-+#include <linux/arequipa.h>
-+#endif
-+
- /*
-  *	Policy code extracted so it's now separate
-  */
-@@ -415,6 +420,24 @@
- 			return;
- 		}
- 	}
-+#ifdef CONFIG_AREQUIPA
-+	newsk->arequipa = NULL;
-+	if (sk->aq_route) {
-+		newsk->aq_route = kmalloc(sizeof(struct rtable),GFP_ATOMIC);
-+		if (newsk->aq_route) {
-+			if (dev == arequipa_dev)
-+				(void) arequipa_attach(skb->atm.vcc->sock,
-+				    newsk);
-+		}
-+		else {
-+			if (sk->opt) kfree(sk->opt);
-+			kfree_s(newsk,sizeof(*sk));
-+			tcp_statistics.TcpAttemptFails++;
-+			kfree_skb(skb,FREE_READ);
-+			return;
-+		}
-+	}
-+#endif
- 	skb_queue_head_init(&newsk->write_queue);
- 	skb_queue_head_init(&newsk->receive_queue);
- 	newsk->send_head = NULL;
-@@ -516,7 +539,7 @@
- 	 */
- 
- 	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
--	newsk->ip_route_cache = rt;
-+	set_rt_cache(newsk,rt);
- 	
- 	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
- 		newsk->window_clamp = rt->rt_window;
-@@ -1771,6 +1794,12 @@
- 		skb->free = 1;
- 		skb->saddr = daddr;
- 		skb->daddr = saddr;
-+
-+#ifdef CONFIG_AREQUIPA
-+		if (dev == arequipa_dev && !sk->arequipa && sk->aq_route &&
-+		    sk->state == TCP_ESTABLISHED)
-+			(void) arequipa_attach(skb->atm.vcc->sock,sk);
-+#endif
- 
- 		/*
- 		 * We may need to add it to the backlog here. 
diff -ur --new-file old/atm/atm.patch new/atm/atm.patch
--- old/atm/atm.patch	Thu Jan  1 01:00:00 1970
+++ new/atm/atm.patch	Wed Jul 31 19:45:24 1996
@@ -0,0 +1,16143 @@
+--- ref/Makefile	Fri Jun  7 10:55:00 1996
++++ work/Makefile	Mon Jun 10 17:41:25 1996
+@@ -126,6 +126,10 @@
+ 
+ DRIVERS := $(DRIVERS) drivers/net/net.a
+ 
++ifdef CONFIG_ATM
++DRIVERS := $(DRIVERS) drivers/atm/atm.a
++endif
++
+ ifdef CONFIG_CD_NO_IDESCSI
+ DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
+ endif
+@@ -288,6 +292,7 @@
+ 	if [ -f SCSI_MODULES  ]; then inst_mod SCSI_MODULES  scsi;  fi; \
+ 	if [ -f FS_MODULES    ]; then inst_mod FS_MODULES    fs;    fi; \
+ 	if [ -f CDROM_MODULES ]; then inst_mod CDROM_MODULES cdrom; fi; \
++	if [ -f ATM_MODULES   ]; then inst_mod ATM_MODULES atm;     fi; \
+ 	\
+ 	ls *.o > .allmods; \
+ 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 .allmods - > .misc; \
+--- ref/Documentation/Configure.help	Fri Jun  7 15:28:46 1996
++++ work/Documentation/Configure.help	Wed Jul 31 19:23:00 1996
+@@ -1164,6 +1164,101 @@
+   you can read some network related routing information from that
+   file. Everything you write to that file will be discarded.
+ 
++Asynchronous Transfer Mode (ATM)
++CONFIG_ATM
++  You are likely to want to enable this.
++
++Enable single-copy
++CONFIG_MMU_HACKS
++  Single-copy avoids intermediate buffering of AAL5 PDUs when using the
++  native ATM API. Enabling single-copy may lead to significant throughput
++  improvements. Note that single-copy probably still has bugs, so
++  enabling it may de-stabilize the system.
++
++Extended debugging for single-copy
++CONFIG_MMU_HACKS_DEBUG
++  Extended debugging records various events and displays that list when
++  an inconsistency is detected. This mechanism is faster than generally
++  using printks, but still has some impact on performance. Enable this
++  if you suspect problems with single-copy.
++
++Classical IP over ATM with ATMARP
++CONFIG_ATM_ATMARP
++  Classical IP over ATM for PVCs and SVCs, supporting InARP and ATMARP.
++  This interface is younger than the old "CLIP" interface (next item)
++  and it may therefore still have some bugs. In the future, it will
++  replace the old interface entirely.
++
++Classical IP over ATM for PVCs (obsolete)
++CONFIG_ATM_CLIP
++  Classical IP over ATM for PVCs only and without InARP. This interface
++  is obsolete and will be removed in later versions of ATM on Linux, as
++  soon as ATMARP is fully mature.
++
++Application REQUested IP over ATM
++CONFIG_AREQUIPA
++  Arequipa is an experimental mechanism to create short-cut ATM SVCs for
++  IP traffic. See
++  ftp://lrcftp.epfl.ch/pub/arequipa/draft-almesberger-arequipa-01.txt
++  for details.
++
++LANE support
++CONFIG_ATM_LANE
++  Lan Emulation emulates services of existing LANs across an ATM network.
++  Client in Linux currently supports only one virtual LAN, and it has
++  not been tested in environment which uses other than DIX Ethernet-
++  data frames, i.e. Appletalk, IPX support has not been tested.
++  Specification for LANE can be found from ATM Forum's specification
++  LAN Emulation Over ATM - Version 1.0.
++
++ATM over TCP
++CONFIG_ATM_TCP
++  ATM over TCP driver. Useful for doing some simple experiments with the
++  native ATM socket API, but not for many other things. You don't need
++  this is you have a "real" ATM card.
++
++Efficient Networks ENI155P
++CONFIG_ATM_ENI
++  Driver for the Efficient Networks ENI155p series 155 Mbps ATM adapters.
++  Both, the C and S variants (512kB and 2MB ob-board RAM, respectively),
++  and the FPGA and the ASIC Tonga versions of the board are supported.
++  The driver works with MMF (-MF) and UTP-5 (-U5) adapters.
++
++Enable extended debugging
++CONFIG_ATM_ENI_DEBUG
++  Extended debugging records various events and displays that list when
++  an inconsistency is detected. This mechanism is faster than generally
++  using printks, but still has some impact on performance. Note that
++  extended debugging may create certain race conditions itself. Enable
++  this ONLY if you suspect problems with the driver.
++
++ZeitNet ZN1221/ZN1225
++CONFIG_ATM_ZATM
++  Driver for the ZeitNet ZN1221 (MMF) and ZN1225 (UTP-5) 155 Mbps ATM
++  adapters.
++
++Enable extended debugging
++CONFIG_ATM_ZATM_DEBUG
++  Extended debugging records various events and displays that list when
++  an inconsistency is detected. This mechanism is faster than generally
++  using printks, but still has some impact on performance. Note that
++  extended debugging may create certain race conditions itself. Enable
++  this ONLY if you suspect problems with the driver.
++
++Rolfs TI TNETA1570
++CONFIG_ATM_TNETA1570
++  Driver for the TNETA1570 155 Mbps ATM adapter, both developed by Rolf
++  Fiedler at TU Chemnitz, see also
++  ftp://ftp.infotech.tu-chemnitz.de/pub/linux-atm
++
++Enable extended debugging
++CONFIG_ATM_TNETA1570_DEBUG
++  Extended debugging records various events and displays that list when
++  an inconsistency is detected. This mechanism is faster than generally
++  using printks, but still has some impact on performance. Note that
++  extended debugging may create certain race conditions itself. Enable
++  this ONLY if you suspect problems with the driver.
++
+ SCSI support?
+ CONFIG_SCSI
+   If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or
+--- ref/kernel/ksyms.c	Wed May 29 15:42:27 1996
++++ work/kernel/ksyms.c	Mon Jun 10 17:43:08 1996
+@@ -62,6 +62,9 @@
+ #ifdef __SMP__
+ #include <linux/smp.h>
+ #endif
++#ifdef CONFIG_ATM
++#include <linux/atmdev.h>
++#endif
+ 
+ extern char *get_options(char *str, int *ints);
+ extern void set_device_ro(int dev,int flag);
+@@ -76,6 +79,10 @@
+ 
+ extern void hard_reset_now(void);
+ 
++#ifdef CONFIG_ATM_TCP
++extern int (*atmtcp_attach_hook)(struct socket *sock);
++#endif
++
+ struct symbol_table symbol_table = {
+ #include <linux/symtab_begin.h>
+ #ifdef MODVERSIONS
+@@ -348,7 +355,16 @@
+ #ifdef CONFIG_BLK_DEV_MD
+ 	X(disk_name),	/* for md.c */
+ #endif
+- 	
++ 
++#ifdef CONFIG_ATM
++	X(atm_dev_register),
++	X(atm_dev_deregister),
++	X(atm_find_ci),
++#endif
++#ifdef CONFIG_ATM_TCP
++	X(atmtcp_attach_hook),
++#endif
++
+ 	/* binfmt_aout */
+ 	X(get_write_access),
+ 	X(put_write_access),
+--- ref/arch/i386/config.in	Mon May 13 06:17:23 1996
++++ work/arch/i386/config.in	Mon Jun 10 17:36:00 1996
+@@ -72,6 +72,10 @@
+   endmenu
+ fi
+ 
++if [ "$CONFIG_ATM" = "y" ]; then
++  source drivers/atm/Config.in
++fi
++
+ mainmenu_option next_comment
+ comment 'ISDN subsystem'
+ 
+--- ref/arch/sparc/config.in	Thu Apr 25 12:22:05 1996
++++ work/arch/sparc/config.in	Thu Jul 11 13:50:26 1996
+@@ -100,6 +100,12 @@
+ 	endmenu
+ fi
+ 
++if [ "$CONFIG_ATM" = "y" ]; then
++	if [ "$CONFIG_INET" = "y" ]; then
++		tristate 'ATM over TCP' CONFIG_ATM_TCP n
++	fi
++fi
++
+ source fs/Config.in
+ 
+ mainmenu_option next_comment
+--- ref/drivers/Makefile	Mon Apr 22 09:59:39 1996
++++ work/drivers/Makefile	Mon Jun 10 17:44:29 1996
+@@ -9,7 +9,7 @@
+ 
+ SUB_DIRS     := block char net #streams
+ MOD_SUB_DIRS := $(SUB_DIRS)
+-ALL_SUB_DIRS := $(SUB_DIRS) pci sbus scsi sound cdrom isdn
++ALL_SUB_DIRS := $(SUB_DIRS) atm pci sbus scsi sound cdrom isdn
+ 
+ ifdef CONFIG_PCI
+ SUB_DIRS += pci
+@@ -50,6 +50,11 @@
+   ifeq ($(CONFIG_ISDN),m)
+   MOD_SUB_DIRS += isdn
+   endif
++endif
++
++ifdef CONFIG_ATM
++SUB_DIRS += atm
++MOD_SUB_DIRS += atm
+ endif
+ 
+ ifeq ($(CONFIG_AP1000),y)
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/Config.in	Wed Jul 17 21:59:55 1996
+@@ -0,0 +1,21 @@
++#
++# ATM device configuration
++#
++comment 'ATM drivers'
++if [ "$CONFIG_INET" = "y" ]; then
++  tristate 'ATM over TCP' CONFIG_ATM_TCP n
++fi
++if [ "$CONFIG_PCI" = "y" ]; then
++  tristate 'Efficient Networks ENI155P' CONFIG_ATM_ENI y
++  if [ ! "$CONFIG_ATM_ENI" = "n" ]; then
++    bool '  Enable extended debugging' CONFIG_ATM_ENI_DEBUG n
++  fi
++  tristate 'ZeitNet ZN1221/ZN1225' CONFIG_ATM_ZATM y
++  if [ ! "$CONFIG_ATM_ZATM" = "n" ]; then
++    bool '  Enable extended debugging' CONFIG_ATM_ZATM_DEBUG n
++  fi
++  bool 'Rolfs TI TNETA1570' CONFIG_ATM_TNETA1570 y
++  if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then
++    bool '  Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n
++  fi
++fi
+--- ref/net/Config.in	Wed Jun  5 13:42:27 1996
++++ work/net/Config.in	Wed Jul 31 18:57:53 1996
+@@ -27,4 +27,17 @@
+ if [ "$CONFIG_NETLINK" = "y" ]; then
+   bool 'Routing messages' CONFIG_RTNETLINK
+ fi
++bool 'Asynchronous Transfer Mode (ATM)' CONFIG_ATM y
++if [ "$CONFIG_ATM" = "y" ]; then
++  bool '  Enable single-copy' CONFIG_MMU_HACKS n
++  if [ "$CONFIG_MMU_HACKS" = "y" ]; then
++    bool '    Extended debugging for single-copy' CONFIG_MMU_HACKS_DEBUG y
++  fi
++  if [ "$CONFIG_INET" = "y" ]; then
++    bool '  Classical IP over ATM with ATMARP' CONFIG_ATM_ATMARP y
++    bool '  Classical IP over ATM for PVCs (obsolete)' CONFIG_ATM_CLIP n
++    bool '  Application REQUested IP over ATM' CONFIG_AREQUIPA y
++  fi
++  bool '  LANE support' CONFIG_ATM_LANE y
++fi
+ endmenu
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/Makefile	Thu Jul 18 20:51:29 1996
+@@ -0,0 +1,38 @@
++# File: drivers/atm/Makefile
++#
++# Makefile for the Linux network (ATM) device drivers.
++#
++
++L_TARGET := atm.a
++L_OBJS	 := atmdev_init.o
++M_OBJS	 :=
++
++include ../../.config
++
++ifeq ($(CONFIG_ATM_ENI),y)
++L_OBJS += eni.o suni.o
++endif
++
++ifeq ($(CONFIG_ATM_ZATM),y)
++L_OBJS += zatm.o uPD98402.o
++endif
++
++ifeq ($(CONFIG_ATM_TNETA1570),y)
++L_OBJS += tneta1570.o suni.o
++endif
++
++ifeq ($(CONFIG_ATM_FORE200),y)
++L_OBJS += fore200.o
++endif
++
++ifeq ($(CONFIG_ATM_TCP),y)
++L_OBJS += atmtcp.o
++else
++  ifeq ($(CONFIG_ATM_TCP),m)
++  M_OBJS += atmtcp.o
++  endif
++endif
++
++EXTRA_CFLAGS=-g
++
++include $(TOPDIR)/Rules.make
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/atmdev_init.c	Thu Jul 18 20:50:30 1996
+@@ -0,0 +1,47 @@
++/* drivers/atm/atmdev_init.c - ATM device driver initialization */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#include <linux/config.h>
++
++
++#ifdef CONFIG_ATM_ENI
++extern int eni_detect(void);
++#endif
++#ifdef CONFIG_ATM_ZATM
++extern int zatm_detect(void);
++#endif
++#ifdef CONFIG_ATM_TNETA1570
++extern int tneta1570_detect(void);
++#endif
++#ifdef CONFIG_ATM_FORE200
++extern int fore200_detect(void);
++#endif
++#ifdef CONFIG_ATM_TCP
++extern int atmtcp_init(void);
++#endif
++
++
++int atmdev_init(void)
++{
++	int devs;
++
++	devs = 0;
++#ifdef CONFIG_ATM_ENI
++	devs += eni_detect();
++#endif
++#ifdef CONFIG_ATM_ZATM
++	devs += zatm_detect();
++#endif
++#ifdef CONFIG_ATM_TNETA1570
++	devs += tneta1570_detect();
++#endif
++#ifdef CONFIG_ATM_FORE200
++	devs += fore200_detect();
++#endif
++#ifdef CONFIG_ATM_TCP
++	devs += atmtcp_init();
++#endif
++	return devs;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/atmtcp.c	Thu Jul  4 17:05:57 1996
+@@ -0,0 +1,316 @@
++/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++/* Various enhancements 1996 by Ronald A. McCormick */
++
++
++#include <linux/module.h>
++#include <linux/kernel.h>		/* printk */
++#include <linux/mm.h>			/* verify_area */
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/in.h>
++#include <linux/sched.h>		/* xtime */
++#include <asm/segment.h>		/* set_fs, get_fs, ... */
++#include <asm/byteorder.h>		/* ntohs, htons */
++
++
++#define DEV_LABEL	"atmtcp"
++
++
++#define MAX_VPI_BITS	8
++#define MAX_VCI_BITS	16
++
++
++#define PRIV(c) ((struct private *) ((c)->dev_data))
++
++
++extern int (*atmtcp_attach_hook)(struct socket *sock);
++
++
++/* BIG BUG: we can never get rid of an ATMTCP interface @@@ */
++
++
++/*
++ *  correct compilation of this is implementation-dependent !
++ */
++
++struct atmtcp_hdr {
++	unsigned short	vpi;		
++	unsigned short	vci;
++	unsigned short	length;		/* ... of data part */
++};
++
++struct private {
++	struct atm_dev	*dev;		/* device back pointer */
++	struct socket	*sock;		/* file descriptor */
++};
++
++
++static int atmtcp_open(struct atm_vcc *vcc,short vpi,int vci)
++{
++	int error;
++
++	error = atm_find_ci(vcc,&vpi,&vci);
++	if (error) return error;
++	vcc->vpi = vpi;
++	vcc->vci = vci;
++	if (vpi == ATM_VPI_UNSPEC  || vci == ATM_VCI_UNSPEC) return 0;
++#ifdef DEBUG
++	printk(KERN_DEBUG DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,
++	    vcc->vpi,vcc->vci);
++#endif
++	vcc->flags |= ATM_VF_ADDR | ATM_VF_READY;
++	return 0;
++}
++
++
++int atmtcp_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
++{
++	if (cmd == ATM_SETCIRANGE) {
++		struct atm_cirange ci;
++		struct atm_vcc *walk;
++
++		memcpy_fromfs(&ci,(void *) arg,sizeof(struct atm_cirange));
++		if (ci.vpi_bits == ATM_CI_MAX) ci.vpi_bits = MAX_VPI_BITS;
++		if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS;
++		if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 ||
++		    ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0)
++			return -EINVAL;
++/*
++ * should all this checking be done in net/atm ?
++ */
++		for (walk = dev->vccs; walk; walk = walk->next)
++			if ((walk->vpi >> ci.vpi_bits) ||
++			    (walk->vci >> ci.vci_bits)) return -EBUSY;
++		dev->ci_range = ci;
++		return 0;
++	}
++	return -EINVAL;
++}
++
++
++
++static int do_recv(struct socket *sock,unsigned char *ubuf,int size,
++    int nonblock)
++{
++	struct iovec iov;
++	struct msghdr msg;
++
++	iov.iov_base = ubuf;
++	iov.iov_len = size;
++
++	msg.msg_name = NULL;
++	msg.msg_namelen = 0;
++	msg.msg_control = NULL;
++	msg.msg_iov = &iov;
++	msg.msg_iovlen = 1;
++
++	return sock->ops->recvmsg(sock,&msg,size,nonblock,0,NULL);
++}
++
++
++static int do_send(struct socket *sock,const void *buff,int len,int nonblock)
++{
++	struct iovec iov;
++	struct msghdr msg;
++
++	iov.iov_base = (void *)buff;
++	iov.iov_len = len;
++
++	msg.msg_name = NULL;
++	msg.msg_namelen = 0;
++	msg.msg_control = NULL;
++	msg.msg_iov = &iov;
++	msg.msg_iovlen = 1;
++
++	return sock->ops->sendmsg(sock,&msg,len,nonblock,0);
++}
++
++
++static int atmtcp_setsockopt(struct atm_vcc *vcc,int level,int optname,
++    char *optval,int optlen)
++{
++	return -EINVAL;
++}
++
++
++static int atmtcp_send(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	struct atmtcp_hdr hdr;
++	unsigned long old_fs;
++	int len,size;
++	void *pos;
++
++	if (!PRIV(vcc->dev)->sock) {
++		dev_kfree_skb(skb,FREE_WRITE);
++		return -EINVAL;
++	}
++	hdr.vpi = htons(vcc->vpi);
++	hdr.vci = htons(vcc->vci);
++	old_fs = get_fs();
++	set_fs(get_ds());
++	hdr.length = htons(skb->len);
++	size = do_send(PRIV(vcc->dev)->sock,(char *) &hdr,sizeof(hdr),0);
++	if (size <= 0) {
++		printk(KERN_WARNING "atmtcp_send: PDU lost; errno = %d\n",
++		    -size);
++		dev_kfree_skb(skb,FREE_WRITE);
++		set_fs(old_fs);
++		vcc->stats->tx_err++;
++		return size;
++	}
++	pos = skb->data;
++	len = skb->len;
++	while (len) {
++		size = do_send(PRIV(vcc->dev)->sock,pos,len,0);
++		if (size < 0 && size != -EAGAIN) {
++			printk(KERN_ERR "atmtcp_send: link failure; errno = "
++			    "%d\n",-size);
++			dev_kfree_skb(skb,FREE_WRITE);
++			set_fs(old_fs);
++			vcc->stats->tx_err++;
++			return size;
++		}
++		if (size > 0) {
++			len -= size;
++			pos += size;
++		}
++	}
++	dev_kfree_skb(skb,FREE_WRITE);
++	set_fs(old_fs);
++	vcc->stats->tx++;
++	return 0;
++}
++
++
++static void atmtcp_poll(struct atm_vcc *vcc,int nonblock)
++{
++	struct atm_dev *dev;
++	struct atm_vcc *walk;
++	struct sk_buff *skb;
++	struct atmtcp_hdr hdr;
++	unsigned long old_fs;
++	int size,left;
++	void *pos;
++
++	if (!(dev = vcc->dev) || !PRIV(dev)->sock) return;
++	if (skb_peek(&vcc->recvq)) nonblock = 1;
++	old_fs = get_fs();
++	set_fs(get_ds());
++	while ((size = do_recv(PRIV(dev)->sock,(char*) &hdr,sizeof(hdr),
++	    nonblock) == sizeof(hdr))) {
++		if (!(skb = vcc->peek(vcc,ntohs(hdr.length),NULL))) {
++			printk(KERN_WARNING "atmtcp_poll: peek reject (%d)\n",
++			    ntohs(hdr.length));
++			break;
++		}
++		skb->atm.timestamp = xtime;
++		skb->len = ntohs(hdr.length);
++		skb->free = 1;
++		skb->atm.iovcnt = 0;
++		pos = skb->data;
++		for (left = ntohs(hdr.length); left; left -= size) {
++			size = do_recv(PRIV(dev)->sock,pos,left,0);
++			if (size == -EAGAIN) size = 0;
++			if (size < 0) {
++				printk(KERN_ERR "atmtcp_poll: bad read: %d\n",
++				    size);
++				vcc->stats->rx_err++;
++				break;
++			}
++			pos += size;
++		}
++		for (walk = dev->vccs; walk; walk = walk->next)
++			if (walk->vpi == ntohs(hdr.vpi) && walk->vci ==
++			    ntohs(hdr.vci)) break;
++		if (walk) {
++			skb_queue_tail(&walk->recvq,skb);
++			wake_up(&walk->sleep);
++			nonblock = 1;
++			vcc->stats->rx++;
++		}
++		else {
++			printk(KERN_ERR "atmtcp_poll: bad label %d.%d at itf "
++			    "%d\n",ntohs(hdr.vpi),ntohs(hdr.vci),dev->number);
++			kfree_skb(skb,FREE_READ);
++			vcc->stats->rx_err++;
++		}
++	}
++	set_fs(old_fs);
++	if (size > 0 && size != sizeof(hdr)) {
++		printk(KERN_ERR "atmtcp_poll: bad header (%d)\n",size);
++		vcc->stats->rx_err++;
++	}
++}
++
++
++static struct atmdev_ops ops = {
++	atmtcp_open,
++	NULL,		/* no close */
++	atmtcp_ioctl,
++	NULL,		/* no getsockopt */
++	atmtcp_setsockopt,
++	atmtcp_send,
++	NULL,		/* no direct writes */
++	atmtcp_poll,
++	NULL,		/* no send_oam */
++	NULL,		/* no phy_put */
++	NULL,		/* no phy_get */
++	NULL		/* no feedback */
++};
++
++
++int atmtcp_attach(struct socket *sock)
++{
++	struct private *dsc;
++
++	if (!suser()) return -EPERM;
++	dsc = kmalloc(sizeof(struct private),GFP_KERNEL);
++	if (!dsc) return -ENOMEM;
++	dsc->dev = atm_dev_register(DEV_LABEL,&ops,0);
++	if (!dsc->dev) {
++		kfree(dsc);
++		return -EBUSY;
++	}
++	dsc->dev->dev_data = dsc;
++	dsc->dev->ci_range.vpi_bits = MAX_VPI_BITS;
++	dsc->dev->ci_range.vci_bits = MAX_VCI_BITS;
++	sock->inode->i_count++;
++	dsc->sock = sock;
++	printk(KERN_NOTICE DEV_LABEL "(itf %d): ready\n",dsc->dev->number);
++	MOD_INC_USE_COUNT;
++	return dsc->dev->number;
++}
++
++
++int atmtcp_init(void)
++{
++	printk(KERN_NOTICE DEV_LABEL ": ready (dynamic device creation)\n");
++	atmtcp_attach_hook = atmtcp_attach;
++	return 1;
++}
++
++
++#ifdef MODULE
++
++int init_module(void)
++{
++	(void) atmtcp_init();
++	return 0;
++}
++
++
++void cleanup_module(void)
++{
++	/*
++	 * We currently have no means to detach ATM devices. That'll follow
++	 * a bit later. (Needs careful usage tracking and open/close
++	 * interlocking.)
++	 */
++	atmtcp_attach_hook = NULL;
++}
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/eni.c	Tue Jul 30 16:32:24 1996
+@@ -0,0 +1,1954 @@
++/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/bios32.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++#include <linux/skbuff.h>
++#include <linux/time.h>
++#include <linux/sched.h> /* for xtime */
++#include <linux/delay.h>
++#include <linux/uio.h>
++#include <asm/system.h>
++#include <asm/string.h>
++#include <asm/byteorder.h>
++
++#include "tonga.h"
++#include "midway.h"
++#include "suni.h"
++#include "eni.h"
++
++
++/*
++ * TODO:
++ *
++ * Show stoppers
++ *  none
++ *
++ * Major
++ *  - test ASIC Tonga support
++ *
++ * Minor
++ *  - OAM support
++ *  - fix bugs listed below
++ */
++
++/*
++ * KNOWN BUGS:
++ *
++ * - may run into JK-JK bug and deadlock
++ * - should allocate UBR channel first
++ * - ATM DD interface doesn't support double connect(2) yet
++ * - buffer space allocation algorithm is stupid
++ *   (RX: should be maxSDU+maxdelay*rate
++ *    TX: should be maxSDU+min(maxSDU,maxdelay*rate) )
++ * - adapter memory may fragment badly
++ * - ATM DD interface doesn't know about CLP
++ * - doesn't support OAM cells
++ * - buddy implementation is hyper-inefficient
++ * - eni_put_free may hang if not putting memory fragments that _complete_
++ *   2^n block
++ * - keeps IRQ even if initialization fails
++ */
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++#ifndef CONFIG_ATM_ENI_DEBUG
++
++
++#define NULLCHECK(x)
++
++#define EVENT(s,a,b)
++
++
++static void event_dump(void)
++{
++}
++
++
++#else
++
++
++/* 
++ * NULL pointer checking
++ */
++
++#define NULLCHECK(x) \
++  if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x))
++
++/*
++ * Very extensive activity logging. Greatly improves bug detection speed but
++ * costs a few Mbps if enabled.
++ */
++
++#define EV 64
++
++static const char *ev[EV];
++static unsigned long ev_a[EV],ev_b[EV];
++static int ec = 0;
++
++
++static void EVENT(const char *s,unsigned long a,unsigned long b)
++{
++	ev[ec] = s; 
++	ev_a[ec] = a;
++	ev_b[ec] = b;
++	ec = (ec+1) % EV;
++}
++
++
++static void event_dump(void)
++{
++	int n,i;
++
++	for (n = 0; n < EV; n++) {
++		i = (ec+n) % EV;
++		printk(KERN_NOTICE);
++		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
++	}
++}
++
++
++#endif /* CONFIG_ATM_ENI_DEBUG */
++
++
++/*
++ * NExx   must not be equal at end
++ * EExx   may be equal at end
++ * xxPJOK verify validity of pointer jumps
++ * xxPMOK operating on a circular buffer of "c" words
++ */
++
++#define NEPJOK(a0,a1,b) \
++    ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1))
++#define EEPJOK(a0,a1,b) \
++    ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1))
++#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b)
++#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b)
++
++
++static int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0,
++  backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0,
++  putting = 0;
++
++static struct atm_dev *eni_boards = NULL;
++
++
++/*-------------------------------- utilities --------------------------------*/
++
++
++static void dump_mem(struct eni_dev *eni_dev)
++{
++	int i;
++
++	for (i = 0; i < eni_dev->free_len; i++)
++		printk(KERN_DEBUG "  %d: 0x%lx %d\n",i,
++		    eni_dev->free_list[i].start,
++		    1 << eni_dev->free_list[i].order);
++}
++
++
++static void dump(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++
++	int i;
++
++	eni_dev = ENI_DEV(dev);
++	printk(KERN_NOTICE "Free memory\n");
++	dump_mem(eni_dev);
++	printk(KERN_NOTICE "TX buffers\n");
++	for (i = 0; i < NR_CHAN; i++)
++		if (eni_dev->tx[i].send)
++			printk(KERN_NOTICE "  TX %d @ 0x%08lx: %ld\n",i,
++			    (unsigned long) eni_dev->tx[i].send,
++			    eni_dev->tx[i].words*4);
++	printk(KERN_NOTICE "RX buffers\n");
++	for (i = 0; i < 1024; i++)
++		if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx)
++			printk(KERN_NOTICE "  RX %d @ 0x%08lx: %ld\n",i,
++			    (unsigned long) ENI_VCC(eni_dev->rx_map[i])->recv,
++			    ENI_VCC(eni_dev->rx_map[i])->words*4);
++	printk(KERN_NOTICE "----\n");
++}
++
++
++static void eni_put_free(struct eni_dev *eni_dev,unsigned long start,
++    unsigned long size)
++{
++	struct eni_free *list;
++	int len,order;
++
++	DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size);
++	start += eni_dev->base_diff;
++	list = eni_dev->free_list;
++	len = eni_dev->free_len;
++	while (size) {
++		if (len >= eni_dev->free_list_size) {
++			printk(KERN_CRIT "eni_put_free overflow (0x%lx,%ld)\n",
++			    start,size);
++			break;
++		}
++		for (order = 0; !((start | size) & (1 << order)); order++);
++		if (MID_MIN_BUF_SIZE > (1 << order)) {
++			printk(KERN_CRIT "eni_put_free: order %d too small\n",
++			    order);
++			break;
++		}
++		list[len].start = start;
++		list[len].order = order;
++		len++;
++		start += 1 << order;
++		size -= 1 << order;
++	}
++	eni_dev->free_len = len;
++	/*dump_mem(eni_dev);*/
++}
++
++
++static unsigned long eni_alloc_mem(struct eni_dev *eni_dev,unsigned long *size)
++{
++	struct eni_free *list;
++	unsigned long start;
++	int len,i,order,best_order,index;
++
++	list = eni_dev->free_list;
++	len = eni_dev->free_len;
++	if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE;
++	if (*size > MID_MAX_BUF_SIZE) return 0;
++	for (order = 0; (1 << order) < *size; order++);
++	DPRINTK("trying: %ld->%d\n",*size,order);
++	best_order = 65; /* we don't have more than 2^64 of anything ... */
++	index = 0; /* silence GCC */
++	for (i = 0; i < len; i++)
++		if (list[i].order == order) {
++			best_order = order;
++			index = i;
++			break;
++		}
++		else if (best_order > list[i].order && list[i].order > order) {
++				best_order = list[i].order;
++				index = i;
++			}
++	if (best_order == 65) return 0;
++	start = list[index].start-eni_dev->base_diff;
++	list[index] = list[--len];
++	eni_dev->free_len = len;
++	*size = 1 << order;
++	eni_put_free(eni_dev,start+*size,(1 << best_order)-*size);
++	DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start);
++	for (i = (*size >> 2)-1; i >= 0; i--) /* never leak data */
++		((unsigned long *) start)[i] = 0;
++	/*dump_mem(eni_dev);*/
++	return start;
++}
++
++
++static void eni_free_mem(struct eni_dev *eni_dev,unsigned long start,
++    unsigned long size)
++{
++	struct eni_free *list;
++	int len,i,order;
++
++	start += eni_dev->base_diff;
++	list = eni_dev->free_list;
++	len = eni_dev->free_len;
++	for (order = -1; size; order++) size >>= 1;
++	DPRINTK("eni_free_mem: 0x%lx+0x%lx (order %d)\n",start,size,order);
++	for (i = 0; i < len; i++)
++		if (list[i].start == (start^(1 << order)) &&
++		    list[i].order == order) {
++			DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i,
++			    list[i].start,start,1 << order,list[i].order,order);
++			list[i] = list[--len];
++			start &= ~(1 << order);
++			order++;
++			i = -1;
++			continue;
++		}
++	if (len >= eni_dev->free_list_size) {
++		printk(KERN_ALERT "eni_free_mem overflow (0x%lx,%d)\n",start,
++		    order);
++		return;
++	}
++	list[len].start = start;
++	list[len].order = order;
++	eni_dev->free_len = len+1;
++	/*dump_mem(eni_dev);*/
++}
++
++
++/*----------------------------------- RX ------------------------------------*/
++
++
++#define ENI_VCC_NOS ((struct atm_vcc *) 1)
++
++
++static __u32 fetch(struct atm_vcc *vcc,int i)
++{
++	return ntohl(ENI_VCC(vcc)->recv[(ENI_VCC(vcc)->descr+i+1) &
++	    (ENI_VCC(vcc)->words-1)]);		           /* ^ skip descr */
++}
++
++
++static void rx_ident_err(struct atm_vcc *vcc)
++{
++	struct atm_dev *dev;
++	struct eni_vcc *eni_vcc;
++
++	dev = vcc->dev;
++	/* immediately halt adapter */
++	ENI_DEV(dev)->reg[MID_MC_S] &= ~(MID_DMA_ENABLE | MID_TX_ENABLE |
++	  MID_RX_ENABLE);
++	/* dump useful information */
++	eni_vcc = ENI_VCC(vcc);
++	printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident "
++	    "mismatch\n",dev->number);
++	printk(KERN_ALERT "  VCI %d, rxing %d, words %ld\n",vcc->vci,
++	    eni_vcc->rxing,eni_vcc->words);
++	printk(KERN_ALERT "  host descr 0x%08lx, rx pos 0x%08lx, descr value "
++	    "0x%08lx\n",eni_vcc->descr,eni_vcc->rx_pos,
++	    eni_vcc->recv[eni_vcc->descr]);
++	printk(KERN_ALERT "  last 0x%08lx, servicing %d\n",
++	    (unsigned long) eni_vcc->last,eni_vcc->servicing);
++	EVENT("---dump ends here---\n",0,0);
++	printk(KERN_NOTICE "---recent events---\n");
++	event_dump();
++	ENI_DEV(dev)->fast = NULL; /* really stop it */
++	ENI_DEV(dev)->slow = NULL;
++	skb_queue_head_init(&ENI_DEV(dev)->rx_queue);
++}
++
++
++static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb,
++    unsigned long skip,unsigned long size,unsigned long eff)
++{
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	unsigned long dma_rd,dma_wr;
++	unsigned long dma[RX_DMA_BUF*2];
++	unsigned long paddr,here;
++	int i,j;
++
++	eni_dev = ENI_DEV(vcc->dev);
++	eni_vcc = ENI_VCC(vcc);
++	paddr = 0; /* GCC, shut up */
++	if (skb) {
++		paddr = (unsigned long) skb->data;
++		if (paddr & 3)
++			printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has "
++			    "mis-aligned RX data (0x%lx)\n",vcc->dev->number,
++			    vcc->vci,paddr);
++		skb->atm.size = size+skip;
++		    /* PDU plus descriptor */
++		skb->atm.vcc = vcc;
++	}
++	j = 0;
++	if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */
++		here = (eni_vcc->descr+skip) & (eni_vcc->words-1);
++		dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci
++		    << MID_DMA_VCI_SHIFT) | MID_DT_JK;
++		j++;
++	}
++	here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1);
++	if (!eff) size += skip;
++	else {
++		unsigned long words;
++
++		if (!size) {
++			DPRINTK("strange things happen ...\n");
++			EVENT("strange things happen ... (skip=%ld,eff=%ld)\n",
++			    size,eff);
++		}
++		words = eff;
++		if (paddr & 15) {
++			unsigned long init;
++
++			init = 4-((paddr & 15) >> 2);
++			if (init > words) init = words;
++			dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
++			    (vcc->vci << MID_DMA_VCI_SHIFT);
++			dma[j++] = paddr;
++			paddr += init << 2;
++			words -= init;
++		}
++		if (words & ~7) {
++#if 0 /* works with *some* PCI chipsets ... */
++			dma[j++] = MID_DT_8W | ((words >> 3) <<
++			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
++			    MID_DMA_VCI_SHIFT);
++			dma[j++] = paddr;
++			paddr += (words & ~7) << 2;
++			words &= 7;
++#else
++			dma[j++] = MID_DT_4W | ((words >> 2) <<
++			    MID_DMA_COUNT_SHIFT) | (vcc->vci <<
++			    MID_DMA_VCI_SHIFT);
++			dma[j++] = paddr;
++			paddr += (words & ~3) << 2;
++			words &= 3;
++#endif
++		}
++		if (words) {
++			dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT)
++			    | (vcc->vci << MID_DMA_VCI_SHIFT);
++			dma[j++] = paddr;
++		}
++	}
++	if (size != eff) {
++		dma[j++] = (here << MID_DMA_COUNT_SHIFT) |
++		    (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK;
++		j++;
++	}
++	if (!j || j > 2*RX_DMA_BUF) {
++		printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n");
++		if (skb) kfree_skb(skb,FREE_READ);
++		return -1;
++	}
++	dma[j-2] |= MID_DMA_END;
++	j = j >> 1;
++	dma_wr = eni_dev->reg[MID_DMA_WR_RX];
++	dma_rd = eni_dev->reg[MID_DMA_RD_RX];
++	if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */
++		printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n",
++		    vcc->dev->number);
++		if (skb) kfree_skb(skb,FREE_READ);
++		return -1;
++	}
++        for (i = 0; i < j; i++) {
++		eni_dev->rx_dma[dma_wr*2] = dma[i*2];
++		eni_dev->rx_dma[dma_wr*2+1] = dma[i*2+1];
++		dma_wr = (dma_wr+1) & (NR_DMA_RX-1);
++        }
++	if (skb) {
++		skb->atm.pos = eni_vcc->descr+size+1;
++		skb_queue_tail(&eni_dev->rx_queue,skb);
++eni_vcc->last = skb;
++rx_enqueued++;
++	}
++	eni_vcc->descr = here;
++	eni_dev->reg[MID_DMA_WR_RX] = dma_wr;
++	return 0;
++}
++
++
++static void discard(struct atm_vcc *vcc,unsigned long size)
++{
++	struct eni_vcc *eni_vcc;
++
++	eni_vcc = ENI_VCC(vcc);
++	EVENT("discard (size=%ld)\n",size,0);
++	while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0);
++	    /* could do a full fallback, but that might be more expensive */
++	if (eni_vcc->rxing) eni_vcc->last->atm.pos += size+1;
++	else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1);
++}
++
++
++/*
++ * TODO: should check whether direct copies (without DMA setup, dequeuing on
++ * interrupt, etc.) aren't much faster for AAL0
++ */
++
++static int rx_aal0(struct atm_vcc *vcc)
++{
++	struct eni_vcc *eni_vcc;
++	unsigned long descr;
++	unsigned long length;
++	struct sk_buff *skb;
++
++	DPRINTK(">rx_aal0\n");
++	eni_vcc = ENI_VCC(vcc);
++	descr = eni_vcc->recv[eni_vcc->descr];
++	if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
++		rx_ident_err(vcc);
++		return 1;
++	}
++	if (descr & MID_RED_T) {
++		DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
++		    vcc->dev->number);
++		length = 0;
++		vcc->stats->rx_err++;
++	}
++	else {
++		length = ATM_CELL_SIZE-1; /* no HEC */
++	}
++	if (!length) skb = NULL;
++	else skb = vcc->peek(vcc,length,fetch);
++	if (!skb) {
++		discard(vcc,length >> 2);
++		return 0;
++	}
++	skb->free = 1; /* not very useful ... */
++	skb->len = length;
++	skb->atm.timestamp = eni_vcc->timestamp;
++	DPRINTK("got len %ld\n",length);
++	if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1;
++	eni_vcc->rxing++;
++	return 0;
++}
++
++
++static int rx_aal5(struct atm_vcc *vcc)
++{
++	struct eni_vcc *eni_vcc;
++	unsigned long descr;
++	unsigned long size,eff,length;
++	struct sk_buff *skb;
++
++	EVENT("rx_aal5\n",0,0);
++	DPRINTK(">rx_aal5\n");
++	eni_vcc = ENI_VCC(vcc);
++	descr = eni_vcc->recv[eni_vcc->descr];
++	if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) {
++		rx_ident_err(vcc);
++		return 1;
++	}
++	if (descr & (MID_RED_T | MID_RED_CRC_ERR)) {
++		if (descr & MID_RED_T) {
++			EVENT("empty cell (descr=0x%08lx)\n",descr,0);
++			DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n",
++			    vcc->dev->number);
++			size = 0;
++		}
++		else {
++			static unsigned long silence = 0;
++
++			if (jiffies > silence) {
++				printk(KERN_WARNING DEV_LABEL "(itf %d): "
++				    "discarding PDU(s) with CRC error\n",
++				    vcc->dev->number);
++				silence = jiffies+2*HZ;
++			}
++			size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
++			EVENT("CRC error (descr=0x%08lx,size=%ld)\n",descr,
++			    size);
++		}
++		eff = length = 0;
++		vcc->stats->rx_err++;
++	}
++	else {
++		size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
++		DPRINTK("size=%ld\n",size);
++		length = eni_vcc->recv[(eni_vcc->descr+size-1) &
++		    (eni_vcc->words-1)] & 0xffff; /* -trailer(2)+header(1) */
++		if (length && length <= (size << 2)-8 && length <=
++		  ATM_MAX_AAL5_PDU) eff = (length+3) >> 2;
++		else {				 /* ^ trailer length (8) */
++			EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr,
++			    length);
++			printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU "
++			    "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n",
++			    vcc->dev->number,vcc->vci,length,size << 2,descr);
++#if 0
++printk("0x%lx[0x%lx] (0x%lx,%ld) size %ld len %ld\n",
++  (unsigned long) eni_vcc->recv,eni_vcc->descr,(unsigned long) eni_vcc,
++  eni_vcc->words,size << 2,length);
++#endif
++			length = eff = 0;
++			vcc->stats->rx_err++;
++		}
++	}
++	if (!eff) skb = NULL;
++	else {
++		skb = vcc->peek(vcc,eff << 2,fetch);
++		if (!skb) {
++			EVENT("peek reject (eff << 2=%ld)\n",eff << 2,0);
++			DPRINTK(DEV_LABEL "(itf %d): peek reject for %ld "
++			    "bytes\n",vcc->dev->number,eff << 2);
++		}
++	}
++	if (!skb) {
++		discard(vcc,size);
++		return 0;
++	}
++	skb->free = 1; /* not very useful ... */
++	skb->len = length;
++	DPRINTK("got len %ld\n",length);
++	if (do_rx_dma(vcc,skb,1,size,eff)) return 1;
++	eni_vcc->rxing++;
++	return 0;
++}
++
++
++static inline int rx_vcc(struct atm_vcc *vcc)
++{
++	volatile unsigned long *vci_dsc;
++	struct eni_vcc *eni_vcc;
++	unsigned long tmp;
++
++	eni_vcc = ENI_VCC(vcc);
++	vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*4;
++	EVENT("rx_vcc(1)\n",0,0);
++	while (eni_vcc->descr != (tmp = (vci_dsc[1] & MID_VCI_DESCR) >>
++	    MID_VCI_DESCR_SHIFT)) {
++		EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n",
++		    eni_vcc->descr,tmp);
++		DPRINTK("CB_DESCR %ld REG_DESCR %ld\n",ENI_VCC(vcc)->descr,
++		    ((vci_dsc[1] & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT));
++		if (ENI_VCC(vcc)->rx(vcc)) return 1;
++	}
++	/* clear IN_SERVICE flag */
++	vci_dsc[0] &= ~MID_VCI_IN_SERVICE;
++	/*
++	 * If new data has arrived between evaluating the while condition and
++	 * clearing IN_SERVICE, we wouldn't be notified until additional data
++	 * follows. So we have to loop again to be sure.
++	 */
++	EVENT("rx_vcc(3)\n",0,0);
++	while (ENI_VCC(vcc)->descr != (tmp = (vci_dsc[1] & MID_VCI_DESCR) >>
++	    MID_VCI_DESCR_SHIFT)) {
++		EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n",
++		    eni_vcc->descr,tmp);
++		DPRINTK("CB_DESCR %ld REG_DESCR %ld\n",ENI_VCC(vcc)->descr,
++		    ((vci_dsc[1] & MID_VCI_DESCR) >> MID_VCI_DESCR_SHIFT));
++		if (ENI_VCC(vcc)->rx(vcc)) return 1;
++	}
++	return 0;
++}
++
++
++static void poll_rx(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++	struct atm_vcc *curr;
++
++	eni_dev = ENI_DEV(dev);
++	while ((curr = eni_dev->fast)) {
++		EVENT("poll_tx.fast\n",0,0);
++		if (rx_vcc(curr)) return;
++		eni_dev->fast = ENI_VCC(curr)->next;
++		ENI_VCC(curr)->next = ENI_VCC_NOS;
++		ENI_VCC(curr)->servicing--;
++	}
++	while ((curr = eni_dev->slow)) {
++		EVENT("poll_tx.slow\n",0,0);
++		if (rx_vcc(curr)) return;
++		eni_dev->slow = ENI_VCC(curr)->next;
++		ENI_VCC(curr)->next = ENI_VCC_NOS;
++		ENI_VCC(curr)->servicing--;
++	}
++}
++
++
++static void get_service(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++	struct atm_vcc *vcc;
++	unsigned long vci;
++
++	DPRINTK(">get_service\n");
++	eni_dev = ENI_DEV(dev);
++	while (eni_dev->reg[MID_SERV_WRITE] != eni_dev->serv_read) {
++		vci = eni_dev->service[eni_dev->serv_read];
++		eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1);
++		vcc = eni_dev->rx_map[vci & 1023];
++		if (!vcc) {
++			printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not "
++			    "found\n",dev->number,vci);
++			continue; /* nasty but we try to go on anyway */
++			/* @@@ nope, doesn't work */
++		}
++		EVENT("getting from service\n",0,0);
++		if (ENI_VCC(vcc)->next != ENI_VCC_NOS) {
++			EVENT("double service\n",0,0);
++			DPRINTK("Grr, servicing VCC %ld twice\n",vci);
++			continue;
++		}
++		ENI_VCC(vcc)->timestamp = xtime;
++		ENI_VCC(vcc)->next = NULL;
++		if (vcc->qos.rxtp.traffic_class == ATM_CBR) {
++			if (eni_dev->fast)
++				ENI_VCC(eni_dev->last_fast)->next = vcc;
++			else eni_dev->fast = vcc;
++			eni_dev->last_fast = vcc;
++		}
++		else {
++			if (eni_dev->slow)
++				ENI_VCC(eni_dev->last_slow)->next = vcc;
++			else eni_dev->slow = vcc;
++			eni_dev->last_slow = vcc;
++		}
++putting++;
++		ENI_VCC(vcc)->servicing++;
++	}
++}
++
++
++static void dequeue_rx(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	struct atm_vcc *vcc;
++	struct sk_buff *skb;
++	volatile unsigned long *vci_dsc;
++	int first;
++
++	eni_dev = ENI_DEV(dev);
++	first = 1;
++	while (1) {
++		skb = skb_dequeue(&eni_dev->rx_queue);
++		if (!skb) {
++			if (first) {
++				DPRINTK(DEV_LABEL "(itf %d): RX but not "
++				    "rxing\n",dev->number);
++				EVENT("nothing to dequeue\n",0,0);
++			}
++			break;
++		}
++		EVENT("dequeued (size=%d,pos=0x%lx)\n",skb->atm.size,
++		    skb->atm.pos);
++/*printk("DMA@0x%X\n",eni_dev->reg[MID_DMA_ADDR]);*/
++rx_dequeued++;
++		vcc = skb->atm.vcc;
++		eni_vcc = ENI_VCC(vcc);
++		first = 0;
++		vci_dsc = eni_dev->vci+(vcc->vci << 2);
++		if (!EEPMOK(eni_vcc->rx_pos,skb->atm.size,(vci_dsc[1] &
++		    MID_VCI_READ) >> MID_VCI_READ_SHIFT,eni_vcc->words)) {
++			EVENT("requeuing\n",0,0);
++			skb_queue_head(&eni_dev->rx_queue,skb);
++			break;
++		}
++		eni_vcc->rxing--;
++		eni_vcc->rx_pos = skb->atm.pos & (eni_vcc->words-1);
++		if (!skb->len) kfree_skb(skb,FREE_READ);
++		else {
++			EVENT("pushing (len=%d)\n",skb->len,0);
++			if (vcc->aal == ATM_AAL0)
++				*(unsigned long *) skb->data =
++				    ntohl(*(unsigned long *) skb->data);
++			vcc->push(vcc,skb);
++			pushed++;
++		}
++		vcc->stats->rx++;
++	}
++	wake_up(&eni_dev->rx_wait);
++}
++
++
++static int open_rx_first(struct atm_vcc *vcc)
++{
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	unsigned long size;
++
++	DPRINTK("open_rx_first\n");
++	eni_dev = ENI_DEV(vcc->dev);
++	eni_vcc = ENI_VCC(vcc);
++	eni_vcc->rx = NULL;
++	if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
++	size = vcc->qos.rxtp.max_sdu*3; /* @@@ improve this */
++	eni_vcc->recv = (volatile unsigned long *) eni_alloc_mem(eni_dev,&size);
++	DPRINTK("rx at 0x%lx\n",(unsigned long) eni_vcc->recv);
++	eni_vcc->words = size >> 2;
++	if (!eni_vcc->recv) return -ENOBUFS;
++	eni_vcc->rx = vcc->aal == ATM_AAL5 ? rx_aal5 : rx_aal0;
++	eni_vcc->descr = 0;
++	eni_vcc->rx_pos = 0;
++	eni_vcc->rxing = 0;
++	eni_vcc->servicing = 0;
++	eni_vcc->next = ENI_VCC_NOS;
++	return 0;
++}
++
++
++static int open_rx_second(struct atm_vcc *vcc)
++{
++	volatile unsigned long *here;
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	unsigned long size;
++	int order;
++
++	DPRINTK("open_rx_second\n");
++	eni_dev = ENI_DEV(vcc->dev);
++	eni_vcc = ENI_VCC(vcc);
++	if (!eni_vcc->rx) return 0;
++	/* set up VCI descriptor */
++	here = eni_dev->vci+(vcc->vci << 2);
++	DPRINTK("loc 0x%x\n",eni_vcc->recv-eni_dev->ram);
++	size = eni_vcc->words >> 8;
++	for (order = -1; size; order++) size >>= 1;
++	here[1] = 0; /* descr, read = 0 */
++	here[2] = 0; /* write, state, count = 0 */
++	if (eni_dev->rx_map[vcc->vci])
++		printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already "
++		    "in use\n",vcc->dev->number,vcc->vci);
++	eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */
++	here[0] = ((vcc->aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) <<
++	    MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE |
++	    (((eni_vcc->recv-eni_dev->ram) >> MID_LOC_SKIP) <<
++	    MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT);
++	return 0;
++}
++
++
++static void close_rx(struct atm_vcc *vcc)
++{
++	volatile unsigned long *here;
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	unsigned long flags,tmp;
++
++	eni_vcc = ENI_VCC(vcc);
++	if (!eni_vcc->rx) return;
++	eni_dev = ENI_DEV(vcc->dev);
++	if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
++		here = eni_dev->vci+(vcc->vci << 2);
++		/* block receiver */
++		*here = (*here & ~MID_VCI_MODE) | (MID_MODE_TRASH <<
++		    MID_VCI_MODE_SHIFT);
++		/* wait for receiver to become idle */
++		udelay(27);
++		/* discard pending cell */
++		*here &= ~MID_VCI_IN_SERVICE;
++		/* don't accept any new ones */
++		eni_dev->rx_map[vcc->vci] = NULL;
++		/* wait for RX queue to drain */
++		DPRINTK("eni_close: waiting for RX ...\n");
++		EVENT("RX closing\n",0,0);
++		save_flags(flags);
++		cli();
++		while (eni_vcc->rxing || eni_vcc->servicing) {
++			EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing,
++			    eni_vcc->servicing);
++			printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing,
++			    eni_vcc->rxing);
++			sleep_on(&eni_dev->rx_wait);
++		}
++		while (eni_vcc->rx_pos != (tmp = eni_dev->vci[vcc->vci*4+1] &
++		    MID_VCI_READ) >> MID_VCI_READ_SHIFT) {
++			EVENT("drain discard (host 0x%lx, nic 0x%lx)\n",
++			    eni_vcc->rx_pos,tmp);
++			printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%lx\n",
++			    eni_vcc->rx_pos,tmp);
++			sleep_on(&eni_dev->rx_wait);
++		}
++		restore_flags(flags);
++	}
++	eni_free_mem(eni_dev,(unsigned long) eni_vcc->recv,
++	    eni_vcc->words << 2);
++	eni_vcc->rx = NULL;
++}
++
++
++static int start_rx(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++
++	eni_dev = ENI_DEV(dev);
++	eni_dev->rx_map = (struct atm_vcc **) get_free_page(GFP_KERNEL);
++	if (!eni_dev->rx_map) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
++		    dev->number);
++		free_page((unsigned long) eni_dev->free_list);
++		return -ENOMEM;
++	}
++	memset(eni_dev->rx_map,0,PAGE_SIZE);
++	eni_dev->fast = eni_dev->last_fast = NULL;
++	eni_dev->slow = eni_dev->last_slow = NULL;
++	eni_dev->rx_wait = NULL;
++	skb_queue_head_init(&eni_dev->rx_queue);
++	eni_dev->serv_read = eni_dev->reg[MID_SERV_WRITE];
++	eni_dev->reg[MID_DMA_WR_RX] = 0;
++	return 0;
++}
++
++
++/*----------------------------------- TX ------------------------------------*/
++
++
++enum enq_res { enq_ok,enq_next,enq_jam };
++
++
++static inline void put_dma(int chan,unsigned long *dma,int *j,
++    unsigned long paddr,unsigned long size)
++{
++	unsigned long init;
++
++	DPRINTK("put_dma: 0x%lx+0x%lx\n",paddr,size);
++	EVENT("put_dma: 0x%lx+0x%lx\n",paddr,size);
++	if (paddr & 3)
++		printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr);
++#if 0 /* don't complain - all cases that pass are handled somewhere else */
++	if (size & 3)
++		printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size);
++#endif
++	size = (size+3) >> 2;
++/* fixme: extra DMA descr when misaligned short PDU @@@ */
++	if (paddr & 31) {
++		init = 8-((paddr & 31) >> 2);
++		if (init > size) init = size;
++		dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) |
++		    (chan << MID_DMA_CHAN_SHIFT);
++		dma[(*j)++] = paddr;
++		paddr += init << 2;
++		size -= init;
++	}
++	if (size & ~7) {
++		dma[(*j)++] = MID_DT_8W | ((size >> 3) << MID_DMA_COUNT_SHIFT)
++		    | (chan << MID_DMA_CHAN_SHIFT);
++		dma[(*j)++] = paddr;
++		paddr += (size & ~7) << 2;
++		size &= 7;
++	}
++	if (size) {
++		dma[(*j)++] = MID_DT_WORD | (size << MID_DMA_COUNT_SHIFT) |
++		    (chan << MID_DMA_CHAN_SHIFT);
++		dma[(*j)++] = paddr;
++	}
++}
++
++
++static enum enq_res do_tx(struct sk_buff *skb)
++{
++	struct atm_vcc *vcc;
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	struct eni_tx *tx;
++	unsigned long dma_rd,dma_wr;
++	unsigned long dma[TX_DMA_BUF*2];
++	unsigned long size; /* in words */
++	int aal5,dma_size,i,j;
++
++	DPRINTK(">do_tx\n");
++	NULLCHECK(skb);
++	EVENT("do_tx: skb=0x%lx, %d bytes\n",(unsigned long) skb,skb->len);
++	vcc = skb->atm.vcc;
++	NULLCHECK(vcc);
++	eni_dev = ENI_DEV(vcc->dev);
++	NULLCHECK(eni_dev);
++	eni_vcc = ENI_VCC(vcc);
++	tx = eni_vcc->tx;
++	NULLCHECK(tx);
++	if ((unsigned long) skb->data & 3)
++		printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned "
++		    "TX data\n",vcc->dev->number,vcc->vci);
++	/*
++	 * Potential future IP speedup: make hard_header big enough to put
++	 * segmentation descriptor directly into PDU. Saves: 4 slave writes,
++	 * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-)
++	 */
++
++	/* check space in buffer */
++	if (!(aal5 = vcc->aal == ATM_AAL5))
++		size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE;
++			/* cell without HEC plus segmentation header (includes
++			   four-byte cell header) */
++	else {
++		size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1;
++			/* add AAL5 trailer */
++		size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE;
++						/* add segmentation header */
++	}
++	if (!NEPMOK(tx->tx_pos,size+TX_GAP,
++	    eni_dev->reg[MID_TX_RDPTR(tx->index)],tx->words)) {
++	    /* leave some space for "too close" */
++		DPRINTK(DEV_LABEL "(itf %d): TX full (size %ld)\n",
++		    vcc->dev->number,size);
++		return enq_next;
++	}
++	/* check DMA */
++	dma_wr = eni_dev->reg[MID_DMA_WR_TX];
++	dma_rd = eni_dev->reg[MID_DMA_RD_TX];
++	dma_size = 2; /* JK for descriptor and final fill */
++DPRINTK("iovcnt = %d\n",skb->atm.iovcnt);
++	if (!skb->atm.iovcnt) dma_size += 3;
++	else dma_size += 3*skb->atm.iovcnt;
++	if (dma_size > TX_DMA_BUF) {
++		printk(KERN_WARNING DEV_LABEL "(itf %d): needs %d DMA entries "
++		    "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF);
++	}
++	DPRINTK("dma_wr is %ld, tx_pos is %ld\n",dma_wr,tx->tx_pos);
++	if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) <
++	     dma_size) {
++		printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n",
++		    vcc->dev->number);
++		return enq_jam;
++	}
++	/* prepare DMA queue entries */
++	j = 0;
++	dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) <<
++	     MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
++	     MID_DT_JK;
++	j++;
++	if (!skb->atm.iovcnt)
++		if (aal5)
++			put_dma(tx->index,dma,&j,(unsigned long) skb->data,
++			    skb->len);
++		else put_dma(tx->index,dma,&j,(unsigned long) skb->data+4,
++			    skb->len-4);
++	else {
++DPRINTK("doing direct send\n");
++		for (i = 0; i < skb->atm.iovcnt; i++)
++			put_dma(tx->index,dma,&j,(unsigned long)
++			    ((struct iovec *) skb->data)[i].iov_base,
++			    ((struct iovec *) skb->data)[i].iov_len);
++	}
++	/* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */
++	dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) <<
++	     MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) |
++	     MID_DMA_END | MID_DT_JK;
++	j++;
++	DPRINTK("DMA at end: %d\n",j);
++	/* store frame */
++	tx->send[tx->tx_pos] = (MID_SEG_TX_ID << MID_SEG_ID_SHIFT) |
++	    (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) |
++	    (tx->resolution << MID_SEG_RATE_SHIFT) |
++	    (size/(ATM_CELL_PAYLOAD/4));
++/*printk("dsc = 0x%08lx\n",tx->send[tx->tx_pos]);*/
++	tx->send[(tx->tx_pos+1) & (tx->words-1)] = (vcc->vci <<
++	    MID_SEG_VCI_SHIFT) | (aal5 ? 0 : (skb->data[3] & 0xf));
++	DPRINTK("size: %ld, len:%ld\n",size,skb->len);
++	if (aal5)
++		tx->send[(tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1)] =
++		    skb->len;
++	j = j >> 1;
++	for (i = 0; i < j; i++) {
++		eni_dev->tx_dma[dma_wr*2] = dma[i*2];
++		eni_dev->tx_dma[dma_wr*2+1] = dma[i*2+1];
++		dma_wr = (dma_wr+1) & (NR_DMA_TX-1);
++	}
++	skb->atm.pos = tx->tx_pos;
++	skb->atm.size = size;
++	ENI_VCC(vcc)->txing += size;
++	tx->tx_pos = (tx->tx_pos+size) & (tx->words-1);
++	DPRINTK("dma_wr set to %ld, tx_pos is now %ld\n",dma_wr,tx->tx_pos);
++	eni_dev->reg[MID_DMA_WR_TX] = dma_wr;
++	skb_queue_tail(&eni_dev->tx_queue,skb);
++queued++;
++	return enq_ok;
++}
++
++
++static void poll_tx(struct atm_dev *dev)
++{
++	struct eni_tx *tx;
++	struct sk_buff *skb;
++	enum enq_res res;
++	int i;
++
++	DPRINTK(">poll_tx\n");
++	for (i = NR_CHAN-1; i >= 0; i--) {
++		tx = &ENI_DEV(dev)->tx[i];
++		if (tx->send)
++			while ((skb = skb_dequeue(&tx->backlog))) {
++				res = do_tx(skb);
++				if (res != enq_ok) {
++					DPRINTK("re-queuing TX PDU\n");
++					skb_queue_head(&tx->backlog,skb);
++requeued++;
++					if (res == enq_jam) return;
++					else break;
++				}
++			}
++	}
++}
++
++
++static void dequeue_tx(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++	struct atm_vcc *vcc;
++	struct sk_buff *skb;
++	struct eni_tx *tx;
++
++	NULLCHECK(dev);
++	eni_dev = ENI_DEV(dev);
++	NULLCHECK(eni_dev);
++	while ((skb = skb_dequeue(&eni_dev->tx_queue))) {
++		vcc = skb->atm.vcc;
++		NULLCHECK(vcc);
++		tx = ENI_VCC(vcc)->tx;
++		NULLCHECK(ENI_VCC(vcc)->tx);
++		DPRINTK("dequeue_tx: next 0x%lx curr 0x%lx\n",skb->atm.pos,
++		    eni_dev->reg[MID_TX_DESCRSTART(tx->index)]);
++		if (ENI_VCC(vcc)->txing < tx->words && skb->atm.pos ==
++		    eni_dev->reg[MID_TX_DESCRSTART(tx->index)]) {
++			skb_queue_head(&eni_dev->tx_queue,skb);
++			break;
++		}
++		ENI_VCC(vcc)->txing -= skb->atm.size;
++		if (vcc->pop) vcc->pop(vcc,skb);
++		else dev_kfree_skb(skb,FREE_WRITE);
++		vcc->stats->tx++;
++		wake_up(&eni_dev->tx_wait);
++dma_complete++;
++	}
++}
++
++
++static int alloc_tx(struct eni_dev *eni_dev,int *pcr,int min,int max,int ubr)
++{
++	static const int pre_div[] = { 4,16,128,2048 };
++	    /* 2^(((x+2)^2-(x+2))/2+1) */
++	int i,pre,res;
++
++	DPRINTK("in pcr: %d/%d%s\n",min,max,ubr ? " (ubr)" : "");
++	for (i = !ubr; i < NR_CHAN; i++)
++		if (!eni_dev->tx[i].send) break;
++	if (i == NR_CHAN) return -EAGAIN;
++	if (min)
++		if (min == ATM_MAX_PCR) pre = res = 0;
++		else {
++			int div;
++
++			for (pre = 0; pre < 3; pre++)
++				if (TS_CLOCK/pre_div[pre]/64 <= min) break;
++			div = pre_div[pre]*min;
++			DPRINTK("min div %d\n",div);
++			res = TS_CLOCK/div-1;
++		}
++	else {
++		int div;
++
++		if (max > eni_dev->tx_bw && !ubr) max = eni_dev->tx_bw;
++		for (pre = 3; pre >= 0; pre--)
++			if (TS_CLOCK/pre_div[pre]/64 > max) break;
++		if (pre < 3) pre++; /* else fail later */
++		div = pre_div[pre]*max;
++		DPRINTK("max div %d\n",div);
++		res = (TS_CLOCK+div-1)/div-1;
++	}
++	if (res < 0) res = 0;
++	if (res > MID_SEG_MAX_RATE) res = MID_SEG_MAX_RATE;
++	eni_dev->tx[i].prescaler = pre;
++	eni_dev->tx[i].resolution = res;
++	eni_dev->tx[i].pcr = *pcr = TS_CLOCK/pre_div[pre]/(res+1);
++	DPRINTK("out pcr: %d (%d:%d) <%d,%d>\n",*pcr,pre,res,min,max);
++	DPRINTK("got chan %d\n",i);
++	if ((min && *pcr < min) || (max && *pcr > max)) return -EINVAL;
++	if (*pcr > eni_dev->tx_bw && !ubr) return -EAGAIN;
++	return i;
++}
++
++
++static int open_tx_first(struct atm_vcc *vcc)
++{
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	unsigned long size,mem;
++	int tx_ind,pcr,order;
++
++	eni_dev = ENI_DEV(vcc->dev);
++	eni_vcc = ENI_VCC(vcc);
++	eni_vcc->tx = NULL;
++	if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
++	eni_vcc->txing = 0;
++	if (vcc->qos.txtp.traffic_class != ATM_UBR)
++		size = vcc->qos.txtp.max_sdu*3; /* @@@ improve */
++	else {
++		if (eni_dev->ubr) {
++			eni_vcc->tx = eni_dev->ubr;
++			return 0;
++		}
++		size = UBR_BUFFER;
++		vcc->qos.txtp.min_pcr = ATM_MAX_PCR;
++		vcc->qos.txtp.max_pcr = 0;
++	}
++	DPRINTK("get_tx\n");
++	mem = eni_alloc_mem(eni_dev,&size);
++	if (!mem) return -ENOBUFS;
++	if ((tx_ind = alloc_tx(eni_dev,&pcr,vcc->qos.txtp.min_pcr,
++	    vcc->qos.txtp.max_pcr,vcc->qos.txtp.traffic_class == ATM_UBR)) < 0)
++	    {
++		eni_free_mem(eni_dev,mem,size);
++		return tx_ind;
++	}
++	if (vcc->qos.txtp.traffic_class == ATM_UBR)
++		eni_dev->ubr = &eni_dev->tx[tx_ind];
++	else eni_dev->tx_bw -= pcr;
++	vcc->qos.txtp.min_pcr = vcc->qos.txtp.max_pcr = pcr;
++	eni_dev->tx[tx_ind].send = (volatile unsigned long *) mem;
++	eni_dev->tx[tx_ind].words = size >> 2;
++	skb_queue_head_init(&eni_dev->tx[tx_ind].backlog);
++	size >>= 10;
++	for (order = -1; size; order++) size >>= 1;
++	eni_dev->reg[MID_TX_PLACE(tx_ind)] = (order << MID_SIZE_SHIFT) |
++	    ((eni_dev->tx[tx_ind].send-eni_dev->ram) >> MID_LOC_SKIP);
++	eni_dev->tx[tx_ind].tx_pos = eni_dev->reg[MID_TX_DESCRSTART(tx_ind)] &
++	    MID_DESCR_START;
++/*printk("mp 0x%lx tp 0x%lx\n",(unsigned long) eni_dev->tx[tx_ind].send,
++  eni_dev->tx[tx_ind].tx_pos);*/
++	eni_vcc->tx = &eni_dev->tx[tx_ind];
++	return 0;
++}
++
++
++static int open_tx_second(struct atm_vcc *vcc)
++{
++	return 0; /* nothing to do */
++}
++
++
++static void close_tx(struct atm_vcc *vcc)
++{
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	unsigned long flags;
++
++	eni_vcc = ENI_VCC(vcc);
++	if (!eni_vcc->tx) return;
++	eni_dev = ENI_DEV(vcc->dev);
++	/* wait for TX queue to drain */
++	DPRINTK("eni_close: waiting for TX ...\n");
++	save_flags(flags);
++	cli();
++	while (skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing) {
++		DPRINTK("%d TX left\n",eni_vcc->txing);
++		sleep_on(&eni_dev->tx_wait);
++	}
++	restore_flags(flags);
++#if 0
++	if (skb_peek(&eni_vcc->tx->backlog))
++		printk(KERN_CRIT DEV_LABEL "SKBs in BACKLOG !!!\n");
++#endif
++	if (eni_vcc->tx != eni_dev->ubr) {
++		eni_free_mem(eni_dev,(unsigned long) eni_vcc->tx->send,
++		    eni_vcc->tx->words << 2);
++		eni_vcc->tx->send = NULL;
++		eni_dev->tx_bw += eni_vcc->tx->pcr;
++	}
++	eni_vcc->tx = NULL;
++}
++
++
++static int start_tx(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++	int i;
++
++	eni_dev = ENI_DEV(dev);
++	eni_dev->lost = 0;
++	eni_dev->tx_bw = ATM_OC3_PCR;
++	eni_dev->tx_wait = NULL;
++	eni_dev->ubr = NULL;
++	skb_queue_head_init(&eni_dev->tx_queue);
++	eni_dev->reg[MID_DMA_WR_TX] = 0;
++	for (i = 0; i < NR_CHAN; i++) {
++		eni_dev->tx[i].send = NULL;
++		eni_dev->tx[i].index = i;
++	}
++	return 0;
++}
++
++
++/*--------------------------------- common ----------------------------------*/
++
++
++static void foo(void)
++{
++printk(KERN_INFO
++  "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n"
++  "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n",
++  tx_complete,dma_complete,queued,requeued,submitted,backlogged,
++  rx_enqueued,rx_dequeued,putting,pushed);
++if (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost);
++}
++
++static void misc_int(struct atm_dev *dev,unsigned long reason)
++{
++	struct eni_dev *eni_dev;
++
++	DPRINTK(">misc_int\n");
++	eni_dev = ENI_DEV(dev);
++	if (reason & MID_STAT_OVFL) {
++		EVENT("stat overflow\n",0,0);
++		eni_dev->lost += eni_dev->reg[MID_STAT] & MID_OVFL_TRASH;
++	}
++	if (reason & MID_SUNI_INT) {
++		EVENT("SUNI int\n",0,0);
++		dev->phy->interrupt(dev);
++		foo();
++	}
++	if (reason & MID_TX_IDENT_MISM) {
++		printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident "
++		    "mismatch\n",dev->number);
++		EVENT("---dump ends here---\n",0,0);
++		printk(KERN_NOTICE "---recent events---\n");
++		event_dump();
++	}
++	if (reason & MID_TX_DMA_OVFL) {
++		printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA "
++		    "overflow\n",dev->number);
++		EVENT("---dump ends here---\n",0,0);
++		printk(KERN_NOTICE "---recent events---\n");
++		event_dump();
++	}
++}
++
++
++static void eni_int(int irq,void *dev_id,struct pt_regs *regs)
++{
++	struct atm_dev *dev;
++	struct eni_dev *eni_dev;
++	unsigned long reason;
++
++	DPRINTK(">eni_int\n");
++	dev = dev_id;
++	eni_dev = ENI_DEV(dev);
++	while ((reason = eni_dev->reg[MID_ISA])) {
++		DPRINTK(DEV_LABEL ": int 0x%lx\n",reason);
++		if (reason & MID_RX_DMA_COMPLETE) {
++			EVENT("INT: RX DMA complete, starting dequeue_rx\n",
++			    0,0);
++			dequeue_rx(dev);
++			EVENT("dequeue_rx done, starting poll_rx\n",0,0);
++			poll_rx(dev);
++			EVENT("poll_rx done\n",0,0);
++			/* poll_tx ? */
++		}
++		if (reason & MID_SERVICE) {
++			EVENT("INT: service, starting get_service\n",0,0);
++			get_service(dev);
++			EVENT("get_service done, starting poll_rx\n",0,0);
++			poll_rx(dev);
++			EVENT("poll_rx done\n",0,0);
++		}
++ 		if (reason & MID_TX_DMA_COMPLETE) {
++			EVENT("INT: TX DMA COMPLETE\n",0,0);
++			dequeue_tx(dev);
++		}
++		if (reason & MID_TX_COMPLETE) {
++			EVENT("INT: TX COMPLETE\n",0,0);
++tx_complete++;
++			wake_up(&eni_dev->tx_wait);
++			poll_tx(dev);
++			/* poll_rx ? */
++		}
++		if (reason & (MID_STAT_OVFL | MID_SUNI_INT | MID_TX_IDENT_MISM
++		    | MID_TX_DMA_OVFL)) {
++			EVENT("misc interrupt\n",0,0);
++			misc_int(dev,reason);
++		}
++	}
++}
++
++
++/*--------------------------------- entries ---------------------------------*/
++
++
++static const char *media_name[] = {
++    "MMF", "SMF", "MMF", "03?", /*  0- 3 */
++    "UTP", "05?", "06?", "07?", /*  4- 7 */
++    "TAXI","09?", "10?", "11?", /*  8-11 */
++    "12?", "13?", "14?", "15?", /* 12-15 */
++    "MMF", "SMF", "18?", "19?", /* 16-19 */
++    "UTP", "21?", "22?", "23?", /* 20-23 */
++    "24?", "25?", "26?", "27?", /* 24-27 */
++    "28?", "29?", "30?", "31?"  /* 28-31 */
++};
++
++
++#define SET_SEPROM \
++  ({ if (!error && !pci_error) { \
++    pci_error = pcibios_write_config_byte(eni_dev->bus,eni_dev->dev_fn, \
++      PCI_TONGA_CTRL,tonga); \
++    udelay(10); /* 10 usecs */ \
++  } })
++#define GET_SEPROM \
++  ({ if (!error && !pci_error) { \
++    pci_error = pcibios_read_config_byte(eni_dev->bus,eni_dev->dev_fn, \
++      PCI_TONGA_CTRL,&tonga); \
++    udelay(10); /* 10 usecs */ \
++  } })
++
++
++static int get_esi_asic(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++	unsigned char tonga;
++	int error,failed,pci_error;
++	int address,i,j;
++
++	eni_dev = ENI_DEV(dev);
++	error = pci_error = 0;
++	tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK;
++	SET_SEPROM;
++	for (i = 0; i < ESI_LEN && !error && !pci_error; i++) {
++		/* start operation */
++		tonga |= SEPROM_DATA;
++		SET_SEPROM;
++		tonga |= SEPROM_CLK;
++		SET_SEPROM;
++		tonga &= ~SEPROM_DATA;
++		SET_SEPROM;
++		tonga &= ~SEPROM_CLK;
++		SET_SEPROM;
++		/* send address */
++		address = ((i+SEPROM_ESI_BASE) << 1)+1;
++		for (j = 7; j >= 0; j--) {
++			tonga = (address >> j) & 1 ? tonga | SEPROM_DATA :
++			    tonga & ~SEPROM_DATA;
++			SET_SEPROM;
++			tonga |= SEPROM_CLK;
++			SET_SEPROM;
++			tonga &= ~SEPROM_CLK;
++			SET_SEPROM;
++		}
++		/* get ack */
++		tonga |= SEPROM_DATA;
++		SET_SEPROM;
++		tonga |= SEPROM_CLK;
++		SET_SEPROM;
++		GET_SEPROM;
++		failed = tonga & SEPROM_DATA;
++		tonga &= ~SEPROM_CLK;
++		SET_SEPROM;
++		tonga |= SEPROM_DATA;
++		SET_SEPROM;
++		if (failed) error = -EIO;
++		else {
++			dev->esi[i] = 0;
++			for (j = 7; j >= 0; j--) {
++				dev->esi[i] <<= 1;
++				tonga |= SEPROM_DATA;
++				SET_SEPROM;
++				tonga |= SEPROM_CLK;
++				SET_SEPROM;
++				GET_SEPROM;
++				if (tonga & SEPROM_DATA) dev->esi[i] |= 1;
++				tonga &= ~SEPROM_CLK;
++				SET_SEPROM;
++				tonga |= SEPROM_DATA;
++				SET_SEPROM;
++			}
++			/* get ack */
++			tonga |= SEPROM_DATA;
++			SET_SEPROM;
++			tonga |= SEPROM_CLK;
++			SET_SEPROM;
++			GET_SEPROM;
++			if (!(tonga & SEPROM_DATA)) error = -EIO;
++			tonga &= ~SEPROM_CLK;
++			SET_SEPROM;
++			tonga |= SEPROM_DATA;
++			SET_SEPROM;
++		}
++		/* stop operation */
++		tonga &= ~SEPROM_DATA;
++		SET_SEPROM;
++		tonga |= SEPROM_CLK;
++		SET_SEPROM;
++		tonga |= SEPROM_DATA;
++		SET_SEPROM;
++	}
++	if (pci_error) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI (%s)\n",
++		    dev->number,pcibios_strerror(pci_error));
++		error = -EIO;
++	}
++	return error;
++}
++
++
++#undef SET_SEPROM
++#undef GET_SEPROM
++
++
++static int get_esi_fpga(struct atm_dev *dev,unsigned long base)
++{
++	struct eni_dev *eni_dev;
++	struct midway_eprom *eprom;
++	int i;
++
++	eprom = (struct midway_eprom *) (base+EPROM_SIZE-sizeof(struct
++	    midway_eprom));
++	eni_dev = ENI_DEV(dev);
++	for (i = 0; i < ESI_LEN; i++)
++		dev->esi[i] = eprom->mac[(i & ~3) | (3-(i & 3))];
++	return 0;
++}
++
++
++static int eni_init(struct atm_dev *dev)
++{
++	struct midway_eprom *eprom;
++	struct eni_dev *eni_dev;
++	unsigned int real_base,base;
++	unsigned short command;
++	unsigned char revision;
++	int error,i,last;
++
++	DPRINTK(">eni_init\n");
++	dev->ci_range.vpi_bits = 0;
++	dev->ci_range.vci_bits = NR_VCI_LD;
++	eni_dev = ENI_DEV(dev);
++	if ((error = pcibios_read_config_word(eni_dev->bus,eni_dev->dev_fn,
++	    PCI_COMMAND,&command)) || (error = pcibios_read_config_dword(
++	    eni_dev->bus,eni_dev->dev_fn,PCI_BASE_ADDRESS_0,&real_base)) ||
++	    (error = pcibios_read_config_byte(eni_dev->bus,eni_dev->dev_fn,
++	    PCI_INTERRUPT_LINE,&eni_dev->irq)) || (error =
++	    pcibios_read_config_byte(eni_dev->bus,eni_dev->dev_fn,
++	    PCI_REVISION_ID,&revision))) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): init error %s\n",
++		    dev->number,pcibios_strerror(error));
++		return -EINVAL;
++	}
++	real_base &= MEM_VALID; /* strip flags */
++	if ((error = pcibios_write_config_word(eni_dev->bus,eni_dev->dev_fn,
++	    PCI_COMMAND,PCI_COMMAND_MEMORY | (eni_dev->asic ?
++	    PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory (%s)"
++		    "\n",dev->number,pcibios_strerror(error));
++		return error;
++	}
++	printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",
++	    dev->number,revision,real_base,eni_dev->irq);
++	if (!(base = (unsigned long) vremap(real_base,MAP_MAX_SIZE))) {
++		printk("\n");
++		printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page "
++		    "mapping\n",dev->number);
++		return error;
++	}
++	eni_dev->base_diff = real_base-base;
++	/* id may not be present in ASIC Tonga boards - check this @@@ */
++	if (!eni_dev->asic) {
++		eprom = (struct midway_eprom *) (base+EPROM_SIZE-sizeof(struct
++		    midway_eprom));
++		if (eprom->magic != ENI155_MAGIC) {
++			printk("\n");
++			printk(KERN_ERR KERN_ERR DEV_LABEL "(itf %d): bad "
++			    "magic - expected 0x%X, got 0x%lX\n",dev->number,
++			    ENI155_MAGIC,eprom->magic);
++			return -EINVAL;
++		}
++	}
++	eni_dev->phy = (volatile unsigned long *) (base+PHY_BASE);
++	eni_dev->reg = (volatile unsigned long *) (base+REG_BASE);
++	eni_dev->ram = (volatile unsigned long *) (base+RAM_BASE);
++	last = (MAP_MAX_SIZE-RAM_BASE)/4;
++	for (i = last-RAM_INCREMENT; i >= 0;
++	    i -= RAM_INCREMENT) {
++		eni_dev->ram[i] = 0x55555555;
++		if (eni_dev->ram[i] != 0x55555555) last = i;
++		else {
++			eni_dev->ram[i] = 0xAAAAAAAA;
++			if (eni_dev->ram[i] != 0xAAAAAAAA) last = i;
++			else eni_dev->ram[i] = i;
++		}
++	}
++	for (i = 0; i < last; i += RAM_INCREMENT)
++		if (eni_dev->ram[i] != i) break;
++	eni_dev->mem = i << 2;
++	memset((void *) eni_dev->ram,0,eni_dev->mem);
++	/* TODO: should shrink allocation now */
++	printk("mem=%dkB (",eni_dev->mem >> 10);
++	/* TODO: check for non-SUNI, check for TAXI ? */
++	if (!(eni_dev->reg[MID_RES_ID_MCON] & 0x200) != !eni_dev->asic) {
++		printk(")\n");
++		printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%0lx\n",
++		    dev->number,eni_dev->reg[MID_RES_ID_MCON]);
++		return -EINVAL;
++	}
++	error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base);
++	if (error) return error;
++	for (i = 0; i < ESI_LEN; i++)
++		printk("%s%02X",i ? "-" : "",dev->esi[i]);
++	printk(")\n");
++	printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number,
++	    eni_dev->reg[MID_RES_ID_MCON] & 0x200 ? "ASIC" : "FPGA",
++	    media_name[eni_dev->reg[MID_RES_ID_MCON] & DAUGTHER_ID]);
++	return suni_init(dev);
++}
++
++
++static int eni_start(struct atm_dev *dev)
++{
++	struct eni_dev *eni_dev;
++	volatile unsigned long *buf;
++	unsigned long buffer_mem;
++	int error;
++
++	DPRINTK(">eni_start\n");
++	eni_dev = ENI_DEV(dev);
++	if (request_irq(eni_dev->irq,&eni_int,0,DEV_LABEL,dev)) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
++		    dev->number,eni_dev->irq);
++		return -EAGAIN;
++	}
++	/* @@@ should release IRQ on error */
++	if ((error = pcibios_write_config_word(eni_dev->bus,eni_dev->dev_fn,
++	    PCI_COMMAND,PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
++	    (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+"
++		    "master (%s)\n",dev->number,pcibios_strerror(error));
++		return error;
++	}
++	if ((error = pcibios_write_config_byte(eni_dev->bus,eni_dev->dev_fn,
++	    PCI_TONGA_CTRL,END_SWAP_DMA))) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap "
++		    "(%s)\n",dev->number,pcibios_strerror(error));
++		return error;
++	}
++	/* determine addresses of internal tables */
++	eni_dev->vci = eni_dev->ram;
++	eni_dev->rx_dma = eni_dev->ram+NR_VCI*4;
++	eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*2;
++	eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*2;
++	buf = eni_dev->service+NR_SERVICE;
++	DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n",
++	    (unsigned long) eni_dev->vci,(unsigned long) eni_dev->rx_dma,
++	    (unsigned long) eni_dev->tx_dma,(unsigned long) eni_dev->service,
++	    buf);
++	/* initialize memory management */
++	buffer_mem = eni_dev->mem-((unsigned long) buf-
++	    (unsigned long) eni_dev->ram);
++	eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2;
++	eni_dev->free_list = (struct eni_free *) kmalloc(
++	    sizeof(struct eni_free)*(eni_dev->free_list_size+1),GFP_KERNEL);
++	if (!eni_dev->free_list) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n",
++		    dev->number);
++		return -ENOMEM;
++	}
++	eni_dev->free_len = 0;
++	eni_put_free(eni_dev,(unsigned long) buf,buffer_mem);
++	memset((void *) eni_dev->vci,0,16*NR_VCI); /* clear VCI table */
++	/*
++	 * byte_addr  free (k)
++	 * 0x00000000     512  VCI table
++	 * 0x00004000	  496  RX DMA
++	 * 0x00005000	  492  TX DMA
++	 * 0x00006000	  488  service list
++	 * 0x00007000	  484  buffers
++	 * 0x00080000	    0  end (512kB)
++	 */
++	eni_dev->reg[MID_IE] = 0xffffffff;
++#if 0
++MID_TX_COMPLETE_0 | MID_TX_DMA_OVFL |
++	    MID_TX_IDENT_MISM | MID_RX_DMA_COMPLETE | MID_TX_DMA_COMPLETE |
++	    MID_SERVICE | MID_SUNI_INT | MID_STAT_OVFL; /* enable interrupts */
++#endif
++	error = start_tx(dev);
++	if (error) return error;
++	error = start_rx(dev);
++	if (error) return error;
++	error = dev->phy->start(dev);
++	if (error) return error;
++	eni_dev->reg[MID_MC_S] |= (1 << MID_INT_SEL_SHIFT) |
++	    MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE;
++	    /* Tonga uses SBus INTReq1 */
++	(void) eni_dev->reg[MID_ISA]; /* clear Midway interrupts */
++	return 0;
++}
++
++
++static void eni_close(struct atm_vcc *vcc)
++{
++	DPRINTK(">eni_close\n");
++	if (!ENI_VCC(vcc)) return;
++	vcc->flags &= ~ATM_VF_READY;
++	close_rx(vcc);
++	close_tx(vcc);
++	DPRINTK("eni_close: done waiting\n");
++	/* deallocate memory */
++	kfree(ENI_VCC(vcc));
++	ENI_VCC(vcc) = NULL;
++	vcc->flags &= ~ATM_VF_ADDR;
++	/*foo();*/
++}
++
++
++static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci)
++{
++	struct atm_vcc *walk;
++
++	if (*vpi == ATM_VPI_ANY) *vpi = 0;
++	if (*vci == ATM_VCI_ANY) {
++		for (*vci = ATM_NOT_RSV_VCI; *vci < NR_VCI; (*vci)++) {
++			if (vcc->qos.rxtp.traffic_class != ATM_NONE &&
++			    ENI_DEV(vcc->dev)->rx_map[*vci])
++				continue;
++			if (vcc->qos.txtp.traffic_class != ATM_NONE) {
++				for (walk = vcc->dev->vccs; walk;
++				    walk = walk->next)
++					if ((walk->flags & ATM_VF_ADDR) &&
++					    walk->vci == *vci &&
++					    walk->qos.txtp.traffic_class !=
++					    ATM_NONE)
++						break;
++				if (walk) continue;
++			}
++			break;
++		}
++		return *vci == NR_VCI ? -EADDRINUSE : 0;
++	}
++	if (*vci == ATM_VCI_UNSPEC) return 0;
++	if (vcc->qos.rxtp.traffic_class != ATM_NONE &&
++	    ENI_DEV(vcc->dev)->rx_map[*vci])
++		return -EADDRINUSE;
++	if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
++	for (walk = vcc->dev->vccs; walk; walk = walk->next)
++		if ((walk->flags & ATM_VF_ADDR) && walk->vci == *vci &&
++		    walk->qos.txtp.traffic_class != ATM_NONE)
++			return -EADDRINUSE;
++	return 0;
++}
++
++
++static int eni_open(struct atm_vcc *vcc,short vpi,int vci)
++{
++	struct eni_dev *eni_dev;
++	struct eni_vcc *eni_vcc;
++	int error;
++
++	DPRINTK(">eni_open\n");
++	EVENT("eni_open\n",0,0);
++	if (!(vcc->flags & ATM_VF_PARTIAL)) ENI_VCC(vcc) = NULL;
++	eni_dev = ENI_DEV(vcc->dev);
++#if 1 /* set to 0 to test atm_find_ci (get_ci usually is faster) */
++	error = get_ci(vcc,&vpi,&vci);
++	if (error) return error;
++#else
++	error = atm_find_ci(vcc,&vpi,&vci);
++	if (error) return error;
++#endif
++	vcc->vpi = vpi;
++	vcc->vci = vci;
++	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
++		vcc->flags |= ATM_VF_ADDR;
++	if (vcc->aal != ATM_AAL0 && vcc->aal != ATM_AAL5) return -EINVAL;
++	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
++	    vcc->vci);
++	if (!(vcc->flags & ATM_VF_PARTIAL)) {
++		eni_vcc = kmalloc(sizeof(struct eni_vcc),GFP_KERNEL);
++		if (!eni_vcc) return -ENOMEM;
++		ENI_VCC(vcc) = eni_vcc;
++		eni_vcc->tx = NULL; /* for eni_close after open_rx */
++		if ((error = open_rx_first(vcc))) {
++			eni_close(vcc);
++			return error;
++		}
++		if ((error = open_tx_first(vcc))) {
++			eni_close(vcc);
++			return error;
++		}
++	}
++	if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
++	if ((error = open_rx_second(vcc))) {
++		eni_close(vcc);
++		return error;
++	}
++	if ((error = open_tx_second(vcc))) {
++		eni_close(vcc);
++		return error;
++	}
++	vcc->flags |= ATM_VF_READY;
++	/* should power down SUNI while !ref_count @@@ */
++	return 0;
++}
++
++
++static int eni_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
++{
++	if (cmd == ENI_MEMDUMP) {
++		dump(dev);
++		return 0;
++	}
++	if (cmd == ATM_SETCIRANGE) {
++		struct atm_cirange ci;
++
++		memcpy_fromfs(&ci,(void *) arg,sizeof(struct atm_cirange));
++		if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) &&
++		    (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX))
++		    return 0;
++		return -EINVAL;
++	}
++	if (!dev->phy->ioctl) return -EINVAL;
++	return dev->phy->ioctl(dev,cmd,arg);
++}
++
++
++static int eni_getsockopt(struct atm_vcc *vcc,int level,int optname,
++    char *optval,int *optlen)
++{
++#ifdef CONFIG_MMU_HACKS
++
++static const struct atm_buffconst bctx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
++static const struct atm_buffconst bcrx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
++
++#else
++
++static const struct atm_buffconst bctx = { 4,0,4,0,0,0 };
++static const struct atm_buffconst bcrx = { 4,0,4,0,0,0 };
++
++#endif
++	int error;
++
++	if (level == SOL_AAL && (optname == SO_BCTXOPT ||
++	    optname == SO_BCRXOPT)) {
++		if (get_fs_long(optlen) < sizeof(struct atm_buffconst))
++			return -EINVAL;
++		put_fs_long(sizeof(struct atm_buffconst),optlen);
++		error = verify_area(VERIFY_WRITE,optval,
++		    sizeof(struct atm_buffconst));
++		if (error) return error;
++		memcpy_tofs(optval,optname == SO_BCTXOPT ? &bctx : &bcrx,
++		    sizeof(struct atm_buffconst));
++		return 0;
++	}
++	return -EINVAL;
++}
++
++
++static int eni_setsockopt(struct atm_vcc *vcc,int level,int optname,
++    char *optval,int optlen)
++{
++	return -EINVAL;
++}
++
++
++static int eni_send(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	unsigned long flags;
++
++	DPRINTK(">eni_send\n");
++	if (!ENI_VCC(vcc)->tx) {
++		dev_kfree_skb(skb,FREE_WRITE);
++		return -EINVAL;
++	}
++	if (!skb) {
++		printk(KERN_CRIT "!skb in eni_send ?\n");
++		dev_kfree_skb(skb,FREE_WRITE);
++		return -EINVAL;
++	}
++	if (vcc->aal == ATM_AAL0) {
++		if (skb->len != ATM_CELL_SIZE-1) {
++			dev_kfree_skb(skb,FREE_WRITE);
++			return -EINVAL;
++		}
++		*(unsigned long *) skb->data = htonl(*(unsigned long *)
++		    skb->data);
++	}
++submitted++;
++	skb->atm.vcc = vcc;
++	save_flags(flags);
++	cli(); /* brute force */
++	if (skb_peek(&ENI_VCC(vcc)->tx->backlog) || do_tx(skb)) {
++		skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb);
++		backlogged++;
++	}
++	restore_flags(flags);
++	return 0;
++}
++
++
++static int eni_sg_send(struct atm_vcc *vcc,unsigned long start,
++    unsigned long size)
++{
++	return vcc->aal == ATM_AAL5 && !((start | size) & 3);
++		/* don't tolerate misalignment */
++}
++
++
++static void eni_phy_put(struct atm_dev *dev,unsigned char value,
++    unsigned long addr)
++{
++	ENI_DEV(dev)->phy[addr] = value;
++}
++
++
++
++static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr)
++{
++	volatile unsigned tmp; /* force 32 bit access */
++
++	tmp = ENI_DEV(dev)->phy[addr];
++	return tmp;
++}
++
++
++static const struct atmdev_ops ops = {
++	eni_open,
++	eni_close,
++	eni_ioctl,
++	eni_getsockopt,
++	eni_setsockopt,
++	eni_send,
++	eni_sg_send,
++	NULL,			/* no poll */
++	NULL,			/* no send_oam */
++	eni_phy_put,
++	eni_phy_get,
++	NULL			/* no feedback */
++};
++
++
++int eni_detect(void)
++{
++	struct atm_dev *dev;
++	struct eni_dev *eni_dev;
++	int devs,type,index;
++
++	if (!pcibios_present()) {
++		printk(KERN_ERR DEV_LABEL " driver but no PCI BIOS ?\n");
++		return 0;
++	}
++	eni_dev = (struct eni_dev *) kmalloc(sizeof(struct eni_dev),
++	    GFP_KERNEL);
++	if (!eni_dev) return -ENOMEM;
++	devs = 0;
++	for (type = 0; type < 2; type++) {
++		index = 0;
++		while (!pcibios_find_device(PCI_VENDOR_ID_EF,type ?
++		    PCI_DEVICE_ID_EF_ATM_ASIC : PCI_DEVICE_ID_EF_ATM_FPGA,
++		    index,&eni_dev->bus,&eni_dev->dev_fn)) {
++			dev = atm_dev_register(DEV_LABEL,&ops,0);
++			if (!dev) break;
++			ENI_DEV(dev) = eni_dev;
++			eni_dev->asic = type;
++			if (eni_init(dev) || eni_start(dev)) {
++				atm_dev_deregister(dev);
++				break;
++			}
++			eni_dev->more = eni_boards;
++			eni_boards = dev;
++			index++;
++			devs++;
++			eni_dev = (struct eni_dev *) kmalloc(sizeof(struct
++			    eni_dev),GFP_KERNEL);
++			if (!eni_dev) break;
++		}
++	}
++	return devs;
++}
++
++
++#ifdef MODULE
++
++int init_module(void)
++{
++	if (!eni_detect()) {
++		printk(KERN_ERROR DEV_LABEL ": no adapter found\n");
++		return -ENXIO;
++	}
++	MOD_INC_USE_COUNT;
++	return 0;
++}
++
++
++void cleanup_module(void)
++{
++	/*
++	 * Well, there's no way to get rid of the driver yet, so we don't
++	 * have to clean up, right ? :-)
++	 */
++}
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/eni.h	Wed Jul 31 19:32:14 1996
+@@ -0,0 +1,113 @@
++/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++ 
++#ifndef DRIVER_ATM_ENI_H
++#define DRIVER_ATM_ENI_H
++
++#include <linux/atmioc.h>
++
++#define ENI_MEMDUMP	_IOW('a',ATMIOC_SARPRV,struct atmif_sioc)
++						/* printk memory map */
++
++
++#ifdef __KERNEL__
++
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++#include <linux/skbuff.h>
++#include <linux/time.h>
++
++#include "midway.h"
++
++
++#define KERNEL_OFFSET	0xC0000000	/* kernel 0x0 is at phys 0xC0000000 */
++#define DEV_LABEL	"eni"
++
++#define UBR_BUFFER	(128*1024)	/* UBR buffer size */
++
++#define RX_DMA_BUF	 5		/* burst and skip a few things */
++#define TX_DMA_BUF	60		/* should be enough for 64 kB */
++
++
++struct eni_free {
++	unsigned long start;		/* counting in bytes */
++	int order;
++};
++
++struct eni_tx {
++	volatile unsigned long *send;	/* base, NULL if unused */
++	int prescaler;			/* shaping prescaler */
++	int resolution;			/* shaping divider */
++	unsigned long tx_pos;		/* current TX write position */
++	unsigned long words;		/* size of TX queue */
++	int index;			/* TX channel number */
++	int pcr;			/* peak cell rate */
++	struct sk_buff_head backlog;	/* queue of waiting TX buffers */
++};
++
++struct eni_vcc {
++	int (*rx)(struct atm_vcc *vcc);	/* RX function, NULL if none */
++	volatile unsigned long *recv;	/* receive buffer */
++	unsigned long words;		/* its size in words */
++	unsigned long descr;		/* next descriptor (RX) */
++	unsigned long rx_pos;		/* current RX descriptor pos */
++	struct eni_tx *tx;		/* TXer, NULL if none */
++	int rxing;			/* number of pending PDUs */
++	int servicing;			/* number of waiting VCs (0 or 1) */
++	int txing;			/* number of pending TX cells/PDUs */
++	struct timeval timestamp;	/* for RX timing */
++	struct atm_vcc *next;		/* next pending RX */
++	struct sk_buff *last;		/* last PDU being DMAed (used to carry
++					   discard information) */
++};
++
++struct eni_dev {
++	/*-------------------------------- base pointers into Midway address
++					   space */
++	volatile unsigned long *phy;	/* PHY interface chip registers */
++	volatile unsigned long *reg;	/* register base */
++	volatile unsigned long *ram;	/* RAM base */
++	volatile unsigned long *vci;	/* VCI table */
++	volatile unsigned long *rx_dma;	/* RX DMA queue */
++	volatile unsigned long *tx_dma;	/* TX DMA queue */
++	volatile unsigned long *service;/* service list */
++	/*-------------------------------- TX part */
++	struct eni_tx tx[NR_CHAN];	/* TX channels */
++	struct eni_tx *ubr;		/* UBR channel */
++	struct sk_buff_head tx_queue;	/* PDUs currently being TX DMAed*/
++	struct wait_queue *tx_wait;	/* for close */
++	int tx_bw;			/* remaining bandwidth */
++	/*-------------------------------- RX part */
++	unsigned long serv_read;	/* host service read index */
++	struct atm_vcc *fast,*last_fast;/* queues of VCCs with pending PDUs */
++	struct atm_vcc *slow,*last_slow;
++	struct atm_vcc **rx_map;	/* for fast lookups */
++	struct sk_buff_head rx_queue;	/* PDUs currently being RX-DMAed */
++	struct wait_queue *rx_wait;	/* for close */
++	/*-------------------------------- statistics */
++	unsigned long lost;		/* number of lost cells (RX) */
++	/*-------------------------------- memory management */
++	unsigned long base_diff;	/* virtual-real base address */
++	int free_len;			/* free list length */
++	struct eni_free *free_list;	/* free list */
++	int free_list_size;		/* maximum size of free list */
++	/*-------------------------------- ENI links */
++	struct atm_dev *more;		/* other ENI devices */
++	/*-------------------------------- general information */
++	int mem;			/* RAM on board (in bytes) */
++	int asic;			/* PCI interface type, 0 for FPGA */
++	unsigned char irq;		/* IRQ */
++	unsigned char bus;		/* PCI stuff */
++	unsigned char dev_fn;
++};
++
++
++#define ENI_DEV(d) ((struct eni_dev *) (d)->dev_data)
++#define ENI_VCC(d) ((struct eni_vcc *) (d)->dev_data)
++
++#endif /* __KERNEL__ */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/midway.h	Mon Jun 10 17:36:06 1996
+@@ -0,0 +1,265 @@
++/* drivers/atm/midway.h - Efficient Networks Midway (SAR) description */
++ 
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef DRIVERS_ATM_MIDWAY_H
++#define DRIVERS_ATM_MIDWAY_H
++
++
++#define NR_VCI		1024		/* number of VCIs */
++#define NR_VCI_LD	10		/* log2(NR_VCI) */
++#define NR_DMA_RX	512		/* RX DMA queue entries */
++#define NR_DMA_TX	512		/* TX DMA queue entries */
++#define NR_SERVICE	NR_VCI		/* service list size */
++#define NR_CHAN		8		/* number of TX channels */
++#define TS_CLOCK	25000000	/* traffic shaper clock (cell/sec) */
++
++#define MAP_MAX_SIZE	0x00400000	/* memory window for max config */
++#define EPROM_SIZE	0x00010000
++#define	MEM_VALID	0xffc00000	/* mask base address with this */
++#define PHY_BASE	0x00020000	/* offset of PHY register are */
++#define REG_BASE	0x00040000	/* offset of Midway register area */
++#define RAM_BASE	0x00200000	/* offset of RAM area */
++#define RAM_INCREMENT	0x00008000	/* probe for RAM every 128kB (32 kw) */
++
++#define MID_VCI_BASE	RAM_BASE
++#define MID_DMA_RX_BASE	(MID_VCI_BASE+NR_VCI*16)
++#define MID_DMA_TX_BASE	(MID_DMA_RX_BASE+NR_DMA_RX*8)
++#define MID_SERVICE_BASE (MID_DMA_TX_BASE+NR_DMA_TX*8)
++#define MID_FREE_BASE	(MID_SERVICE_BASE+NR_SERVICE*4)
++
++#define MAC_LEN 6 /* atm.h */
++
++#define MID_MIN_BUF_SIZE (1024)		/*   1 kB is minimum */
++#define MID_MAX_BUF_SIZE (128*1024)	/* 128 kB is maximum */
++
++#define RX_DESCR_SIZE	1		/* RX PDU descr is 1 longword */
++#define TX_DESCR_SIZE	2		/* TX PDU descr is 2 longwords */
++#define AAL5_TRAILER	(ATM_AAL5_TRAILER/4) /* AAL5 trailer is 2 longwords */
++
++#define TX_GAP		8		/* TX buffer gap (words) */
++
++/*
++ * Midway Reset/ID
++ *
++ * All values read-only. Writing to this register resets Midway chip.
++ */
++
++#define MID_RES_ID_MCON	0x00		/* Midway Reset/ID */
++
++#define MID_ID		0xf0000000	/* Midway version */
++#define MID_SHIFT	24
++#define MID_MOTHER_ID	0x00000700	/* mother board id */
++#define MID_MOTHER_SHIFT 8
++#define MID_CON_TI	0x00000080	/* 0: normal ctrl; 1: SABRE */
++#define MID_CON_SUNI	0x00000040	/* 0: UTOPIA; 1: SUNI */
++#define MID_CON_V6	0x00000020	/* 0: non-pipel UTOPIA (required iff
++					   !CON_SUNI; 1: UTOPIA */
++#define DAUGTHER_ID	0x0000001f	/* daugther board id */
++
++/*
++ * Interrupt Status Acknowledge, Interrupt Status & Interrupt Enable
++ */
++
++#define MID_ISA		0x01		/* Interrupt Status Acknowledge */
++#define MID_IS		0x02		/* Interrupt Status */
++#define MID_IE		0x03		/* Interrupt Enable */
++
++#define MID_TX_COMPLETE_7 0x00010000	/* channel N completed a PDU */
++#define MID_TX_COMPLETE_6 0x00008000	/* transmission */
++#define MID_TX_COMPLETE_5 0x00004000
++#define MID_TX_COMPLETE_4 0x00002000
++#define MID_TX_COMPLETE_3 0x00001000
++#define MID_TX_COMPLETE_2 0x00000800
++#define MID_TX_COMPLETE_1 0x00000400
++#define MID_TX_COMPLETE_0 0x00000200
++#define MID_TX_COMPLETE	0x0001fe00	/* any TX */
++#define MID_TX_DMA_OVFL	0x00000100	/* DMA to adapter overflow */
++#define MID_TX_IDENT_MISM 0x00000080	/* TX: ident mismatch => halted */
++#define MID_DMA_LERR_ACK 0x00000040	/* LERR - SBus ? */
++#define MID_DMA_ERR_ACK	0x00000020	/* DMA error */
++#define	MID_RX_DMA_COMPLETE 0x00000010	/* DMA to host done */
++#define MID_TX_DMA_COMPLETE 0x00000008	/* DMA from host done */
++#define MID_SERVICE	0x00000004	/* something in service list */
++#define MID_SUNI_INT	0x00000002	/* interrupt from SUNI */
++#define MID_STAT_OVFL	0x00000001	/* statistics overflow */
++
++/*
++ * Master Control/Status
++ */
++
++#define MID_MC_S	0x04
++
++#define MID_INT_SELECT	0x000001C0	/* Interrupt level (000: off) */
++#define MID_INT_SEL_SHIFT 6
++#define	MID_TX_LOCK_MODE 0x00000020	/* 0: streaming; 1: TX ovfl->lock */
++#define MID_DMA_ENABLE	0x00000010	/* R: 0: disable; 1: enable
++					   W: 0: no change; 1: enable */
++#define MID_TX_ENABLE	0x00000008	/* R: 0: TX disabled; 1: enabled
++					   W: 0: no change; 1: enable */
++#define MID_RX_ENABLE	0x00000004	/* like TX */
++#define MID_WAIT_1MS	0x00000002	/* R: 0: timer not running; 1: running
++					   W: 0: no change; 1: no interrupts
++							       for 1 ms */
++#define MID_WAIT_500US	0x00000001	/* like WAIT_1MS, but 0.5 ms */
++
++/*
++ * Statistics
++ *
++ * Cleared when reading.
++ */
++
++#define MID_STAT		0x05
++
++#define MID_VCI_TRASH	0xFFFF0000	/* trashed cells because of VCI mode */
++#define MID_VCI_TRASH_SHIFT 16
++#define MID_OVFL_TRASH	0x0000FFFF	/* trashed cells because of overflow */
++
++/*
++ * Address registers
++ */
++
++#define MID_SERV_WRITE	0x06	/* free pos in service area (R, 10 bits) */
++#define MID_DMA_ADDR	0x07	/* virtual DMA address (R, 32 bits) */
++#define MID_DMA_WR_RX	0x08	/* (RW, 9 bits) */
++#define MID_DMA_RD_RX	0x09
++#define MID_DMA_WR_TX	0x0A
++#define MID_DMA_RD_TX	0x0B
++
++/*
++ * Transmit Place Registers (0x10+4*channel)
++ */
++
++#define MID_TX_PLACE(c)	(0x10+4*(c))
++
++#define MID_SIZE	0x00003800	/* size, N*256 x 32 bit */
++#define MID_SIZE_SHIFT	11
++#define MID_LOCATION	0x000007FF	/* location in adapter memory (word) */
++
++#define MID_LOC_SKIP	8		/* 8 bits of location are always zero
++					   (applies to all uses of location) */
++
++/*
++ * Transmit ReadPtr Registers (0x11+4*channel)
++ */
++
++#define MID_TX_RDPTR(c)	(0x11+4*(c))
++
++#define MID_READ_PTR	0x00007FFF	/* next word for PHY */
++
++/*
++ * Transmit DescrStart Registers (0x12+4*channel)
++ */
++
++#define MID_TX_DESCRSTART(c) (0x12+4*(c))
++
++#define MID_DESCR_START	0x00007FFF	/* seg buffer being DMAed */
++
++#define ENI155_MAGIC	0xa54b872d
++
++struct midway_eprom {
++	unsigned char mac[MAC_LEN],inv_mac[MAC_LEN];
++	unsigned char pad[36];
++	unsigned long serial,inv_serial;
++	unsigned long magic,inv_magic;
++};
++
++
++/*
++ * VCI table entry
++ */
++
++#define MID_VCI_IN_SERVICE	0x00000001	/* set if VCI is currently in
++						   service list */
++#define MID_VCI_SIZE		0x00038000	/* reassembly buffer size,
++						   2*<size> kB */
++#define MID_VCI_SIZE_SHIFT	15
++#define MID_VCI_LOCATION	0x1ffc0000	/* buffer location */
++#define MID_VCI_LOCATION_SHIFT	18
++#define MID_VCI_PTI_MODE	0x20000000	/* 0: trash, 1: preserve */
++#define MID_VCI_MODE		0xc0000000
++#define MID_VCI_MODE_SHIFT	30
++#define MID_VCI_READ		0x00007fff
++#define MID_VCI_READ_SHIFT	0
++#define MID_VCI_DESCR		0x7fff0000
++#define MID_VCI_DESCR_SHIFT	16
++#define MID_VCI_COUNT		0x000007ff
++#define MID_VCI_COUNT_SHIFT	0
++#define MID_VCI_STATE		0x0000c000
++#define MID_VCI_STATE_SHIFT	14
++#define MID_VCI_WRITE		0x7fff0000
++#define MID_VCI_WRITE_SHIFT	16
++
++#define MID_MODE_TRASH	0
++#define MID_MODE_RAW	1
++#define MID_MODE_AAL5	2
++
++/*
++ * Reassembly buffer descriptor
++ */
++
++#define MID_RED_COUNT		0x000007ff
++#define MID_RED_CRC_ERR		0x00000800
++#define MID_RED_T		0x00001000
++#define MID_RED_CE		0x00010000
++#define MID_RED_CLP		0x01000000
++#define MID_RED_IDEN		0xfe000000
++#define MID_RED_SHIFT		25
++
++#define MID_RED_RX_ID		0x1b		/* constant identifier */
++
++/*
++ * Segmentation buffer descriptor
++ */
++
++#define MID_SEG_COUNT		MID_RED_COUNT
++#define MID_SEG_RATE		0x01f80000
++#define MID_SEG_RATE_SHIFT	19
++#define MID_SEG_PR		0x06000000
++#define MID_SEG_PR_SHIFT	25
++#define MID_SEG_AAL5		0x08000000
++#define MID_SEG_ID		0xf0000000
++#define MID_SEG_ID_SHIFT	28
++#define MID_SEG_MAX_RATE	63
++
++#define MID_SEG_CLP		0x00000001
++#define MID_SEG_PTI		0x0000000e
++#define MID_SEG_PTI_SHIFT	1
++#define MID_SEG_VCI		0x00003ff0
++#define MID_SEG_VCI_SHIFT	4
++
++#define MID_SEG_TX_ID		0xb		/* constant identifier */
++
++/*
++ * DMA entry
++ */
++
++#define MID_DMA_COUNT		0xffff0000
++#define MID_DMA_COUNT_SHIFT	16
++#define MID_DMA_END		0x00000020
++#define MID_DMA_TYPE		0x0000000f
++
++#define MID_DT_JK	0x3
++#define MID_DT_WORD	0x0
++#define MID_DT_2W	0x7
++#define MID_DT_4W	0x4
++#define MID_DT_8W	0x5
++#define MID_DT_16W	0x6
++#define MID_DT_2WM	0xf
++#define MID_DT_4WM	0xc
++#define MID_DT_8WM	0xd
++#define MID_DT_16WM	0xe
++
++/* only for RX*/
++#define MID_DMA_VCI		0x0000ffc0
++#define	MID_DMA_VCI_SHIFT	6
++
++/* only for TX */
++#define MID_DMA_CHAN		0x000001c0
++#define MID_DMA_CHAN_SHIFT	6
++
++#define MID_DT_BYTE	0x1
++#define MID_DT_HWORD	0x2
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/suni.c	Mon Jun 10 17:36:06 1996
+@@ -0,0 +1,271 @@
++/* drivers/atm/suni.c - PMC SUNI (PHY) driver */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++#include <asm/system.h>
++#include <asm/param.h>
++#include <asm/segment.h>
++
++#include "suni.h"
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++struct suni_priv {
++	struct sonet_stats sonet_stats; /* link diagnostics */
++	unsigned char loop_mode;        /* loopback mode */
++	struct atm_dev *dev;		/* device back-pointer */
++	struct suni_priv *next;		/* next SUNI */
++};
++
++
++#define PRIV(dev) ((struct suni_priv *) dev->phy_data)
++
++#define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg)
++#define GET(reg) dev->ops->phy_get(dev,SUNI_##reg)
++#define REG_CHANGE(mask,shift,value,reg) \
++  PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg)
++
++
++static struct timer_list poll_timer = { NULL, NULL, 0L, 0L, NULL };
++static int start_timer = 1;
++static struct suni_priv *sunis = NULL;
++
++
++static void suni_hz(unsigned long dummy)
++{
++	struct suni_priv *walk;
++	struct atm_dev *dev;
++	struct sonet_stats *stats;
++
++	for (walk = sunis; walk; walk = walk->next) {
++		dev = walk->dev;
++		stats = &walk->sonet_stats;
++		PUT(0,MRI); /* latch counters */
++		udelay(1);
++		stats->section_bip += (GET(RSOP_SBL) & 0xff) |
++		    ((GET(RSOP_SBM) & 0xff) << 8);
++		if (stats->section_bip < 0) stats->section_bip = LONG_MAX;
++		stats->line_bip += (GET(RLOP_LBL) & 0xff) |
++		    ((GET(RLOP_LB) & 0xff) << 8) |
++		    ((GET(RLOP_LBM) & 0xff) << 16);
++		if (stats->line_bip < 0) stats->line_bip = LONG_MAX;
++		stats->path_bip += (GET(RPOP_PBL) & 0xff) |
++		    ((GET(RPOP_PBM) & 0xff) << 8);
++		if (stats->path_bip < 0) stats->path_bip = LONG_MAX;
++		stats->line_febe += (GET(RLOP_LFL) & 0xff) |
++		    ((GET(RLOP_LF) & 0xff) << 8) |
++		    ((GET(RLOP_LFM) & 0xff) << 16);
++		if (stats->line_febe < 0) stats->line_febe = LONG_MAX;
++		stats->path_febe += (GET(RPOP_PFL) & 0xff) |
++		    ((GET(RPOP_PFM) & 0xff) << 8);
++		if (stats->path_febe < 0) stats->path_febe = LONG_MAX;
++		stats->corr_hcs += GET(RACP_CHEC) & 0xff;
++		if (stats->corr_hcs < 0) stats->corr_hcs = LONG_MAX;
++		stats->uncorr_hcs += GET(RACP_UHEC) & 0xff;
++		if (stats->uncorr_hcs < 0) stats->uncorr_hcs = LONG_MAX;
++		stats->rx_cells += (GET(RACP_RCCL) & 0xff) |
++		    ((GET(RACP_RCC) & 0xff) << 8) |
++		    ((GET(RACP_RCCM) & 0xff) << 16);
++		if (stats->rx_cells < 0) stats->rx_cells = LONG_MAX;
++		stats->tx_cells += (GET(TACP_TCCL) & 0xff) |
++		    ((GET(TACP_TCC) & 0xff) << 8) |
++		    ((GET(TACP_TCCM) & 0xff) << 16);
++		if (stats->tx_cells < 0) stats->tx_cells = LONG_MAX;
++	}
++	if (!start_timer) {
++		del_timer(&poll_timer);
++		poll_timer.expires = jiffies+HZ;
++		add_timer(&poll_timer);
++	}
++}
++
++
++static int fetch_stats(struct atm_dev *dev,struct sonet_stats *arg,int zero)
++{
++	unsigned long flags;
++
++	save_flags(flags);
++	cli();
++	if (arg)
++		memcpy_tofs(arg,&PRIV(dev)->sonet_stats,
++		    sizeof(struct sonet_stats));
++	if (zero)
++		memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
++	restore_flags(flags);
++	return 0;
++}
++
++
++#define HANDLE_FLAG(flag,reg,bit) \
++  if (todo & flag) { \
++    if (set) PUT(GET(reg) | bit,reg); \
++    else PUT(GET(reg) & ~bit,reg); \
++    todo &= ~flag; \
++  }
++
++
++static int change_diag(struct atm_dev *dev,unsigned long arg,int set)
++{
++	int todo;
++
++	todo = get_fs_long(arg);
++	HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8);
++	HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP);
++	HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3);
++	HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF);
++	HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS);
++	HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS);
++	HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS);
++	HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS);
++	put_fs_long(todo,arg);
++	return 0;
++}
++
++
++#undef HANDLE_FLAG
++
++
++static int get_diag(struct atm_dev *dev,unsigned long arg)
++{
++	int set;
++
++	set = 0;
++	if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP;
++	if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP;
++	if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP;
++	/* SONET_INS_FRAME is one-shot only */
++	if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS;
++	if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS;
++	if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS;
++	if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS;
++	put_fs_long(set,arg);
++	return 0;
++}
++
++
++static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
++{
++	int error;
++
++	switch (cmd) {
++		case SONET_GETSTATZ:
++		case SONET_GETSTAT:
++			return fetch_stats(dev,(struct sonet_stats *) arg,
++			    cmd == SONET_GETSTATZ);
++		case SONET_SETDIAG:
++			return change_diag(dev,arg,1);
++		case SONET_CLRDIAG:
++			return change_diag(dev,arg,0);
++		case SONET_GETDIAG:
++			return get_diag(dev,arg);
++		case SONET_SETFRAMING:
++			if (!suser()) return -EPERM;
++			if (arg != SONET_FRAME_SONET) return -EINVAL;
++			return 0;
++		case SONET_GETFRAMING:
++			put_fs_long(SONET_FRAME_SONET,arg);
++			return 0;
++		case SONET_GETFRSENSE:
++			return -EINVAL;
++		case SUNI_SETLOOP:
++			if (!suser()) return -EPERM;
++			if (arg > SUNI_LM_LOOP) return -EINVAL;
++			PUT((GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE)) |
++			    (arg == SUNI_LM_DIAG ? SUNI_MCT_DLE : 0) |
++			    (arg == SUNI_LM_LOOP ? SUNI_MCT_LLE : 0),MCT);
++			PRIV(dev)->loop_mode = arg;
++			return 0;
++		case SUNI_GETLOOP:
++			error = verify_area(VERIFY_WRITE,(void *) arg,
++			    sizeof(int));
++			if (error) return error;
++			put_fs_long(PRIV(dev)->loop_mode,arg);
++			return 0;
++		default:
++			return -EINVAL;
++	}
++}
++
++
++static void suni_int(struct atm_dev *dev)
++{
++	printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number,
++	    GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ?  "lost" : "detected again");
++}
++
++
++static int suni_start(struct atm_dev *dev)
++{
++	unsigned long flags;
++
++	if (!(PRIV(dev) = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
++		return -ENOMEM;
++	PRIV(dev)->dev = dev;
++	save_flags(flags);
++	cli();
++	PRIV(dev)->next = sunis;
++	sunis = PRIV(dev);
++	restore_flags(flags);
++	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
++	PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE);
++		/* interrupt on loss of signal */
++	(void) GET(RSOP_SIS); /* clear SUNI interrupts */
++	PRIV(dev)->loop_mode = SUNI_LM_NONE;
++	suni_hz(0); /* clear SUNI counters */
++	(void) fetch_stats(dev,NULL,1); /* clear kernel counters */
++	cli();
++	if (!start_timer) restore_flags(flags);
++	else {
++		start_timer = 0;
++		restore_flags(flags);
++		/*init_timer(&poll_timer);*/
++		poll_timer.expires = jiffies+HZ;
++		poll_timer.function = suni_hz;
++#if 0
++printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.prev,
++    (unsigned long) poll_timer.next);
++#endif
++		add_timer(&poll_timer);
++	}
++	return 0;
++}
++
++
++static const struct atmphy_ops suni_ops = {
++	suni_start,
++	suni_ioctl,
++	suni_int
++};
++
++
++int suni_init(struct atm_dev *dev)
++{
++	unsigned char mri;
++
++	mri = GET(MRI); /* reset SUNI */
++	PUT(mri | SUNI_MRI_RESET,MRI);
++	PUT(mri,MRI);
++	PUT(0,MT); /* disable all tests */
++	REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET,
++	    TPOP_APM); /* use SONET */
++	REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP,
++	    TACP_IUCHP); /* idle cells */
++	PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP);
++	dev->phy = &suni_ops;
++	return 0;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/suni.h	Wed Jul 31 19:32:14 1996
+@@ -0,0 +1,219 @@
++/* drivers/atm/suni.h - PMC SUNI (PHY) declarations */
++ 
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef DRIVER_ATM_SUNI_H
++#define DRIVER_ATM_SUNI_H
++
++#include <linux/atmdev.h>
++#include <linux/atmioc.h>
++
++
++/* SUNI registers */
++
++#define SUNI_MRI		0x00	/* Master Reset and Identity / Load
++					   Meter */
++#define SUNI_MC			0x01	/* Master Configuration */
++#define SUNI_MIS		0x02	/* Master Interrupt Status */
++			  /* no 0x03 */
++#define SUNI_MCM		0x04	/* Master Clock Monitor */
++#define SUNI_MCT		0x05	/* Master Control */
++#define SUNI_CSCS		0x06	/* Clock Synthesis Control and Status */
++#define SUNI_CRCS		0x07	/* Clock Recovery Control and Status */
++			     /* 0x08-0x0F reserved */
++#define SUNI_RSOP_CIE		0x10	/* RSOP Control/Interrupt Enable */
++#define SUNI_RSOP_SIS		0x11	/* RSOP Status/Interrupt Status */
++#define SUNI_RSOP_SBL		0x12	/* RSOP Section BIP-8 LSB */
++#define SUNI_RSOP_SBM		0x13	/* RSOP Section BIP-8 MSB */
++#define SUNI_TSOP_CTRL		0x14	/* TSOP Control */
++#define SUNI_TSOP_DIAG		0x15	/* TSOP Diagnostic */
++			     /* 0x16-0x17 reserved */
++#define SUNI_RLOP_CS		0x18	/* RLOP Control/Status */
++#define SUNI_RLOP_IES		0x19	/* RLOP Interrupt Enable/Status */
++#define SUNI_RLOP_LBL		0x1A	/* RLOP Line BIP-8/24 LSB */
++#define SUNI_RLOP_LB		0x1B	/* RLOP Line BIP-8/24 */
++#define SUNI_RLOP_LBM		0x1C	/* RLOP Line BIP-8/24 MSB */
++#define SUNI_RLOP_LFL		0x1D	/* RLOP Line FEBE LSB */
++#define SUNI_RLOP_LF 		0x1E	/* RLOP Line FEBE */
++#define SUNI_RLOP_LFM		0x1F	/* RLOP Line FEBE MSB */
++#define SUNI_TLOP_CTRL		0x20	/* TLOP Control */
++#define SUNI_TLOP_DIAG		0x21	/* TLOP Diagnostic */
++			     /* 0x22-0x2F reserved */
++#define SUNI_RPOP_SC		0x30	/* RPOP Status/Control */
++#define SUNI_RPOP_IS		0x31	/* RPOP Interrupt Status */
++			     /* 0x32 reserved */
++#define SUNI_RPOP_IE		0x33	/* RPOP Interrupt Enable */
++			     /* 0x34-0x36 reserved */
++#define SUNI_RPOP_PSL		0x37	/* RPOP Path Signal Label */
++#define SUNI_RPOP_PBL		0x38	/* RPOP Path BIP-8 LSB */
++#define SUNI_RPOP_PBM		0x39	/* RPOP Path BIP-8 MSB */
++#define SUNI_RPOP_PFL		0x3A	/* RPOP Path FEBE LSB */
++#define SUNI_RPOP_PFM		0x3B	/* RPOP Path FEBE MSB */
++			     /* 0x3C reserved */
++#define SUNI_RPOP_PBC		0x3D	/* RPOP Path BIP-8 Configuration */
++			     /* 0x3E-0x3F reserved */
++#define SUNI_TPOP_CD		0x40	/* TPOP Control/Diagnostic */
++#define SUNI_TPOP_PC		0x41	/* TPOP Pointer Control */
++			     /* 0x42-0x44 reserved */
++#define SUNI_TPOP_APL		0x45	/* TPOP Arbitrary Pointer LSB */
++#define SUNI_TPOP_APM		0x46	/* TPOP Arbitrary Pointer MSB */
++			     /* 0x47 reserved */
++#define SUNI_TPOP_PSL		0x48	/* TPOP Path Signal Label */
++#define SUNI_TPOP_PS		0x49	/* TPOP Path Status */
++			     /* 0x4A-0x4F reserved */
++#define SUNI_RACP_CS		0x50	/* RACP Control/Status */
++#define SUNI_RACP_IES		0x51	/* RACP Interrupt Enable/Status */
++#define SUNI_RACP_MHP		0x52	/* RACP Match Header Pattern */
++#define SUNI_RACP_MHM		0x53	/* RACP Match Header Mask */
++#define SUNI_RACP_CHEC		0x54	/* RACP Correctable HCS Error Count */
++#define SUNI_RACP_UHEC		0x55	/* RACP Uncorrectable HCS Err Count */
++#define SUNI_RACP_RCCL		0x56	/* RACP Receive Cell Counter LSB */
++#define SUNI_RACP_RCC		0x57	/* RACP Receive Cell Counter */
++#define SUNI_RACP_RCCM		0x58	/* RACP Receive Cell Counter MSB */
++#define SUNI_RACP_CFG		0x59	/* RACP Configuration */
++			     /* 0x5A-0x5F reserved */
++#define SUNI_TACP_CS		0x60	/* TACP Control/Status */
++#define SUNI_TACP_IUCHP		0x61	/* TACP Idle/Unassigned Cell Hdr Pat */
++#define SUNI_TACP_IUCPOP	0x62	/* TACP Idle/Unassigned Cell Payload
++					   Octet Pattern */
++#define SUNI_TACP_FIFO		0x63	/* TACP FIFO Configuration */
++#define SUNI_TACP_TCCL		0x64	/* TACP Transmit Cell Counter LSB */
++#define SUNI_TACP_TCC		0x65	/* TACP Transmit Cell Counter */
++#define SUNI_TACP_TCCM		0x66	/* TACP Transmit Cell Counter MSB */
++#define SUNI_TACP_CFG		0x67	/* TACP Configuration */
++			     /* 0x68-0x7F reserved */
++#define	SUNI_MT			0x80	/* Master Test */
++			     /* 0x81-0xFF reserved */
++
++/* SUNI register values */
++
++
++/* MRI is reg 0 */
++#define SUNI_MRI_ID		0x0f	/* R, SUNI revision number */
++#define SUNI_MRI_ID_SHIFT 	0
++#define SUNI_MRI_TYPE		0x70	/* R, SUNI type (lite is 011) */
++#define SUNI_MRI_TYPE_SHIFT 	4
++#define SUNI_MRI_RESET		0x80	/* RW, reset & power down chip
++					   0: normal operation
++					   1: reset & low power */
++/* MCT is reg 5 */
++#define SUNI_MCT_LOOPT		0x01	/* RW, timing source, 0: from
++					   TRCLK+/- */
++#define SUNI_MCT_DLE		0x02	/* RW, diagnostic loopback */
++#define SUNI_MCT_LLE		0x04	/* RW, line loopback */
++#define SUNI_MCT_FIXPTR		0x20	/* RW, disable transmit payload pointer
++					   adjustments
++					   0: payload ptr controlled by TPOP
++					      ptr control reg
++					   1: payload pointer fixed at 522 */
++#define SUNI_MCT_LCDV		0x40	/* R, loss of cell delineation */
++#define SUNI_MCT_LCDE		0x80	/* RW, loss of cell delineation
++					   interrupt (1: on) */
++/* RSOP_CIE is reg 0x10 */
++#define SUNI_RSOP_CIE_OOFE	0x01	/* RW, enable interrupt on frame alarm
++					   state change */
++#define SUNI_RSOP_CIE_LOFE	0x02	/* RW, enable interrupt on loss of
++					   frame state change */
++#define SUNI_RSOP_CIE_LOSE	0x04	/* RW, enable interrupt on loss of
++					   signal state change */
++#define SUNI_RSOP_CIE_BIPEE	0x08	/* RW, enable interrupt on section
++					   BIP-8 error (B1) */
++#define SUNI_RSOP_CIE_FOOF	0x20	/* W, force RSOP out of frame at next
++					   boundary */
++#define SUNI_RSOP_CIE_DDS	0x40	/* RW, disable scrambling */
++
++/* RSOP_SIS is reg 0x11 */
++#define SUNI_RSOP_SIS_OOFV	0x01	/* R, out of frame */
++#define SUNI_RSOP_SIS_LOFV	0x02	/* R, loss of frame */
++#define SUNI_RSOP_SIS_LOSV	0x04	/* R, loss of signal */
++#define SUNI_RSOP_SIS_OOFI	0x08	/* R, out of frame interrupt */
++#define SUNI_RSOP_SIS_LOFI	0x10	/* R, loss of frame interrupt */
++#define SUNI_RSOP_SIS_LOSI	0x20	/* R, loss of signal interrupt */
++#define SUNI_RSOP_SIS_BIPEI	0x40	/* R, section BIP-8 interrupt */
++
++/* TSOP_CTRL is reg 0x14 */
++#define SUNI_TSOP_CTRL_LAIS	0x01	/* insert alarm indication signal */
++#define SUNI_TSOP_CTRL_DS	0x40	/* disable scrambling */
++
++/* TSOP_DIAG is reg 0x15 */
++#define SUNI_TSOP_DIAG_DFP	0x01	/* insert single bit error cont. */
++#define SUNI_TSOP_DIAG_DBIP8	0x02	/* insert section BIP err (cont) */
++#define SUNI_TSOP_DIAG_DLOS	0x04	/* set line to zero (loss of signal) */
++
++/* TLOP_DIAG is reg 0x21 */
++#define SUNI_TLOP_DIAG_DBIP	0x01	/* insert line BIP err (continuously) */
++
++/* TPOP_DIAG is reg 0x40 */
++#define SUNI_TPOP_DIAG_PAIS	0x01	/* insert STS path alarm ind (cont) */
++#define SUNI_TPOP_DIAG_DB3	0x02	/* insert path BIP err (continuously) */
++
++/* TPOP_APM is reg 0x46 */
++#define SUNI_TPOP_APM_APTR	0x03	/* RW, arbitrary pointer, upper 2
++					   bits */
++#define SUNI_TPOP_APM_APTR_SHIFT 0
++#define SUNI_TPOP_APM_S		0x0c	/* RW, "unused" bits of payload
++					   pointer */
++#define SUNI_TPOP_APM_S_SHIFT	2
++#define SUNI_TPOP_APM_NDF	0xf0	 /* RW, NDF bits */
++#define SUNI_TPOP_APM_NDF_SHIFT	4
++
++#define SUNI_TPOP_S_SONET	0	/* set S bits to 00 */
++#define SUNI_TPOP_S_SDH		2	/* set S bits to 10 */
++
++/* RACP_IES is reg 0x51 */
++#define SUNI_RACP_IES_FOVRI	0x02	/* R, FIFO overrun */
++#define SUNI_RACP_IES_UHCSI	0x04	/* R, uncorrectable HCS error */
++#define SUNI_RACP_IES_CHCSI	0x08	/* R, correctable HCS error */
++#define SUNI_RACP_IES_OOCDI	0x10	/* R, change of cell delineation
++					   state */
++#define SUNI_RACP_IES_FIFOE	0x20	/* RW, enable FIFO overrun interrupt */
++#define SUNI_RACP_IES_HCSE	0x40	/* RW, enable HCS error interrupt */
++#define SUNI_RACP_IES_OOCDE	0x80	/* RW, enable cell delineation state
++					   change interrupt */
++
++/* TACP_CS is reg 0x60 */
++#define SUNI_TACP_CS_FIFORST	0x01	/* RW, reset transmit FIFO (sticky) */
++#define SUNI_TACP_CS_DSCR	0x02	/* RW, disable payload scrambling */
++#define SUNI_TACP_CS_HCAADD	0x04	/* RW, add coset polynomial to HCS */
++#define SUNI_TACP_CS_DHCS	0x10	/* RW, insert HCS errors */
++#define SUNI_TACP_CS_FOVRI	0x20	/* R, FIFO overrun */
++#define SUNI_TACP_CS_TSOCI	0x40	/* R, TSOC input high */
++#define SUNI_TACP_CS_FIFOE	0x80	/* RW, enable FIFO overrun interrupt */
++
++/* TACP_IUCHP is reg 0x61 */
++#define SUNI_TACP_IUCHP_CLP	0x01	/* RW, 8th bit of 4th octet of i/u
++					   pattern */
++#define SUNI_TACP_IUCHP_PTI	0x0e	/* RW, 5th-7th bits of 4th octet of i/u
++					   pattern */
++#define SUNI_TACP_IUCHP_PTI_SHIFT 1
++#define SUNI_TACP_IUCHP_GFC	0xf0	/* RW, 1st-4th bits of 1st octet of i/u
++					   pattern */
++#define SUNI_TACP_IUCHP_GFC_SHIFT 4
++
++/* MT is reg 0x80 */
++#define SUNI_MT_HIZIO		0x01	/* RW, all but data bus & MP interface
++					   tri-state */
++#define SUNI_MT_HIZDATA		0x02	/* W, also tri-state data bus */
++#define SUNI_MT_IOTST		0x04	/* RW, enable test mode */
++#define SUNI_MT_DBCTRL		0x08	/* W, control data bus by CSB pin */
++#define SUNI_MT_PMCTST		0x10	/* W, PMC test mode */
++
++
++#define SUNI_IDLE_PATTERN       0x6a    /* idle pattern */
++
++/* ioctls */
++
++#define SUNI_GETLOOP	_IOR('a',ATMIOC_PHYPRV,int)	/* get loopback mode */
++#define SUNI_SETLOOP	_IO('a',ATMIOC_PHYPRV+1)	/* set loopback mode */
++
++#define SUNI_LM_NONE	0	/* no loopback */
++#define SUNI_LM_DIAG	1	/* diagnostic (i.e. loop TX to RX) */
++#define SUNI_LM_LOOP	2	/* line (i.e. loop RX to TX) */
++
++
++#ifdef __KERNEL__
++int suni_init(struct atm_dev *dev);
++#endif
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/tonga.h	Mon Jun 10 17:36:07 1996
+@@ -0,0 +1,20 @@
++/* drivers/atm/tonga.h - Efficient Networks Tonga (PCI bridge) declarations */
++ 
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef DRIVER_ATM_TONGA_H
++#define DRIVER_ATM_TONGA_H
++
++#define PCI_TONGA_CTRL	0x60	/* control register */
++
++#define END_SWAP_DMA	0x80	/* endian swap on DMA */
++#define END_SWAP_BYTE	0x40	/* endian swap on slave byte accesses */
++#define END_SWAP_WORD	0x20	/* endian swap on slave word accesses */
++#define SEPROM_MAGIC	0x0c	/* obscure required pattern (ASIC only) */
++#define SEPROM_DATA	0x02	/* serial EEPROM data (ASIC only) */
++#define SEPROM_CLK	0x01	/* serial EEPROM clock (ASIC only) */
++
++#define SEPROM_ESI_BASE	64	/* start of ESI in serial EEPROM */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/uPD98401.h	Mon Jun 10 17:36:08 1996
+@@ -0,0 +1,292 @@
++/* drivers/atm/uPD98401.h - NEC uPD98401 (SAR) declarations */
++ 
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef DRIVERS_ATM_uPD98401_H
++#define DRIVERS_ATM_uPD98401_H
++
++
++#define MAX_CRAM_SIZE	(1 << 18)	/* 2^18 words */
++#define RAM_INCREMENT	1024		/* check in 4 kB increments */
++
++#define uPD98401_PORTS	0x24		/* probably more ? */
++
++
++/*
++ * Commands
++ */
++
++#define uPD98401_OPEN_CHAN	0x20000000 /* open channel */
++#define uPD98401_CHAN_ADDR	0x0003fff8 /*	channel address */
++#define uPD98401_CHAN_ADDR_SHIFT 3
++#define uPD98401_CLOSE_CHAN	0x24000000 /* close channel */
++#define uPD98401_CHAN_RT	0x02000000 /*	RX/TX (0 TX, 1 RX) */
++#define uPD98401_DEACT_CHAN	0x28000000 /* deactivate channel */
++#define uPD98401_TX_READY	0x30000000 /* TX ready */
++#define uPD98401_ADD_BAT	0x34000000 /* add batches */
++#define uPD98401_POOL		0x000f0000 /* pool number */
++#define uPD98401_POOL_SHIFT	16
++#define uPD98401_POOL_NUMBAT	0x0000ffff /* number of batches */
++#define uPD98401_NOP		0x3f000000 /* NOP */
++#define uPD98401_IND_ACC	0x00000000 /* Indirect Access */
++#define uPD98401_IA_RW		0x10000000 /*	Read/Write (0 W, 1 R) */
++#define uPD98401_IA_B3		0x08000000 /*	Byte select, 1 enable */
++#define uPD98401_IA_B2		0x04000000
++#define uPD98401_IA_B1		0x02000000
++#define uPD98401_IA_B0		0x01000000
++#define uPD98401_IA_BALL	0x0f000000 /*   whole longword */
++#define uPD98401_IA_TGT		0x000c0000 /*	Target */
++#define uPD98401_IA_TGT_SHIFT	18
++#define uPD98401_IA_TGT_CM	0	   /*	- Control Memory */
++#define uPD98401_IA_TGT_SAR	1	   /*	- uPD98401 registers */
++#define uPD98401_IA_TGT_PHY	3	   /*   - PHY device */
++#define uPD98401_IA_ADDR	0x0003ffff
++
++/*
++ * Command Register Status
++ */
++
++#define uPD98401_BUSY		0x80000000 /* SAR is busy */
++#define uPD98401_LOCKED		0x40000000 /* SAR is locked by other CPU */
++
++/*
++ * Indications
++ */
++
++/* Normal (AAL5) Receive Indication */
++#define uPD98401_AAL5_UINFO	0xffff0000 /* user-supplied information */
++#define uPD98401_AAL5_UINFO_SHIFT 16
++#define uPD98401_AAL5_SIZE	0x0000ffff /* PDU size (in _CELLS_ !!) */
++#define uPD98401_AAL5_CHAN	0x7fff0000 /* Channel number */
++#define uPD98401_AAL5_CHAN_SHIFT	16
++#define uPD98401_AAL5_ERR	0x00008000 /* Error indication */
++#define uPD98401_AAL5_CI	0x00004000 /* Congestion Indication */
++#define uPD98401_AAL5_CLP	0x00002000 /* CLP (>= 1 cell had CLP=1) */
++#define uPD98401_AAL5_ES	0x00000f00 /* Error Status */
++#define uPD98401_AAL5_ES_SHIFT	8
++#define uPD98401_AAL5_ES_NONE	0	   /*	No error */
++#define uPD98401_AAL5_ES_FREE	1	   /*	Receiver free buf underflow */
++#define uPD98401_AAL5_ES_FIFO	2	   /*	Receiver FIFO overrun */
++#define uPD98401_AAL5_ES_TOOBIG	3	   /*	Maximum length violation */
++#define uPD98401_AAL5_ES_CRC	4	   /*	CRC error */
++#define uPD98401_AAL5_ES_ABORT	5	   /*	User abort */
++#define uPD98401_AAL5_ES_LENGTH	6	   /*   Length violation */
++#define uPD98401_AAL5_ES_T1	7	   /*	T1 error (timeout) */
++#define uPD98401_AAL5_ES_DEACT	8	   /*	Deactivated with DEACT_CHAN */
++#define uPD98401_AAL5_POOL	0x0000001f /* Free buffer pool number */
++
++/* Raw Cell Indication */
++#define uPD98401_RAW_UINFO	uPD98401_AAL5_UINFO
++#define uPD98401_RAW_UINFO_SHIFT uPD98401_AAL5_UINFO_SHIFT
++#define uPD98401_RAW_HEC	0x000000ff /* HEC */
++#define uPD98401_RAW_CHAN	uPD98401_AAL5_CHAN
++#define uPD98401_RAW_CHAN_SHIFT uPD98401_AAL5_CHAN_SHIFT
++
++/* Transmit Indication */
++#define uPD98401_TXI_CONN	0x7fff0000 /* Connection Number */
++#define uPD98401_TXI_CONN_SHIFT	16
++#define uPD98401_TXI_ACTIVE	0x00008000 /* Channel remains active */
++#define uPD98401_TXI_PQP	0x00007fff /* Packet Queue Pointer */
++
++/*
++ * Directly Addressable Registers
++ */
++
++#define uPD98401_GMR	0x00	/* General Mode Register */
++#define uPD98401_GSR	0x01	/* General Status Register */
++#define uPD98401_IMR	0x02	/* Interrupt Mask Register */
++#define uPD98401_RQU	0x03	/* Receive Queue Underrun */
++#define uPD98401_RQA	0x04	/* Receive Queue Alert */
++#define uPD98401_ADDR	0x05	/* Last Burst Address */
++#define uPD98401_VER	0x06	/* Version Number */
++#define uPD98401_SWR	0x07	/* Software Reset */
++#define uPD98401_CMR	0x08	/* Command Register */
++#define uPD98401_CMR_L	0x09	/* Command Register and Lock/Unlock */
++#define uPD98401_CER	0x0a	/* Command Extension Register */
++#define uPD98401_CER_L	0x0b	/* Command Ext Reg and Lock/Unlock */
++
++#define uPD98401_MSH(n) (0x10+(n))	/* Mailbox n Start Address High */
++#define uPD98401_MSL(n) (0x14+(n))	/* Mailbox n Start Address High */
++#define uPD98401_MBA(n) (0x18+(n))	/* Mailbox n Bottom Address */
++#define uPD98401_MTA(n) (0x1c+(n))	/* Mailbox n Tail Address */
++#define uPD98401_MWA(n) (0x20+(n))	/* Mailbox n Write Address */
++
++/* GMR is at 0x00 */
++#define uPD98401_GMR_ONE	0x80000000 /* Must be set to one */
++#define uPD98401_GMR_SLM	0x40000000 /* Address mode (0 word, 1 byte) */
++#define uPD98401_GMR_CPE	0x00008000 /* Control Memory Parity Enable */
++#define uPD98401_GMR_LP		0x00004000 /* Loopback */
++#define uPD98401_GMR_WA		0x00002000 /* Early Bus Write Abort/RDY */
++#define uPD98401_GMR_RA		0x00001000 /* Early Read Abort/RDY */
++#define uPD98401_GMR_SZ		0x00000f00 /* Burst Size Enable */
++#define uPD98401_BURST16	0x00000800 /*	16-word burst */
++#define uPD98401_BURST8		0x00000400 /*	 8-word burst */
++#define uPD98401_BURST4		0x00000200 /*	 4-word burst */
++#define uPD98401_BURST2		0x00000100 /*	 2-word burst */
++#define uPD98401_GMR_AD		0x00000080 /* Address (burst resolution) Disable */
++#define uPD98401_GMR_BO		0x00000040 /* Byte Order (0 little, 1 big) */
++#define uPD98401_GMR_PM		0x00000020 /* Bus Parity Mode (0 byte, 1 word)*/
++#define uPD98401_GMR_PC		0x00000010 /* Bus Parity Control (0even,1odd) */
++#define uPD98401_GMR_BPE	0x00000008 /* Bus Parity Enable */
++#define uPD98401_GMR_DR		0x00000004 /* Receive Drop Mode (0drop,1don't)*/
++#define uPD98401_GMR_SE		0x00000002 /* Shapers Enable */
++#define uPD98401_GMR_RE		0x00000001 /* Receiver Enable */
++
++/* GSR is at 0x01, IMR is at 0x02 */
++#define uPD98401_INT_PI		0x80000000 /* PHY interrupt */
++#define uPD98401_INT_RQA	0x40000000 /* Receive Queue Alert */
++#define uPD98401_INT_RQU	0x20000000 /* Receive Queue Underrun */
++#define uPD98401_INT_RD		0x10000000 /* Receiver Deactivated */
++#define uPD98401_INT_SPE	0x08000000 /* System Parity Error */
++#define uPD98401_INT_CPE	0x04000000 /* Control Memory Parity Error */
++#define uPD98401_INT_SBE	0x02000000 /* System Bus Error */
++#define uPD98401_INT_IND	0x01000000 /* Initialization Done */
++#define uPD98401_INT_RCR	0x0000ff00 /* Raw Cell Received */
++#define uPD98401_INT_RCR_SHIFT	8
++#define uPD98401_INT_MF		0x000000f0 /* Mailbox Full */
++#define uPD98401_INT_MF_SHIFT	4
++#define uPD98401_INT_MM		0x0000000f /* Mailbox Modified */
++
++/* VER is at 0x06 */
++#define uPD98401_MAJOR		0x0000ff00 /* Major revision */
++#define uPD98401_MAJOR_SHIFT	8
++#define uPD98401_MINOR		0x000000ff /* Minor revision */
++
++/*
++ * Indirectly Addressable Registers
++ */
++
++#define uPD98401_IM(n)	(0x40000+(n))	/* Scheduler n I and M */
++#define uPD98401_X(n)	(0x40010+(n))	/* Scheduler n X */
++#define uPD98401_Y(n)	(0x40020+(n))	/* Scheduler n Y */
++#define uPD98401_PC(n)	(0x40030+(n))	/* Scheduler n P, C, p and c */
++#define uPD98401_PS(n)	(0x40040+(n))	/* Scheduler n priority and status */
++
++/* IM contents */
++#define uPD98401_IM_I		0xff000000 /* I */
++#define uPD98401_IM_I_SHIFT	24
++#define uPD98401_IM_M		0x00ffffff /* M */
++
++/* PC contents */
++#define uPD98401_PC_P		0xff000000 /* P */
++#define uPD98401_PC_P_SHIFT	24
++#define uPD98401_PC_C		0x00ff0000 /* C */
++#define uPD98401_PC_C_SHIFT	16
++#define uPD98401_PC_p		0x0000ff00 /* p */
++#define uPD98401_PC_p_SHIFT	8
++#define uPD98401_PC_c		0x000000ff /* c */
++
++/* PS contents */
++#define uPD98401_PS_PRIO	0xf0	/* Priority level (0 high, 15 low) */
++#define uPD98401_PS_PRIO_SHIFT	4
++#define uPD98401_PS_S		0x08	/* Scan - must be 0 (internal) */
++#define uPD98401_PS_R		0x04	/* Round Robin (internal) */
++#define uPD98401_PS_A		0x02	/* Active (internal) */
++#define uPD98401_PS_E		0x01	/* Enabled */
++
++#define uPD98401_TOS	0x40100	/* Top of Stack Control Memory Address */
++#define uPD98401_SMA	0x40200	/* Shapers Control Memory Start Address */
++#define uPD98401_PMA	0x40201	/* Receive Pool Control Memory Start Address */
++#define uPD98401_T1R	0x40300	/* T1 Register */
++#define uPD98401_VRR	0x40301	/* VPI/VCI Reduction Register/Recv. Shutdown */
++#define uPD98401_TSR	0x40302	/* Time-Stamp Register */
++
++/* VRR is at 0x40301 */
++#define uPD98401_VRR_SDM	0x80000000 /* Shutdown Mode */
++#define uPD98401_VRR_SHIFT	0x000f0000 /* VPI/VCI Shift */
++#define uPD98401_VRR_SHIFT_SHIFT 16
++#define uPD98401_VRR_MASK	0x0000ffff /* VPI/VCI mask */
++
++/*
++ * TX packet descriptor
++ */
++
++#define uPD98401_TXPD_SIZE	16	   /* descriptor size (in bytes) */
++
++#define uPD98401_TXPD_V		0x80000000 /* Valid bit */
++#define uPD98401_TXPD_DP	0x40000000 /* Descriptor (1) or Pointer (0) */
++#define uPD98401_TXPD_SM	0x20000000 /* Single (1) or Multiple (0) */
++#define uPD98401_TXPD_CLPM	0x18000000 /* CLP mode */
++#define uPD98401_CLPM_0		0	   /*	00 CLP = 0 */
++#define uPD98401_CLPM_1		3	   /*	11 CLP = 1 */
++#define uPD98401_CLPM_LAST	1	   /*	01 CLP unless last cell */
++#define uPD98401_TXPD_CLPM_SHIFT 27
++#define uPD98401_TXPD_PTI	0x07000000 /* PTI pattern */
++#define uPD98401_TXPD_PTI_SHIFT	24
++#define uPD98401_TXPD_GFC	0x00f00000 /* GFC pattern */
++#define uPD98401_TXPD_GFC_SHIFT	20
++#define uPD98401_TXPD_C10	0x00040000 /* insert CRC-10 */
++#define uPD98401_TXPD_AAL5	0x00020000 /* AAL5 processing */
++#define uPD98401_TXPD_MB	0x00010000 /* TX mailbox number */
++#define uPD98401_TXPD_UU	0x0000ff00 /* CPCS-UU */
++#define uPD98401_TXPD_UU_SHIFT	8
++#define uPD98401_TXPD_CPI	0x000000ff /* CPI */
++
++/*
++ * TX buffer descriptor
++ */
++
++#define uPD98401_TXBD_SIZE	8	   /* descriptor size (in bytes) */
++
++#define uPD98401_TXBD_LAST	0x80000000 /* last buffer in packet */
++
++/*
++ * TX VC table
++ */
++
++/* 1st word has the same structure as in a TX packet descriptor */
++#define uPD98401_TXVC_L		0x80000000 /* last buffer */
++#define uPD98401_TXVC_SHP	0x0f000000 /* shaper number */
++#define uPD98401_TXVC_SHP_SHIFT	24
++#define uPD98401_TXVC_VPI	0x00ff0000 /* VPI */
++#define uPD98401_TXVC_VPI_SHIFT	16
++#define uPD98401_TXVC_VCI	0x0000ffff /* VCI */
++#define uPD98401_TXVC_QRP	6	   /* Queue Read Pointer is in word 6 */
++
++/*
++ * RX free buffer pools descriptor
++ */
++
++#define uPD98401_RXFP_ALERT	0x70000000 /* low water mark */
++#define uPD98401_RXFP_ALERT_SHIFT 28
++#define uPD98401_RXFP_BFSZ	0x0f000000 /* buffer size, 64*2^n */
++#define uPD98401_RXFP_BFSZ_SHIFT 24
++#define uPD98401_RXFP_BTSZ	0x00ff0000 /* batch size, n+1 */
++#define uPD98401_RXFP_BTSZ_SHIFT 16
++#define uPD98401_RXFP_REMAIN	0x0000ffff /* remaining batches in pool */
++
++/*
++ * RX VC table
++ */
++
++#define uPD98401_RXVC_BTSZ	0xff000000 /* remaining free buffers in batch */
++#define uPD98401_RXVC_BTSZ_SHIFT 24
++#define uPD98401_RXVC_MB	0x00200000 /* RX mailbox number */
++#define uPD98401_RXVC_POOL	0x001f0000 /* free buffer pool number */
++#define uPD98401_RXVC_POOL_SHIFT 16
++#define uPD98401_RXVC_UINFO	0x0000ffff /* user-supplied information */
++#define uPD98401_RXVC_T1	0xffff0000 /* T1 timestamp */
++#define uPD98401_RXVC_T1_SHIFT	16
++#define uPD98401_RXVC_PR	0x00008000 /* Packet Reception, 1 if busy */
++#define uPD98401_RXVC_DR	0x00004000 /* FIFO Drop */
++#define uPD98401_RXVC_OD	0x00001000 /* Drop OAM cells */
++#define uPD98401_RXVC_AR	0x00000800 /* AAL5 or raw cell; 1 if AAL5 */
++#define uPD98401_RXVC_MAXSEG	0x000007ff /* max number of segments per PDU */
++#define uPD98401_RXVC_REM	0xfffe0000 /* remaining words in curr buffer */
++#define uPD98401_RXVC_REM_SHIFT	17
++#define uPD98401_RXVC_CLP	0x00010000 /* CLP received */
++#define uPD98401_RXVC_BFA	0x00008000 /* Buffer Assigned */
++#define uPD98401_RXVC_BTA	0x00004000 /* Batch Assigned */
++#define uPD98401_RXVC_CI	0x00002000 /* Congestion Indication */
++#define uPD98401_RXVC_DD	0x00001000 /* Dropping incoming cells */
++#define uPD98401_RXVC_DP	0x00000800 /* like PR ? */
++#define uPD98401_RXVC_CURSEG	0x000007ff /* Current Segment count */
++
++/*
++ * RX lookup table
++ */
++
++#define uPD98401_RXLT_ENBL	0x8000	   /* Enable */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/uPD98402.h	Mon Jun 10 17:36:08 1996
+@@ -0,0 +1,106 @@
++/* drivers/atm/uPD98402.h - NEC uPD98402 (PHY) declarations */
++ 
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef DRIVERS_ATM_uPD98402_H
++#define DRIVERS_ATM_uPD98402_H
++
++/*
++ * Registers
++ */
++
++#define uPD98402_CMR		0x00	/* Command Register */
++#define uPD98402_MDR		0x01	/* Mode Register */
++#define uPD98402_PICR		0x02	/* PHY Interrupt Cause Register */
++#define uPD98402_PIMR		0x03	/* PHY Interrupt Mask Register */
++#define uPD98402_ACR		0x04	/* Alarm Cause Register */
++#define uPD98402_ACMR		0x05	/* Alarm Cause Mask Register */
++#define uPD98402_PCR		0x06	/* Performance Cause Register */
++#define uPD98402_PCMR		0x07	/* Performance Cause Mask Register */
++#define uPD98402_IACM		0x08	/* Internal Alarm Cause Mask Register */
++#define uPD98402_B1ECT		0x09	/* B1 Error Count Register */
++#define uPD98402_B2ECT		0x0a	/* B2 Error Count Register */
++#define uPD98402_B3ECT		0x0b	/* B3 Error Count Regster */
++#define uPD98402_PFECB		0x0c	/* Path FEBE Count Register */
++#define uPD98402_LECCT		0x0d	/* Line FEBE Count Register */
++#define uPD98402_HECCT		0x0e	/* HEC Error Count Register */
++#define uPD98402_FJCT		0x0f	/* Frequence Justification Count Reg */
++#define uPD98402_PCOCR		0x10	/* Perf. Counter Overflow Cause Reg */
++#define uPD98402_PCOMR		0x11	/* Perf. Counter Overflow Mask Reg */
++#define uPD98402_C11T		0x20	/* C11T Data Register */
++#define uPD98402_C12T		0x21	/* C12T Data Register */
++#define uPD98402_C13T		0x22	/* C13T Data Register */
++#define uPD98402_F1T		0x23	/* F1T Data Register */
++#define uPD98402_K2T		0x25	/* K2T Data Register */
++#define uPD98402_C2T		0x26	/* C2T Data Register */
++#define uPD98402_F2T		0x27	/* F2T Data Register */
++#define uPD98402_C11R		0x30	/* C11T Data Register */
++#define uPD98402_C12R		0x31	/* C12T Data Register */
++#define uPD98402_C13R		0x32	/* C13T Data Register */
++#define uPD98402_F1R		0x33	/* F1T Data Register */
++#define uPD98402_K2R		0x35	/* K2T Data Register */
++#define uPD98402_C2R		0x36	/* C2T Data Register */
++#define uPD98402_F2R		0x37	/* F2T Data Register */
++
++/* CMR is at 0x00 */
++#define uPD98402_CMR_PFRF	0x01	/* Send path FERF */
++#define uPD98402_CMR_LFRF	0x02	/* Send line FERF */
++#define uPD98402_CMR_PAIS	0x04	/* Send path AIS */
++#define uPD98402_CMR_LAIS	0x08	/* Send line AIS */
++
++/* MDR is at 0x01 */
++#define uPD98402_MDR_ALP	0x01	/* ATM layer loopback */
++#define uPD98402_MDR_TPLP	0x02	/* PMD loopback, to host */
++#define uPD98402_MDR_RPLP	0x04	/* PMD loopback, to network */
++#define uPD98402_MDR_SS0	0x08	/* SS0 */
++#define uPD98402_MDR_SS1	0x10	/* SS1 */
++#define uPD98402_MDR_SS_MASK	0x18	/* mask */
++#define uPD98402_MDR_SS_SHIFT	3	/* shift */
++#define uPD98402_MDR_HEC	0x20	/* disable HEC inbound processing */
++#define uPD98402_MDR_FSR	0x40	/* disable frame scrambler */
++#define uPD98402_MDR_CSR	0x80	/* disable cell scrambler */
++
++/* PICR is at 0x02, PIMR is at 0x03 */
++#define uPD98402_INT_PFM	0x01	/* performance counter has changed */
++#define uPD98402_INT_ALM	0x02	/* line fault */
++#define uPD98402_INT_RFO	0x04	/* receive FIFO overflow */
++#define uPD98402_INT_PCO	0x08	/* performance counter overflow */
++#define uPD98402_INT_OTD	0x20	/* OTD has occurred */
++#define uPD98402_INT_LOS	0x40	/* Loss Of Signal */
++#define uPD98402_INT_LOF	0x80	/* Loss Of Frame */
++
++/* ACR is as 0x04, ACMR is at 0x05 */
++#define uPD98402_ALM_PFRF	0x01	/* path FERF */
++#define uPD98402_ALM_LFRF	0x02	/* line FERF */
++#define uPD98402_ALM_PAIS	0x04	/* path AIS */
++#define uPD98402_ALM_LAIS	0x08	/* line AIS */
++#define uPD98402_ALM_LOD	0x10	/* loss of delineation */
++#define uPD98402_ALM_LOP	0x20	/* loss of pointer */
++#define uPD98402_ALM_OOF	0x40	/* out of frame */
++
++/* PCR is at 0x06, PCMR is at 0x07 */
++#define uPD98402_PFM_PFEB	0x01	/* path FEBE */
++#define uPD98402_PFM_LFEB	0x02	/* line FEBE */
++#define uPD98402_PFM_B3E	0x04	/* B3 error */
++#define uPD98402_PFM_B2E	0x08	/* B2 error */
++#define uPD98402_PFM_B1E	0x10	/* B1 error */
++#define uPD98402_PFM_FJ		0x20	/* frequency justification */
++
++/* IACM is at 0x08 */
++#define uPD98402_IACM_PFRF	0x01	/* don't generate path FERF */
++#define uPD98402_IACM_LFRF	0x02	/* don't generate line FERF */
++
++/* PCOCR is at 0x010, PCOMR is at 0x11 */
++#define uPD98402_PCO_B1EC	0x01	/* B1ECT overflow */
++#define uPD98402_PCO_B2EC	0x02	/* B2ECT overflow */
++#define uPD98402_PCO_B3EC	0x04	/* B3ECT overflow */
++#define uPD98402_PCO_PFBC	0x08	/* PFEBC overflow */
++#define uPD98402_PCO_LFBC	0x10	/* LFEVC overflow */
++#define uPD98402_PCO_HECC	0x20	/* HECCT overflow */
++#define uPD98402_PCO_FJC	0x40	/* FJCT overflow */
++
++
++int uPD98402_init(struct atm_dev *dev);
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/uPD98402.c	Mon Jun 10 17:36:09 1996
+@@ -0,0 +1,201 @@
++/* drivers/atm/uPD98402.c - NEC uPD98402 (PHY) declarations */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#include <linux/sched.h> /* for jiffies */
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++#include <asm/segment.h>
++
++#include "uPD98402.h"
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++struct uPD98402_priv {
++	struct sonet_stats sonet_stats;	/* link diagnostics */
++	unsigned char framing;		/* SONET/SDH framing */
++};
++
++
++#define PRIV(dev) ((struct uPD98402_priv *) dev->phy_data)
++
++#define PUT(val,reg) dev->ops->phy_put(dev,val,uPD98402_##reg)
++#define GET(reg) dev->ops->phy_get(dev,uPD98402_##reg)
++
++
++static int fetch_stats(struct atm_dev *dev,struct sonet_stats *arg,int zero)
++{
++	unsigned long flags;
++ 
++	save_flags(flags);
++	cli();
++	PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT);
++	if (arg)
++		memcpy_tofs(arg,&PRIV(dev)->sonet_stats,
++		    sizeof(struct sonet_stats));
++	if (zero) {
++		memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
++		PRIV(dev)->sonet_stats.corr_hcs = -1;
++		PRIV(dev)->sonet_stats.tx_cells = -1;
++		PRIV(dev)->sonet_stats.rx_cells = -1;
++	}
++	restore_flags(flags);
++	return 0;
++}
++
++
++static int set_framing(struct atm_dev *dev,unsigned char framing)
++{
++	static const unsigned char sonet[] = { 1,2,3,0 };
++	static const unsigned char sdh[] = { 1,0,0,2 };
++	const char *set;
++	unsigned long flags;
++ 
++	switch (framing) {
++		case SONET_FRAME_SONET:
++			set = sonet;
++			break;
++		case SONET_FRAME_SDH:
++			set = sdh;
++			break;
++		default:
++			return -EINVAL;
++	}
++	save_flags(flags);
++	cli();
++	PUT(set[0],C11T);
++	PUT(set[1],C12T);
++	PUT(set[2],C13T);
++	PUT((GET(MDR) & ~uPD98402_MDR_SS_MASK) | (set[3] <<
++	    uPD98402_MDR_SS_SHIFT),MDR);
++	restore_flags(flags);
++	return 0;
++}
++
++
++static int get_sense(struct atm_dev *dev,unsigned long arg)
++{
++	unsigned long flags;
++ 
++	save_flags(flags);
++	cli();
++	put_fs_byte(GET(C11R),arg);
++	put_fs_byte(GET(C12R),arg+1);
++	put_fs_byte(GET(C13R),arg+2);
++	put_fs_byte(0xff,arg+3);
++	put_fs_byte(0xff,arg+4);
++	put_fs_byte(0xff,arg+5);
++	restore_flags(flags);
++	return 0;
++}
++
++
++static int uPD98402_ioctl(struct atm_dev *dev,unsigned int cmd,
++    unsigned long arg)
++{
++	switch (cmd) {
++
++		case SONET_GETSTATZ:
++                case SONET_GETSTAT:
++			return fetch_stats(dev,(struct sonet_stats *) arg,
++			    cmd == SONET_GETSTATZ);
++		case SONET_SETFRAMING:
++			return set_framing(dev,arg);
++		case SONET_GETFRAMING:
++			put_fs_long(PRIV(dev)->framing,arg);
++			return 0;
++		case SONET_GETFRSENSE:
++			return get_sense(dev,arg);
++		default:
++		    return -EINVAL;
++	}
++}
++
++
++static void stat_event(struct atm_dev *dev)
++{
++	unsigned char events;
++
++	events = GET(PCR);
++	if (events & uPD98402_PFM_PFEB)
++		if ((PRIV(dev)->sonet_stats.path_febe += GET(PFECB)) < 0)
++			PRIV(dev)->sonet_stats.path_febe = LONG_MAX;
++	if (events & uPD98402_PFM_LFEB)
++		if ((PRIV(dev)->sonet_stats.line_febe += GET(LECCT)) < 0)
++			PRIV(dev)->sonet_stats.line_febe = LONG_MAX;
++	if (events & uPD98402_PFM_B3E)
++		if ((PRIV(dev)->sonet_stats.path_bip += GET(B3ECT)) < 0)
++			PRIV(dev)->sonet_stats.path_bip = LONG_MAX;
++	if (events & uPD98402_PFM_B2E)
++		if ((PRIV(dev)->sonet_stats.line_bip += GET(B2ECT)) < 0)
++			PRIV(dev)->sonet_stats.line_bip = LONG_MAX;
++	if (events & uPD98402_PFM_B1E)
++		if ((PRIV(dev)->sonet_stats.section_bip += GET(B1ECT)) < 0)
++			PRIV(dev)->sonet_stats.section_bip = LONG_MAX;
++}
++
++
++static void uPD98402_int(struct atm_dev *dev)
++{
++	static unsigned long silence = 0;
++	unsigned char reason;
++
++	while ((reason = GET(PICR))) {
++		if (reason & uPD98402_INT_LOS)
++			printk(KERN_NOTICE "%s(itf %d): signal lost\n",
++			    dev->type,dev->number);
++		if (reason & uPD98402_INT_PFM) stat_event(dev);
++		if (reason & uPD98402_INT_PCO) {
++			(void) GET(PCOCR); /* clear interrupt cause */
++			PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT);
++		}
++		if ((reason & uPD98402_INT_RFO) && jiffies > silence) {
++			printk(KERN_WARNING "%s(itf %d): uPD98402 receive "
++			    "FIFO overflow\n",dev->type,dev->number);
++			silence = jiffies+HZ/2;
++		}
++	}
++}
++
++
++static int uPD98402_start(struct atm_dev *dev)
++{
++DPRINTK("phy_start\n");
++	if (!(PRIV(dev) = kmalloc(sizeof(struct uPD98402_priv),GFP_KERNEL)))
++		return -ENOMEM;
++	memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats));
++	(void) GET(PCR); /* clear performance events */
++	PUT(uPD98402_PFM_FJ,PCMR); /* ignore frequency adj */
++	(void) GET(PCOCR); /* clear overflows */
++	PUT(~uPD98402_PCO_HECC,PCOMR);
++	(void) GET(PICR); /* clear interrupts */
++	PUT(~(uPD98402_INT_PFM | uPD98402_INT_ALM | uPD98402_INT_RFO |
++	  uPD98402_INT_LOS),PIMR); /* enable them */
++	(void) fetch_stats(dev,NULL,1); /* clear kernel counters */
++	return 0;
++}
++
++
++
++static const struct atmphy_ops uPD98402_ops = {
++	uPD98402_start,
++	uPD98402_ioctl,	/* no ioctl yet */
++	uPD98402_int
++};
++
++
++int uPD98402_init(struct atm_dev *dev)
++{
++DPRINTK("phy_init\n");
++	dev->phy = &uPD98402_ops;
++	return 0;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/zatm.c	Tue Jul 30 22:08:08 1996
+@@ -0,0 +1,1659 @@
++/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/bios32.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <linux/delay.h>
++#include <linux/ioport.h> /* for request_region */
++#include <linux/uio.h>
++#include <asm/byteorder.h>
++#include <asm/system.h>
++#include <asm/string.h>
++#include <asm/io.h>
++
++#include "uPD98401.h"
++#include "uPD98402.h"
++#include "zeprom.h"
++#include "zatm.h"
++
++
++/*
++ * TODO:
++ *
++ * Minor features
++ *  - support 64 kB SDUs (will have to use multibuffer batches then :-( )
++ *  - proper use of CDV, credit = max(1,CDVT*PCR)
++ *  - AAL0
++ *  - better receive timestamps
++ *  - OAM
++ */
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++#ifndef CONFIG_ATM_ZATM_DEBUG
++
++
++#define NULLCHECK(x)
++
++#define EVENT(s,a,b)
++
++
++static void event_dump(void)
++{
++}
++
++
++#else
++
++
++/* 
++ * NULL pointer checking
++ */
++
++#define NULLCHECK(x) \
++  if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x))
++
++/*
++ * Very extensive activity logging. Greatly improves bug detection speed but
++ * costs a few Mbps if enabled.
++ */
++
++#define EV 64
++
++static const char *ev[EV];
++static unsigned long ev_a[EV],ev_b[EV];
++static int ec = 0;
++
++
++static void EVENT(const char *s,unsigned long a,unsigned long b)
++{
++	ev[ec] = s; 
++	ev_a[ec] = a;
++	ev_b[ec] = b;
++	ec = (ec+1) % EV;
++}
++
++
++static void event_dump(void)
++{
++	int n,i;
++
++	printk(KERN_NOTICE "----- event dump follows -----\n");
++	for (n = 0; n < EV; n++) {
++		i = (ec+n) % EV;
++		printk(KERN_NOTICE);
++		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
++	}
++	printk(KERN_NOTICE "----- event dump ends here -----\n");
++}
++
++
++#endif /* CONFIG_ATM_ZATM_DEBUG */
++
++
++#define RING_BUSY	1	/* indication from do_tx that PDU has to be
++				   backlogged */
++
++static struct atm_dev *zatm_boards = NULL;
++static unsigned long dummy[2] = {0,0};
++
++
++#define zin_n(r) inl_p(zatm_dev->base+r*4)
++#define zin(r) inl_p(zatm_dev->base+uPD98401_##r*4)
++#define zout(v,r) outl_p(v,zatm_dev->base+uPD98401_##r*4)
++#define zwait while (zin(CMR) & uPD98401_BUSY)
++
++/* RX0, RX1, TX0, TX1 */
++static const int mbx_entries[NR_MBX] = { 1024,1024,1024,1024 };
++static const int mbx_esize[NR_MBX] = { 16,16,4,4 }; /* entry size in bytes */
++
++#define MBX_SIZE(i) (mbx_entries[i]*mbx_esize[i])
++
++
++/*-------------------------------- utilities --------------------------------*/
++
++
++static void zpokel(struct zatm_dev *zatm_dev,unsigned long value,
++    unsigned long addr)
++{
++	zwait;
++	zout(value,CER);
++	zout(uPD98401_IND_ACC | uPD98401_IA_BALL |
++	    (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
++}
++
++
++static unsigned long zpeekl(struct zatm_dev *zatm_dev,unsigned long addr)
++{
++	zwait;
++	zout(uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |
++	  (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);
++	zwait;
++	return zin(CER);
++}
++
++
++/*------------------------------- free lists --------------------------------*/
++
++
++#define HEAD_SIZE (3*sizeof(void *))
++
++
++static void refill_pool(struct atm_dev *dev,int pool)
++{
++	struct zatm_dev *zatm_dev;
++	struct sk_buff *skb,*first;
++	unsigned long flags;
++	int align,offset,free,count,size;
++
++	EVENT("refill_pool\n",0,0);
++	zatm_dev = ZATM_DEV(dev);
++	size = (64 << (pool <= ZATM_AAL5_POOL_BASE ? 0 :
++	    pool-ZATM_AAL5_POOL_BASE))+HEAD_SIZE;
++	if (size < PAGE_SIZE) {
++		align = 32; /* for 32 byte alignment */
++		offset = HEAD_SIZE;
++	}
++	else {
++		align = 4096;
++		offset = zatm_dev->pool_info[pool].offset+HEAD_SIZE;
++	}
++	size += align;
++	save_flags(flags);
++	cli();
++	free = zpeekl(zatm_dev,zatm_dev->pool_base+2*pool) &
++	    uPD98401_RXFP_REMAIN;
++	restore_flags(flags);
++	if (free >= zatm_dev->pool_info[pool].low_water) return;
++	EVENT("starting ... POOL: 0x%08lx, 0x%08lx\n",
++	    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
++	    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
++	EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
++	count = 0;
++	first = NULL;
++	while (free < zatm_dev->pool_info[pool].high_water) {
++		skb = alloc_skb(size,GFP_ATOMIC);
++		if (!skb) {
++			printk(KERN_WARNING DEV_LABEL "(Itf %d): got no new "
++			    "skb (%d) with %d free\n",dev->number,size,free);
++			break;
++		}
++		if (!first) first = skb;
++		count++;
++		/*
++		 * 2nd skb head structure:
++		 *   [0] pointer to buffer (for SAR)
++		 *   [1] buffer descr link pointer (for SAR)
++		 *   [2] back pointer to skb (for poll_rx)
++		 *   [3] data
++		 *   ...
++		 */
++		skb->free = 1;
++		skb->data = (void *) ((((unsigned long) skb->data+align+
++		    offset-1) & ~(align-1))-offset);
++		((void **) skb->data)[0] = skb->data+HEAD_SIZE;
++		((void **) skb->data)[1] = NULL;
++		((void **) skb->data)[2] = skb;
++		EVENT("enq skb 0x%08lx/0x%08lx\n",(unsigned long) skb,
++		    (unsigned long) skb->data);
++		cli();
++		if (zatm_dev->last_free[pool])
++			((void **) zatm_dev->last_free[pool]->data)[-2] =
++			    skb->data;
++		zatm_dev->last_free[pool] = skb;
++		skb->data += HEAD_SIZE;
++		skb_queue_tail(&zatm_dev->pool[pool],skb);
++		restore_flags(flags);
++		free++;
++	}
++	if (first) {
++		cli();
++		zwait;
++		zout((unsigned long) first->data-HEAD_SIZE,CER);
++		zout(uPD98401_ADD_BAT | (pool << uPD98401_POOL_SHIFT) | count,
++		    CMR);
++		restore_flags(flags);
++		EVENT ("POOL: 0x%08lx, 0x%08lx\n",
++		    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),
++		    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));
++		EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
++	}
++}
++
++
++static void drain_free(struct atm_dev *dev,int pool)
++{
++	struct sk_buff *skb;
++
++	while ((skb = skb_dequeue(&ZATM_DEV(dev)->pool[pool])))
++		kfree_skb(skb,FREE_READ);
++}
++
++
++static int pool_index(int max_pdu)
++{
++	int i;
++
++	max_pdu += ATM_CELL_PAYLOAD-1;
++	if (max_pdu > 65536) return -1;
++	for (i = 0; (64 << i) < max_pdu; i++);
++	return i+ZATM_AAL5_POOL_BASE;
++}
++
++
++/* use_pool isn't reentrant */
++
++
++static void use_pool(struct atm_dev *dev,int pool)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned long flags;
++	int size;
++
++	zatm_dev = ZATM_DEV(dev);
++	if (!(zatm_dev->pool_info[pool].ref_count++)) {
++		skb_queue_head_init(&zatm_dev->pool[pool]);
++		size = pool-ZATM_AAL5_POOL_BASE;
++		if (size < 0) size = 0; /* 64B... */
++		else if (size > 10) size = 10; /* ... 64kB */
++		save_flags(flags);
++		cli();
++		zpokel(zatm_dev,((zatm_dev->pool_info[pool].low_water/4) <<
++		    uPD98401_RXFP_ALERT_SHIFT) |
++		    (1 << uPD98401_RXFP_BTSZ_SHIFT) |
++		    (size << uPD98401_RXFP_BFSZ_SHIFT),
++		    zatm_dev->pool_base+pool*2);
++		zpokel(zatm_dev,(unsigned long) dummy,zatm_dev->pool_base+
++		    pool*2+1);
++		restore_flags(flags);
++		zatm_dev->last_free[pool] = NULL;
++		refill_pool(dev,pool);
++	}
++	DPRINTK("pool %d: %d\n",pool,zatm_dev->pool_info[pool].ref_count);
++}
++
++
++static void unuse_pool(struct atm_dev *dev,int pool)
++{
++	if (!(--ZATM_DEV(dev)->pool_info[pool].ref_count))
++		drain_free(dev,pool);
++}
++
++
++static void zatm_feedback(struct atm_vcc *vcc,struct sk_buff *skb,
++    unsigned long start,unsigned long dest,int len)
++{
++	struct zatm_pool_info *pool;
++	unsigned long offset,flags;
++
++	DPRINTK("start 0x%08lx dest 0x%08lx len %d\n",start,dest,len);
++	if (len < PAGE_SIZE) return;
++	pool = &ZATM_DEV(vcc->dev)->pool_info[ZATM_VCC(vcc)->pool];
++	offset = (dest-start) & (PAGE_SIZE-1);
++	save_flags(flags);
++	cli();
++	if (!offset || pool->offset == offset) {
++		pool->next_cnt = 0;
++		return;
++	}
++	if (offset != pool->next_off) {
++		pool->next_off = offset;
++		pool->next_cnt = 0;
++		return;
++	}
++	if (++pool->next_cnt >= pool->next_thres) {
++		pool->offset = pool->next_off;
++		pool->next_cnt = 0;
++	}
++	restore_flags(flags);
++}
++
++
++/*----------------------------------- RX ------------------------------------*/
++
++
++#if 0
++static void exception(struct atm_vcc *vcc)
++{
++   static int count = 0;
++   struct zatm_dev *zatm_dev = ZATM_DEV(vcc->dev);
++   struct zatm_vcc *zatm_vcc = ZATM_VCC(vcc);
++   unsigned long *qrp;
++   int i;
++
++   if (count++ > 2) return;
++   for (i = 0; i < 8; i++)
++	printk("TX%d: 0x%08lx\n",i,
++	  zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+i));
++   for (i = 0; i < 5; i++)
++	printk("SH%d: 0x%08lx\n",i,
++	  zpeekl(zatm_dev,uPD98401_IM(zatm_vcc->shaper)+16*i));
++   qrp = (unsigned long *) zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
++     uPD98401_TXVC_QRP);
++   printk("qrp=0x%08lx\n",(unsigned long) qrp);
++   for (i = 0; i < 4; i++) printk("QRP[%d]: 0x%08lx",i,qrp[i]);
++}
++#endif
++
++
++static const char *err_txt[] = {
++	"No error",
++	"RX buf underflow",
++	"RX FIFO overrun",
++	"Maximum len violation",
++	"CRC error",
++	"User abort",
++	"Length violation",
++	"T1 error",
++	"Deactivated",
++	"???",
++	"???",
++	"???",
++	"???",
++	"???",
++	"???",
++	"???"
++};
++
++
++static void poll_rx(struct atm_dev *dev,int mbx)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned long pos,x;
++	int error;
++
++	EVENT("poll_rx\n",0,0);
++	zatm_dev = ZATM_DEV(dev);
++	pos = (zatm_dev->mbx_start[mbx] & ~0xffff) | zin(MTA(mbx));
++	while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
++		unsigned long *here;
++		struct sk_buff *skb;
++		struct atm_vcc *vcc;
++		int cells,size,chan;
++
++		EVENT("MBX: host 0x%lx, nic 0x%lx\n",pos,x);
++		here = (unsigned long *) pos;
++		if (((pos += 16) & 0xffff) == zatm_dev->mbx_end[mbx])
++			pos = zatm_dev->mbx_start[mbx];
++		cells = here[0] & uPD98401_AAL5_SIZE;
++#if 0
++{
++unsigned long *x;
++printk("RX IND: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",here[0],here[1],here[2],
++  here[3]);
++		printk("POOL: 0x%08lx, 0x%08lx\n",zpeekl(zatm_dev,
++		      zatm_dev->pool_base),
++		      zpeekl(zatm_dev,zatm_dev->pool_base+1));
++		x = (unsigned long *) here[2];
++		printk("[0..3] = 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n",
++		    x[0],x[1],x[2],x[3]);
++}
++#endif
++		error = 0;
++		if (here[3] & uPD98401_AAL5_ERR) {
++			error = (here[3] & uPD98401_AAL5_ES) >>
++			    uPD98401_AAL5_ES_SHIFT;
++			if (error == uPD98401_AAL5_ES_DEACT ||
++			    error == uPD98401_AAL5_ES_FREE) continue;
++		}
++EVENT("error code 0x%x/0x%x\n",(here[3] & uPD98401_AAL5_ES) >>
++  uPD98401_AAL5_ES_SHIFT,error);
++		skb = (struct sk_buff *) ((void **) here[2])[2];
++		skb->atm.timestamp = xtime;
++#if 0
++printk("[-3..0] 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",((unsigned *) skb->data)[-3],
++  ((unsigned *) skb->data)[-2],((unsigned *) skb->data)[-1],
++  ((unsigned *) skb->data)[0]);
++#endif
++		EVENT("skb 0x%08lx, here 0x%08lx\n",(unsigned long) skb,
++		    (unsigned long) here);
++#if 0
++printk("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
++#endif
++		size = error ? 0 : ntohs(((unsigned short *) skb->data)[cells*
++		    ATM_CELL_PAYLOAD/sizeof(unsigned short)-3]);
++		EVENT("got skb 0x%lx, size %d\n",(unsigned long) skb,size);
++		chan = (here[3] & uPD98401_AAL5_CHAN) >>
++		    uPD98401_AAL5_CHAN_SHIFT;
++		if (chan < zatm_dev->chans && zatm_dev->rx_map[chan]) {
++			vcc = zatm_dev->rx_map[chan];
++			if (skb == zatm_dev->last_free[ZATM_VCC(vcc)->pool])
++				zatm_dev->last_free[ZATM_VCC(vcc)->pool] = NULL;
++			skb_unlink(skb);
++		}
++		else {
++			printk(KERN_ERR DEV_LABEL "(itf %d): RX indication "
++			    "for non-existing channel\n",dev->number);
++			size = 0;
++			vcc = NULL;
++			event_dump();
++		}
++		if (error) {
++			static unsigned long silence = 0;
++			static int last_error = 0;
++
++			if (error != last_error || jiffies > silence) {
++				printk(KERN_WARNING DEV_LABEL "(itf %d): "
++				    "chan %d error %s\n",dev->number,chan,
++				    err_txt[error]);
++				last_error = error;
++				silence = jiffies+2*HZ;
++			}
++			size = 0;
++		}
++		if (size && (size > cells*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER ||
++		    size <= (cells-1)*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER)) {
++			printk(KERN_ERR DEV_LABEL "(itf %d): size %d with %d "
++			    "cells\n",dev->number,size,cells);
++			size = 0;
++			event_dump();
++		}
++		if (size > ATM_MAX_AAL5_PDU) {
++			printk(KERN_ERR DEV_LABEL "(itf %d): size too big "
++			    "(%d)\n",dev->number,size);
++			size = 0;
++			event_dump();
++		}
++		if (!size) {
++			kfree_skb(skb,FREE_READ);
++			if (vcc) vcc->stats->rx_err++;
++		}
++		else {
++			skb->len = size;
++			skb->atm.vcc = vcc;
++			vcc->push(skb->atm.vcc,skb);
++			vcc->stats->rx++;
++		}
++#if 0
++if (vcc && size == 2) exception(vcc);
++#endif
++	}
++	zout(pos & 0xffff,MTA(mbx));
++#if 0 /* probably a stupid idea */
++	refill_pool(dev,zatm_vcc->pool);
++		/* maybe this saves us a few interrupts */
++#endif
++}
++
++
++static int open_rx_first(struct atm_vcc *vcc)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	unsigned long flags;
++	unsigned short chan;
++	int cells;
++
++	DPRINTK("open_rx_first (0x%x)\n",inb_p(0xc053));
++	zatm_dev = ZATM_DEV(vcc->dev);
++	zatm_vcc = ZATM_VCC(vcc);
++	zatm_vcc->rx_chan = 0;
++	if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
++	if (vcc->aal == ATM_AAL5) {
++		if (vcc->qos.rxtp.max_sdu > 65464)
++			vcc->qos.rxtp.max_sdu = 65464;
++			/* fix this - we may want to receive 64kB SDUs
++			   later */
++		cells = (vcc->qos.rxtp.max_sdu+ATM_AAL5_TRAILER+
++		    ATM_CELL_PAYLOAD-1)/ATM_CELL_PAYLOAD;
++		zatm_vcc->pool = pool_index(cells*ATM_CELL_PAYLOAD);
++	}
++	else {
++		cells = 1;
++		zatm_vcc->pool = ZATM_AAL0_POOL;
++	}
++	if (zatm_vcc->pool < 0) return -EMSGSIZE;
++	save_flags(flags);
++	cli();
++	zwait;
++	zout(uPD98401_OPEN_CHAN,CMR);
++	zwait;
++	DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
++	chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
++	restore_flags(flags);
++	DPRINTK("chan is %d\n",chan);
++	if (!chan) return -EAGAIN;
++	use_pool(vcc->dev,zatm_vcc->pool);
++	DPRINTK("pool %d\n",zatm_vcc->pool);
++	/* set up VC descriptor */
++	cli();
++	zpokel(zatm_dev,zatm_vcc->pool << uPD98401_RXVC_POOL_SHIFT,
++	    chan*VC_SIZE/4);
++	zpokel(zatm_dev,uPD98401_RXVC_OD | (vcc->aal == ATM_AAL5 ?
++	    uPD98401_RXVC_AR : 0) | cells,chan*VC_SIZE/4+1);
++	zpokel(zatm_dev,0,chan*VC_SIZE/4+2);
++	zatm_vcc->rx_chan = chan;
++	zatm_dev->rx_map[chan] = vcc;
++	restore_flags(flags);
++	return 0;
++}
++
++
++static int open_rx_second(struct atm_vcc *vcc)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	unsigned long flags;
++	int pos,shift;
++
++	DPRINTK("open_rx_second (0x%x)\n",inb_p(0xc053));
++	zatm_dev = ZATM_DEV(vcc->dev);
++	zatm_vcc = ZATM_VCC(vcc);
++	if (!zatm_vcc->rx_chan) return 0;
++	save_flags(flags);
++	cli();
++	/* should also handle VPI @@@ */
++	pos = vcc->vci >> 1;
++	shift = (1-(vcc->vci & 1)) << 4;
++	zpokel(zatm_dev,(zpeekl(zatm_dev,pos) & ~(0xffff << shift)) |
++	    ((zatm_vcc->rx_chan | uPD98401_RXLT_ENBL) << shift),pos);
++	restore_flags(flags);
++	return 0;
++}
++
++
++static void close_rx(struct atm_vcc *vcc)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	unsigned long flags;
++	int pos,shift;
++
++	zatm_vcc = ZATM_VCC(vcc);
++	zatm_dev = ZATM_DEV(vcc->dev);
++	if (!zatm_vcc->rx_chan) return;
++DPRINTK("close_rx\n");
++	/* disable receiver */
++	save_flags(flags);
++	if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
++		cli();
++		pos = vcc->vci >> 1;
++		shift = (1-(vcc->vci & 1)) << 4;
++		zpokel(zatm_dev,zpeekl(zatm_dev,pos) & ~(0xffff << shift),pos);
++		restore_flags(flags);
++		udelay(48000/zatm_dev->khz+1);
++	}
++	cli();
++	zwait;
++	zout(uPD98401_DEACT_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
++	    uPD98401_CHAN_ADDR_SHIFT),CMR);
++	zwait;
++	zout(uPD98401_CLOSE_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
++	    uPD98401_CHAN_ADDR_SHIFT),CMR);
++	zwait;
++	if (!(zin(CMR) & uPD98401_CHAN_ADDR))
++		printk(KERN_CRIT DEV_LABEL "(itf %d): can't close RX channel "
++		    "%d\n",vcc->dev->number,zatm_vcc->rx_chan);
++	restore_flags(flags);
++	zatm_dev->rx_map[zatm_vcc->rx_chan] = NULL;
++	zatm_vcc->rx_chan = 0;
++	unuse_pool(vcc->dev,zatm_vcc->pool);
++}
++
++
++static int start_rx(struct atm_dev *dev)
++{
++	struct zatm_dev *zatm_dev;
++	int size,i;
++
++DPRINTK("start_rx\n");
++	zatm_dev = ZATM_DEV(dev);
++	size = sizeof(struct atm_vcc *)*zatm_dev->chans;
++	zatm_dev->rx_map = (struct atm_vcc **) kmalloc(size,GFP_KERNEL);
++	if (!zatm_dev->rx_map) return -ENOMEM;
++	memset(zatm_dev->rx_map,0,size);
++	/* set VPI/VCI split (use all VCIs and give what's left to VPIs) */
++	zpokel(zatm_dev,(1 << dev->ci_range.vci_bits)-1,uPD98401_VRR);
++	/* prepare free buffer pools */
++	for (i = 0; i <= ZATM_LAST_POOL; i++) {
++		zatm_dev->pool_info[i].ref_count = 0;
++		zatm_dev->pool_info[i].rqa_count = 0;
++		zatm_dev->pool_info[i].rqu_count = 0;
++		zatm_dev->pool_info[i].low_water = LOW_MARK;
++		zatm_dev->pool_info[i].high_water = HIGH_MARK;
++		zatm_dev->pool_info[i].offset = 0;
++		zatm_dev->pool_info[i].next_off = 0;
++		zatm_dev->pool_info[i].next_cnt = 0;
++		zatm_dev->pool_info[i].next_thres = OFF_CNG_THRES;
++	}
++	return 0;
++}
++
++
++/*----------------------------------- TX ------------------------------------*/
++
++
++static int do_tx(struct sk_buff *skb)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	unsigned long *dsc;
++	unsigned long flags;
++
++	EVENT("do_tx\n",0,0);
++	DPRINTK("sending skb %p\n",skb);
++	zatm_dev = ZATM_DEV(skb->atm.vcc->dev);
++	zatm_vcc = ZATM_VCC(skb->atm.vcc);
++	EVENT("iovcnt=%d\n",skb->atm.iovcnt,0);
++	save_flags(flags);
++	cli();
++	if (!skb->atm.iovcnt) {
++		if (zatm_vcc->txing == RING_ENTRIES-1) {
++			restore_flags(flags);
++			return RING_BUSY;
++		}
++		zatm_vcc->txing++;
++		dsc = zatm_vcc->ring+zatm_vcc->ring_curr;
++		zatm_vcc->ring_curr = (zatm_vcc->ring_curr+RING_WORDS) &
++		    (RING_ENTRIES*RING_WORDS-1);
++		dsc[1] = 0;
++		dsc[2] = skb->len;
++		dsc[3] = (unsigned long) skb->data;
++		mb();
++		dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM
++		    | (skb->atm.vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0);
++		EVENT("dsc (0x%08lx)\n",(unsigned long) dsc,0);
++	}
++	else {
++printk("NONONONOO!!!!\n");
++#if 0
++		unsigned long *put;
++		int i;
++
++		dsc = (unsigned long *) kmalloc(uPD98401_TXPD_SIZE*2+
++		    uPD98401_TXBD_SIZE*skb->atm.iovcnt,GFP_ATOMIC);
++		if (!dsc) {
++			dev_kfree_skb(skb,FREE_WRITE);
++			return -EAGAIN;
++		}
++		/* @@@ should check alignment */
++		put = dsc+8;
++		dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP |
++		    (skb->atm.vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0);
++		dsc[1] = 0;
++		dsc[2] = skb->atm.iovcnt*uPD98401_TXBD_SIZE;
++		dsc[3] = (unsigned long) put;
++		for (i = 0; i < skb->atm.iovcnt; i++) {
++			*put++ = ((struct iovec *) skb->data)[i].iov_len;
++			*put++ = (unsigned long) ((struct iovec *)
++			    skb->data)[i].iov_base;
++		}
++		put[-2] |= uPD98401_TXBD_LAST;
++#endif
++	}
++	skb->atm.pos = (unsigned long) dsc;
++	skb_queue_tail(&zatm_vcc->tx_queue,skb);
++	DPRINTK("QRP=0x%08lx\n",zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
++	  uPD98401_TXVC_QRP));
++	zwait;
++	zout(uPD98401_TX_READY | (zatm_vcc->tx_chan <<
++	    uPD98401_CHAN_ADDR_SHIFT),CMR);
++	restore_flags(flags);
++	EVENT("done\n",0,0);
++	return 0;
++}
++
++
++static inline void dequeue_tx(struct atm_vcc *vcc)
++{
++	struct zatm_vcc *zatm_vcc;
++	struct sk_buff *skb;
++
++	EVENT("dequeue_tx\n",0,0);
++	zatm_vcc = ZATM_VCC(vcc);
++	skb = skb_dequeue(&zatm_vcc->tx_queue);
++	if (!skb) {
++		printk(KERN_CRIT DEV_LABEL "(itf %d): dequeue_tx but not "
++		    "txing\n",vcc->dev->number);
++		return;
++	}
++if (*(unsigned long *) skb->atm.pos != (uPD98401_TXPD_V | uPD98401_TXPD_DP |
++  uPD98401_TXPD_SM | uPD98401_TXPD_AAL5)) printk("@#*$!!!!  (%08lx)\n",
++  *(unsigned long *) skb->atm.pos);
++	*(unsigned long *) skb->atm.pos = 0; /* mark as invalid */
++	zatm_vcc->txing--;
++	if (vcc->pop) vcc->pop(vcc,skb);
++	else dev_kfree_skb(skb,FREE_WRITE);
++	while ((skb = skb_dequeue(&zatm_vcc->backlog)))
++		if (do_tx(skb) == RING_BUSY) {
++			skb_queue_head(&zatm_vcc->backlog,skb);
++			break;
++		}
++	vcc->stats->tx++;
++	wake_up(&zatm_vcc->tx_wait);
++}
++
++
++static void poll_tx(struct atm_dev *dev,int mbx)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned long pos,x;
++
++	EVENT("poll_tx\n",0,0);
++	zatm_dev = ZATM_DEV(dev);
++	pos = (zatm_dev->mbx_start[mbx] & ~0xffff) | zin(MTA(mbx));
++	while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {
++		int chan;
++
++#if 1
++  unsigned long data,*addr;
++
++		EVENT("MBX: host 0x%lx, nic 0x%lx\n",pos,x);
++		addr = (unsigned long *) pos;
++		data = *addr;
++		chan = (data & uPD98401_TXI_CONN) >> uPD98401_TXI_CONN_SHIFT;
++		EVENT("addr = 0x%08lx, data = 0x%08lx,",(unsigned long) addr,
++		    data);
++		EVENT("chan = %d\n",chan,0);
++#else
++NO !
++		chan = (zatm_dev->mbx_start[mbx][pos >> 2] & uPD98401_TXI_CONN)
++		>> uPD98401_TXI_CONN_SHIFT;
++#endif
++		if (chan < zatm_dev->chans && zatm_dev->tx_map[chan])
++			dequeue_tx(zatm_dev->tx_map[chan]);
++		else {
++			printk(KERN_CRIT DEV_LABEL "(itf %d): TX indication "
++			    "for non-existing channel %d\n",dev->number,chan);
++			event_dump();
++		}
++		if (((pos += 4) & 0xffff) == zatm_dev->mbx_end[mbx])
++			pos = zatm_dev->mbx_start[mbx];
++	}
++	zout(pos & 0xffff,MTA(mbx));
++}
++
++
++static int alloc_shaper(struct atm_dev *dev,int *pcr,int min,int max,int ubr)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned long flags;
++	unsigned long i,m,c;
++	int shaper;
++
++	DPRINTK("alloc_shaper (min = %d, max = %d)\n",min,max);
++	zatm_dev = ZATM_DEV(dev);
++	if (!zatm_dev->free_shapers) return -EAGAIN;
++	for (shaper = 0; !((zatm_dev->free_shapers >> shaper) & 1); shaper++);
++	zatm_dev->free_shapers &= ~1 << shaper;
++	if (ubr) {
++		c = 5;
++		i = m = 1;
++		zatm_dev->ubr_ref_cnt++;
++		zatm_dev->ubr = shaper;
++	}
++	else {
++		if (min) {
++			if (min <= 255) {
++				i = min;
++				m = ATM_OC3_PCR;
++			}
++			else {
++				i = 255;
++				m = ATM_OC3_PCR*255/min;
++			}
++		}
++		else {
++			if (max > zatm_dev->tx_bw) max = zatm_dev->tx_bw;
++			if (max <= 255) {
++				i = max;
++				m = ATM_OC3_PCR;
++			}
++			else {
++				i = 255;
++				m = (ATM_OC3_PCR*255+max-1)/max;
++			}
++		}
++		if (i > m) {
++			printk(KERN_CRIT DEV_LABEL "shaper algorithm botched "
++			    "[%d,%d] -> i=%ld,m=%ld\n",min,max,i,m);
++			m = i;
++		}
++		*pcr = i*ATM_OC3_PCR/m;
++		c = 20; /* @@@ should use max_cdv ! */
++		if ((min && *pcr < min) || (max && *pcr > max)) return -EINVAL;
++		if (zatm_dev->tx_bw < *pcr) return -EAGAIN;
++		zatm_dev->tx_bw -= *pcr;
++	}
++	save_flags(flags);
++	cli();
++	DPRINTK("i = %d, m = %d, PCR = %d\n",i,m,*pcr);
++	zpokel(zatm_dev,(i << uPD98401_IM_I_SHIFT) | m,uPD98401_IM(shaper));
++	zpokel(zatm_dev,c << uPD98401_PC_C_SHIFT,uPD98401_PC(shaper));
++	zpokel(zatm_dev,0,uPD98401_X(shaper));
++	zpokel(zatm_dev,0,uPD98401_Y(shaper));
++	zpokel(zatm_dev,uPD98401_PS_E,uPD98401_PS(shaper));
++	restore_flags(flags);
++	return shaper;
++}
++
++
++static void dealloc_shaper(struct atm_dev *dev,int shaper)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned long flags;
++
++	zatm_dev = ZATM_DEV(dev);
++	if (shaper == zatm_dev->ubr) {
++		if (--zatm_dev->ubr_ref_cnt) return;
++		zatm_dev->ubr = -1;
++	}
++	save_flags(flags);
++	cli();
++	zpokel(zatm_dev,zpeekl(zatm_dev,uPD98401_PS(shaper)) & ~uPD98401_PS_E,
++	    uPD98401_PS(shaper));
++	restore_flags(flags);
++	zatm_dev->free_shapers |= 1 << shaper;
++}
++
++
++static void close_tx(struct atm_vcc *vcc)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	unsigned long flags;
++	int chan;
++struct sk_buff *skb;
++int once = 1;
++
++	zatm_vcc = ZATM_VCC(vcc);
++	zatm_dev = ZATM_DEV(vcc->dev);
++	chan = zatm_vcc->tx_chan;
++	if (!chan) return;
++	DPRINTK("close_tx\n");
++	save_flags(flags);
++	cli();
++	while (skb_peek(&zatm_vcc->backlog)) {
++if (once) {
++printk("waiting for backlog to drain ...\n");
++event_dump();
++once = 0;
++}
++		sleep_on(&zatm_vcc->tx_wait);
++	}
++once = 1;
++	while ((skb = skb_peek(&zatm_vcc->tx_queue))) {
++if (once) {
++printk("waiting for TX queue to drain ... %p\n",skb);
++event_dump();
++once = 0;
++}
++		DPRINTK("waiting for TX queue to drain ... %p\n",skb);
++		sleep_on(&zatm_vcc->tx_wait);
++	}
++#if 0
++	zwait;
++	zout(uPD98401_DEACT_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
++#endif
++	zwait;
++	zout(uPD98401_CLOSE_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
++	zwait;
++	if (!(zin(CMR) & uPD98401_CHAN_ADDR))
++		printk(KERN_CRIT DEV_LABEL "(itf %d): can't close TX channel "
++		    "%d\n",vcc->dev->number,chan);
++	restore_flags(flags);
++	zatm_vcc->tx_chan = 0;
++	zatm_dev->tx_map[chan] = NULL;
++	if (zatm_vcc->shaper != zatm_dev->ubr) {
++		zatm_dev->tx_bw += vcc->qos.txtp.min_pcr;
++		dealloc_shaper(vcc->dev,zatm_vcc->shaper);
++	}
++	if (zatm_vcc->ring) kfree(zatm_vcc->ring);
++}
++
++
++static int open_tx_first(struct atm_vcc *vcc)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	unsigned long flags,*loop;
++	unsigned short chan;
++	int pcr;
++
++	DPRINTK("open_tx_first\n");
++	zatm_dev = ZATM_DEV(vcc->dev);
++	zatm_vcc = ZATM_VCC(vcc);
++	zatm_vcc->tx_chan = 0;
++	if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
++	save_flags(flags);
++	cli();
++	zwait;
++	zout(uPD98401_OPEN_CHAN,CMR);
++	zwait;
++	DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));
++	chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;
++	restore_flags(flags);
++	DPRINTK("chan is %d\n",chan);
++	if (!chan) return -EAGAIN;
++	if (vcc->qos.txtp.traffic_class == ATM_UBR && zatm_dev->ubr != -1)
++		zatm_vcc->shaper = zatm_dev->ubr;
++	else {
++		if (vcc->qos.txtp.traffic_class == ATM_UBR)
++			vcc->qos.txtp.max_sdu = ATM_MAX_AAL5_PDU;
++		if ((zatm_vcc->shaper = alloc_shaper(vcc->dev,&pcr,
++		    vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,
++		    vcc->qos.txtp.traffic_class == ATM_UBR)) < 0) {
++			close_tx(vcc);
++			return zatm_vcc->shaper;
++		}
++		vcc->qos.txtp.min_pcr = vcc->qos.txtp.max_pcr = pcr;
++	}
++	zatm_vcc->tx_chan = chan;
++	skb_queue_head_init(&zatm_vcc->tx_queue);
++	zatm_vcc->tx_wait = NULL;
++	/* initialize ring */
++	zatm_vcc->ring = kmalloc(RING_SIZE,GFP_KERNEL);
++	if (!zatm_vcc->ring) return -ENOMEM;
++	memset(zatm_vcc->ring,0,RING_SIZE);
++	loop = zatm_vcc->ring+RING_ENTRIES*RING_WORDS;
++	loop[0] = uPD98401_TXPD_V;
++	loop[1] = loop[2] = 0;
++	loop[3] = (unsigned long) zatm_vcc->ring;
++	zatm_vcc->ring_curr = 0;
++	zatm_vcc->txing = 0;
++	skb_queue_head_init(&zatm_vcc->backlog);
++	zpokel(zatm_dev,(unsigned long) zatm_vcc->ring,
++	    chan*VC_SIZE/4+uPD98401_TXVC_QRP);
++	return 0;
++}
++
++
++static int open_tx_second(struct atm_vcc *vcc)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	unsigned long flags;
++
++	DPRINTK("open_tx_second\n");
++	zatm_dev = ZATM_DEV(vcc->dev);
++	zatm_vcc = ZATM_VCC(vcc);
++	if (!zatm_vcc->tx_chan) return 0;
++	save_flags(flags);
++	/* set up VC descriptor */
++	cli();
++	zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4);
++	zpokel(zatm_dev,uPD98401_TXVC_L | (zatm_vcc->shaper <<
++	    uPD98401_TXVC_SHP_SHIFT) | (vcc->vpi << uPD98401_TXVC_VPI_SHIFT) |
++	    vcc->vci,zatm_vcc->tx_chan*VC_SIZE/4+1);
++	zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4+2);
++	restore_flags(flags);
++	zatm_dev->tx_map[zatm_vcc->tx_chan] = vcc;
++	return 0;
++}
++
++
++static int start_tx(struct atm_dev *dev)
++{
++	struct zatm_dev *zatm_dev;
++	int i;
++
++	DPRINTK("start_tx\n");
++	zatm_dev = ZATM_DEV(dev);
++	zatm_dev->tx_map = (struct atm_vcc **) kmalloc(sizeof(struct atm_vcc *)*
++	    zatm_dev->chans,GFP_KERNEL);
++	if (!zatm_dev->tx_map) return -ENOMEM;
++	zatm_dev->tx_bw = ATM_OC3_PCR;
++	zatm_dev->free_shapers = (1 << NR_SHAPERS)-1;
++	zatm_dev->ubr = -1;
++	zatm_dev->ubr_ref_cnt = 0;
++	/* initialize shapers */
++	for (i = 0; i < NR_SHAPERS; i++) zpokel(zatm_dev,0,uPD98401_PS(i));
++	return 0;
++}
++
++
++/*------------------------------- interrupts --------------------------------*/
++
++
++static void zatm_int(int irq,void *dev_id,struct pt_regs *regs)
++{
++	struct atm_dev *dev;
++	struct zatm_dev *zatm_dev;
++	unsigned long reason;
++
++	dev = dev_id;
++	zatm_dev = ZATM_DEV(dev);
++	while ((reason = zin(GSR))) {
++		EVENT("reason 0x%lx\n",reason,0);
++		if (reason & uPD98401_INT_PI) {
++			EVENT("PHY int\n",0,0);
++			dev->phy->interrupt(dev);
++		}
++		if (reason & uPD98401_INT_RQA) {
++			unsigned long pools;
++			int i;
++
++			pools = zin(RQA);
++			EVENT("RQA (0x%08lx)\n",pools,0);
++			for (i = 0; pools; i++) {
++				if (pools & 1) {
++					refill_pool(dev,i);
++					zatm_dev->pool_info[i].rqa_count++;
++				}
++				pools >>= 1;
++			}
++		}
++		if (reason & uPD98401_INT_RQU) {
++			unsigned long pools;
++			int i;
++			pools = zin(RQU);
++			printk(KERN_WARNING DEV_LABEL "(itf %d): RQU 0x%08lx\n",
++			    dev->number,pools);
++			event_dump();
++			for (i = 0; pools; i++) {
++				if (pools & 1) {
++					refill_pool(dev,i);
++					zatm_dev->pool_info[i].rqu_count++;
++				}
++				pools >>= 1;
++			}
++		}
++		/* don't handle RD */
++		if (reason & uPD98401_INT_SPE)
++			printk(KERN_ALERT DEV_LABEL "(itf %d): system parity "
++			    "error at 0x%08x\n",dev->number,zin(ADDR));
++		if (reason & uPD98401_INT_CPE)
++			printk(KERN_ALERT DEV_LABEL "(itf %d): control memory "
++			    "parity error at 0x%08x\n",dev->number,zin(ADDR));
++		if (reason & uPD98401_INT_SBE) {
++			printk(KERN_ALERT DEV_LABEL "(itf %d): system bus "
++			    "error at 0x%08x\n",dev->number,zin(ADDR));
++			event_dump();
++		}
++		/* don't handle IND */
++		if (reason & uPD98401_INT_MF) {
++			printk(KERN_CRIT DEV_LABEL "(itf %d): mailbox full "
++			    "(0x%lx)\n",dev->number,(reason & uPD98401_INT_MF)
++			    >> uPD98401_INT_MF_SHIFT);
++			event_dump();
++			    /* @@@ should try to recover */
++		}
++		if (reason & uPD98401_INT_MM) {
++			if (reason & 1) poll_rx(dev,0);
++			if (reason & 2) poll_rx(dev,1);
++			if (reason & 4) poll_tx(dev,2);
++			if (reason & 8) poll_tx(dev,3);
++		}
++		/* @@@ handle RCRn */
++	}
++}
++
++
++/*----------------------------- (E)EPROM access -----------------------------*/
++
++
++static void eprom_set(struct zatm_dev *zatm_dev,unsigned long value,
++    unsigned short cmd)
++{
++	int error;
++
++	if ((error = pcibios_write_config_dword(zatm_dev->bus,zatm_dev->dev_fn,
++	    cmd,value)))
++		printk(KERN_ERR DEV_LABEL ": PCI write failed (%s)\n",
++		    pcibios_strerror(error));
++}
++
++
++static unsigned long eprom_get(struct zatm_dev *zatm_dev,unsigned short cmd)
++{
++	unsigned int value;
++	int error;
++
++	if ((error = pcibios_read_config_dword(zatm_dev->bus,zatm_dev->dev_fn,
++	    cmd,&value)))
++		printk(KERN_ERR DEV_LABEL ": PCI read failed (%s)\n",
++		    pcibios_strerror(error));
++	return value;
++}
++
++
++static void eprom_put_bits(struct zatm_dev *zatm_dev,unsigned long data,
++    int bits,unsigned short cmd)
++{
++	unsigned long value;
++	int i;
++
++	for (i = bits-1; i >= 0; i--) {
++		value = ZEPROM_CS | (((data >> i) & 1) ? ZEPROM_DI : 0);
++		eprom_set(zatm_dev,value,cmd);
++		eprom_set(zatm_dev,value | ZEPROM_SK,cmd);
++		eprom_set(zatm_dev,value,cmd);
++	}
++}
++
++
++static void eprom_get_byte(struct zatm_dev *zatm_dev,unsigned char *byte,
++    unsigned short cmd)
++{
++	int i;
++
++	*byte = 0;
++	for (i = 8; i; i--) {
++		eprom_set(zatm_dev,ZEPROM_CS,cmd);
++		eprom_set(zatm_dev,ZEPROM_CS | ZEPROM_SK,cmd);
++		*byte <<= 1;
++		if (eprom_get(zatm_dev,cmd) & ZEPROM_DO) *byte |= 1;
++		eprom_set(zatm_dev,ZEPROM_CS,cmd);
++	}
++}
++
++
++static unsigned char eprom_try_esi(struct atm_dev *dev,unsigned short cmd,
++    int offset,int swap)
++{
++	unsigned char buf[ZEPROM_SIZE];
++	struct zatm_dev *zatm_dev;
++	int i;
++
++	zatm_dev = ZATM_DEV(dev);
++	for (i = 0; i < ZEPROM_SIZE; i += 2) {
++		eprom_set(zatm_dev,ZEPROM_CS,cmd); /* select EPROM */
++		eprom_put_bits(zatm_dev,ZEPROM_CMD_READ,ZEPROM_CMD_LEN,cmd);
++		eprom_put_bits(zatm_dev,i >> 1,ZEPROM_ADDR_LEN,cmd);
++		eprom_get_byte(zatm_dev,buf+i+swap,cmd);
++		eprom_get_byte(zatm_dev,buf+i+1-swap,cmd);
++		eprom_set(zatm_dev,0,cmd); /* deselect EPROM */
++	}
++	memcpy(dev->esi,buf+offset,ESI_LEN);
++	return memcmp(dev->esi,"\0\0\0\0\0",ESI_LEN); /* assumes ESI_LEN == 6 */
++}
++
++
++static void eprom_get_esi(struct atm_dev *dev)
++{
++	if (eprom_try_esi(dev,ZEPROM_V1_REG,ZEPROM_V1_ESI_OFF,1)) return;
++	(void) eprom_try_esi(dev,ZEPROM_V2_REG,ZEPROM_V2_ESI_OFF,0);
++}
++
++
++/*--------------------------------- entries ---------------------------------*/
++
++
++static int zatm_init(struct atm_dev *dev)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned short command;
++	unsigned char revision;
++	int error,i,last;
++	unsigned long t0,t1,t2;
++
++	DPRINTK(">zatm_init\n");
++	zatm_dev = ZATM_DEV(dev);
++	if ((error = pcibios_read_config_word(zatm_dev->bus,zatm_dev->dev_fn,
++	    PCI_COMMAND,&command)) || (error = pcibios_read_config_dword(
++	    zatm_dev->bus,zatm_dev->dev_fn,PCI_BASE_ADDRESS_0,&zatm_dev->base))
++	    || (error = pcibios_read_config_byte(zatm_dev->bus,zatm_dev->dev_fn,
++	    PCI_INTERRUPT_LINE,&zatm_dev->irq)) || (error =
++	    pcibios_read_config_byte(zatm_dev->bus,zatm_dev->dev_fn,
++	    PCI_REVISION_ID,&revision))) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): init error %s\n",
++		    dev->number,pcibios_strerror(error));
++		return -EINVAL;
++	}
++	zatm_dev->base &= PCI_BASE_ADDRESS_IO_MASK;
++	if ((error = pcibios_write_config_word(zatm_dev->bus,zatm_dev->dev_fn,
++	    PCI_COMMAND,command | PCI_COMMAND_IO | PCI_COMMAND_MASTER))) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): can't enable IO (%s)\n",
++		    dev->number,pcibios_strerror(error));
++		return error;
++	}
++	eprom_get_esi(dev);
++	printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",
++	    dev->number,revision,zatm_dev->base,zatm_dev->irq);
++	/* reset uPD98401 */
++	zout(0,SWR);
++	while (!(zin(GSR) & uPD98401_INT_IND));
++	zout(uPD98401_GMR_ONE /*uPD98401_BURST4*/,GMR);
++	last = MAX_CRAM_SIZE;
++	for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
++		zpokel(zatm_dev,0x55555555,i);
++		if (zpeekl(zatm_dev,i) != 0x55555555) last = i;
++		else {
++			zpokel(zatm_dev,0xAAAAAAAA,i);
++			if (zpeekl(zatm_dev,i) != 0xAAAAAAAA) last = i;
++			else zpokel(zatm_dev,i,i);
++		}
++	}
++	for (i = 0; i < last; i += RAM_INCREMENT)
++		if (zpeekl(zatm_dev,i) != i) break;
++	zatm_dev->mem = i << 2;
++	while (i) zpokel(zatm_dev,0,--i);
++	/* reset again to rebuild memory pointers */
++	zout(0,SWR);
++	while (!(zin(GSR) & uPD98401_INT_IND));
++	zout(uPD98401_GMR_ONE | uPD98401_BURST8 | uPD98401_BURST4 |
++	    uPD98401_BURST2 | uPD98401_GMR_PM | uPD98401_GMR_DR,GMR);
++	/* TODO: should shrink allocation now */
++	printk("mem=%dkB,%s (",zatm_dev->mem >> 10,zatm_dev->copper ? "UTP" :
++	    "MMF");
++	for (i = 0; i < ESI_LEN; i++)
++		printk("%02X%s",dev->esi[i],i == ESI_LEN-1 ? ")\n" : "-");
++	do {
++		t0 = zpeekl(zatm_dev,uPD98401_TSR);
++		udelay(10);
++		t1 = zpeekl(zatm_dev,uPD98401_TSR);
++		udelay(1010);
++		t2 = zpeekl(zatm_dev,uPD98401_TSR);
++	}
++	while (t0 > t1 || t1 > t2);
++	zatm_dev->khz = t2-2*t1+t0;
++	printk(KERN_NOTICE DEV_LABEL "(itf %d): uPD98401 %d.%d at %d.%03d "
++	    "MHz\n",dev->number,
++	    (zin(VER) & uPD98401_MAJOR) >> uPD98401_MAJOR_SHIFT,
++            zin(VER) & uPD98401_MINOR,zatm_dev->khz/1000,zatm_dev->khz % 1000);
++	return uPD98402_init(dev);
++}
++
++
++static int zatm_start(struct atm_dev *dev)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned long curr;
++	int pools,vccs,rx;
++	int error,i,ld;
++
++	DPRINTK("zatm_start\n");
++	zatm_dev = ZATM_DEV(dev);
++	if (request_irq(zatm_dev->irq,&zatm_int,0,DEV_LABEL,dev)) {
++		printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
++		    dev->number,zatm_dev->irq);
++		return -EAGAIN;
++	}
++	request_region(zatm_dev->base,uPD98401_PORTS,DEV_LABEL);
++	/* define memory regions */
++	pools = NR_POOLS;
++	if (NR_SHAPERS*SHAPER_SIZE > pools*POOL_SIZE)
++		pools = NR_SHAPERS*SHAPER_SIZE/POOL_SIZE;
++	vccs = (zatm_dev->mem-NR_SHAPERS*SHAPER_SIZE-pools*POOL_SIZE)/
++	    (2*VC_SIZE+RX_SIZE);
++	ld = -1;
++	for (rx = 1; rx < vccs; rx <<= 1) ld++;
++	dev->ci_range.vpi_bits = 0; /* @@@ no VPI for now */
++	dev->ci_range.vci_bits = ld;
++	zatm_dev->chans = vccs; /* ??? */
++	curr = rx*RX_SIZE/4;
++	DPRINTK("RX pool 0x%08lx\n",curr);
++	zpokel(zatm_dev,curr,uPD98401_PMA); /* receive pool */
++	zatm_dev->pool_base = curr;
++	curr += pools*POOL_SIZE/4;
++	DPRINTK("Shapers 0x%08lx\n",curr);
++	zpokel(zatm_dev,curr,uPD98401_SMA); /* shapers */
++	curr += NR_SHAPERS*SHAPER_SIZE/4;
++	DPRINTK("Free    0x%08lx\n",curr);
++	zpokel(zatm_dev,curr,uPD98401_TOS); /* free pool */
++	printk(KERN_INFO DEV_LABEL "(itf %d): %d shapers, %d pools, %d RX, "
++	    "%ld VCs\n",dev->number,NR_SHAPERS,pools,rx,
++	    (zatm_dev->mem-curr*4)/VC_SIZE);
++	/* create mailboxes */
++	for (i = 0; i < NR_MBX; i++) zatm_dev->mbx_start[i] = 0;
++	for (i = 0; i < NR_MBX; i++)
++		if (mbx_entries[i]) {
++			unsigned long here;
++
++			here = (unsigned long) kmalloc(2*MBX_SIZE(i),
++			    GFP_KERNEL);
++			if (!here) return -ENOMEM;
++			if ((here^(here+MBX_SIZE(i))) & ~0xffff) /* paranoia */
++				here = (here & ~0xffff)+0x10000;
++			DPRINTK("mbx@0x%08lx-0x%08lx\n",here,here+MBX_SIZE(i));
++			zatm_dev->mbx_start[i] = here;
++			zatm_dev->mbx_end[i] = (here+MBX_SIZE(i)) & 0xffff;
++			zout(here >> 16,MSH(i));
++			zout(here,MSL(i));
++			zout((here+MBX_SIZE(i)) & 0xffff,MBA(i));
++			zout(here & 0xffff,MTA(i));
++			zout(here & 0xffff,MWA(i));
++		}
++	error = start_tx(dev);
++	if (error) return error;
++	error = start_rx(dev);
++	if (error) return error;
++	error = dev->phy->start(dev);
++	if (error) return error;
++	zout(0xffffffff,IMR); /* enable interrupts */
++	/* enable TX & RX */
++	zout(zin(GMR) | uPD98401_GMR_SE | uPD98401_GMR_RE,GMR);
++	return 0;
++}
++
++
++static void zatm_close(struct atm_vcc *vcc)
++{
++        DPRINTK(">zatm_close\n");
++        if (!ZATM_VCC(vcc)) return;
++	vcc->flags &= ~ATM_VF_READY;
++        close_rx(vcc);
++EVENT("close_tx\n",0,0);
++        close_tx(vcc);
++        DPRINTK("zatm_close: done waiting\n");
++        /* deallocate memory */
++        kfree(ZATM_VCC(vcc));
++        ZATM_VCC(vcc) = NULL;
++	vcc->flags &= ~ATM_VF_ADDR;
++}
++
++
++static int zatm_open(struct atm_vcc *vcc,short vpi,int vci)
++{
++	struct zatm_dev *zatm_dev;
++	struct zatm_vcc *zatm_vcc;
++	int error;
++
++	DPRINTK(">zatm_open\n");
++	zatm_dev = ZATM_DEV(vcc->dev);
++	if (!(vcc->flags & ATM_VF_PARTIAL)) ZATM_VCC(vcc) = NULL;
++	error = atm_find_ci(vcc,&vpi,&vci);
++	if (error) return error;
++	vcc->vpi = vpi;
++	vcc->vci = vci;
++	if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
++		vcc->flags |= ATM_VF_ADDR;
++	if (vcc->aal != ATM_AAL5) return -EINVAL; /* @@@ AAL0 */
++	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
++	    vcc->vci);
++	if (!(vcc->flags & ATM_VF_PARTIAL)) {
++		zatm_vcc = kmalloc(sizeof(struct zatm_vcc),GFP_KERNEL);
++		if (!zatm_vcc) {
++			vcc->flags &= ~ATM_VF_ADDR;
++			return -ENOMEM;
++		}
++		ZATM_VCC(vcc) = zatm_vcc;
++		ZATM_VCC(vcc)->tx_chan = 0; /* for zatm_close after open_rx */
++		if ((error = open_rx_first(vcc))) {
++	                zatm_close(vcc);
++	                return error;
++	        }
++		if ((error = open_tx_first(vcc))) {
++			zatm_close(vcc);
++			return error;
++	        }
++	}
++	if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
++	if ((error = open_rx_second(vcc))) {
++		zatm_close(vcc);
++		return error;
++        }
++	if ((error = open_tx_second(vcc))) {
++		zatm_close(vcc);
++		return error;
++        }
++	vcc->flags |= ATM_VF_READY;
++        return 0;
++}
++
++
++static int zatm_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
++{
++	struct zatm_dev *zatm_dev;
++	unsigned long flags;
++	int error;
++
++	zatm_dev = ZATM_DEV(dev);
++	switch (cmd) {
++		case ZATM_GETPOOLZ:
++			if (!suser()) return -EPERM;
++			/* fall through */
++		case ZATM_GETPOOL:
++			{
++				int pool;
++
++				error = verify_area(VERIFY_WRITE,(void *) arg,
++				    sizeof(struct zatm_pool_req));
++				if (error) return error;
++				error = verify_area(VERIFY_READ,(void *) arg,
++				    sizeof(struct zatm_pool_req));
++							/* paranoia ? */
++				if (error) return error;
++				pool = get_fs_long(&((struct zatm_pool_req *)
++				    arg)->pool_num);
++				if (pool < 0 || pool > ZATM_LAST_POOL)
++					return -EINVAL;
++				save_flags(flags);
++				cli();
++				memcpy_tofs(&((struct zatm_pool_req *) arg)->
++				    info,&zatm_dev->pool_info[pool],
++				    sizeof(struct zatm_pool_info));
++				if (cmd == ZATM_GETPOOLZ) {
++					zatm_dev->pool_info[pool].rqa_count = 0;
++					zatm_dev->pool_info[pool].rqu_count = 0;
++				}
++				restore_flags(flags);
++				return 0;
++			}
++		case ZATM_SETPOOL:
++			{
++				int pool,low,high,next_thres;
++
++				if (!suser()) return -EPERM;
++				error = verify_area(VERIFY_READ,(void *) arg,
++				    sizeof(struct zatm_pool_req));
++				if (error) return error;
++				pool = get_fs_long(&((struct zatm_pool_req *)
++				    arg)->pool_num);
++				if (pool < 0 || pool > ZATM_LAST_POOL)
++					return -EINVAL;
++				low = get_fs_long(&((struct zatm_pool_req *)
++				    arg)->info.low_water);
++				high = get_fs_long(&((struct zatm_pool_req *)
++				    arg)->info.high_water);
++				next_thres = get_fs_long(
++				    &((struct zatm_pool_req *) arg)->info.
++				    next_thres);
++				if (!low)
++					low = zatm_dev->pool_info[pool].
++					    low_water;
++				if (!high)
++					high = zatm_dev->pool_info[pool].
++					    high_water;
++				if (!next_thres)
++					next_thres = zatm_dev->pool_info[pool].
++					    next_thres;
++				if (low >= high || low < 0) return -EINVAL;
++				save_flags(flags);
++				cli();
++				zatm_dev->pool_info[pool].low_water = low;
++				zatm_dev->pool_info[pool].high_water = high;
++				zatm_dev->pool_info[pool].next_thres =
++				    next_thres;
++				restore_flags(flags);
++				return 0;
++			}
++		default:
++        		if (!dev->phy->ioctl) return -EINVAL;
++		        return dev->phy->ioctl(dev,cmd,arg);
++	}
++}
++
++
++static int zatm_getsockopt(struct atm_vcc *vcc,int level,int optname,
++    char *optval,int *optlen)
++{
++#ifdef CONFIG_MMU_HACKS
++
++static const struct atm_buffconst bctx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
++static const struct atm_buffconst bcrx = { PAGE_SIZE,0,PAGE_SIZE,0,0,0 };
++
++#else
++
++static const struct atm_buffconst bctx = { 4,0,4,0,0,0 };
++static const struct atm_buffconst bcrx = { 4,0,4,0,0,0 };
++
++#endif
++	int error;
++
++	if (level == SOL_AAL && (optname == SO_BCTXOPT ||
++	    optname == SO_BCRXOPT)) {
++		if (get_fs_long(optlen) < sizeof(struct atm_buffconst))
++			return -EINVAL;
++		put_fs_long(sizeof(struct atm_buffconst),optlen);
++		error = verify_area(VERIFY_WRITE,optval,
++		    sizeof(struct atm_buffconst));
++		if (error) return error;
++		memcpy_tofs(optval,optname == SO_BCTXOPT ? &bctx : &bcrx,
++		    sizeof(struct atm_buffconst));
++		return 0;
++	}
++	return -EINVAL;
++}
++
++
++static int zatm_setsockopt(struct atm_vcc *vcc,int level,int optname,
++    char *optval,int optlen)
++{
++	return -EINVAL;
++}
++
++
++#if 0
++static int zatm_sg_send(struct atm_vcc *vcc,unsigned long start,
++    unsigned long size)
++{
++	return vcc->aal == ATM_AAL5;
++	   /* @@@ should check size and maybe alignment*/
++}
++#endif
++
++
++static int zatm_send(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	int error;
++
++	EVENT(">zatm_send 0x%08x\n",(unsigned long) skb,0);
++	if (!ZATM_VCC(vcc)->tx_chan || !(vcc->flags & ATM_VF_READY)) {
++		dev_kfree_skb(skb,FREE_WRITE);
++		return -EINVAL;
++	}
++	if (!skb) {
++		printk(KERN_CRIT "!skb in zatm_send ?\n");
++		dev_kfree_skb(skb,FREE_WRITE);
++		return -EINVAL;
++	}
++	skb->atm.vcc = vcc;
++	error = do_tx(skb);
++	if (error != RING_BUSY) return error;
++	skb_queue_tail(&ZATM_VCC(vcc)->backlog,skb);
++	return 0;
++}
++
++
++static void zatm_phy_put(struct atm_dev *dev,unsigned char value,
++    unsigned long addr)
++{
++	struct zatm_dev *zatm_dev;
++
++	zatm_dev = ZATM_DEV(dev);
++	zwait;
++	zout(value,CER);
++	zout(uPD98401_IND_ACC | uPD98401_IA_B0 |
++	    (uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
++}
++
++
++static unsigned char zatm_phy_get(struct atm_dev *dev,unsigned long addr)
++{
++	struct zatm_dev *zatm_dev;
++
++	zatm_dev = ZATM_DEV(dev);
++	zwait;
++	zout(uPD98401_IND_ACC | uPD98401_IA_B0 | uPD98401_IA_RW |
++	  (uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
++	zwait;
++	return zin(CER) & 0xff;
++}
++
++
++static const struct atmdev_ops ops = {
++	zatm_open,
++	zatm_close,
++	zatm_ioctl,
++	zatm_getsockopt,
++	zatm_setsockopt,
++	zatm_send,
++	NULL /*zatm_sg_send*/,
++	NULL,			/* no poll */
++	NULL,			/* no send_oam */
++	zatm_phy_put,
++	zatm_phy_get,
++	zatm_feedback
++};
++
++
++int zatm_detect(void)
++{
++	struct atm_dev *dev;
++	struct zatm_dev *zatm_dev;
++	int devs,type,index;
++
++	if (!pcibios_present()) {
++		printk(KERN_ERR DEV_LABEL " driver but no PCI BIOS ?\n");
++		return 0;
++	}
++	zatm_dev = (struct zatm_dev *) kmalloc(sizeof(struct zatm_dev),
++	    GFP_KERNEL);
++	if (!zatm_dev) return -ENOMEM;
++	devs = 0;
++	for (type = 0; type < 2; type++) {
++		index = 0;
++		while (!pcibios_find_device(PCI_VENDOR_ID_ZEITNET,type ?
++		    PCI_DEVICE_ID_ZEITNET_1225 : PCI_DEVICE_ID_ZEITNET_1221,
++		    index,&zatm_dev->bus,&zatm_dev->dev_fn)) {
++			dev = atm_dev_register(DEV_LABEL,&ops,0);
++			if (!dev) break;
++			ZATM_DEV(dev) = zatm_dev;
++			zatm_dev->copper = type;
++			if (zatm_init(dev) || zatm_start(dev)) {
++				atm_dev_deregister(dev);
++				break;
++			}
++			zatm_dev->more = zatm_boards;
++			zatm_boards = dev;
++			index++;
++			devs++;
++			zatm_dev = (struct zatm_dev *) kmalloc(sizeof(struct
++			    zatm_dev),GFP_KERNEL);
++			if (!zatm_dev) break;
++		}
++	}
++	return devs;
++}
++
++
++#ifdef MODULE
++ 
++int init_module(void)
++{
++	if (!zatm_detect()) {
++		printk(KERN_ERROR DEV_LABEL ": no adapter found\n");
++		return -ENXIO;
++	}
++	MOD_INC_USE_COUNT;
++	return 0;
++}
++ 
++ 
++void cleanup_module(void)
++{
++	/*
++	 * Well, there's no way to get rid of the driver yet, so we don't
++	 * have to clean up, right ? :-)
++	 */
++}
++ 
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/zatm.h	Wed Jul 31 19:32:42 1996
+@@ -0,0 +1,128 @@
++/* drivers/atm/zatm.h - ZeitNet ZN122x device driver declarations */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef DRIVER_ATM_ZATM_H
++#define DRIVER_ATM_ZATM_H
++
++#include <linux/atmioc.h>
++
++#define ZATM_GETPOOL	_IOW('a',ATMIOC_SARPRV+1,struct atmif_sioc)
++						/* get pool statistics */
++#define ZATM_GETPOOLZ	_IOW('a',ATMIOC_SARPRV+2,struct atmif_sioc)
++						/* get statistics and zero */
++#define ZATM_SETPOOL	_IOW('a',ATMIOC_SARPRV+3,struct atmif_sioc)
++						/* set pool parameters */
++
++
++struct zatm_pool_info {
++	int ref_count;			/* free buffer pool usage counters */
++	int low_water,high_water;	/* refill parameters */
++	int rqa_count,rqu_count;	/* queue condition counters */
++	int offset,next_off;		/* alignment optimizations: offset */
++	int next_cnt,next_thres;	/* repetition counter and threshold */
++};
++
++struct zatm_pool_req {
++	int pool_num;			/* pool number */
++	struct zatm_pool_info info;	/* actual information */
++};
++
++
++#define ZATM_OAM_POOL		0	/* free buffer pool for OAM cells */
++#define ZATM_AAL0_POOL		1	/* free buffer pool for AAL0 cells */
++#define ZATM_AAL5_POOL_BASE	2	/* first AAL5 free buffer pool */
++#define ZATM_LAST_POOL	ZATM_AAL5_POOL_BASE+10 /* max. 64 kB */
++
++
++
++#ifdef __KERNEL__
++
++#include <linux/skbuff.h>
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++
++
++#define DEV_LABEL	"zatm"
++
++#define MAX_AAL5_PDU	10240	/* allocate for AAL5 PDUs of this size */
++#define MAX_RX_SIZE_LD	14	/* ceil(log2((MAX_AAL5_PDU+47)/48)) */
++
++#define LOW_MARK	12	/* start adding new buffers if less than 12 */
++#define HIGH_MARK	30	/* stop adding buffers after reaching 30 */
++#define OFF_CNG_THRES	5	/* threshold for offset changes */
++
++#define RX_SIZE		2	/* RX lookup entry size (in bytes) */
++#define NR_POOLS	32	/* number of free buffer pointers */
++#define POOL_SIZE	8	/* buffer entry size (in bytes) */
++#define NR_SHAPERS	16	/* number of shapers */
++#define SHAPER_SIZE	4	/* shaper entry size (in bytes) */
++#define VC_SIZE		32	/* VC dsc (TX or RX) size (in bytes) */
++
++#define RING_ENTRIES	32	/* ring entries (without back pointer) */
++#define RING_WORDS	4	/* ring element size */
++#define RING_SIZE	(sizeof(unsigned long)*(RING_ENTRIES+1)*RING_WORDS)
++
++#define NR_MBX		4	/* four mailboxes */
++#define MBX_RX_0	0	/* mailbox indices */
++#define MBX_RX_1	1
++#define MBX_TX_0	2
++#define MBX_TX_1	3
++
++
++struct zatm_vcc {
++	/*-------------------------------- RX part */
++	int rx_chan;			/* RX channel, 0 if none */
++	int pool;			/* free buffer pool */
++	/*-------------------------------- TX part */
++	int tx_chan;			/* TX channel, 0 if none */
++	int shaper;			/* shaper, <0 if none */
++	struct sk_buff_head tx_queue;	/* list of buffers in transit */
++	struct wait_queue *tx_wait;	/* for close */
++	unsigned long *ring;		/* transmit ring */
++	int ring_curr;			/* current write position */
++	int txing;			/* number of transmits in progress */
++	struct sk_buff_head backlog;	/* list of buffers waiting for ring */
++};
++
++struct zatm_dev {
++	/*-------------------------------- TX part */
++	int tx_bw;			/* remaining bandwidth */
++	unsigned long free_shapers;	/* bit set */
++	int ubr;			/* UBR shaper; -1 if none */
++	int ubr_ref_cnt;		/* number of VCs using UBR shaper */
++	/*-------------------------------- RX part */
++	int pool_ref[NR_POOLS];		/* free buffer pool usage counters */
++	volatile struct sk_buff *last_free[NR_POOLS];
++					/* last entry in respective pool */
++	struct sk_buff_head pool[NR_POOLS];/* free buffer pools */
++	struct zatm_pool_info pool_info[NR_POOLS]; /* pool information */
++	/*-------------------------------- maps */
++	struct atm_vcc **tx_map;	/* TX VCCs */
++	struct atm_vcc **rx_map;	/* RX VCCs */
++	int chans;			/* map size, must be 2^n */
++	/*-------------------------------- mailboxes */
++	unsigned long mbx_start[NR_MBX];/* start addresses */
++	unsigned short mbx_end[NR_MBX];	/* end offset (in bytes) */
++	/*-------------------------------- other pointers */
++	unsigned long pool_base;	/* Free buffer pool dsc (word addr) */
++	/*-------------------------------- ZATM links */
++	struct atm_dev *more;		/* other ZATM devices */
++	/*-------------------------------- general information */
++	int mem;			/* RAM on board (in bytes) */
++	int khz;			/* timer clock */
++	int copper;			/* PHY type */
++	unsigned char irq;		/* IRQ */
++	unsigned int base;		/* IO base address */
++	unsigned char bus;		/* PCI stuff */
++	unsigned char dev_fn;
++};
++
++
++#define ZATM_DEV(d) ((struct zatm_dev *) (d)->dev_data)
++#define ZATM_VCC(d) ((struct zatm_vcc *) (d)->dev_data)
++
++#endif __KERNEL__
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/zeprom.h	Wed Jul 10 20:16:37 1996
+@@ -0,0 +1,34 @@
++/* drivers/atm/zeprom.h - ZeitNet ZN122x EEPROM (NM93C46) declarations */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef DRIVER_ATM_ZEPROM_H
++#define DRIVER_ATM_ZEPROM_H
++
++/* Different versions use different control registers */
++
++#define ZEPROM_V1_REG	PCI_VENDOR_ID	/* PCI register */
++#define ZEPROM_V2_REG	0x40
++
++/* Bits in contol register */
++
++#define ZEPROM_SK	0x80000000	/* strobe (probably on raising edge) */
++#define ZEPROM_CS	0x40000000	/* Chip Select */
++#define ZEPROM_DI	0x20000000	/* Data Input */
++#define ZEPROM_DO	0x10000000	/* Data Output */
++
++#define ZEPROM_SIZE	32		/* 32 bytes */
++#define ZEPROM_V1_ESI_OFF 24		/* ESI offset in EEPROM (V1) */
++#define ZEPROM_V2_ESI_OFF 4		/* ESI offset in EEPROM (V2) */
++
++#define ZEPROM_CMD_LEN	3		/* commands are three bits */
++#define ZEPROM_ADDR_LEN	6		/* addresses are six bits */
++
++/* Commands (3 bits) */
++
++#define ZEPROM_CMD_READ	6
++
++/* No other commands are needed. */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/tneta1570.h	Mon Jun 10 17:36:11 1996
+@@ -0,0 +1,310 @@
++/* drivers/atm/tneta1570.h - TI TNETA1570 (SAR) declarations */
++ 
++/* Written 1996 by Rolf Fiedler
++ * based on the atm drivers by Werner Almesberger, EPFL LRC 
++ */
++
++#ifndef DRIVERS_ATM_TNETA1570_H
++#define DRIVERS_ATM_TNETA1570_H
++
++#include <linux/atmioc.h>
++
++#ifdef __KERNEL__
++
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++#include <linux/skbuff.h>
++#include <linux/time.h>
++
++#define NR_VCI     256
++#define NR_VCI_LD  8
++#define NR_VPI     32
++#define NR_VPI_LD  5
++#define MAX_VCI    255
++#define MAX_VPI    255
++
++/* this is better put in pci.h, but I want to keep my driver independent */
++#define MEM_VALID 0xfffffff0
++#ifndef PCI_VENDOR_ID_TI
++#define PCI_VENDOR_ID_TI 0x104c
++#endif
++#ifndef PCI_DEVICE_ID_TI_TNETA1570
++#define PCI_DEVICE_ID_TI_TNETA1570 0xa001
++#endif
++
++#define KERNEL_OFFSET    0xc0000000    /* kernel 0 is at linear 0xc0000000 */
++#define DEV_LABEL	 "tneta1570"
++#define RAM_INCREMENT	1024		/* check in 4 kB increments */
++
++/*---------------------------------------------------------*/
++
++/* transmit DMA state table, in control memory */
++struct tneta_tx_dma_state_table{
++     unsigned int control;
++     unsigned int current_buffer_ptr;
++     unsigned int atm_header;
++     unsigned int dma_state_flag;          /* only initialized value */
++     unsigned int next_buffer_ptr;
++     unsigned int start_of_buffer_address;
++     unsigned int partial_AAL5_tx_CRC;
++     unsigned int AAL5_control_length_field;
++};
++
++/* receive DMA state table, in control memory */
++#define RX_DMA_CONTROL_AAL5 ((1<<29)|(1<<26))
++#define RX_DMA_CONTROL_AAL0 ((1<<29)|(1<<27))
++#define RX_TIME_OUT         (0xfff<<12)
++#define DMA_ON              (1<<31)          
++#define DMA_FILTER          (1<<30)
++struct tneta_rx_dma_state_table{
++     unsigned int control;             /* needs initialization */
++     unsigned int current_buffer_ptr;
++     unsigned int sob_ptr;
++     unsigned int EOP;
++     unsigned int sop_ptr;
++     unsigned int AAL0_cells;          /* needs initialization if AAL0 */
++     unsigned int dma_on_idx;          /* needs initialization */
++     unsigned int rx_timeout;          /* needs initialization */
++};
++
++#define MAX_AAL5_PDU	9600	        /* max size for AAL5 PDUs buffers */
++#define MAX_AAL5_CELLS  200
++#define MAX_AAL0_PDU    48
++#define MAX_AAL0_CELLS  1
++#define AAL0_BUFS       32
++#define AAL5_BUFS       16
++#define FBR_AAL0_32     ((2<<16) | (1<<10) | (0))
++#define FBR_AAL5_16     ((MAX_AAL5_CELLS<<16) | (0<<10) | (0))
++#define RX_HDR          20
++#define MAX_FBR_ENTRIES 256
++struct tneta_rx_fbrptr {
++        unsigned int * buf_ptr;
++        unsigned int buf_size;
++};
++
++#define AAL5_IND    (1<<26)
++struct tneta_rx_compl {
++     unsigned int atm_header;
++     unsigned int error;
++     unsigned int buf_ptr;
++     unsigned int trailer;
++     unsigned int control;             /* needs initialization */
++     unsigned int res1;
++     unsigned int res2;
++     unsigned int res3;
++};
++
++/* device dependent vcc data */
++#define TX_SEG_RING_SIZE 256
++
++struct tneta1570_vcc {
++	/*-------------------------------- RX part */
++        int (*rx)(struct atm_vcc *vcc);
++        int rxing;                        /* pending cells */
++        struct wait_queue * rx_wait;      /* for close */
++        int dma_channel;                  /* for close */
++        int fbr_idx;
++        /*-------------------------------- TX part */
++        struct wait_queue * tx_wait;      /* for close */
++        int txing;                        /* cells to be tx'd */
++        int scheduler_idx;                /* index in scheduler table */
++        unsigned int * seg_ring;          /* size aligned 1K */
++        unsigned int * seg_ring_mptr;     /* word aligned */
++        int seg_ring_idx;
++   
++        struct atm_vcc *next;             /* next pending rx */
++        struct sk_buff *last;             /* last PDU */
++};
++
++
++/* device dependent data */
++#define TXCMPLR_SZ_IRQ 256   /* size aligned */
++#define TXCMPLR_SZ_NOI 128   /* size aligned */
++#define RXCMPLR_SZ_IRQ 128   /* size aligned */
++#define RXCMPLR_SZ_NOI 64    /* size aligned */
++#define SAR_REG_WORD(dev, x) (dev->reg[x])
++#define SAR_REG_SHORT(dev, x) (((unsigned short *)dev->reg)[x])
++#define MAX_SCHED_ENTRIES 1022
++#define MAX_TXDMA_ENTRIES 1022
++#define TX_CMPL_R_IRQ(dev)  (dev->txcmplringptr_irq[dev->txcmpl_ring_idx_irq])
++#define TX_CMPL_R_NOI(dev)  (dev->txcmplringptr_noi[dev->txcmpl_ring_idx_noi])
++#define RX_CMPL_R_IRQ(dev)  (dev->rxcmplringptr_irq[dev->rxcmpl_ring_idx_irq])
++#define RX_CMPL_R_NOI(dev)  (dev->rxcmplringptr_noi[dev->rxcmpl_ring_idx_noi])
++
++struct tneta1570_dev {
++	/*-------------------------------- TX part */
++        int txcmpl_ring_idx_noi, txcmpl_ring_idx_irq;
++        unsigned int *txcmplringptr_noi, *txcmplringptr_irq;
++        unsigned int *txcmpl_ring;
++
++        /*-------------------------------- RX part */
++        int rxcmpl_ring_idx_noi, rxcmpl_ring_idx_irq;
++        struct tneta_rx_compl *rxcmplringptr_noi, *rxcmplringptr_irq;
++        struct tneta_rx_compl *rxcmpl_ring;
++
++        /*-------------------------------- maps */
++        int oam_fbr_idx;
++   
++        /*-------------------------------- stats */
++        unsigned int lost;              /* lost rx cells */
++        /*-------------------------------- other pointers */
++
++	/*-------------------------------- TNETA links */
++	struct atm_dev *more;		/* other TNETA devices */
++	/*-------------------------------- general information */
++   
++        volatile unsigned long *ram;           /* base of phy device */
++        volatile unsigned long *scheduler;     /* base of scheduler table */
++        volatile unsigned int *reg;           /* base of sar regs device */
++        volatile struct tneta_rx_fbrptr *free_buf_ptr;  /* free buffer pointers */
++        volatile unsigned long *rx_vpi_vci;    /* rx vpi vci table */
++        volatile struct tneta_tx_dma_state_table *tx_dma_state;  /* tx dma state table */
++        volatile struct tneta_rx_dma_state_table *rx_dma_state;  /* rx dma state table */
++        volatile unsigned long *phy;           /* base of phy device */
++   
++	int mem;			/* RAM on board (in bytes) */
++	void * base;		        /* board base address */
++        unsigned long base_diff;        /* virtual - phy base address */
++        unsigned int pci_map_size;      /* pci map size of board */     
++        unsigned char irq;		/* IRQ */
++	unsigned char bus;		/* PCI stuff */
++	unsigned char dev_fn;
++};
++
++
++#define TNETA1570_DEV(d) ((struct tneta1570_dev *) (d)->dev_data)
++#define TNETA1570_VCC(d) ((struct tneta1570_vcc *) (d)->dev_data)
++
++/*---------------------------------------------------------*/
++
++/*
++ * Directly Addressable Registers, memory mapped
++ */
++#define TNETA_REG_BASE_OFFSET   0x3200   /* offset PCI base to registers */
++#define TNETA_CONFIG         0   /* configuration register */
++#define TNETA_STATUS         1   /* status register */
++#define TNETA_INT_MASK       2   /* interrupt mask register */ 
++#define TNETA_S_RAT          6   /* 3L  RAT cycle # rx DMA state table */
++#define TNETA_S_RGT          7   /* 3H global reass. timer */
++#define TNETA_RXUNKNOWN      4   /* rx unknown register */
++#define TNETA_S_TXCOMPLSIZEI 10  /* 5L TX compl. ring size W/ interrupt */
++#define TNETA_S_TXCOMPLSIZE  11  /* 5H TX compl. ring size W/O interrupt */
++#define TNETA_S_RXCOMPLSIZEI 12  /* 6L RX completion ring size W/ interrupt */ 
++#define TNETA_S_RXCOMPLSIZE  13  /* 6H RX completion ring size W/O interrupt */ 
++#define TNETA_S_FIFO_FREE    14  /* 7L FIFO free-buffer-size */
++#define TNETA_S_TXSEGSIZE    15  /* 7H Segmentation ring size */
++#define TNETA_S_AAL_DISCARD  16  /* 8L discarded AAL5 rx cell counter */
++#define TNETA_S_HEC_ERR      17  /* 8H HEC error counter */
++#define TNETA_UNKNOWN_P      9   /* Unknown Protocols RX # */
++#define TNETA_CELL_RXC       10  /* ATM Cells RX'd # */
++#define TNETA_CELL_TXC       11  /* ATM Cells TX'd # */
++#define TNETA_S_TXM_RXM      24  /* 12L TX FIFO & RX FIFO max occupancy */ 
++#define TNETA_S_VCIM         25  /* 12H VCI mask */ 
++#define TNETA_S_SCHEDSIZE    26  /* 13L scheduler-table-size register */
++#define TNETA_RESET          14  /* software reset register */
++
++#define TNETA_TXCOMPLNOI     128 /* TX completion ring W/O interrupt pointer */
++#define TNETA_TXCOMPLIRQ     129 /* TX completion ring W/  interrupt pointer */
++#define TNETA_RXCOMPLNOI     130 /* RX completion ring W/O interrupt pointer */
++#define TNETA_RXCOMPLIRQ     131 /* RX completion ring W/  interrupt pointer */
++
++/* configuration register bits */
++#define TNETA_R0_UNI       0x2000
++#define TNETA_R0_MAX_RETRY (0xf << 9)  /* 1111 max retry master */
++#define TNETA_R0_LOOP      0x0100      /* set to loop-back (reset!, no enable) */
++#define TNETA_R0_TX_HECERR 0x0080      /* force HEC error */
++#define TNETA_R0_SMALL_MAP 0x0040
++#define TNETA_R0_TX_ENABLE 0x0020
++#define TNETA_R0_RX_ENABLE 0x0010
++#define TNETA_R0_ENDIAN    (0x0 << 3)  /* 00 little endian */
++#define TNETA_R0_PERBUFFER 0x002
++#define TNETA_R0_RAT_ENABL 0x001       /* enable reass. aging timer */
++#define TNETA_R0_STANDARD_MODE (TNETA_R0_UNI)
++                             
++/* status register bits */
++#define TNETA_R1_PCI_MODE  0x400        /* 32/64 bit */
++#define TNETA_R1_RX_FREEZE 0x200        /* rx ring overflow */
++#define TNETA_R1_TX_FREEZE 0x100        /* tx ring overflow */
++#define TNETA_R1_CP_RX     0x080        /* packet reassembly completed */
++#define TNETA_R1_RX_IRR    0x040        /* rx-unknown written */
++#define TNETA_R1_HEC_OVER  0x020        /* HEC error counter overflow */
++#define TNETA_R1_UP_OVER   0x010        /* unknown proto counter overflow */
++#define TNETA_R1_APD_OVER  0x008        /* AAL5 PDU discard counter overflow */
++#define TNETA_R1_ACR_OVER  0x004        /* ATM cell rxd counter overflow */
++#define TNETA_R1_ACT_OVER  0x002        /* ATM cell txd counter overflow */
++#define TNETA_R1_CP_TX     0x001        /* packet segmentation completed */
++#define TNETA_R2_STANDARD_INTS (TNETA_R1_RX_FREEZE | \
++                                TNETA_R1_TX_FREEZE | \
++                                TNETA_R1_CP_RX | \
++                                TNETA_R1_CP_TX)
++/* control memory map offsets */
++#define TNETA_SCHED_TABLE             0x0
++#define TNETA_FREE_BUFFER_POINTERS    0x3800
++#define TNETA_RX_VPIVCI_DMA_POINTERS  0x4000
++#define TNETA_TX_DMA_STATE_TABLE      0x8000
++#define TNETA_RX_DMA_STATE_TABLE      0x10000
++#define TNETA_SUNI_OFFSET             (0x20000 << 2)
++
++/* reserved control memory area */
++#define RESERVED_LL                   0xc00   /* lower limit */
++#define RESERVED_UL                   0xe00   /* upper limit */
++
++#define TNETA_SUNI_RDREQ              0x00100
++#define TNETA_SUNI_RD_D_AV            (0x10)
++
++#define OWN (0x1 << 31)        /* own bit, set to 1 if owned by tneta1570 */
++#define SEG_PTR(x) (0x3fffff00 & ((unsigned int)x >> 2))
++#define BUF_PTR(x) (((unsigned int)x >> 2) & 0x3fffffff)
++                   /* pointer to tx data buffer header, aligned to 4 byte */
++
++/* tx data buffer header */
++struct tx_buffer_descriptor{
++     unsigned int control;
++     unsigned int next_buffer;
++     unsigned int atm_header;
++     unsigned int AAL5_control;
++};
++
++/* control word layout */
++#define TNETA_TXDBH_CTRL_RDY        (0x1 << 31)
++#define TNETA_TXDBH_CTRL_SOP        (0x1 << 30)
++#define TNETA_TXDBH_CTRL_EOP        (0x1 << 29)
++#define TNETA_TXDBH_CTRL_ABORT      (0x1 << 28)
++#define TNETA_TXDBH_CTRL_AAL0_PTI   (0x0 << 26)  
++#define TNETA_TXDBH_CTRL_AAL5       (0x1 << 26)  
++#define TNETA_TXDBH_CTRL_AAL0_NOPTI (0x2 << 26)  
++#define TNETA_TXDBH_CTRL_INT_NOINT  (0x1 << 25)
++#define TNETA_TXDBH_CTRL_BUF_OFFSET (0xff << 16)
++#define TNETA_TXDBH_CTRL_BYTE_COUNT (0xffff << 0)
++
++#define AAL5_PDU_INT (TNETA_TXDBH_CTRL_RDY | \
++                      TNETA_TXDBH_CTRL_SOP | \
++                      TNETA_TXDBH_CTRL_EOP | \
++                      TNETA_TXDBH_CTRL_AAL5 | \
++                      TNETA_TXDBH_CTRL_INT_NOINT)
++
++#define AAL0_PDU_INT (TNETA_TXDBH_CTRL_RDY | \
++                      TNETA_TXDBH_CTRL_SOP | \
++                      TNETA_TXDBH_CTRL_EOP | \
++                      TNETA_TXDBH_CTRL_AAL0_PTI)
++
++/* ATM header */
++#define TNETA_TXDBH_ATMH_GFC        (0xff << 24)
++#define TNETA_TXDBH_ATMH_VPI        (0xff << 16)
++#define TNETA_TXDBH_ATMH_VCI        (0xffff << 4)
++#define TNETA_TXDBH_ATMH_PTI        (0x7 << 1)
++#define TNETA_TXDBH_ATMH_CLP        (0x1 << 0)					
++	
++/* AAL5 control */
++#define TNETA_TXDBH_AAL5_CPCS_UU    (0xff << 24)
++#define TNETA_TXDBH_AAL5_CPI        (0xffff << 16)
++#define TNETA_TXDBH_AAL5_USER       (0xffff << 0)
++
++/* TX completion rings */
++#define TNETA_TXCR_OWN         (0x1 << 31)
++#define TNETA_TXCR_ABORT       (0x1 << 30)
++#define TNETA_TXCR_BUFADDR     (0x3fffffff << 0)
++
++#endif __KERNEL__
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/tneta1570.c	Fri Jul 26 17:35:25 1996
+@@ -0,0 +1,1479 @@
++/* drivers/atm/tneta1570.c - ti tneta1570 atm driver */
++ 
++/* Written 1996 by Rolf Fiedler (rolf.fiedler@infotech.tu-chemnitz.de)
++ * based on the atm drivers by Werner Almesberger, EPFL LRC
++ */
++
++#include <linux/config.h> /* for extended debugging options */
++
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/bios32.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/sonet.h>
++#include <linux/skbuff.h>
++#include <linux/time.h>
++#include <linux/sched.h> /* for xtime */
++#include <linux/delay.h>
++#include <linux/uio.h>
++#include <asm/system.h>
++#include <asm/string.h>
++#include <asm/byteorder.h>
++
++#include "tneta1570.h"
++#include "suni.h"
++
++#define SLOW_DOWN_FACTOR 8
++
++/*
++ * KNOWN BUGS:
++ *
++ * doesn't work a bit
++ */
++
++inline void write_sched_tab(struct tneta1570_dev * dev, int i, unsigned int v);
++inline int read_sched_tab(struct tneta1570_dev * dev, int i);
++static void dequeue_OAM(struct atm_dev *dev);   /* dequeue OAM cells */
++static void dequeue_AAL0(struct atm_dev *dev);  /* dequeue AAL0 cells */
++static void dequeue_AAL5(struct atm_dev *dev);  /* dequeue AAL5 cells */
++static int alloc_dma(struct atm_vcc *vcc);
++
++#if 0
++#define DPRINTK printk
++#else
++#define DPRINTK (void) 
++#endif
++
++#ifndef CONFIG_ATM_TNETA1570_DEBUG
++
++
++#define NULLCHECK(x)
++
++#define EVENT(s,a,b)
++
++
++static inline void event_dump(void)
++{
++}
++
++
++#else
++
++
++/* 
++ * NULL pointer checking
++ */
++
++#define NULLCHECK(x) \
++  if ((unsigned long) (x) < 0x30) printk(#x "==0x%x\n", (int) (x))
++
++/*
++ * Very extensive activity logging. Greatly improves bug detection speed but
++ * costs a few Mbps if enabled.
++ */
++
++#define EV 64
++
++static const char *ev[EV];
++static unsigned long ev_a[EV],ev_b[EV];
++static int ec = 0;
++
++static void EVENT(const char *s,unsigned long a,unsigned long b)
++{
++	ev[ec] = s; 
++	ev_a[ec] = a;
++	ev_b[ec] = b;
++	ec = (ec+1) % EV;
++}
++
++
++static void event_dump(void)
++{
++	int n,i;
++
++	for (n = 0; n < EV; n++) {
++		i = (ec+n) % EV;
++		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);
++	}
++}
++
++
++#endif /* CONFIG_ATM_TNETA1570_DEBUG */
++
++
++static int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0,
++  backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0,
++  putting = 0;
++
++static struct atm_dev *tneta1570_boards = NULL;
++
++
++/*-------------------------------- utilities --------------------------------*/
++
++
++static void dump_mem(struct tneta1570_dev *tneta1570_dev)
++{
++}
++
++
++static void dump(struct atm_dev *dev)
++{
++    struct tneta1570_dev *tneta1570_dev;
++
++    tneta1570_dev = TNETA1570_DEV(dev);
++    printk("\nFree memory\n");
++    dump_mem(tneta1570_dev);
++    printk("TX buffers\n");
++
++    printk("RX buffers\n");
++
++    printk("----\n");
++}
++
++/* --------------------------memory allocation-------------------------------*/
++
++
++/*
++ * allocate a skbuff with enough room for the SDU header
++ */
++static struct sk_buff *tneta1570_alloc_tx(struct atm_vcc *vcc, unsigned int size)
++{
++        struct sk_buff *ptr;
++   
++        ptr = alloc_skb(size + 48, GFP_KERNEL);
++        if(ptr == NULL) return NULL;
++        skb_reserve(ptr, 48);        /* room for push */
++        return ptr;
++}
++
++
++
++/*----------------------------------- RX ------------------------------------*/
++
++/*
++ * alloc_dma
++ * find entry in dma state table for this vpi/vci
++ * if vpi already open increase valid vci range
++ * else open vpi, set vci range to vci
++ * return index to dma channel, return 0 on error
++ */
++static int alloc_dma(struct atm_vcc *vcc)
++{
++        unsigned char unsorted[MAX_VPI], sorted[MAX_VPI];
++        unsigned int x;
++        int i;
++        struct tneta1570_dev *tneta1570_dev;
++
++        EVENT(">alloc_rx_dma\n",0,0);
++   
++        tneta1570_dev = TNETA1570_DEV(vcc->dev);
++         
++        x = tneta1570_dev->rx_vpi_vci[vcc->vpi];
++    
++        if(x != 0) {
++	        x = 0x7fff & (x >> 16);
++	        return  x + vcc->vci - 0x800;  /* idx = value - offset */
++	} else {
++                /* create a new vpi entry */
++	        /* first, find dma channel range */
++                for(i=0; i<MAX_VPI+1; i++) {
++	                if(tneta1570_dev->rx_vpi_vci[i] & OWN) {
++                                x = tneta1570_dev->rx_vpi_vci[i];
++			        unsorted[i] = 0x7f & (x >> 24);
++		        } else {
++		                unsorted[i] = 0;
++			}
++		        sorted[i] = 0;
++		}
++                for(i=0; i<MAX_VPI+1; i++) {
++                        if(unsorted[i] != 0)
++		                sorted[unsorted[i]-8] = 1;
++		}
++                for(i=0; i<MAX_VPI+1; i++) {
++                        if(unsorted[i] == 0)
++		                break;
++		}
++                if(i==MAX_VPI+1) return 0;
++                tneta1570_dev->rx_vpi_vci[vcc->vpi] = OWN | ((i+8)<<24) | (MAX_VCI);
++	        return 256*i + vcc->vci;
++	}
++}
++
++
++/*
++ * let the protocol have a look at the buffer
++ */
++static unsigned long tneta1570_fetch(struct atm_vcc *vcc, int i)
++{
++  return 0; /* not implemented yet */
++}
++
++/*
++ * interrupt driven dequeue for rx buffer
++ * -> for each and every post in the completion ring do
++ *    increment completion ring ptr (modulo RXCMPLR_SIZE_IRQ)
++ *    get skb address from buffer address
++ *    get free-buffer ring address from tneta1570_vcc
++ *    prepare skb with header information
++ *    vcc->push skb
++ *    alloc (ATOMIC) new skb for this vci, put in free-buffer ring
++ *    increment free-buffer ring index (modulo FBR_SIZE)
++ */
++static void dequeue_rx(struct atm_dev * dev)
++{
++        struct tneta1570_dev * tneta1570_dev;
++
++        EVENT(">dequeue_rx\n",0,0);
++        NULLCHECK(dev);
++	tneta1570_dev = TNETA1570_DEV(dev);
++	NULLCHECK(tneta1570_dev);
++
++        /* find here completion ring entries */
++        EVENT(">completion ring post: %08x, %d\n",
++ 	        (unsigned int)&RX_CMPL_R_IRQ(tneta1570_dev).atm_header,
++	        tneta1570_dev->rxcmpl_ring_idx_irq); 
++
++        while(!(RX_CMPL_R_IRQ(tneta1570_dev).control & OWN)) { 
++                if((RX_CMPL_R_IRQ(tneta1570_dev).control & 0xff) == 0) {
++	                dequeue_OAM(dev);
++                } else { 
++		        if((RX_CMPL_R_IRQ(tneta1570_dev).error & AAL5_IND)) {
++	                        dequeue_AAL5(dev);
++                        } else {
++	                        dequeue_AAL0(dev);
++
++			}
++		}
++                RX_CMPL_R_IRQ(tneta1570_dev).control = OWN;
++                tneta1570_dev->rxcmpl_ring_idx_irq++;
++                tneta1570_dev->rxcmpl_ring_idx_irq %= RXCMPLR_SZ_IRQ;
++	}
++        return ;
++}
++
++static void dequeue_OAM(struct atm_dev *dev)  /* dequeue AAL0 cells */
++{
++        struct tneta1570_dev * tneta1570_dev;
++        struct sk_buff *skb, *new_skb;
++        unsigned int *p, *fbr;
++
++        EVENT(">dequeue_rx_OAM\n",0,0);
++        NULLCHECK(dev);
++	tneta1570_dev = TNETA1570_DEV(dev);
++	NULLCHECK(tneta1570_dev);
++
++/*        if(vcc->push_oam) printk(" PushOAM is present.\n"); */
++         
++        p = (unsigned int *)(RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr << 2);
++        skb = (struct sk_buff *)(*(p-1));  /* get skb */
++	      
++        /* get new buffer before dequeueing old
++         */
++        new_skb = alloc_skb(MAX_AAL0_PDU+RX_HDR, GFP_ATOMIC);
++        if(new_skb == NULL) {
++        /* add just received buffer to free list, drop pdu :-( */
++                fbr = tneta1570_dev->free_buf_ptr[0].buf_ptr;
++                fbr[tneta1570_dev->oam_fbr_idx] = OWN | ((int)p >> 2);
++                tneta1570_dev->oam_fbr_idx++;
++                tneta1570_dev->oam_fbr_idx %= AAL0_BUFS;
++                return;
++	}
++	   
++        /* add newly allocated buffer to free list */
++        fbr = tneta1570_dev->free_buf_ptr[0].buf_ptr;
++        fbr[tneta1570_dev->oam_fbr_idx] = OWN | ((unsigned int)(new_skb->data+4) >> 2);
++	*(struct sk_buff **)(new_skb->data) = new_skb;
++        tneta1570_dev->oam_fbr_idx++;
++        tneta1570_dev->oam_fbr_idx %= AAL0_BUFS;
++	   
++        /* push received buffer to protocol */
++		   
++	skb->data[16] = skb->data[12];
++	skb->data[17] = skb->data[13];
++	skb->data[18] = skb->data[14];
++	skb->data[19] = skb->data[15];
++	skb->data += 4*4;  /* skip header */
++	skb->len  = 52;
++        /* should check for multiple buffers @@@@ */
++	skb->tail = skb->data + skb->len;
++		   
++        if (/* vcc->push */ 1) {
++	        dev_kfree_skb(skb, GFP_ATOMIC);
++                printk(" Don't know how to call push !\n");
++	        /* vcc->push(vcc,skb); */
++	} else {
++	        dev_kfree_skb(skb, GFP_ATOMIC);
++	        printk(DEV_LABEL "(itf %d) No push in protocol.\n", 
++		       dev->number); 
++	}
++}
++
++
++static void dequeue_AAL0(struct atm_dev *dev)  /* dequeue AAL0 cells */
++{
++        struct tneta1570_dev * tneta1570_dev;
++        struct tneta1570_vcc * tneta1570_vcc;
++   	struct atm_vcc *vcc;
++	struct sk_buff *skb, *new_skb;
++        unsigned int *p, v, *fbr;
++   
++        EVENT(">dequeue_rx_AAL0\n",0,0);
++        NULLCHECK(dev);
++	tneta1570_dev = TNETA1570_DEV(dev);
++	NULLCHECK(tneta1570_dev);
++   
++        p = (unsigned int *)(RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr << 2);
++        skb = (struct sk_buff *)(*(p-1));  /* get skb */
++	      
++        vcc = skb->atm.vcc;
++        NULLCHECK(vcc);
++        tneta1570_vcc = TNETA1570_VCC(vcc);
++        NULLCHECK(tneta1570_vcc);   
++        /* get new buffer before dequeueing old
++         * if error in peek drop pdu
++         */
++        new_skb = vcc->peek(vcc, MAX_AAL0_PDU+RX_HDR, NULL);
++        if(new_skb == NULL) {
++        /* add just received buffer to free list, drop pdu :-( */
++                v = RX_CMPL_R_IRQ(tneta1570_dev).control;
++                fbr = tneta1570_dev->free_buf_ptr[0xff & v].buf_ptr;
++                fbr[tneta1570_vcc->fbr_idx] = OWN | ((int)p >> 2);
++                tneta1570_vcc->fbr_idx++;
++                tneta1570_vcc->fbr_idx %= AAL0_BUFS;
++                return;
++	}
++	   
++        /* add newly allocated buffer to free list */
++                       
++	v = RX_CMPL_R_IRQ(tneta1570_dev).control;
++        fbr = tneta1570_dev->free_buf_ptr[0xff & v].buf_ptr;
++        fbr[tneta1570_vcc->fbr_idx] = OWN | ((unsigned int)(new_skb->data+4) >> 2);
++	*(struct sk_buff **)(new_skb->data) = new_skb;
++        new_skb->atm.vcc = vcc;      /* link vcc info */
++        tneta1570_vcc->fbr_idx++;
++        tneta1570_vcc->fbr_idx %= AAL0_BUFS;
++	   
++        /* push received buffer to protocol */
++		   
++	skb->data[16] = skb->data[12];
++	skb->data[17] = skb->data[13];
++	skb->data[18] = skb->data[14];
++	skb->data[19] = skb->data[15];
++	skb->data += 4*4;  /* skip header */
++	skb->len  = 52;
++        /* should check for multiple buffers @@@@ */
++	skb->tail = skb->data + skb->len;
++        skb->free = 1;   /* set free flag, is there need for it? */
++		   
++        if (vcc->push) {
++                vcc->push(vcc,skb);
++	} else {
++	        printk(DEV_LABEL "(itf %d) No push in protocol.\n", 
++		       dev->number); 
++	}
++        /* update statistics */
++        vcc->stats->rx++;
++rx_dequeued++;
++}
++  
++   
++   
++static void dequeue_AAL5(struct atm_dev *dev)   /* dequeue AAL5 PDU */
++{
++        struct tneta1570_dev *tneta1570_dev;
++        struct tneta1570_vcc * tneta1570_vcc;
++        struct atm_vcc *vcc;
++	struct sk_buff *skb, *new_skb;
++        unsigned int *p, v, *fbr;
++   
++        EVENT(">dequeue_rx_AAL5\n",0,0);
++        NULLCHECK(dev);
++	tneta1570_dev = TNETA1570_DEV(dev);
++	NULLCHECK(tneta1570_dev);
++   
++        p = (unsigned int *)(RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr << 2);
++        skb = (struct sk_buff *)(*(p-1));  /* get skb */
++
++        DPRINTK(" p %08x, skb %08x, head %08x, err %08x, sop %08x, trailer %08x, idx %08x",
++	         p, skb, 
++	         RX_CMPL_R_IRQ(tneta1570_dev).atm_header,
++	         RX_CMPL_R_IRQ(tneta1570_dev).error, 
++	         RX_CMPL_R_IRQ(tneta1570_dev).buf_ptr,
++	         RX_CMPL_R_IRQ(tneta1570_dev).trailer,
++	         RX_CMPL_R_IRQ(tneta1570_dev).control);
++   
++        vcc = skb->atm.vcc;
++        DPRINTK(" vcc %08x", vcc);
++   
++        NULLCHECK(vcc);
++        tneta1570_vcc = TNETA1570_VCC(vcc);
++        NULLCHECK(tneta1570_vcc);   
++        /* get new buffer before dequeueing old
++         * if error in peek drop pdu
++         */
++        new_skb = vcc->peek(vcc, MAX_AAL5_PDU+RX_HDR, NULL);
++        if(new_skb == NULL) {
++        /* add just received buffer to free list :-( */
++                v = RX_CMPL_R_IRQ(tneta1570_dev).control;
++	        fbr = tneta1570_dev->free_buf_ptr[0xff & v].buf_ptr;
++	        fbr[tneta1570_vcc->fbr_idx] = OWN | ((int)p >> 2);
++	        tneta1570_vcc->fbr_idx++;
++	        tneta1570_vcc->fbr_idx %= AAL5_BUFS;
++	        return;
++	}
++        /* add newly allocated buffer to free list */
++                        
++	v = RX_CMPL_R_IRQ(tneta1570_dev).control;
++        fbr = tneta1570_dev->free_buf_ptr[v].buf_ptr;
++        fbr[tneta1570_vcc->fbr_idx] = OWN | ((unsigned int)(new_skb->data+4) >> 2);
++	*(struct sk_buff **)(new_skb->data) = new_skb;
++        new_skb->atm.vcc = vcc;   /* link context info */
++        tneta1570_vcc->fbr_idx++;
++        tneta1570_vcc->fbr_idx %= AAL5_BUFS;
++	   
++        /* push received buffer to protocol */
++	skb->data += 5*4;  /* skip header */
++	skb->len  = RX_CMPL_R_IRQ(tneta1570_dev).trailer & 0xffff;
++        /* should check for multiple buffers @@@@ */
++	skb->tail = skb->data + skb->len;
++        skb->free = 1;   /* set free flag, is there need for it? */
++   
++        if (vcc->push) {
++                vcc->push(vcc,skb);
++	} else {
++	        printk(DEV_LABEL "(itf %d) No push in protocol.\n", 
++	               dev->number); 
++	}
++        /* update statistics */
++        vcc->stats->rx++;
++rx_dequeued++;
++}
++
++   
++/*
++ * --- bring rx up ---
++ * init vpi/vci table - well, already done by tneta1570_init
++ * kmalloc completion rings 
++ * write ptrs to completion rings into sar regs
++ * init completion rings to all OWN
++ * write completion ring related data to device structure, set index to 0
++ * init dma channels 0, 1, 2 for reception of OAM cells
++ */
++static int start_rx(struct atm_dev *dev)
++{
++        int i;
++        unsigned int x;
++        unsigned int *ptr;
++        struct sk_buff *buf;
++        struct tneta1570_dev *tneta1570_dev;
++
++        EVENT(">start_rx\n",0,0);
++        tneta1570_dev = TNETA1570_DEV(dev);
++   	tneta1570_dev->lost = 0;
++   
++        /* init rx completion rings */
++        tneta1570_dev->rxcmpl_ring =
++                       kmalloc(2*RXCMPLR_SZ_IRQ*sizeof(struct tneta_rx_compl),
++			       GFP_KERNEL);
++        DPRINTK("RX_CMPL_R->%08x", tneta1570_dev->rxcmpl_ring);
++        if(!tneta1570_dev->rxcmpl_ring) {
++	        printk(DEV_LABEL "(itf %d) malloc on rx start failed.\n", dev->number);
++	        return -ENOMEM;
++	}
++        /* align completion-ring with irq to its size */
++        x = (unsigned int)(&tneta1570_dev->rxcmpl_ring[RXCMPLR_SZ_IRQ]);
++        tneta1570_dev->rxcmplringptr_irq = (struct tneta_rx_compl *)
++                     (x & (~(RXCMPLR_SZ_IRQ*sizeof(struct tneta_rx_compl) - 1)));
++        /* the rest is for the completion ring w/o irq */   
++        if((tneta1570_dev->rxcmplringptr_irq - RXCMPLR_SZ_NOI) 
++	   > tneta1570_dev->rxcmpl_ring) {
++                tneta1570_dev->rxcmplringptr_noi = 
++	                tneta1570_dev->rxcmplringptr_irq - RXCMPLR_SZ_NOI;
++        } else {
++                tneta1570_dev->rxcmplringptr_noi = 
++	                tneta1570_dev->rxcmplringptr_irq + RXCMPLR_SZ_IRQ;
++        }
++        EVENT(">init rx completion ring irq\n",0,0);
++        for(i=0; i<RXCMPLR_SZ_IRQ; i++)
++                tneta1570_dev->rxcmplringptr_irq[i].control = OWN;
++        EVENT(">init tx completion ring noi\n",0,0);
++        for(i=0; i<RXCMPLR_SZ_NOI; i++)
++                tneta1570_dev->rxcmplringptr_noi[i].control = OWN;
++   
++        tneta1570_dev->rxcmpl_ring_idx_noi = 0;
++        tneta1570_dev->rxcmpl_ring_idx_irq = 0;
++
++        SAR_REG_SHORT(tneta1570_dev, TNETA_S_RXCOMPLSIZEI) = RXCMPLR_SZ_IRQ-1; 
++        SAR_REG_SHORT(tneta1570_dev, TNETA_S_RXCOMPLSIZE)  = RXCMPLR_SZ_NOI-1; 
++        SAR_REG_WORD(tneta1570_dev, TNETA_RXCOMPLNOI) = (unsigned int)tneta1570_dev->rxcmplringptr_noi;
++        SAR_REG_WORD(tneta1570_dev, TNETA_RXCOMPLIRQ) = (unsigned int)tneta1570_dev->rxcmplringptr_irq;
++
++        /* init dma 0,1,2 for oam */
++   
++        /* alloc memory for oam fbr & buffers */
++        ptr = kmalloc(AAL0_BUFS*sizeof(struct tneta_rx_fbrptr), GFP_KERNEL);
++        if(ptr == NULL) printk(DEV_LABEL "PANIC on start rx \n");
++        for(i=0; i<AAL0_BUFS; i++) {
++	        buf = alloc_skb(MAX_AAL0_PDU+RX_HDR, GFP_KERNEL);
++                if(buf == NULL) printk(DEV_LABEL "PANIC on start rx \n");
++                *(struct sk_buff **)(buf->data) = buf;  /* link */
++                ptr[i] = OWN | ((unsigned int)(buf->data + 4) >> 2);
++	}
++
++        tneta1570_dev->oam_fbr_idx = 0;
++        /* oam cells use fbr 0 */
++        tneta1570_dev->free_buf_ptr[0].buf_ptr = ptr;
++        tneta1570_dev->free_buf_ptr[0].buf_size = FBR_AAL0_32;
++   
++        tneta1570_dev->rx_dma_state[0].control = RX_DMA_CONTROL_AAL0;
++        tneta1570_dev->rx_dma_state[0].AAL0_cells = MAX_AAL0_CELLS;
++        tneta1570_dev->rx_dma_state[0].rx_timeout = RX_TIME_OUT;
++        tneta1570_dev->rx_dma_state[0].dma_on_idx = DMA_ON + 0; 
++        tneta1570_dev->rx_dma_state[1].control = RX_DMA_CONTROL_AAL0;
++        tneta1570_dev->rx_dma_state[1].AAL0_cells = MAX_AAL0_CELLS;
++        tneta1570_dev->rx_dma_state[1].rx_timeout = RX_TIME_OUT;
++        tneta1570_dev->rx_dma_state[1].dma_on_idx = DMA_ON + 0; 
++        tneta1570_dev->rx_dma_state[2].control = RX_DMA_CONTROL_AAL0;
++        tneta1570_dev->rx_dma_state[2].AAL0_cells = MAX_AAL0_CELLS;
++        tneta1570_dev->rx_dma_state[2].rx_timeout = RX_TIME_OUT;
++        tneta1570_dev->rx_dma_state[2].dma_on_idx = DMA_ON + 0; 
++
++        SAR_REG_WORD(tneta1570_dev, TNETA_CONFIG) |= TNETA_R0_RX_ENABLE;
++
++        return 0;
++}
++
++/* 
++ * open vpi/vci for rx
++ * alloc 16 words for free-buffer ring
++ * alloc 16 skbs, size: if AAL5
++ *                         IP-MTU + a little something (64K SDUs need multiple bufs)
++ *                      else
++ *                         48 + a little something
++ * enter pointers to skbs in free-buffer ring
++ * find unused free-buffer ring-pointer table entry
++ * put pointer to free-buffer ring in ring-pointer table & tneta1570_vcc
++ * check if vpi has already alloc'd a dma table range
++ *       if not, alloc range in dma table for vpi (size is 256*8 words)
++ * if AAL5 -> set dma state table to AAL5
++ * else use counter terminated AAL0 (cell count 1)
++ * prepare dma state table entry for this vpi/vci (may turn rx for this vci on)
++ * if range < vci -> set range to vci (max) (this definitely turns it on)
++ */
++static int open_rx(struct atm_vcc *vcc)
++{
++        int i, dma_idx, fbr_idx;
++        struct sk_buff *skb;
++        unsigned int *ptr;
++        struct tneta1570_dev *tneta1570_dev;
++        struct tneta1570_vcc *tneta1570_vcc;
++
++        EVENT(">open_rx\n",0,0);
++   
++        tneta1570_dev = TNETA1570_DEV(vcc->dev);
++        tneta1570_vcc = TNETA1570_VCC(vcc);
++
++   	tneta1570_vcc->rx_wait = NULL;
++
++	tneta1570_vcc->dma_channel = alloc_dma(vcc);
++        if(!tneta1570_vcc->dma_channel) return -1;
++
++        dma_idx = tneta1570_vcc->dma_channel;
++   
++        for(fbr_idx = 0; fbr_idx<MAX_FBR_ENTRIES; fbr_idx++)
++	        if(tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr == 0)
++	                break;
++        if(fbr_idx == MAX_FBR_ENTRIES) return -1;
++	   
++        if(vcc->aal == ATM_AAL0) {
++                /* alloc memory for fbr & buffers */
++                ptr = kmalloc(AAL0_BUFS*sizeof(struct tneta_rx_fbrptr),
++		              GFP_KERNEL);
++                if(ptr == NULL) printk(DEV_LABEL "PANIC on open rx \n");
++                for(i=0; i<AAL0_BUFS; i++) {
++	                skb = vcc->peek(vcc, MAX_AAL0_PDU+RX_HDR, NULL);
++                        if(skb == NULL) printk(DEV_LABEL "PANIC on open rx \n");
++		        *(struct sk_buff **)(skb->data) = skb; /* link */
++		        skb->atm.vcc = vcc; /* link vcc info */
++                        ptr[i] = OWN | ((unsigned int)(skb->data+4) >> 2);
++	        }
++
++   	        tneta1570_vcc->fbr_idx = 0;
++
++	        tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr = ptr;
++                tneta1570_dev->free_buf_ptr[fbr_idx].buf_size = FBR_AAL0_32;
++   
++                tneta1570_dev->rx_dma_state[dma_idx].control = RX_DMA_CONTROL_AAL0;
++                tneta1570_dev->rx_dma_state[dma_idx].AAL0_cells = MAX_AAL0_CELLS;
++                tneta1570_dev->rx_dma_state[dma_idx].rx_timeout = RX_TIME_OUT;
++                tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx = DMA_ON + fbr_idx;
++	   
++	} else if(vcc->aal == ATM_AAL5) {
++                /* alloc memory for fbr & buffers */
++                ptr = kmalloc(AAL5_BUFS*sizeof(struct tneta_rx_fbrptr),
++		              GFP_KERNEL);
++                if(ptr == NULL) printk(DEV_LABEL "PANIC on open rx \n");
++                for(i=0; i<AAL5_BUFS; i++) {
++	                skb = vcc->peek(vcc, MAX_AAL5_PDU+RX_HDR, NULL);
++                        if(skb == NULL) printk(DEV_LABEL "PANIC on open rx \n");
++		        *(struct sk_buff **)(skb->data) = skb; /* link */
++		        skb->atm.vcc = vcc;  /* link vcc info */
++                        ptr[i] = OWN | ((unsigned int)(skb->data+4) >> 2);
++	        }
++
++	        tneta1570_vcc->fbr_idx = 0;
++	   
++	        tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr = ptr;
++                tneta1570_dev->free_buf_ptr[fbr_idx].buf_size = FBR_AAL5_16;
++   
++                tneta1570_dev->rx_dma_state[dma_idx].control = RX_DMA_CONTROL_AAL5;
++                tneta1570_dev->rx_dma_state[dma_idx].rx_timeout = RX_TIME_OUT;
++                tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx = DMA_ON + fbr_idx;
++	   
++	} else return -1;
++        return 0;
++}
++
++/* 
++ * close vpi/vci for rx
++ * 
++ * get free-buffer ring table address
++ * clear dma state table entry (rx for vci off)
++ * get free-buffer ring address
++ * wait for rx to drain
++ * free all remaining skbs (the ones with own enabled, e.g. all)
++ * free free-buffer ring
++ * set free-buffer-ring ptr to 0 to indicate 'freeness'
++ * check if vpi has another vci enabled
++ * -> yes:    if other_vci > this_vci -> do nothing
++ *            else                    -> set max vci to highest other vci
++ * -> no: set vci in vpi/vci table to 0
++ */
++static void close_rx(struct atm_vcc *vcc)
++{
++        int i, fbr_idx, dma_idx, x;
++        struct sk_buff *skb;
++        unsigned int *buf, *ptr;
++        struct tneta1570_dev *tneta1570_dev;
++        struct tneta1570_vcc *tneta1570_vcc;
++
++        EVENT(">close_rx\n",0,0);
++   
++        tneta1570_dev = TNETA1570_DEV(vcc->dev);
++        tneta1570_vcc = TNETA1570_VCC(vcc);
++
++        dma_idx = tneta1570_vcc->dma_channel;
++        fbr_idx = tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx & 0xff;
++        /* wait for EOP */
++        while(tneta1570_dev->rx_dma_state[dma_idx].control & OWN);
++        tneta1570_dev->rx_dma_state[dma_idx].dma_on_idx = 0; /* off */
++        /* mark as empty */
++        ptr = tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr;
++        tneta1570_dev->free_buf_ptr[fbr_idx].buf_ptr = 0;  /* mark fbr free */
++        x = tneta1570_dev->free_buf_ptr[fbr_idx].buf_size;
++        x = x >> 10;  /* ring size */
++        x &= 0x3f; 
++        x++;
++        x = 16 * x;
++        for(i=0; i<x; i++) {
++	        if(ptr[i] && OWN) {
++	                buf = (unsigned int *)(ptr[i] << 2);
++	                skb = (struct sk_buff *)(*(buf-1));
++	                skb->free = 1; /* ???? */
++                        kfree_skb(skb, FREE_READ);
++		}
++        }
++        kfree(ptr);
++
++        /* if last vci on this vpi is closed, close vpi */
++
++}
++
++
++/*----------------------------------- TX ------------------------------------*/
++
++/* 
++ * -- bring tx up --
++ * init scheduler table to all 0s
++ * kmalloc tx completion rings
++ * write ptrs to completion rings into sar regs
++ * init completion rings to all 0x80000000s
++ * write completion ring related data to device structure, set index to 0
++ * enable tx in sar
++ */
++static int start_tx(struct atm_dev *dev)
++{
++        int i;
++        unsigned int x, *seg_ring, *seg_ring_mptr;
++        struct tneta1570_dev *tneta1570_dev;
++
++        EVENT(">start_tx\n",0,0);
++   
++        tneta1570_dev = TNETA1570_DEV(dev);
++     
++        /* init tx completion rings */
++        tneta1570_dev->txcmpl_ring=kmalloc(2*TXCMPLR_SZ_IRQ*4, GFP_KERNEL);
++        if(!tneta1570_dev->txcmpl_ring) {
++	        printk(DEV_LABEL "(itf %d) malloc on tx start failed.\n", dev->number);
++	        return -ENOMEM;
++	}
++        DPRINTK("TX_CMPL_R->%08x", tneta1570_dev->txcmpl_ring);
++        /* align completion-ring with irq to its size */
++        x = (unsigned int)(&tneta1570_dev->txcmpl_ring[TXCMPLR_SZ_IRQ]);
++        tneta1570_dev->txcmplringptr_irq = (unsigned int *)(x & (~(TXCMPLR_SZ_IRQ*4 - 1)));
++        /* the rest is for the completion ring w/o irq */   
++        if((tneta1570_dev->txcmplringptr_irq - TXCMPLR_SZ_NOI) > tneta1570_dev->txcmpl_ring) {
++                tneta1570_dev->txcmplringptr_noi = tneta1570_dev->txcmplringptr_irq - TXCMPLR_SZ_NOI;
++        } else {
++                tneta1570_dev->txcmplringptr_noi = tneta1570_dev->txcmplringptr_irq + TXCMPLR_SZ_IRQ;
++        }
++        EVENT(">init tx completion ring irq\n",0,0);
++        for(i=0; i<TXCMPLR_SZ_IRQ; i++)
++                tneta1570_dev->txcmplringptr_irq[i] = OWN;
++        EVENT(">init tx completion ring noi\n",0,0);
++        for(i=0; i<TXCMPLR_SZ_NOI; i++)
++                tneta1570_dev->txcmplringptr_noi[i] = OWN;
++   
++        tneta1570_dev->txcmpl_ring_idx_noi = 0;
++        tneta1570_dev->txcmpl_ring_idx_irq = 0;
++
++        /* dma state reread bug fix - allocate dma 1 */
++        seg_ring_mptr=kmalloc(2*TX_SEG_RING_SIZE*4, GFP_KERNEL);
++        if(!seg_ring_mptr) {
++	        printk(DEV_LABEL "(itf %d) malloc on tx open failed.\n",
++		       dev->number);
++	        return -ENOMEM;
++	}
++        x = (unsigned int)(seg_ring_mptr + TX_SEG_RING_SIZE);
++        seg_ring = (unsigned int *)(x & ~(TX_SEG_RING_SIZE * 4 - 1));
++
++        for(i=0; i<TX_SEG_RING_SIZE; i++) 
++                seg_ring[i]=0;            /* turn off - high cost :-( */
++
++        write_sched_tab(tneta1570_dev, 0, 1);
++        tneta1570_dev->tx_dma_state[1].dma_state_flag = (OWN | SEG_PTR(seg_ring));
++
++        /* init registers */ 
++        SAR_REG_SHORT(tneta1570_dev, TNETA_S_TXCOMPLSIZEI) = TXCMPLR_SZ_IRQ - 1; 
++        SAR_REG_SHORT(tneta1570_dev, TNETA_S_TXCOMPLSIZE)  = TXCMPLR_SZ_NOI - 1; 
++        SAR_REG_SHORT(tneta1570_dev, TNETA_S_TXSEGSIZE)  = TX_SEG_RING_SIZE - 1; 
++        SAR_REG_SHORT(tneta1570_dev, TNETA_S_SCHEDSIZE) = 2 + SLOW_DOWN_FACTOR;
++   
++        SAR_REG_WORD(tneta1570_dev, TNETA_TXCOMPLNOI) = (unsigned int)tneta1570_dev->txcmplringptr_noi;
++        SAR_REG_WORD(tneta1570_dev, TNETA_TXCOMPLIRQ) = (unsigned int)tneta1570_dev->txcmplringptr_irq;
++
++        SAR_REG_WORD(tneta1570_dev, TNETA_CONFIG) |= TNETA_R0_TX_ENABLE;        
++
++        return 0;
++}
++ 
++/*
++ * -- open vpi/vci --
++ * find free entry in scheduler table
++ * init tx_dma_state table for this vpi/vci
++ * kmalloc tx_seg_ring, init with all 0s
++ * add entry to scheduler table
++ */
++static int open_tx(struct atm_vcc *vcc)
++{
++        int scheduler_idx, i;
++        unsigned int x;
++        struct tneta1570_dev *tneta1570_dev;
++        struct tneta1570_vcc *tneta1570_vcc;
++
++        EVENT(">open_tx\n",0,0);
++   
++        tneta1570_dev = TNETA1570_DEV(vcc->dev);
++        tneta1570_vcc = TNETA1570_VCC(vcc);
++
++   	tneta1570_vcc->tx_wait = NULL;
++
++        if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
++
++        tneta1570_vcc->seg_ring_mptr=kmalloc(2*TX_SEG_RING_SIZE*4, GFP_KERNEL);
++        if(!tneta1570_vcc->seg_ring_mptr) {
++	        printk(DEV_LABEL "(itf %d) malloc on tx open failed.\n",
++		       vcc->dev->number);
++	        return -ENOMEM;
++	}
++        /* align seg-ring to its size */
++        x = (unsigned int)(tneta1570_vcc->seg_ring_mptr + TX_SEG_RING_SIZE);
++        tneta1570_vcc->seg_ring = (unsigned int *)(x & ~(TX_SEG_RING_SIZE * 4 - 1));
++
++        EVENT("> seg ring is at phy %x\n", (unsigned int)tneta1570_vcc->seg_ring,0);
++        for(i=0; i<TX_SEG_RING_SIZE; i++) 
++                tneta1570_vcc->seg_ring[i]=0;
++        tneta1570_vcc->seg_ring_idx = 0;
++
++        /* find free scheduler table entry */
++        scheduler_idx=0;
++        while(read_sched_tab(tneta1570_dev, scheduler_idx) != 0) {
++                scheduler_idx++;
++                if(scheduler_idx >= MAX_SCHED_ENTRIES) {
++		        printk(DEV_LABEL "(itf %d) tx scheduler full on open.\n", 
++			       vcc->dev->number);
++                        return -ENOMEM;
++	        }
++	}
++
++        if((scheduler_idx < SLOW_DOWN_FACTOR) && 
++	   (scheduler_idx + SLOW_DOWN_FACTOR < MAX_SCHED_ENTRIES))
++                SAR_REG_SHORT(tneta1570_dev, TNETA_S_SCHEDSIZE) =
++                                             scheduler_idx + SLOW_DOWN_FACTOR;
++        else SAR_REG_SHORT(tneta1570_dev, TNETA_S_SCHEDSIZE) =scheduler_idx;
++
++        tneta1570_vcc->scheduler_idx = scheduler_idx;
++        write_sched_tab(tneta1570_dev, scheduler_idx, scheduler_idx+1); /* 1 in entry 0 */
++   
++        tneta1570_dev->tx_dma_state[scheduler_idx+1].dma_state_flag = 
++             (OWN | SEG_PTR(tneta1570_vcc->seg_ring));
++
++        tneta1570_vcc->txing = 0;  /* init txing flag */
++
++        return 0;
++}
++
++/* 
++ * -- close vpi/vci --
++ * remove scheduler table entry
++ * free tx_seg_ring
++ */
++static void close_tx(struct atm_vcc *vcc)
++{
++        struct tneta1570_dev *tneta1570_dev;
++        struct tneta1570_vcc *tneta1570_vcc;
++
++        EVENT(">close_tx\n",0,0);
++   
++        tneta1570_dev = TNETA1570_DEV(vcc->dev);
++        tneta1570_vcc = TNETA1570_VCC(vcc);
++
++        while(tneta1570_vcc->txing) {
++	     sleep_on(&tneta1570_vcc->tx_wait);  /* wait for tx to drain */
++	}
++  
++        /* remove ptr to segmentation ring */
++        
++        tneta1570_dev->tx_dma_state[tneta1570_vcc->scheduler_idx+1].dma_state_flag = 0;
++        write_sched_tab(tneta1570_dev, tneta1570_vcc->scheduler_idx, 0); 
++   
++        kfree(tneta1570_vcc->seg_ring_mptr);    
++}
++
++/* 
++ * -- send skb --
++ * prepare buffer with header data 
++ * set entry in segmentation ring for vpi/vci and increment index
++ * smile ;-)
++ */
++static int do_tx(struct sk_buff * skb)
++{
++        struct atm_vcc *vcc;
++        struct tneta1570_dev *tneta1570_dev;
++        struct tneta1570_vcc *tneta1570_vcc;
++        unsigned int seg_ring_entry, i;
++        unsigned int *buffer;
++   
++        NULLCHECK(skb);
++        EVENT(">do_tx: skb=0x%lx, %d bytes\n",(unsigned long) skb,skb->len);
++        vcc = skb->atm.vcc;
++        NULLCHECK(vcc);
++        tneta1570_dev = TNETA1570_DEV(vcc->dev);
++        NULLCHECK(tneta1570_dev);
++        tneta1570_vcc = TNETA1570_VCC(vcc);
++        if ((unsigned long) skb->data & 2) {
++                printk(DEV_LABEL "(itf %d): VCI %d has mis-aligned TX data\n",
++                    vcc->dev->number,vcc->vci);
++		dev_kfree_skb(skb,FREE_WRITE);
++	        return -EINVAL;
++	}
++        /* ping seems to be a problem here, its not using my alloc function */
++        /* prepare buffer descriptor */
++        if((skb->data - skb->head) < 24) {
++                DPRINTK(DEV_LABEL "(itf %d): skbuff push impossible (%d)\n",
++                    vcc->dev->number, skb->data - skb->head);
++                /* copy the data if push not possible */
++                if(!(buffer=kmalloc(skb->len + 24, GFP_ATOMIC))) {
++                        printk(DEV_LABEL "(itf %d): malloc on send failed.\n",
++                               vcc->dev->number);
++			dev_kfree_skb(skb,FREE_WRITE);
++                        return -ENOMEM;
++	        }
++                EVENT(">tx mem alloc'd at %08x\n", (unsigned int)buffer,0);
++                NULLCHECK(skb->data);
++                if(vcc->aal == ATM_AAL0) {
++                        buffer[3] = 0;
++                        buffer[4] = ((unsigned int *)skb->data)[0];  /* copy atm header */
++                        buffer[5] = 0;
++                        for(i=0; i<skb->len/4+1; i++)
++		                buffer[6+i] = ((unsigned int *)skb->data)[1+i];
++	                buffer[2] = AAL0_PDU_INT | (0xffff & skb->len); /* offset 0 */
++	        } else {
++                        buffer[3] = 0;
++                        buffer[4] = (vcc->vpi << 20) | (vcc->vci << 4);  /* prepare atm header */
++                        buffer[5] = 0;
++                        for(i=0; i<skb->len/4+1; i++)
++		                buffer[6+i] = ((unsigned int *)skb->data)[i];
++	                buffer[2] = AAL5_PDU_INT | (0xffff & skb->len);
++                }
++                buffer[1] = (unsigned int)skb;
++                buffer[0] = 0; /* push size */
++                EVENT(">data copied\n",0,0);
++	} else { /* push skb and put header in front of sdu */
++                if(vcc->aal == ATM_AAL0) { /* sdu contains header */
++                        buffer = (unsigned int *)skb_push(skb, 20);  /* make room for 1+4 words header */
++                        buffer[3] = 0;
++                        buffer[4] = buffer[5];  /* copy atm header */
++                        buffer[5] = 0;
++	                buffer[2] = AAL0_PDU_INT | (0xffff & (skb->len-20)); /* offset 0 */
++                        buffer[0] = 20; /* push size */
++	        } else {
++                        buffer = (unsigned int *)skb_push(skb, 24);  /* make room for 1+4 words header */
++                        buffer[3] = 0;
++                        buffer[4] = (vcc->vpi << 20) | (vcc->vci << 4);  /* prepare atm header */
++                        buffer[5] = 0;
++	                buffer[2] = AAL5_PDU_INT | (0xffff & (skb->len-24));
++                        buffer[0] = 24; /* push size */
++                }
++                buffer[1] = (unsigned int)skb;  /* store skb ptr for dequeue */
++	}
++
++        seg_ring_entry = ((unsigned int)(&buffer[2]) >> 2) | OWN;
++        tneta1570_vcc->seg_ring[tneta1570_vcc->seg_ring_idx] = seg_ring_entry;
++        DPRINTK(">zippered up");
++        DPRINTK(">sched %d,"
++ 		">dma %08x,"
++ 		">sridx %d >seg_r %08x (%08x),"
++ 		">buffer %08x, (%08x),"
++		"atm header %08x\n",
++ 	        read_sched_tab(tneta1570_dev, tneta1570_vcc->scheduler_idx),
++ 		tneta1570_dev->tx_dma_state[tneta1570_vcc->scheduler_idx+1].dma_state_flag,
++ 	        tneta1570_vcc->seg_ring_idx,
++ 	        tneta1570_vcc->seg_ring, 
++ 		tneta1570_vcc->seg_ring[tneta1570_vcc->scheduler_idx],
++ 	        &buffer[2], buffer[2], buffer[4]);
++	 
++        /* index to seg.ring entry for next SDU */
++        tneta1570_vcc->seg_ring_idx++;
++        tneta1570_vcc->seg_ring_idx %= TX_SEG_RING_SIZE;
++
++        tneta1570_vcc->txing++;
++        return 0;
++}
++
++/*
++ * -- dequeue tx buffer on tx complete interrupt --
++ * check completion ring
++ * while valid entry 
++ *     dequeue skb (needs a bit of backtracking)
++ *     increment completion ring index
++ */
++static void dequeue_tx(struct atm_dev * dev)
++{
++	struct tneta1570_dev *tneta1570_dev;
++	struct atm_vcc *vcc;
++	struct sk_buff *skb;
++        unsigned int * p, v;
++
++        EVENT(">dequeue_tx\n",0,0);
++   
++        NULLCHECK(dev);
++	tneta1570_dev = TNETA1570_DEV(dev);
++	NULLCHECK(tneta1570_dev);
++        
++        /* find here completion ring entries */
++        EVENT(">completion ring post: %08x, %d\n", TX_CMPL_R_IRQ(tneta1570_dev),
++		tneta1570_dev->txcmpl_ring_idx_irq); 
++        while(!(TX_CMPL_R_IRQ(tneta1570_dev) & OWN)) { 
++	        p = (unsigned int *)(TX_CMPL_R_IRQ(tneta1570_dev) << 2);
++	        TX_CMPL_R_IRQ(tneta1570_dev) = OWN;
++	   	tneta1570_dev->txcmpl_ring_idx_irq++;
++	        tneta1570_dev->txcmpl_ring_idx_irq %= TXCMPLR_SZ_IRQ;
++	        skb = (struct sk_buff *)(*(p-1));  /* get skb */
++                v = *(p-2);    /* get skb push size */
++
++                if(v==0) {     /* free copy area */
++		        kfree(p-2);
++                } else {       /* correct skb */
++                        skb_pull(skb, v);
++		}
++	      
++	        vcc = skb->atm.vcc;
++                NULLCHECK(vcc);
++	        if (vcc->pop) vcc->pop(vcc,skb);
++		else dev_kfree_skb(skb,FREE_WRITE);
++
++	        vcc->stats->tx++;
++	        TNETA1570_VCC(vcc)->txing--;
++	        wake_up(&(TNETA1570_VCC(vcc)->tx_wait));
++dma_complete++;
++	};
++}
++
++
++
++/*--------------------------------- common ----------------------------------*/
++
++
++static void foo(void)
++{
++printk("tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n"
++  "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n",
++  tx_complete,dma_complete,queued,requeued,submitted,backlogged,
++  rx_enqueued,rx_dequeued,putting,pushed);
++printk("loss: %d\n",TNETA1570_DEV(tneta1570_boards)->lost);
++}
++
++
++static void tneta1570_int(int irq,void *dev_id,struct pt_regs *regs)
++{
++	struct atm_dev *dev;
++	struct tneta1570_dev *tneta1570_dev;
++	unsigned long reason;
++
++	EVENT(">tneta_int\n",0,0);
++	dev = dev_id;
++	tneta1570_dev = TNETA1570_DEV(dev);
++	while ( (reason = 0x3ff & SAR_REG_WORD(tneta1570_dev, TNETA_STATUS)) ) {
++		DPRINTK(DEV_LABEL ": int 0x%08x\n",reason);
++		if (reason & TNETA_R1_RX_FREEZE) {
++			EVENT("INT: RX Freeze - RX ring overflow\n",0,0);
++		}	/* ? */
++		if (reason & TNETA_R1_TX_FREEZE) {
++			EVENT("INT: TX Freeze - TX ring overflow\n",0,0);
++		        dequeue_tx(dev);
++		}	/* ? */
++		if (reason & TNETA_R1_CP_RX) {
++			EVENT("INT: RX Complete - RX buffer completed\n",0,0);
++                        dequeue_rx(dev);
++		}	/* ? */
++	        if (reason & TNETA_R1_CP_TX) {
++			EVENT("INT: TX Complete - TX Buffer completed\n",0,0);
++		        dequeue_tx(dev);
++			/* pop buffers which have been completed */
++		}
++	}
++}
++
++/* 
++ * perform SAR software reset 
++ */
++static int reset_sar(struct tneta1570_dev * tneta1570_dev)
++{
++   int i;
++   unsigned int pci_config[64];
++   
++   for(i=0; i<64; i++)
++      if(pcibios_read_config_dword(tneta1570_dev->bus,
++				   tneta1570_dev->dev_fn,
++				   i*4,
++				   &pci_config[i])
++	    !=PCIBIOS_SUCCESSFUL) return -1;
++
++   SAR_REG_WORD(tneta1570_dev, TNETA_RESET) = 0;  /* reset sar */
++
++   for(i=0; i<64; i++)
++      if(pcibios_write_config_dword(tneta1570_dev->bus,
++				    tneta1570_dev->dev_fn,
++				    i*4,
++				    pci_config[i])
++	    !=PCIBIOS_SUCCESSFUL) return -1;
++   return 0;
++   
++}
++
++
++inline void write_sched_tab(struct tneta1570_dev * dev, int i, unsigned int v)
++{ 
++        if(1 & i)
++                dev->scheduler[i>>1] = (dev->scheduler[i>>1] & 0xffff) | (v << 16);
++        else
++                dev->scheduler[i>>1] = (dev->scheduler[i>>1] & 0xffff0000) | (v & 0xffff);
++}
++
++inline int read_sched_tab(struct tneta1570_dev * dev, int i)
++{ 
++        if(1 & i)
++                return (dev->scheduler[i>>1] >> 16);
++        else
++                return (0xffff & dev->scheduler[i>>1]);
++}
++
++/*--------------------------------- entries ---------------------------------*/
++
++
++static int tneta1570_init(struct atm_dev *dev)
++{
++	struct tneta1570_dev *tneta1570_dev;
++	unsigned int real_base,base;
++	unsigned short command;
++	unsigned char revision;
++	int error,i,last;
++
++	EVENT(">tneta1570_init\n",0,0);
++
++        dev->ci_range.vpi_bits = NR_VPI_LD;
++	dev->ci_range.vci_bits = NR_VCI_LD;
++	tneta1570_dev = TNETA1570_DEV(dev);
++
++        if ((error = pcibios_read_config_word(tneta1570_dev->bus,
++			    tneta1570_dev->dev_fn, PCI_COMMAND,&command)) 
++	    || (error = pcibios_read_config_dword(tneta1570_dev->bus,
++	                    tneta1570_dev->dev_fn,PCI_BASE_ADDRESS_0,&real_base)) 
++	    || (error = pcibios_read_config_byte(tneta1570_dev->bus,
++			    tneta1570_dev->dev_fn, PCI_INTERRUPT_LINE,&tneta1570_dev->irq)) 
++	    || (error = pcibios_read_config_byte(tneta1570_dev->bus,
++			    tneta1570_dev->dev_fn, PCI_REVISION_ID,&revision))) {
++ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
++		    pcibios_strerror(error));
++		return -EINVAL;
++	}
++
++        /* find mapping size of board */
++        if(pcibios_write_config_dword(tneta1570_dev->bus,
++				      tneta1570_dev->dev_fn,
++				      PCI_BASE_ADDRESS_0,
++				      0xffffffff)!=PCIBIOS_SUCCESSFUL)
++        {
++ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
++		    pcibios_strerror(error));
++		return -EINVAL;
++        }
++
++        if(pcibios_read_config_dword(tneta1570_dev->bus,
++			             tneta1570_dev->dev_fn,
++				     PCI_BASE_ADDRESS_0,
++				     &(tneta1570_dev->pci_map_size))!=PCIBIOS_SUCCESSFUL)
++        { 
++ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
++		    pcibios_strerror(error));
++		return -EINVAL;
++        }
++        tneta1570_dev->pci_map_size=~tneta1570_dev->pci_map_size+1;
++   
++        if(pcibios_write_config_dword(tneta1570_dev->bus,
++				      tneta1570_dev->dev_fn,
++				      PCI_BASE_ADDRESS_0,
++				      real_base)!=PCIBIOS_SUCCESSFUL)
++        { 
++ 		printk(DEV_LABEL "(itf %d): init error %s\n",dev->number,
++		    pcibios_strerror(error));
++		return -EINVAL;
++	}
++   
++   
++        real_base &= MEM_VALID; /* strip flags */
++	if ((error = pcibios_write_config_word(tneta1570_dev->bus,
++			                       tneta1570_dev->dev_fn,
++					       PCI_COMMAND,
++					       PCI_COMMAND_MEMORY)))
++        {
++		printk(DEV_LABEL "(itf %d): can't enable memory (%s)\n",
++		    dev->number,pcibios_strerror(error));
++		return error;
++	}
++	printk(DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",dev->number,
++	    revision,real_base,tneta1570_dev->irq);
++	if (!(base = (unsigned long) vremap(real_base,tneta1570_dev->pci_map_size))) {
++		printk(DEV_LABEL "(itf %d): can't set up page mapping\n",
++		    dev->number);
++		return error;
++	}
++	tneta1570_dev->base_diff = real_base-base;
++	tneta1570_dev->reg = (volatile unsigned int *) (base+TNETA_REG_BASE_OFFSET);
++	tneta1570_dev->scheduler = (volatile unsigned long *) (base+TNETA_SCHED_TABLE);
++	tneta1570_dev->ram = (volatile unsigned long *) (base+TNETA_SCHED_TABLE);
++        tneta1570_dev->free_buf_ptr = (volatile struct tneta_rx_fbrptr *) (base+TNETA_FREE_BUFFER_POINTERS);
++	tneta1570_dev->rx_vpi_vci = (volatile unsigned long *) (base+TNETA_RX_VPIVCI_DMA_POINTERS);
++	tneta1570_dev->tx_dma_state = (volatile struct tneta_tx_dma_state_table *) (base+TNETA_TX_DMA_STATE_TABLE);
++	tneta1570_dev->rx_dma_state = (volatile struct tneta_rx_dma_state_table *) (base+TNETA_RX_DMA_STATE_TABLE);
++	tneta1570_dev->phy = (volatile unsigned long *) (base+TNETA_SUNI_OFFSET);
++
++        tneta1570_dev->txcmpl_ring_idx_noi = 0;
++        tneta1570_dev->txcmpl_ring_idx_irq = 0;
++        tneta1570_dev->rxcmpl_ring_idx_noi = 0;
++        tneta1570_dev->rxcmpl_ring_idx_irq = 0;
++
++        /* test board memory, find amount of memory installed */ 
++        last = (TNETA_SUNI_OFFSET)/4;  /* max up to phy ctrl */
++	for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
++	        if(i>RESERVED_UL || i<RESERVED_LL) {
++	   	        tneta1570_dev->ram[i] = 0x55555555;
++		        if (tneta1570_dev->ram[i] != 0x55555555) last = i;
++		        else {
++			        tneta1570_dev->ram[i] = 0xAAAAAAAA;
++			        if (tneta1570_dev->ram[i] != 0xAAAAAAAA) last = i;
++			        else tneta1570_dev->ram[i] = i;
++		        }
++	        }
++	}
++	for (i = 0; i < last; i += RAM_INCREMENT)
++	        if(i>RESERVED_UL || i<RESERVED_LL)
++   	                if (tneta1570_dev->ram[i] != i) break;
++
++        tneta1570_dev->mem = i << 2; /* byte count */
++        /* init control memory with all 0s (including regs) */
++        last = i;
++        /* bring sar up */
++        for(i=0; i<last; i++)
++	        if(i>RESERVED_UL || i<RESERVED_LL)
++                        tneta1570_dev->ram[i] = 0; 
++
++	printk("mem=%dkB\n",tneta1570_dev->mem >> 10);
++
++        /* reset SAR */
++        reset_sar(tneta1570_dev);
++
++        for(i=0; i<last; i++)
++	        if(i>RESERVED_UL || i<RESERVED_LL)
++                        tneta1570_dev->ram[i] = 0; 
++        /* TODO: check for non-SUNI, check for TAXI ? */
++	return suni_init(dev);
++}
++
++
++static int tneta1570_start(struct atm_dev *dev)
++{
++	struct tneta1570_dev *tneta1570_dev;
++	int error;
++
++	EVENT(">tneta1570_start\n",0,0);
++	tneta1570_dev = TNETA1570_DEV(dev);
++	if (request_irq(tneta1570_dev->irq,&tneta1570_int,0,DEV_LABEL,dev)) {
++		printk(DEV_LABEL "(itf %d): IRQ%d is already in use\n",
++		    dev->number,tneta1570_dev->irq);
++		return -EAGAIN;
++	}
++	/* @@@ should release IRQ on error */
++
++        if ((error = pcibios_write_config_word(tneta1570_dev->bus,
++					       tneta1570_dev->dev_fn,
++	                                       PCI_COMMAND,
++					       PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)))
++        {
++		printk(DEV_LABEL "(itf %d): can't enable memory+master (%s)\n",
++		    dev->number,pcibios_strerror(error));
++		return error;
++	}
++
++        /* bring suni up */
++	error = dev->phy->start(dev);
++	if (error) return error;
++   
++        SAR_REG_WORD(tneta1570_dev, TNETA_CONFIG) = TNETA_R0_STANDARD_MODE;
++        SAR_REG_WORD(tneta1570_dev, TNETA_INT_MASK) = TNETA_R2_STANDARD_INTS;
++        
++        error = start_tx(dev);
++	if (error) return error;
++	error = start_rx(dev);
++	if (error) return error;
++
++        return 0;
++}
++
++
++
++static void tneta1570_close(struct atm_vcc *vcc)
++{
++	EVENT(">tneta1570_close\n",0,0);
++	if (!TNETA1570_VCC(vcc)) return;
++	vcc->flags &= ~ATM_VF_READY;
++	close_rx(vcc);
++	close_tx(vcc);
++	EVENT("tneta1570_close: done waiting\n",0,0);
++	/* deallocate memory */
++	kfree(TNETA1570_VCC(vcc));
++	TNETA1570_VCC(vcc) = NULL;
++	vcc->flags &= ~ATM_VF_ADDR;
++	/*foo();*/
++}
++
++
++/* trying to find a connection identifier */
++/* 
++ * not implemented yet
++ */
++static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci)
++{
++	return 0;
++}
++
++
++static int tneta1570_open(struct atm_vcc *vcc,short vpi,int vci)
++{
++	struct tneta1570_dev *tneta1570_dev;
++	struct tneta1570_vcc *tneta1570_vcc;
++	int error;
++
++	EVENT(">tneta1570_open\n",0,0);
++	TNETA1570_VCC(vcc) = NULL;
++        vcc->alloc_tx = tneta1570_alloc_tx; /* set my skb_alloc function */
++   
++        tneta1570_dev = TNETA1570_DEV(vcc->dev);
++#if 0   /* set to 0 to test atm_find_ci (get_ci usually is faster) */
++	error = get_ci(vcc,&vpi,&vci); /* get connection id */
++	if (error) return error;
++#else
++	error = atm_find_ci(vcc,&vpi,&vci);  /* get connection id */
++	if (error) return error;
++#endif
++	vcc->vpi = vpi;
++	vcc->vci = vci;
++	if (vcc->aal != ATM_AAL0 && vcc->aal != ATM_AAL5) return -EINVAL;
++	tneta1570_vcc = kmalloc(sizeof(struct tneta1570_vcc),GFP_KERNEL);
++	if (!tneta1570_vcc) return -ENOMEM;
++	TNETA1570_VCC(vcc) = tneta1570_vcc;
++	DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
++	    vcc->vci);
++	vcc->flags |= ATM_VF_ADDR; /* set flag */
++	if ((error = open_rx(vcc))) {    /* open rx */
++	        tneta1570_close(vcc);    /* close on error */
++		return error;
++	}
++	if ((error = open_tx(vcc))) {    /* open tx */
++		tneta1570_close(vcc);
++		return error;
++	}
++	vcc->flags |= ATM_VF_READY;  /* set IF ready */
++	/* should power down SUNI while !ref_count @@@ */
++	return 0;
++}
++
++
++static int tneta1570_ioctl(struct atm_dev *dev,unsigned int cmd,unsigned long arg)
++{
++	if (cmd == 12345678) dump(dev);
++	if (!dev->phy->ioctl) return -EINVAL;
++	return dev->phy->ioctl(dev,cmd,arg);
++}
++
++
++static int tneta1570_getsockopt(struct atm_vcc *vcc,int level,int optname,
++    char *optval,int *optlen)
++{
++	return -EINVAL;
++}
++
++
++static int tneta1570_setsockopt(struct atm_vcc *vcc,int level,int optname,
++    char *optval,int optlen)
++{
++	return -EINVAL;
++}
++
++/*
++ * -- send a PDU (AAL5 or AAL0)
++ */
++static int tneta1570_send(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	EVENT(">tneta1570_send\n",0,0);
++	if (!skb) {
++		printk("!skb in tneta1570_send ?\n");
++		dev_kfree_skb(skb,FREE_WRITE);
++		return -EINVAL;
++	}
++	if (vcc->aal == ATM_AAL0) {
++		if (skb->len != ATM_CELL_SIZE-1) {
++			dev_kfree_skb(skb,FREE_WRITE);
++			return -EINVAL;
++		}
++     	        *(unsigned long *) skb->data = htonl(*(unsigned long *)
++		                                       skb->data);
++	                /* correct byte order of an AAL0 header */
++	}
++   
++submitted++;
++	skb->atm.vcc = vcc;
++
++        return do_tx(skb);
++}
++
++/* Scatter/Gather send, build vector and send from user space
++ * not yet implemented
++ */
++static int tneta1570_sg_send(struct atm_vcc *vcc,unsigned long start,
++    unsigned long size)
++{
++	return vcc->aal == ATM_AAL5 && !((start | size) & 3);
++		/* don't tolerate misalignment */
++}
++
++
++static void tneta1570_phy_put(struct atm_dev *dev,unsigned char value,
++    unsigned long addr)
++{
++	TNETA1570_DEV(dev)->phy[addr] = value;
++}
++
++
++
++static unsigned char tneta1570_phy_get(struct atm_dev *dev,unsigned long addr)
++{
++	volatile unsigned tmp; /* force 32 bit access */
++
++        /* set address */
++	TNETA1570_DEV(dev)->phy[addr+TNETA_SUNI_RDREQ]=0;
++        /* read the bugger */
++        tmp=TNETA1570_DEV(dev)->phy[addr];
++
++	return (unsigned char)(0xff & tmp);
++}
++
++
++static struct atmdev_ops ops = {
++	tneta1570_open,
++	tneta1570_close,
++	tneta1570_ioctl,
++	tneta1570_getsockopt,
++	tneta1570_setsockopt,
++	tneta1570_send,
++	NULL,                   /* no tneta1570_sg_send */
++	NULL,			/* no poll */
++	NULL,			/* no send_oam ???? */
++	tneta1570_phy_put,
++	tneta1570_phy_get,
++	NULL			/* no feedback */
++};
++
++
++int tneta1570_detect(void)
++{
++	struct atm_dev *dev;
++	struct tneta1570_dev *tneta1570_dev;
++	int index;
++
++	if (!pcibios_present()) {
++		printk(DEV_LABEL " driver but no PCI BIOS ?\n");
++		return 0;
++	}
++	tneta1570_dev = (struct tneta1570_dev *) kmalloc(sizeof(struct tneta1570_dev),
++	    GFP_KERNEL);
++	if (!tneta1570_dev) return -ENOMEM;
++	index = 0;
++	while (!pcibios_find_device(PCI_VENDOR_ID_TI,
++	                            PCI_DEVICE_ID_TI_TNETA1570,
++				    index,
++	                            &tneta1570_dev->bus,
++				    &tneta1570_dev->dev_fn))
++        {
++		dev = atm_dev_register(DEV_LABEL,&ops,0);
++		if (!dev) break;
++		TNETA1570_DEV(dev) = tneta1570_dev;
++	   
++	        if (tneta1570_init(dev) || tneta1570_start(dev)) {
++			atm_dev_deregister(dev);
++			break;
++		}
++		tneta1570_dev->more = tneta1570_boards;
++		tneta1570_boards = dev;
++		index++;
++		tneta1570_dev = (struct tneta1570_dev *) kmalloc(sizeof(struct tneta1570_dev),
++		    GFP_KERNEL);
++		if (!tneta1570_dev) break;
++                
++	}
++	return index;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/drivers/atm/fore200.c	Thu Jul 18 20:59:50 1996
+@@ -0,0 +1,26 @@
++/* drivers/atm/fore200.c - This is just a test, not a real driver */
++ 
++/* Written 1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <asm/sbus.h>
++
++
++int fore200_detect(void)
++{
++	struct linux_sbus *bus;
++	struct linux_sbus_device *sdev = 0;
++
++	for_each_sbus(bus) {
++		for_each_sbusdev(sdev, bus) {
++			if (strcmp(sdev->prom_name,"FORE,sba-200") == 0) {
++				printk(KERN_NOTICE "fore200: found an SBA-200 "
++				    "in slot %x,offset=%08lx,irq=%d",
++				    sdev->slot,sdev->offset,sdev->irqs->pri);
++			}
++		}
++	}
++	return 0;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atm.h	Wed Jul 31 17:57:46 1996
+@@ -0,0 +1,225 @@
++/* atm.h - general ATM declarations */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef _LINUX_ATM_H
++#define _LINUX_ATM_H
++
++/*
++ * BEGIN_xx and END_xx markers are used for automatic generation of
++ * documentation. Do not change them.
++ */
++
++#include <linux/atmsap.h>
++#include <linux/atmioc.h>
++
++
++/* general ATM constants */
++#define ATM_CELL_SIZE		    53	/* ATM cell size incl. header */
++#define ATM_CELL_PAYLOAD	    48	/* ATM payload size */
++#define ATM_AAL0_SDU		    52	/* AAL0 SDU size */
++#define ATM_MAX_AAL34_PDU	 65535	/* maximum AAL3/4 PDU payload */
++#define ATM_AAL5_TRAILER	     8	/* AAL5 trailer size */
++#define ATM_MAX_AAL5_PDU	 65535	/* maximum AAL5 PDU payload */
++#define ATM_MAX_CDV		  9999	/* maximum (default) CDV */
++#define ATM_NOT_RSV_VCI		    32	/* first non-reserved VCI value */
++
++#define ATM_MAX_VPI		   255	/* maximum VPI at the UNI */
++#define ATM_MAX_VPI_NNI		  4096	/* maximum VPI at the NNI */
++#define ATM_MAX_VCI		 65535	/* maximum VCI */
++
++/*
++ * The following items should be added to sys/socket.h aka linux/socket.h
++ */
++
++/* address families */
++#define AF_ATMPVC	6		/* ATM PVCs */
++#define AF_ATMSVC	7		/* ATM SVCs */
++
++/* "protcol" values for the socket system call */
++#define ATM_AAL0	0		/* "raw" ATM cells */
++#define ATM_AAL1	1		/* AAL1 (CBR) */
++#define ATM_AAL2	2		/* AAL2 (VBR) */
++#define ATM_AAL34	3		/* AAL3/4 (data) */
++#define ATM_AAL5	5		/* AAL5 (data) */
++#define ATM_SAAL	12		/* signaling AAL */
++
++/*
++ * UGLY - there should only be one protocol family (PF_ATM) for both
++ * address families. A quick glance at some kernel internals seems to
++ * suggest to me that doing it the "right" way might involve some
++ * swimming against the stream ...
++ */
++
++/* protocol families */
++#define PF_ATMPVC	AF_ATMPVC
++#define PF_ATMSVC	AF_ATMSVC
++
++/* layers for getsockopt/setsockopt */
++#define SOL_ATM		2
++#define SOL_AAL		3
++
++/*
++ * ATM layer (values used for flags must be 2^n, non-flag values should use
++ * the remaining values)
++ */
++
++#define SO_SETCLP	1		/* set CLP bit value - TODO */
++#define SO_CIRANGE	3		/* connection identifier range;
++					   socket must be bound or connected */
++#define SO_ATMQOS	5		/* Quality of Service setting */
++
++/* AAL layer */
++#define SO_AALTYPE	0		/* AAL type, get only */
++
++/* socket layer */
++#define SO_BCTXOPT	16		/* not ATM specific - should go */
++#define SO_BCRXOPT	17		/*   somewhere else */
++
++
++/* for SO_BCTXOPT and SO_BCRXOPT */
++
++struct atm_buffconst {
++	unsigned long	buf_fac;	/* buffer alignment factor */
++	unsigned long	buf_off;	/* buffer alignment offset */
++	unsigned long	size_fac;	/* buffer size factor */
++	unsigned long	size_off;	/* buffer size offset */
++	unsigned long	min_size;	/* minimum size */
++	unsigned long	max_size;	/* maximum size, 0 = unlimited */
++};
++
++
++/* ATM cell header (for AAL0) */
++
++/* BEGIN_CH */
++#define ATM_HDR_GFC_MASK	0xf0000000
++#define ATM_HDR_GFC_SHIFT	28
++#define ATM_HDR_VPI_MASK	0x0ff00000
++#define ATM_HDR_VPI_SHIFT	20
++#define ATM_HDR_VCI_MASK	0x000ffff0
++#define ATM_HDR_VCI_SHIFT	4
++#define ATM_HDR_PTI_MASK	0x0000000e
++#define ATM_HDR_PTI_SHIFT	1
++#define ATM_HDR_CLP		0x00000001
++/* END_CH */
++
++
++/* PTI codings */
++
++/* BEGIN_PTI */
++#define ATM_PTI_US0	0  /* user data cell, congestion not exp, SDU-type 0 */
++#define ATM_PTI_US1	1  /* user data cell, congestion not exp, SDU-type 1 */
++#define ATM_PTI_UCES0	2  /* user data cell, cong. experienced, SDU-type 0 */
++#define ATM_PTI_UCES1	3  /* user data cell, cong. experienced, SDU-type 1 */
++#define ATM_PTI_SEGF5	4  /* segment OAM F5 flow related cell */
++#define ATM_PTI_E2EF5	5  /* end-to-end OAM F5 flow related cell */
++#define ATM_PTI_RSV_RM	6  /* reserved for traffic control/resource mgmt */
++#define ATM_PTI_RSV	7  /* reserved */
++/* END_PTI */
++
++
++/*
++ * The following items should stay in linux/atm.h, which should be linked to
++ * netatm/atm.h
++ */
++
++/* Traffic description */
++
++#define ATM_NONE	0		/* no traffic */
++#define ATM_UBR		1
++#define ATM_CBR		2
++#define ATM_VBR		3
++#define ATM_ABR		4
++#define ATM_ANYCLASS	5		/* compatible with everything */
++
++#define ATM_MAX_PCR	-1		/* maximum available PCR */
++
++#define class traffic_class		/* backward compatibility */
++struct atm_trafprm {
++	unsigned char	traffic_class;	/* traffic class (ATM_UBR, ...) */
++	int		max_pcr;	/* maximum PCR in cells per second */
++	int		min_pcr;	/* minimum PCR in cells per second */
++	int		max_cdv;	/* maximum CDV in microseconds */
++	int		max_sdu;	/* maximum SDU in bytes */
++};
++
++struct atm_qos {
++	struct atm_trafprm txtp;	/* parameters in TX direction */
++	struct atm_trafprm rxtp;	/* parameters in RX direction */
++};
++
++/* PVC addressing */
++
++#define ATM_ITF_ANY	-1		/* "magic" PVC address values */
++#define ATM_VPI_ANY	-1
++#define ATM_VCI_ANY	-1
++#define ATM_VPI_UNSPEC	-2
++#define ATM_VCI_UNSPEC	-2
++
++
++struct sockaddr_atmpvc {
++	unsigned short 	sap_family;	/* address family, AF_ATMPVC  */
++	struct {			/* PVC address */
++		short	itf;		/* ATM interface */
++		short	vpi;		/* VPI (only 8 bits at UNI) */
++		int	vci;		/* VCI (only 16 bits at UNI) */
++	}		sap_addr;	/* PVC address */
++};
++
++/* SVC addressing */
++
++#define	ATM_ESA_LEN	20		/* ATM End System Address length */
++#define ATM_E164_LEN	12		/* maximum E.164 number length */
++
++#define ATM_MAX_BLLI	16		/* maximum number of BLLI elements */
++
++#define ATM_AFI_DCC	0x39		/* DCC ATM Format */
++#define ATM_AFI_ICD	0x47		/* ICD ATM Format */
++#define ATM_AFI_E164	0x45		/* E.164 ATM Format */
++
++struct sockaddr_atmsvc {
++    unsigned short 	sas_family;	/* address family, AF_ATMSVC */
++    struct {				/* SVC address */
++        unsigned char	prv[ATM_ESA_LEN];/* private ATM address */
++        char		pub[ATM_E164_LEN+1]; /* public address (E.164) */
++    					/* unused addresses must be bzero'ed */
++        struct atm_blli	*blli;		/* local SAP, low-layer information */
++        struct atm_bhli	bhli;		/* local SAP, high-layer information */
++    }			sas_addr;	/* SVC address */
++};
++
++
++/*
++ * Some stuff for linux/sockios.h
++ */
++
++struct atmif_sioc {
++    int number;
++    int length;
++    void *arg;
++};
++
++
++#define SIOCSIFATMTCP	_IO('a',ATMIOC_ITF)	/* set ATMTCP mode */
++
++/*
++ * The following ioctls are obsolete and will be removed in a later release
++ */
++
++#define SIOCGIFATMADDR	_IOR('a',ATMIOC_ITF+1,struct atmif_sioc)
++					/* get local ATM address */
++#define SIOCSIFATMADDR	_IOR('a',ATMIOC_ITF+2,struct atmif_sioc)
++					/* set local ATM address */
++
++#ifdef __KERNEL__
++
++#include <linux/net.h>	/* struct net_proto */
++
++
++void atmpvc_proto_init(struct net_proto *pro);
++void atmsvc_proto_init(struct net_proto *pro);
++
++#endif /* __KERNEL__ */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atmclip.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,37 @@
++/* atmclip.h - Classical IP over ATM */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef LINUX_ATMCLIP_H
++#define LINUX_ATMCLIP_H
++
++#include <linux/sockios.h>
++#include <linux/atmioc.h>
++
++
++#define CLIP_NULENCAP	SIOCDEVPRIVATE		/* set NULL encapsulation */
++#define CLIP_LLCENCAP	(SIOCDEVPRIVATE+1)	/* set LLC/SNAP encap. */
++
++
++#define RFC1483LLC_LEN	8		/* LLC+OUI+PID = 8 */
++#define RFC1626_MTU	9180		/* RFC1626 default MTU */
++
++#define CLIP_DEFAULT_IDLETIMER 1200	/* 20 minutes, see RFC1755 */
++#define CLIP_CHECK_INTERVAL	 10	/* check every ten seconds */
++
++#define SIOCMKCLIP	_IO('a',ATMIOC_CLIP)	/* create IP interface */
++#define CLIP_PVC	_IO('a',ATMIOC_CLIP+4)	/* IP itf becomes CLIP itf */
++
++#ifdef __KERNEL__
++
++#include <linux/skbuff.h>
++#include <linux/atmdev.h>
++#include <linux/atm.h>
++
++
++int atm_init_clip(struct atm_vcc *vcc);
++
++#endif /* __KERNEL__ */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atmdev.h	Wed Jul 31 19:30:44 1996
+@@ -0,0 +1,242 @@
++/* atmdev.h - ATM device driver declarations */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef LINUX_ATMDEV_H
++#define LINUX_ATMDEV_H
++
++
++#include <linux/config.h>
++#include <linux/uio.h>
++#include <linux/atmioc.h>
++#include <asm/atomic.h>
++
++
++#define MAX_ATM_ITF	10	/* for now, should be lowered */
++#define MAX_ATM_VCC	128	/* for now, should be dynamic */
++
++#define ESI_LEN		6
++
++#define ATM_OC3_PCR	(155520000/270*260/8/53)
++			/* OC3 link rate:  155520000 bps
++			   SONET overhead: /270*260 (9 section, 1 path)
++			   bits per cell:  /8/53
++			   max cell rate:  353207.547 cells/sec */
++
++#define ATM_SD(s)	((struct atm_vcc *) ((s)->data))
++
++
++struct atm_aal_stats {
++	long		tx,tx_err;	/* TX okay and errors */
++	long		rx,rx_err;	/* RX okay and errors */
++	long		rx_drop;	/* RX out of memory */
++};
++
++
++struct atm_dev_stats {
++	struct atm_aal_stats aal0;
++	struct atm_aal_stats aal34;
++	struct atm_aal_stats aal5;
++};
++
++
++#define ATM_GETNAMES	_IOW('a',ATMIOC_ITF+3,struct atm_iobuf)
++					/* get interface names (numbers) */
++#define ATM_GETTYPE	_IOW('a',ATMIOC_ITF+4,struct atmif_sioc)
++					/* get interface type name */
++#define ATM_GETESI	_IOW('a',ATMIOC_ITF+5,struct atmif_sioc)
++					/* get interface ESI */
++#define ATM_GETADDR	_IOW('a',ATMIOC_ITF+6,struct atmif_sioc)
++					/* get itf's local ATM addr. list */
++#define ATM_RSTADDR	_IOW('a',ATMIOC_ITF+7,struct atmif_sioc)
++					/* reset itf's ATM address list */
++#define ATM_ADDADDR	_IOW('a',ATMIOC_ITF+8,struct atmif_sioc)
++					/* add a local ATM address */
++#define ATM_DELADDR	_IOW('a',ATMIOC_ITF+9,struct atmif_sioc)
++					/* remove a local ATM address */
++#define ATM_GETCIRANGE	_IOW('a',ATMIOC_ITF+10,struct atmif_sioc)
++					/* get connection identifier range */
++#define ATM_SETCIRANGE	_IOW('a',ATMIOC_ITF+11,struct atmif_sioc)
++					/* set connection identifier range */
++#define ATM_GETSTAT	_IOW('a',ATMIOC_SARCOM+0,struct atmif_sioc)
++					/* get AAL layer statistics */
++#define ATM_GETSTATZ	_IOW('a',ATMIOC_SARCOM+1,struct atmif_sioc)
++					/* get AAL layer statistics and zero */
++
++/* for ATM_GETTYPE */
++#define ATM_ITFTYP_LEN	8	/* maximum length of interface type name */
++
++
++
++struct atm_iobuf {
++	int length;
++	void *buffer;
++};
++
++/* for ATM_GETCIRANGE / ATM_SETCIRANGE */
++
++#define ATM_CI_MAX      -1              /* use maximum range of VPI/VCI */
++ 
++struct atm_cirange {
++	char	vpi_bits;		/* 1..8, ATM_CI_MAX (-1) for maximum */
++	char	vci_bits;		/* 1..16, ATM_CI_MAX (-1) for maximum */
++};
++
++
++#define ATM_BACKLOG_DEFAULT 32 /* if we get more, we're likely to time out
++				  anyway */
++
++#ifdef __KERNEL__
++
++#include <linux/sched.h> /* struct wait_queue */
++#include <linux/time.h> /* struct timeval */
++#include <linux/net.h>
++#include <linux/skbuff.h> /* struct sk_buff */
++#include <linux/atm.h>
++
++#ifdef CONFIG_AREQUIPA
++#include <net/sock.h> /* for struct sock */
++#endif
++
++
++#define ATM_VF_ADDR	1	/* Address is in use. Set by anybody, cleared
++				   by device driver. */
++#define ATM_VF_READY	2	/* VC is ready to transfer data. Set by device
++				   driver, cleared by anybody. */
++#define ATM_VF_PARTIAL	4	/* resources are bound to PVC (partial PVC
++				   setup), controlled by socket layer */
++#define ATM_VF_BOUND	4	/* local SAP is set, controlled by SVC socket
++				   layer */
++#define ATM_VF_REGIS	8	/* registered with demon, controlled by SVC
++				   socket layer */
++#define ATM_VF_RELEASED 16	/* demon has indicated/requested release,
++				   controlled by SVC socket layer */
++#define ATM_VF_HASQOS	32	/* QOS parameters have been set */
++#define ATM_VF_LISTEN	64	/* socket is used for listening */
++#define ATM_VF_META	128	/* SVC socket isn't used for normal data
++				   traffic and doesn't depend on signaling
++				   to be available */
++#define ATM_VF_AQREL	256	/* Arequipa socket is being released */
++
++struct atm_vcc {
++	unsigned short	flags;		/* VCC flags (ATM_VF_*) */
++	unsigned char	family;		/* address family; 0 if unused */
++	unsigned char	aal;		/* ATM Adaption Layer */
++	short		vpi;		/* VPI and VCI (types must be equal */
++					/* with sockaddr) */
++	int 		vci;
++	unsigned long	aal_options;	/* AAL layer options */
++	unsigned long	atm_options;	/* ATM layer options */
++	struct atm_dev	*dev;		/* device back pointer */
++	struct atm_qos	qos;		/* QOS */
++	unsigned long	tx_quota,rx_quota; /* buffer quotas */
++	atomic_t	tx_inuse,rx_inuse; /* buffer space in use */
++	void (*push)(struct atm_vcc *vcc,struct sk_buff *skb);
++	void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */
++	struct sk_buff *(*peek)(struct atm_vcc *vcc,unsigned long pdu_size,
++	    __u32 (*fetch)(struct atm_vcc *vcc,int i));
++					/* super-efficient xfers; note that */
++					/* PDU_SIZE may be rounded */
++	struct sk_buff *(*alloc_tx)(struct atm_vcc *vcc,unsigned int size);
++					/* TX allocation routine - can be */
++					/* modified by protocol or by driver.*/
++					/* NOTE: this interface will change */
++	int (*push_oam)(struct atm_vcc *vcc,void *cell);
++	void		*dev_data;	/* per-device data */
++	void		*proto_data;	/* per-protocol data */
++	struct timeval	timestamp;	/* AAL timestamps */
++	struct sk_buff_head recvq;	/* receive queue */
++	struct atm_aal_stats *stats;	/* pointer to AAL stats group */
++	struct wait_queue *sleep;	/* if socket is busy */
++	struct atm_vcc	*prev,*next;
++	/* SVC part --- may move later */
++	short		itf;		/* interface number */
++	struct sockaddr_atmsvc local;
++	struct sockaddr_atmsvc remote;
++	void (*callback)(struct atm_vcc *vcc);
++	struct sk_buff_head listenq;
++	int		backlog_quota;	/* number of connection requests we */
++					/* can still accept */
++	int		reply;
++	void		*user_back;	/* user backlink - not touched */
++#ifdef CONFIG_AREQUIPA
++	struct sock	*upper;		/* our "master" */
++	struct socket	*sock;		/* back pointer to our own socket */
++#endif
++};
++
++
++struct atm_dev_addr {
++	struct sockaddr_atmsvc addr;	/* ATM address */
++	struct atm_dev_addr *next;	/* next address */
++};
++
++
++struct atm_dev {
++	const struct atmdev_ops *ops;	/* device operations; NULL if unused */
++	const struct atmphy_ops *phy;	/* PHY operations, may be undefined */
++					/* (NULL) */
++	const char	*type;		/* device type name */
++	int		number;		/* device index */
++	struct atm_vcc	*vccs;		/* VCC table (or NULL) */
++	struct atm_vcc	*last;		/* last VCC (or undefined) */
++	void		*dev_data;	/* per-device data */
++	void		*phy_data;	/* private PHY date */
++	unsigned long	flags;		/* device flags, TBD */
++	struct atm_dev_addr *local;	/* local ATM addresses */
++	unsigned char	esi[ESI_LEN];	/* ESI ("MAC" addr) */
++	struct atm_cirange ci_range;	/* VPI/VCI range */
++	struct atm_dev_stats stats;	/* statistics */
++/* */ int sending;
++};
++
++
++/*
++ * ioctl, getsockopt, setsockopt, sg_send, and poll are optional and can
++ * be set to NULL.
++ */
++
++#define ATM_OF_IMMED	1	/* Attempt immediate delivery */
++#define ATM_OF_INRATE	2	/* Attempt in-rate delivery */
++
++struct atmdev_ops { /* only send is required */
++	int (*open)(struct atm_vcc *vcc,short vpi,int vci);
++	void (*close)(struct atm_vcc *vcc);
++	int (*ioctl)(struct atm_dev *dev,unsigned int cmd,unsigned long arg);
++	int (*getsockopt)(struct atm_vcc *vcc,int level,int optname,
++	    char *optval,int *optlen);
++	int (*setsockopt)(struct atm_vcc *vcc,int level,int optname,
++	    char *optval,int optlen);
++	int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
++	int (*sg_send)(struct atm_vcc *vcc,unsigned long start,
++	    unsigned long size);
++#if 0 /* keep the current hack for now */
++	int (*send_iovec)(struct atm_vcc *vcc,struct iovec *iov,int size,
++	    void (*discard)(struct atm_vcc *vcc,void *user),void *user);
++#endif
++	void (*poll)(struct atm_vcc *vcc,int nonblock);
++	int (*send_oam)(struct atm_vcc *vcc,void *cell,int flags);
++	void (*phy_put)(struct atm_dev *dev,unsigned char value,
++	    unsigned long addr);
++	unsigned char (*phy_get)(struct atm_dev *dev,unsigned long addr);
++	void (*feedback)(struct atm_vcc *vcc,struct sk_buff *skb,
++	    unsigned long start,unsigned long dest,int len);
++};
++
++
++struct atmphy_ops {
++	int (*start)(struct atm_dev *dev);
++	int (*ioctl)(struct atm_dev *dev,unsigned int cmd,unsigned long arg);
++	void (*interrupt)(struct atm_dev *dev);
++};
++
++
++struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
++    unsigned long flags);
++void atm_dev_deregister(struct atm_dev *dev);
++int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci);
++
++#endif /* __KERNEL__ */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atmsap.h	Mon Jun 10 17:36:14 1996
+@@ -0,0 +1,129 @@
++/* atmsap.h - ATM Service Access Point addressing definitions */
++
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef _LINUX_ATMSAP_H
++#define _LINUX_ATMSAP_H
++
++/*
++ * BEGIN_xx and END_xx markers are used for automatic generation of
++ * documentation. Do not change them.
++ */
++
++
++/*
++ * Layer 2 protocol identifiers
++ */
++
++/* BEGIN_L2 */
++#define ATM_L2_NONE	0	/* L2 not specified */
++#define ATM_L2_ISO1745  0x01	/* Basic mode ISO 1745 */
++#define ATM_L2_Q291	0x02	/* ITU-T Q.291 */
++#define ATM_L2_X25_LL	0x06	/* ITU-T X.25, link layer */
++#define ATM_L2_X25_ML	0x07	/* ITU-T X.25, multilink */
++#define ATM_L2_LAPB	0x08	/* Extended LAPB, half-duplex */
++#define ATM_L2_HDLC_ARM	0x09	/* HDLC ARM (ISO 4335) */
++#define ATM_L2_HDLC_NRM	0x0a	/* HDLC NRM (ISO 4335) */
++#define ATM_L2_HDLC_ABM	0x0b	/* HDLC ABM (ISO 4335) */
++#define ATM_L2_ISO8802	0x0c	/* LAN LLC (ISO 8802/2) */
++#define ATM_L2_X75	0x0d	/* ITU-T X.75, SLP */
++#define ATM_L2_Q922	0x0e	/* ITU-T Q.922 */
++#define ATM_L2_USER	0x10	/* user-specified */
++#define ATM_L2_ISO7776	0x11	/* ISO 7776 DTE-DTE */
++/* END_L2 */
++
++
++/*
++ * Layer 3 protocol identifiers
++ */
++
++/* BEGIN_L3 */
++#define ATM_L3_NONE	0	/* L3 not specified */
++#define ATM_L3_X25	0x06	/* ITU-T X.25, packet layer */
++#define ATM_L3_ISO8208	0x07	/* ISO/IEC 8208 */
++#define ATM_L3_X223	0x08	/* ITU-T X.223/ISO 8878 */
++#define ATM_L3_ISO8473	0x09	/* ISO/IEC 8473 */
++#define ATM_L3_T70	0x0a	/* ITU-T T.70 minimum network layer */
++#define ATM_L3_TR9577	0x0b	/* ISO/IEC TR 9577 */
++#define ATM_L3_USER	0x10	/* user-specified */
++/* END_L3 */
++
++
++/*
++ * High layer identifiers
++ */
++
++/* BEGIN_HL */
++#define ATM_HL_NONE	0	/* HL not specified */
++#define ATM_HL_ISO	0x01	/* ISO */
++#define ATM_HL_USER	0x02	/* user-specific */
++#if defined(UNI30) || defined(ALLOW_UNI30)
++#define ATM_HL_HLP	0x03	/* high layer profile - UNI 3.0 only */
++#endif
++#define ATM_HL_VENDOR	0x04	/* vendor-specific application identifier */
++/* END_HL */
++
++
++/*
++ * ITU-T coded mode of operation
++ */
++
++/* BEGIN_IMD */
++#define ATM_IMD_NONE	 0	/* mode not specified */
++#define ATM_IMD_NORMAL	 1	/* normal mode of operation */
++#define ATM_IMD_EXTENDED 2	/* extended mode of operation */
++/* END_IMD */
++
++/*
++ * Selected ISO/IEC TR 9577 Network Layer Protocol Identifiers (NLPID)
++ */
++
++#define NLPID_IEEE802_1_SNAP	0x80	/* IEEE 802.1 SNAP */
++
++/*
++ * SAP structures
++ */
++
++#define ATM_MAX_HLI	7	/* maximum high-layer information length */
++
++
++struct atm_blli {
++    unsigned char l2_proto;	/* layer 2 protocol */
++    union {
++	struct {
++	    unsigned char mode;	/* mode of operation (ATM_IMD_xxx), 0 if */
++				/* absent */
++	    unsigned char window; /* window size (k), 1-127 (0 to omit) */
++	} itu;			/* ITU-T encoding */
++	unsigned char user;	/* user-specified l2 information */
++    } l2;
++    unsigned char l3_proto;	/* layer 3 protocol */
++    union {
++	struct {
++	    unsigned char mode;	/* mode of operation (ATM_IMD_xxx), 0 if */
++				/* absent */
++	    unsigned char def_size; /* default packet size (log2), 4-12 (0 to */
++				    /* omit) */
++	    unsigned char window;/* packet window size, 1-127 (0 to omit) */
++	} itu;			/* ITU-T ecoding */
++	unsigned char user;	/* user specified l3 information */
++	struct {		/* if l3_proto = ATM_L3_TR9577 */
++	    unsigned char ipi;	/* initial protocol id */
++	    unsigned char snap[5];/* IEEE 802.1 SNAP identifier */
++				  /* (only if ipi == NLPID_IEEE802_1_SNAP) */
++	} tr9577;
++    } l3;
++    struct atm_blli *next;	/* next BLLI or NULL (undefined when used in */
++				/* atmsvc_msg) */
++};
++
++
++struct atm_bhli {
++    unsigned char hl_type;	/* high layer information type */
++    unsigned char hl_length;	/* length (only if hl_type == ATM_HL_USER || */
++				/* hl_type == ATM_HL_ISO) */
++    unsigned char hl_info[ATM_MAX_HLI];/* high layer information */
++};
++
++#endif
+--- ref/include/linux/skbuff.h	Sun Jun  9 11:23:42 1996
++++ work/include/linux/skbuff.h	Wed Jul 31 19:30:20 1996
+@@ -112,6 +112,16 @@
+ 	unsigned char 	*end;			/* End pointer					*/
+ 	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
+ 	__u16		redirport;		/* Redirect port				*/
++#ifdef CONFIG_ATM
++	struct {
++		int		size;		/* PDU size (adapter too) */
++		unsigned long	pos;		/* adapter data */
++		struct atm_vcc	*vcc;		/* ATM VCC */
++		int		iovcnt;		/* 0 for "normal" operation */
++		struct timeval	timestamp;	/* timestamp or 0 */
++	} atm;
++#endif
++
+ };
+ 
+ #ifdef CONFIG_SKB_LARGE
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/sonet.h	Mon Jun 10 17:36:15 1996
+@@ -0,0 +1,52 @@
++/* sonet.h - SONET/SHD physical layer control */
++ 
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef LINUX_SONET_H
++#define LINUX_SONET_H
++
++struct sonet_stats {
++	long section_bip;		/* section parity errors (B1) */
++	long line_bip;			/* line parity errors (B2) */
++	long path_bip;			/* path parity errors (B3) */
++	long line_febe;			/* line parity errors at remote */
++	long path_febe;			/* path parity errors at remote */
++	long corr_hcs;			/* correctable header errors */
++	long uncorr_hcs;		/* uncorrectable header errors */
++	long tx_cells;			/* cells sent */
++	long rx_cells;			/* cells received */
++};
++
++#define SONET_GETSTAT	_IOR('a',ATMIOC_PHYTYP,struct sonet_stats)
++					/* get statistics */
++#define SONET_GETSTATZ	_IOR('a',ATMIOC_PHYTYP+1,struct sonet_stats)
++					/* ... and zero counters */
++#define SONET_SETDIAG	_IOWR('a',ATMIOC_PHYTYP+2,int)
++					/* set error insertion */
++#define SONET_CLRDIAG	_IOWR('a',ATMIOC_PHYTYP+3,int)
++					/* clear error insertion */
++#define SONET_GETDIAG	_IOR('a',ATMIOC_PHYTYP+4,int)
++					/* query error insertion */
++#define SONET_SETFRAMING _IO('a',ATMIOC_PHYTYP+5)
++					/* set framing mode (SONET/SDH) */
++#define SONET_GETFRAMING _IOR('a',ATMIOC_PHYTYP+6,int)
++					/* get framing mode */
++#define SONET_GETFRSENSE _IOR('a',ATMIOC_PHYTYP+7, \
++  unsigned char[SONET_FRSENSE_SIZE])	/* get framing sense information */
++
++#define SONET_INS_SBIP	  1		/* section BIP */
++#define SONET_INS_LBIP	  2		/* line BIP */
++#define SONET_INS_PBIP	  4		/* path BIP */
++#define SONET_INS_FRAME	  8		/* out of frame */
++#define SONET_INS_LOS	 16		/* set line to zero */
++#define SONET_INS_LAIS	 32		/* line alarm indication signal */
++#define SONET_INS_PAIS	 64		/* path alarm indication signal */
++#define SONET_INS_HCS	128		/* insert HCS error */
++
++#define SONET_FRAME_SONET 0		/* SONET STS-3 framing */
++#define SONET_FRAME_SDH   1		/* SDH STM-1 framing */
++
++#define SONET_FRSENSE_SIZE 6		/* C1[3],H1[3] (0xff for unknown) */
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atmsvc.h	Wed Jul 31 18:20:21 1996
+@@ -0,0 +1,75 @@
++/* atmsvc.h - ATM signaling kernel-demon interface definitions */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef _LINUX_ATMSVC_H
++#define _LINUX_ATMSVC_H
++
++#include <linux/atm.h>
++#include <linux/atmioc.h>
++
++
++#define ATMSIGD_CTRL _IO('a',ATMIOC_SPECIAL)
++				/* become ATM signaling demon control socket */
++
++enum atmsvc_msg_type { as_catch_null,as_bind,as_connect,as_accept,as_listen,
++  as_okay,as_error,as_indicate,as_close,as_itf_notify };
++
++struct atmsvc_msg {
++	enum atmsvc_msg_type type;
++	unsigned long vcc;
++	unsigned long listen_vcc;	/* indicate */
++	int reply;			/* for okay and close:		   */
++					/*   < 0: error before active	   */
++					/*        (sigd has discarded ctx) */
++					/*   ==0: success		   */
++				        /*   > 0: error when active (still */
++					/*        need to close)	   */
++	unsigned char aal;		/* AAL */
++	struct sockaddr_atmpvc pvc;	/* indicate, okay (connect) */
++	struct sockaddr_atmsvc local;	/* local SVC address */
++	struct atm_qos qos;		/* QOS parameters */
++	struct sockaddr_atmsvc svc;	/* MUST BE BEFORE "blli", SVC address */
++	struct atm_blli blli[1];	/* MUST BE LAST */
++};
++
++/*
++ * Message contents:
++ *
++ * Message   Dir  vcc listen_vcc reply aal    pvc       --svc--    local
++ *                                         addr *xtp addr sap *xtp addr
++ * bind	     K->D  X      -        -    -   -    -    X    X   -    -
++ * - okay    D->K  X      -        -    -   -    -    X    X   -    -
++ * connect   K->D  X      -        -    X   -    -    X    X   X    X
++ * - okay    D->K  X      -        -    -   X    X    -    -   -    X
++ * listen    K->D  X      -        -    X   -    -    X    X   X    -
++ * - okay    D->K  X      -        -    -   -    -    -    -   -    -
++ * indicate  D->K  -      X        -    -   X    1    X    X   X    -
++ * accept    K->D  X      X        -    -   -    -    -    -   -    -
++ * - okay    D->K  X      -        -    -   -    -    -    -   -    X
++ * close     K->D  X      -        -    -   -    -    -    -   -    -
++ * close     D->K  X      -        X    -   -    -    -    -   -    -
++ *
++ *  1  same value as in svc.*xtp
++ *
++ * Note: we may have to get rid of the PVC part in indicate messages later,
++ *       so it's a good idea not to depend on it too heavily.
++ */
++
++/*
++ * In an as_itf_update message, only the fields type and pvc.sas_addr.itf are
++ * valid.
++ */
++
++/*
++ * Some policy stuff for atmsigd and for net/atm/svc.c. Both have to agree on
++ * what PCR is used to request bandwidth from the device driver. net/atm/svc.c
++ * tries to do better than that, but only if there's no routing decision (i.e.
++ * if signaling only uses one ATM interface).
++ */
++
++#define SELECT_TOP_PCR(tp) ((tp).max_pcr && (tp).max_pcr != ATM_MAX_PCR ? \
++  (tp).max_pcr : (tp).min_pcr)
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atmioc.h	Mon Jun 10 17:36:16 1996
+@@ -0,0 +1,32 @@
++/* atmioc.h - ranges for ATM-related ioctl numbers */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef _LINUX_ATMIOC_H
++#define _LINUX_ATMIOC_H
++
++#include <asm/ioctl.h>
++		/* everybody including atmioc.h will also need _IO{,R,W,WR} */
++
++#define ATMIOC_PHYCOM	  0x00 /* PHY device common ioctls, globally unique */
++#define ATMIOC_PHYCOM_END 0x0f
++#define ATMIOC_PHYTYP	  0x10 /* PHY dev type ioctls, unique per PHY type */
++#define ATMIOC_PHYTYP_END 0x2f
++#define ATMIOC_PHYPRV	  0x30 /* PHY dev private ioctls, unique per driver */
++#define ATMIOC_PHYPRV_END 0x4f
++#define ATMIOC_SARCOM	  0x50 /* SAR device common ioctls, globally unique */
++#define ATMIOC_SARCOM_END 0x50
++#define ATMIOC_SARPRV	  0x60 /* SAR dev private ioctls, unique per driver */
++#define ATMIOC_SARPRV_END 0x7f
++#define ATMIOC_ITF	  0x80 /* Interface ioctls, globally unique */
++#define ATMIOC_ITF_END	  0x8f
++/* 0x90-0xbf: Reserved for future use */
++#define ATMIOC_AREQUIPA	  0xc0 /* Application requested IP over ATM, glob. u. */
++#define ATMIOC_LANE	  0xd0 /* LAN Emulation, globally unique */
++#define	ATMIOC_CLIP	  0xe0 /* Classical IP over ATM control, globally u. */
++#define	ATMIOC_CLIP_END	  0xef
++#define	ATMIOC_SPECIAL	  0xf0 /* Special-purpose controls, globally unique */
++#define	ATMIOC_SPECIAL_END 0xff
++
++#endif
+--- ref/net/Makefile	Thu May 16 15:35:55 1996
++++ work/net/Makefile	Mon Jun 10 17:50:26 1996
+@@ -8,12 +8,17 @@
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
+ MOD_SUB_DIRS := ipv4
+-ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipx unix appletalk netrom #decnet
++ALL_SUB_DIRS := 802 atm ax25 bridge core ethernet ipv4 ipx unix appletalk \
++		netrom #decnet
+ SUB_DIRS     := core ethernet unix
+ MOD_LIST_NAME := NET_MISC_MODULES
+ 
+ ifeq ($(CONFIG_NET),y)
+ SUB_DIRS += 802
++endif
++
++ifeq ($(CONFIG_ATM),y)
++SUB_DIRS += atm
+ endif
+ 
+ ifeq ($(CONFIG_INET),y)
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/Makefile	Wed Jul 17 19:31:55 1996
+@@ -0,0 +1,54 @@
++#
++# Makefile for the ATM Protocol Families.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++include ../../.config
++
++CFLAGS += -g
++
++ifeq ($(CONFIG_ATM),y)
++
++OBJS	= common.o dev.o mmuio.o pvc.o raw.o signaling.o static.o svc.o
++
++ifeq ($(CONFIG_ATM_ATMARP),y)
++OBJS += atmarp.o
++NEED_IPCOM = ipcommon.o
++endif
++
++ifeq ($(CONFIG_ATM_CLIP),y)
++OBJS += clip.o
++NEED_IPCOM = ipcommon.o
++endif
++
++ifeq ($(CONFIG_AREQUIPA),y)
++OBJS += arequipa.o
++NEED_IPCOM = ipcommon.o
++endif
++
++OBJS += $(NEED_IPCOM)
++
++ifeq ($(CONFIG_PROC_FS),y)
++OBJS += proc.o
++endif
++
++ifeq ($(CONFIG_ATM_LANE),y)
++OBJS += lec.o lec_arpc.o
++endif
++
++atm.o:	$(OBJS)
++	$(LD) -r -o atm.o $(OBJS)
++
++else
++
++atm.o:
++	$(AR) rcs atm.o
++
++endif
++
++
++include $(TOPDIR)/Rules.make
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/clip.c	Wed Jul 31 17:15:40 1996
+@@ -0,0 +1,115 @@
++/* net/atm/clip.c - Classical IP over ATM */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/in.h>
++#include <linux/mmuio.h>
++#include <linux/atmdev.h>
++#include <linux/atmclip.h>
++#include <net/sock.h>
++#include <netinet/in.h>
++
++#include "protocols.h"
++#include "common.h"
++#include "ipcommon.h"
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++
++static int pvc_num = 0;
++
++
++/*static*/ void atm_push_clip(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	DPRINTK("clip push\n");
++	if (!skb) {
++		printk(KERN_ALERT "ARGL!\n");
++		unregister_netdev((struct device *) vcc->proto_data);
++		vcc->proto_data = NULL;
++		return;
++	}
++	skb->dev = vcc->proto_data;
++	ipcom_push(skb);
++}
++
++
++static int clip_xmit(struct sk_buff *skb,struct device *dev)
++{
++	DPRINTK("clip xmit\n");
++	if (!CLIP(dev)->vcc) return -ENOTCONN; /* @@@ discard skb ? */
++	ipcom_xmit(dev,CLIP(dev)->vcc,skb);
++	CLIP(dev)->stats.tx_packets++;
++	return 0;
++}
++
++
++#if 0
++static int clip_sg_xmit(struct sk_buff *skb,struct device *dev)
++{
++/*
++struct iovec *iov;
++int i;
++
++iov = (struct iovec *) skb->data;
++for (i = 0; i < skb->atm.iovcnt; i++)
++  printk("[%d] 0x%lx + %d\n",i,(unsigned long) iov[i].iov_base,
++    iov[i].iov_len);
++
++	printk("clip *DIRECT* xmit (iovcnt: %d)\n",skb->atm.iovcnt);
++*/
++	CLIP(dev)->vcc->dev->ops->send(CLIP(dev)->vcc,skb);
++	CLIP(dev)->stats.tx_packets++;
++	return 0;
++}
++#endif
++
++
++static int clip_init(struct device *dev)
++{
++	ipcom_init(dev,clip_xmit,0);
++	return 0;
++}
++
++
++
++int atm_init_clip(struct atm_vcc *vcc)
++{
++	struct device *dev;
++	int number;
++
++	DPRINTK("atm_init_clip\n");
++	if (!suser()) return -EPERM;
++	vcc->aal = ATM_AAL5;
++	vcc->push = atm_push_clip;
++	vcc->peek = atm_peek_clip;
++	vcc->pop = atm_pop_clip;
++	vcc->push_oam = NULL;
++	vcc->proto_data = dev = kmalloc(sizeof(struct device)+
++	    sizeof(struct clip_priv),GFP_KERNEL);
++	if (!dev) return -ENOMEM;
++	memset(dev,0,sizeof(struct device)+sizeof(struct clip_priv));
++	dev->name = CLIP(dev)->name;
++	number = pvc_num++;
++	sprintf(dev->name,"atm%d",number);
++	dev->init = clip_init;
++	CLIP(dev)->vcc = vcc;
++	if (register_netdev(dev)) return -EIO;
++	DPRINTK("registered %s,0x%lx\n",dev->name,(unsigned long) vcc);
++	return number;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/common.c	Wed Jul 31 19:29:44 1996
+@@ -0,0 +1,899 @@
++/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++#include <linux/net.h>		/* struct socket, struct net_proto, struct
++				   proto_ops */
++#include <linux/atm.h>		/* ATM stuff */
++#include <linux/atmdev.h>
++#include <linux/atmclip.h>	/* CLIP_*ENCAP */
++#include <linux/atmarp.h>	/* manifest constants */
++#include <linux/sonet.h>	/* for ioctls */
++#include <linux/socket.h>	/* SOL_SOCKET */
++#include <linux/errno.h>	/* error codes */
++#include <linux/kernel.h>	/* suser */
++#include <linux/mm.h>		/* verify_area */
++#include <linux/sched.h>
++#include <linux/time.h>		/* struct timeval */
++#include <linux/skbuff.h>
++#include <linux/mmuio.h>
++#include <linux/uio.h>
++
++#ifdef CONFIG_AREQUIPA
++#include <linux/arequipa.h>
++#endif
++
++#ifdef CONFIG_ATM_LANE
++#include <linux/atmlec.h>
++#include "lec.h"
++#include "lec_arpc.h"
++#endif
++
++#include "static.h"		/* atm_find_dev */
++#include "common.h"		/* prototypes */
++#include "protocols.h"		/* atm_init_<transport> */
++#include "tunable.h"		/* tunable parameters */
++#include "atmarp.h"		/* for clip_create */
++#include "signaling.h"		/* for WAITING and sigd_attach */
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size)
++{
++	struct sk_buff *skb;
++
++	if (size+vcc->tx_inuse+ATM_PDU_OVHD > vcc->tx_quota) return NULL;
++	while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule();
++	DPRINTK("AlTx %d += %d\n",vcc->tx_inuse,skb->truesize);
++	atomic_add(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse);
++	return skb;
++}
++
++
++int atm_create(struct socket *sock,int protocol)
++{
++	struct atm_vcc *vcc;
++	int error;
++
++	ATM_SD(sock) = NULL;
++	if (sock->type == SOCK_STREAM) return -EINVAL;
++	if (!(vcc = alloc_atm_vcc())) return -ENOMEM;
++#ifdef CONFIG_AREQUIPA
++	vcc->upper = NULL;
++	vcc->sock = sock;
++#endif
++	vcc->flags = 0;
++	vcc->dev = NULL;
++	vcc->family = sock->ops->family;
++	vcc->alloc_tx = alloc_tx;
++	vcc->callback = NULL;
++	memset(&vcc->local,0,sizeof(struct sockaddr_atmsvc));
++	memset(&vcc->remote,0,sizeof(struct sockaddr_atmsvc));
++	vcc->tx_quota = ATM_TXBQ_DEF;
++	vcc->rx_quota = ATM_RXBQ_DEF;
++	vcc->tx_inuse = vcc->rx_inuse = 0;
++	switch (protocol) {
++		/*
++		 * Note: ATM_AAL0 also covers the "0 for default" case,
++		 *	 although some people may not like that default ;-)
++		 */
++		case ATM_AAL0:
++			error = atm_init_aal0(vcc);
++			break;
++		case ATM_AAL34:
++			error = atm_init_aal34(vcc);
++			break;
++		case ATM_AAL5:
++			error = atm_init_aal5(vcc);
++			break;
++		default:
++			error = -EPROTOTYPE;
++	}
++	if (error) {
++		free_atm_vcc(vcc);
++		return error;
++	}
++	vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
++	vcc->atm_options = vcc->aal_options = 0;
++	vcc->timestamp.tv_sec = vcc->timestamp.tv_usec = 0;
++	vcc->sleep = NULL;
++	skb_queue_head_init(&vcc->recvq);
++	skb_queue_head_init(&vcc->listenq);
++	ATM_SD(sock) = vcc;
++	return 0;
++}
++
++
++int atm_release_vcc(struct atm_vcc *vcc,int free_vcc)
++{
++	struct sk_buff *skb;
++
++	vcc->flags &= ~ATM_VF_READY;
++	if (vcc->dev) {
++		if (vcc->dev->ops->close) vcc->dev->ops->close(vcc);
++		if (vcc->push) vcc->push(vcc,NULL); /* atmarpd has no push */
++		while ((skb = skb_dequeue(&vcc->recvq)))
++			kfree_skb(skb,FREE_READ);
++		if (vcc->prev) vcc->prev->next = vcc->next;
++		else vcc->dev->vccs = vcc->next;
++		if (vcc->next) vcc->next->prev = vcc->prev;
++		else vcc->dev->last = vcc->prev;
++	}
++	if (free_vcc) free_atm_vcc(vcc);
++	return 0;
++}
++
++
++extern void atm_push_clip(struct atm_vcc *vcc,struct sk_buff *skb);
++
++
++int atm_release(struct socket *sock,struct socket *peer)
++{
++	struct atm_vcc *vcc;
++
++	vcc = ATM_SD(sock);
++	if (!vcc
++#ifdef CONFIG_ATM_CLIP
++	    || (vcc->push == atm_push_clip && (vcc->vpi || vcc->vci))
++#endif
++	    )
++		return 0; /* ugly */
++	return atm_release_vcc(vcc,1);
++}
++
++
++static int adjust_tp(struct atm_trafprm *tp,unsigned char aal)
++{
++	int max_sdu;
++
++	if (!tp->class) return 0;
++	if (tp->class != ATM_UBR && !tp->min_pcr && !tp->max_pcr)
++		return -EINVAL;
++	switch (aal) {
++		case ATM_AAL0:
++			max_sdu = ATM_CELL_SIZE-1;
++			break;
++		case ATM_AAL34:
++			max_sdu = ATM_MAX_AAL34_PDU;
++			break;
++		default:
++			printk(KERN_WARNING "ATM: AAL problems ... "
++			    "(%d)\n",aal);
++			/* fall through */
++		case ATM_AAL5:
++			max_sdu = ATM_MAX_AAL5_PDU;
++	}
++	if (!tp->max_sdu) tp->max_sdu = max_sdu;
++	else if (tp->max_sdu > max_sdu) return -EINVAL;
++	if (!tp->max_cdv) tp->max_cdv = ATM_MAX_CDV;
++	return 0;
++}
++
++
++static int check_ci(struct atm_vcc *vcc,short vpi,int vci)
++{
++	struct atm_vcc *walk;
++
++	for (walk = vcc->dev->vccs; walk; walk = walk->next)
++		if ((walk->flags & ATM_VF_ADDR) && walk->vpi == vpi &&
++		    walk->vci == vci && ((walk->qos.txtp.traffic_class !=
++		    ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) ||
++		    (walk->qos.rxtp.traffic_class != ATM_NONE &&
++		    vcc->qos.rxtp.traffic_class != ATM_NONE)))
++			return -EADDRINUSE;
++		/* allow VCCs with same VPI/VCI iff they don't collide on
++		   TX/RX (but we may refuse such sharing for other reasons,
++		   e.g. if protocol requires to have both channels) */
++	return 0;
++}
++
++
++int atm_find_ci(struct atm_vcc *vcc,short *vpi,int *vci)
++{
++	static short p = 0; /* poor man's per-device cache */
++	static int c = 0;
++	short old_p;
++	int old_c;
++
++	if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY)
++		return check_ci(vcc,*vpi,*vci);
++	/* last scan may have left values out of bounds for current device */
++	if (*vpi != ATM_VPI_ANY) p = *vpi;
++	else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
++	if (*vci != ATM_VCI_ANY) c = *vci;
++	else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits)
++			c = ATM_NOT_RSV_VCI;
++	old_p = p;
++	old_c = c;
++	do {
++		if (!check_ci(vcc,p,c)) {
++			*vpi = p;
++			*vci = c;
++			return 0;
++		}
++		if (*vci == ATM_VCI_ANY) {
++			c++;
++			if (c >= 1 << vcc->dev->ci_range.vci_bits)
++				c = ATM_NOT_RSV_VCI;
++		}
++		if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) &&
++		    *vpi == ATM_VPI_ANY) {
++			p++;
++			if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0;
++		}
++	}
++	while (old_p != p || old_c != c);
++	return -EADDRINUSE;
++}
++
++
++static int atm_do_connect(struct atm_vcc *vcc,int itf,int vpi,int vci)
++{
++	volatile struct atm_dev *dev; /* need volatile for sequence */
++	int error;
++
++	if (itf >= MAX_ATM_ITF) return -EINVAL;
++	dev = &atm_dev[itf];
++	if (!dev->ops) return -ENODEV;
++	if ((vpi != ATM_VPI_UNSPEC && vpi >> dev->ci_range.vpi_bits) ||
++	    (vci != ATM_VCI_UNSPEC && vci >> dev->ci_range.vci_bits))
++		return -EINVAL;
++	if (vci > 0 && vci < ATM_NOT_RSV_VCI && !suser()) return -EPERM;
++	switch (vcc->aal) {
++		case ATM_AAL0:
++			vcc->stats = &((struct atm_dev *) dev)->stats.aal0;
++			break;
++		case ATM_AAL34:
++			vcc->stats = &((struct atm_dev *) dev)->stats.aal34;
++			break;
++		case ATM_AAL5:
++			vcc->stats = &((struct atm_dev *) dev)->stats.aal5;
++			break;
++		default:
++			printk(KERN_WARNING "%s(itf %d): unsupported AAL "
++			    "(%d)\n",vcc->dev->type,vcc->dev->number,vcc->aal);
++			return -EINVAL;
++	}
++	error = adjust_tp(&vcc->qos.txtp,vcc->aal);
++	if (!error) error = adjust_tp(&vcc->qos.rxtp,vcc->aal);
++	if (error) return error;
++	vcc->dev = (struct atm_dev *) dev;
++	DPRINTK("VCC %d.%d, AAL %d\n",vpi,vci,vcc->aal);
++	DPRINTK("  TX: %d, PCR %d..%d, SDU %d\n",vcc->qos.txtp.traffic_class,
++	    vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu);
++	DPRINTK("  RX: %d, PCR %d..%d, SDU %d\n",vcc->qos.rxtp.class,
++	    vcc->qos.rxtp.min_pcr,vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu);
++	if (dev->ops->open) {
++		error = dev->ops->open(vcc,vpi,vci);
++		if (error) {
++			vcc->dev = NULL;
++			return error;
++		}
++	}
++	vcc->prev = dev->last;
++	vcc->next = NULL;
++	if (!dev->vccs) dev->vccs = vcc;
++	else dev->last->next = vcc;
++	dev->last = vcc;
++	return 0;
++}
++
++
++int atm_connect(struct socket *sock,int itf,short vpi,int vci)
++{
++	struct atm_vcc *vcc;
++	int error;
++
++	DPRINTK("atm_connect (vpi %d, vci %d)\n",vpi,vci);
++	if (sock->state == SS_CONNECTED) return -EISCONN;
++	if (sock->state != SS_UNCONNECTED) return -EINVAL;
++	if (!(vpi || vci)) return -EINVAL;
++	vcc = ATM_SD(sock);
++	if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC)
++		vcc->flags &= ~ATM_VF_PARTIAL;
++	else if (vcc->flags & ATM_VF_PARTIAL) return -EINVAL;
++	printk("atm_connect (TX: cl %d,bw %d-%d,sdu %d; RX: cl %d,bw %d-%d,"
++	    "sdu %d)\n",vcc->qos.txtp.traffic_class,vcc->qos.txtp.min_pcr,
++	    vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu,
++	    vcc->qos.rxtp.traffic_class,vcc->qos.rxtp.min_pcr,
++	    vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu);
++	if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
++	    vcc->qos.rxtp.traffic_class == ATM_ANYCLASS)
++		return -EINVAL;
++	if (itf != ATM_ITF_ANY) error = atm_do_connect(vcc,itf,vpi,vci);
++	else {
++		int i;
++
++		error = -ENODEV;
++		for (i = 0; i < MAX_ATM_ITF; i++)
++			if (atm_dev[i].ops)
++				if (!(error = atm_do_connect(vcc,i,vpi,vci)))
++					break;
++	}
++	if (error) return error;
++	if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
++		vcc->flags |= ATM_VF_PARTIAL;
++	else sock->state = SS_CONNECTED;
++	return 0;
++}
++
++
++int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len,
++    int nonblock,int flags,int *addr_len)
++{
++	struct atm_vcc *vcc;
++	struct sk_buff *skb;
++	unsigned long cpu_flags;
++	int eff_len;
++
++	void *buff;
++	int size;
++
++	if (sock->state != SS_CONNECTED || flags || m->msg_name)
++		return -EINVAL;
++	if (m->msg_iovlen != 1) return -ENOSYS; /* fix this later @@@ */
++	buff = m->msg_iov->iov_base;
++	size = m->msg_iov->iov_len;
++	/* verify_area is done by net/socket.c */
++	vcc = ATM_SD(sock);
++	if (vcc->dev->ops->poll && (vcc->flags & ATM_VF_READY))
++		while (1) {
++			vcc->dev->ops->poll(vcc,nonblock);
++			if (current->signal & ~current->blocked)
++				return -ERESTARTSYS;
++			if (skb_peek(&vcc->recvq)) break;
++			if (nonblock) return -EAGAIN;
++		}
++	save_flags(cpu_flags);
++	cli();
++	while (!(skb = skb_dequeue(&vcc->recvq))) {
++		if (vcc->flags & ATM_VF_RELEASED) return vcc->reply;
++		if (!(vcc->flags & ATM_VF_READY)) return 0;
++		if (nonblock) {
++			restore_flags(cpu_flags);
++			return -EAGAIN;
++		}
++		interruptible_sleep_on(&vcc->sleep);
++		if (current->signal & ~current->blocked) {
++			restore_flags(cpu_flags);
++			return -ERESTARTSYS;
++		}
++	}
++	restore_flags(cpu_flags);
++	vcc->timestamp = skb->atm.timestamp;
++	eff_len = skb->len > size ? size : skb->len;
++	if (vcc->dev->ops->feedback)
++		vcc->dev->ops->feedback(vcc,skb,(unsigned long) skb->data,
++		    (unsigned long) buff,eff_len);
++	DPRINTK("RcvM %d -= %d\n",vcc->rx_inuse,skb->truesize);
++	atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->rx_inuse);
++#ifdef CONFIG_MMU_HACKS
++	mmucp_tofs((unsigned long) buff,eff_len,skb,(unsigned long) skb->data);
++#else
++#ifdef DUMP_PACKETS
++        if (vcc->vci==5 || vcc->vci >32) {
++                char buf[300];
++                int i;
++                
++                for(i=0;i<99 && i < skb->len;i++) {
++                        sprintf(buf+i*3,"%2.2x ",
++                                0xff&skb->data[i]);
++                }
++                printk("recv %d:%s\n",vcc->vci,buf);
++        }          
++#endif      
++	memcpy_tofs(buff,skb->data,eff_len);
++	kfree_skb(skb,FREE_READ);
++#endif
++	return eff_len;
++}
++
++
++int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len,
++  int nonblock,int flags)
++{
++	struct atm_vcc *vcc;
++	struct sk_buff *skb;
++	int eff;
++
++	const void *buff;
++	int size;
++
++	if (sock->state != SS_CONNECTED || flags || m->msg_name)
++		return -EINVAL;
++	if (m->msg_iovlen != 1) return -ENOSYS; /* fix this later @@@ */
++	buff = m->msg_iov->iov_base;
++	size = m->msg_iov->iov_len;
++	vcc = ATM_SD(sock);
++	if (vcc->flags & ATM_VF_RELEASED) return vcc->reply;
++	if (!(vcc->flags & ATM_VF_READY)) return -EPIPE;
++	if (!size) return 0;
++	/* verify_area is done by net/socket.c */
++#ifdef CONFIG_MMU_HACKS
++	if (vcc->dev->ops->sg_send && vcc->dev->ops->sg_send(vcc,
++	    (unsigned long) buff,size)) {
++		int res,max_iov;
++
++		max_iov = 2+size/PAGE_SIZE;
++		/*
++		 * Doesn't use alloc_tx yet - this will change later. @@@
++		 */
++		while (!(skb = alloc_skb(sizeof(struct iovec)*max_iov,
++		    GFP_KERNEL))) {
++			if (nonblock) return -EAGAIN;
++			interruptible_sleep_on(&vcc->sleep);
++			if (current->signal & ~current->blocked)
++				return -ERESTARTSYS;
++		}
++		skb->free = 1;
++		skb->len = size;
++		res = lock_user((unsigned long) buff,size,max_iov,
++		    (struct iovec *) skb->data);
++		if (res < 0) {
++			kfree_skb(skb,FREE_WRITE);
++			if (res != -EAGAIN) return res;
++		}
++		else {
++			DPRINTK("res is %d\n",res);
++			DPRINTK("Asnd %d += %d\n",vcc->tx_inuse,skb->truesize);
++			atomic_add(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse);
++			skb->atm.iovcnt = res;
++			skb_device_lock(skb);
++			vcc->dev->ops->send(vcc,skb);
++			/* FIXME: security: may send up to 3 "garbage" bytes */
++			return size;
++		}
++	}
++#endif
++	eff = (size+3) & ~3; /* align to word boundary */
++	while (!(skb = vcc->alloc_tx(vcc,eff))) {
++		if (nonblock) return -EAGAIN;
++		interruptible_sleep_on(&vcc->sleep);
++		if (current->signal & ~current->blocked)
++			return -ERESTARTSYS;
++		if (vcc->flags & ATM_VF_RELEASED) return vcc->reply;
++		if (!(vcc->flags & ATM_VF_READY)) return -EPIPE;
++	}
++	skb->free = 1;
++	skb->len = size;
++	skb->atm.iovcnt = 0;
++	memcpy_fromfs(skb->data,buff,size);
++	if (eff != size) memset(skb->data+size,0,eff-size);
++#if 0 /* experimental */
++	vcc->dev->sending = 1;
++#endif
++	skb_device_lock(skb);
++#ifdef DUMP_PACKETS
++        if (vcc->vci==5 || vcc->vci >32) {
++                char buf[300];
++                int i;
++                
++                for(i=0;i<99 && i < skb->len;i++) {
++                        sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
++                }
++                printk("send %d:%s\n",vcc->vci,buf);
++        }
++#endif
++	vcc->dev->ops->send(vcc,skb);
++#if 0 /* experimental */
++	while (vcc->dev->sending) sleep_on(&vcc->sleep);
++#endif
++	return size;
++}
++
++
++int atm_select(struct socket *sock,int sel_type,select_table *wait)
++{
++	struct atm_vcc *vcc;
++
++	vcc = ATM_SD(sock);
++	switch(sel_type) {
++		case SEL_IN:
++			if (vcc->dev && vcc->dev->ops->poll)
++				vcc->dev->ops->poll(ATM_SD(sock),1);
++			if (sock->state == SS_CONNECTING) break;
++			if (!skb_peek(&vcc->recvq) &&
++			    !skb_peek(&vcc->listenq) &&
++			    !(vcc->flags & ATM_VF_RELEASED)) break;
++			if (vcc->dev && vcc->dev->ops->poll) return 0;
++			return 1;
++		case SEL_OUT:
++			if (sock->state == SS_CONNECTING) break;
++			if (vcc->qos.txtp.traffic_class == ATM_NONE) return 1;
++			if (vcc->qos.txtp.max_sdu+vcc->tx_inuse+ATM_PDU_OVHD >
++			  vcc->tx_quota) break;
++			return 1;
++		case SEL_EX:
++			if (sock->state != SS_CONNECTING) return 0;
++			if (vcc->reply == WAITING) break;
++			return 1;
++	}
++	select_wait(&vcc->sleep,wait);
++	return 0;
++}
++
++
++
++static int fetch_stats(struct atm_dev *dev,struct atm_dev_stats *arg,int zero)
++{
++	unsigned long flags;
++ 
++	save_flags(flags);
++	cli();
++	if (arg)
++		memcpy_tofs(arg,&dev->stats,sizeof(struct atm_dev_stats));
++        if (zero)
++                memset(&dev->stats,0,sizeof(struct atm_dev_stats));
++        restore_flags(flags);
++	return 0;
++}
++
++
++extern int clip_ioctl(void *dev,void *rq,int cmd); /* @@@ lie and cheat ... */
++
++
++#ifdef CONFIG_AREQUIPA
++
++/* @@@ stolen from net/socket.c - should be in a common header file */
++
++
++struct socket *sockfd_lookup(int fd)
++{
++	struct file *file;
++	struct inode *inode;
++
++	if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd])) 
++		return NULL;
++	inode = file->f_inode;
++	if (!inode || !inode->i_sock) return NULL;
++	return &inode->u.socket_i;
++}
++
++#endif
++
++
++int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
++{
++	struct atm_dev *dev;
++	unsigned long eff_arg;
++	int rsize,wsize,len;
++	int error;
++
++	rsize = wsize = 0;
++	switch (cmd) {
++		case ATM_GETNAMES:
++			{
++				struct atm_iobuf *buf;
++
++				error = verify_area(VERIFY_READ,(void *) arg,
++				    sizeof(struct atm_iobuf));
++				if (error) return error;
++				buf = (struct atm_iobuf *) arg;
++				error = verify_area(VERIFY_WRITE,
++				    (void *) get_fs_long(&buf->buffer),
++				    get_fs_long(&buf->length));
++				if (error) return error;
++				error = verify_area(VERIFY_WRITE,
++				    (void *) &buf->length,sizeof(int));
++				if (error) return error;
++				return atm_dev_list((void *) get_fs_long(&buf->
++				    buffer),&buf->length);
++			}
++		case SIOCGSTAMP: /* borrowed from IP */
++			if (!ATM_SD(sock)->timestamp.tv_sec) return -ENOENT;
++			error = verify_area(VERIFY_WRITE,(void *) arg,
++			    sizeof(struct timeval));
++			if (error) return error;
++			memcpy_tofs((void *) arg,&ATM_SD(sock)->timestamp,
++			    sizeof(struct timeval));
++			return 0;
++		case ATMSIGD_CTRL:
++			if (!suser()) return -EPERM;
++			error = sigd_attach(ATM_SD(sock));
++			if (!error) sock->state = SS_CONNECTED;
++			return error;
++#ifdef CONFIG_ATM_CLIP
++		case CLIP_PVC:
++			if (!suser()) return -EPERM;
++			return atm_init_clip(ATM_SD(sock));
++		case CLIP_NULENCAP:
++		case CLIP_LLCENCAP:
++			if (!suser()) return -EPERM;
++			return clip_ioctl(ATM_SD(sock)->proto_data,NULL,cmd);
++				/* rather crude hack ... */
++#endif
++#ifdef CONFIG_ATM_ATMARP
++		case SIOCMKCLIP:
++			if (!suser()) return -EPERM;
++			return clip_create(arg);
++		case ATMARPD_CTRL:
++			if (!suser()) return -EPERM;
++			error = atm_init_atmarp(ATM_SD(sock));
++			if (!error) sock->state = SS_CONNECTED;
++			return error;
++		case ATMARP_MKIP:
++			if (!suser()) return -EPERM;
++			return atmarp_mkip(ATM_SD(sock),arg);
++		case ATMARP_SETENTRY:
++			if (!suser()) return -EPERM;
++			return atmarp_setentry(ATM_SD(sock),arg);
++		case ATMARP_ENCAP:
++			if (!suser()) return -EPERM;
++			return atmarp_encap(ATM_SD(sock),arg);
++#endif
++#ifdef CONFIG_AREQUIPA
++		case AREQUIPA_PRESET:
++			{
++				struct socket *upper;
++
++				if (!(upper = sockfd_lookup(arg)))
++					return -ENOTSOCK;
++				if (upper->ops->family != PF_INET)
++					return -EPROTOTYPE;
++				return arequipa_preset(sock,
++				    (struct sock *) upper->data);
++			}
++		case AREQUIPA_INCOMING:
++			return arequipa_incoming(sock);
++		case AREQUIPA_CTRL:
++			if (!suser()) return -EPERM;
++			error = arequipad_attach(ATM_SD(sock));
++			if (!error) sock->state = SS_CONNECTED;
++			return error;
++		case AREQUIPA_CLS3RD:
++			if (!suser()) return -EPERM;
++			arequipa_close_vcc((struct atm_vcc *) arg);
++			return 0;
++#endif
++#ifdef CONFIG_ATM_LANE
++                case ATMLEC_CTRL:
++                        if (!suser()) return -EPERM;
++                        error = lecd_attach(ATM_SD(sock));
++                        if (!error) sock->state = SS_CONNECTED;
++                        return error;
++                case ATMLEC_MCAST:
++                        if (!suser()) return -EPERM;
++                        return lec_mcast_attach(ATM_SD(sock));
++                case ATMLEC_DATA:
++                        if (!suser()) return -EPERM;
++                        return lec_vcc_attach(ATM_SD(sock), (void*)arg);
++#endif
++		case ATM_GETTYPE:
++			wsize = -1; /* special - don't check length */
++			break;
++		case ATM_GETESI:
++			wsize = ESI_LEN;
++			break;
++		case ATM_GETSTATZ:
++			if (!suser()) return -EPERM;
++			/* fall through */
++		case ATM_GETSTAT:
++			wsize = sizeof(struct atm_dev_stats);
++			break;
++		case ATM_GETCIRANGE:
++			wsize = sizeof(struct atm_cirange);
++			break;
++		case ATM_SETCIRANGE:
++			if (!suser()) return -EPERM;
++			rsize = sizeof(struct atm_cirange);
++			break;
++		case SONET_GETSTATZ:
++			if (!suser()) return -EPERM;
++			/* fall through */
++		case SONET_GETSTAT:
++			wsize = sizeof(struct sonet_stats);
++			break;
++		case SONET_GETDIAG:
++			wsize = sizeof(int);
++			break;
++		case SONET_SETDIAG:
++		case SONET_CLRDIAG:
++			if (!suser()) return -EPERM;
++			rsize = sizeof(int);
++			break;
++		case SONET_SETFRAMING:
++			rsize = sizeof(int);
++			break;
++		case SONET_GETFRAMING:
++			wsize = sizeof(int);
++			break;
++		case SONET_GETFRSENSE:
++			wsize = SONET_FRSENSE_SIZE;
++			break;
++		default:
++			wsize = -1; /* just in case ... */
++	}
++	error = verify_area(VERIFY_READ,(void *) arg,
++	    sizeof(struct atmif_sioc));
++	if (error) return error;
++	if (wsize) {
++		error = verify_area(VERIFY_WRITE,(void *) arg,
++		    sizeof(struct atmif_sioc));
++		if (error) return error;
++	}
++	if (!(dev = atm_find_dev(get_fs_long(&((struct atmif_sioc *) arg)->
++	    number)))) return -ENODEV;
++	len = get_fs_long(&((struct atmif_sioc *) arg)->length);
++	eff_arg = get_fs_long(&((struct atmif_sioc *) arg)->arg);
++	if (!eff_arg && (rsize || wsize)) return -EINVAL;
++	if (rsize > 0) {
++		if (len != rsize) return -EINVAL;
++		error = verify_area(VERIFY_READ,(void *) eff_arg,len);
++		if (error) return error;
++	}
++	if (wsize > 0) {
++		if (len != wsize) return -EINVAL;
++		error = verify_area(VERIFY_WRITE,(void *) eff_arg,len);
++		if (error) return error;
++		put_fs_long(wsize,&((struct atmif_sioc *) arg)->length);
++	}
++	switch (cmd) {
++		case ATM_GETTYPE:
++			{
++				int length;
++
++				length = strlen(dev->type);
++				if (len <length+1) return -EINVAL;
++				error = verify_area(VERIFY_WRITE,
++				    (void *) eff_arg,length+1);
++				if (error) return error;
++				memcpy_tofs((void *) eff_arg,dev->type,
++				    length+1);
++				put_fs_long(length,
++				    &((struct atmif_sioc *) arg)->length);
++				return length;
++			}
++		case ATM_GETESI:
++			memcpy_tofs((void *) eff_arg,dev->esi,ESI_LEN);
++			return wsize;
++		case ATM_GETSTATZ:
++		case ATM_GETSTAT:
++			error = fetch_stats(dev,(void *) eff_arg,
++			    cmd == ATM_GETSTATZ);
++			return error < 0 ? error : wsize;
++		case ATM_GETCIRANGE:
++			memcpy_tofs((void *) eff_arg,&dev->ci_range,
++			    sizeof(struct atm_cirange));
++			return wsize;
++		default:
++			if (!dev->ops->ioctl) return -EINVAL;
++			error = dev->ops->ioctl(dev,cmd,eff_arg);
++			if (error >= 0) 
++				put_fs_long(len,
++				    &((struct atmif_sioc *) arg)->length);
++			return error;
++	}
++}
++
++
++int atm_setsockopt(struct socket *sock,int level,int optname,
++    char *optval,int optlen)
++{
++	struct atm_vcc *vcc;
++	int error;
++
++	error = verify_area(VERIFY_READ,optval,optlen);
++	if (error) return -EINVAL;
++	vcc = ATM_SD(sock);
++	if (level == SOL_SOCKET) {
++		unsigned long value;
++
++		switch (optname) {
++			case SO_SNDBUF:
++				if (optlen != sizeof(value)) return -EINVAL;
++				value = get_fs_long(optval);
++				if (!value) value = ATM_TXBQ_DEF;
++				if (value < ATM_TXBQ_MIN) value = ATM_TXBQ_MIN;
++				if (value > ATM_TXBQ_MAX) value = ATM_TXBQ_MAX;
++				vcc->tx_quota = value;
++				return 0;
++			case SO_RCVBUF:
++				if (optlen != sizeof(value)) return -EINVAL;
++				value = get_fs_long(optval);
++				if (!value) value = ATM_RXBQ_DEF;
++				if (value < ATM_RXBQ_MIN) value = ATM_RXBQ_MIN;
++				if (value > ATM_RXBQ_MAX) value = ATM_RXBQ_MAX;
++				vcc->rx_quota = value;
++				return 0;
++			default:
++				return -EINVAL;
++		}
++	}
++	if (level == SOL_ATM) {
++		switch (optname) {
++			case SO_ATMQOS:
++				if (sock->state != SS_UNCONNECTED)
++					return -EBADFD;
++				if (optlen != sizeof(struct atm_qos))
++					return -EINVAL;
++				memcpy_fromfs(&vcc->qos,optval,optlen);
++				vcc->flags |= ATM_VF_HASQOS;
++				return 0;
++			default:
++				break;
++		}
++	}
++	if (!vcc->dev || !vcc->dev->ops->setsockopt) return -EINVAL;
++	return vcc->dev->ops->setsockopt(vcc,level,optname,optval,optlen);
++}
++
++
++static int putsockopt(void *data,int len,char *optval,int *optlen)
++{
++	int error;
++
++	if (get_fs_long(optlen) < len) return -EINVAL;
++	put_fs_long(len,optlen);
++	error = verify_area(VERIFY_WRITE,optval,len);
++	if (error) return error;
++	memcpy_tofs(optval,data,len);
++	return 0;
++}
++
++
++int atm_getsockopt(struct socket *sock,int level,int optname,
++    char *optval,int *optlen)
++{
++	struct atm_vcc *vcc;
++	int error;
++
++	error = verify_area(VERIFY_READ,optlen,sizeof(*optlen)); /* paranoia? */
++	if (error) return error;
++	error = verify_area(VERIFY_WRITE,optlen,sizeof(*optlen));
++	if (error) return error;
++	vcc = ATM_SD(sock);
++	switch (level) {
++		case SOL_SOCKET:
++			switch (optname) {
++				case SO_SNDBUF:
++					return putsockopt(&vcc->tx_quota,
++					    sizeof(unsigned long),optval,
++					    optlen);
++				case SO_RCVBUF:
++					return putsockopt(&vcc->rx_quota,
++					    sizeof(unsigned long),optval,
++					    optlen);
++				case SO_BCTXOPT:
++				case SO_BCRXOPT:
++					level = SOL_AAL; /* cheat */
++					break;
++				default:
++					return -EINVAL;
++			}
++			/* fall through */
++		case SOL_AAL:
++			switch (optname) {
++				case SO_AALTYPE:
++					return putsockopt(&vcc->aal,
++					    sizeof(unsigned char),optval,
++					    optlen);
++				default:
++					break;
++			}
++			break;
++		case SOL_ATM:
++			switch (optname) {
++				case SO_ATMQOS:
++					if (!(vcc->flags & ATM_VF_HASQOS))
++						return -EINVAL;
++					return putsockopt(&vcc->qos,
++					    sizeof(struct atm_qos),optval,
++					    optlen);
++				default:
++					break;
++			}
++			/* fall through */
++		default:
++			break;
++	}
++	if (!vcc->dev || !vcc->dev->ops->getsockopt) return -EINVAL;
++	return vcc->dev->ops->getsockopt(vcc,level,optname,optval,optlen);
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/common.h	Wed Jul 31 18:20:21 1996
+@@ -0,0 +1,32 @@
++/* net/atm/common.h - ATM sockets (common part for PVC and SVC) */
++ 
++/* Written 1995 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef NET_ATM_COMMON_H
++#define NET_ATM_COMMON_H
++
++#include <linux/net.h>
++
++int atm_create(struct socket *sock,int protocol);
++int atm_release(struct socket *sock,struct socket *peer);
++int atm_connect(struct socket *sock,int itf,short vpi,int vci);
++int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len,
++    int nonblock,int flags,int *addr_len);
++int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len,
++  int nonblock,int flags);
++int atm_select(struct socket *sock,int sel_type,select_table *wait);
++int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg);
++int atm_setsockopt(struct socket *sock,int level,int optname,char *optval,
++    int optlen);
++int atm_getsockopt(struct socket *sock,int level,int optname,char *optval,
++    int *optlen);
++
++int atm_release_vcc(struct atm_vcc *vcc,int free_vcc);
++
++/* SVC */
++
++int copy_svc_addr(struct sockaddr_atmsvc *to,struct sockaddr_atmsvc *from);
++void svc_callback(struct atm_vcc *vcc);
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/dev.c	Mon Jun 10 17:36:19 1996
+@@ -0,0 +1,38 @@
++/* net/atm/dev.c - ATM device registeration */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/atmdev.h>
++#include <linux/kernel.h>
++#include <linux/uio.h>
++
++#include "static.h"
++
++
++struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
++    unsigned long flags)
++{
++	volatile struct atm_dev *dev; /* need volatile to ensure sequence */
++
++	dev = alloc_atm_dev(type);
++	if (!dev) {
++		printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
++		    type);
++		return NULL;
++	}
++	dev->number = 0;
++	while (atm_find_dev(dev->number)) dev->number++;
++	dev->vccs = dev->last = NULL;
++	dev->dev_data = NULL;
++	dev->ops = ops;
++	dev->flags = flags;
++	memset((void *) &dev->stats,0,sizeof(struct atm_dev_stats));
++	return (struct atm_dev *) dev;
++}
++
++
++void atm_dev_deregister(struct atm_dev *dev)
++{
++	free_atm_dev(dev);
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/pvc.c	Mon Jul 29 10:40:33 1996
+@@ -0,0 +1,162 @@
++/* net/atm/pvc.c - ATM PVC sockets */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++#include <linux/net.h>		/* struct socket, struct net_proto,
++				   struct proto_ops */
++#include <linux/atm.h>		/* ATM stuff */
++#include <linux/atmdev.h>	/* ATM devices */
++#include <linux/atmclip.h>	/* Classical IP over ATM */
++#include <linux/errno.h>	/* error codes */
++#include <linux/kernel.h>	/* printk */
++#include <linux/skbuff.h>
++#ifdef CONFIG_AREQUIPA
++#include <linux/arequipa.h>
++#endif
++
++#include "static.h"		/* static data ... */
++#include "common.h"		/* common for PVCs and SVCs */
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++
++static int pvc_dup(struct socket *newsock,struct socket *oldsock)
++{
++	return -EOPNOTSUPP;
++}
++
++
++static int pvc_shutdown(struct socket *sock,int how)
++{
++	return -EOPNOTSUPP;
++}
++
++
++static int pvc_bind(struct socket *sock,struct sockaddr *sockaddr,
++    int sockaddr_len)
++{
++	struct sockaddr_atmpvc *addr;
++	struct atm_vcc *vcc;
++
++	if (sockaddr_len != sizeof(struct sockaddr_atmpvc)) return -EINVAL;
++	addr = (struct sockaddr_atmpvc *) sockaddr;
++	if (addr->sap_family != AF_ATMPVC) return -EAFNOSUPPORT;
++	vcc = ATM_SD(sock);
++	if (!(vcc->flags & ATM_VF_HASQOS)) return -EBADFD;
++	if (vcc->flags & ATM_VF_PARTIAL) {
++		if (vcc->vpi != ATM_VPI_UNSPEC) addr->sap_addr.vpi = vcc->vpi;
++		if (vcc->vci != ATM_VCI_UNSPEC) addr->sap_addr.vci = vcc->vci;
++	}
++	return atm_connect(sock,addr->sap_addr.itf,addr->sap_addr.vpi,
++	    addr->sap_addr.vci);
++}
++
++
++static int pvc_connect(struct socket *sock,struct sockaddr *sockaddr,
++    int sockaddr_len,int flags)
++{
++	return pvc_bind(sock,sockaddr,sockaddr_len);
++}
++
++
++static int pvc_listen(struct socket *sock,int backlog)
++{
++	return -EOPNOTSUPP;
++}
++
++
++static int pvc_accept(struct socket *sock,struct socket *newsock,int flags)
++{
++	return -EOPNOTSUPP;
++}
++
++
++static int pvc_getname(struct socket *sock,struct sockaddr *sockaddr,
++    int *sockaddr_len,int peer)
++{
++	struct sockaddr_atmpvc *addr;
++	struct atm_vcc *vcc;
++
++#if 0 /* add some sanity checks later ... @@@ */
++	if (sock->state != SS_CONNECTED) return -EINVAL;
++#endif
++	if (*sockaddr_len < sizeof(struct sockaddr_atmpvc)) return -EINVAL;
++        *sockaddr_len = sizeof(struct sockaddr_atmpvc);
++	addr = (struct sockaddr_atmpvc *) sockaddr;
++	vcc = ATM_SD(sock);
++	addr->sap_family = AF_ATMPVC;
++	addr->sap_addr.itf = vcc->dev->number;
++	addr->sap_addr.vpi = vcc->vpi;
++	addr->sap_addr.vci = vcc->vci;
++	return 0;
++}
++
++
++static int pvc_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
++{
++	/* put PVC-specific code here */
++	return atm_ioctl(sock,cmd,arg);
++}
++
++
++static int pvc_setsockopt(struct socket *sock,int level,int optname,
++    char *optval,int optlen)
++{
++	/* put PVC-specific code here */
++	return atm_setsockopt(sock,level,optname,optval,optlen);
++}
++
++
++static int pvc_getsockopt(struct socket *sock,int level,int optname,
++    char *optval,int *optlen)
++{
++	/* put PVC-specific code here */
++	return atm_getsockopt(sock,level,optname,optval,optlen);
++}
++
++
++static struct proto_ops pvc_proto_ops = {
++	PF_ATMPVC,
++	atm_create,
++	pvc_dup,
++	atm_release,
++	pvc_bind,
++	pvc_connect,
++	NULL,			/* no socketpair */
++	pvc_accept,
++	pvc_getname,
++	atm_select,
++	pvc_ioctl,
++	pvc_listen,
++	pvc_shutdown,
++	pvc_setsockopt,
++	pvc_getsockopt,
++	NULL,			/* no fcntl */
++	atm_sendmsg,
++	atm_recvmsg
++};
++
++
++/*
++ *	Initialize the ATM PVC protocol family
++ */
++
++
++extern int atmdev_init(void);
++
++
++void atmpvc_proto_init(struct net_proto *pro)
++{
++	atm_static_init();
++	if (sock_register(pvc_proto_ops.family,&pvc_proto_ops) < 0) {
++		printk(KERN_ERR "ATMPVC: can't register");
++		return;
++	}
++#ifdef CONFIG_AREQUIPA
++	(void) atm_init_arequipa();
++#endif
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/static.c	Fri Jul 19 15:21:05 1996
+@@ -0,0 +1,114 @@
++/* net/atm/static.c - Staticly allocated resources */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/ctype.h>
++#include <linux/string.h>
++#include <linux/atmdev.h>
++#include <asm/segment.h> /* for get_fs_long and put_fs_long */
++
++#include "static.h"
++
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++
++struct atm_dev atm_dev[MAX_ATM_ITF];
++struct atm_vcc atm_vcc[MAX_ATM_VCC];
++
++
++void atm_static_init(void)
++{
++	int i;
++
++	for (i = 0; i < MAX_ATM_ITF; i++) atm_dev[i].ops = NULL;
++	for (i = 0; i < MAX_ATM_VCC; i++) atm_vcc[i].family = 0;
++}
++
++
++struct atm_dev *alloc_atm_dev(const char *type)
++{
++	static int curr_dev = 0;
++	int last_dev;
++
++	last_dev = curr_dev;
++	do {
++		if (!atm_dev[curr_dev].ops) {
++			memset(&atm_dev[curr_dev],0,sizeof(struct atm_dev));
++			atm_dev[curr_dev].type = type;
++			return atm_dev+curr_dev;
++		}
++		if (++curr_dev == MAX_ATM_ITF) curr_dev = 0;
++	}
++	while (last_dev != curr_dev);
++	return NULL;
++}
++
++
++struct atm_vcc *alloc_atm_vcc(void)
++{
++	static int curr_vcc = 0;
++	int last_vcc;
++
++	last_vcc = curr_vcc;
++	do {
++		if (!atm_vcc[curr_vcc].family) return atm_vcc+curr_vcc;
++		if (++curr_vcc == MAX_ATM_VCC) curr_vcc = 0;
++	}
++	while (last_vcc != curr_vcc);
++	return NULL;
++}
++
++
++void free_atm_dev(struct atm_dev *dev)
++{
++	dev->ops = NULL;
++}
++
++
++void free_atm_vcc(struct atm_vcc *vcc)
++{
++	vcc->family = 0;
++}
++
++
++struct atm_dev *atm_find_dev(int number)
++{
++	int i;
++
++	for (i = 0; i < MAX_ATM_ITF; i++)
++		if (atm_dev[i].ops && atm_dev[i].number == number)
++			return &atm_dev[i];
++	return NULL;
++}
++
++
++int atm_dev_list(int *buffer,int *u_length)
++{
++	int length,left,i;
++
++	length = get_fs_long(u_length);
++	left = length/sizeof(int);
++	if (!left) return -EINVAL;
++	for (i = 0; i < MAX_ATM_ITF; i++)
++		if (atm_dev[i].ops) {
++			if (!left) break;
++			put_fs_long(atm_dev[i].number,buffer);
++			buffer++;
++			left--;
++		}
++	put_fs_long(length-(length/sizeof(int)-left)*sizeof(int),u_length);
++	return length/sizeof(int)-left;
++}
++
++
++void for_all_vccs(void (*fn)(struct atm_vcc *vcc))
++{
++	int i;
++
++	for (i = 0; i < MAX_ATM_VCC; i++)
++		if (atm_vcc[i].family) fn(atm_vcc+i);
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/static.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,29 @@
++/* net/atm/static.h - Staticly allocated resources */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef NET_ATM_STATIC_H
++#define NET_ATM_STATIC_H
++
++#include <linux/atmdev.h>
++
++
++/*
++ * This is evil ...
++ */
++
++extern struct atm_dev atm_dev[MAX_ATM_ITF];
++extern struct atm_vcc atm_vcc[MAX_ATM_VCC];
++
++
++void atm_static_init(void);
++struct atm_dev *alloc_atm_dev(const char *type);
++struct atm_vcc *alloc_atm_vcc(void);
++void free_atm_dev(struct atm_dev *dev);
++void free_atm_vcc(struct atm_vcc *vcc);
++struct atm_dev *atm_find_dev(int number);
++int atm_dev_list(int *buffer,int *u_length);
++void for_all_vccs(void (*fn)(struct atm_vcc *vcc));
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/svc.c	Mon Jul 29 10:44:01 1996
+@@ -0,0 +1,617 @@
++/* net/atm/svc.c - ATM SVC sockets */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/string.h>
++#include <linux/net.h>		/* struct socket, struct net_proto,
++				   struct proto_ops */
++#include <linux/errno.h>	/* error codes */
++#include <linux/kernel.h>	/* printk */
++#include <linux/skbuff.h>
++#include <linux/wait.h>
++#include <linux/sched.h>	/* jiffies and HZ */
++#include <linux/fcntl.h>	/* O_NONBLOCK */
++#include <linux/atm.h>		/* ATM stuff */
++#include <linux/atmsap.h>
++#include <linux/atmsvc.h>
++#include <linux/atmdev.h>
++
++#include "static.h"
++#include "common.h"		/* common for PVCs and SVCs */
++#include "signaling.h"
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++/*
++ * Note: since all this is still nicely synchronized with the signaling demon,
++ *       there's no need to protect sleep loops with clis. If signaling is
++ *       moved into the kernel, that would change.
++ */
++
++
++void svc_callback(struct atm_vcc *vcc)
++{
++	wake_up(&vcc->sleep);
++}
++
++
++static int svc_create(struct socket *sock,int protocol)
++{
++	int error;
++
++	error = atm_create(sock,protocol);
++	if (error) return error;
++	ATM_SD(sock)->callback = svc_callback;
++	return 0;
++}
++
++
++static int svc_dup(struct socket *newsock,struct socket *oldsock)
++{
++	return svc_create(newsock,ATM_SD(oldsock)->aal);
++}
++
++
++static int svc_shutdown(struct socket *sock,int how)
++{
++	return -EOPNOTSUPP;
++}
++
++
++static void svc_disconnect(struct atm_vcc *vcc)
++{
++	struct sk_buff *skb;
++
++	DPRINTK("svc_disconnect\n");
++	if (vcc->flags & ATM_VF_REGIS) {
++		sigd_enq(vcc,as_close,NULL,NULL,NULL);
++		while (!(vcc->flags & ATM_VF_RELEASED) && sigd)
++		    sleep_on(&vcc->sleep);
++	}
++	while ((skb = skb_dequeue(&vcc->listenq))) {
++		vcc->flags &= ~ATM_VF_RELEASED;
++		DPRINTK("LISTEN REL\n");
++		sigd_enq(NULL,as_close,vcc,NULL,NULL); /* reject */
++		while (!(vcc->flags & ATM_VF_RELEASED) && sigd)
++		    sleep_on(&vcc->sleep);
++		dev_kfree_skb(skb,FREE_WRITE);
++	}
++	vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_RELEASED); /* may retry later */
++}
++
++
++static int svc_release(struct socket *sock,struct socket *peer)
++{
++	DPRINTK("svc_release\n");
++	if (!ATM_SD(sock)) return 0;
++	ATM_SD(sock)->flags &= ~ATM_VF_READY;
++	atm_release_vcc(ATM_SD(sock),0);
++	svc_disconnect(ATM_SD(sock));
++	    /* VCC pointer is used as a reference, so we must not free it
++	       (thereby subjecting it to re-use) before all pending connections
++	        are closed */
++	free_atm_vcc(ATM_SD(sock));
++	return 0;
++}
++
++
++int copy_svc_addr(struct sockaddr_atmsvc *to,struct sockaddr_atmsvc *from)
++{
++	struct atm_blli *walk,**link,*next;
++	int error,bllis;
++
++	*to = *from;
++	link = &to->sas_addr.blli;
++	error = bllis = 0;
++	for (walk = from->sas_addr.blli; walk; walk = (struct atm_blli *)
++	    get_fs_long((unsigned long) &walk->next)) {
++		if (++bllis > ATM_MAX_BLLI) {
++			error = -E2BIG;
++			break;
++		}
++		*link = kmalloc(sizeof(struct atm_blli),GFP_KERNEL);
++		if (!*link) {
++			error = -ENOMEM;
++			break;
++		}
++		error = verify_area(VERIFY_READ,walk,sizeof(struct atm_blli));
++		    /* kmalloc may swap out user pages */
++		if (error) break;
++		memcpy_fromfs(*link,walk,sizeof(struct atm_blli));
++		link = &(*link)->next;
++	}
++	*link = NULL;
++	if (!error) return 0;
++	for (walk = to->sas_addr.blli; walk; walk = next) {
++		next = walk->next;
++		kfree(walk);
++	}
++	return error;
++}
++
++
++static int svc_bind(struct socket *sock,struct sockaddr *sockaddr,
++    int sockaddr_len)
++{
++	struct sockaddr_atmsvc *addr;
++	struct atm_vcc *vcc;
++	int error;
++
++	if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) return -EINVAL;
++	if (sock->state == SS_CONNECTED) return -EISCONN;
++	if (sock->state != SS_UNCONNECTED) return -EINVAL;
++	addr = (struct sockaddr_atmsvc *) sockaddr;
++	if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
++	vcc = ATM_SD(sock);
++	vcc->flags &= ~ATM_VF_BOUND; /* failing rebind will kill old binding */
++	/* @@@ check memory (de)allocation on rebind */
++	if (!(vcc->flags & ATM_VF_HASQOS)) return -EBADFD;
++	error = copy_svc_addr(&vcc->local,addr);
++	if (error) return error;
++	vcc->reply = WAITING;
++	sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local);
++	while (vcc->reply == WAITING && sigd) sleep_on(&vcc->sleep);
++	vcc->flags &= ~ATM_VF_REGIS; /* doesn't count */
++	if (!sigd) return -EUNATCH; /* good code ? @@@ */
++        if (!vcc->reply) vcc->flags |= ATM_VF_BOUND;
++	return vcc->reply;
++}
++
++
++static int svc_connect(struct socket *sock,struct sockaddr *sockaddr,
++    int sockaddr_len,int flags)
++{
++	struct sockaddr_atmsvc *addr;
++	struct atm_vcc *vcc;
++	int error;
++
++	DPRINTK("svc_connect\n");
++	if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) return -EINVAL;
++	if (sock->state == SS_CONNECTED) return -EISCONN;
++	vcc = ATM_SD(sock);
++	if (sock->state == SS_CONNECTING) {
++		if (vcc->reply == WAITING) return -EALREADY;
++		sock->state = SS_UNCONNECTED;
++		if (vcc->reply) return vcc->reply;
++	}
++	else {
++		if (sock->state != SS_UNCONNECTED) return -EINVAL;
++		addr = (struct sockaddr_atmsvc *) sockaddr;
++		if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
++		if (!(vcc->flags & ATM_VF_HASQOS)) return -EBADFD;
++		if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
++		    vcc->qos.rxtp.traffic_class == ATM_ANYCLASS)
++			return -EINVAL;
++		if (!vcc->qos.txtp.traffic_class &&
++		    !vcc->qos.rxtp.traffic_class) return -EINVAL;
++		error = copy_svc_addr(&vcc->remote,addr);
++		if (error) return error;
++		vcc->reply = WAITING;
++		sigd_enq(vcc,as_connect,NULL,NULL,&vcc->remote);
++		if (flags & O_NONBLOCK) {
++			sock->state = SS_CONNECTING;
++			return -EINPROGRESS;
++		}
++		while (vcc->reply == WAITING && sigd) {
++			interruptible_sleep_on(&vcc->sleep);
++			if (current->signal & ~current->blocked) {
++				DPRINTK("*ABORT*\n");
++				/*
++				 * This is tricky:
++				 *   Kernel -------close------> Demon
++				 *   Kernel <-----close(0)----- Demon
++			         * or
++				 *   Kernel -------close------> Demon
++				 *   Kernel <--close(error)---- Demon
++				 * or
++				 *   Kernel -------close------> Demon
++				 *   Kernel <------okay-------- Demon
++				 *   Kernel <-----close(0)----- Demon
++				 */
++				vcc->flags &= ~ATM_VF_RELEASED;
++				sigd_enq(vcc,as_close,NULL,NULL,NULL);
++				while (vcc->reply == WAITING && sigd)
++					sleep_on(&vcc->sleep);
++				if (!vcc->reply)
++					while (!(vcc->flags & ATM_VF_RELEASED)
++					    && sigd) sleep_on(&vcc->sleep);
++				vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_RELEASED);
++				    /* we're gone now but may connect later */
++				return -EINTR;
++			}
++		}
++		if (!sigd) return -EUNATCH;
++		if (vcc->reply) return vcc->reply;
++	}
++/*
++ * Not supported yet
++ *
++ * #ifndef CONFIG_SINGLE_SIGITF
++ */
++	vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp);
++	vcc->qos.txtp.min_pcr = 0;
++/*
++ * #endif
++ */
++	if (!(error = atm_connect(sock,vcc->itf,vcc->vpi,vcc->vci)))
++		sock->state = SS_CONNECTED;
++	else (void) svc_disconnect(ATM_SD(sock));
++	return error;
++}
++
++
++static int svc_listen(struct socket *sock,int backlog)
++{
++	struct atm_vcc *vcc;
++
++	DPRINTK("svc_listen\n");
++	/* let server handle listen on unbound sockets */
++	vcc = ATM_SD(sock);
++	vcc->reply = WAITING;
++	sigd_enq(vcc,as_listen,NULL,NULL,&vcc->local);
++	while (vcc->reply == WAITING && sigd) sleep_on(&vcc->sleep);
++	if (!sigd) return -EUNATCH;
++	vcc->flags |= ATM_VF_LISTEN;
++	vcc->backlog_quota = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT;
++	return vcc->reply;
++}
++
++
++static int svc_accept(struct socket *sock,struct socket *newsock,int flags)
++{
++	struct sk_buff *skb;
++	struct atmsvc_msg *msg;
++	struct atm_vcc *old_vcc,*new_vcc;
++	int error;
++
++	DPRINTK("svc_accept\n");
++	old_vcc = ATM_SD(sock);
++	new_vcc = ATM_SD(newsock);
++	while (1) {
++		while (!(skb = skb_dequeue(&old_vcc->listenq)) && sigd) {
++			if (flags & O_NONBLOCK) return 0; /* not -EAGAIN ? */
++			interruptible_sleep_on(&old_vcc->sleep);
++			if (current->signal & ~current->blocked)
++				return -ERESTARTSYS;
++		}
++		/* should try to atm_connect now and possibly send a close on
++		   error */
++		/* wait should be short, so we ignore the non-blocking flag */
++		new_vcc->reply = WAITING;
++		sigd_enq(new_vcc,as_accept,old_vcc,NULL,NULL);
++		while (new_vcc->reply == WAITING && sigd)
++			sleep_on(&new_vcc->sleep);
++		if (!sigd) return -EUNATCH;
++		old_vcc->backlog_quota++;
++		if (!new_vcc->reply) break;
++		dev_kfree_skb(skb,FREE_WRITE);
++	}
++	if (!sigd) return -EUNATCH;
++	msg = (struct atmsvc_msg *) skb->data;
++	new_vcc->qos = msg->qos;
++	new_vcc->remote = msg->svc;
++	/* copy BLLI @@@ */
++	new_vcc->remote.sas_addr.blli = NULL;
++	error = atm_connect(newsock,msg->pvc.sap_addr.itf,msg->pvc.sap_addr.vpi,
++	    msg->pvc.sap_addr.vci);
++	dev_kfree_skb(skb,FREE_WRITE);
++	if (error) {
++		(void) svc_disconnect(ATM_SD(newsock));
++		/* @@@ or should we try additional connections just until
++		   we'd block ? (then we MUST return) */
++		return error == -EAGAIN ? -EBUSY : error;
++	}
++	newsock->state = SS_CONNECTED;
++	return 0;
++}
++
++
++static int svc_getname(struct socket *sock,struct sockaddr *sockaddr,
++    int *sockaddr_len,int peer)
++{
++	struct sockaddr_atmsvc *addr;
++
++	/* this will be fun ... we have: public and private
++	   address, bhli and possibly a lot of bllis. Now address buffers
++	   are static ... argl */
++	/* The solution: use pointers to link bllis */
++	/* naturally, we still need some place to put all those nasty
++	   little bllis ... */
++#if 0
++	/* sigh ... */
++	if (*sockaddr_len < sizeof(struct sockaddr_atmsvc)) return -EINVAL;
++#endif
++	*sockaddr_len = sizeof(struct sockaddr_atmsvc);
++	addr = (struct sockaddr_atmsvc *) sockaddr;
++	memcpy(addr,peer ? &ATM_SD(sock)->remote : &ATM_SD(sock)->local,
++	    sizeof(struct sockaddr_atmsvc));
++	addr->sas_addr.blli = NULL; /* @@@ no - copy it */
++	return 0;
++}
++
++
++static int check_addr(struct sockaddr_atmsvc *addr)
++{
++	int i;
++
++	if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
++	if (!*addr->sas_addr.prv)
++		if (!*addr->sas_addr.pub) return -EINVAL;
++		else return 0;
++	for (i = 1; i < ATM_E164_LEN+1; i++)
++		if (!addr->sas_addr.prv[i]) return 0;
++	return -EINVAL;
++}
++
++
++static int identical(struct sockaddr_atmsvc *a,struct sockaddr_atmsvc *b)
++{
++	if (*a->sas_addr.prv)
++		if (memcmp(a->sas_addr.prv,b->sas_addr.prv,ATM_ESA_LEN))
++			return 0;
++	if (!*a->sas_addr.pub) return !*b->sas_addr.pub;
++	if (!*b->sas_addr.pub) return 0;
++	return !strcmp(a->sas_addr.pub,b->sas_addr.pub);
++}
++
++
++/*
++ * Avoid modification of any list of local interfaces while reading it
++ * (which may involve page faults and therefore rescheduling)
++ */
++
++
++static volatile int local_lock = 0;
++static struct wait_queue *local_wait = NULL;
++
++
++static void lock_local(void)
++{
++	while (local_lock) sleep_on(&local_wait);
++	local_lock = 1;
++}
++
++
++static void unlock_local(void)
++{
++	local_lock = 0;
++	wake_up(&local_wait);
++}
++
++
++static void notify_sigd(struct atm_dev *dev)
++{
++	struct sockaddr_atmpvc pvc;
++
++	pvc.sap_addr.itf = dev->number;
++	sigd_enq(NULL,as_itf_notify,NULL,&pvc,NULL);
++}
++
++
++static void reset_addr(struct atm_dev *dev)
++{
++	struct atm_dev_addr *this;
++
++	lock_local();
++	while (dev->local) {
++		this = dev->local;
++		dev->local = this->next;
++		kfree(this);
++	}
++	unlock_local();
++	notify_sigd(dev);
++}
++
++
++static int add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
++{
++	struct atm_dev_addr **walk;
++
++	lock_local();
++	for (walk = &dev->local; *walk; walk = &(*walk)->next)
++		if (identical(&(*walk)->addr,addr)) {
++			unlock_local();
++			return -EEXIST;
++		}
++	*walk = kmalloc(sizeof(struct atm_dev_addr),GFP_KERNEL);
++	if (!*walk) {
++		unlock_local();
++		return -ENOMEM;
++	}
++	(*walk)->addr = *addr;
++	(*walk)->next = NULL;
++	unlock_local();
++	notify_sigd(dev);
++	return 0;
++}
++
++
++static int del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
++{
++	struct atm_dev_addr **walk,*this;
++
++	lock_local();
++	for (walk = &dev->local; *walk; walk = &(*walk)->next)
++		if (identical(&(*walk)->addr,addr)) break;
++	if (!*walk) {
++		unlock_local();
++		return -ENOENT;
++	}
++	this = *walk;
++	*walk = this->next;
++	kfree(this);
++	unlock_local();
++	notify_sigd(dev);
++	return 0;
++}
++
++
++static int get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *buf,int size)
++{
++	struct atm_dev_addr *walk;
++	int total;
++
++	lock_local();
++	total = 0;
++	for (walk = dev->local; walk; walk = walk->next) {
++		total += sizeof(struct sockaddr_atmsvc);
++		if (total > size) {
++			unlock_local();
++			return -E2BIG;
++		}
++		memcpy_tofs(buf,&walk->addr,sizeof(struct sockaddr_atmsvc));
++		buf++;
++	}
++	unlock_local();
++	return total;
++}
++
++
++static int svc_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg)
++{
++	struct atm_dev *dev;
++	void *buf;
++	int rsize,wsize,len;
++	int error;
++
++	rsize = wsize = 0;
++	switch (cmd) {
++		case SIOCGIFATMADDR:
++			wsize = sizeof(struct sockaddr_atmsvc);
++			break;
++		case SIOCSIFATMADDR:
++		case ATM_ADDADDR:
++		case ATM_DELADDR:
++			rsize = sizeof(struct sockaddr_atmsvc);
++		case ATM_RSTADDR:
++			if (!suser()) return -EPERM;
++			break;
++		case ATM_GETADDR:
++			wsize = -1;
++			break;
++		default:
++			return atm_ioctl(sock,cmd,arg);
++	}
++	error = verify_area(VERIFY_READ,(void *) arg,
++	    sizeof(struct atmif_sioc));
++	if (error) return error;
++	if (wsize) {
++		error = verify_area(VERIFY_WRITE,(void *) arg,
++		    sizeof(struct atmif_sioc));
++		if (error) return error;
++	}
++	if (!(dev = atm_find_dev(get_fs_long(&((struct atmif_sioc *) arg)->
++	    number)))) return -ENODEV;
++	len = get_fs_long(&((struct atmif_sioc *) arg)->length);
++	buf = (void *) get_fs_long(&((struct atmif_sioc *) arg)->arg);
++	if (cmd == SIOCSIFATMADDR && !buf) rsize = 0; /* @@@ */
++	if (!buf && (rsize || wsize)) return -EINVAL;
++	if (rsize > 0) {
++		if (len != rsize) return -EINVAL;
++		error = verify_area(VERIFY_READ,buf,len);
++		if (error) return error;
++	}
++	if (wsize > 0) {
++		if (len != wsize) return -EINVAL;
++		error = verify_area(VERIFY_WRITE,buf,len);
++		if (error) return error;
++		put_fs_long(wsize,&((struct atmif_sioc *) arg)->length);
++	}
++	switch (cmd) {
++		case SIOCGIFATMADDR:
++			{
++				struct sockaddr_atmsvc addr;
++
++				memset(&addr,0,sizeof(addr));
++				memcpy_tofs((struct sockaddr_atmsvc *) buf,
++				    dev->local ? &dev->local->addr : &addr,
++				    sizeof(struct sockaddr_atmsvc));
++				return wsize;
++			}
++		case ATM_RSTADDR:
++			reset_addr(dev);
++			return 0;
++		case SIOCSIFATMADDR: /* OBSOLETE - REMOVE WHEN ILMID UPDATED */
++			reset_addr(dev);
++			if (!buf) return 0;
++			cmd = ATM_ADDADDR;
++			/* fall through */
++		case ATM_ADDADDR:
++		case ATM_DELADDR:
++			{
++				struct sockaddr_atmsvc addr;
++
++				memcpy_fromfs(&addr,buf,sizeof(addr));
++				error = check_addr(&addr);
++				if (error) return error;
++				if (cmd == ATM_ADDADDR)
++					return add_addr(dev,&addr);
++				else return del_addr(dev,&addr);
++			}
++		case ATM_GETADDR:
++			error = get_addr(dev,buf,len);
++			if (error < 0) return error;
++			put_fs_long(error,
++				&((struct atmif_sioc *) arg)->length);
++			return error;
++	}
++	return -EINVAL; /* actually, we're in trouble if we end up here ... */
++}
++
++
++static int svc_setsockopt(struct socket *sock,int level,int optname,
++    char *optval,int optlen)
++{
++	/* stuff for SVCs */
++	return atm_setsockopt(sock,level,optname,optval,optlen);
++}
++
++
++static int svc_getsockopt(struct socket *sock,int level,int optname,
++    char *optval,int *optlen)
++{
++	/* stuff for SVCs */
++	return atm_getsockopt(sock,level,optname,optval,optlen);
++}
++
++
++static struct proto_ops svc_proto_ops = {
++	PF_ATMSVC,
++	svc_create,
++	svc_dup,
++	svc_release,
++	svc_bind,
++	svc_connect,
++	NULL,			/* no socketpair */
++	svc_accept,
++	svc_getname,
++	atm_select,
++	svc_ioctl,
++	svc_listen,
++	svc_shutdown,
++	svc_setsockopt,
++	svc_getsockopt,
++	NULL,			/* no fcntl */
++	atm_sendmsg,
++	atm_recvmsg
++};
++
++
++/*
++ *	Initialize the ATM SVC protocol family
++ */
++
++void atmsvc_proto_init(struct net_proto *pro)
++{
++	if (sock_register(svc_proto_ops.family,&svc_proto_ops) < 0) {
++		printk(KERN_ERR "ATMSVC: can't register");
++		return;
++	}
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/signaling.c	Wed Jul 31 19:29:23 1996
+@@ -0,0 +1,218 @@
++/* net/atm/signaling.c - ATM signaling */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/errno.h>	/* error codes */
++#include <linux/kernel.h>	/* printk, suser */
++#include <linux/skbuff.h>
++#include <linux/wait.h>
++#include <linux/sched.h>	/* jiffies and HZ */
++#include <linux/atm.h>		/* ATM stuff */
++#include <linux/atmsap.h>
++#include <linux/atmsvc.h>
++#include <linux/atmdev.h>
++
++#include "tunable.h"
++#include "static.h"
++#include "signaling.h"
++
++
++#undef WAIT_FOR_DEMON		/* #define this if system calls on SVC sockets
++				   should block until the demon runs.
++				   Danger: may cause nasty hangs if the demon
++				   crashes. */
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++struct atm_vcc *sigd = NULL;
++static struct wait_queue *sigd_sleep = NULL;
++
++
++static int sigd_send(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	struct atmsvc_msg *msg;
++
++	msg = (struct atmsvc_msg *) skb->data;
++	DPRINTK("sigd_send %d (0x%lx)\n",(int) msg->type,msg->vcc);
++	vcc = (struct atm_vcc *) msg->vcc;
++	switch (msg->type) {
++		case as_okay:
++			vcc->reply = msg->reply;
++			if (!*vcc->local.sas_addr.prv &&
++			    !*vcc->local.sas_addr.pub) {
++				vcc->local.sas_family = AF_ATMSVC;
++				memcpy(vcc->local.sas_addr.prv,
++				    msg->local.sas_addr.prv,ATM_ESA_LEN);
++				memcpy(vcc->local.sas_addr.pub,
++				    msg->local.sas_addr.pub,ATM_E164_LEN+1);
++			}
++			if (vcc->vpi || vcc->vci) break;
++			vcc->itf = msg->pvc.sap_addr.itf;
++			vcc->vpi = msg->pvc.sap_addr.vpi;
++			vcc->vci = msg->pvc.sap_addr.vci;
++			if (vcc->vpi || vcc->vci) vcc->qos = msg->qos;
++			break;
++		case as_error:
++			vcc->flags &= ~(ATM_VF_REGIS | ATM_VF_READY);
++			vcc->reply = msg->reply;
++			break;
++		case as_indicate:
++			vcc = (struct atm_vcc *) msg->listen_vcc;
++			DPRINTK("as_indicate!!!\n");
++			if (!vcc->backlog_quota) {
++				sigd_enq(0,as_close,(struct atm_vcc *)
++				    msg->listen_vcc,NULL,NULL);
++				return 0;
++			}
++			vcc->backlog_quota--;
++			skb_queue_tail(&vcc->listenq,skb);
++			if (vcc->callback) {
++				DPRINTK("waking vcc->sleep 0x%lx\n",
++				    (unsigned long) &vcc->sleep);
++				vcc->callback(vcc);
++			}
++			return 0;
++		case as_close:
++			vcc->flags |= ATM_VF_RELEASED;
++			vcc->flags &= ~ATM_VF_READY;
++			vcc->reply = msg->reply;
++			break;
++		default:
++			printk(KERN_ALERT "sigd_send: bad message type %d\n",
++			    (int) msg->type);
++			return -EINVAL;
++	}
++	if (vcc->callback) vcc->callback(vcc);
++	dev_kfree_skb(skb,FREE_WRITE);
++	return 0;
++}
++
++
++void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
++    const struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
++    const struct sockaddr_atmsvc *svc)
++{
++	struct sk_buff *skb;
++	struct atmsvc_msg *msg;
++	struct atm_blli *walk;
++	int size,i;
++#ifdef WAIT_FOR_DEMON
++	static unsigned long silence = 0;
++#endif
++
++	DPRINTK("sigd_enq %d (0x%lx)\n",(int) type,(unsigned long) vcc);
++	size = sizeof(struct atmsvc_msg)-sizeof(struct atm_blli);
++	if (svc)
++		for (walk = svc->sas_addr.blli; walk; walk = walk->next)
++			size += sizeof(struct atm_blli);
++	while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule();
++	skb->free = 1;
++	skb->len = size;
++	msg = (struct atmsvc_msg *) skb->data;
++	msg->type = type;
++	msg->vcc = (unsigned long) vcc;
++	msg->listen_vcc = (unsigned long) listen_vcc;
++	msg->aal = vcc ? vcc->aal : 0;
++	msg->qos = vcc->qos;
++	if (!svc) msg->svc.sas_family = 0;
++	else {
++		msg->svc = *svc;
++		i = 0;
++		for (walk = svc->sas_addr.blli; walk; walk = walk->next)
++			msg->blli[i++] = *walk;
++	}
++	if (vcc) msg->local = vcc->local;
++	if (!pvc) memset(&msg->pvc,0,sizeof(msg->pvc));
++	else msg->pvc = *pvc;
++	while (!sigd) {
++#ifdef WAIT_FOR_DEMON
++/*
++ * Don't ever enable this as Arequipa depends on being able to send a
++ * message to the signaling demon from an interrupt.
++ */
++		if (silence < jiffies) {
++			printk(KERN_INFO "atmsvc: waiting for signaling demon "
++			    "...\n");
++			silence = jiffies+30*HZ;
++		}
++		sleep_on(&sigd_sleep);
++#else
++		printk(KERN_WARNING "atmsvc: no signaling demon\n");
++		kfree_skb(skb,FREE_READ);
++		return;
++#endif
++	}
++	atomic_add(skb->truesize+ATM_PDU_OVHD,&sigd->rx_inuse);
++	skb_queue_tail(&sigd->recvq,skb);
++	wake_up(&sigd->sleep);
++	if (vcc) vcc->flags |= ATM_VF_REGIS;
++}
++
++
++static void trigger(struct atm_vcc *vcc)
++{
++	if (vcc->family == PF_ATMSVC && !(vcc->flags & ATM_VF_META)) {
++		vcc->flags |= ATM_VF_RELEASED;
++		vcc->reply = -EUNATCH;
++		wake_up(&vcc->sleep);
++	}
++}
++
++
++static void sigd_close(struct atm_vcc *vcc)
++{
++	struct sk_buff *skb;
++
++	DPRINTK("sigd_close\n");
++	sigd = NULL;
++	if (skb_peek(&vcc->recvq))
++		printk(KERN_ERR "sigd_close: closing with requests pending\n");
++	while ((skb = skb_dequeue(&vcc->recvq))) kfree_skb(skb,FREE_READ);
++	for_all_vccs(trigger);
++}
++
++
++static struct atmdev_ops sigd_dev_ops = {
++	NULL,		/* no open */
++	sigd_close,	/* close */
++	NULL,		/* no ioctl */
++	NULL,		/* no getsockopt */
++	NULL,		/* no setsockopt */
++	sigd_send,	/* send */
++	NULL,		/* no sg_send */
++	NULL,		/* no poll */
++	NULL,		/* no phy_put */
++	NULL,		/* no phy_get */
++	NULL		/* no feedback */
++};
++
++
++static struct atm_dev sigd_dev = {
++	&sigd_dev_ops,
++	NULL,		/* no PHY */
++    	"sig",		/* type */
++	999,		/* dummy device number */
++	NULL,NULL,	/* pretend not to have any VCCs */
++	NULL,NULL,	/* no data */
++	0,		/* no flags */
++	NULL,		/* no local address */
++	{ 0 }		/* no ESI, no statistics */
++};
++
++
++int sigd_attach(struct atm_vcc *vcc)
++{
++	if (sigd) return -EADDRINUSE;
++	DPRINTK("sigd_attach\n");
++	sigd = vcc;
++	vcc->dev = &sigd_dev;
++	vcc->flags |= ATM_VF_READY | ATM_VF_META;
++	wake_up(&sigd_sleep);
++	return 0;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/signaling.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,25 @@
++/* net/atm/signaling.h - ATM signaling */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef NET_ATM_SIGNALING_H
++#define NET_ATM_SIGNALING_H
++
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/atmsvc.h>
++
++
++#define WAITING 1 /* for reply: 0: no error, < 0: error, ... */
++
++
++extern struct atm_vcc *sigd; /* needed in svc_release */
++
++
++void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
++    const struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
++    const struct sockaddr_atmsvc *svc);
++int sigd_attach(struct atm_vcc *vcc);
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/atmarp.c	Wed Jul 31 15:25:49 1996
+@@ -0,0 +1,607 @@
++/* atmarp.c - RFC1577 ATM ARP */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#include <linux/string.h>
++#include <linux/errno.h>
++#include <linux/kernel.h> /* for UINT_MAX */
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/wait.h>
++#include <linux/timer.h>
++#include <linux/if_arp.h> /* for some manifest constants */
++#include <linux/notifier.h>
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/atmclip.h>
++#include <linux/atmarp.h>
++#include <linux/ip.h> /* for net/route.h */
++#include <linux/in.h> /* for struct sockaddr_in */
++#include <net/route.h> /* for struct rtable and ip_rt_route */
++#include <asm/param.h> /* for HZ */
++#include <asm/byteorder.h> /* for htons etc. */
++
++
++/*
++ * WARNING: This code will eventually become full ATMARP support as defined in
++ *	    RFC1577 (actually, the plan is to move on quickly to RFC1577+), but
++ *	    right now it's certainly full of bugs and severe design errors.
++ *	    Don't use it as a reference for anything.
++ */
++
++
++#include "common.h"
++#include "tunable.h"
++#include "protocols.h" /* for atm_push_raw */
++#include "ipcommon.h"
++#include "atmarp.h"
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++/*
++ * Relation between address/subddress and private/public address:
++ *
++ * Caller 	Called			Address		Subaddress
++ * -----------  ----------------------- --------------	----------
++ * private	same private net	private		-
++ * public-only	private via public	public (E.164)	private
++ * private net	public UNI		public (NSAP)	-
++ *
++ * See also: ATM Forum UNI 3.0 (or 3.1), Annex A
++ */
++
++
++struct device *clip_devs = NULL;
++struct atm_vcc *atmarpd = NULL;
++static struct wait_queue *atmarpd_sleep = NULL;
++static struct timer_list idle_timer = { NULL, NULL, 0L, 0L, NULL };
++static int start_timer = 1;
++
++
++#define WAITING 1 /* see also signaling.h */
++
++
++static int atmarpd_send(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	struct atmarp_ctrl *ctrl;
++ 
++        ctrl = (struct atmarp_ctrl *) skb->data;
++	if (ctrl->magic != ATMARP_CTRL_MAGIC) {
++		printk(KERN_ALERT "atmarpd_send: bad magic 0x%x\n",
++		    ctrl->magic);
++		return -EPROTO;
++	}
++	if (ctrl->type != act_complete) {
++		printk(KERN_ALERT "atmarpd_send: bad type 0x%x\n",ctrl->type);
++		return -EPROTO;
++	}
++	if (!ctrl->reply) {
++		printk(KERN_ALERT "atmarpd_send: no reply\n");
++		return -EPROTO;
++	}
++	*ctrl->reply = ctrl->arg;
++	wake_up(&atmarpd_sleep);
++	dev_kfree_skb(skb,FREE_WRITE);
++	return 0;
++}
++
++
++static int send_demon(enum atmarp_ctrl_type type,int itf,unsigned long arg,
++    void *data,int length)
++{
++	struct atmarp_ctrl *ctrl;
++	struct sk_buff *skb;
++	int size,need_reply;
++	volatile int reply;
++
++	DPRINTK("send_demon(%d)\n",type);
++	if (!atmarpd) return -EUNATCH;
++	size = sizeof(struct atmarp_ctrl)+(data ? length : 0);
++	skb = alloc_skb(size,GFP_ATOMIC);
++	if (!skb) return -ENOMEM;
++	skb->free = 1;
++	skb->len = size;
++	need_reply = type == act_ioctl || type == act_create;
++	ctrl = (struct atmarp_ctrl *) skb->data;
++	ctrl->magic = ATMARP_CTRL_MAGIC;
++	ctrl->type = type;
++	ctrl->reply = need_reply ? &reply : NULL;
++	ctrl->itf_num = itf;
++	ctrl->arg = arg;
++	if (data) memcpy(ctrl->data,data,length);
++	reply = WAITING;
++	atomic_add(skb->truesize+ATM_PDU_OVHD,&atmarpd->rx_inuse);
++	skb_queue_tail(&atmarpd->recvq,skb);
++	wake_up(&atmarpd->sleep);
++	if (!need_reply) return 0;
++	while (reply == WAITING && atmarpd) sleep_on(&atmarpd_sleep);
++	return atmarpd ? reply : -EUNATCH;
++}
++
++
++static void remove(struct atmarp_entry *entry) /* @@@ ugly ! */
++{
++	struct atmarp_entry **walk;
++
++	for (walk = &PRIV(entry->dev)->table; *walk; walk = &(*walk)->next)
++		if (*walk == entry) {
++			*walk = entry->next;
++			return;
++		}
++	printk(KERN_CRIT "ATMARP: remove failed (0x%08lx)\n",
++	    (unsigned long) entry);
++}
++
++
++static inline void time_out_entry(struct atmarp_entry **entry)
++{
++	struct atmarp_entry *next;
++
++	DPRINTK("VC TIMED OUT\n");
++	if ((*entry)->vcc) {
++		(*entry)->vcc->flags |= ATM_VF_RELEASED;
++		(*entry)->vcc->flags &= ~ATM_VF_READY;
++	}
++	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;
++	}
++}
++
++
++static void idle_timer_check(unsigned long dummy)
++{
++	struct device *itf;
++	struct atmarp_entry **entry;
++	unsigned long expire;
++
++	idle_timer.expires = UINT_MAX;
++	for (itf = clip_devs; itf; itf = PRIV(itf)->next)
++		for (entry = &PRIV(itf)->table; *entry;
++		    entry = &(*entry)->next)
++			if ((*entry)->idle_timeout) {
++				expire = (*entry)->last_use+(*entry)->
++				    idle_timeout;
++				if (expire < jiffies) {
++					time_out_entry(entry);
++					if (!*entry) break;
++				}
++				else if (expire < idle_timer.expires)
++						idle_timer.expires = expire;
++			}
++	if (idle_timer.expires < jiffies+CLIP_CHECK_INTERVAL*HZ)
++		idle_timer.expires = jiffies+CLIP_CHECK_INTERVAL*HZ;
++	del_timer(&idle_timer);
++	add_timer(&idle_timer);
++}
++
++
++static void atm_push_ip(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++#if 0
++	DPRINTK("clip push\n");
++#endif
++	if (!skb) {
++		DPRINTK("removing AE\n");
++		AE(vcc)->old_push(vcc,NULL);
++		if (AE(vcc)->ip) remove(AE(vcc));
++		kfree(AE(vcc));
++		return;
++	}
++	AE(vcc)->last_use = jiffies;
++	skb->dev = AE(vcc)->dev;
++	skb->mac.raw = skb->data;
++	if (!skb->dev->hard_header_len) skb->protocol = htons(ETH_P_IP);
++	else if (skb->len < RFC1483LLC_LEN || memcmp(skb->data,llc_oui,
++		    sizeof(llc_oui))) skb->protocol = 0;
++			/* probably wrong encap ... */
++		else {
++			skb->protocol = ((unsigned short *) skb->data)[3];
++			skb_pull(skb,RFC1483LLC_LEN);
++			if (vcc && skb->protocol == htons(ETH_P_ARP)) {
++				PRIV(skb->dev)->stats.rx_packets++;
++				atm_push_raw(vcc,skb);
++				return;
++			}
++		}
++	PRIV(skb->dev)->stats.rx_packets++;
++	netif_rx(skb);
++}
++
++
++static struct atmarp_entry *new_entry(int timeout)
++{
++	struct atmarp_entry *entry;
++
++	entry = kmalloc(sizeof(struct atmarp_entry),GFP_ATOMIC);
++	if (!entry) return NULL;
++	entry->ip = 0;
++	entry->vcc = NULL;
++	entry->encap = 1;
++	entry->dev = clip_devs;
++	entry->old_push = NULL;
++	entry->last_use = jiffies;
++	entry->idle_timeout = timeout*HZ;
++	entry->queued = NULL;
++	entry->next = NULL;
++	return entry;
++}
++
++
++static void attach_entry(struct atm_vcc *vcc,struct atmarp_entry *entry)
++{
++	AE(vcc) = entry;
++	entry->old_push = vcc->push;
++	vcc->push = atm_push_ip;
++	entry->vcc = vcc;
++}
++
++
++static int clip_hard_header(struct sk_buff *skb,struct device *dev,
++    unsigned short type,void *daddr,void *saddr,unsigned len)
++{
++	void *here;
++
++	here = skb_push(skb,dev->hard_header_len);
++	memcpy(here,llc_oui,sizeof(llc_oui));
++	((unsigned short *) here)[3] = htons(type);
++	return -RFC1483LLC_LEN;
++}
++
++
++static int clip_rebuild_header(void *buff,struct device *dev,unsigned long dst,
++    struct sk_buff *skb)
++{
++#if 0
++	void *here;
++
++	here = skb->data; /*skb_push(skb,dev->hard_header_len);*/
++	memcpy(here,llc_oui,sizeof(llc_oui));
++	((unsigned short *) here)[3] = htons(ETH_P_IP);
++#endif
++	return 0;
++}
++
++
++static int clip_xmit(struct sk_buff *skb,struct device *dev)
++{
++	struct atmarp_entry *entry;
++
++#if 0
++  int i;
++	DPRINTK("new clip_xmit (0x%x)\n",skb->raddr);
++/*(int *) 0xffff0000 = 42;*/
++for (i = 0; i < skb->len; i++) printk("%02X ",skb->data[i]);
++printk("\n");
++#endif
++	for (entry = PRIV(dev)->table; entry; entry = entry->next)
++		if (entry->ip == skb->raddr) break;
++	if (!entry) {
++		DPRINTK("no entry - queuing\n");
++		send_demon(act_need,PRIV(dev)->number,skb->raddr,NULL,0);
++		entry = new_entry(ATMARP_RETRY_DELAY);
++		entry->queued = skb;
++		entry->ip = skb->raddr;
++		entry->next = PRIV(dev)->table;
++		PRIV(dev)->table = entry;
++		idle_timer_check(0);
++		return 0;
++	}
++	if (!entry->vcc || !(entry->vcc->flags & ATM_VF_READY)) {
++		DPRINTK("not found - discarding\n");
++		dev_kfree_skb(skb,FREE_WRITE);
++		return 0;
++		/* Should return -EHOSTUNREACH, but then it will retry
++		   forever, so we just discard the packet. */
++	}
++	if (!entry->encap) skb_push(skb,RFC1483LLC_LEN);
++		/* assumes that we'll never attempt to xmit the same packet
++		   twice @@@ */
++	else {
++		memcpy((void *) skb->data,llc_oui,sizeof(llc_oui));
++		((unsigned short *) skb->data)[3] = htons(ETH_P_IP);
++	}
++	DPRINTK("CX(A) %d += %d\n",entry->vcc->tx_inuse,skb->truesize);
++	atomic_add(skb->truesize,&entry->vcc->tx_inuse);
++	skb->atm.iovcnt = 0;
++	AE(entry->vcc)->last_use = jiffies;
++	entry->vcc->dev->ops->send(entry->vcc,skb);
++	PRIV(dev)->stats.tx_packets++;
++	return 0;
++}
++
++
++static struct enet_statistics *atm_clip_get_stats(struct device *dev)
++{
++	return &PRIV(dev)->stats;
++}
++
++
++int atmarp_mkip(struct atm_vcc *vcc,int timeout)
++{
++	struct atmarp_entry *entry;
++
++	DPRINTK("MKIP\n");
++	entry = new_entry(timeout);
++	if (!entry) return -ENOMEM;
++	attach_entry(vcc,entry);
++	idle_timer_check(0);
++	return 0;
++}
++
++
++int atmarp_setentry(struct atm_vcc *vcc,unsigned long ip)
++{
++	struct atmarp_entry **walk,**next,*succ;
++	struct rtable *route;
++	struct sk_buff *queued;
++
++	DPRINTK("SETENTRY 0x%lx\n",ip);
++	if (vcc->push != atm_push_ip) {
++		printk(KERN_WARNING "atmarp_setentry: VCC has no ARP entry\n");
++		return -EBADF;
++	}
++	if (!ip) {
++		if (!AE(vcc)->ip) {
++			printk(KERN_ERR "hiding hidden ATMARP entry\n");
++			return 0;
++		}
++		DPRINTK("setentry: remove\n");
++		remove(AE(vcc));
++		AE(vcc)->ip = 0;
++		return 0;
++	}
++	DPRINTK("setentry: route\n");
++	route = ip_rt_route(ip,1);
++	if (!route) return -EHOSTUNREACH;
++	AE(vcc)->dev = route->rt_dev;
++	if (AE(vcc)->ip) {
++		DPRINTK("setentry: update\n");
++		DPRINTK("(updating)\n");
++		AE(vcc)->ip = ip;
++		return 0;
++	}
++	DPRINTK("setentry: add\n");
++	queued = NULL;
++	for (walk = &PRIV(AE(vcc)->dev)->table; *walk; walk = next) {
++		next = &(*walk)->next;
++		if ((*walk)->ip == ip) {
++			if ((*walk)->vcc) continue;
++				/* more than one VC to dest */
++			if ((*walk)->queued) {
++				DPRINTK("setentry: flushing\n");
++				if (queued)
++					printk(KERN_CRIT "atmarp: bad news - "
++					    "more than one skb queued\n");
++				queued = (*walk)->queued;
++			}
++			succ = (*walk)->next;
++			kfree(*walk);
++			*walk = succ;
++			next = walk;
++			continue;
++		}
++	}
++	DPRINTK("(adding)\n");
++	AE(vcc)->ip = ip;
++	AE(vcc)->next =  PRIV(AE(vcc)->dev)->table;
++	PRIV(AE(vcc)->dev)->table = AE(vcc);
++	if (queued) clip_xmit(queued,route->rt_dev);
++	return 0;
++}
++
++
++int atmarp_encap(struct atm_vcc *vcc,int mode)
++{
++	AE(vcc)->encap = mode;
++	return 0;
++}
++
++
++static int atmarp_ioctl(struct device *dev,unsigned int cmd,void *arg)
++{
++	struct atmarpreq req;
++	__u32 *ip;
++	int error;
++
++	DPRINTK("atmarp_ioctl\n");
++	error = verify_area(VERIFY_READ,arg,sizeof(struct atmarpreq));
++	if (error) return error;
++	memcpy_fromfs(&req,arg,sizeof(struct atmarpreq));
++	if (req.arp_pa.sa_family != AF_INET) return -EPFNOSUPPORT;
++	ip = &((struct sockaddr_in *) &req.arp_pa)->sin_addr.s_addr;
++	if (!(*ip & ~dev->pa_mask) && !(req.arp_flags & ATF_ARPSRV))
++		return -EINVAL;
++	switch (cmd) {
++		case SIOCSARP:
++		case SIOCDARP:
++		case SIOCGARP:
++			return send_demon(act_ioctl,PRIV(dev)->number,cmd,&req,
++			    sizeof(struct atmarpreq));
++			/* @@@ get will need special treatment */
++		default:
++			return -EINVAL;
++	}
++	return 0;
++}
++
++
++static int clip_open(struct device *dev)
++{
++	DPRINTK("clip_open called\n");
++	return 0;
++}
++
++
++static int clip_stop(struct device *dev)
++{
++	DPRINTK("clip_stop\n");
++	/* @@@ just kill it on error ? */
++	return 0;
++}
++
++
++static int clip_init(struct device *dev)
++{
++	DPRINTK("init %s\n",dev->name);
++	dev->hard_start_xmit = clip_xmit;
++	/* sg_xmit ... */
++	dev->open = clip_open;
++	dev->stop = clip_stop;
++	ether_setup(dev);
++	dev->tbusy = 0; /* @@@ check */
++	dev->hard_header = clip_hard_header;
++	dev->do_ioctl = NULL;
++	dev->ip_arp = atmarp_ioctl;
++	dev->rebuild_header = clip_rebuild_header;
++	dev->get_stats = atm_clip_get_stats;
++	dev->hard_header_len = RFC1483LLC_LEN;
++	dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
++	dev->flags |= IFF_NOARP; /* we do our own ARP ... */
++	dev->mtu = RFC1626_MTU;
++	return send_demon(act_create,PRIV(dev)->number,0,NULL,0);
++}
++
++
++int clip_create(int number) /* remove by downing */
++{
++	struct device *dev,*walk;
++
++	if (number != -1) {
++		for (walk = clip_devs; walk; walk = PRIV(walk)->next)
++			if (PRIV(walk)->number == number) return -EEXIST;
++	}
++	else {
++		number = 0;
++		for (walk = clip_devs; walk; walk = PRIV(walk)->next)
++			if (PRIV(walk)->number >= number)
++				number = PRIV(walk)->number+1;
++	}
++	dev = kmalloc(sizeof(struct device)+sizeof(struct atmarp_priv),
++	    GFP_KERNEL);
++	if (!dev) return -ENOMEM;
++	memset(dev,0,sizeof(struct device)+sizeof(struct atmarp_priv));
++	dev->name = PRIV(dev)->name;
++	sprintf(dev->name,"atm%d",number);
++	dev->init = clip_init;
++	if (register_netdev(dev)) return -EIO; /* free dev ? */
++	PRIV(dev)->number = number;
++	PRIV(dev)->table = NULL;
++	PRIV(dev)->next = clip_devs;
++	clip_devs = dev;
++	DPRINTK("registered (net:%s)\n",dev->name);
++	return number;
++}
++
++
++static int clip_device_event(struct notifier_block *this,unsigned long event,
++    void *dev)
++{
++	/* ignore non-CLIP devices */
++	if (((struct device *) dev)->init != clip_init) return NOTIFY_DONE;
++	switch (event) {
++		case NETDEV_UP:
++			(void) send_demon(act_up,PRIV(dev)->number,0,NULL,0);
++			break;
++		case NETDEV_DOWN:
++			DPRINTK("clip_device_event NETDEV_DOWN\n");
++			(void) send_demon(act_down,PRIV(dev)->number,0,NULL,0);
++			break;
++		case NETDEV_REBOOT:
++			/* ignore */
++			break;
++		default:
++			printk(KERN_ERR "clip_device_event: unknown event "
++			    "%ld\n",event);
++			break;
++	}
++	return NOTIFY_DONE;
++}
++
++
++static struct notifier_block clip_dev_notifier = {
++	clip_device_event,
++	NULL,
++	0
++};
++
++
++static void atmarpd_close(struct atm_vcc *vcc)
++{
++        struct sk_buff *skb;
++
++        DPRINTK("atmarpd_close\n");
++        atmarpd = NULL; /* assumed to be atomic */
++	barrier();
++	unregister_netdevice_notifier(&clip_dev_notifier);
++	wake_up(&atmarpd_sleep);
++        if (skb_peek(&vcc->recvq))
++                printk(KERN_ERR "atmarpd_close: closing with requests "
++		    "pending\n");
++        while ((skb = skb_dequeue(&vcc->recvq))) kfree_skb(skb,FREE_READ);
++	DPRINTK("(done)\n");
++}
++
++
++static struct atmdev_ops atmarpd_dev_ops = {
++	NULL,		/* no open */
++	atmarpd_close,	/* close */
++	NULL,		/* no ioctl */
++	NULL,		/* no getsockopt */
++	NULL,		/* no setsockopt */
++	atmarpd_send,	/* send */
++	NULL,		/* no sg_send */
++	NULL,		/* no poll */
++	NULL,		/* no phy_put */
++	NULL,		/* no phy_get */
++	NULL		/* no feedback */
++};
++
++static struct atm_dev atmarpd_dev = {
++	&atmarpd_dev_ops,
++	NULL,		/* no PHY */
++	"arpd",		/* type */
++	999,		/* dummy device number */
++	NULL,NULL,	/* pretend not to have any VCCs */
++	NULL,NULL,	/* no data */
++	0,		/* no flags */
++	NULL,		/* no local address */
++	{ 0 }		/* no ESI, no statistics */
++};
++
++
++int atm_init_atmarp(struct atm_vcc *vcc)
++{
++	if (atmarpd) return -EADDRINUSE;
++	if (start_timer) {
++		start_timer = 0;
++		idle_timer.expires = jiffies+CLIP_CHECK_INTERVAL*HZ;
++		idle_timer.function = idle_timer_check;
++		add_timer(&idle_timer);
++	}
++	atmarpd = vcc;
++	vcc->flags |= ATM_VF_READY | ATM_VF_META;
++	    /* allow replies and avoid getting closed if signaling dies */
++	vcc->dev = &atmarpd_dev;
++	vcc->aal = ATM_AAL5; /* lie */
++	vcc->push = NULL;
++	vcc->peek = NULL; /* crash */
++	vcc->pop = NULL; /* crash */
++	vcc->push_oam = NULL; /* crash */
++	register_netdevice_notifier(&clip_dev_notifier);
++	return 0;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/atmarp.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,54 @@
++/* net/atm/atmarp.h - RFC1577 ATM ARP */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++ 
++#ifndef NET_ATM_ATMARP_H
++#define NET_ATM_ATMARP_H
++
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/atmarp.h>
++
++
++#define PRIV(dev) ((struct atmarp_priv *) ((struct device *) (dev)+1))
++#define AE(vcc) ((struct atmarp_entry *) (vcc->user_back))
++
++
++struct atmarp_entry {
++	unsigned long	ip;		/* IP address, 0 if none */
++	struct atm_vcc	*vcc;		/* active VCC */
++	unsigned char encap;		/* 0: NULL, 1: LLC/SNAP */
++	struct device	*dev;		/* device back pointer */
++	void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb);
++					/* keep old push fn for detaching */
++	unsigned long last_use;		/* last send or receive operation */
++	unsigned long idle_timeout;	/* keep open idle for so many jiffies*/
++	struct sk_buff *queued;		/* queue one skb when resolving */
++	struct atmarp_entry *next;	/* ugly linked list ... */
++};
++
++/* Entry is PVC iff vcc && vcc->family == AF_ATMPVC */
++ 
++
++struct atmarp_priv {
++	struct atmarp_entry *table;	/* ARP table */
++	char name[8];			/* interface name */
++	int number;			/* for convenience ... */
++	struct enet_statistics stats;
++	struct device *next;		/* next CLIP interface */
++};
++
++
++extern struct device *clip_devs;
++extern struct atm_vcc *atmarpd; /* ugly */
++
++
++int clip_create(int number);
++int atmarp_mkip(struct atm_vcc *vcc,int timeout);
++int atmarp_setentry(struct atm_vcc *vcc,unsigned long ip);
++int atmarp_encap(struct atm_vcc *vcc,int mode);
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/ipcommon.h	Tue Jul 30 18:42:55 1996
+@@ -0,0 +1,66 @@
++/* net/atm/ipcommon.h - Common items for all ways of doing IP over ATM */
++
++/* Written 1996 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef NET_ATM_IPCOMMON_H
++#define NET_ATM_IPCOMMON_H
++
++
++#include <linux/string.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <linux/atmdev.h>
++#include <linux/atmclip.h>
++
++
++extern const unsigned char llc_oui[6];
++
++
++#define CLIP(dev) ((struct clip_priv *) ((struct device *) (dev)+1))
++
++
++struct clip_priv {
++	char name[8];
++	struct atm_vcc *vcc;
++	struct enet_statistics stats;
++};
++
++
++static inline void ipcom_push(struct sk_buff *skb)
++{
++	skb->mac.raw = skb->data;
++	if (!skb->dev->hard_header_len) skb->protocol = htons(ETH_P_IP);
++	else if (skb->len < RFC1483LLC_LEN || memcmp(skb->data,llc_oui,
++		    sizeof(llc_oui))) skb->protocol = 0;
++			/* probably wrong encap ... */
++		else {
++			skb->protocol = ((unsigned short *) skb->data)[3];
++			skb_pull(skb,RFC1483LLC_LEN);
++		}
++	CLIP(skb->dev)->stats.rx_packets++;
++	netif_rx(skb);
++}
++
++
++static inline void ipcom_xmit(struct device *dev,struct atm_vcc *vcc,
++    struct sk_buff *skb)
++{
++	if (dev->hard_header_len) {
++		memcpy(skb->data,llc_oui,sizeof(llc_oui));
++		((unsigned short *) skb->data)[3] = htons(ETH_P_IP); /* hack */
++	}
++	atomic_add(skb->truesize,&vcc->tx_inuse);
++	skb->atm.iovcnt = 0;
++	vcc->dev->ops->send(vcc,skb);
++}
++
++
++struct sk_buff *atm_peek_clip(struct atm_vcc *vcc,unsigned long pdu_size,
++    __u32 (*fetch)(struct atm_vcc *vcc,int i));
++void atm_pop_clip(struct atm_vcc *vcc,struct sk_buff *skb);
++void ipcom_init(struct device *dev,
++    int (*hard_start_xmit)(struct sk_buff *skb,struct device *dev),
++    unsigned short extra_flags);
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/ipcommon.c	Mon Jul 29 11:30:37 1996
+@@ -0,0 +1,181 @@
++/* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */
++
++/* Written 1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++#include <linux/string.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <linux/in.h>
++#include <linux/atmdev.h>
++#include <linux/atmclip.h>
++
++#include "common.h"
++#include "ipcommon.h"
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++const unsigned char llc_oui[] = {
++	0xaa,	/* DSAP: non-ISO */
++	0xaa,	/* SSAP: non-ISO */
++	0x03,	/* Ctrl: Unnumbered Information Command PDU */
++	0x00,	/* OUI: EtherType */
++	0x00,
++	0x00 };
++
++
++static int clip_hard_header(struct sk_buff *skb,struct device *dev,
++    unsigned short type,void *daddr,void *saddr,unsigned len)
++{
++	void *here;
++
++	if (!dev->hard_header_len) return 0; /* just in case ... */
++	here = skb_push(skb,dev->hard_header_len);
++	memcpy(here,llc_oui,sizeof(llc_oui));
++	((unsigned short *) here)[3] = htons(type);
++	return -RFC1483LLC_LEN;
++}
++
++
++static int clip_rebuild_header(void *buff,struct device *dev,unsigned long dst,
++    struct sk_buff *skb)
++{
++	/* no ARP with this type of IP over ATM */
++	return 0;
++}
++
++
++struct sk_buff *atm_peek_clip(struct atm_vcc *vcc,unsigned long pdu_size,
++    __u32 (*fetch)(struct atm_vcc *vcc,int i))
++{
++        struct sk_buff *skb;
++	int header_size;
++
++	/* TODO: check against buffer quota (should even check against upper
++		 layer protocol socket quota) */
++	header_size = 0;
++	if (fetch && pdu_size > 28) { /* > IP+UDP header */
++		unsigned long type;
++
++/* doesn't work yet */
++type = ntohl(fetch(vcc,2));
++/*printk("type is 0x%08lx\n",type);*/
++		type = ntohl(fetch(vcc,2)) & 0xff0000;
++		if (type == IPPROTO_UDP << 8) header_size = 28; /* bytes */
++		else if (type == IPPROTO_TCP << 8) header_size = 40; /* bytes */
++	}
++	if (!header_size) skb = alloc_skb((pdu_size+3) & ~3,GFP_ATOMIC);
++	else {
++		skb = alloc_skb(((pdu_size+3) & ~3)+PAGE_SIZE+header_size-1,
++		    GFP_ATOMIC);
++		if (skb) {
++			DPRINTK("data before: 0x%lx\n",
++			    (unsigned long) skb->data);
++		        skb->data = (unsigned char *) (((unsigned long) skb->
++			    data+header_size+PAGE_SIZE-1) & ~(PAGE_SIZE-1))-
++			    header_size;
++			DPRINTK("data after: 0x%lx\n",
++			    (unsigned long) skb->data);
++		}
++	}
++	if (!skb) {
++		CLIP(vcc->proto_data)->stats.rx_dropped++;
++		vcc->stats->rx_drop++;
++	}
++        return skb;
++}
++ 
++ 
++void atm_pop_clip(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++#ifdef CONFIG_MMU_HACKS
++	if (skb->atm.iovcnt)
++		unlock_user(skb->atm.iovcnt-1,(struct iovec *) skb->data+1);
++#endif
++/*printk("popping (r:%d,w:%d)\n",skb->sk->rmem_alloc,skb->sk->wmem_alloc);*/
++	dev_kfree_skb(skb,FREE_WRITE);
++}
++
++
++
++static struct enet_statistics *atm_clip_get_stats(struct device *dev)
++{
++	return &CLIP(dev)->stats;
++}
++
++
++/*@@@static*/ int clip_ioctl(struct device *dev,struct ifreq *rq,int cmd)
++{
++	if (!suser()) return -EPERM;
++	switch (cmd) {
++		case CLIP_NULENCAP:
++#if 0
++			dev->type = ARPHDR_ATMNULL;
++#endif
++			dev->hard_header_len = 0;
++			return 0;
++		case CLIP_LLCENCAP:
++#if 0
++			dev->type = ARPHDR_ATMLLC;
++#endif
++			dev->hard_header_len = RFC1483LLC_LEN;
++			return 0;
++		default:
++			return -EOPNOTSUPP;
++	}
++}
++
++
++static int clip_open(struct device *dev)
++{
++	DPRINTK("clip_open called\n");
++	return 0;
++}
++
++
++static int clip_stop(struct device *dev)
++{
++	DPRINTK("DOWN! (%s,0x%lx)\n",dev->name,(unsigned long) CLIP(dev)->vcc);
++	atm_release_vcc(CLIP(dev)->vcc,1);
++	return 0;
++}
++
++
++void ipcom_init(struct device *dev,
++    int (*hard_start_xmit)(struct sk_buff *skb,struct device *dev),
++    unsigned short extra_flags)
++{
++	DPRINTK("ipcom_init\n");
++	DPRINTK("configuring %s\n",dev->name);
++	dev->hard_start_xmit = hard_start_xmit;
++#if 0
++#ifdef CONFIG_MMU_HACKS
++	dev->sg_xmit = clip_sg_xmit;
++#else
++	dev->sg_xmit = NULL;
++#endif
++#endif
++	dev->open = clip_open;
++	dev->stop = clip_stop;
++	ether_setup(dev);
++	dev->tbusy = 0; /* @@@ check */
++	dev->hard_header = clip_hard_header;
++	dev->do_ioctl = clip_ioctl;
++	dev->rebuild_header = clip_rebuild_header;
++	dev->get_stats = atm_clip_get_stats;
++	dev->hard_header_len = RFC1483LLC_LEN;
++	dev->flags |= IFF_NOARP | IFF_POINTOPOINT | extra_flags;
++#if 0
++	dev->type = ARPHDR_ATMLLC;
++#endif
++	dev->mtu = RFC1626_MTU;
++	memset(&CLIP(dev)->stats,0,sizeof(struct enet_statistics));
++
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/arequipa.c	Wed Jul 31 17:06:38 1996
+@@ -0,0 +1,367 @@
++/* net/atm/arequipa.c - Application requested IP over ATM */
++ 
++/* Written 1996 by Jean-Michel Pittet and Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/in.h>
++#include <linux/mmuio.h>
++#include <linux/atmdev.h>
++#include <linux/atmclip.h>
++#include <linux/arequipa.h>
++#include <linux/route.h>
++#include <net/sock.h>
++#include <netinet/in.h>
++#include <asm/system.h> /* cli and such */
++
++#include "protocols.h"
++#include "tunable.h"
++#include "signaling.h"  /* for indirect closing, see below */
++#include "common.h"
++#include "ipcommon.h"
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++struct device *arequipa_dev = NULL;
++    /* must use a different null value if skb->dev can ever be NULL */
++struct atm_vcc *aqd = NULL;
++
++struct rtable arequipa_rt = {
++	NULL,		/* rt_next */
++	0L,		/* rt_dst */
++	0L,		/* rt_src */
++	0L,		/* rt_gateway */
++	2,		/* rt_refcnt */
++	1,		/* rt_use */
++	0,		/* rt_window */
++	0L,		/* rt_lastuse */
++	NULL,		/* rt_hh */
++	NULL,		/* rt_dev */
++	RTF_UP,		/* rt_flags */
++	RFC1626_MTU,	/* rt_mtu */
++	0,		/* rt_irtt */
++	0		/* rt_tos */
++};
++
++
++/*
++ * Closing is tricky. Since we may be in an interrupt when executing
++ * arequipa_close, we can't just go and call close_fp. So what we do it instead
++ * is to ask arequipad nicely to close the VC. arequipad issues an
++ * AREQUIPA_CLS3RD ioctl to close us (via arequipa_close_vcc). Now we're in a
++ * process context and can sleep, etc. Ain't life sweet ?
++ */
++
++
++static void aqd_enq(struct atm_vcc *vcc)
++{
++	struct sk_buff *skb;
++
++	if (!aqd) {
++		printk(KERN_CRIT "aqd_enq: no Arequipa demon\n");
++		return;
++	}
++	skb = alloc_skb(sizeof(vcc),GFP_ATOMIC);
++	if (!skb) {
++		printk(KERN_CRIT "adq_enq: out of memory\n");
++		return;
++	}
++	skb->free = 1;
++	skb->len = sizeof(vcc);
++	*(struct atm_vcc **) skb->data = vcc;
++	atomic_add(skb->truesize+ATM_PDU_OVHD,&aqd->rx_inuse);
++	skb_queue_tail(&aqd->recvq,skb);
++	wake_up(&aqd->sleep);
++}
++
++
++int arequipa_close(struct sock *upper)
++{
++	struct socket *lower;
++	unsigned long flags;
++
++	DPRINTK("arequipa_close\n");
++	if (!(lower = upper->arequipa)) return -ENOTCONN;
++	save_flags(flags);
++	cli();
++	upper->arequipa = NULL;
++	ATM_SD(lower)->upper = NULL;
++	if (!(ATM_SD(lower)->flags & ATM_VF_AQREL)) aqd_enq(ATM_SD(lower));
++	ATM_SD(lower)->flags |= ATM_VF_AQREL;
++	restore_flags(flags);
++	return 0;
++}
++
++
++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 "
++		    "have ATM_VF_AQREL set\n",vcc);
++	else close_fp(vcc->sock->file);
++}
++
++
++static void arequipa_callback(struct atm_vcc *vcc)
++{
++	unsigned long flags;
++
++	DPRINTK("arequipa_callback\n");
++	svc_callback(vcc);
++	if (!(vcc->flags & ATM_VF_RELEASED)) return;
++	vcc->callback = svc_callback; /* paranoia ... */
++	save_flags(flags);
++	cli();
++	if (vcc->upper) {
++		if (!vcc->upper->arequipa)
++			printk("arequipa_callback: upper pretends not to "
++			    "use Arequipa\n");
++		ip_rt_put(vcc->upper->ip_route_cache);
++		vcc->upper->ip_route_cache = NULL;
++		vcc->upper->arequipa = NULL;
++	}
++	if (vcc->flags & ATM_VF_AQREL) {
++		restore_flags(flags);
++		return;
++	}
++	vcc->flags |= ATM_VF_AQREL;
++	restore_flags(flags);
++	arequipa_close_vcc(vcc);
++	return;
++}
++
++
++static int check_aq_vcc(struct socket *lower)
++{
++	if (lower->ops->family != PF_ATMSVC && lower->ops->family != PF_ATMPVC)
++		return -EPROTOTYPE;
++	if (lower->state != SS_CONNECTED) return -ENOTCONN;
++	if (ATM_SD(lower)->aal != ATM_AAL5) return -EPROTONOSUPPORT;
++	return 0;
++}
++
++
++/*static*/ void atm_push_arequipa(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	if (!skb) return; /* it's okay to close Arequipa VCs */
++	/*DPRINTK("arequipa push(%ld)\n",skb->len);*/
++	skb->dev = arequipa_dev;
++	ipcom_push(skb);
++}
++
++
++static void make_aq_vcc(struct socket *lower)
++{
++	struct atm_vcc *vcc;
++	unsigned long flags;
++
++	save_flags(flags);
++	cli();
++	vcc = ATM_SD(lower);
++	vcc->pop = atm_pop_clip;
++	vcc->callback = arequipa_callback;
++	vcc->push = atm_push_arequipa;
++	vcc->peek = atm_peek_clip;
++	vcc->push_oam = NULL;
++	restore_flags(flags);
++	lower->file->f_count++;
++}
++
++
++int arequipa_attach(struct socket *lower,struct sock *upper)
++{
++	struct rtable *rt;
++	int error;
++
++	if (upper->arequipa) {
++		printk(KERN_WARNING "arequipa_attach: upper already uses "
++		    "Arequipa\n");
++		return -EISCONN;
++	}
++	error = check_aq_vcc(lower);
++	if (error) return error;
++	if (ATM_SD(lower)->upper) {
++		printk(KERN_WARNING "arequipa_attach: lower is already "
++		    "attached\n");
++		return -EISCONN;
++	}
++	DPRINTK("arequipa_attach %p (i_count=%d,f_count=%d)\n",upper,
++	  lower->inode->i_count,lower->file->f_count);
++	upper->arequipa = lower;
++	ATM_SD(lower)->upper = upper;
++	rt = upper->ip_route_cache; /* revalidate cache */
++	upper->ip_route_cache = NULL;
++	set_rt_cache(upper,rt);
++	return 0;
++}
++
++
++int arequipa_expect(struct sock *upper,int on)
++{
++	DPRINTK("arequipa_expect %d\n",on);
++	if (!aqd) {
++		printk(KERN_ERR "arequipa_expect: no Arequipa demon\n");
++		return -EUNATCH;
++	}
++	if (on) {
++		if (upper->aq_route) return 0;
++		upper->aq_route = kmalloc(sizeof(struct rtable),GFP_KERNEL);
++		return upper->aq_route ? 0 : -ENOMEM;
++	}
++	if (!upper->aq_route) return 0;
++	if (upper->arequipa) return -EBUSY;
++	kfree(upper->aq_route);
++	upper->aq_route = NULL;
++	return 0;
++}
++
++
++int arequipa_preset(struct socket *lower,struct sock *upper)
++{
++	int error;
++
++	if (!aqd) {
++		printk(KERN_ERR "arequipa_preset: no Arequipa demon\n");
++		return -EUNATCH;
++	}
++	error = arequipa_expect(upper,1);
++	if (error) return error;
++	error = arequipa_attach(lower,upper);
++	if (!error) make_aq_vcc(lower);
++	return error;
++}
++
++
++int arequipa_incoming(struct socket *lower)
++{
++	int error;
++
++	if (!suser()) return -EPERM;
++	error = check_aq_vcc(lower);
++	if (error) return error;
++	ATM_SD(lower)->upper = NULL;
++	make_aq_vcc(lower);
++	DPRINTK("aq_incoming %d\n",lower->file->f_count);
++	return 0;
++}
++
++
++static int arequipa_xmit(struct sk_buff *skb,struct device *dev)
++{
++	struct atm_vcc *vcc;
++
++	/*DPRINTK("arequipa xmit\n");*/
++	if (!skb->sk || !skb->sk->arequipa ||
++	    !ATM_SD(skb->sk->arequipa)) {
++		printk("arequipa_xmit: discarding orphaned packets\n");
++	        dev_kfree_skb(skb,FREE_WRITE);
++		return 0;
++	}
++	vcc = ATM_SD(skb->sk->arequipa);
++	if (!(vcc->flags & ATM_VF_READY)) {
++		printk("arequipa_xmit: not ready\n");
++	        dev_kfree_skb(skb,FREE_WRITE);
++		return 0;
++	}
++	ipcom_xmit(arequipa_dev,vcc,skb);
++	CLIP(arequipa_dev)->stats.tx_packets++;
++	return 0;
++}
++
++
++static int arequipa_init(struct device *dev)
++{
++	ipcom_init(dev,arequipa_xmit,IFF_UP);
++	dev->pa_addr = 0x01020304;
++	dev->pa_mask = ~0L;
++	dev->pa_alen = 4;
++	return 0;
++}
++
++
++int atm_init_arequipa(void)
++{
++	DPRINTK("atm_init_arequipa\n");
++	if (!suser()) return -EPERM;
++	arequipa_dev = kmalloc(sizeof(struct device)+sizeof(struct clip_priv),
++	    GFP_KERNEL);
++	if (!arequipa_dev) return -ENOMEM;
++	arequipa_rt.rt_dev = arequipa_dev;
++	memset(arequipa_dev,0,sizeof(struct device)+sizeof(struct clip_priv));
++	arequipa_dev->name = "arequipa";
++	arequipa_dev->init = arequipa_init;
++	arequipa_init(arequipa_dev);
++	return 0;
++}
++
++
++static void aqd_close(struct atm_vcc *vcc)
++{
++        struct sk_buff *skb;
++
++        DPRINTK("aqd_close\n");
++        aqd = NULL; /* assumed to be atomic */
++	barrier();
++        if (skb_peek(&vcc->recvq))
++                printk(KERN_CRIT "aqd_close: closing with requests "
++		    "pending\n");
++        while ((skb = skb_dequeue(&vcc->recvq))) kfree_skb(skb,FREE_READ);
++}
++
++
++static struct atmdev_ops aqd_dev_ops = {
++	NULL,		/* no open */
++	aqd_close,	/* close */
++	NULL,		/* no ioctl */
++	NULL,		/* no getsockopt */
++	NULL,		/* no setsockopt */
++	NULL,		/* no send */
++	NULL,		/* no sg_send */
++	NULL,		/* no poll */
++	NULL,		/* no phy_put */
++	NULL,		/* no phy_get */
++	NULL		/* no feedback */
++};
++
++
++static struct atm_dev aqd_dev = {
++	&aqd_dev_ops,
++	NULL,		/* no PHY */
++	"aqd",		/* type */
++	999,		/* dummy device number */
++	NULL,NULL,	/* pretend not to have any VCCs */
++	NULL,NULL,	/* no data */
++	0,		/* no flags */
++	NULL,		/* no local address */
++	{ 0 }		/* no ESI, no statistics */
++};
++
++
++int arequipad_attach(struct atm_vcc *vcc)
++{
++	if (aqd) return -EADDRINUSE;
++	aqd = vcc;
++	vcc->flags |= ATM_VF_READY | ATM_VF_META;
++	    /* allow replies and avoid getting closed if signaling dies */
++	vcc->dev = &aqd_dev;
++	vcc->aal = ATM_AAL5; /* lie */
++	vcc->push = NULL;
++	vcc->peek = NULL; /* crash */
++	vcc->pop = NULL; /* crash */
++	vcc->push_oam = NULL; /* crash */
++        return 0;
++
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/tunable.h	Tue Jul 30 22:30:54 1996
+@@ -0,0 +1,26 @@
++/* net/atm/tunable.h - Tunable parameters of ATM support */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef NET_ATM_TUNABLE_H
++#define NET_ATM_TUNABLE_H
++
++#if 0
++/* this is just a reminder - TTS is a device-specific parameter and shall be
++   used inside device drivers only */
++#define ATM_TTS		1000	/* worst-case time to service of device
++				   drivers, in microseconds */
++#endif
++
++#define ATM_RXBQ_DEF	(  64*1024)  /* default RX buffer quota, in bytes */
++#define ATM_TXBQ_DEF	(  64*1024)  /* default TX buffer quota, in bytes */
++#define ATM_RXBQ_MIN	(   1*1024)  /* RX buffer minimum, in bytes */
++#define ATM_TXBQ_MIN	(   1*1024)  /* TX buffer minimum, in bytes */
++#define ATM_RXBQ_MAX	(1024*1024)  /* RX buffer quota limit, in bytes */
++#define ATM_TXBQ_MAX	(1024*1024)  /* TX buffer quota limit, in bytes */
++
++#define ATM_PDU_OVHD	0	/* number of bytes to charge against buffer
++				   quota per PDU */
++
++#endif
+--- ref/net/ipv4/af_inet.c	Fri Jun  7 09:14:29 1996
++++ work/net/ipv4/af_inet.c	Mon Jun 10 17:51:57 1996
+@@ -105,6 +105,14 @@
+ #include <linux/kerneld.h>
+ #endif
+ 
++#ifdef CONFIG_ATM_TCP
++#include <linux/atm.h>
++int (*atmtcp_attach_hook)(struct socket *sock) = NULL;
++#endif
++#ifdef CONFIG_AREQUIPA
++#include <linux/arequipa.h>
++#endif
++
+ #define min(a,b)	((a)<(b)?(a):(b))
+ 
+ extern struct proto packet_prot;
+@@ -415,6 +423,10 @@
+ 
+ 	if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
+ 	{
++#ifdef CONFIG_AREQUIPA
++		if (sk->arequipa) arequipa_close(sk);
++		if (sk->aq_route) kfree_s(sk->aq_route,sizeof(struct rtable));
++#endif
+ 		if(sk->opt)
+ 			kfree(sk->opt);
+ 		ip_rt_put(sk->ip_route_cache);
+@@ -662,6 +674,10 @@
+ 			sk_free(sk);
+ 			return(-ESOCKTNOSUPPORT);
+ 	}
++#ifdef CONFIG_AREQUIPA
++	sk->arequipa = NULL;
++	sk->aq_route = NULL;
++#endif
+ 	sk->socket = sock;
+ #ifdef CONFIG_TCP_NAGLE_OFF
+ 	sk->nonagle = 1;
+@@ -1334,7 +1350,18 @@
+ 				return((*dlci_ioctl_hook)(cmd, (void *) arg));
+ #endif
+ 			return -ENOPKG;
+-
++#ifdef CONFIG_ATM_TCP
++		case SIOCSIFATMTCP:
++			if (atmtcp_attach_hook)
++				return atmtcp_attach_hook(sock);
++			return -EINVAL;
++#endif
++#ifdef CONFIG_AREQUIPA
++		case AREQUIPA_EXPECT:
++			return arequipa_expect(sk,arg);
++		case AREQUIPA_CLOSE:
++			return arequipa_close(sk);
++#endif
+ 		default:
+ 			if ((cmd >= SIOCDEVPRIVATE) &&
+ 			   (cmd <= (SIOCDEVPRIVATE + 15)))
+--- ref/net/protocols.c	Sat Mar 30 12:20:34 1996
++++ work/net/protocols.c	Mon Jun 10 17:52:25 1996
+@@ -42,6 +42,10 @@
+ #include <linux/trdevice.h>
+ extern void rif_init(struct net_proto *);
+ #endif
++#ifdef CONFIG_ATM
++#include <linux/atm.h>
++#endif
++
+ /*
+  *	Protocol Table
+  */
+@@ -73,6 +77,10 @@
+ #endif
+ #ifdef CONFIG_ATALK
+   { "DDP",	atalk_proto_init },			/* Netatalk Appletalk driver	*/
++#endif
++#ifdef CONFIG_ATM
++  { "ATMPVC",   atmpvc_proto_init },
++  { "ATMSVC",   atmsvc_proto_init },
+ #endif
+   { NULL,	NULL		}			/* End marker			*/
+ };
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/mmuio.c	Mon Jul 29 13:00:29 1996
+@@ -0,0 +1,455 @@
++/* net/atm/mmuio.c - MMU-supported high-speed I/O */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++
++
++#ifdef CONFIG_MMU_HACKS
++
++#include <linux/mmuio.h>
++#include <asm/atomic.h>
++
++
++#define invalidate flush_tlb_all /* @@@ improve this */
++
++
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/swap.h>
++#include <linux/pagemap.h>
++#include <linux/uio.h>
++#include <asm/page.h>
++#include <asm/pgtable.h>
++#include <asm/segment.h>
++
++#include <asm/bitops.h>
++
++#include <linux/skbuff.h>
++
++#include <linux/netdevice.h> /* needed to include net/sock.h */
++#include <net/sock.h>
++
++
++/* #define MAX_SC_LOCKS 3 */
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++#ifndef CONFIG_MMU_HACKS_DEBUG
++ 
++#define EVENT(s,a,b,c) DPRINTK(s,a,b,c)
++ 
++static void event_dump(void) {}
++
++#else
++ 
++/*
++ * Very extensive activity logging. Greatly improves bug detection speed but
++ * costs a few Mbps if enabled.
++ */
++ 
++#define EV 64
++ 
++static const char *ev[EV];
++
++static unsigned long ev_a[EV],ev_b[EV],ev_c[EV];
++static int ec = 0;
++ 
++ 
++static void EVENT(const char *s,unsigned long a,unsigned long b,unsigned long c)
++{
++	ev[ec] = s;
++	ev_a[ec] = a;
++	ev_b[ec] = b;
++	ev_c[ec] = c;
++	ec = (ec+1) % EV;
++}
++
++
++static void event_dump(void)
++{
++	int n,i;
++ 
++	printk(KERN_NOTICE "----- Event dump follows -----\n");
++	for (n = 0; n < EV; n++) {
++		i = (ec+n) % EV;
++		printk(KERN_NOTICE);
++		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i],ev_c[i]);
++	}
++	printk(KERN_NOTICE "----- Event dump ends here -----\n");
++}
++ 
++ 
++#endif /* CONFIG_MMU_HACKS_DEBUG */
++
++/*
++ * Helper functions to walk through page tables. If CREATE is set, they add
++ * new entries if needed to reach a given PTE. PTEs are never created. If
++ * CREATE is not set, *PMD and *PTE might become NULL while passing unavailable
++ * memory regions.
++ */
++
++
++static inline int mmu_resolve(unsigned long addr,pgd_t **pgd,pmd_t **pmd,
++    pte_t **pte,int create)
++{
++	*pgd = pgd_offset(current->mm,addr);
++	*pmd = create ? pmd_alloc(*pgd,addr) : pmd_offset(*pgd,addr);
++	if (!*pmd) {
++		if (create) invalidate();
++		*pte = NULL;
++		return -ENOMEM;
++	}
++	*pte = create ? pte_alloc(*pmd,addr) : pte_offset(*pmd,addr);
++	if (*pte) return 0;
++	if (create) invalidate();
++	return -ENOMEM;
++}
++
++
++static inline int mmu_step(unsigned long addr,pgd_t **pgd,pmd_t **pmd,
++    pte_t **pte,int create)
++{
++	if (addr & (PTRS_PER_PTE*PAGE_SIZE-1)) {
++		if (*pte) (*pte)++;
++	}
++	else {
++		if (addr & (PTRS_PER_PMD*PTRS_PER_PTE*PAGE_SIZE-1)) {
++			if (*pmd) (*pmd)++;
++		}
++		else {
++			(*pgd)++;
++			*pmd = create ? pmd_alloc(*pgd,addr) :
++			    pmd_offset(*pgd,addr);
++			if (!*pmd) {
++				if (create) invalidate();
++				*pte = NULL;
++				return -ENOMEM;
++			}
++		}
++		*pte = create ? pte_alloc(*pmd,addr) : pte_offset(*pmd,addr);
++		if (!*pte) {
++			if (create) invalidate();
++			return -ENOMEM;
++		}
++	}
++	return 0;
++}
++
++
++/*
++ * Removes a range of pages belonging to the current process. This helps to
++ * avoid undesirable copying when COW or swapped-out pages are overwritten in
++ * one sweep.
++ */
++
++
++void free_range(unsigned long start,unsigned long size)
++{
++	pgd_t *pgd;
++	pmd_t *pmd;
++	pte_t *pte;
++	unsigned long end;
++
++	end = (start+size) & ~(PAGE_SIZE-1);
++	start = (start+PAGE_SIZE-1) & ~(PAGE_SIZE-1);
++	if (start <= end) return;
++	(void) mmu_resolve(start,&pgd,&pmd,&pte,0);
++	while (1) {
++		if (pte && !pte_none(*pte)) {
++			pte_t old_page;
++
++			old_page = *pte;
++			pte_clear(pte);
++			if (!pte_present(old_page))
++				swap_free(pte_val(old_page));
++			else {
++				current->mm->rss--;
++				free_page(pte_page(old_page));
++			}
++		}
++		if ((start += PAGE_SIZE) >= end) break;
++		(void) mmu_step(start,&pgd,&pmd,&pte,0);
++	}
++	invalidate();
++}
++
++
++/*
++ * Copies data by mapping kernel pages into the current process. If the data is
++ * mis-aligned or if no whole pages can be copied, ordinary memory-to-memory
++ * copies are done.
++ *
++ * TODO: Speed improvement: if copying "almost" a page, don't copy from kernel
++ * to user, but still map the kernel page and copy the user data instead.
++ * This may also reduce the number of bad COW/swap activity.
++ *
++ */
++
++
++struct page_descriptor {
++        struct page_descriptor *next;
++        struct block_header *firstfree;
++        int order;
++        int nfree;
++};
++
++
++/*
++ * Since we always work on "big" buffers (>= one memory page), kmalloc's 
++ * page sharing doesn't get in the way.
++ */
++
++
++extern volatile unsigned long net_skbcount;
++
++
++static void free_around(struct sk_buff *skb,unsigned long start,
++    unsigned long end)
++{
++	struct page_descriptor *dsc;
++	unsigned long order,first,last;
++
++	net_skbcount--;
++	/* FIXME: should also update kmalloc counters @@@ */
++	dsc = (struct page_descriptor *) ((unsigned long) skb->head &
++	    PAGE_MASK);
++	order = dsc->order;
++	order = order < 7 ? 0 : order-7;
++	first = (unsigned long) dsc;
++	last = first+(PAGE_SIZE << order);
++	if (mem_map[MAP_NR(first)].count != 1) {
++		printk(KERN_CRIT "free_around: mem_map[%ld].count is 0x%x\n",
++		    MAP_NR(first),mem_map[MAP_NR(first)].count);
++		event_dump();
++		return;
++	}
++	while (first < last) {
++		mem_map[MAP_NR(first)].count = 1;
++		if (first < start || first >= end) free_page(first);
++		first += PAGE_SIZE;
++	}
++}
++
++
++/* fixme: what if reading into shared memory region ? */
++void mmucp_tofs(unsigned long user,unsigned long size,struct sk_buff *skb,
++    unsigned long kernel)
++{
++	unsigned long extra;
++	pgd_t *pgd;
++	pmd_t *pmd;
++	pte_t *pte;
++	int error;
++	unsigned long hole_start;
++
++	if (size > skb->len) size = skb->len;
++	EVENT("mmucp_tofs 0x%lx to 0x%lx+%ld\n",kernel,user,size);
++	if (skb->prev || skb->next || skb->lock || skb->users > 1) {
++		memcpy_tofs((void *) user,(void *) kernel,size);
++		return;
++	}
++	if (((kernel^user) & (PAGE_SIZE-1)) || size < PAGE_SIZE) {
++		EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,size);
++		memcpy_tofs((void *) user,(void *) kernel,size);
++		kfree_skb(skb,FREE_READ);
++		return;
++	}
++	if ((extra = -user & (PAGE_SIZE-1))) {
++		if ((size -= extra) < PAGE_SIZE) {
++			EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,
++			    size+extra);
++			memcpy_tofs((void *) user,(void *) kernel,size+extra);
++			kfree_skb(skb,FREE_READ);
++			return;
++		}
++		EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,size);
++		memcpy_tofs((void *) user,(void *) kernel,extra);
++		user += extra;
++		kernel += extra;
++	}
++	if ((error = mmu_resolve(user,&pgd,&pmd,&pte,1)) < 0) {
++		invalidate();
++		oom(current);
++		return;
++	}
++	hole_start = kernel;
++	while (1) {
++		pte_t old_page;
++
++		if (mem_map[MAP_NR(pte_page(*pte))].count > 1) {
++			EVENT("memcpy(0x%lx,0x%lx,PAGE_SIZE);\n",user,kernel,0);
++			memcpy_tofs((void *) user,(void *) kernel,PAGE_SIZE);
++		}
++		else {
++			old_page = *pte;
++			pte_clear(pte);
++			if (!pte_none(old_page))
++				if (!pte_present(old_page))
++					swap_free(pte_val(old_page));
++				else {
++					current->mm->rss--;
++					free_page(pte_page(old_page));
++				}
++			mem_map[MAP_NR(kernel)].count = 1;
++			/* Page is now owned only by user. */
++			*pte = mk_pte(kernel,PAGE_SHARED);
++			*pte = pte_mkdirty(*pte);
++			EVENT("mapped 0x%lx at 0x%lx\n",kernel,user,0);
++			current->mm->rss++;
++		}
++		user += PAGE_SIZE;
++		kernel += PAGE_SIZE;
++		if ((size -= PAGE_SIZE) < PAGE_SIZE) break;
++		if ((error = mmu_step(user,&pgd,&pmd,&pte,1)) < 0) {
++			kernel -= PAGE_SIZE; /* back off */
++			size = 0;
++			oom(current);
++			break;
++		}
++	}
++	if (size) {
++		EVENT("memcpy(0x%lx,0x%lx,%ld);\n",user,kernel,size);
++		memcpy_tofs((void *) user,(void *) kernel,size);
++	}
++	invalidate();
++	/* use skb code for all the administrative overhead */
++	skb->count++;
++	kfree_skb(skb,FREE_READ);
++	if (skb->count == 1) free_around(skb,hole_start,kernel);
++	else {
++		printk(KERN_CRIT "mmu_tofs: skb->count == %d\n",skb->count);
++		event_dump();
++	}
++}
++
++
++/*
++ * Fault user pages (current process) in physical memory, lock them there,
++ * and set them COW so that they stay around even if the user process tries
++ * to scribble over them. The locked physical page ranges are recorded in the
++ * scatter-gather vector IOV.
++ *
++ * FIXME: Should check user's physical memory size limit.
++ */
++
++
++int lock_user(unsigned long start,unsigned long size,int iov_max,
++    struct iovec *iov)
++{
++	struct vm_area_struct *vma;
++	unsigned long end,last,from,page;
++	pgd_t *pgd;
++	pmd_t *pmd;
++	pte_t *pte;
++	int iovcnt,error;
++
++	EVENT("lock_user: %ld@0x%lx\n",size,start,0);
++	end = start+size;
++	if (start >= end) return 0;
++	for (vma = find_vma(current,start); vma; vma = vma->vm_next)
++		if (vma->vm_flags & (VM_SHARED | VM_SHM)) return -EAGAIN;
++		else if (vma->vm_end >= end) break;
++	iovcnt = 0;
++	if ((error = mmu_resolve(start,&pgd,&pmd,&pte,1)) < 0) return error;
++	last = from = 0;
++	while (1) {
++		mem_map_t *map;
++
++		EVENT("<0x%lx|0x%lx|0x%lx>\n",(unsigned long) pgd,
++		    (unsigned long) pmd,(unsigned long) pte);
++		EVENT("0x%lx,0x%lx,%d\n",start,end,iovcnt);
++		if (pte_none(*pte) || !pte_present(*pte)) {
++			struct vm_area_struct *vma;
++ 
++			EVENT("handling missing page\n",0,0,0);
++			if (!(vma = find_vma(current,start))) {
++				printk(KERN_CRIT "lock_user: VMA (0x%lx) not "
++				    "found",start);
++				event_dump();
++				return -EINVAL;
++			}
++			do_no_page(current,vma,start,vma->vm_flags & VM_WRITE);
++		}
++		page = pte_page(*pte);
++		EVENT("got page 0x%lx\n",page,0,0);
++		if (!page) {
++			printk(KERN_ERR "lock_user: Gnorf, no page\n");
++			event_dump();
++			return -EINVAL;
++		}
++		map = mem_map+MAP_NR(page);
++		if (PageReserved(map)) {
++			printk(KERN_ERR "lock_user: reserved\n");
++			event_dump();
++			return -EINVAL;
++		}
++		atomic_inc(&map->count);
++		*pte = pte_wrprotect(*pte);
++		if (!last) {
++			from = page+(start & (PAGE_SIZE-1));
++			start &= ~(PAGE_SIZE-1);
++		}
++		else if (page != last+PAGE_SIZE) {
++				if (iovcnt >= iov_max) return -ENOSPC;
++				iov->iov_base = (caddr_t) from;
++				iov->iov_len = last+PAGE_SIZE-from;
++				EVENT("putting ... last=0x%lx,from=0x%lx\n",
++				    last,from,0);
++				iovcnt++;
++				iov++;
++                                from = page;
++			}
++		last = page;
++		if ((start += PAGE_SIZE) >= end) break;
++		if ((error = mmu_step(start,&pgd,&pmd,&pte,1)) < 0)
++			return error;
++	}
++	invalidate();
++	if (iovcnt >= iov_max) return -ENOSPC;
++	iov->iov_base = (caddr_t) from;
++	iov->iov_len = last+(end & (PAGE_SIZE-1))-from;
++	if (start == end) iov->iov_len += PAGE_SIZE;
++/*
++for (i = 0; i <= iovcnt; i++)
++  printk("iov[%d].iov_base = 0x%lx\niov[%d].iov_len = 0x%lx\n",
++i,(unsigned long) iov[i-iovcnt].iov_base,i,iov[i-iovcnt].iov_len);
++*/
++	return iovcnt+1;
++}
++
++
++/*
++ * Release user pages locked with lock_user. wrprotect isn't cleared, so we'll
++ * get a few extra protection faults (COW handling doesn't copy pages that are
++ * not shared), but that shouldn't do any harm.
++ */
++
++
++void unlock_user(int iovcnt,struct iovec *iov)
++{
++	unsigned long walk,end;
++ 
++	while (iovcnt--) {
++		end = (unsigned long) iov->iov_base+iov->iov_len;
++		for (walk = (unsigned long) iov->iov_base & ~(PAGE_SIZE-1);
++		    walk < end; walk += PAGE_SIZE) {
++			mem_map_t *map;
++
++			map = mem_map+MAP_NR(walk);
++			free_page(walk);
++		}
++		iov++;
++	}
++}
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/mmuio.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,25 @@
++/* mmuio.h - MMU-supported high-speed I/O */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef _LINUX_MMUIO_H
++#define _LINUX_MMUIO_H
++
++#include <linux/config.h>
++
++#ifdef CONFIG_MMU_HACKS
++
++#include <linux/uio.h>
++#include <linux/skbuff.h>
++
++void free_range(unsigned long start,unsigned long size);
++void mmucp_tofs(unsigned long user,unsigned long size,struct sk_buff *skb,
++    unsigned long kernel);
++int lock_user(unsigned long start,unsigned long size,int iov_max,
++    struct iovec *iov);
++void unlock_user(int iovcnt,struct iovec *iov);
++
++#endif
++
++#endif
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/raw.c	Tue Jul 30 19:13:44 1996
+@@ -0,0 +1,139 @@
++/* net/atm/raw.c - Raw AAL0 and AAL5 transports */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <linux/atmdev.h>
++#include <linux/kernel.h>
++#include <linux/skbuff.h>
++#include <linux/mm.h>
++#include <linux/mmuio.h>
++#include <linux/uio.h>
++
++#include "common.h"
++#include "protocols.h"
++#include "tunable.h"		/* tunable parameters */
++
++
++#if 0
++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
++#else
++#define DPRINTK(format,args...)
++#endif
++
++
++/*
++ * SKB == NULL indicates that the link is being closed
++ */
++
++void atm_push_raw(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++	if (skb) {
++		DPRINTK("APushR %d += %d\n",vcc->rx_inuse,skb->truesize);
++		atomic_add(skb->truesize+ATM_PDU_OVHD,&vcc->rx_inuse);
++		skb_queue_tail(&vcc->recvq,skb);
++		wake_up(&vcc->sleep);
++	}
++}
++
++
++/*
++ * A return value of NULL means to discard the PDU
++ */
++
++
++static struct sk_buff *atm_peek_aal0(struct atm_vcc *vcc,unsigned long pdu_size,
++    __u32 (*fetch)(struct atm_vcc *vcc,int i))
++{
++	struct sk_buff *skb;
++
++	if (pdu_size+vcc->rx_inuse+ATM_PDU_OVHD <= vcc->rx_quota) {
++		skb = alloc_skb(pdu_size,GFP_ATOMIC);
++		if (skb) return skb;
++	}
++	vcc->stats->rx_drop++;
++	return NULL;
++}
++
++
++/*
++ * atm_peek_aal5 is currently also used for AAL3/4
++ */
++
++
++static struct sk_buff *atm_peek_aal5(struct atm_vcc *vcc,unsigned long pdu_size,
++    __u32 (*fetch)(struct atm_vcc *vcc,int i))
++{
++	struct sk_buff *skb;
++
++	if (pdu_size+vcc->rx_inuse+ATM_PDU_OVHD <= vcc->rx_quota)
++		if (pdu_size < PAGE_SIZE) {
++			skb = alloc_skb((pdu_size+3) & ~3,GFP_ATOMIC);
++			if (skb) return skb;
++		}
++		else {
++			skb = alloc_skb(((pdu_size+3) & ~3)+PAGE_SIZE-1,
++			    GFP_ATOMIC);
++			if (skb) {
++				skb->data = (unsigned char *)
++				    (((unsigned long) skb->data+PAGE_SIZE-1) &
++				    ~(PAGE_SIZE-1));
++DPRINTK("PEEK: data at 0x%lx\n",(unsigned long) skb->data);
++				return skb;
++			}
++		}
++	vcc->stats->rx_drop++;
++	return NULL;
++}
++
++
++static void atm_pop_raw(struct atm_vcc *vcc,struct sk_buff *skb)
++{
++#ifdef CONFIG_MMU_HACKS
++	if (skb->atm.iovcnt)
++		unlock_user(skb->atm.iovcnt,(struct iovec *) skb->data);
++#endif
++	DPRINTK("APopR (%d) %d -= %d\n",vcc->vci,vcc->tx_inuse,skb->truesize);
++	atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse);
++	dev_kfree_skb(skb,FREE_WRITE);
++#if 0 /* experimental */
++if (vcc->dev->sending != 1) printk("sending == %d !!!\n",vcc->dev->sending);
++	vcc->dev->sending--;
++#endif
++	wake_up(&vcc->sleep);
++}
++
++
++int atm_init_aal0(struct atm_vcc *vcc)
++{
++	vcc->aal = ATM_AAL0;
++	vcc->push = atm_push_raw;
++	vcc->peek = atm_peek_aal0;
++	vcc->pop = atm_pop_raw;
++	vcc->push_oam = NULL;
++	return 0;
++}
++
++
++int atm_init_aal34(struct atm_vcc *vcc)
++{
++	vcc->aal = ATM_AAL34;
++	vcc->push = atm_push_raw;
++	vcc->peek = atm_peek_aal5; /* same procedure */
++	vcc->pop = atm_pop_raw;
++	vcc->push_oam = NULL;
++	return 0;
++}
++
++
++int atm_init_aal5(struct atm_vcc *vcc)
++{
++	vcc->aal = ATM_AAL5;
++	vcc->push = atm_push_raw;
++	vcc->peek = atm_peek_aal5;
++	vcc->pop = atm_pop_raw;
++	vcc->push_oam = NULL;
++	return 0;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/protocols.h	Mon Jun 10 17:36:28 1996
+@@ -0,0 +1,17 @@
++/* net/atm/protocols.h - ATM protocol handler entry points */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#ifndef NET_ATM_PROTOCOLS_H
++#define NET_ATM_PROTOCOLS_H
++
++void atm_push_raw(struct atm_vcc *vcc,struct sk_buff *skb);
++
++int atm_init_aal0(struct atm_vcc *vcc);	/* "raw" AAL0 */
++int atm_init_aal34(struct atm_vcc *vcc);/* "raw" AAL3/4 transport */
++int atm_init_aal5(struct atm_vcc *vcc);	/* "raw" AAL5 transport */
++int atm_init_clip(struct atm_vcc *vcc);	/* Classical IP over ATM (RFC1577) */
++int atm_init_atmarp(struct atm_vcc *vcc);/* ATM ARP */
++
++#endif
+--- ref/include/linux/netdevice.h	Sun Jun  9 11:25:36 1996
++++ work/include/linux/netdevice.h	Wed Jul 31 19:30:44 1996
+@@ -182,6 +182,8 @@
+   int			  (*set_mac_address)(struct device *dev, void *addr);
+ #define HAVE_PRIVATE_IOCTL
+   int			  (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);
++  int			  (*ip_arp)(struct device *dev,unsigned int cmd,
++				    void *arg);
+ #define HAVE_SET_CONFIG
+   int			  (*set_config)(struct device *dev, struct ifmap *map);
+ #define HAVE_HEADER_CACHE
+@@ -284,6 +286,8 @@
+ /* These functions live elsewhere (drivers/net/net_init.c, but related) */
+ 
+ extern void		ether_setup(struct device *dev);
++extern int		ether_arp(struct device *dev,unsigned int cmd,
++				  void *arg);
+ extern void		tr_setup(struct device *dev);
+ extern int		ether_config(struct device *dev, struct ifmap *map);
+ /* Support for loadable net-drivers */
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atmarp.h	Wed Jul 31 18:20:20 1996
+@@ -0,0 +1,98 @@
++/* atmarp.h - ATM ARP protocol and kernel-demon interface definitions */
++ 
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef _LINUX_ATMARP_H
++#define _LINUX_ATMARP_H
++
++#include <linux/socket.h>
++#include <linux/atm.h>
++#include <linux/atmioc.h>
++
++
++/* RFC 1577 ATM ARP header */
++
++struct atmarphdr {
++	unsigned short	ar_hrd;	/* Hardware type */
++	unsigned short	ar_pro;	/* Protocol type */
++	unsigned char	ar_shtl;/* Type & length of source ATM number (q) */
++	unsigned char	ar_sstl;/* Type & length of source ATM subaddress (r) */
++	unsigned short	ar_op;	/* Operation code (request, reply, or NAK) */
++	unsigned char	ar_spln;/* Length of source protocol address (s) */
++	unsigned char	ar_thtl;/* Type & length of target ATM number (x) */
++	unsigned char	ar_tstl;/* Type & length of target ATM subaddress (y) */
++	unsigned char	ar_tpln;/* Length of target protocol address (z) */
++	/* ar_sha, at_ssa, ar_spa, ar_tha, ar_tsa, ar_tpa */
++	unsigned char data[1];
++};
++
++#define	TL_LEN	0x3f	/* ATMARP Type/Length field structure */
++#define	TL_E164	0x40
++
++
++#define ATF_NULL	0x040	/* use NULL encapsulation */
++#define ATF_ARPSRV	0x080	/* entry describes ARP server */
++
++
++#define MAX_ATMARP_SIZE (sizeof(struct atmarphdr)-1+2*(ATM_E164_LEN+ \
++			ATM_ESA_LEN+4))
++
++#define ATMARP_RETRY_DELAY 30		/* request next resolution or forget
++					   NAK after 30 sec - should go into
++					   atmclip.h */
++
++/* These really ought to go into include/linux/if_arp.h */
++
++/* ARP protocol HARDWARE identifiers. */
++#define ARPHRD_ATM	19		/* ATM Forum			*/
++
++/* ARP protocol opcodes. */
++#define ARPOP_InREQUEST	8		/* InARP request		*/
++#define ARPOP_InREPLY	9		/* InARP reply			*/
++#define ARPOP_NAK	10		/* (ATM)ARP NAK			*/
++
++
++#define ATMARPD_CTRL	_IO('a',ATMIOC_CLIP+1)	/* become atmarpd ctrl sock */
++#define ATMARP_MKIP	_IO('a',ATMIOC_CLIP+2)	/* attach socket to IP */
++#define ATMARP_SETENTRY	_IO('a',ATMIOC_CLIP+3)	/* fill or hide ARP entry */
++#define ATMARP_ENCAP	_IO('a',ATMIOC_CLIP+5)	/* change encapsulation */
++
++/* ATMARP ioctl request. */
++struct atmarpreq {
++	struct sockaddr		arp_pa;		/* protocol address */
++	struct sockaddr_atmsvc	arp_ha;		/* PVC or SVC address */
++	struct atm_qos		arp_qos;	/* requested QOS */
++	int			arp_flags;	/* flags */
++};
++
++
++struct atmarp_arpsioc {
++	struct sockaddr		pa;	/* protocol address */
++	int			aa_len;	/* size of ATM address */
++	struct sockaddr_atmsvc	aa;	/* SVC address */
++};
++
++
++#define ATMARP_CTRL_MAGIC	0xac	/* put this into the magic byte */
++
++enum atmarp_ctrl_type {
++	act_invalid,		/* catch uninitialized structures */
++	act_need,		/* need address resolution */
++	act_create,		/* interface has been created */
++	act_up,			/* interface is coming up */
++	act_down,		/* interface is going down */
++	act_ioctl,		/* ioctl follows */
++	act_complete		/* demon indicates completion */
++};
++
++struct atmarp_ctrl {
++	unsigned char		magic;	/* constant */
++	enum atmarp_ctrl_type	type;	/* message type */
++	volatile int		*reply;	/* reply address, NULL is asynch */
++	int			itf_num;/* interface number (if present) */
++	unsigned long		arg;	/* argument, e.g. IP address */
++	unsigned char		data[1];/*optional data */
++};
++
++#endif
+--- ref/net/ipv4/udp.c	Tue Jun  4 14:34:43 1996
++++ work/net/ipv4/udp.c	Mon Jun 10 19:11:22 1996
+@@ -109,6 +109,11 @@
+ #include <net/route.h>
+ #include <net/checksum.h>
+ 
++#ifdef CONFIG_AREQUIPA
++#include <linux/atmdev.h>
++#include <linux/arequipa.h>
++#endif
++
+ /*
+  *	Snmp MIB for the UDP layer
+  */
+@@ -616,7 +621,7 @@
+ 	sk->dummy_th.dest = usin->sin_port;
+ 	sk->state = TCP_ESTABLISHED;
+ 	udp_cache_zap();
+-	sk->ip_route_cache = rt;
++	set_rt_cache(sk,rt);
+ 	return(0);
+ }
+ 
+@@ -655,6 +660,12 @@
+ 
+ static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
+ {
++#ifdef CONFIG_AREQUIPA
++	if (skb->dev == arequipa_dev && !sk->arequipa && sk->aq_route) {
++		if (!sk->ip_route_cache) printk("WNG: no route\n");
++		(void) arequipa_attach(skb->atm.vcc->sock,sk);
++	}
++#endif
+ 	skb->sk = sk;
+ 
+ 	if (sk->users) {
+--- ref/net/ipv4/tcp.c	Sat Jun  8 11:09:09 1996
++++ work/net/ipv4/tcp.c	Wed Jul 17 19:39:31 1996
+@@ -1926,6 +1926,11 @@
+ 	 *	Put in the IP header and routing stuff.
+ 	 */
+ 
++#ifdef CONFIG_AREQUIPA
++	ip_rt_put(sk->ip_route_cache);
++	set_rt_cache(sk,ip_rt_route(sk->daddr,sk->localroute));
++#endif
++
+ 	tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
+ 		IPPROTO_TCP, sk->opt, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
+ 	if (tmp < 0)
+--- ref/include/linux/proc_fs.h	Sun Jun  9 11:23:42 1996
++++ work/include/linux/proc_fs.h	Wed Jul 31 17:57:46 1996
+@@ -34,6 +34,7 @@
+ 	PROC_KSYMS,
+ 	PROC_DMA,	
+ 	PROC_IOPORTS,
++	PROC_ATM,
+ #ifdef __SMP_PROF__
+ 	PROC_SMP_PROF,
+ #endif
+@@ -148,6 +149,16 @@
+ #define PROC_DYNAMIC_FIRST 4096
+ #define PROC_NDYNAMIC      4096
+ 
++enum atm_directory_inos {
++	PROC_ATM_DEVICES = 384,
++	PROC_ATM_ARP,
++        PROC_ATM_LEC,
++	PROC_ATM_SVC,
++	PROC_ATM_PVC,
++	PROC_ATM_AREQUIPA,
++	PROC_ATM_LAST
++};
++
+ #define PROC_SUPER_MAGIC 0x9fa0
+ 
+ /*
+@@ -187,6 +198,7 @@
+ extern struct proc_dir_entry proc_root;
+ extern struct proc_dir_entry proc_net;
+ extern struct proc_dir_entry proc_scsi;
++extern struct proc_dir_entry proc_atm;
+ extern struct proc_dir_entry proc_sys;
+ extern struct proc_dir_entry proc_pid;
+ extern struct proc_dir_entry proc_pid_fd;
+@@ -275,5 +287,6 @@
+ extern struct inode_operations proc_kmsg_inode_operations;
+ extern struct inode_operations proc_link_inode_operations;
+ extern struct inode_operations proc_fd_inode_operations;
++extern struct inode_operations proc_atm_inode_operations;
+ 
+ #endif
+--- ref/fs/proc/root.c	Tue Apr 30 12:09:45 1996
++++ work/fs/proc/root.c	Mon Jun 10 17:36:31 1996
+@@ -15,6 +15,10 @@
+ #include <linux/config.h>
+ #include <asm/bitops.h>
+ 
++
++extern void atm_proc_init(void);
++
++
+ /*
+  * Offset of the first process in the /proc root directory..
+  */
+@@ -148,6 +152,16 @@
+ 	NULL, NULL				/* parent, subdir */
+ };
+ 
++struct proc_dir_entry proc_atm = {
++	PROC_ATM, 3, "atm",
++	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
++	0, &proc_dir_inode_operations,
++	NULL, NULL,
++	NULL,
++	&proc_root, NULL
++};
++
++
+ int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
+ {
+ 	dp->next = dir->subdir;
+@@ -296,6 +310,11 @@
+ 	proc_register(&proc_root, &proc_net);
+ 	proc_register(&proc_root, &proc_scsi);
+ 	proc_register(&proc_root, &proc_sys_root);
++
++#ifdef CONFIG_ATM
++	proc_register(&proc_root, &proc_atm);
++	atm_proc_init();
++#endif
+ 
+ #ifdef CONFIG_DEBUG_MALLOC
+ 	proc_register(&proc_root, &(struct proc_dir_entry) {
+--- ref/fs/proc/inode.c	Thu Apr 25 15:32:45 1996
++++ work/fs/proc/inode.c	Mon Jun 10 17:36:32 1996
+@@ -4,6 +4,7 @@
+  *  Copyright (C) 1991, 1992  Linus Torvalds
+  */
+ 
++#include <linux/config.h>
+ #include <linux/sched.h>
+ #include <linux/proc_fs.h>
+ #include <linux/kernel.h>
+@@ -161,6 +162,14 @@
+ 		return;
+ 	}
+ 
++#ifdef CONFIG_ATM
++	if (ino >= PROC_ATM_DEVICES && ino < PROC_ATM_LAST) {
++		inode->i_mode = S_IFREG | S_IRUGO;
++		inode->i_op = &proc_atm_inode_operations;
++		return;
++	}
++#endif
++
+ 	if (!pid) {
+ 		switch (ino) {
+ 			case PROC_KMSG:
+@@ -175,6 +184,13 @@
+                                 inode->i_nlink = 2;
+                                 inode->i_op = &proc_scsi_inode_operations;
+                                 break;
++#ifdef CONFIG_ATM
++			case PROC_ATM:
++				inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
++				inode->i_nlink = 2;
++				inode->i_op = &proc_atm_inode_operations;
++				break;
++#endif
+ 			case PROC_KCORE:
+ 				inode->i_mode = S_IFREG | S_IRUSR;
+ 				inode->i_op = &proc_kcore_inode_operations;
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/proc.c	Wed Jul 31 16:34:28 1996
+@@ -0,0 +1,484 @@
++/* net/atm/proc.c - ATM /proc interface */
++
++/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
++
++
++#include <linux/config.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/mm.h>
++#include <linux/fs.h>
++#include <linux/stat.h>
++#include <linux/proc_fs.h>
++#include <linux/errno.h>
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/netdevice.h>
++#include <linux/atmclip.h>
++#include <linux/atmarp.h>
++#include <linux/if_arp.h>
++#include <linux/arequipa.h> /* to get aqd */
++#include <asm/param.h> /* for HZ */
++
++#include "static.h" /* ugly */
++#include "signaling.h" /* to get sigd - ugly too */
++#include "atmarp.h"
++
++#ifdef CONFIG_ATM_LANE
++#include "lec_arpc.h"
++extern struct lec_arp_table *lec_arp_tables[LEC_ARP_TABLE_SIZE];
++extern struct lec_arp_table *lec_arp_empty_ones;
++extern struct lec_arp_table *lec_no_forward;
++#endif
++
++#ifdef CONFIG_AREQUIPA
++void atm_push_arequipa(struct atm_vcc *vcc,struct sk_buff *skb);
++#endif
++
++/*extern void eni_proc(int i,char *buf); - not yet @@@ */
++extern void atm_push_clip(struct atm_vcc *vcc,struct sk_buff *skb);
++
++
++static int atm_header(ino_t ino,char *buf)
++{
++	switch (ino) {
++		case PROC_ATM_DEVICES:
++			sprintf(buf,"Itf Type    ESI/\"MAC\"addr "
++			    "AAL(TX,err,RX,err,drop) ...\n");
++			break;
++		case PROC_ATM_ARP:
++			sprintf(buf,"IPitf TypeEncp Idle IP address      "
++			    "ATM address\n");
++			break;
++		case PROC_ATM_SVC:
++			sprintf(buf,"Itf VPI VCI   State      Remote\n");
++			break;
++		case PROC_ATM_PVC:
++			sprintf(buf,"Itf VPI VCI   AAL RX(PCR,Class) "
++			    "TX(PCR,Class)\n");
++			break;
++#ifdef CONFIG_ATM_LANE
++                case PROC_ATM_LEC:
++                        sprintf(buf," MAC          ATM destination                          Status            Flags VPI/VCI Recv VPI/VCI\n");
++                        break;
++#endif
++#ifdef CONFIG_AREQUIPA
++		case PROC_ATM_AREQUIPA:
++			sprintf(buf,"Itf VPI VCI   State    Socket\n");
++			break;
++#endif
++		default:
++			return -EINVAL;
++	}
++	return strlen(buf);
++}
++
++
++static void add_stats(char *buf,const char *aal,
++  const struct atm_aal_stats *stats)
++{
++	sprintf(strchr(buf,0),"%s ( %ld %ld %ld %ld %ld )",aal,stats->tx,
++	    stats->tx_err,stats->rx,stats->rx_err,stats->rx_drop);
++}
++
++
++static void dev_info(const struct atm_dev *dev,char *buf)
++{
++	int off,i;
++
++	off = sprintf(buf,"%3d %-8s",dev->number,dev->type);
++	for (i = 0; i < ESI_LEN; i++)
++		off += sprintf(buf+off,"%02x",dev->esi[i]);
++	strcat(buf,"  ");
++	add_stats(buf,"0",&dev->stats.aal0);
++	strcat(buf,"  ");
++	add_stats(buf,"5",&dev->stats.aal5);
++	strcat(buf,"\n");
++}
++
++
++#ifdef CONFIG_ATM_ATMARP
++
++
++static int svc_addr(char *buf,struct sockaddr_atmsvc *addr)
++{
++	static int code[] = { 1,2,10,6,1,0 };
++	static int e164[] = { 1,8,4,6,1,0 };
++	int *fields;
++	int len,i,j,pos;
++
++	len = 0;
++	if (*addr->sas_addr.pub) {
++		strcpy(buf,addr->sas_addr.pub);
++		len = strlen(addr->sas_addr.pub);
++		buf += len;
++		if (*addr->sas_addr.pub) {
++			*buf += '+';
++			len++;
++		}
++	}
++	else if (!*addr->sas_addr.prv) {
++			strcpy(buf,"(none)");
++			return strlen(buf);
++		}
++	if (*addr->sas_addr.prv) {
++		len += 44;
++		pos = 0;
++		fields = *addr->sas_addr.prv == ATM_AFI_E164 ? e164 : code;
++		for (i = 0; fields[i]; i++) {
++			for (j = fields[i]; j; j--) {
++				sprintf(buf,"%02X",addr->sas_addr.prv[pos++]);
++				buf += 2;
++			}
++			if (fields[i+1]) *buf++ = '.';
++		}
++	}
++	return len;
++}
++
++
++static void atmarp_info(struct device *dev,struct atmarp_entry *entry,
++    char *buf)
++{
++	unsigned char *ip;
++	int svc,off,ip_len;
++
++	svc = !entry->vcc || entry->vcc->family == AF_ATMSVC;
++	off = sprintf(buf,"%-6s%-4s%-4s%5ld ",dev->name,svc ? "SVC" : "PVC",
++	    entry->encap ? "LLC" : "NULL",(jiffies-entry->last_use)/HZ);
++	ip = (unsigned char *) &entry->ip;
++	ip_len = sprintf(buf+off,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
++	off += ip_len;
++	while (ip_len++ < 16) buf[off++] = ' ';
++	if (!entry->vcc) strcpy(buf+off,"(incomplete)\n");
++	else if (!svc)
++			sprintf(buf+off,"%d.%d.%d\n",entry->vcc->dev->number,
++			    entry->vcc->vpi,entry->vcc->vci);
++		else {
++			off += svc_addr(buf+off,&entry->vcc->remote);
++			strcpy(buf+off,"\n");
++		}
++}
++
++
++#endif
++
++
++static void pvc_info(struct atm_vcc *vcc,char *buf)
++{
++	static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
++	static const char *aal_name[] = {
++		"0",	"1",	"2",	"3/4",	/*  0- 3 */
++		"???",	"5",	"???",	"???" };/*  4- 7 */
++	int off;
++
++	off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
++	    vcc->dev->number,vcc->vpi,vcc->vci,
++	    vcc->aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
++	    aal_name[vcc->aal],vcc->qos.rxtp.min_pcr,
++	    class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
++	    class_name[vcc->qos.txtp.traffic_class]);
++#ifdef CONFIG_ATM_CLIP
++	if (vcc->push == atm_push_clip) {
++		struct device *dev;
++
++		dev = (struct device *) vcc->proto_data;
++		off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",dev->name);
++		if (dev->hard_header_len == RFC1483LLC_LEN)
++		    off += sprintf(buf+off,"LLC/SNAP");
++		else if (dev->hard_header_len == 0)
++		    off += sprintf(buf+off,"None");
++		else off += sprintf(buf+off,"Unknown");
++	}
++#endif
++	strcpy(buf+off,"\n");
++}
++
++
++static const char *vcc_state(struct atm_vcc *vcc)
++{
++    if (vcc->flags & ATM_VF_READY) return "CONNECTED";
++    if (vcc->flags & ATM_VF_RELEASED) return "CLOSING";
++    if (vcc->flags & ATM_VF_LISTEN) return "LISTEN";
++    if (vcc->flags & ATM_VF_REGIS) return "INUSE";
++    if (vcc->flags & ATM_VF_BOUND) return "BOUND";
++    return "IDLE";
++}
++
++
++static void svc_info(struct atm_vcc *vcc,char *buf)
++{
++	char *here;
++	int i;
++
++	if (!vcc->dev) sprintf(buf,"Unassigned    ");
++	else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,vcc->vci);
++	here = strchr(buf,0);
++	here += sprintf(here,"%-10s ",vcc == sigd ? "Signaling" :
++#ifdef CONFIG_ATM_ATMARP
++	    vcc == atmarpd ? "ATMARPctrl" :
++#endif
++#ifdef CONFIG_AREQUIPA
++	    vcc == aqd ? "Arequipa" :
++#endif
++	    vcc_state(vcc));
++	here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
++	    *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
++	if (*vcc->remote.sas_addr.prv)
++		for (i = 0; i < ATM_ESA_LEN; i++)
++			here += sprintf(here,"%02x",
++			    vcc->remote.sas_addr.prv[i]);
++	strcat(here,"\n");
++}
++
++
++#ifdef CONFIG_AREQUIPA
++
++
++static const char *arequipa_state(const struct atm_vcc *vcc)
++{
++	if (!(vcc->flags & ATM_VF_REGIS)) return "DOOMED";
++	if (vcc->upper) return "ATTACHED";
++	return "DANGLING";
++}
++
++
++static void arequipa_info(struct atm_vcc *vcc,char *buf)
++{
++	char *here;
++ 
++	if (!vcc->dev) sprintf(buf,"Unassigned    ");
++	else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,vcc->vci);
++        here = strchr(buf,0);
++        here += sprintf(here,"%-8s ",arequipa_state(vcc));
++	if (vcc->upper)
++		here += sprintf(here,"%ld",vcc->upper->socket &&
++		    SOCK_INODE(vcc->upper->socket) ?
++		    SOCK_INODE(vcc->upper->socket)->i_ino : 0);
++	strcat(here,"\n");
++}
++
++
++#endif
++
++
++#ifdef CONFIG_ATM_LANE
++static char*
++lec_arp_get_status_string(unsigned char status)
++{
++  switch(status) {
++  case ESI_UNKNOWN:
++    return "ESI_UNKNOWN       ";
++  case ESI_ARP_PENDING:
++    return "ESI_ARP_PENDING   ";
++  case ESI_VC_PENDING:
++    return "ESI_VC_PENDING    ";
++  case ESI_FLUSH_PENDING:
++    return "ESI_FLUSH_PENDING ";
++  case ESI_FORWARD_DIRECT:
++    return "ESI_FORWARD_DIRECT";
++  default:
++    return "<Unknown>         ";
++  }
++}
++
++static void 
++lec_info(struct lec_arp_table *entry, char *buf)
++{
++        int j, offset=0;
++        
++
++        for(j=0;j<ETH_ALEN;j++) {
++                offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
++        }
++        offset+=sprintf(buf+offset, " ");
++        for(j=0;j<ATM_ESA_LEN;j++) {
++                offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
++        }
++        offset+=sprintf(buf+offset, " %s %4.4x",
++                        lec_arp_get_status_string(entry->status),
++                        entry->flags&0xffff);
++        if (entry->vcc) {
++                offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi, 
++                                entry->vcc->vci);
++        }
++        if (entry->recv_vcc) {
++                offset+=sprintf(buf+offset, "     %3d %3d", 
++                                entry->recv_vcc->vpi, entry->recv_vcc->vci);
++        }
++
++        sprintf(buf+offset,"\n");
++}
++#endif
++
++
++static int atm_info(ino_t ino,loff_t *pos,char *buf)
++{
++	switch (ino) {
++		case PROC_ATM_DEVICES:
++			while (*pos <= MAX_ATM_ITF)
++				if (atm_dev[*pos-1].ops) break;
++				else (*pos)++;
++			if (*pos > MAX_ATM_ITF) return 0;
++			dev_info(atm_dev+*pos-1,buf);
++			break;
++#ifdef CONFIG_ATM_ATMARP
++		case PROC_ATM_ARP:
++			{
++				struct device *dev;
++				struct atmarp_entry *entry;
++				int count;
++
++				count = *pos;
++				for (dev = clip_devs; dev;
++				    dev = PRIV(dev)->next)
++					for (entry = PRIV(dev)->table; entry;
++					    entry = entry->next)
++						if (!--count) {
++							atmarp_info(dev,entry,
++							    buf);
++							return strlen(buf);
++						}
++				return 0;
++			}
++			return 0;
++#endif
++		case PROC_ATM_SVC:
++			while (*pos <= MAX_ATM_VCC)
++				if (atm_vcc[*pos-1].family == PF_ATMSVC) break;
++				else (*pos)++;
++			if (*pos > MAX_ATM_VCC) return 0;
++			svc_info(atm_vcc+*pos-1,buf);
++			break;
++		case PROC_ATM_PVC:
++			while (*pos <= MAX_ATM_VCC)
++				if (atm_vcc[*pos-1].family == PF_ATMPVC &&
++				    atm_vcc[*pos-1].dev) break;
++				else (*pos)++;
++			if (*pos > MAX_ATM_VCC) return 0;
++			pvc_info(atm_vcc+*pos-1,buf);
++			break;
++#ifdef CONFIG_AREQUIPA
++		case PROC_ATM_AREQUIPA:
++			while (*pos <= MAX_ATM_VCC)
++				if (atm_vcc[*pos-1].family == PF_ATMSVC &&
++				    atm_vcc[*pos-1].push == atm_push_arequipa)
++					break;
++				else (*pos)++;
++			if (*pos > MAX_ATM_VCC) return 0;
++			arequipa_info(atm_vcc+*pos-1,buf);
++			break;
++#endif
++#ifdef CONFIG_ATM_LANE
++               case PROC_ATM_LEC:
++                 {
++                          struct lec_arp_table *entry;
++                          int i, count;
++                          
++                          count = *pos;
++                          for(i=0;i<LEC_ARP_TABLE_SIZE;i++)
++                                  for(entry=lec_arp_tables[i];entry;
++                                      entry=entry->next) {
++                                          if (!--count) {
++                                                  lec_info(entry, buf);
++                                                  return strlen(buf);
++                                          }
++                                  }
++                          for(entry=lec_arp_empty_ones;
++                              entry; entry=entry->next) {
++                                  if (!--count) {
++                                          lec_info(entry, buf);
++                                          return strlen(buf);
++                                  }
++                          }
++                          for(entry=lec_no_forward;
++                              entry; entry=entry->next) {
++                                  if (!--count) {
++                                          lec_info(entry, buf);
++                                          return strlen(buf);
++                                  }
++                          }
++                          return 0;
++                 }
++#endif
++		default:
++			return -EINVAL;
++	}
++	return strlen(buf);
++}
++
++
++static int proc_atm_read(struct inode *inode,struct file *file,char *buf,
++    int count)
++{
++	unsigned long page;
++	int length;
++
++	if (count < 0) return -EINVAL;
++	page = get_free_page(GFP_KERNEL);
++	if (!page) return -ENOMEM;
++	if (file->f_pos)
++		length = atm_info(inode->i_ino,&file->f_pos,(char *) page);
++	else length = atm_header(inode->i_ino,(char *) page);
++	if (length > count) length = -EINVAL;
++	if (length >= 0) {
++		memcpy_tofs(buf,(char *) page,length);
++		file->f_pos++;
++	}
++	free_page(page);
++	return length;
++}
++
++
++static struct file_operations proc_atm_operations = {
++	NULL,			/* lseek */
++	proc_atm_read,		/* read */
++	NULL,			/* write */
++	NULL,			/* readdir */
++	NULL,			/* select */
++	NULL,			/* ioctl */
++	NULL,			/* mmap */
++	NULL,			/* no special open code */
++	NULL,			/* no special release */
++	NULL			/* can't fsync */
++};
++
++struct inode_operations proc_atm_inode_operations = {
++	&proc_atm_operations,	/* default ATM directory file-ops */
++	NULL,			/* create */
++	NULL,			/* lookup */
++	NULL,			/* link */
++	NULL,			/* unlink */
++	NULL,			/* symlink */
++	NULL,			/* mkdir */
++	NULL,			/* rmdir */
++	NULL,			/* mknod */
++	NULL,			/* rename */
++	NULL,			/* readlink */
++	NULL,			/* follow_link */
++	NULL,			/* readpage */
++	NULL,			/* writepage */
++	NULL,			/* bmap */
++	NULL,			/* truncate */
++	NULL			/* permission */
++};
++
++
++#define FILE(ino,name,len) &proc_atm, \
++    (&(struct proc_dir_entry) { ino, len, name, \
++    S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_atm_inode_operations, NULL })
++ 
++ 
++void atm_proc_init(void)
++{
++	proc_register(FILE(PROC_ATM_DEVICES,"devices",7));
++	proc_register(FILE(PROC_ATM_ARP,"arp",3));
++	proc_register(FILE(PROC_ATM_SVC,"svc",3));
++	proc_register(FILE(PROC_ATM_PVC,"pvc",3));
++#ifdef CONFIG_ATM_LANE
++        proc_register(FILE(PROC_ATM_LEC,"lec",3));
++#endif
++#ifdef CONFIG_AREQUIPA
++	proc_register(FILE(PROC_ATM_AREQUIPA,"arequipa",8));
++#endif
++}
+--- ref/drivers/char/console.c	Wed May 15 08:06:55 1996
++++ work/drivers/char/console.c	Mon Jun 10 17:36:33 1996
+@@ -1972,6 +1972,43 @@
+ 	}
+ }
+ 
++
++static unsigned long tty_base = 0;
++ 
++ 
++static void put_scon(char ch)
++{
++	unsigned long flags;
++ 
++	while (1) {
++		while (!(inb_p(tty_base+5) & 0x20));
++		save_flags(flags);
++		cli();
++		if (inb_p(tty_base+5) & 0x20) break;
++		restore_flags(flags);
++	}
++	outb_p(ch,tty_base);
++	restore_flags(flags);
++}
++
++
++static void print_scon(const char *str)
++{
++	if (!tty_base) return;
++	while (*str) {
++		if (*str == '\n') put_scon('\r');
++		put_scon(*str++);
++	}
++}
++ 
++ 
++void serial_console_setup(char *str,int *ints)
++{
++	if (ints[0] != 1 || ints[1] < 0 || ints[1] > 3) return;
++	tty_base = ((unsigned short *) 0x400)[ints[1]];
++}
++
++
+ /*
+  *  unsigned long con_init(unsigned long);
+  *
+@@ -2090,6 +2127,11 @@
+ 		display_desc, video_num_columns, video_num_lines,
+ 		MIN_NR_CONSOLES, (MIN_NR_CONSOLES == 1) ? "" : "s",
+ 	        MAX_NR_CONSOLES);
++
++	if (tty_base) {
++		register_console(print_scon);
++		return kmem_start;
++	}
+ 
+ 	/*
+ 	 * can't register TGA yet, because PCI bus probe has *not* taken
+--- ref/init/main.c	Mon May 20 19:33:57 1996
++++ work/init/main.c	Mon Jun 10 17:36:34 1996
+@@ -172,6 +172,8 @@
+ #endif
+ 
+ 
++void serial_console_setup(char *str,int *ints);
++
+ #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
+ extern void ipc_init(void);
+ #endif
+@@ -409,6 +411,7 @@
+ #ifdef CONFIG_BAYCOM
+ 	{ "baycom=", baycom_setup },
+ #endif
++	{ "scon=", serial_console_setup },
+ 	{ 0, 0 }
+ };
+ 
+--- ref/drivers/block/genhd.c	Sun May 19 12:29:22 1996
++++ work/drivers/block/genhd.c	Thu Jul 18 21:05:07 1996
+@@ -52,6 +52,7 @@
+ extern int blk_dev_init(void);
+ extern int scsi_dev_init(void);
+ extern int net_dev_init(void);
++extern int atmdev_init(void); 
+ 
+ /*
+  * disk_name() is used by genhd.c and md.c.
+@@ -654,6 +655,9 @@
+ #endif
+ #ifdef CONFIG_INET
+ 	net_dev_init();
++#endif
++#ifdef CONFIG_ATM
++	(void) atmdev_init();
+ #endif
+ 	console_map_init();
+ 
+--- ref/drivers/net/net_init.c	Mon Mar 25 07:58:21 1996
++++ work/drivers/net/net_init.c	Mon Jun 10 17:36:34 1996
+@@ -170,6 +170,7 @@
+ 	dev->set_mac_address 	= eth_mac_addr;
+ 	dev->header_cache_bind 	= eth_header_cache_bind;
+ 	dev->header_cache_update= eth_header_cache_update;
++	dev->ip_arp = ether_arp;
+ 
+ 	dev->type		= ARPHRD_ETHER;
+ 	dev->hard_header_len 	= ETH_HLEN;
+--- ref/drivers/net/eql.c	Mon May  6 11:26:08 1996
++++ work/drivers/net/eql.c	Mon Jun 10 17:36:35 1996
+@@ -248,6 +248,7 @@
+ 	dev->open		= eql_open;
+ 	dev->stop		= eql_close;
+ 	dev->do_ioctl		= eql_ioctl;
++	dev->ip_arp		= ether_arp;
+ 	dev->hard_start_xmit	= eql_slave_xmit;
+ 	dev->get_stats		= eql_get_stats;
+   
+--- ref/drivers/net/pi2.c	Fri Mar  1 06:50:45 1996
++++ work/drivers/net/pi2.c	Mon Jun 10 17:36:37 1996
+@@ -1414,6 +1414,7 @@
+     dev->open = pi_open;
+     dev->stop = pi_close;
+     dev->do_ioctl = pi_ioctl;
++    dev->ip_arp = ether_arp;
+     dev->hard_start_xmit = pi_send_packet;
+     dev->get_stats = pi_get_stats;
+ 
+--- ref/drivers/net/ppp.c	Wed May 29 06:32:38 1996
++++ work/drivers/net/ppp.c	Mon Jun 10 17:36:38 1996
+@@ -399,6 +399,7 @@
+ 	dev->stop	      = ppp_dev_close;
+ 	dev->get_stats	      = ppp_dev_stats;
+ 	dev->do_ioctl	      = ppp_dev_ioctl;
++	dev->ip_arp	      = ether_arp;
+ 	dev->addr_len	      = 0;
+ 	dev->tx_queue_len     = 10;
+ 	dev->type	      = ARPHRD_PPP;
+--- ref/net/ipv4/arp.c	Wed Jun  5 13:42:27 1996
++++ work/net/ipv4/arp.c	Mon Jun 10 17:36:38 1996
+@@ -2165,7 +2165,7 @@
+  *	Handle an ARP layer I/O control request.
+  */
+ 
+-int arp_ioctl(unsigned int cmd, void *arg)
++static int do_arp_ioctl(unsigned int cmd, void *arg)
+ {
+ 	int err;
+ 	struct arpreq r;
+@@ -2176,12 +2176,7 @@
+ 	{
+ 		case SIOCDARP:
+ 		case SIOCSARP:
+-			if (!suser())
+-				return -EPERM;
+ 		case SIOCGARP:
+-			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+-			if (err)
+-				return err;
+ 			memcpy_fromfs(&r, arg, sizeof(struct arpreq));
+ 			break;
+ 		case OLD_SIOCDARP:
+@@ -2370,6 +2365,47 @@
+ 
+ 
+ 
++int ether_arp(struct device *dev,unsigned int cmd,void *arg)
++{
++	return do_arp_ioctl(cmd,arg);
++}
++
++
++int arp_ioctl(unsigned int cmd,void *arg)
++{
++	struct arpreq req;
++	struct rtable *route;
++	int error;
++
++	switch (cmd) {
++		case SIOCSARP:
++		case SIOCDARP:
++			if (!suser()) return -EPERM;
++			/* fall through */
++		case SIOCGARP:
++			error = verify_area(VERIFY_READ,arg,sizeof(req));
++			if (error) return error;
++			memcpy_fromfs(&req,arg,sizeof(req));
++			if (req.arp_pa.sa_family != AF_INET)
++				return -EPFNOSUPPORT;
++			break;
++		default:
++			/* Here's the deal: unknown ioctls are passed, but
++			   their request structure _must_ begin with a struct
++			   sockaddr. */
++			error = verify_area(VERIFY_READ,arg,
++			    sizeof(struct sockaddr));
++			if (error) return error;
++			memcpy_fromfs(&req.arp_pa,arg,sizeof(struct sockaddr));
++	}
++	route = ip_rt_route(((struct sockaddr_in *) &req.arp_pa)->sin_addr.
++	    s_addr,1);
++	if (!route) return -EHOSTUNREACH;
++	if (!route->rt_dev->ip_arp) return -EINVAL;
++	return route->rt_dev->ip_arp(route->rt_dev,cmd,arg);
++}
++  
++  
+ /*
+  *	Called once on startup.
+  */
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/atmlec.h	Wed Jul 31 18:20:21 1996
+@@ -0,0 +1,58 @@
++/*
++ * 
++ * ATM Lan Emulation Daemon vs. driver interface
++ *
++ * carnil@cs.tut.fi
++ *
++ */
++
++#ifndef _ATMLEC_H_
++#define _ATMLEC_H_
++
++#include <linux/atmioc.h>
++#include <linux/atm.h>
++#include <linux/if_ether.h>
++/* ATM lec daemon control socket */
++#define ATMLEC_CTRL _IO('a',ATMIOC_LANE)
++#define ATMLEC_DATA _IO('a',ATMIOC_LANE+1)
++#define ATMLEC_MCAST _IO('a',ATMIOC_LANE+2)
++
++typedef enum { l_set_mac_addr, l_del_mac_addr, 
++               l_flush_xmt, l_svc_setup, 
++               l_add_permanent, l_addr_delete,
++               l_topology_change, l_flush_complete,
++               l_arp_update, l_reset, l_config,
++               l_flush_tran_id, l_set_lecid,
++               l_arp_xmt } atmlec_msg_type;
++#define ATMLEC_MSG_TYPE_MAX l_arp_xmt
++
++struct atmlec_config_msg {
++        unsigned int maximum_unknown_frame_count;
++        unsigned long max_unknown_frame_time;
++        unsigned short max_retry_count;
++        unsigned long aging_time;
++        unsigned long forward_delay_time;
++        unsigned long arp_response_time;
++        unsigned long flush_timeout;
++        unsigned long path_switching_delay;
++};
++ 
++struct atmlec_msg {
++        atmlec_msg_type type;
++        union {
++                struct {
++                        unsigned char mac_addr[ETH_ALEN];
++                        unsigned char atm_addr[ATM_ESA_LEN];
++                        unsigned long flag;/* Topology_change flag, 
++                                              remoteflag, permanent flag,
++                                              lecid, transaction id */
++                } normal;
++                struct atmlec_config_msg config;
++        } content;
++};
++
++struct atmlec_ioc {
++        unsigned char atm_addr[ATM_ESA_LEN];
++        unsigned char receive;    /* 1= receive vcc, 0 = send_vcc */
++};
++#endif /* _ATMLEC_H_ */
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/lec.c	Tue Jul 30 18:43:49 1996
+@@ -0,0 +1,589 @@
++/*
++ * lec.c: Lan Emulation driver 
++ * Marko Kiiskila carnil@cs.tut.fi
++ *
++ */
++
++#include <linux/kernel.h>
++
++/* We are ethernet device */
++#include <linux/if_ether.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <net/sock.h>
++#include <linux/skbuff.h>
++#include <asm/byteorder.h>
++#include <net/arp.h>
++#include <linux/proc_fs.h>
++
++/* And atm device */
++#include <linux/atmdev.h>
++#include <linux/atmlec.h>
++
++#include "lec.h"
++#include "lec_arpc.h"
++
++#define DPRINTK(format,args...)
++
++struct atm_vcc *lecd = NULL;
++
++#define DUMP_PACKETS 0 /* 0 = None,
++                       * 1 = 30 first bytes
++                       * 2 = Whole packet
++                       */
++
++static int lec_open(struct device *dev);
++static int lec_send_packet(struct sk_buff *skb, struct device *dev);
++static int lec_close(struct device *dev);
++static struct enet_statistics *lec_get_stats(struct device *dev);
++static int lec_init(struct device *dev);
++
++static char myname[] = "lec0";
++
++extern struct lec_arp_table *lec_arp_empty_ones;
++
++/* Have we register_netdev() yet? */
++unsigned char registered = 0;
++
++static struct device dev_lec = {
++        myname,
++        0, 0, /* rmem_end, rmem_start */
++        0, 0, /* mem_end, mem_start */
++        0, 0, /* base_addr, irq */
++        0, 0, /* start, interrupt */
++        0, NULL, /*  tbusy, *next */
++        lec_init /* Initialization func */
++}; 
++
++struct lec_priv {
++        struct enet_statistics stats;
++        unsigned short lecid;      /* Lecid of this client */
++};
++
++/*
++ * Open/initialize the netdevice. This is called (in the current kernel)
++ * sometime after booting when the 'ifconfig' program is run.
++ *
++ * This routine should set everything up anew at each open, even
++ * registers that "should" only need to be set once at boot, so that
++ * there is non-reboot way to recover if something goes wrong.
++ */
++
++static int 
++lec_open(struct device *dev)
++{
++        struct lec_priv *priv = (struct lec_priv *)dev->priv;
++        
++        dev->tbusy = 0;
++        dev->start = 1;
++        dev->interrupt = 1;
++        if (priv)
++                memset(&priv->stats,0,sizeof(struct enet_statistics));
++        
++        return 0;
++}
++
++static int 
++lec_send_packet(struct sk_buff *skb, struct device *dev)
++{
++        struct lec_priv *priv = (struct lec_priv *)dev->priv;
++        struct lecdatahdr_8023 *lec_h;
++        struct atm_vcc *send_vcc;
++#if DUMP_PACKETS > 0
++        char buf[300];
++        int i=0;
++#endif /* DUMP_PACKETS >0 */
++        
++        DPRINTK("Lec_send_packet called\n");  
++        if (!lecd) {
++                printk("LEC:No lecd attached\n");
++                if (priv)
++                        priv->stats.tx_errors++;
++                return -EUNATCH;
++        } 
++        if (dev->tbusy) {
++                /*
++                 * If we get here, some higher level has decided we are broken.
++                 * There should really be a "kick me" function call instead.
++                 */
++                printk(KERN_WARNING "%s: transmit timed out.\n", dev->name);
++                dev->tbusy=0;
++        }
++        /*
++         * If some higher layer thinks we've missed an tx-done interrupt
++         * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
++         * itself.
++         */
++        if (skb == NULL) {
++                dev_tint(dev);
++                return 0;
++        }
++        /*
++         * Block a timer-based transmit from overlapping. This could better be
++         * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
++         */
++
++        if (set_bit(0, (void*)&dev->tbusy) != 0) {
++                printk(KERN_WARNING "%s: Transmitter access conflict.\n", 
++                       dev->name);
++        } else {
++                DPRINTK("skbuff head:%lx data:%lx tail:%lx end:%lx\n",
++                        (long)skb->head, (long)skb->data, (long)skb->tail,
++                        (long)skb->end);
++                
++                /* Put le header to place */
++                lec_h = (struct lecdatahdr_8023*)skb->data;
++                lec_h->le_header = htons(priv->lecid); 
++                
++#if DUMP_PACKETS > 0
++                printk("LEC: send datalen:%ld lecid:%4.4x\n", 
++                        skb->len, priv->lecid);
++#if DUMP_PACKETS >= 2
++                for(i=0;i<skb->len && i <99;i++) {
++                        sprintf(buf+i*3,"%2.2x ",0xff&skb->data[i]);
++                }
++#elif DUMP_PACKETS >= 1
++                for(i=0;i<skb->len && i < 30;i++) {
++                        sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
++                }
++#endif /* DUMP_PACKETS >= 1 */
++                if (i==skb->len)
++                        printk("%s\n",buf);
++                else
++                        printk("%s...\n",buf);
++#endif /* DUMP_PACKETS > 0 */
++                /* Send to right vcc */
++                send_vcc = lec_arp_resolve(lec_h->h_dest);
++                if (!send_vcc || !(send_vcc->flags & ATM_VF_READY)) {    
++                        if (priv)
++                                priv->stats.tx_errors++;
++                        DPRINTK("LEC:lec_send_packet: dropping ..\n");
++                        dev_kfree_skb(skb, FREE_WRITE);
++                        return 0;
++                } else {
++#if DUMP_PACKETS > 0                    
++                        printk("LEC:sending to vpi:%d vci:%d\n",
++                                send_vcc->vpi, send_vcc->vci);
++#endif /* DUMP_PACKETS > 0 */
++                        /* Minimum ethernet-frame size */
++                        if (skb->len <62)
++                                skb->len = 62;
++			atomic_add(skb->truesize,&send_vcc->tx_inuse);
++                        skb->atm.iovcnt = 0;
++                        send_vcc->dev->ops->send(send_vcc, skb);
++                        if (priv)
++                                priv->stats.tx_packets++;
++                }
++        }
++        /* Should we wait for card's device driver to notify us? */
++        dev->tbusy=0;
++        
++        return 0;
++}
++
++/* The inverse routine to net_open(). */
++static int 
++lec_close(struct device *dev) 
++{
++        dev->tbusy = 1;
++        dev->start = 0;
++        return 0;
++}
++
++/*
++ * Get the current statistics.
++ * This may be called with the card open or closed.
++ */
++static struct enet_statistics *
++lec_get_stats(struct device *dev)
++{
++        struct lec_priv *priv = (struct lec_priv *)dev->priv;
++        
++        return (struct enet_statistics *)&priv->stats;
++}
++
++int 
++lec_hard_header(struct sk_buff *skb, struct device *dev, 
++               unsigned short type, void *daddr, void *saddr, 
++               unsigned len)
++{
++        struct lec_priv *priv = (struct lec_priv *)dev->priv;
++        struct lecdatahdr_8023 *hdr = 
++                (struct lecdatahdr_8023 *)skb_push(skb, LEC_HEADER_LEN); 
++                
++        /* Set lecid header */
++        if (priv)
++                hdr->le_header = htons(priv->lecid);
++        else
++                hdr->le_header = 0;
++        
++        /* Set the protocol type. */
++        if(type!=ETH_P_802_3)
++                hdr->h_type = htons(type);
++        else
++                hdr->h_type = htons(len);
++        
++        /* Source hw address */
++        if (saddr)
++                memcpy(hdr->h_source, saddr, dev->addr_len);
++        else
++                memcpy(hdr->h_source, dev->dev_addr, dev->addr_len);
++        
++        /* Destination addr */
++        if (daddr) {
++                memcpy(hdr->h_dest, daddr, dev->addr_len);
++                hdr->le_header = 0;
++                return dev->hard_header_len;
++        }
++        return -dev->hard_header_len;
++}
++
++int 
++lec_rebuild_header(void *buff, struct device *dev, unsigned long dst,
++                  struct sk_buff *skb)
++{
++        struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023*)buff;
++        
++        /*
++         *      Only ARP/IP is currently supported
++         */
++        if (hdr->h_type !=  htons(ETH_P_IP)) {
++                printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",
++                       dev->name,(int)hdr->h_type);
++                memcpy(hdr->h_source, dev->dev_addr, dev->addr_len);
++                return 0;
++        }
++        /*
++         *      Try and get ARP to resolve the header.
++         */
++#ifdef CONFIG_INET       
++        return arp_find(hdr->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
++#else
++        return 0;
++#endif
++}
++
++/*
++ * Upper level calls this function to bind hardware header cache entry.
++ * If the call is successful, then corresponding Address Resolution Protocol
++ * (maybe, not ARP) takes responsibility for updating cache content.
++ */
++void 
++lec_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
++                     unsigned short htype, __u32 daddr)
++{
++        struct hh_cache *hh;
++        
++        if (htype != ETH_P_IP) {
++                printk("eth_header_cache_bind: %04x cache is not implemented\n",
++                       htype);
++                return;
++        }
++        if (arp_bind_cache(hhp, dev, htype, daddr))
++                return;
++        if ((hh=*hhp) != NULL) {
++                memcpy(hh->hh_data+8, dev->dev_addr, ETH_ALEN);
++                hh->hh_data[14] = htype>>8;
++                hh->hh_data[15] = htype&0xFF;
++        }
++}
++
++/*
++ * Called by Address Resolution module to notify changes in address.
++ */
++void 
++lec_header_cache_update(struct hh_cache *hh, struct device *dev, 
++                       unsigned char *haddr)
++{
++        if (hh->hh_type != ETH_P_IP) {
++                printk("lec_header_cache_update: %04x cache is not implemented\n", 
++                       hh->hh_type);
++                return;
++        }
++        memcpy(hh->hh_data+2, haddr, ETH_ALEN);
++        hh->hh_uptodate = 1;
++}
++
++static int 
++lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
++{
++        struct lec_priv *priv = (struct lec_priv *)dev_lec.priv;
++        struct atmlec_msg *mesg;
++        int i;
++        
++        mesg = (struct atmlec_msg *)skb->data;
++        switch(mesg->type) {
++        case l_set_mac_addr:
++                for (i=0;i<6;i++) {
++                        dev_lec.dev_addr[i] = mesg->content.normal.mac_addr[i];
++                }    
++                break;
++        case l_del_mac_addr:
++                for(i=0;i<6;i++) {
++                        dev_lec.dev_addr[i] = 0;
++                }
++                break;
++        case l_add_permanent:
++                lec_add_permanent(mesg->content.normal.mac_addr,
++                                  mesg->content.normal.atm_addr);
++                break;
++        case l_addr_delete:
++                lec_addr_delete(mesg->content.normal.atm_addr, 
++                                mesg->content.normal.flag);
++                break;
++        case l_topology_change:
++                lec_topology_flag_change_set(mesg->content.normal.flag);
++                break;
++        case l_flush_complete:
++                lec_flush_complete(mesg->content.normal.atm_addr, 
++                                   mesg->content.normal.flag);
++                break;
++        case l_arp_update:
++                lec_arp_update(mesg->content.normal.mac_addr,
++                               mesg->content.normal.atm_addr,
++                               mesg->content.normal.flag);
++                break;
++        case l_reset:
++                lec_reset();
++                break;
++        case l_config:
++                lec_config(&mesg->content.config);
++                break;
++        case l_flush_tran_id:
++                lec_set_flush_tran_id(mesg->content.normal.mac_addr,
++                                      mesg->content.normal.flag);
++                break;
++        case l_set_lecid:
++                if (priv)
++                        priv->lecid = (unsigned short)(0xffff&mesg->content.normal.flag);
++                break;
++        default:
++                printk("LEC: Unknown message type %d\n",mesg->type);
++                dev_kfree_skb(skb, FREE_WRITE);
++                return -EINVAL;
++        }
++        dev_kfree_skb(skb, FREE_WRITE);
++        return 0;
++}
++
++static void 
++lec_atm_close(struct atm_vcc *vcc)
++{
++        struct sk_buff *skb;
++
++        lecd = NULL;
++        /* Do something needful? */
++
++        dev_lec.tbusy = 1;
++        dev_lec.start = 0;
++
++        lec_arp_destroy();
++
++#if 0
++        unregister_netdev(&dev_lec);
++        /* Should we do this? */
++        if (priv) {
++                kfree_s(priv, sizeof(struct lec_priv));
++                dev_lec.priv = NULL;
++        }
++#endif
++        if (skb_peek(&vcc->recvq))
++		printk("LEC lec_atm_close: closing with messages pending\n");
++        while ((skb = skb_dequeue(&vcc->recvq))) 
++		dev_kfree_skb(skb,FREE_READ);
++  
++	printk("LEC: Shut down!\n");
++}
++
++static struct atmdev_ops lecdev_ops = {
++        NULL, /*open*/
++        lec_atm_close, /*close*/
++        NULL, /*ioctl*/
++        NULL, /*getsockopt */
++        NULL, /*setsockopt */
++        lec_atm_send, /*send */
++        NULL, /*sg_send */
++        NULL, /*poll */
++        NULL, /*phy_put*/
++        NULL, /*phy_get*/
++        NULL  /*feedback*/
++};
++
++static struct atm_dev lecatm_dev = {
++        &lecdev_ops,
++        NULL,	    /*PHY*/
++        "lec",	    /*type*/
++        999,	    /*dummy device number*/
++        NULL,NULL,  /*no VCCs*/
++        NULL,NULL,  /*no data*/
++        0,	    /*no flags*/
++        NULL,	    /* no local address*/
++        { 0 }	    /*no ESI, no statistics */
++};
++
++int 
++send_to_lecd(atmlec_msg_type type, unsigned char *mac_addr, 
++	    unsigned char *atm_addr)
++{
++	struct sk_buff *skb;
++	struct atmlec_msg *mesg;
++  
++	if (!lecd) {
++		return -1;
++	}
++	skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
++	if (!skb)
++		return -1;
++	skb->free = 1;
++	skb->len = sizeof(struct atmlec_msg);
++	mesg = (struct atmlec_msg *)skb->data;
++	mesg->type = type;
++	if (mac_addr)
++		memcpy(&mesg->content.normal.mac_addr, mac_addr, ETH_ALEN);
++	if (atm_addr)
++		memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
++
++	skb_queue_tail(&lecd->recvq, skb);
++        wake_up(&lecd->sleep);
++        return 0;
++}
++
++static int 
++lec_init(struct device *dev)
++{
++        dev->priv = kmalloc(sizeof(struct lec_priv), GFP_KERNEL);
++        if (dev->priv == NULL)
++                return -ENOMEM;
++
++        memset(dev->priv,0,sizeof(struct lec_priv));
++
++        ether_setup(dev);
++        dev->open = lec_open;
++        dev->stop = lec_close;
++        dev->hard_start_xmit = lec_send_packet;
++        dev->hard_header_len = LEC_HEADER_LEN;
++
++        dev->hard_header = lec_hard_header;
++        dev->rebuild_header = lec_rebuild_header;
++        dev->header_cache_bind  = lec_header_cache_bind;
++        dev->header_cache_update= lec_header_cache_update;
++
++        dev->get_stats = lec_get_stats;
++        dev->set_multicast_list = NULL;
++        dev->do_ioctl  = NULL;
++        printk("LEC: Initialized!\n");
++        return 0;
++}
++
++static unsigned char lec_ctrl_magic[] = {
++        0xff,
++        0x00,
++        0x01,
++        0x01 };
++
++void 
++lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
++{
++        struct lec_priv *priv = (struct lec_priv *)dev_lec.priv;
++        struct lecdatahdr_8023 *hdr;
++#if DUMP_PACKETS >0
++        int i=0;
++        char buf[300];
++
++        printk("LEC: lec_push vcc vpi:%d vci:%d\n",vcc->vpi, vcc->vci);
++#endif
++        if (!skb) {
++                DPRINTK("LEC: null skb\n");
++                lec_vcc_close(vcc);
++                return;
++        }
++#if DUMP_PACKETS > 0
++        printk("LEC: rcv datalen:%ld lecid:%4.4x\n", skb->len, priv->lecid);
++#if DUMP_PACKETS >= 2
++        for(i=0;i<skb->len && i <99;i++) {
++                sprintf(buf+i*3,"%2.2x ",0xff&skb->data[i]);
++        }
++#elif DUMP_PACKETS >= 1
++        for(i=0;i<skb->len && i < 30;i++) {
++                sprintf(buf+i*3,"%2.2x ", 0xff&skb->data[i]);
++        }
++#endif /* DUMP_PACKETS >= 1 */
++        if (i==skb->len)
++                printk("%s\n",buf);
++        else
++                printk("%s...\n",buf);
++#endif /* DUMP_PACKETS > 0 */
++        if (memcmp(skb->data, lec_ctrl_magic, 4) ==0) { /* Control frame, to daemon*/
++                DPRINTK("LEC: To daemon\n");
++                skb_queue_tail(&vcc->recvq, skb);
++                wake_up(&vcc->sleep);
++        } else { /* Data frame, queue to protocol handlers */
++                hdr = (struct lecdatahdr_8023 *)skb->data;
++                if (hdr->le_header == htons(priv->lecid) ||
++                    !lecd) { /* Probably looping back, or if lecd is missing,
++				lecd has gone down */
++                        DPRINTK("Ignoring loopback frame...\n");
++                        dev_kfree_skb(skb, FREE_READ);
++                        return;
++                }
++                if (lec_arp_empty_ones) { /* FILTER DATA!!!! */
++                        lec_arp_check_empties(vcc, skb);
++                }
++                skb->dev = &dev_lec;
++                skb->mac.raw = (unsigned char *)skb->data;
++                skb->mac.ethernet = (struct ethhdr *)skb_pull(skb, 2);     
++                if (*hdr->h_dest&1) {
++                        if (memcmp(hdr->h_dest,dev_lec.broadcast, ETH_ALEN)==0)
++                                skb->pkt_type=PACKET_BROADCAST;
++                        else
++                                skb->pkt_type=PACKET_MULTICAST;
++                } else if (dev_lec.flags&(IFF_PROMISC|IFF_ALLMULTI)) {
++                        if (memcmp(hdr->h_dest,dev_lec.dev_addr, ETH_ALEN))
++                                skb->pkt_type=PACKET_OTHERHOST;
++                } 
++                if (ntohs(hdr->h_type)>=1536)
++                        skb->protocol = hdr->h_type;
++                else 
++                        skb->protocol = htons(ETH_P_802_2);
++                skb->h.raw = skb_pull(skb, ETH_HLEN);
++                netif_rx(skb);
++                if (priv)
++                        priv->stats.rx_packets++;
++        }
++}
++
++int 
++lec_vcc_attach(struct atm_vcc *vcc, void *arg)
++{
++        struct atmlec_ioc ioc_data;
++
++        /* Lecd must be up in this case */
++        memcpy_fromfs(&ioc_data, arg, sizeof(struct atmlec_ioc));
++        lec_vcc_added(&ioc_data, vcc, vcc->push);
++        vcc->push = lec_push;
++        return 0;
++}
++
++/* Initialize device. */
++int 
++lecd_attach(struct atm_vcc *vcc)
++{  
++        int result;
++
++        if (lecd) {
++                printk("LEC: Lecd already contacted\n");
++                return -EADDRINUSE;
++        }
++	
++        lec_arp_init();
++	if (!registered) {
++		registered = 1;
++		if ((result =register_netdev(&dev_lec)) != 0)    
++			return result;
++	}
++        lecd = vcc;
++        vcc->dev = &lecatm_dev;
++        vcc->flags |= ATM_VF_READY;
++
++        return 0;
++}
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/lec.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,53 @@
++/*
++ *
++ * Lan Emulation client header file
++ *
++ * Marko Kiiskila carnil@cs.tut.fi
++ *
++ */
++
++#ifndef _LEC_H_
++#define _LEC_H_
++
++#include <linux/atmdev.h>
++#include <linux/if_ether.h>
++
++#define LEC_HEADER_LEN 16
++
++struct lecdatahdr_8023 {
++  unsigned short le_header;
++  unsigned char h_dest[ETH_ALEN];
++  unsigned char h_source[ETH_ALEN];
++  unsigned short h_type;
++};
++
++struct lecdatahdr_8025 {
++  unsigned short le_header;
++  unsigned char ac_pad;
++  unsigned char fc;
++  unsigned char h_dst[ETH_ALEN];
++  unsigned char h_source[ETH_ALEN];
++};
++
++/*
++ * ATM LAN Emulation supports both LLC & Dix Ethernet EtherType
++ * frames. 
++ * 1. Dix Ethernet EtherType frames encoded by placing EtherType
++ *    field in h_type field. Data follows immediatelly after header.
++ * 2. LLC Data frames whose total length, including LLC field and data,
++ *    but not padding required to meet the minimum data frame length, 
++ *    is less than 1536(0x0600) MUST be encoded by placing that length
++ *    in the the h_type field. The LLC field follows header immediatelly.
++ * 3. LLC data frames longer than this maximum MUST be encoded by placing
++ *    the value 0 in the h_type field.
++ *
++ */
++
++int lecd_attach(struct atm_vcc *vcc);
++int lec_vcc_attach(struct atm_vcc *vcc, void *arg);
++int make_lec(struct atm_vcc *vcc);
++int send_to_lecd(atmlec_msg_type type, unsigned char *mac_addr,
++                unsigned char *atm_addr);
++void lec_push(struct atm_vcc *vcc, struct sk_buff *skb);
++#endif _LEC_H_
++
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/lec_arpc.c	Thu Jul 11 13:11:57 1996
+@@ -0,0 +1,1090 @@
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <asm/param.h>
++#include <asm/atomic.h>
++#include <net/route.h>
++
++#include <linux/atmlec.h>
++
++#include "lec.h"
++#include "lec_arpc.h"
++
++#define DPRINTK(format,args...)
++#define DEBUG_ARP_TABLE 0
++
++struct lec_arp_table *lec_arp_tables[LEC_ARP_TABLE_SIZE] =
++{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,  /* At least this gets */
++  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; /* initialized */
++
++/* Used for storing VCC's that don't have a MAC address attached yet */
++struct lec_arp_table *lec_arp_empty_ones = NULL;
++
++/* Used for storing VCC's (and forward packets from) which are to
++   age out by not using them to forward packets. 
++   This is because to some LE clients there will be 2 VCCs. Only
++   one of them gets used. */
++struct lec_arp_table *lec_no_forward = NULL;
++
++static atomic_t lec_arp_lock_var = 0;
++
++struct atm_vcc *mcast_vcc = NULL;
++
++#define LEC_ARP_REFRESH_INTERVAL (3*HZ)
++
++static void lec_arp_check_expire(unsigned long dumle);
++static __inline__ void lec_arp_expire_arp(unsigned long dumb);
++static void lec_clear_all(void);
++void dump_arp_table(void);
++
++static struct timer_list lec_arp_timer = 
++{ NULL,NULL, LEC_ARP_REFRESH_INTERVAL, 0L, &lec_arp_check_expire };
++static unsigned char lec_freeze;
++
++/* C10 */
++static unsigned int lec_maximum_unknown_frame_count = 1;
++/* Within the period of time defined by this variable, the client will send 
++   no more than C10 frames to BUS for a given unicast destination. (C11) */
++static unsigned long lec_max_unknown_frame_time = (1*HZ);
++/* If no traffic has been sent in this vcc for this period of time,
++   vcc will be torn down (C12)*/
++static unsigned long lec_vcc_timeout_period = (1200*HZ);
++/* An LE Client MUST not retry an LE_ARP_REQUEST for a 
++   given frame's LAN Destination more than maximum retry count times,
++   after the first LEC_ARP_REQUEST (C13)*/
++static unsigned short lec_max_retry_count = 1;
++/* Max time the client will maintain an entry in its arp cache in
++   absence of a verification of that relationship (C17)*/
++static unsigned long lec_aging_time = (300*HZ);
++/* Max time the client will maintain an entry in cache when
++   topology change flag is true (C18) */
++static unsigned long lec_forward_delay_time = (15*HZ);
++/* Topology change flag  (C19)*/
++static int lec_topology_change = 0;
++/* Max time the client expects an LE_ARP_REQUEST/LE_ARP_RESPONSE
++   cycle to take (C20)*/
++static unsigned long lec_arp_response_time = (1*HZ);
++/* Time limit ot wait to receive an LE_FLUSH_RESPONSE after the
++   LE_FLUSH_REQUEST has been sent before taking recover action. (C21)*/
++static unsigned long lec_flush_timeout = (4*HZ);
++/* The time since sending a frame to the bus after which the
++   LE Client may assume that the frame has been either discarded or
++   delivered to the recipient (C22) */
++static unsigned long lec_path_switching_delay = (6*HZ);
++
++/* 
++ * Arp table funcs
++ */
++
++#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE -1))
++
++static __inline__ void 
++lec_arp_lock(void)
++{
++        atomic_inc(&lec_arp_lock_var);
++}
++
++static __inline__ void 
++lec_arp_unlock(void)
++{
++        atomic_dec(&lec_arp_lock_var);
++}
++
++/*
++ * Initialization of arp-cache
++ */
++void 
++lec_arp_init(void)
++{
++        unsigned short i;
++
++        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                lec_arp_tables[i] = NULL;
++        }
++        lec_arp_timer.expires = jiffies+LEC_ARP_REFRESH_INTERVAL;
++        lec_freeze = 0;
++        add_timer(&lec_arp_timer);
++}
++
++void
++lec_arp_destroy(void)
++{
++        del_timer(&lec_arp_timer);  
++        lec_freeze = 1;
++        lec_clear_all();
++}
++
++void
++lec_arp_clear_vccs(struct lec_arp_table *entry)
++{
++        if (entry->vcc) {
++                entry->vcc->push = entry->old_push;
++                entry->vcc->flags |= ATM_VF_RELEASED;
++                entry->vcc->flags &= ~ATM_VF_READY;
++                entry->vcc = NULL;
++        }
++        if (entry->recv_vcc) {
++                entry->recv_vcc->push = entry->old_recv_push;
++                entry->recv_vcc->flags |= ATM_VF_RELEASED;
++                entry->recv_vcc->flags &= ~ATM_VF_READY;
++                entry->recv_vcc = NULL;
++        }        
++}
++
++/*
++ * Insert entry to lec_arp_table
++ */
++static __inline__ void 
++lec_arp_put(struct lec_arp_table *to_put)
++{
++        unsigned short place;
++        unsigned long flags;
++
++        save_flags(flags);
++        cli();
++
++        place = HASH(to_put->mac_addr[ETH_ALEN-1]);
++        to_put->next = lec_arp_tables[place];
++        lec_arp_tables[place] = to_put;
++  
++        restore_flags(flags);
++        DPRINTK("LEC_ARP: Added entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
++                0xff&to_put->mac_addr[0], 0xff&to_put->mac_addr[1],
++                0xff&to_put->mac_addr[2], 0xff&to_put->mac_addr[3],
++                0xff&to_put->mac_addr[4], 0xff&to_put->mac_addr[5]);
++        dump_arp_table();
++}
++
++/*
++ * Remove entry from lec_arp_table
++ */
++static __inline__ int 
++lec_arp_remove(struct lec_arp_table *to_remove)
++{
++        unsigned short place;
++        struct lec_arp_table *tmp;
++        unsigned long flags;
++        int remove_vcc=1;
++
++        save_flags(flags);
++        cli();
++
++        if (!to_remove) {
++                restore_flags(flags);
++                return -1;
++        }
++        place = HASH(to_remove->mac_addr[ETH_ALEN-1]);
++        tmp = lec_arp_tables[place];
++        if (tmp == to_remove) {
++                lec_arp_tables[place] = tmp->next;
++        } else {
++                while(tmp && tmp->next != to_remove) {
++                        tmp = tmp->next;
++                }
++                if (!tmp) {/* Entry was not found */
++                        restore_flags(flags);
++                        return -1;
++                }
++        }
++        tmp->next = to_remove->next;
++        del_timer(&to_remove->timer);
++  
++        /* If this is the only MAC connected to this VCC, also tear down
++           the VCC */
++        if (to_remove->status >= ESI_FLUSH_PENDING) {
++                /*
++                 * ESI_FLUSH_PENDING, ESI_FORWARD_DIRECT
++                 */
++                for(place=0;place<LEC_ARP_TABLE_SIZE;place++) {
++                        for(tmp=lec_arp_tables[place];tmp!=NULL;tmp=tmp->next){
++                                if (memcmp(tmp->atm_addr, to_remove->atm_addr,
++                                           ATM_ESA_LEN)==0) {
++                                        remove_vcc=0;
++                                        break;
++                                }
++                        }
++                }
++                if (remove_vcc)
++                        lec_arp_clear_vccs(to_remove);
++        }
++        restore_flags(flags);
++
++        DPRINTK("LEC_ARP: Removed entry:%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
++                0xff&to_remove->mac_addr[0], 0xff&to_remove->mac_addr[1],
++                0xff&to_remove->mac_addr[2], 0xff&to_remove->mac_addr[3],
++                0xff&to_remove->mac_addr[4], 0xff&to_remove->mac_addr[5]);
++
++        dump_arp_table();
++        return 0;
++}
++
++#if DEBUG_ARP_TABLE
++static char*
++get_status_string(unsigned char st)
++{
++        switch(st) {
++        case ESI_UNKNOWN:
++                return "ESI_UNKNOWN";
++        case ESI_ARP_PENDING:
++                return "ESI_ARP_PENDING";
++        case ESI_VC_PENDING:
++                return "ESI_VC_PENDING";
++        case ESI_FLUSH_PENDING:
++                return "ESI_FLUSH_PENDING";
++        case ESI_FORWARD_DIRECT:
++                return "ESI_FORWARD_DIRECT";
++        default:
++                return "<UNKNOWN>";
++        }
++}
++#endif
++
++void
++dump_arp_table(void)
++{
++#if DEBUG_ARP_TABLE
++        int i,j, offset;
++        struct lec_arp_table *rulla;
++        char buf[512];
++
++        printk("Dump:\n");
++        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                rulla = lec_arp_tables[i];
++                offset = 0;
++                offset += sprintf(buf,"%d: %p\n",i, rulla);
++                while (rulla) {
++                        offset += sprintf(buf+offset,"Mac:");
++                        for(j=0;j<ETH_ALEN;j++) {
++                                offset+=sprintf(buf+offset,
++                                                "%2.2x ",
++                                                rulla->mac_addr[j]&0xff);
++                        }
++                        offset +=sprintf(buf+offset,"Atm:");
++                        for(j=0;j<ATM_ESA_LEN;j++) {
++                                offset+=sprintf(buf+offset,
++                                                "%2.2x ",
++                                                rulla->atm_addr[j]&0xff);
++                        }      
++                        offset+=sprintf(buf+offset,
++                                        "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
++                                        rulla->vcc?rulla->vcc->vpi:0, 
++                                        rulla->vcc?rulla->vcc->vci:0,
++                                        rulla->recv_vcc?rulla->recv_vcc->vpi:0,
++                                        rulla->recv_vcc?rulla->recv_vcc->vci:0,
++                                        rulla->last_used,
++                                        rulla->timestamp, rulla->no_tries);
++                        offset+=sprintf(buf+offset,
++                                        "Flags:%x, Packets_flooded:%x, Status: %s ",
++                                        rulla->flags, rulla->packets_flooded, 
++                                        get_status_string(rulla->status));
++                        offset+=sprintf(buf+offset,"->%p\n",rulla->next);
++                        rulla = rulla->next;
++                }
++                printk("%s",buf);
++        }
++        rulla = lec_arp_empty_ones;
++        if (rulla)
++                printk("Empty ones\n");  
++        while(rulla) {
++                offset=0;
++                offset += sprintf(buf+offset,"Mac:");
++                for(j=0;j<ETH_ALEN;j++) {
++                        offset+=sprintf(buf+offset,"%2.2x ",
++                                        rulla->mac_addr[j]&0xff);
++                }
++                offset +=sprintf(buf+offset,"Atm:");
++                for(j=0;j<ATM_ESA_LEN;j++) {
++                        offset+=sprintf(buf+offset,"%2.2x ",
++                                        rulla->atm_addr[j]&0xff);
++                }      
++                offset+=sprintf(buf+offset,
++                                "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
++                                rulla->vcc?rulla->vcc->vpi:0, 
++                                rulla->vcc?rulla->vcc->vci:0, 
++                                rulla->recv_vcc?rulla->recv_vcc->vpi:0,
++                                rulla->recv_vcc?rulla->recv_vcc->vci:0,
++                                rulla->last_used, 
++                                rulla->timestamp, rulla->no_tries);
++                offset+=sprintf(buf+offset,
++                                "Flags:%x, Packets_flooded:%x, Status: %s ",
++                                rulla->flags, rulla->packets_flooded, 
++                                get_status_string(rulla->status));
++                offset+=sprintf(buf+offset,"->%lx\n",(long)rulla->next);
++                rulla = rulla->next;
++                printk("%s",buf);
++        }
++#endif
++}
++
++
++/* 
++ * Find entry by mac_address
++ */
++static __inline__ struct lec_arp_table*
++lec_arp_find(unsigned char *mac_addr)
++{
++        unsigned short place;
++        struct lec_arp_table *to_return;
++
++        DPRINTK("LEC_ARP: lec_arp_find :%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
++                mac_addr[0]&0xff, mac_addr[1]&0xff, mac_addr[2]&0xff, 
++                mac_addr[3]&0xff, mac_addr[4]&0xff, mac_addr[5]&0xff);
++        lec_arp_lock();
++        place = HASH(mac_addr[ETH_ALEN-1]);
++  
++        to_return = lec_arp_tables[place];
++        while(to_return) {
++                if (memcmp(mac_addr, to_return->mac_addr, ETH_ALEN) == 0) {
++                        to_return->last_used = jiffies;
++                        lec_arp_unlock();
++                        return to_return;
++                }
++                to_return = to_return->next;
++        }
++        lec_arp_unlock();
++        return NULL;
++}
++
++static struct lec_arp_table*
++make_entry(unsigned char *mac_addr)
++{
++        struct lec_arp_table *to_return;
++
++        to_return=(struct lec_arp_table *)kmalloc(sizeof(struct lec_arp_table),
++                                                  GFP_ATOMIC);
++        if (!to_return) {
++                printk("LEC: Arp entry kmalloc failed\n");
++                return NULL;
++        }
++        memset(to_return,0,sizeof(struct lec_arp_table));
++        memcpy(to_return->mac_addr, mac_addr, ETH_ALEN);
++        to_return->timer.function = lec_arp_expire_arp;
++        to_return->timer.data = (unsigned long)to_return;
++        return to_return;
++}
++
++/*
++ *
++ * Arp sent timer expired
++ *
++ */
++static void
++lec_arp_expire_arp(unsigned long data)
++{
++        struct lec_arp_table *entry;
++
++        entry = (struct lec_arp_table *)data;
++        del_timer(&entry->timer);
++
++        if (entry->status == ESI_ARP_PENDING) {
++                send_to_lecd(l_arp_xmt, entry->mac_addr, NULL);
++                entry->no_tries++;
++                if(entry->no_tries <= lec_max_retry_count) {
++                        entry->timer.expires = jiffies + (1*HZ);
++                        add_timer(&entry->timer);
++                } else 
++                        /* For safety */
++                        entry->timer.next = entry->timer.prev = NULL;
++        }
++}
++
++/*
++ *
++ * Unknown/unused vcc expire, remove associated entry
++ *
++ */
++static void
++lec_arp_expire_vcc(unsigned long data)
++{
++        struct lec_arp_table *to_remove = (struct lec_arp_table*)data;
++        struct lec_arp_table *entry = NULL;
++
++        del_timer(&to_remove->timer);
++
++        DPRINTK("LEC_ARP: lec_arp_expire_vcc vpi:%d vci:%d\n",
++                to_remove->vcc?to_remove->vcc->vpi:0,
++                to_remove->vcc?to_remove->vcc->vci:0);
++        if (to_remove == lec_arp_empty_ones)
++                lec_arp_empty_ones = to_remove->next;                
++        else {
++                entry = lec_arp_empty_ones;
++                while (entry && entry->next != to_remove)
++                        entry = entry->next;
++                if (entry)
++                        entry->next = to_remove->next;
++        }
++        if (!entry)
++                if (to_remove == lec_no_forward) {
++                        lec_no_forward = to_remove->next;
++                } else {
++                        entry = lec_no_forward;
++                        while (entry && entry->next != to_remove)
++                                entry = entry->next;
++                        if (entry)
++                                entry->next = to_remove->next;
++                }
++        lec_arp_clear_vccs(to_remove);
++        kfree_s(to_remove, sizeof(struct lec_arp_table));
++}
++
++/*
++ * Expire entries.
++ * 1. Re-set timer
++ * 2. For each entry, delete entries that have aged past the age limit.
++ * 3. For each entry, depending on the status of the entry, perform
++ *    the following maintenance.
++ *    a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the
++ *       tick_count is above the max_unknown_frame_time, clear
++ *       the tick_count to zero and clear the packets_flooded counter
++ *       to zero. This supports the packet rate limit per address
++ *       while flooding unknowns.
++ *    b. If the status is ESI_FLUSH_PENDING and the tick_count is greater
++ *       than or equal to the path_switching_delay, change the status
++ *       to ESI_FORWARD_DIRECT. This causes the flush period to end
++ *       regardless of the progress of the flush protocol.
++ */
++static void
++lec_arp_check_expire(unsigned long dumle)
++{
++        struct lec_arp_table *entry, *next;
++        unsigned long now;
++        unsigned long time_to_check;
++        int i;
++
++        del_timer(&lec_arp_timer);
++
++        DPRINTK("lec_arp_check_expire\n");
++        if (!lec_arp_lock_var) {
++                lec_arp_lock();
++                now = jiffies;
++                for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                        for(entry = lec_arp_tables[i];entry != NULL;) {
++                                if ((entry->flags) & LEC_REMOTE_FLAG && 
++                                    lec_topology_change)
++                                        time_to_check = lec_forward_delay_time;
++                                else
++                                        time_to_check = lec_aging_time;
++
++                                if((now-entry->last_used > time_to_check) && 
++                                   !(entry->flags & LEC_PERMANENT_FLAG)) {
++                                        /* Remove entry */
++                                        printk("LEC:Remove entry1\n");
++                                        del_timer(&entry->timer);
++                                        next = entry->next;      
++                                        lec_arp_remove(entry);
++                                        kfree_s(entry, 
++                                                sizeof(struct lec_arp_table));
++                                        entry = next;
++                                } else {
++                                        /* Something else */
++                                        if ((entry->status == ESI_VC_PENDING ||
++                                             entry->status == ESI_ARP_PENDING) 
++                                            &&
++                                            now-entry->timestamp >= 
++                                            lec_max_unknown_frame_time) {
++                                                entry->timestamp = jiffies;
++                                                entry->packets_flooded = 0;
++                                        }
++                                        if (entry->status == ESI_FLUSH_PENDING 
++                                            &&
++                                            now-entry->timestamp >= 
++                                            lec_path_switching_delay) {
++                                                entry->last_used = jiffies;
++                                                entry->status = 
++                                                        ESI_FORWARD_DIRECT;
++                                        }
++                                        entry = entry->next;
++                                }
++                        }
++                }
++                lec_arp_unlock();
++        }
++        lec_arp_timer.expires = jiffies + LEC_ARP_REFRESH_INTERVAL;
++        if (!lec_freeze)
++                add_timer(&lec_arp_timer);
++}
++
++/*
++ * Remove all entries
++ */
++static void
++lec_clear_all(void)
++{
++        struct lec_arp_table *entry, *next;
++        unsigned long flags;
++        int i;
++        
++        save_flags(flags);
++        cli();
++        
++        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                for(entry =lec_arp_tables[i];entry != NULL; entry=next) {
++                        next = entry->next;
++                        lec_arp_remove(entry);
++                        kfree_s(entry, sizeof(struct lec_arp_table));
++                }
++        }
++        entry = lec_arp_empty_ones;
++        while(entry) {
++                next = entry->next;
++                del_timer(&entry->timer);
++                lec_arp_clear_vccs(entry);
++                kfree_s(entry, sizeof(struct lec_arp_table));
++                entry = next;
++        }
++        lec_arp_empty_ones = NULL;
++        entry = lec_no_forward;
++        while(entry) {
++                next = entry->next;
++                del_timer(&entry->timer);
++                lec_arp_clear_vccs(entry);
++                kfree_s(entry, sizeof(struct lec_arp_table));
++                entry = next;
++        }
++        lec_no_forward = NULL;
++        mcast_vcc = NULL; /* To make sure... */
++        memset(lec_arp_tables, 0, 
++               sizeof(struct lec_arp_table*)*LEC_ARP_TABLE_SIZE);
++        restore_flags(flags);
++}
++
++/* 
++ * Remove all entries not marked permanent
++ */
++void
++lec_reset(void)
++{
++        int i;
++        struct lec_arp_table *entry, *next;
++
++        lec_arp_lock();
++
++        printk("lec_reset\n");
++        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                for(entry=lec_arp_tables[i];entry != NULL; entry=next) {
++                        next = entry->next;
++                        if (!(entry->flags & LEC_PERMANENT_FLAG)) {
++                                lec_arp_remove(entry);
++                                kfree_s(entry, sizeof(struct lec_arp_table));
++                        }
++                }
++        }
++        lec_arp_unlock();
++}
++
++/*
++ * Try to find vcc where mac_address is attached.
++ * 
++ */
++struct atm_vcc*
++lec_arp_resolve(unsigned char *mac_to_find)
++{
++        struct lec_arp_table *entry;
++
++        if (mac_to_find[0]&0x01) {
++                return mcast_vcc;
++        }
++        entry = lec_arp_find(mac_to_find);
++  
++        if (entry) {
++                if (entry->status == ESI_FORWARD_DIRECT) {
++                        /* Connection Ok */
++                        return entry->vcc;
++                }
++                if (entry->status == ESI_UNKNOWN) {
++                        /* We want arp-request to be sent */
++                        entry->status = ESI_ARP_PENDING;
++                        entry->timestamp = jiffies;
++                        entry->no_tries = 1;
++                        entry->packets_flooded=1;
++                        send_to_lecd(l_arp_xmt, mac_to_find, NULL);
++                        del_timer(&entry->timer);
++                        entry->timer.expires = jiffies + (1*HZ);
++                        entry->timer.function = lec_arp_expire_arp;
++                        add_timer(&entry->timer);
++                        return mcast_vcc;
++                }
++                /* Data direct VC not yet set up, check to see if the unknown
++                   frame count is greater than the limit. If the limit has
++                   not been reached, allow the caller to send packet to
++                   BUS. */
++                if (entry->packets_flooded < lec_maximum_unknown_frame_count) {
++                        entry->packets_flooded++;
++                        DPRINTK("LEC_ARP: Flooding..\n");
++                        return mcast_vcc;
++                }
++                return NULL;
++        } else {
++                /* No matching entry was found */
++                entry = make_entry(mac_to_find);
++                DPRINTK("LEC_ARP: Making entry\n");
++                if (!entry) {
++                        return mcast_vcc;
++                }
++                entry->packets_flooded =1;
++                entry->status = ESI_ARP_PENDING;
++                entry->no_tries = 1;
++                entry->last_used = entry->timestamp = jiffies;
++                send_to_lecd(l_arp_xmt, mac_to_find, NULL);
++
++                entry->timer.expires = jiffies + (1*HZ);
++                add_timer(&entry->timer);
++                lec_arp_put(entry);
++                return mcast_vcc;
++        }
++}
++
++/*
++ * Add permanent entry to arp table.
++ */
++struct lec_arp_table*
++lec_add_permanent(unsigned char *mac_addr, unsigned char *atmaddr)
++{
++        struct lec_arp_table *to_add;
++
++        to_add = make_entry(mac_addr);
++        if (!to_add) {
++                return NULL;
++        }
++        memcpy(to_add->atm_addr, atmaddr, ATM_ESA_LEN);
++        to_add->flags |= LEC_PERMANENT_FLAG;
++        to_add->last_used = jiffies;
++        /* Connection forming must be started */
++        send_to_lecd(l_svc_setup, NULL, atmaddr);
++        lec_arp_lock();
++        lec_arp_put(to_add);
++        lec_arp_unlock();
++        return to_add;
++}
++
++int
++lec_addr_delete(unsigned char *atm_addr, unsigned long permanent)
++{
++        struct lec_arp_table *entry, *next;
++        int i;
++
++        lec_arp_lock();
++        printk("lec_addr_delete\n");
++        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                for(entry=lec_arp_tables[i];entry != NULL; entry=next) {
++                        next = entry->next;
++                        if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN)
++                            && (permanent || 
++                                !(entry->flags & LEC_PERMANENT_FLAG)))
++                                lec_arp_remove(entry);
++                        kfree_s(entry, sizeof(struct lec_arp_table));
++                        lec_arp_unlock();
++                        return 0;
++                }
++        }
++        lec_arp_unlock();
++        return -1;
++}
++
++/*
++ * Notifies:  Response to arp_request (atm_addr != NULL) 
++ */
++void
++lec_arp_update(unsigned char *mac_addr,
++              unsigned char *atm_addr,
++              unsigned long remoteflag)
++{
++        struct lec_arp_table *entry, *tmp;
++        int i;
++
++        lec_arp_lock();
++
++        if (lec_arp_empty_ones) {
++                entry = lec_arp_empty_ones;
++                if (!memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN)) {
++                        lec_arp_empty_ones = entry->next;
++                } else {
++                        while(entry->next && memcmp(entry->next->atm_addr, 
++                                                    atm_addr, ATM_ESA_LEN))
++                                entry = entry->next;
++                        if (entry->next) {
++                                tmp = entry;
++                                entry = entry->next;
++                                tmp->next = entry->next;
++                        } else
++                                entry = NULL;
++                        
++                }
++                if (entry) {
++                        del_timer(&entry->timer);
++                        tmp = lec_arp_find(mac_addr);
++                        if (tmp) {
++                                del_timer(&tmp->timer);
++                                tmp->status = ESI_FORWARD_DIRECT;
++                                memcpy(tmp->atm_addr, atm_addr, ATM_ESA_LEN);
++                                tmp->vcc = entry->vcc;
++                                tmp->old_push = entry->old_push;
++                                kfree_s(entry, sizeof(struct lec_arp_table));
++                                entry=tmp;
++                        } else {
++                                entry->status = ESI_FORWARD_DIRECT;
++                                memcpy(entry->mac_addr, mac_addr, ETH_ALEN);
++                                lec_arp_put(tmp);
++                        }
++                        if (remoteflag)
++                                entry->flags|=LEC_REMOTE_FLAG;
++                        else
++                                entry->flags&=~LEC_REMOTE_FLAG;
++                        lec_arp_unlock();
++                        return;
++                }
++        }
++        entry = lec_arp_find(mac_addr);
++        if (!entry) {
++                entry = make_entry(mac_addr);
++                memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN);
++                entry->status = ESI_UNKNOWN;
++                lec_arp_put(entry);
++                /* Temporary, changes before end of function */
++        }
++        memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN);
++        del_timer(&entry->timer);
++        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                for(tmp=lec_arp_tables[i];tmp;tmp=tmp->next) {
++                        if (entry != tmp &&
++                            !memcmp(tmp->atm_addr, atm_addr,
++                                    ATM_ESA_LEN)) { 
++                                /* Vcc to this host exists */
++                                if (tmp->status > ESI_VC_PENDING) {
++                                        /*
++                                         * ESI_FLUSH_PENDING,
++                                         * ESI_FORWARD_DIRECT
++                                         */
++                                        entry->vcc = tmp->vcc;
++                                        entry->old_push=tmp->old_push;
++                                }
++                                entry->status=tmp->status;
++                                break;
++                        }
++                }
++        }
++        if (remoteflag)
++                entry->flags|=LEC_REMOTE_FLAG;
++        else
++                entry->flags&=~LEC_REMOTE_FLAG;
++        if (entry->status == ESI_ARP_PENDING ||
++            entry->status == ESI_UNKNOWN) {
++                entry->status = ESI_VC_PENDING;
++                send_to_lecd(l_svc_setup, NULL, atm_addr);
++        }
++        lec_arp_unlock();
++}
++
++/*
++ * Notifies: Vcc setup ready 
++ */
++void
++lec_vcc_added(struct atmlec_ioc *ioc_data,
++             struct atm_vcc *vcc,
++             void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb))
++{
++        struct lec_arp_table *entry;
++        int i, found_entry=0;
++        unsigned char bus_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff,0xff};
++
++        lec_arp_lock();
++        if (ioc_data->receive == 2) {
++                /* Vcc for BUS distribute */
++                DPRINTK("LEC_ARP: Attaching mcast distribute\n");
++                entry = lec_arp_find(bus_mac);
++                if (!entry) {
++                        printk("LEC_ARP: Multicast entry not found!\n");
++                        lec_arp_unlock();
++                        return;
++                }
++                memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
++                entry->recv_vcc = vcc;
++                entry->old_recv_push = old_push;
++                lec_arp_unlock();
++                return;
++        } else if (ioc_data->receive == 1) {
++                /* Vcc which we don't want to make default vcc, attach it
++                   anyway. */
++                DPRINTK("LEC_ARP:Attaching data direct, not default :%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
++                        ioc_data->atm_addr[0],ioc_data->atm_addr[1],
++                        ioc_data->atm_addr[2],ioc_data->atm_addr[3],
++                        ioc_data->atm_addr[4],ioc_data->atm_addr[5],
++                        ioc_data->atm_addr[6],ioc_data->atm_addr[7],
++                        ioc_data->atm_addr[8],ioc_data->atm_addr[9],
++                        ioc_data->atm_addr[10],ioc_data->atm_addr[11],
++                        ioc_data->atm_addr[12],ioc_data->atm_addr[13],
++                        ioc_data->atm_addr[14],ioc_data->atm_addr[15],
++                        ioc_data->atm_addr[16],ioc_data->atm_addr[17],
++                        ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
++                entry = make_entry(bus_mac);
++                memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
++                memset(entry->mac_addr, 0, ETH_ALEN);
++                entry->recv_vcc = vcc;
++                entry->old_recv_push = old_push;
++                entry->status = ESI_UNKNOWN;
++                entry->timer.expires = jiffies + lec_vcc_timeout_period;
++                entry->timer.function = lec_arp_expire_vcc;
++                entry->next = lec_no_forward;
++                lec_no_forward = entry;
++                lec_arp_unlock();
++                return;
++        }
++        DPRINTK("LEC_ARP:Attaching data direct, default:%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
++                        ioc_data->atm_addr[0],ioc_data->atm_addr[1],
++                        ioc_data->atm_addr[2],ioc_data->atm_addr[3],
++                        ioc_data->atm_addr[4],ioc_data->atm_addr[5],
++                        ioc_data->atm_addr[6],ioc_data->atm_addr[7],
++                        ioc_data->atm_addr[8],ioc_data->atm_addr[9],
++                        ioc_data->atm_addr[10],ioc_data->atm_addr[11],
++                        ioc_data->atm_addr[12],ioc_data->atm_addr[13],
++                        ioc_data->atm_addr[14],ioc_data->atm_addr[15],
++                        ioc_data->atm_addr[16],ioc_data->atm_addr[17],
++                ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
++        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                for (entry = lec_arp_tables[i];entry!=NULL;entry=entry->next) {
++                        if (memcmp(ioc_data->atm_addr, entry->atm_addr, 
++                                   ATM_ESA_LEN)==0) {
++                                DPRINTK("LEC_ARP: Attaching data direct\n");
++                                DPRINTK("Currently -> Vcc: %d, Rvcc:%d\n",
++                                        entry->vcc?entry->vcc->vci:0,
++                                        entry->recv_vcc?entry->recv_vcc:0);
++                                found_entry=1;
++                                del_timer(&entry->timer);
++                                if (vcc) {
++                                        lec_arp_clear_vccs(entry);
++                                }
++                                entry->vcc = vcc;
++                                entry->old_push = old_push;
++                                if (entry->status == ESI_VC_PENDING) {
++                                        if (lec_maximum_unknown_frame_count==0)
++                                                entry->status = 
++                                                        ESI_FORWARD_DIRECT;
++                                        else {
++                                                entry->timestamp = jiffies;
++                                                entry->status = 
++                                                        ESI_FLUSH_PENDING;
++                                                send_to_lecd(l_flush_xmt, 
++                                                             entry->mac_addr, 
++                                                             entry->atm_addr);
++                                        }
++                                } else {
++                                        /* They were forming a connection
++                                           to us, and we to them. Our
++                                           ATM address is numerically lower
++                                           than theirs, so we make connection
++                                           we formed into default VCC (8.1.11).
++                                           Connection they made gets torn
++                                           down. This might confuse some
++                                           clients. Can be changed if
++                                           someone reports trouble... */
++                                        ;
++                                }
++                        }
++                }
++        }
++        if (found_entry) {
++                lec_arp_unlock();
++                dump_arp_table();
++                return;
++        }
++        /* Not found, snatch address from first data packet that arrives from
++           this vcc */
++        entry = make_entry(bus_mac);
++        entry->vcc = vcc;
++        entry->old_push = old_push;
++        memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
++        memset(entry->mac_addr, 0, ETH_ALEN);
++        entry->status = ESI_UNKNOWN;
++        entry->next = lec_arp_empty_ones;
++        lec_arp_empty_ones = entry;
++        entry->timer.expires = jiffies + lec_vcc_timeout_period;
++        entry->timer.function = lec_arp_expire_vcc;
++        add_timer(&entry->timer);
++        lec_arp_unlock();
++}
++
++void
++lec_flush_complete(unsigned char *atm_addr, unsigned long tran_id)
++{
++        struct lec_arp_table *entry;
++        int i;
++  
++        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                for (entry=lec_arp_tables[i];entry!=NULL;entry=entry->next) {
++                        if (memcmp(atm_addr, entry->atm_addr,ATM_ESA_LEN)==0 &&
++                            entry->flush_tran_id == tran_id) {
++                                entry->status = ESI_FORWARD_DIRECT;
++                                return;
++                        }
++                }
++        }
++        printk("LEC_ARP: Flush_complete: entry not found : %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", 
++               0xff&atm_addr[0],0xff&atm_addr[1],0xff&atm_addr[2],
++               0xff&atm_addr[3],0xff&atm_addr[4],0xff&atm_addr[5],
++               0xff&atm_addr[6],0xff&atm_addr[7],0xff&atm_addr[8],
++               0xff&atm_addr[9],0xff&atm_addr[10],0xff&atm_addr[11],
++               0xff&atm_addr[12],0xff&atm_addr[13],0xff&atm_addr[14],
++               0xff&atm_addr[15],0xff&atm_addr[16],0xff&atm_addr[17],
++               0xff&atm_addr[18],0xff&atm_addr[19]);
++}
++
++void 
++lec_topology_flag_change_set(unsigned long topology_change_flag)
++{
++        lec_topology_change = topology_change_flag;  
++}
++
++void
++lec_set_flush_tran_id(unsigned char *mac_addr, unsigned long tran_id)
++{
++        struct lec_arp_table *entry;
++
++        entry = lec_arp_find(mac_addr);
++        if (!entry) {
++                printk("LEC_ARP: Set_flush_tran_id: entry not found\n");
++                return;
++        }
++        entry->flush_tran_id = tran_id;
++}
++
++int 
++lec_mcast_attach(struct atm_vcc *vcc)
++{
++        unsigned char mac_addr[] = {
++                0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++        struct lec_arp_table *to_add;
++  
++        lec_arp_lock();
++        to_add = make_entry(mac_addr);
++        if (!to_add)
++                return -ENOMEM;
++        to_add->status = ESI_FORWARD_DIRECT;
++        to_add->flags |= LEC_PERMANENT_FLAG;
++        to_add->vcc = vcc;  
++        to_add->old_push = vcc->push;
++        vcc->push = lec_push;
++        mcast_vcc = vcc;
++        lec_arp_put(to_add);
++        lec_arp_unlock();
++        return 0;
++}
++
++void
++lec_config(struct atmlec_config_msg *conf)
++{
++        lec_maximum_unknown_frame_count = conf->maximum_unknown_frame_count;
++        lec_max_unknown_frame_time = (conf->max_unknown_frame_time*HZ);
++        lec_max_retry_count = conf->max_retry_count;
++        lec_aging_time = (conf->aging_time*HZ);
++        lec_forward_delay_time = (conf->forward_delay_time*HZ);
++        lec_arp_response_time = (conf->arp_response_time*HZ);
++        lec_flush_timeout = (conf->flush_timeout*HZ);
++        lec_path_switching_delay = (conf->path_switching_delay*HZ);
++}
++
++void
++lec_vcc_close(struct atm_vcc *vcc)
++{
++        struct lec_arp_table *entry, *next;
++        int i;
++
++        DPRINTK("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n",vcc->vpi,vcc->vci);
++        dump_arp_table();
++        lec_arp_lock();
++        for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
++                for(entry = lec_arp_tables[i];entry!=NULL; entry=next) {
++                        next = entry->next;
++                        if (vcc == entry->vcc) {
++                                lec_arp_remove(entry);
++                                kfree_s(entry, sizeof(struct lec_arp_table));
++                                if (mcast_vcc == vcc) {
++                                        mcast_vcc = NULL;
++                                }
++                        } else if (vcc == entry->recv_vcc) { 
++                                /* Bus distribution closed */
++                                mcast_vcc = NULL;
++                                lec_arp_remove(entry);
++                                lec_arp_unlock();
++                                vcc->push(vcc, NULL);
++                                return;
++                        }
++                }
++        }
++        entry=lec_arp_empty_ones;
++        while(entry && entry->vcc==vcc) {
++                lec_arp_clear_vccs(entry);
++                lec_arp_empty_ones=entry->next;
++                kfree_s(entry, sizeof(struct lec_arp_table));
++                entry=lec_arp_empty_ones;
++        }
++        for(;entry!=NULL;entry=next) {
++                next=entry->next;
++                if (vcc == next->vcc) {
++                        lec_arp_clear_vccs(next);
++                        entry->next = next->next;
++                        kfree_s(next, sizeof(struct lec_arp_table));
++                }
++        }
++        entry=lec_no_forward;
++        while(entry && entry->recv_vcc==vcc) {
++                lec_arp_clear_vccs(entry);
++                lec_no_forward=entry->next;
++                kfree_s(entry, sizeof(struct lec_arp_table));
++                entry=lec_no_forward;
++        }
++        for(;entry!=NULL;entry=next) {
++                next=entry->next;
++                if (vcc == next->recv_vcc) {
++                        lec_arp_clear_vccs(next);
++                        entry->next = next->next;
++                        kfree_s(next, sizeof(struct lec_arp_table));
++                }
++        }
++        lec_arp_unlock();
++}
++
++void
++lec_arp_check_empties(struct atm_vcc *vcc, struct sk_buff *skb)
++{
++        struct lec_arp_table *entry, *prev;
++        struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data;
++  
++        lec_arp_lock();
++        entry = lec_arp_empty_ones;
++        if (vcc == entry->vcc) {
++                del_timer(&entry->timer);
++                memcpy(entry->mac_addr, hdr->h_source, ETH_ALEN);
++                entry->status = ESI_FORWARD_DIRECT;
++                lec_arp_empty_ones = entry->next;
++                /* We might have got an entry */
++                if ((prev=lec_arp_find(hdr->h_source))) {
++                        lec_arp_remove(prev);
++                        kfree_s(prev, sizeof(struct lec_arp_table));
++                }
++                lec_arp_put(entry);
++                lec_arp_unlock();
++                return;
++        }
++        prev = entry;
++        entry = entry->next;
++        while (entry && entry->vcc != vcc) {
++                prev= entry;
++                entry = entry->next;
++        }
++        if (!entry) {
++                DPRINTK("LEC_ARP: Arp_check_empties: entry not found!\n");
++                lec_arp_unlock();
++                return;
++        }
++        del_timer(&entry->timer);
++        memcpy(entry->mac_addr, hdr->h_source, ETH_ALEN);
++        entry->status = ESI_FORWARD_DIRECT;
++        prev->next = entry->next;
++        if ((prev = lec_arp_find(hdr->h_source))) {
++                lec_arp_remove(prev);
++                kfree_s(prev, sizeof(struct lec_arp_table));
++        }
++        lec_arp_put(entry);
++        lec_arp_unlock();  
++}
++
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/net/atm/lec_arpc.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,104 @@
++/*
++ * Lec arp cache
++ * Marko Kiiskila carnil@cs.tut.fi
++ *
++ */
++#ifndef _LEC_ARP_H
++#define _LEC_ARP_H
++#include <linux/atm.h>
++#include <linux/atmdev.h>
++#include <linux/if_ether.h>
++#include <linux/atmlec.h>
++
++struct lec_arp_table {
++        struct lec_arp_table *next;          /* Linked entry list */
++        unsigned char atm_addr[ATM_ESA_LEN]; /* Atm address */
++        unsigned char mac_addr[ETH_ALEN];    /* Mac address */
++        struct atm_vcc *vcc;                 /* Vcc this entry is attached */
++        struct atm_vcc *recv_vcc;            /* Vcc we receive data from */
++        void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb); 
++                                             /* Push that leads to daemon */
++        void (*old_recv_push)(struct atm_vcc *vcc, struct sk_buff *skb);
++                                             /* Push that leads to daemon */
++        void (*old_close)(struct atm_vcc *vcc);
++                                             /* We want to see when this
++                                              * vcc gets closed */
++        unsigned long last_used;             /* For expiry */
++        unsigned long timestamp;             /* Used for various timestamping
++                                              * things:
++                                              * 1. FLUSH started 
++                                              *    (status=ESI_FLUSH_PENDING)
++                                              * 2. Counting to 
++                                              *    max_unknown_frame_time
++                                              *    (status=ESI_ARP_PENDING||
++                                              *     status=ESI_VC_PENDING)
++                                              */
++        unsigned char no_tries;              /* No of times arp retry has been 
++                                                tried */
++        unsigned char status;                /* Status of this entry */
++        unsigned short flags;                /* Flags for this entry */
++        unsigned short packets_flooded;      /* Data packets flooded */
++        unsigned long flush_tran_id;         /* Transaction id in flush protocol */
++        struct timer_list timer;             /* Arping timer */
++};
++
++/* Status fields */
++#define ESI_UNKNOWN 0       /*
++                             * Next packet sent to this mac address
++                             * causes ARP-request to be sent 
++                             */
++#define ESI_ARP_PENDING 1   /*
++                             * There is no ATM address associated with this
++                             * 48-bit address.  The LE-ARP protocol is in
++                             * progress.
++                             */
++#define ESI_VC_PENDING 2    /*
++                             * There is a valid ATM address associated with 
++                             * this 48-bit address but there is no VC set 
++                             * up to that ATM address.  The signaling 
++                             * protocol is in process.
++                             */
++#define ESI_FLUSH_PENDING 4 /*
++                             * The LEC has been notified of the FLUSH_START
++                             * status and it is assumed that the flush 
++                             * protocol is in process.
++                             */
++#define ESI_FORWARD_DIRECT 5 /*
++                              * Either the Path Switching Delay (C22) has 
++                              * elapsed or the LEC has notified the Mapping 
++                              * that the flush protocol has completed.  In 
++                              * either case, it is safe to forward packets 
++                              * to this address via the data direct VC.
++                              */
++
++/* Flag values */
++#define LEC_REMOTE_FLAG      0x0001
++#define LEC_PERMANENT_FLAG   0x0002
++
++/* Hash table size */
++#define LEC_ARP_TABLE_SIZE 16
++
++/* Protos */
++void lec_reset(void);
++void lec_arp_init(void);
++int lec_mcast_attach(struct atm_vcc *vcc);
++void lec_arp_destroy(void);
++void lec_vcc_close(struct atm_vcc *vcc);
++
++struct atm_vcc *lec_arp_resolve(unsigned char *mac_to_addr);
++void lec_vcc_added(struct atmlec_ioc *ioc_data, struct atm_vcc *vcc,
++                  void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb));
++void lec_arp_check_empties(struct atm_vcc *vcc, struct sk_buff *skb);
++struct lec_arp_table *lec_add_permanent(unsigned char *mac_addr, 
++                                       unsigned char *atmaddr);
++int lec_addr_delete(unsigned char *mac_addr, unsigned long permanent);
++void lec_topology_flag_change_set(unsigned long topology_change_flag);
++void lec_flush_complete(unsigned char *atm_addr, unsigned long tran_id);
++void lec_arp_update(unsigned char *mac_addr, unsigned char *atm_addr,
++                   unsigned long remoteflag);
++void lec_set_flush_tran_id(unsigned char *mac_addr, unsigned long tran_id);
++void lec_config(struct atmlec_config_msg *conf);
++void lec_arp_check(unsigned char *to_check);
++void lec_proc_info(char *buf);
++
++#endif
+--- ref/include/net/route.h	Sun Jun  9 11:28:48 1996
++++ work/include/net/route.h	Wed Jul 31 19:33:37 1996
+@@ -26,6 +26,7 @@
+ #define _ROUTE_H
+ 
+ #include <linux/config.h>
++#include <net/sock.h>
+ 
+ /*
+  * 0 - no debugging messages
+@@ -158,11 +159,17 @@
+ #endif
+ #endif
+ 
++
+ extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
+ 						       __u32 daddr, int local)
+ {
+ 	struct rtable * rt = *rp;
+ 
++#ifdef CONFIG_AREQUIPA
++	extern struct device *arequipa_dev;
++
++	if (rt && rt->rt_dev == arequipa_dev) return rt;
++#endif
+ 	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
+ 	    || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
+ 	{
+@@ -172,6 +179,35 @@
+ 	}
+ 	return rt;
+ }	
++
++
++extern __inline__ void set_rt_cache(struct sock *sk,struct rtable *rt)
++{
++#ifndef CONFIG_AREQUIPA
++	sk->ip_route_cache = rt;
++#else
++	extern struct rtable arequipa_rt;
++
++	if (!sk->arequipa) {
++		sk->ip_route_cache = rt;
++		return;
++	}
++	if (!sk->aq_route) {
++		printk(KERN_CRIT "set_rt_cache: !sk->aq_route\n");
++		sk->ip_route_cache = rt;
++		return;
++	}
++#if 0 /* only needed if preloading the route cache for Arequipa */
++        if (sk->ip_route_cache) ip_rt_put(sk->ip_route_cache);
++#endif
++        sk->ip_route_cache = NULL;
++        if (!rt) return;
++	*sk->aq_route = arequipa_rt;
++        sk->ip_route_cache = sk->aq_route;
++	sk->aq_route->rt_src = rt->rt_src;
++	ip_rt_put(rt);
++#endif
++}
+ 
+ 
+ #endif	/* _ROUTE_H */
+--- ref/include/net/sock.h	Sun Jun  9 11:25:41 1996
++++ work/include/net/sock.h	Wed Jul 31 19:30:44 1996
+@@ -251,8 +251,8 @@
+ 	unsigned char		max_ack_backlog;
+ 	unsigned char		priority;
+ 	unsigned char		debug;
+-	unsigned short		rcvbuf;
+-	unsigned short		sndbuf;
++	unsigned long		rcvbuf;
++	unsigned long		sndbuf;
+ 	unsigned short		type;
+ 	unsigned char		localroute;	/* Route locally only */
+ #ifdef CONFIG_AX25
+@@ -301,7 +301,13 @@
+ 	int			ip_mc_loop;		/* Loopback */
+ 	char			ip_mc_name[MAX_ADDR_LEN];/* Multicast device name */
+ 	struct ip_mc_socklist	*ip_mc_list;		/* Group array */
+-#endif  
++#endif
++#ifdef CONFIG_AREQUIPA
++	struct socket		*arequipa;   /* dedicated link layer socket,
++						NULL if not connected */
++	struct rtable		*aq_route;   /* buffer for fake route, NULL
++						if neither present nor expect */
++#endif
+ 
+ /*
+  *	This part is used for the timeout functions (timer.c). 
+--- /dev/null	Mon Jul 18 01:46:18 1994
++++ work/include/linux/arequipa.h	Wed Jul 31 19:34:38 1996
+@@ -0,0 +1,45 @@
++/* arequipa.h - Arequipa interface definitions */
++ 
++/* Written 1996 by Jean-Michel Pittet and Werner Almesberger, EPFL LRC */
++ 
++
++#ifndef _LINUX_AREQUIPA_H
++#define _LINUX_AREQUIPA_H
++
++#include <linux/atmioc.h>
++
++
++#define AREQUIPA_PRESET		_IO('a',ATMIOC_AREQUIPA)
++#define AREQUIPA_INCOMING	_IO('a',ATMIOC_AREQUIPA+1)
++#define AREQUIPA_EXPECT		_IO('a',ATMIOC_AREQUIPA+2)
++#define AREQUIPA_CLOSE		_IO('a',ATMIOC_AREQUIPA+3)
++#define AREQUIPA_CTRL		_IO('a',ATMIOC_AREQUIPA+4)
++#define AREQUIPA_CLS3RD		_IO('a',ATMIOC_AREQUIPA+5)
++
++
++#ifdef __KERNEL__
++
++#include <linux/net.h>
++#include <linux/netdevice.h>
++#include <net/sock.h>
++#include <net/route.h>
++
++
++extern struct atm_vcc *aqd; /* for net/atm/proc.c */
++/* extern struct rtable arequipa_rt; - not needed */
++extern struct device *arequipa_dev;
++
++int atm_init_arequipa(void);
++int arequipa_attach(struct socket *lower,struct sock *upper);
++
++int arequipa_preset(struct socket *lower,struct sock *upper);
++int arequipa_expect(struct sock *upper,int on);
++int arequipa_incoming(struct socket *lower);
++int arequipa_close(struct sock *upper);
++
++int arequipad_attach(struct atm_vcc *vcc);
++void arequipa_close_vcc(struct atm_vcc *vcc);
++
++#endif /* __KERNEL__ */
++
++#endif
+--- ref/net/socket.c	Thu Jun  6 20:22:25 1996
++++ work/net/socket.c	Mon Jun 10 18:30:51 1996
+@@ -778,7 +778,7 @@
+ 		sock_release(newsock);
+ 		return(-EINVAL);
+ 	}
+-	sock->file=current->files->fd[fd];
++	newsock->file=current->files->fd[fd];
+ 	
+ 	if (upeer_sockaddr)
+ 	{
+--- ref/net/ipv4/tcp_input.c	Sun Jun  9 10:39:09 1996
++++ work/net/ipv4/tcp_input.c	Mon Jun 10 17:36:44 1996
+@@ -33,6 +33,11 @@
+ #include <linux/config.h>
+ #include <net/tcp.h>
+ 
++#ifdef CONFIG_AREQUIPA
++#include <linux/atmdev.h>
++#include <linux/arequipa.h>
++#endif
++
+ /*
+  *	Policy code extracted so it's now separate
+  */
+@@ -415,6 +420,24 @@
+ 			return;
+ 		}
+ 	}
++#ifdef CONFIG_AREQUIPA
++	newsk->arequipa = NULL;
++	if (sk->aq_route) {
++		newsk->aq_route = kmalloc(sizeof(struct rtable),GFP_ATOMIC);
++		if (newsk->aq_route) {
++			if (dev == arequipa_dev)
++				(void) arequipa_attach(skb->atm.vcc->sock,
++				    newsk);
++		}
++		else {
++			if (sk->opt) kfree(sk->opt);
++			kfree_s(newsk,sizeof(*sk));
++			tcp_statistics.TcpAttemptFails++;
++			kfree_skb(skb,FREE_READ);
++			return;
++		}
++	}
++#endif
+ 	skb_queue_head_init(&newsk->write_queue);
+ 	skb_queue_head_init(&newsk->receive_queue);
+ 	newsk->send_head = NULL;
+@@ -516,7 +539,7 @@
+ 	 */
+ 
+ 	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+-	newsk->ip_route_cache = rt;
++	set_rt_cache(newsk,rt);
+ 	
+ 	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
+ 		newsk->window_clamp = rt->rt_window;
+@@ -1771,6 +1794,12 @@
+ 		skb->free = 1;
+ 		skb->saddr = daddr;
+ 		skb->daddr = saddr;
++
++#ifdef CONFIG_AREQUIPA
++		if (dev == arequipa_dev && !sk->arequipa && sk->aq_route &&
++		    sk->state == TCP_ESTABLISHED)
++			(void) arequipa_attach(skb->atm.vcc->sock,sk);
++#endif
+ 
+ 		/*
+ 		 * We may need to add it to the backlog here. 
diff -ur --new-file old/atm/doc/usage.tex new/atm/doc/usage.tex
--- old/atm/doc/usage.tex	Fri Jul 19 15:43:12 1996
+++ new/atm/doc/usage.tex	Wed Jul 31 18:25:35 1996
@@ -1,7 +1,7 @@
 %%def%:=
 
 %:\begin{verbatim}
-%:Usage instructions  -  ATM on Linux, release 0.14 (pre-alpha)
+%:Usage instructions  -  ATM on Linux, release 0.15 (pre-alpha)
 %:-------------------------------------------------------------
 %:
 %:\end{verbatim}
@@ -38,14 +38,14 @@
 
 \title{ATM on Linux \\
   User's guide \\
-  Release 0.14 (pre-alpha)}
+  Release 0.15 (pre-alpha)}
 \author{Werner Almesberger \\
   {\tt werner.almesberger@lrc.di.epfl.ch} \\
   \\
   Laboratoire de R\'eseaux de Communication (LRC) \\
   EPFL, CH-1015 Lausanne, Switzerland}
  
-\date{July 19, 1996}
+\date{July 31, 1996}
 
 \begin{document}
 \maketitle
@@ -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.14.tar.gz}
+    \url{ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.15.tar.gz}
   \item the Linux kernel, version 2.0, e.g. from
 \url{ftp://ftp.cs.helsinki.fi/pub/Software/Linux/Kernel/v2.0/linux-2.0.tar.gz}
   \item Perl, version 4 or 5
@@ -96,7 +96,7 @@
 distribution:
 
 \begin{verbatim}
-tar xfz atm-0.14.tar.gz
+tar xfz atm-0.15.tar.gz
 \end{verbatim}
 
 and the kernel source:
@@ -109,7 +109,7 @@
 
 \begin{verbatim}
 cd linux
-patch -s -p1 <../atm/atm-0.14.patch
+patch -s -p1 <../atm/atm.patch
 \end{verbatim}
 
 The distribution installs into the following directories:
@@ -117,8 +117,8 @@
 \begin{description}
   \item[\path{atm/}] Documentation in ASCII format, kernel patch, top-level
     \name{Makefile}, and distribution scripts
-  \item[\path{atm/sigd/}] UNI 3.0 and UNI 3.1 signaling demon: \name{atmsigd},
-    test program: \name{svc}
+  \item[\path{atm/sigd/}] UNI 3.0 and UNI 3.1 signaling demon: \name{atmsigd}
+  \item[\path{atm/saal/}] Signaling AAL library (SSCOP, SSCF, and SAAL)
   \item[\path{atm/qgen/}] Q.2931-style message handling
   \item[\path{atm/ilmid/}] ILMI address registration demon: \name{ilmid}
   \item[\path{atm/maint/}] ATM maintenance programs: \name{atmaddr},
@@ -160,9 +160,9 @@
 \end{verbatim}
 
 The ``MMU hacks'' add single-copy support for raw AAL5 on adapters whose
-driver supports this (currently only the ENI155p). It's generally a good
-idea to enable extended debugging, although this might slow down the
-drivers a bit.
+driver supports this (currently only the ENI155p). Extended debugging should
+only be enabled when tracing specific problems, because it slows down the
+drivers, and it may introduce new race conditions.
 
 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}.
@@ -223,7 +223,8 @@
 \end{verbatim}
 
 \verb"make install" will install executables in the directory
-\path{/usr/local/bin}. Libraries and header files are installed in
+\path{/usr/local/bin} and \path{/usr/local/sbin}, respectively.
+Libraries and header files are installed in
 \path{/usr/lib} and \path{/usr/include}, respectively. Man pages are
 installed in \path{/usr/local/man}.
 
@@ -452,9 +453,9 @@
 \url{ftp://ftp.sgi.com/sgi/src/ttcp}. The following options have been added:
 
 \begin{description}
-  \item[\raw{-a}] use native ATM instead of UDP/TCP. (The address must be in
-    the format \raw{$[$\meta{itf}.$]$\meta{vpi}.\meta{vci}} for PVCs, or a valid
-    ATM end system address for SVCs.)
+  \item[\raw{-a}] use native ATM instead of UDP/TCP. The address must be in
+    the format \raw{$[$\meta{itf}.$]$\meta{vpi}.\meta{vci}} for PVCs, or a
+    valid ATM end system address for SVCs.
   \item[\raw{-P \meta{num}}] use a CBR connection with a peak cell rate of
     \meta{num} cells per second. Default is to use UBR.
   \item[\raw{-C}] disable (UDP) checksums
@@ -578,9 +579,14 @@
 If \name{atmsigd} is killed, all system calls requiring interaction with it
 will return with an error and set \name{errno} to \name{EUNATCH}.
 
-Note that \name{atmsigd} provides only very limited functionality and not all
-messages conform to UNI 3.1 yet. Signaling is known to work for Classical
-IP over ATM with a Fore ASX-200 switch.
+Note that \name{atmsigd} provides only very limited functionality.
+Signaling is known to work for Classical IP over ATM with a Fore ASX-200
+switch.
+
+By default, \name{atmsigd} is configured to conform to UNI 3.0. It can be
+compiled for UNI 3.1 by changing the \raw{STANDARDS=} line at the beginning
+of \path{atm/Rules.make}, and running \raw{make clean; make} in
+\path{atm/qgen} and \path{atm/sigd} (in this order).
 
 Note that \name{atmsigd} is configured to be paranoid. If it detects unusual
 problems, it frequently terminates. This will (obviously) change in the
@@ -590,24 +596,15 @@
 the current directory.
 %%% {\bf NOT YET DOCUMENTED}
 
-The \name{svc} program is used to test SVCs:
-
-\begin{command}
-svc \meta{atm\_address}
-\end{command}
-
-attempts to set up a call to the specified destination (BHLI and BLLI
-are omitted, so most end systems will refuse such calls).
-
-\begin{command}
-svc
-\end{command}
-
-listens for incoming calls (without BHLI nor BLLI) and accepts them.
-
 
 \subsection{ILMI demon}
 
+ILMI provides a mechanism for automatic address configuration. If there is
+no switch or if the switch doesn't support ILMI, the ATM addresses must
+be configured manually (see section \ref{atmaddr}). Note that the ILMI
+demon should not be used on interfaces where addresses are manually
+configured.
+
 The ILMI demon is started as follows:
 
 \begin{command}
@@ -645,7 +642,8 @@
 \label{atmaddr}
 
 If your switch doesn't support ILMI, you have to set the ATM address
-manually on the switch and on the PC(s). Under Linux, use the
+manually on the switch and on the PC(s). On the Linux side, make sure that
+\name{ilmid} doesn't run, then use the
 \name{atmaddr} command to set the address:
 
 \begin{command}
@@ -960,8 +958,7 @@
 Arequipa (Application REQUested IP over ATM, \cite{Arequipa}) is an extension
 to IP over ATM that adds support for using direct ATM connections (with QOS
 parameters, etc.) to carry traffic between INET sockets. On a system that
-should accept incoming Arequipa connections, the Arequipa demon has to be
-started:
+should use Arequipa connections, the Arequipa demon has to be started:
 
 \begin{command}
 \# arequipad \[-b\] \[-d\] \[-n\]
@@ -977,6 +974,9 @@
 
 The status of Arequipa connections is shown in \path{/proc/atm/arequipa}.
 
+If \name{atmsigd} is not running, all attempts to use Arequipa fail and
+the ioctls set \name{errno} to \name{EUNATCH}.
+
 The following example illustrates the use of Arequipa with \name{ttcp\_atm}.
 A machine ``a'' sends data to a machine ``b'', which is also registered with
 that name in the DNS. Furthermore, ``b'' has an entry with the name ``b-atm''
@@ -990,6 +990,7 @@
 then run on ``a'':
 
 \begin{verbatim}
+# arequipad -b
 # ttcp_atm -t -s -Q b-atm b
 \end{verbatim}
 
diff -ur --new-file old/atm/doc/usage.txt new/atm/doc/usage.txt
--- old/atm/doc/usage.txt	Fri Jul 19 15:43:40 1996
+++ new/atm/doc/usage.txt	Wed Jul 31 18:58:08 1996
@@ -1,4 +1,4 @@
-Usage instructions  -  ATM on Linux, release 0.14 (pre-alpha)
+Usage instructions  -  ATM on Linux, release 0.15 (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.14.tar.gz  
+    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.15.tar.gz  
   - the Linux kernel, version 2.0, e.g. from  
     ftp://ftp.cs.helsinki.fi/pub/Software/Linux/Kernel/v2.0/linux-2.0.tar.gz  
   - Perl, version 4 or 5 
@@ -31,7 +31,7 @@
 all the files listed above there. Then extract the ATM on Linux 
 distribution:
 
-tar xfz atm-0.14.tar.gz
+tar xfz atm-0.15.tar.gz
 
 and the kernel source:
 
@@ -40,14 +40,14 @@
 Finally, you can extract the ATM-related patches:
 
 cd linux
-patch -s -p1 <../atm/atm-0.14.patch
+patch -s -p1 <../atm/atm.patch
 
 The distribution installs into the following directories:
 
   atm/  Documentation in ASCII format, kernel patch, top-level Makefile, 
     and distribution scripts 
-  atm/sigd/  UNI 3.0 and UNI 3.1 signaling demon: atmsigd, test program: 
-    svc 
+  atm/sigd/  UNI 3.0 and UNI 3.1 signaling demon: atmsigd 
+  atm/saal/  Signaling AAL library (SSCOP, SSCF, and SAAL) 
   atm/qgen/  Q.2931-style message handling 
   atm/ilmid/  ILMI address registration demon: ilmid 
   atm/maint/  ATM maintenance programs: atmaddr, atmdiag, atmdump, atmtcp, 
@@ -84,9 +84,9 @@
   Enable extended debugging (CONFIG_ATM_TNETA1570_DEBUG)
 
 The "MMU hacks" add single-copy support for raw AAL5 on adapters whose 
-driver supports this (currently only the ENI155p). It's generally a good 
-idea to enable extended debugging, although this might slow down the 
-drivers a bit.
+driver supports this (currently only the ENI155p). Extended debugging 
+should only be enabled when tracing specific problems, because it slows 
+down the drivers, and it may introduce new race conditions.
 
 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 .
@@ -139,9 +139,10 @@
 make
 make install
 
-make install will install executables in the directory /usr/local/bin. 
-Libraries and header files are installed in /usr/lib and /usr/include, 
-respectively. Man pages are installed in /usr/local/man.
+make install will install executables in the directory /usr/local/bin and 
+/usr/local/sbin, respectively. Libraries and header files are installed in 
+/usr/lib and /usr/include, respectively. Man pages are installed in 
+/usr/local/man.
 
 
 Device setup
@@ -345,9 +346,9 @@
 with this package. The original is on  ftp://ftp.sgi.com/sgi/src/ttcp . The 
 following options have been added:
 
-   -a   use native ATM instead of UDP/TCP. (The address must be in the 
+   -a   use native ATM instead of UDP/TCP. The address must be in the 
     format  [<itf>.]<vpi>.<vci>  for PVCs, or a valid ATM end system 
-    address for SVCs.) 
+    address for SVCs. 
    -P <num>   use a CBR connection with a peak cell rate of <num> cells per 
     second. Default is to use UBR. 
    -C   disable (UDP) checksums 
@@ -456,9 +457,13 @@
 If atmsigd is killed, all system calls requiring interaction with it will 
 return with an error and set errno to EUNATCH.
 
-Note that atmsigd provides only very limited functionality and not all 
-messages conform to UNI 3.1 yet. Signaling is known to work for Classical 
-IP over ATM with a Fore ASX-200 switch.
+Note that atmsigd provides only very limited functionality. Signaling is 
+known to work for Classical IP over ATM with a Fore ASX-200 switch.
+
+By default, atmsigd is configured to conform to UNI 3.0. It can be compiled 
+for UNI 3.1 by changing the  STANDARDS=  line at the beginning of 
+atm/Rules.make, and running  make clean; make  in atm/qgen and atm/sigd (in 
+this order).
 
 Note that atmsigd is configured to be paranoid. If it detects unusual 
 problems, it frequently terminates. This will (obviously) change in the 
@@ -467,21 +472,16 @@
 atmsigd also looks for a configuration file atmsigd.conf in the current 
 directory.
 
-The svc program is used to test SVCs:
-
-  svc <atm_address>  
-
-attempts to set up a call to the specified destination (BHLI and BLLI are 
-omitted, so most end systems will refuse such calls).
-
-  svc  
-
-listens for incoming calls (without BHLI nor BLLI) and accepts them.
-
 
 ILMI demon
 ----------
 
+ILMI provides a mechanism for automatic address configuration. If there is 
+no switch or if the switch doesn't support ILMI, the ATM addresses must be 
+configured manually (see section "Manual address configuration"). Note that 
+the ILMI demon should not be used on interfaces where addresses are 
+manually configured.
+
 The ILMI demon is started as follows:
 
   # ilmid [-b] [-d] [-l <logfile>] [-x] [<itf>]  
@@ -511,8 +511,8 @@
 ----------------------------
 
 If your switch doesn't support ILMI, you have to set the ATM address 
-manually on the switch and on the PC(s). Under Linux, use the atmaddr 
-command to set the address:
+manually on the switch and on the PC(s). On the Linux side, make sure that 
+ilmid doesn't run, then use the atmaddr command to set the address:
 
   # atmaddr -a [<itf>] <atm_address>  
 
@@ -777,8 +777,7 @@
 Arequipa (Application REQUested IP over ATM, [6]) is an extension to IP 
 over ATM that adds support for using direct ATM connections (with QOS 
 parameters, etc.) to carry traffic between INET sockets. On a system that 
-should accept incoming Arequipa connections, the Arequipa demon has to be 
-started:
+should use Arequipa connections, the Arequipa demon has to be started:
 
   # arequipad [-b] [-d] [-n]  
 
@@ -789,6 +788,9 @@
 
 The status of Arequipa connections is shown in /proc/atm/arequipa.
 
+If atmsigd is not running, all attempts to use Arequipa fail and the ioctls 
+set errno to EUNATCH.
+
 The following example illustrates the use of Arequipa with ttcp_atm. A 
 machine "a" sends data to a machine "b", which is also registered with that 
 name in the DNS. Furthermore, "b" has an entry with the name "b-atm" in the 
@@ -799,6 +801,7 @@
 
 then run on "a":
 
+# arequipad -b
 # ttcp_atm -t -s -Q b-atm b
 
 
diff -ur --new-file old/atm/ip/clip.c new/atm/ip/clip.c
--- old/atm/ip/clip.c	Tue Jun 11 12:17:46 1996
+++ new/atm/ip/clip.c	Mon Jul 29 13:34:00 1996
@@ -28,7 +28,7 @@
     struct sockaddr_atmpvc addr;
     struct atm_qos qos;
     const char *name;
-    int s,null;
+    int s,null,itf;
 
     name = argv[0];
     null = 0;
@@ -44,10 +44,6 @@
 	perror("socket");
 	return 1;
     }
-    if (ioctl(s,CLIP_PVC,0) < 0) {
-	perror("ioctl CLIP_PVC");
-	return 1;
-    }
     memset(&addr,0,sizeof(addr));
     if (text2atm(argv[1],(struct sockaddr *) &addr,sizeof(addr),T2A_PVC) < 0)
 	usage(argv[0]);
@@ -68,10 +64,15 @@
 	perror("bind");
 	return 1;
     }
+    if ((itf = ioctl(s,CLIP_PVC,0)) < 0) {
+	perror("ioctl CLIP_PVC");
+	return 1;
+    }
     if (null)
 	if (ioctl(s,CLIP_NULENCAP,0) < 0) {
 	    perror("ioctl CLIP_NULENCAP");
 	    return 1;
 	}
+    printf("atm%d\n",itf);
     return 0;
 }
diff -ur --new-file old/atm/lib/arequipa.c new/atm/lib/arequipa.c
--- old/atm/lib/arequipa.c	Thu Jun  6 13:16:53 1996
+++ new/atm/lib/arequipa.c	Wed Jul 31 15:47:49 1996
@@ -19,20 +19,23 @@
     struct atm_blli blli;
     int s;
 
-    if ((s = socket(PF_ATMSVC,SOCK_DGRAM,ATM_AAL5)) < 0) return -1;
+    if ((s = socket(addr->sas_family,SOCK_DGRAM,ATM_AAL5)) < 0) return -1;
     if (setsockopt(s,SOL_ATM,SO_ATMQOS,qos,sizeof(struct atm_qos)) < 0) {
 	(void) close(s);
 	return -1;
     }
     a = *addr;
-    a.sas_addr.bhli.hl_type = ATM_HL_USER;
-    a.sas_addr.bhli.hl_length = strlen("AREQ");
-    strcpy(a.sas_addr.bhli.hl_info,"AREQ");
-    a.sas_addr.blli = &blli;
-    blli.l2_proto = ATM_L2_ISO8802;
-    blli.l3_proto = ATM_L3_NONE;
-    blli.next = NULL;
-    if (connect(s,(struct sockaddr *) &a,sizeof(a)) < 0) {
+    if (a.sas_family == AF_ATMSVC) {
+	a.sas_addr.bhli.hl_type = ATM_HL_USER;
+	a.sas_addr.bhli.hl_length = strlen("AREQ");
+	strcpy(a.sas_addr.bhli.hl_info,"AREQ");
+	a.sas_addr.blli = &blli;
+	blli.l2_proto = ATM_L2_ISO8802;
+	blli.l3_proto = ATM_L3_NONE;
+	blli.next = NULL;
+    }
+    if (connect(s,(struct sockaddr *) &a,a.sas_family == AF_ATMSVC ? sizeof(a)
+      : sizeof(struct sockaddr_atmpvc)) < 0) {
 	(void) close (s);
 	return -1;
     }
diff -ur --new-file old/atm/lib/atm2text.c new/atm/lib/atm2text.c
--- old/atm/lib/atm2text.c	Thu Mar 14 11:59:59 1996
+++ new/atm/lib/atm2text.c	Fri Jul 19 18:01:50 1996
@@ -73,6 +73,10 @@
     if (!*addr->sas_addr.pub && !*addr->sas_addr.prv) return FATAL;
     if (*addr->sas_addr.pub) {
 	len = strlen(addr->sas_addr.pub);
+	if (!*addr->sas_addr.prv && length >= len+2) {
+	    *buffer++ = '+';
+	    length--;
+	}
 	if (length < len+1) return FATAL;
 	strcpy(buffer,addr->sas_addr.pub);
 	buffer += len;
diff -ur --new-file old/atm/lib/text2atm.c new/atm/lib/text2atm.c
--- old/atm/lib/text2atm.c	Thu Apr 25 20:52:05 1996
+++ new/atm/lib/text2atm.c	Fri Jul 19 17:47:57 1996
@@ -164,7 +164,7 @@
 {
     int i,dot,result;
 
-    if (*text == ':') text++;
+    if (*text == ':' || *text == '+') text++;
     for (i = dot = 0; *text; text++)
 	if (isdigit(*text)) {
 	    if (i == ATM_E164_LEN) return TRY_OTHER; /* too long */
diff -ur --new-file old/atm/maint/zntune.c new/atm/maint/zntune.c
--- old/atm/maint/zntune.c	Tue Jun 11 19:48:25 1996
+++ new/atm/maint/zntune.c	Tue Jul 30 22:35:01 1996
@@ -92,13 +92,13 @@
 		return 1;
 	    }
 	    if (first)
-		printf("Pool Size Ref Low High Alarm Under Offs NxOf Count "
+		printf("Pool Size Ref Low High Alarm   Under Offs NxOf Count "
 		  "Thres\n");
 	    printf(" %2d  ",i);
 	    size = 64 << (i < 2 ? 0 : i-2);
 	    if (size < 1024) printf("%4d",size);
 	    else printf("%3dk",size >> 10);
-	    printf(" %3d %3d %4d %5d %5d %4d %4d%6d%6d\n",
+	    printf(" %3d %3d %4d%8d%6d %4d %4d%6d%6d\n",
 	      req.info.ref_count,req.info.low_water,req.info.high_water,
 	      req.info.rqa_count,req.info.rqu_count,req.info.offset,
 	      req.info.next_off,req.info.next_cnt,req.info.next_thres);
diff -ur --new-file old/atm/mkdiff new/atm/mkdiff
--- old/atm/mkdiff	Thu Jan  1 01:00:00 1970
+++ new/atm/mkdiff	Wed Jul 31 19:44:59 1996
@@ -0,0 +1,80 @@
+#!/bin/sh
+TMP=${TMPDIR:-/usr/tmp}
+ARCH=/home/almesber/l/arch
+KPATH=/usr/src:/home/almesber/k
+
+del_new=true
+if [ "$1" = -keep -o "$1" = --keep ]; then
+    del_new=false
+    shift
+fi
+if [ -z "$*" ]; then
+    if [ ! -r VERSION ]; then
+	echo "need ./VERSION for fully automated procedure" 1>&2
+	exit 1
+    fi
+    read version <VERSION || exit 1
+    second=$ARCH/atm-$version.tar.gz
+    first=$ARCH/`cd $ARCH; ls -t1 atm-*.tar.gz |
+      sed "/atm-$version\.tar\.gz/{n;q;};d" | tail -1`
+    if [ ! -r $first -o ! -r $second ]; then
+	echo "automatic detection didn't work" 1>&2
+	exit 1
+    fi
+else
+    if [ -r "$1" ] && echo $1 | grep ^/ >/dev/null 2>&1; then
+	first=$1
+    else
+	first=$ARCH/$1
+	if [ ! -r $first ]; then
+	    echo "$1: not found (also tried $first)" 1>&2
+	    exit 1
+	fi
+    fi
+    if [ -r "$2" ] && echo $2 | grep ^/ >/dev/null 2>&1; then
+	second=$2
+    else
+	second=`dirname $first`/$2
+	if [ ! -r $second ]; then
+	    echo "$2: not found (also tried $second)" 1>&2
+	    exit 1
+	fi
+    fi
+fi
+echo $first '->' $second
+cd $TMP || exit 1
+mkdir old new || exit 1
+cd old
+tar xfz $first || exit 1
+cd ../new
+tar xfz $second || exit 1
+cd ..
+for n in old new; do
+    cd $n
+    {
+	read version
+	read version
+    } <atm/.kernel || exit 1
+    file=linux-$version.tar.gz
+    for m in `echo $KPATH | tr : ' '`; do
+	if [ -r $m/$file ]; then
+	    break
+	fi
+    done
+    if [ ! -r $m/$file ]; then
+	echo "no kernel $version found in $KPATH" 1>&2
+	exit 1
+    fi
+    tar xfz $m/$file || exit 1
+    cd linux
+    patch -s -p1 <../atm/atm*.patch || exit 1
+    find . -name \*.orig -exec rm {} \;
+    cd ../..
+done
+base=`basename $second .tar.gz`
+diff -ur --new-file old/linux new/linux >$base.kernel-diff
+diff -ur --new-file old/atm new/atm >$base.dist-diff
+gzip -9 -f $base.kernel-diff || exit 1
+gzip -9 -f $base.dist-diff || exit 1
+rm -rf old
+if $del_new; then rm -rf new; fi
diff -ur --new-file old/atm/mkdist new/atm/mkdist
--- old/atm/mkdist	Fri Jul 19 15:46:36 1996
+++ new/atm/mkdist	Wed Jul 31 19:26:08 1996
@@ -7,7 +7,7 @@
     cd $SRCDIR
     ./mkpatch
 )
-cp $SRCDIR/atm.patch atm-$VERSION.patch
+cp $SRCDIR/atm.patch .
 cd doc
 make usage.txt
 cd ../..
@@ -15,15 +15,15 @@
   atm/VERSION atm/README atm/README.DRIVERS atm/CREDITS atm/USAGE \
     atm/CHANGES atm/BUGS \
     atm/COPYING atm/COPYING.GPL \
-    atm/Makefile atm/Rules.make atm/mkdist atm/.kernel \
+    atm/Makefile atm/Rules.make atm/mkdist atm/mkdiff atm/.kernel \
   atm/doc/README atm/doc/usage.tex atm/doc/usage.txt atm/doc/url.sty \
     atm/doc/Makefile atm/doc/rlatex atm/doc/t2a.pl \
   atm/sigd/Makefile atm/sigd/atmsigd.c atm/sigd/cfg.l atm/sigd/cfg.y \
     atm/sigd/io.h atm/sigd/io.c atm/sigd/kernel.c atm/sigd/proto.h \
-    atm/sigd/proto.c atm/sigd/q2931.c atm/sigd/saal.h atm/sigd/saal.c \
-    atm/sigd/sscf.h atm/sigd/sscf.c atm/sigd/sscop.h atm/sigd/sscop.c \
-    atm/sigd/timeout.c atm/sigd/timeout.h atm/sigd/svc.c \
+    atm/sigd/proto.c atm/sigd/q2931.c atm/sigd/timeout.c atm/sigd/timeout.h \
     atm/sigd/sap.h atm/sigd/sap.c atm/sigd/mkmess.pl \
+  atm/saal/Makefile atm/saal/saal.h atm/saal/saal.c atm/saal/sscf.h \
+    atm/saal/sscf.c atm/saal/sscop.h atm/saal/sscop.c \
   atm/qgen/TODO atm/qgen/Makefile atm/qgen/common.h \
     atm/qgen/common.c atm/qgen/file.h atm/qgen/file.c atm/qgen/first.c \
     atm/qgen/second.c atm/qgen/third.c atm/qgen/op.h atm/qgen/qgen.h \
@@ -78,4 +78,4 @@
     atm/led/address.c atm/led/conn.c atm/led/main.c atm/led/kernel_itf.c \
     atm/led/Makefile \
   atm/aqd/Makefile atm/aqd/arequipad.c atm/aqd/io.h atm/aqd/io.c \
-  atm/atm-$VERSION.patch | gzip -9 >$ARCHDIR/atm-$VERSION.tar.gz
+  atm/atm.patch | gzip -9 >$ARCHDIR/atm-$VERSION.tar.gz
diff -ur --new-file old/atm/qgen/Makefile new/atm/qgen/Makefile
--- old/atm/qgen/Makefile	Thu Jul 18 21:41:42 1996
+++ new/atm/qgen/Makefile	Fri Jul 26 12:14:25 1996
@@ -2,7 +2,6 @@
 OBJS=common.o file.o first.o lex.yy.o qgen.o second.o third.o y.tab.o
 TRASH=q.out.h q.out.c qd.out.c qd.dump.c
 PGMS=qgen q.out.o #qtest
-STANDARDS=-DUNI31 -DALLOW_UNI30
 
 include ../Rules.make
 
diff -ur --new-file old/atm/qgen/qlib.c new/atm/qgen/qlib.c
--- old/atm/qgen/qlib.c	Tue Mar 12 17:40:48 1996
+++ new/atm/qgen/qlib.c	Tue Jul 30 23:05:04 1996
@@ -182,6 +182,7 @@
 	    perror("out of memory");
 	    return -1;
 	}
+	memset(dsc->length,0,sizeof(int)*Q_VARLEN_FIELDS);
     }
     dsc->error = 0;
     return 0;
diff -ur --new-file old/atm/saal/Makefile new/atm/saal/Makefile
--- old/atm/saal/Makefile	Thu Jan  1 01:00:00 1970
+++ new/atm/saal/Makefile	Wed Jul 31 14:37:04 1996
@@ -0,0 +1,11 @@
+LIBS=
+OBJS=saal.o sscf.o sscop.o
+
+all:				libsaal.a
+
+include ../Rules.make
+
+../saal/libsaal.a:		libsaal.a
+
+libsaal.a:			$(OBJS)
+				ar rcs libsaal.a $(OBJS)
diff -ur --new-file old/atm/saal/saal.c new/atm/saal/saal.c
--- old/atm/saal/saal.c	Thu Jan  1 01:00:00 1970
+++ new/atm/saal/saal.c	Wed Sep 27 15:14:45 1995
@@ -0,0 +1,13 @@
+/* saal.c - SAAL = SSCF+SSCOP */
+
+/* Written 1995 by Werner Almesberger, EPFL-LRC */
+
+
+#include "sscop.h"
+#include "saal.h"
+
+
+void saal_pdu(SAAL_DSC *dsc,void *buffer,int length)
+{
+    sscop_pdu(&dsc->sscop,buffer,length);
+}
diff -ur --new-file old/atm/saal/saal.h new/atm/saal/saal.h
--- old/atm/saal/saal.h	Thu Jan  1 01:00:00 1970
+++ new/atm/saal/saal.h	Wed Sep 27 11:07:23 1995
@@ -0,0 +1,24 @@
+/* saal.h - SAAL user interface */
+
+/* Written 1995 by Werner Almesberger, EPFL-LRC */
+
+
+#ifndef SAAL_H
+#define SAAL_H
+
+#include "sscf.h"
+#include "sscop.h"
+
+
+#define SAAL_DSC SSCF_DSC
+#define SAAL_USER_OPS SSCF_USER_OPS
+#define start_saal start_sscf
+#define stop_saal stop_sscf
+#define saal_estab_req sscf_estab_req
+#define saal_rel_req sscf_rel_req
+#define saal_send sscf_send
+#define saal_unitdata sscf_unitdata
+
+void saal_pdu(SAAL_DSC *dsc,void *buffer,int length);
+
+#endif
diff -ur --new-file old/atm/saal/sscf.c new/atm/saal/sscf.c
--- old/atm/saal/sscf.c	Thu Jan  1 01:00:00 1970
+++ new/atm/saal/sscf.c	Fri Feb  9 17:36:05 1996
@@ -0,0 +1,278 @@
+/* sscf.c - SSCF (Q.2130) protocol */
+
+/* Written 1995 by Werner Almesberger, EPFL-LRC */
+
+
+#include "atmd.h"
+
+#include "sscop.h"
+#include "sscf.h"
+
+
+#define COMPONENT "SSCF"
+
+
+/* --- SSCOP configuration ------------------------------------------------- */
+
+
+#define SSCF_MaxCC		      4	/* give up after 4 retries */
+#define SSCF_MaxPD		     25	/* POLL after 25 SDs */
+#define SSCF_Timer_CC		1000000 /* 1 sec */
+#define SSCF_Timer_KEEPALIVE	2000000	/* 2 sec */
+#define SSCF_Timer_NORESP	7000000 /* 7 sec */
+#define SSCF_Timer_POLL		 750000 /* 750 ms */
+#define SSCF_Timer_IDLE	       15000000 /* 15 sec */
+
+
+static const char *state_name[] = { "1/2","2/2","4/10","3/4","2/5" };
+
+
+/* --- Helper function(s) -------------------------------------------------- */
+
+
+static void next_state(SSCF_DSC *dsc,SSCF_STATE state)
+{
+    diag(COMPONENT,DIAG_DEBUG,"entering state %s",state_name[state]);
+    dsc->state = state;
+}
+
+
+/* --- Invocation from SSCOP ----------------------------------------------- */
+
+
+static void sscf_estab_ind(void *user_data,void *uu_data,int uu_length)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state != sscf_11)
+	diag(COMPONENT,DIAG_FATAL,"sscf_estab_ind in state %s",
+	  state_name[dsc->state]);
+    next_state(dsc,sscf_410);
+    sscop_estab_resp(&dsc->sscop,NULL,0,1);
+    if (dsc->ops->estab_ind) dsc->ops->estab_ind(dsc->user,uu_data,uu_length);
+}
+
+
+static void sscf_estab_conf(void *user_data,
+  void *uu_data,int uu_length)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state != sscf_22)
+	diag(COMPONENT,DIAG_FATAL,"sscf_estab_conf in state %s",
+	  state_name[dsc->state]);
+    next_state(dsc,sscf_410);
+    if (dsc->ops->estab_conf)
+	dsc->ops->estab_conf(dsc->user,uu_data,uu_length);
+    
+}
+
+
+static void sscf_restart(void *user_data,void *uu_data,int uu_length,int ind)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if ((!ind && dsc->state != sscf_34) || (ind && (dsc->state == sscf_11 ||
+      dsc->state == sscf_34)))
+	diag(COMPONENT,DIAG_FATAL,"sscf_restart (ind = %d) in state %s",
+	  state_name[dsc->state],ind);
+    sscop_estab_resp(&dsc->sscop,NULL,0,1);
+    next_state(dsc,sscf_410);
+    if (dsc->ops->restart) dsc->ops->restart(dsc->user,uu_data,uu_length,ind);
+}
+
+
+static void sscf_rec_ind(void *user_data)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state != sscf_410)
+	diag(COMPONENT,DIAG_FATAL,"sscf_rec_ind in state %s",
+	  state_name[dsc->state]);
+    sscop_rec_resp(&dsc->sscop);
+    if (dsc->ops->estab_ind) dsc->ops->estab_ind(dsc->user,NULL,0);
+}
+
+
+static void sscf_rel_ind(void *user_data,void *uu_data,int uu_length,int user)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state == sscf_11 || dsc->state == sscf_34)
+	diag(COMPONENT,DIAG_FATAL,"sscf_rel_ind in state %s",
+	  state_name[dsc->state]);
+    next_state(dsc,sscf_11);
+    if (dsc->ops->rel_ind)
+	dsc->ops->rel_ind(dsc->user,user ? uu_data : NULL,uu_length);
+}
+
+
+static void sscf_rel_conf(void *user_data)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state != sscf_34)
+	diag(COMPONENT,DIAG_FATAL,"sscf_rel_conf in state %s",
+	  state_name[dsc->state]);
+    next_state(dsc,sscf_11);
+    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
+}
+
+
+static void sscf_data_ind(void *user_data,void *data,int length,int sn)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state != sscf_410)
+	diag(COMPONENT,DIAG_FATAL,"sscf_data_ind in state %s",
+	  state_name[dsc->state]);
+    if (dsc->ops->data_ind) dsc->ops->data_ind(dsc->user,data,length);
+    
+}
+
+
+static void sscf_res_ind(void *user_data,void *uu_data,int uu_length)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state != sscf_410)
+	diag(COMPONENT,DIAG_FATAL,"sscf_res_ind in state %s",
+	  state_name[dsc->state]);
+    sscop_res_resp(&dsc->sscop);
+    if (dsc->ops->estab_ind) dsc->ops->estab_ind(dsc->user,uu_data,uu_length);
+}
+
+
+static void sscf_res_conf(void *user_data)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->state != sscf_25)
+	diag(COMPONENT,DIAG_FATAL,"sscf_res_conf in state %s",
+	  state_name[dsc->state]);
+    next_state(dsc,sscf_410);
+    if (dsc->ops->estab_conf) dsc->ops->estab_conf(dsc->user,NULL,0);
+}
+
+
+static void sscf_unitdata_ind(void *user_data,void *data,int length)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->ops->unitdata) dsc->ops->unitdata(dsc->user,data,length);
+}
+
+
+static void sscf_cpcs_send(void *user_data,void *data,int length)
+{
+    SSCF_DSC *dsc = user_data;
+
+    if (dsc->ops->cpcs_send)
+	dsc->ops->cpcs_send(dsc->user,data,length);
+}
+
+
+static SSCOP_USER_OPS sscf_ops = {
+   sscf_estab_ind, *
+   sscf_estab_conf, *
+   sscf_rel_ind, *
+   sscf_rel_conf, *
+   sscf_restart,
+   sscf_res_ind,
+   sscf_res_conf,
+   sscf_rec_ind,
+   sscf_data_ind,
+   sscf_unitdata_ind,
+   NULL, /* no retr_ind */
+   NULL, /* no retr_comp */
+   NULL, /* no maa_data */
+   NULL, /* no maa_error */
+   sscf_cpcs_send
+};
+
+
+/* --- Invocation from user ------------------------------------------------ */
+
+
+void start_sscf(SSCF_DSC *dsc,SSCF_USER_OPS *ops,void *user_data)
+{
+    dsc->state = sscf_11;
+    dsc->ops = ops;
+    dsc->user = user_data;
+    start_sscop(&dsc->sscop,&sscf_ops,dsc);
+    dsc->sscop.cf_max_cc = SSCF_MaxCC;
+    dsc->sscop.cf_max_pd = SSCF_MaxPD;
+    dsc->sscop.cf_timer_cc = SSCF_Timer_CC;
+    dsc->sscop.cf_timer_poll = SSCF_Timer_POLL;
+    dsc->sscop.cf_timer_noresp = SSCF_Timer_NORESP;
+    dsc->sscop.cf_timer_keepalive = SSCF_Timer_KEEPALIVE;
+    dsc->sscop.cf_timer_idle = SSCF_Timer_IDLE;
+}
+
+
+void stop_sscf(SSCF_DSC *dsc)
+{
+    stop_sscop(&dsc->sscop);
+}
+
+
+void sscf_estab_req(SSCF_DSC *dsc,void *uu_data,int uu_length)
+{
+    switch (dsc->state) {
+	case sscf_11:
+	case sscf_34:
+	    next_state(dsc,sscf_22);
+	    sscop_estab_req(&dsc->sscop,uu_data,uu_length,1);
+	    return;
+	case sscf_410:
+	    next_state(dsc,sscf_25);
+	    sscop_res_req(&dsc->sscop,uu_data,uu_length);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_FATAL,"sscf_estab_req invoked in state %s",
+      state_name[dsc->state]);
+}
+
+
+void sscf_rel_req(SSCF_DSC *dsc,void *uu_data,int uu_length)
+{
+    switch (dsc->state) {
+	case sscf_11:
+	    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
+	    return;
+	case sscf_22:
+	case sscf_410:
+	case sscf_25:
+	    next_state(dsc,sscf_34);
+	    sscop_rel_req(&dsc->sscop,uu_data,uu_length);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_FATAL,"sscf_rel_req invoked in state %s",
+      state_name[dsc->state]);
+}
+
+
+void sscf_send(SSCF_DSC *dsc,void *data,int length)
+{
+    switch (dsc->state) {
+	case sscf_11:
+	    return;
+	case sscf_410:
+	    sscop_send(&dsc->sscop,data,length);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_WARN,"sscf_send invoked in state %s",
+      state_name[dsc->state]); /* make fatal later @@@ */
+}
+
+
+void sscf_unitdata(SSCF_DSC *dsc,void *data,int length)
+{
+    sscop_unitdata(&dsc->sscop,data,length);
+}
diff -ur --new-file old/atm/saal/sscf.h new/atm/saal/sscf.h
--- old/atm/saal/sscf.h	Thu Jan  1 01:00:00 1970
+++ new/atm/saal/sscf.h	Fri Oct  6 11:33:07 1995
@@ -0,0 +1,56 @@
+/* sscf.h - SSCF (Q.2130) user interface */
+
+/* Written 1995 by Werner Almesberger, EPFL-LRC */
+
+
+#ifndef SSCF_H
+#define SSCF_H
+
+#include "sscop.h"
+
+
+typedef enum { sscf_11,sscf_22,sscf_410,sscf_34,sscf_25 } SSCF_STATE;
+
+typedef struct {
+    SSCF_STATE state;
+    struct _sscf_user_ops *ops;
+    void *user;
+    SSCOP_DSC sscop;
+} SSCF_DSC;
+
+
+typedef struct _sscf_user_ops {
+    void (*estab_ind)(void *user_data,void *uu_data,int uu_length);
+	/* AAL-ESTABLISH.indication */
+    void (*estab_conf)(void *user_data,void *uu_data,int uu_length);
+	/* AAL-ESTABLISH.confirm */
+    void (*rel_ind)(void *user_data,void *uu_data,int uu_length);
+	/* AAL-RELEASE.indication */
+    void (*rel_conf)(void *user_data); /* AAL-RELEASE.confirm */
+    void (*restart)(void *user_data,void *uu_data,int uu_length,int ind);
+	/* AAL-RELEASE.indication or AAL-RELEASE.confirm immediately followed
+	   by AAL-ESTABLISH.indication */
+    void (*data_ind)(void *user_data,void *data,int length);
+	/* AAL-DATA.indication */
+    void (*unitdata)(void *user_data,void *data,int length);
+	/* AAL-UNITDATA.indication */
+    void (*cpcs_send)(void *user_data,void *data,int length);
+} SSCF_USER_OPS;
+
+
+/* Attach/detach protocol */
+
+void start_sscf(SSCF_DSC *dsc,SSCF_USER_OPS *ops,void *user_data);
+void stop_sscf(SSCF_DSC *dsc);
+
+/* Connection control */
+
+void sscf_estab_req(SSCF_DSC *dsc,void *uu_data,int uu_length);
+void sscf_rel_req(SSCF_DSC *dsc,void *uu_data,int uu_length);
+
+/* Send data */
+
+void sscf_send(SSCF_DSC *dsc,void *data,int length);
+void sscf_unitdata(SSCF_DSC *dsc,void *data,int length);
+
+#endif
diff -ur --new-file old/atm/saal/sscop.c new/atm/saal/sscop.c
--- old/atm/saal/sscop.c	Thu Jan  1 01:00:00 1970
+++ new/atm/saal/sscop.c	Fri May 31 15:44:11 1996
@@ -0,0 +1,1995 @@
+/* sscop.c - SSCOP (Q.2110) protocol */
+ 
+/* Written 1995-1996 by Werner Almesberger, EPFL-LRC */
+ 
+
+#if 1 /* debugging only */
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h> /* for htonl, ntohl */
+
+#include "atmd.h"
+
+#include "sscop.h"
+
+
+/*
+ * This is a quite exact translation of the ITU-T SSCOP SDL diagrams. They
+ * are a pleasant example of how protocols _should_ be specified. It took me
+ * less than a week to implement this.
+ *
+ * Calls back to the SSCOP user are always done last in a sequence of actions
+ * to avoid reentrancy problems if the user invokes SSCOP from the callback
+ * function. (Exception: data indications are delivered when needed. So you
+ * shouldn't kill the protocol stack in the middle of this. Releasing the
+ * call and such is fine, though.)
+ *
+ * Sequences of an AA-RELASE.indication/confirm immediately followed by a
+ * AA-ESTABLISH.indication have been replaced by a new primitive called
+ * "restart". This way, the SSCOP user is able to distinguish conditions where
+ * SSCOP is in idle state after a callback from conditions where SSCOP wants to
+ * continue (and where the user many not want to stop SSCOP).
+ *
+ * The entity receiving management information (e.g. maa_error) must not issue
+ * any SSCOP primitives from that callback routine. Instead, actions must be
+ * queued until SSCOP finishes processing of the current event.
+ *
+ * Comments of the type xx.yy are section or figure numbers in Q.2110
+ *
+ * KNOWN BUGS: The queue and buffer management stinks, especially the "buffer
+ *	       cloning". All this ought to be rewritten to use skbuffs. Since
+ *	       this will happen anyway if SSCOP ever gets integrated into the
+ *	       kernel, we can safely ignore the problem for now.
+ *
+ *	       The lower layer is always assumed to be ready for sending. If
+ *	       this is not the case (shouldn't happen), the PDU should be
+ *	       discarded (or the process might be blocked for a few cycles).
+ */
+
+
+#define COMPONENT "SSCOP"
+
+
+/*#define debug (void)*/
+
+/* Configurable SSCOP parameters */
+
+#define SSCOP_CF_MR	 30	/* buffer size */
+#define SSCOP_CF_MaxCC	 10	/* max timeouts */
+#define SSCOP_CF_MaxPD	100	/* SD/POLL ratio */
+#define SSCOP_CF_MaxSTAT 67	/* max elements in STAT PDU, 7.7 */
+
+#undef POLL_AFTER_RETRANSMISSION /* 20.38, #define this if needed */
+
+
+/* Timers */
+
+/*
+ * Assumptions: RTT = 1 sec, remote needs about 1 sec to wake up. SSCF changes
+ * all this anyway.
+ */
+
+#define TIMER_CC	 2000000 /* 2 sec (== RTT+eps) */
+#define TIMER_POLL	 1000000 /* 1 sec (== RTT) */
+#define TIMER_NORESP	 7000000 /* 7 sec (keepalive+RTT+eps) */
+#define TIMER_KEEPALIVE  5000000 /* 5 sec (> poll && > RTT) */
+#define TIMER_IDLE	30000000 /* 30 sec (>> keepalive) */
+
+
+/* SSCOP PDU types, 7.1 */
+
+#define SSCOP_BGN	 1 /* Request Initialization */
+#define SSCOP_BGAK	 2 /* Request Acknowledgement */
+#define SSCOP_BGREJ	 7 /* Connection Reject */
+#define SSCOP_END	 3 /* Disconnect Command */
+#define SSCOP_ENDAK	 4 /* Disconnect Acknowledgement */
+#define SSCOP_RS	 5 /* Resynchronization Command */
+#define SSCOP_RSAK	 6 /* Resynchronization Acknowledgement */
+#define SSCOP_ER	 9 /* Recovery Command */
+#define SSCOP_ERAK	15 /* Recovery Acknowledgement */
+#define SSCOP_SD	 8 /* Sequence Connection-mode Data */
+#define SSCOP_POLL	10 /* Transmitter State Information with request ... */
+#define SSCOP_STAT	11 /* Solicited Receiver State Information */
+#define SSCOP_USTAT	12 /* Unsolicited Receiver State Information */
+#define SSCOP_UD	13 /* Unnumbered User Data */
+#define SSCOP_MD	14 /* Unnumbered Management Data */
+
+
+/* Trailer format macros */
+
+#define SSCOP_TRAIL(type,pad,n) (htonl((n) | ((type) << 24) | ((pad) << 30)))
+#define SSCOP_S_BIT 0x10000000
+#define SSCOP_TYPE(last) ((ntohl(last) >> 24) & 15)
+#define SSCOP_PAD(last) (ntohl(last) >> 30)
+#define SSCOP_N(last) (ntohl(last) & 0xffffff)
+#define SSCOP_S(last) (ntohl(last) & SSCOP_S_BIT)
+
+
+/* Some helper macros */
+
+#define START_TIMER(t) ({ STOP_TIMER(dsc->timer_##t); \
+  dsc->timer_##t = start_timer(dsc->cf_timer_##t, sscop_##t##_exp,dsc); })
+#define STOP_TIMER(t) ({ if (t) stop_timer(t); t = NULL; })
+#define MOD24(x) ((x) & ((1 << 24)-1))
+#define INC24(v) (v = MOD24(v+1))
+#define INC8(v) (v = (v+1) & 255)
+#define NORMALIZE(x,b) ( \
+    (b) < (1 << 23) ? \
+      (x) < (b)+(1 << 23) ? (x)+(1 << 24) : x : \
+      (x) < (b)-(1 << 23) ? (x)+(1 << 24) : x)
+#define NORM_RX(v) NORMALIZE((v),dsc->vr_r)
+#define NORM_TX(v) NORMALIZE((v),dsc->vt_a)
+
+
+/* Helper macros for PDU construction and decomposition */
+
+#define PDU_VARS \
+  unsigned char type; \
+  int length; \
+  int s,ps,r,mr,sq
+#define DECOMPOSE_PDU(dsc,msg,size) decompose_pdu(dsc,msg,size,&type, \
+  &length,&s,&ps,&r,&mr,&sq)
+#define PRINT_PDU(dsc,label,data) print_pdu(dsc,label,type,data,&length, \
+  &s,&ps,&r,&mr,&sq)
+
+
+static const char *pdu_name[] = { "???","BGN","BGAK","END","ENDAK","RS","RSAK",
+  "BGREJ","SD","ER","POLL","STAT","USTAT","UD","MD","ERAK" };
+
+static const char *state_name[] = { "Idle","OutConnPend","InConnPend",
+  "OutDiscPend","OutResyPend","InResyPend","OutRecPend","RecRespPend",
+  "InRecPend","DataTransReady" };
+
+
+static void maa_error(SSCOP_DSC *dsc,char code,int count)
+{
+    static const char *const msgs[] = {
+	"A:SD PDU","B:BGN PDU","C:BGAK PDU","D:BGREJ PDU","E:END PDU",
+	"F:ENDAK PDU","G:POLL PDU","H:STAT PDU","I:USTAT PDU","J:RS",
+	"K:RSAK PDU","L:ER","M:ERAK","O:VT(CC)>=MaxCC",
+	"P:Timer_NO_RESPONSE expiry","Q:SD or POLL, N(S) error",
+	"R:STAT N(PS) error","S:STAT N(R) or list elements error",
+	"T:USTAT N(R) or list elements error","U:PDU length violation",
+	"V:SD PDUs must be retransmitted","W:Lack of credit",
+	"X:Credit obtained","\0:Unknown error code" };
+    const char *const *walk;
+
+    if (dsc->ops->maa_error)
+	if (!dsc->ops->maa_error(dsc->user,code,count)) return;
+    for (walk = msgs; **walk; walk++)
+	if (**walk == code) break;
+    if (code != 'V')
+	diag(COMPONENT,DIAG_WARN,"layer management - error %c \"%s\"",code,
+	  (*walk)+2);
+    else diag(COMPONENT,DIAG_WARN,"layer management - error %c,%d \"%s\"",code,
+      count,(*walk)+2);
+}
+
+
+static void print_pdu(SSCOP_DSC *dsc,const char *label,unsigned char type,
+  void *data,const int *length,const int *s,const int *ps,const int *r,
+  const int *mr,const int *sq)
+{
+    int len;
+    int *list;
+
+    switch (type) {
+	case SSCOP_SD:
+	    diag(COMPONENT,DIAG_DEBUG,"%s SD(S=%d,len=%d)",label,*s,*length);
+	    break;
+	case SSCOP_POLL:
+	    diag(COMPONENT,DIAG_DEBUG,"%s POLL(PS=%d,S=%d)",label,*ps,*s);
+	    break;
+	case SSCOP_STAT:
+	    if (*length & 3) {
+		diag(COMPONENT,DIAG_WARN,"%s STAT PDU has wrong size (%d)",
+		  label,*length);
+		break;
+	    }
+	    len = *length/4;
+	    diag(COMPONENT,DIAG_DEBUG,"%s STAT(PS=%d,MR=%d,R=%d,items=%d:",
+	      label,*ps,*mr,*r,len);
+	    list = (int *) data;
+	    while (len > 1) {
+		diag(COMPONENT,DIAG_DEBUG,"%s    %d..%d",label,ntohl(list[0]),
+		  ntohl(list[1]));
+		list += 2;
+		len -= 2;
+	    }
+	    if (!len)
+		diag(COMPONENT,DIAG_DEBUG,"%s    <last is absent>)",label);
+	    else diag(COMPONENT,DIAG_DEBUG,"%s    <next is> %d)",label,
+		  ntohl(*list));
+	    break;
+	case SSCOP_USTAT:
+	    if (*length != 8) {
+		diag(COMPONENT,DIAG_WARN,"%s USTAT PDU has wrong size (%d)",
+		  label,*length);
+		break;
+	    }
+	    list = (int *) data;
+	    diag(COMPONENT,DIAG_DEBUG,"%s USTAT(MR=%d,R=%d,%d..%d)",label,*mr,
+	      *r,ntohl(list[0]),
+	      ntohl(list[1]));
+	    break;
+	case SSCOP_UD:
+	    diag(COMPONENT,DIAG_DEBUG,"%s UD(len=%d)",label,*length);
+	    break;
+	case SSCOP_MD:
+	    diag(COMPONENT,DIAG_DEBUG,"%s MD(len=%d)",label,*length);
+	    break;
+	case SSCOP_BGN:
+	    diag(COMPONENT,DIAG_DEBUG,"%s BGN(SQ=%d,MR=%d,len=%d)",label,*sq,
+	      *mr,*length);
+	    break;
+	case SSCOP_BGAK:
+	    diag(COMPONENT,DIAG_DEBUG,"%s BGAK(MR=%d,len=%d)",label,*mr,
+	      *length);
+	    break;
+	case SSCOP_BGREJ:
+	    diag(COMPONENT,DIAG_DEBUG,"%s BGREJ(len=%d)",label,*length);
+	    break;
+	case SSCOP_END:
+	    diag(COMPONENT,DIAG_DEBUG,"%s END(S=%d,len=%d)",label,*s,*length);
+	    break;
+	case SSCOP_ENDAK:
+	    diag(COMPONENT,DIAG_DEBUG,"%s ENDAK()",label);
+	    break;
+	case SSCOP_RS:
+	    diag(COMPONENT,DIAG_DEBUG,"%s RS(SQ=%d,MR=%d,len=%d)",label,*sq,
+	      *mr,*length);
+	    break;
+	case SSCOP_RSAK:
+	    diag(COMPONENT,DIAG_DEBUG,"%s RSAK(MR=%d)",label,*mr);
+	    break;
+	case SSCOP_ER:
+	    diag(COMPONENT,DIAG_DEBUG,"%s ER(MR=%d)",label,*mr);
+	    break;
+	case SSCOP_ERAK:
+	    diag(COMPONENT,DIAG_DEBUG,"%s ERAK(MR=%d)",label,*mr);
+	    break;
+	default:
+	    diag(COMPONENT,DIAG_ERROR,"%s unknown PDU type %d\n",label,type);
+    }
+}
+
+
+static int decompose_pdu(SSCOP_DSC *dsc,void *msg,int size,
+  unsigned char *type,int *length,int *s,int *ps,int *r,int *mr,int *sq)
+{
+    unsigned long *last;
+    unsigned char pad;
+    int n;
+
+/*
+ * *length is undefined if PDU has no variable-length data part
+ */
+
+    if (size < 4 || (size & 3)) {
+	diag(COMPONENT,DIAG_DEBUG,"invalid message length (%d)",size);
+	maa_error(dsc,'U',0);
+	return -1;
+    }
+    last = (unsigned long *) ((char *) msg+size-4);
+    *type = SSCOP_TYPE(*last);
+    pad = SSCOP_PAD(*last);
+    n = SSCOP_N(*last);
+    *length = size-4-pad;
+    switch (*type) {
+	case SSCOP_SD:
+	    *s = n;
+	    break;
+	case SSCOP_POLL:
+	    if (size != 8) {
+		diag(COMPONENT,DIAG_DEBUG,"POLL PDU has bad length (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *s = n;
+	    *ps = SSCOP_N(*(unsigned long *) msg);
+	    break;
+	case SSCOP_STAT:
+	    if (size < 12) {
+		diag(COMPONENT,DIAG_DEBUG,"STAT PDU too short (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    if (*length & 3) {
+		diag(COMPONENT,DIAG_DEBUG,"STAT PDU has bad length (%d)",
+		  length);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *r = n;
+	    *mr = SSCOP_N(last[-1]);
+	    *ps = SSCOP_N(last[-2]);
+	    *length -= 8;
+	    break;
+	case SSCOP_USTAT:
+	    if (size != 16) {
+		diag(COMPONENT,DIAG_DEBUG,"USTAT PDU has bad length (%d)",
+		  size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *r = n;
+	    *mr = SSCOP_N(last[-1]);
+	    *length -= 4;
+	    break;
+	case SSCOP_UD:
+	case SSCOP_MD:
+	    break;
+	case SSCOP_BGN:
+	    if (size < 8) {
+		diag(COMPONENT,DIAG_DEBUG,"BGN PDU too short (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *mr = n;
+	    *sq = SSCOP_N(last[-1]) & 0xff;
+	    *length -= 4;
+	    break;
+	case SSCOP_BGAK:
+	    if (size < 8) {
+		diag(COMPONENT,DIAG_DEBUG,"BGAK PDU too short (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *mr = n;
+	    *length -= 4;
+	    break;
+	case SSCOP_BGREJ:
+	    if (size < 8) {
+		diag(COMPONENT,DIAG_DEBUG,"BGREJ PDU too short (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *length -= 4;
+	    break;
+	case SSCOP_END:
+	    if (size < 8) {
+		diag(COMPONENT,DIAG_DEBUG,"END PDU too short (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *s = !!(ntohl(*last) & SSCOP_S_BIT);
+	    *length -= 4;
+	    break;
+	case SSCOP_ENDAK:
+	    if (size != 8) {
+		diag(COMPONENT,DIAG_DEBUG,"ENDAK PDU has bad length (%d)",
+		  size);
+		maa_error(dsc,'U',0);
+		if (size < 4) return -1; /* make it work with Fore */
+	    }
+	    break;
+	case SSCOP_RS:
+	    if (size < 8) {
+		diag(COMPONENT,DIAG_DEBUG,"RS PDU too short (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *mr = n;
+	    *sq = SSCOP_N(last[-1]) & 0xff;
+	    *length -= 4;
+	    break;
+	case SSCOP_RSAK:
+	    if (size != 8) {
+		diag(COMPONENT,DIAG_DEBUG,"RSAK PDU has bad length (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *mr = n;
+	    break;
+	case SSCOP_ER:
+	    if (size != 8) {
+		diag(COMPONENT,DIAG_DEBUG,"ER PDU has bad length (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *mr = n;
+	    *sq = SSCOP_N(last[-1]) & 0xff;
+	    break;
+	case SSCOP_ERAK:
+	    if (size != 8) {
+		diag(COMPONENT,DIAG_DEBUG,"ERAK PDU has bad length (%d)",size);
+		maa_error(dsc,'U',0);
+		return -1;
+	    }
+	    *mr = n;
+	    break;
+	default:
+	    diag(COMPONENT,DIAG_DEBUG,"unknown PDU type %d (0x%x)",*type,
+	      *type);
+	    return -1;
+    }
+    return 0;
+}
+
+
+static BUFFER *build_pdu(SSCOP_DSC *dsc,unsigned char type,void *data,
+  int num)
+{
+    BUFFER *buf;
+    unsigned long *trailer;
+    int pad;
+
+    buf = buffer_create(num+12,dsc->vt_s); /* space for trailers */
+    if (data && num) {
+	memset((char *) buf->data+(num & ~3),0,4); /* clear padding area */
+	memcpy((unsigned char *) buf->data,data,num);
+	pad = (4-(num & 3)) & 3;
+	trailer = (unsigned long *) ((char *) buf->data+num+pad);
+    }
+    else {
+	pad = 0;
+	trailer = (unsigned long *) buf->data;
+    }
+    diag(COMPONENT,DIAG_DEBUG,"generating %s PDU",pdu_name[type]);
+    switch (type) {
+	case SSCOP_BGN:
+	case SSCOP_RS:
+	case SSCOP_ER:
+	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vt_sq);
+	    *trailer++ = SSCOP_TRAIL(type,pad,dsc->vr_mr);
+	    break;
+	case SSCOP_BGAK:
+	case SSCOP_RSAK:
+	case SSCOP_ERAK:
+	    *trailer++ = SSCOP_TRAIL(0,0,0);
+	    *trailer++ = SSCOP_TRAIL(type,pad,dsc->vr_mr);
+	    break;
+	case SSCOP_END: /* S bit is indicated by "num" */
+	    *trailer++ = SSCOP_TRAIL(0,0,0);
+	    *trailer++ = SSCOP_TRAIL(type,pad,0) | (num ? htonl(SSCOP_S_BIT) :
+	      0);
+	    break;
+	case SSCOP_BGREJ:
+	case SSCOP_ENDAK:
+	    *trailer++ = SSCOP_TRAIL(0,0,0);
+	    *trailer++ = SSCOP_TRAIL(type,pad,0);
+	    break;
+	case SSCOP_POLL:
+	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vt_ps);
+	    /* fall through */
+	case SSCOP_SD:
+	    *trailer++ = SSCOP_TRAIL(type,pad,dsc->vt_s);
+	    break;
+	case SSCOP_STAT:
+	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vr_ps);
+	    /* fall through */
+	case SSCOP_USTAT:
+	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vr_mr);
+	    *trailer++ = SSCOP_TRAIL(type,0,dsc->vr_r);
+	    break;
+	case SSCOP_UD:
+	case SSCOP_MD:
+	    *trailer++ = SSCOP_TRAIL(type,pad,0);
+	    break;
+	default:
+	    diag(COMPONENT,DIAG_FATAL,
+	      "requested construction of unknown PDU type %d",type);
+    }
+    buf->length = (char *) trailer-(char *) buf->data;
+    return buf;
+}
+
+
+static void dump_pdu(void *data,int len)
+{
+    int i;
+
+    if (!debug) return;
+    for (i = 0; i < len; i++) {
+	if (!(i & 15)) printf("%s%04X:",i ? "\n" : "",i);
+	printf(" %02X",((unsigned char *) data)[i]);
+    }
+    putchar('\n');
+}
+
+
+static void emit_pdu(SSCOP_DSC *dsc,BUFFER *buf)
+{
+    BUFFER **last;
+
+    dump_pdu(buf->data,buf->length);
+    {
+	PDU_VARS;
+
+	if (DECOMPOSE_PDU(dsc,buf->data,buf->length))
+	    diag(COMPONENT,DIAG_FATAL,"composed garbage");
+	PRINT_PDU(dsc,"SEND",buf->data);
+    }
+    if (dsc->ops->cpcs_send)
+	dsc->ops->cpcs_send(dsc->user,buf->data,buf->length);
+    switch (SSCOP_TYPE(*(unsigned long *) ((char *) buf->data+buf->length-4))) {
+	case SSCOP_BGN:
+	    last = &dsc->last_bgn;
+	    break;
+	case SSCOP_END:
+	    last = &dsc->last_end;
+	    break;
+	case SSCOP_RS:
+	    last = &dsc->last_rs;
+	    break;
+	case SSCOP_ER:
+	    last = &dsc->last_er;
+	    break;
+	default:
+	    buffer_discard(buf);
+	    return;
+    }
+    if (*last && *last != buf) {
+	buffer_discard(*last);
+	*last = NULL;
+    }
+    *last = buf;
+}
+
+
+static void resend_pdu(SSCOP_DSC *dsc,BUFFER *buf)
+{
+    if (!buf) diag(COMPONENT,DIAG_FATAL,"resend buffer is NULL");
+    emit_pdu(dsc,buf);
+}
+
+
+static void send_pdu(SSCOP_DSC *dsc,unsigned char type,void *data,int size)
+{
+    BUFFER *buf;
+
+    buf = build_pdu(dsc,type,data,size);
+    emit_pdu(dsc,buf);
+}
+
+
+static void next_state(SSCOP_DSC *dsc,SSCOP_STATE state)
+{
+    diag(COMPONENT,DIAG_DEBUG,"entering state %s (%d)",state_name[state],
+      (int) state);
+    dsc->state = state;
+}
+
+
+static void bad_pdu(SSCOP_DSC *dsc,unsigned char type)
+{
+		/*           111111 */
+		/* 0123456789012345 */
+    maa_error(dsc,"?BCxFJKDALGHIyzM"[type],0);
+}
+
+
+static void send_ustat(SSCOP_DSC *dsc,int first,int last)
+{
+    unsigned long range[2];
+
+    range[0] = htonl(first);
+    range[1] = htonl(last);
+    send_pdu(dsc,SSCOP_USTAT,range,8);
+}
+
+
+/* --- Utility functions --------------------------------------------------- */
+
+
+static void clear_receive_buffer(SSCOP_DSC *dsc)
+{
+    queue_clear(&dsc->rx_buf);
+}
+
+
+static void clear_transmission_buffer(SSCOP_DSC *dsc)
+{
+    queue_clear(&dsc->tx_buf);
+}
+
+
+static void clear_transmission_queue(SSCOP_DSC *dsc)
+{
+    queue_clear(&dsc->tx_q);
+}
+
+
+static void clear_retransmission_queue(SSCOP_DSC *dsc)
+{
+    queue_clear(&dsc->rt_q);
+}
+
+
+/* --- SSCOP subroutines --------------------------------------------------- */
+
+
+static void sscop_poll_exp(void *user);
+static void sscop_keepalive_exp(void *user);
+static void sscop_noresp_exp(void *user);
+
+
+static void release_buffers(SSCOP_DSC *dsc) /* 20.49 */
+{
+    clear_transmission_queue(dsc);
+    clear_transmission_buffer(dsc);
+    clear_retransmission_queue(dsc);
+    clear_receive_buffer(dsc);
+}
+
+
+static void clear_transmitter(SSCOP_DSC *dsc) /* 20.49 */
+{
+    if (!dsc->clear_buffers) {
+	clear_transmission_queue(dsc);
+	clear_transmission_buffer(dsc);
+    }
+}
+
+
+static void prepare_recovery(SSCOP_DSC *dsc) /* 20.49 */
+{
+    if (dsc->clear_buffers) {
+	clear_transmission_queue(dsc);
+	clear_transmission_buffer(dsc);
+    }
+    clear_retransmission_queue(dsc);
+}
+
+
+static void prepare_retrieval(SSCOP_DSC *dsc) /* 20.49 */
+{
+    prepare_recovery(dsc);
+    clear_receive_buffer(dsc);
+}
+
+
+static void deliver_data(SSCOP_DSC *dsc) /* 20.49 */
+{
+    BUFFER *buf;
+
+    if (!dsc->clear_buffers) {
+	while ((buf = queue_get(&dsc->rx_buf))) {
+	    if (dsc->ops->data_ind)
+		dsc->ops->data_ind(dsc->user,buf->data,buf->length,buf->key);
+	    buffer_discard(buf);
+	}
+    }
+    clear_receive_buffer(dsc);
+}
+
+
+static void initialize_state_variables(SSCOP_DSC *dsc) /* 20.49 */
+{
+    dsc->vt_s = dsc->vt_ps = dsc->vt_a = 0;
+    dsc->vt_pa = 1;
+    dsc->vt_pd = 0;
+    dsc->credit = 1;
+    dsc->vr_r = dsc->vr_h = 0;
+}
+
+
+static int detect_retransmission(SSCOP_DSC *dsc,int sq) /* 20.50 */
+{
+    if (dsc->vr_sq == sq) return 1;
+    dsc->vr_sq = sq;
+    return 0;
+}
+
+
+static void set_poll_timer(SSCOP_DSC *dsc) /* 20.50 */
+{
+    if (queue_peek(&dsc->tx_q) || dsc->vt_s != dsc->vt_a) START_TIMER(poll);
+    else START_TIMER(keepalive);
+}
+
+
+static void reset_data_transfer_timers(SSCOP_DSC *dsc) /* 20.51 */
+{
+    STOP_TIMER(dsc->timer_poll);
+    STOP_TIMER(dsc->timer_keepalive);
+    STOP_TIMER(dsc->timer_noresp);
+    STOP_TIMER(dsc->timer_idle);
+}
+
+
+static void set_data_transfer_timers(SSCOP_DSC *dsc) /* 20.51 */
+{
+    START_TIMER(poll);
+    START_TIMER(noresp);
+}
+
+
+static void initialize_vr_mr(SSCOP_DSC *dsc)
+{
+    dsc->vr_mr = MOD24(dsc->vr_h+SSCOP_CF_MR);
+}
+
+
+static void update_vr_mr(SSCOP_DSC *dsc)
+{
+    initialize_vr_mr(dsc);
+}
+
+
+/* --- Timer handlers ------------------------------------------------------ */
+
+
+static void sscop_cc_exp(void *user)
+{
+    SSCOP_DSC *dsc = user;
+
+    diag(COMPONENT,DIAG_DEBUG,"Timer CC has expired");
+    dsc->timer_cc = NULL;
+    switch (dsc->state) {
+	case sscop_outconn: /* 20.9 */
+	    if (dsc->vt_cc < dsc->cf_max_cc) {
+		dsc->vt_cc++;
+		resend_pdu(dsc,dsc->last_bgn);
+		START_TIMER(cc);
+		return;
+	    }
+	    maa_error(dsc,'O',0);
+	    send_pdu(dsc,SSCOP_END,NULL,1);
+	    next_state(dsc,sscop_idle);
+	    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
+	    return;
+	case sscop_outdisc: /* 20.15 */
+	    if (dsc->vt_cc < dsc->cf_max_cc) {
+		dsc->vt_cc++;
+		resend_pdu(dsc,dsc->last_end);
+		START_TIMER(cc);
+		return;
+	    }
+	    maa_error(dsc,'O',0);
+	    next_state(dsc,sscop_idle);
+	    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
+	    return;
+	case sscop_outres: /* 20.18 */
+	    if (dsc->vt_cc < dsc->cf_max_cc) {
+		dsc->vt_cc++;
+		resend_pdu(dsc,dsc->last_rs);
+		START_TIMER(cc);
+		return;
+	    }
+	    maa_error(dsc,'O',0);
+	    send_pdu(dsc,SSCOP_END,NULL,1);
+	    next_state(dsc,sscop_idle);
+	    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
+	    return;
+	case sscop_outrec: /* 20.24 */
+	    if (dsc->vt_cc < dsc->cf_max_cc) {
+		dsc->vt_cc++;
+		resend_pdu(dsc,dsc->last_er);
+		START_TIMER(cc);
+		return;
+	    }
+	    maa_error(dsc,'O',0);
+	    send_pdu(dsc,SSCOP_END,NULL,1);
+	    clear_receive_buffer(dsc);
+	    next_state(dsc,sscop_idle);
+	    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_FATAL,"Timer CC expired in state %s",
+      state_name[dsc->state]);
+}
+
+
+static void sscop_common_exp(void *user)
+{
+    SSCOP_DSC *dsc = user;
+
+    if (dsc->state != sscop_ready) {
+	diag(COMPONENT,DIAG_FATAL,"sscop_common_exp invoked in state %s",
+	  state_name[dsc->state]);
+	return;
+    }
+    INC24(dsc->vt_ps);
+    send_pdu(dsc,SSCOP_POLL,NULL,0);
+    dsc->vt_pd = 0;
+    set_poll_timer(dsc);
+}
+
+
+static void sscop_poll_exp(void *user)
+{
+    SSCOP_DSC *dsc = user;
+
+    diag(COMPONENT,DIAG_DEBUG,"Timer POLL has expired");
+    dsc->timer_poll = NULL;
+    sscop_common_exp(user);
+}
+
+
+static void sscop_keepalive_exp(void *user)
+{
+    SSCOP_DSC *dsc = user;
+
+    diag(COMPONENT,DIAG_DEBUG,"Timer KEEPALIVE has expired");
+    dsc->timer_keepalive = NULL;
+    sscop_common_exp(user);
+}
+
+
+static void sscop_idle_exp(void *user)
+{
+    SSCOP_DSC *dsc = user;
+
+    diag(COMPONENT,DIAG_DEBUG,"Timer IDLE has expired");
+    dsc->timer_idle = NULL;
+    START_TIMER(noresp);
+    sscop_poll_exp(user);
+}
+
+
+static void sscop_noresp_exp(void *user)
+{
+    SSCOP_DSC *dsc = user;
+
+    diag(COMPONENT,DIAG_DEBUG,"Timer NORESP has expired");
+    dsc->timer_noresp = NULL;
+    if (dsc->state != sscop_ready) {
+	diag(COMPONENT,DIAG_FATAL,"Timer NORESP expired in state %s",
+	  state_name[dsc->state]);
+	return;
+    }
+    reset_data_transfer_timers(dsc);
+    maa_error(dsc,'P',0);
+    send_pdu(dsc,SSCOP_END,NULL,1);
+    prepare_retrieval(dsc);
+    next_state(dsc,sscop_idle);
+    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
+}
+
+
+/* --- Transmit engine ----------------------------------------------------- */
+
+
+static void try_to_send(SSCOP_DSC *dsc)
+{
+    BUFFER *buf,*buf2;
+
+    if (dsc->state != sscop_ready) return;
+    while (queue_peek(&dsc->rt_q) || NORM_TX(dsc->vt_s) <
+      NORM_TX(dsc->vt_ms) || dsc->timer_idle) {
+	buf = queue_get(&dsc->rt_q);
+	if (buf) {
+	    if (!(buf2 = queue_lookup(&dsc->tx_buf,buf->key)))
+		diag(COMPONENT,DIAG_FATAL,"didn't find PDU %d in TX buffer",
+		  buf->key);
+	    emit_pdu(dsc,buf);
+	    buf2->extra = dsc->vt_ps;
+#ifdef POLL_AFTER_RETRANSMISSION
+	    if (!queue_peek(dsc->rt_q))
+		/* fall through to goto */
+#endif
+	    goto B; /* sigh ... */
+	}
+	else {
+	    if (!queue_peek(&dsc->tx_q)) return;
+	    if (NORM_TX(dsc->vt_s) < NORM_TX(dsc->vt_ms)) {
+		buf = queue_get(&dsc->tx_q);
+		buf2 = build_pdu(dsc,SSCOP_SD,buf->data,buf->length);
+		buffer_discard(buf);
+		buf2->key = dsc->vt_s;
+		buf2->extra = dsc->vt_ps;
+		emit_pdu(dsc,buffer_clone(buf2));
+		queue_put(&dsc->tx_buf,buf2);
+		INC24(dsc->vt_s);
+	B:
+		/* B */
+		dsc->vt_pd++;
+		if (dsc->timer_poll) {
+		    if (dsc->vt_pd < dsc->cf_max_pd) continue;
+		}
+		else {
+		    if (!dsc->timer_idle) STOP_TIMER(dsc->timer_keepalive);
+		    else {
+			STOP_TIMER(dsc->timer_idle);
+			START_TIMER(noresp);
+		    }
+		    if (dsc->vt_pd < dsc->cf_max_pd) {
+			START_TIMER(poll);
+			continue;
+		    }
+		}
+	    }
+	    else {
+		STOP_TIMER(dsc->timer_idle);
+		START_TIMER(noresp);
+	    }
+	}
+	/* A */
+	INC24(dsc->vt_ps);
+	send_pdu(dsc,SSCOP_POLL,NULL,0);
+	dsc->vt_pd = 0;
+	START_TIMER(poll);
+    }
+}
+
+
+/* --- Incoming non-CC PDUs in Data Transfer Ready state ------------------- */
+
+
+static void start_error_recov(SSCOP_DSC *dsc,char code)
+{
+    reset_data_transfer_timers(dsc);
+    maa_error(dsc,code,0);
+    /* D */
+    dsc->vt_cc = 1;
+    INC8(dsc->vt_sq);
+    initialize_vr_mr(dsc);
+    send_pdu(dsc,SSCOP_ER,NULL,0);
+    prepare_recovery(dsc);
+    START_TIMER(cc);
+    next_state(dsc,sscop_outrec);
+}
+
+
+static void data_sd(SSCOP_DSC *dsc,int s,void *msg,int length) /* 20.38-39 */
+{
+    BUFFER *buf;
+
+    if (NORM_RX(s) >= NORM_RX(dsc->vr_mr)) {
+	if (NORM_RX(dsc->vr_h) >= NORM_RX(dsc->vr_mr)) {
+	    send_ustat(dsc,dsc->vr_h,dsc->vr_mr);
+	    dsc->vr_h = dsc->vr_mr;
+	}
+	return;
+    }
+    if (s == dsc->vr_r) {
+	if (dsc->ops->data_ind) dsc->ops->data_ind(dsc->user,msg,length,s);
+	if (s == dsc->vr_h) {
+	    dsc->vr_r = dsc->vr_h = MOD24(s+1);
+	    update_vr_mr(dsc);
+	    return;
+	}
+	while (1) {
+	    INC24(dsc->vr_r);
+	    buf = queue_lookup(&dsc->rx_buf,dsc->vr_r);
+	    if (!buf) break;
+	    queue_remove(&dsc->rx_buf,buf);
+	    if (dsc->ops->data_ind)
+		dsc->ops->data_ind(dsc->user,buf->data,buf->length,buf->key);
+	    buffer_discard(buf);
+	}
+	return;
+    }
+    buf = buffer_create(length,s);
+    memcpy(buf->data,msg,length);
+    if (s == dsc->vr_h) {
+	queue_put(&dsc->rx_buf,buf);
+	INC24(dsc->vr_h);
+	update_vr_mr(dsc);
+	return;
+    }
+    if (NORM_RX(dsc->vr_h) < NORM_RX(s)) {
+	queue_put(&dsc->rx_buf,buf);
+	send_ustat(dsc,dsc->vr_h,s);
+	dsc->vr_h = MOD24(s+1);
+	update_vr_mr(dsc);
+	return;
+    }
+    if (queue_lookup(&dsc->rx_buf,s)) queue_put(&dsc->rx_buf,buf);
+    else {
+	buffer_discard(buf);
+	start_error_recov(dsc,'Q');
+    }
+}
+
+
+/*
+ * Some of the NORM_RXs in data_poll are certainly unnecessary. Maybe even
+ * all of them could be removed ...
+ */
+
+
+static void data_poll(SSCOP_DSC *dsc,int s) /* 20.41-42 */
+{
+    int curr,i;
+
+    if (!dsc->list) dsc->list = alloc(dsc->cf_max_stat*sizeof(int));
+    if (NORM_RX(dsc->vr_h) > NORM_RX(s)) {
+	start_error_recov(dsc,'Q');
+	return;
+    }
+    dsc->vr_h = NORM_RX(dsc->vr_mr) < NORM_RX(s) ? dsc->vr_mr : s;
+    update_vr_mr(dsc);
+    /* K */
+    curr = 0;
+    i = dsc->vr_r;
+    while (NORM_RX(i) < NORM_RX(dsc->vr_h)) {
+	if (queue_lookup(&dsc->rx_buf,i)) {
+	    INC24(i);
+	    continue;
+	}
+	dsc->list[curr++] = htonl(i);
+	if (curr >= dsc->cf_max_stat) {
+	    send_pdu(dsc,SSCOP_STAT,dsc->list,curr*4);
+	    curr = 0;
+	    dsc->list[curr++] = htonl(i);
+	}
+	do INC24(i);
+	while (i != dsc->vr_h && !queue_lookup(&dsc->rx_buf,i));
+	dsc->list[curr++] = htonl(i);
+    }
+    if (NORM_RX(i) > NORM_RX(dsc->vr_h)) dsc->list[curr++] = htonl(i);
+    send_pdu(dsc,SSCOP_STAT,dsc->list,curr*4);
+}
+
+
+static void data_ustat(SSCOP_DSC *dsc,int mr,int r,int e1,int e2) /* 20.43 */
+{
+    BUFFER *buf,*buf2;
+    int seq1,seq2,i;
+
+    if (NORM_TX(dsc->vt_a) <= NORM_TX(r) && NORM_TX(r) < NORM_TX(dsc->vt_s)) {
+	for (i = dsc->vt_a; i != r; INC24(i)) {
+	    buf = queue_lookup(&dsc->tx_buf,i);
+	    if (buf) {
+		queue_remove(&dsc->tx_buf,buf);
+		buffer_discard(buf);
+	    }
+	}
+	dsc->vt_a = r;
+	dsc->vt_ms = mr;
+	seq1 = e1;
+	seq2 = e2;
+	if (NORM_TX(dsc->vt_a) <= NORM_TX(seq1) && NORM_TX(seq1) <
+	  NORM_TX(seq2) && NORM_TX(seq2) < NORM_TX(dsc->vt_s)) {
+	    /* G */
+	    while ((buf = queue_lookup(&dsc->tx_buf,seq1))) {
+		buf2 = buffer_clone(buf);
+		queue_put(&dsc->rt_q,buf2);
+		INC24(seq1);
+		if (seq1 == seq2) {
+		    maa_error(dsc,'V',e2-e1);
+		    return;
+		}
+	    }
+	}
+    }
+    /* F */
+    start_error_recov(dsc,'T');
+}
+
+
+static void data_stat(SSCOP_DSC *dsc,int ps,int mr,int r,unsigned long *list,
+  int length) /* 20.44-46 */
+{
+    BUFFER *buf,*buf2;
+    unsigned long *curr,tmp;
+    int i,count,seq1,seq2;
+
+    if (NORM_TX(dsc->vt_pa) > NORM_TX(ps) || NORM_TX(ps) >
+      NORM_TX(dsc->vt_ps)) {
+	start_error_recov(dsc,'R');
+	return;
+    }
+    if (NORM_TX(dsc->vt_a) > NORM_TX(r) || NORM_TX(r) >
+      NORM_TX(dsc->vt_s)) {
+	/* H */
+	start_error_recov(dsc,'S');
+	return;
+    }
+    for (i = dsc->vt_a; i != r; INC24(i)) {
+	buf = queue_lookup(&dsc->tx_buf,i);
+	if (buf) {
+	    queue_remove(&dsc->tx_buf,buf);
+	    buffer_discard(buf);
+	}
+    }
+    dsc->vt_a = r;
+    dsc->vt_pa = ps;
+    dsc->vt_ms = mr;
+    i = length;
+    curr = list;
+    count = 0;
+    if (i > 1) {
+	tmp = *curr++;
+	seq1 = SSCOP_N(tmp);
+	i--;
+	if (NORM_TX(seq1) >= NORM_TX(dsc->vt_s)) {
+	    /* H */
+	    start_error_recov(dsc,'S');
+	    return;
+	}
+	/* I */
+	do {
+	    tmp = *curr++;
+	    seq2 = SSCOP_N(tmp);
+	    i--;
+	    if (NORM_TX(seq1) >= NORM_TX(seq2) ||
+	      NORM_TX(seq2) > NORM_TX(dsc->vt_s)) {
+		/* H */
+		start_error_recov(dsc,'S');
+		return;
+	    }
+	    do {
+		if (!(buf = queue_lookup(&dsc->tx_buf,seq1))) {
+		    /* H */
+		    start_error_recov(dsc,'S');
+		    return;
+		}
+		if (NORM_TX(buf->extra) < NORM_TX(ps) &&
+		  NORM_TX(ps) <= NORM_TX(dsc->vt_ps) &&
+		  !queue_lookup(&dsc->rt_q,seq1)) {
+		    buf2 = buffer_clone(buf);
+		    queue_put(&dsc->rt_q,buf2);
+		    count++;
+		}
+		INC24(seq1);
+	    }
+	    while (seq1 != seq2);
+	    /* J */
+	    if (!i) break;
+	    tmp = *curr++;
+	    seq2 = SSCOP_N(tmp);
+	    i--;
+	    if (NORM_TX(seq1) >= NORM_TX(seq2) ||
+	      NORM_TX(seq2) > NORM_TX(dsc->vt_s)) {
+		/* H */
+		start_error_recov(dsc,'S');
+		return;
+	    }
+	    do {
+		if (dsc->clear_buffers) {
+		    buf = queue_lookup(&dsc->tx_buf,seq1);
+		    if (buf) {
+			queue_remove(&dsc->tx_buf,buf);
+			buffer_discard(buf);
+		    }
+		}
+		INC24(seq1);
+	    }
+	    while (seq1 != seq2);
+	}
+	while (i);
+	maa_error(dsc,'V',count);
+    }
+    /* L */
+    if (dsc->credit != (NORM_TX(dsc->vt_s) < NORM_TX(dsc->vt_ms)))
+	maa_error(dsc,(dsc->credit = !dsc->credit) ? 'X' : 'W',0);
+    if (dsc->timer_poll) START_TIMER(noresp);
+    else if (!dsc->timer_idle) {
+	    STOP_TIMER(dsc->timer_keepalive);
+	    STOP_TIMER(dsc->timer_noresp);
+	    START_TIMER(idle);
+        }
+}
+
+
+/* --- Incoming PDUs ------------------------------------------------------- */
+
+
+/*
+ * Returns 0 if the descriptor might conceivably be gone when returning,
+ * 1 otherwise.
+ */
+ 
+static int handle_sscop_pdu(SSCOP_DSC *dsc,void *msg,int size)
+{
+    PDU_VARS;
+
+    dump_pdu(msg,size);
+    if (DECOMPOSE_PDU(dsc,msg,size)) return 1;
+    PRINT_PDU(dsc,"RECV",msg);
+    switch (type) {
+	case SSCOP_UD:
+	    if (dsc->ops->unitdata) dsc->ops->unitdata(dsc->user,msg,length);
+	    return 1;
+	case SSCOP_MD:
+	    if (dsc->ops->maa_data) dsc->ops->maa_data(dsc->user,msg,length);
+	    return 1;
+	default:
+	    break;
+    }
+    switch (dsc->state) {
+	case sscop_idle:
+	    switch (type) {
+		case SSCOP_BGREJ:
+		    bad_pdu(dsc,type);
+		    return 1;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			send_pdu(dsc,SSCOP_BGREJ,NULL,0); /* no SSCOP-UU */
+			return 1;
+		    }
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->estab_ind)
+			dsc->ops->estab_ind(dsc->user,msg,length);
+		    return 1;
+		case SSCOP_ENDAK:
+		    return 1;
+		case SSCOP_END:
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    return 1;
+		case SSCOP_ER:
+		case SSCOP_POLL:
+		case SSCOP_SD:
+		case SSCOP_BGAK:
+		case SSCOP_ERAK:
+		case SSCOP_STAT:
+		case SSCOP_USTAT:
+		case SSCOP_RS:
+		case SSCOP_RSAK:
+		    bad_pdu(dsc,type);
+		    send_pdu(dsc,SSCOP_END,NULL,1);
+		    return 1;
+		default:
+		    break;
+	    }
+	    break;
+	case sscop_outconn: /* 20.8-10 */
+	    switch (type) {
+		case SSCOP_ENDAK:
+		case SSCOP_SD:
+		case SSCOP_ERAK:
+		case SSCOP_END:
+		case SSCOP_STAT:
+		case SSCOP_USTAT:
+		case SSCOP_POLL:
+		case SSCOP_ER:
+		case SSCOP_RSAK:
+		case SSCOP_RS:
+		    return 1; /* ignore PDU */
+		case SSCOP_BGAK:
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    initialize_state_variables(dsc);
+		    set_data_transfer_timers(dsc);
+		    next_state(dsc,sscop_ready);
+		    if (dsc->ops->estab_conf)
+			dsc->ops->estab_conf(dsc->user,msg,length);
+		    return 1;
+		case SSCOP_BGREJ:
+		    STOP_TIMER(dsc->timer_cc);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,1);
+		    return 0;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) return 1;
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    initialize_vr_mr(dsc);
+		    send_pdu(dsc,SSCOP_BGAK,NULL,0);
+		    initialize_state_variables(dsc);
+		    set_data_transfer_timers(dsc);
+		    next_state(dsc,sscop_ready);
+		    if (dsc->ops->estab_conf)
+			dsc->ops->estab_conf(dsc->user,msg,length);
+		    return 1;
+		default:
+		    break;
+	    }
+	    break;
+	case sscop_inconn: /* 20.11-13 */
+	    switch (type) {
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) return 1;
+		    dsc->vt_ms = mr;
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,1);
+		    return 1;
+		case SSCOP_ER:
+		case SSCOP_BGAK:
+		case SSCOP_ERAK:
+		case SSCOP_RSAK:
+		case SSCOP_RS:
+		    bad_pdu(dsc,type);
+		    return 1;
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    bad_pdu(dsc,type);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,1);
+		    return 0;
+		case SSCOP_SD:
+		case SSCOP_USTAT:
+		case SSCOP_STAT:
+		case SSCOP_POLL:
+		    bad_pdu(dsc,type);
+		    send_pdu(dsc,SSCOP_END,NULL,1);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_END:
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,!s);
+		    return 0;
+		default:
+		    break;
+	    }
+	    break;
+	case sscop_outdisc: /* 20.14-16 */
+	    switch (type) {
+		case SSCOP_SD:
+		case SSCOP_BGAK:
+		case SSCOP_POLL:
+		case SSCOP_STAT:
+		case SSCOP_USTAT:
+		case SSCOP_ERAK:
+		case SSCOP_RS:
+		case SSCOP_RSAK:
+		case SSCOP_ER:
+		    return 1;
+		case SSCOP_END:
+		    STOP_TIMER(dsc->timer_cc);
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
+		    return 0;
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    STOP_TIMER(dsc->timer_cc);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
+		    return 0;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			send_pdu(dsc,SSCOP_BGAK,NULL,0);
+			resend_pdu(dsc,dsc->last_end);
+			return 1;
+		    }
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,0);
+		    return 1;
+	    }
+	case sscop_outres: /* 20.17-19 */
+	    switch (type) {
+		case SSCOP_ER:
+		case SSCOP_POLL:
+		case SSCOP_STAT:
+		case SSCOP_USTAT:
+		case SSCOP_BGAK:
+		case SSCOP_ERAK:
+		case SSCOP_SD:
+		    return 1; /* ignore */
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			send_pdu(dsc,SSCOP_BGAK,NULL,0);
+			resend_pdu(dsc,dsc->last_rs);
+			return 1;
+		    }
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,1);
+		    return 1;
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    STOP_TIMER(dsc->timer_cc);
+		    bad_pdu(dsc,type);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_END:
+		    STOP_TIMER(dsc->timer_cc);
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,!s);
+		    return 0;
+		case SSCOP_RS:
+		    if (detect_retransmission(dsc,sq)) return 1;
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    initialize_vr_mr(dsc);
+		    send_pdu(dsc,SSCOP_RSAK,NULL,0);
+		    initialize_state_variables(dsc);
+		    set_data_transfer_timers(dsc);
+		    next_state(dsc,sscop_ready);
+		    if (dsc->ops->res_conf) dsc->ops->res_conf(dsc->user);
+		    return 1;
+		case SSCOP_RSAK:
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    initialize_state_variables(dsc);
+		    set_data_transfer_timers(dsc);
+		    next_state(dsc,sscop_ready);
+		    if (dsc->ops->res_conf) dsc->ops->res_conf(dsc->user);
+		    return 1;
+		default:
+		    break;
+	    }
+	    break;
+	case sscop_inres: /* 20.20-22 */
+	    switch (type) {
+		case SSCOP_SD:
+		case SSCOP_POLL:
+		case SSCOP_STAT:
+		case SSCOP_USTAT:
+		    send_pdu(dsc,SSCOP_END,NULL,1);
+		    /* fall through */
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    bad_pdu(dsc,type);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_END:
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,!s);
+		    return 0;
+		case SSCOP_ER:
+		case SSCOP_BGAK:
+		case SSCOP_ERAK:
+		case SSCOP_RSAK:
+		    bad_pdu(dsc,type);
+		    return 1;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,1);
+		    return 1;
+		case SSCOP_RS:
+		    if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
+		    return 1;
+		default:
+		    break;
+	    }
+	    break;
+	case sscop_outrec: /* 20.23-26 */
+	    switch (type) {
+		case SSCOP_BGAK:
+		case SSCOP_RSAK:
+		    bad_pdu(dsc,type);
+		    return 1;
+		case SSCOP_ERAK:
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_recresp);
+		    deliver_data(dsc);
+		    if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
+		    return 1;
+		case SSCOP_END:
+		    STOP_TIMER(dsc->timer_cc);
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    clear_receive_buffer(dsc);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,!s);
+		    return 0;
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    bad_pdu(dsc,type);
+		    STOP_TIMER(dsc->timer_cc);
+		    clear_receive_buffer(dsc);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_STAT:
+		case SSCOP_USTAT:
+		case SSCOP_POLL:
+		case SSCOP_SD:
+		    return 1;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    clear_receive_buffer(dsc);
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,1);
+		    return 1;
+		case SSCOP_ER:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    initialize_vr_mr(dsc);
+		    send_pdu(dsc,SSCOP_ERAK,NULL,0);
+		    deliver_data(dsc);
+		    next_state(dsc,sscop_recresp);
+		    if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
+		    return 1;
+		case SSCOP_RS:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    STOP_TIMER(dsc->timer_cc);
+		    dsc->vt_ms = mr;
+		    clear_receive_buffer(dsc);
+		    next_state(dsc,sscop_inres);
+		    if (dsc->ops->res_ind)
+			dsc->ops->res_ind(dsc->user,msg,length);
+		    return 0;
+		default:
+		    break;
+	    }
+	case sscop_recresp: /* 20.27-29 */
+	    switch (type) {
+		case SSCOP_BGAK:
+		case SSCOP_RSAK:
+		    bad_pdu(dsc,type);
+		    return 1;
+		case SSCOP_ERAK:
+		case SSCOP_SD:
+		case SSCOP_POLL:
+		    return 1;
+		case SSCOP_END:
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,!s);
+		    return 0;
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    bad_pdu(dsc,type);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_RS:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inres);
+		    if (dsc->ops->res_ind)
+			dsc->ops->res_ind(dsc->user,msg,length);
+		    return 1;
+		case SSCOP_ER:
+		    if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
+		    else send_pdu(dsc,SSCOP_ERAK,NULL,0);
+		    return 1;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,1);
+		    return 1;
+		case SSCOP_STAT:
+		case SSCOP_USTAT:
+		    bad_pdu(dsc,type);
+		    send_pdu(dsc,SSCOP_END,NULL,1);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		default:
+		    break;
+	    }
+	case sscop_inrec: /* 20.30-33 */
+	    switch (type) {
+		case SSCOP_END:
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,!s);
+		    return 0;
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    bad_pdu(dsc,type);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_USTAT:
+		case SSCOP_STAT:
+		case SSCOP_POLL:
+		case SSCOP_SD:
+		    bad_pdu(dsc,type);
+		    send_pdu(dsc,SSCOP_END,NULL,1);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_RSAK:
+		case SSCOP_BGAK:
+		case SSCOP_ERAK:
+		    bad_pdu(dsc,type);
+		    return 1;
+		case SSCOP_RS:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inres);
+		    if (dsc->ops->res_ind)
+			dsc->ops->res_ind(dsc->user,msg,length);
+		    return 1;
+		case SSCOP_ER:
+		    if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
+		    return 1;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			bad_pdu(dsc,type);
+			return 1;
+		    }
+		    dsc->vt_ms = mr;
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,1);
+		    return 1;
+		default:
+		    break;
+	    }
+	case sscop_ready: /* 20.34-46 */
+	    switch (type) {
+		case SSCOP_BGAK:
+		case SSCOP_ERAK:
+		case SSCOP_RSAK:
+		    return 1;
+		case SSCOP_ER:
+		    if (detect_retransmission(dsc,sq)) {
+			START_TIMER(noresp);
+			send_pdu(dsc,SSCOP_ERAK,NULL,0);
+			return 1;
+		    }
+		    reset_data_transfer_timers(dsc);
+		    dsc->vt_ms = mr;
+		    prepare_recovery(dsc);
+		    next_state(dsc,sscop_inrec);
+		    deliver_data(dsc);
+		    if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
+		    return 1;
+		case SSCOP_BGN:
+		    if (detect_retransmission(dsc,sq)) {
+			START_TIMER(noresp);
+			send_pdu(dsc,SSCOP_BGAK,NULL,0);
+			return 1;
+		    }
+		    reset_data_transfer_timers(dsc);
+		    dsc->vt_ms = mr;
+		    prepare_retrieval(dsc);
+		    next_state(dsc,sscop_inconn);
+		    if (dsc->ops->restart)
+			dsc->ops->restart(dsc->user,msg,length,1);
+		    return 1;
+		case SSCOP_ENDAK:
+		case SSCOP_BGREJ:
+		    reset_data_transfer_timers(dsc);
+		    bad_pdu(dsc,type);
+		    prepare_retrieval(dsc);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,NULL,0,0);
+		    return 0;
+		case SSCOP_RS:
+		    if (detect_retransmission(dsc,sq)) {
+			START_TIMER(noresp);
+			send_pdu(dsc,SSCOP_RSAK,NULL,0);
+			return 1;
+		    }
+		    reset_data_transfer_timers(dsc);
+		    dsc->vt_ms = mr;
+		    prepare_retrieval(dsc);
+		    next_state(dsc,sscop_inres);
+		    if (dsc->ops->res_ind)
+			dsc->ops->res_ind(dsc->user,msg,length);
+		    return 1;
+		case SSCOP_END:
+		    reset_data_transfer_timers(dsc);
+		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
+		    prepare_retrieval(dsc);
+		    next_state(dsc,sscop_idle);
+		    if (dsc->ops->rel_ind)
+			dsc->ops->rel_ind(dsc->user,msg,length,!s);
+		    return 0;
+		case SSCOP_SD:
+		    data_sd(dsc,s,msg,length);
+		    return 1;
+		case SSCOP_POLL:
+		    dsc->vr_ps = ps; /* store */
+		    data_poll(dsc,s);
+		    return 1;
+		case SSCOP_USTAT:
+		    data_ustat(dsc,mr,r,SSCOP_N(((unsigned long *) msg)[0]),
+		      SSCOP_N(((unsigned long *) msg)[1]));
+		    return 1;
+		case SSCOP_STAT:
+		    data_stat(dsc,ps,mr,r,msg,length/4);
+		    return 1;
+		default:
+		    break;
+	    }
+    }
+    return 1;
+}
+
+
+void sscop_pdu(SSCOP_DSC *dsc,void *msg,int size)
+{
+    if (handle_sscop_pdu(dsc,msg,size))
+	if (dsc->state == sscop_ready) try_to_send(dsc);
+}
+
+
+/* --- From SSCOP user ----------------------------------------------------- */
+
+
+void sscop_retrieve(SSCOP_DSC *dsc,int rn)
+{
+    BUFFER *buf;
+    int i;
+
+    if (dsc->state != sscop_idle && dsc->state != sscop_inconn &&
+      dsc->state != sscop_outdisc && dsc->state != sscop_inres &&
+      dsc->state != sscop_recresp && dsc->state != sscop_inrec)
+	diag(COMPONENT,DIAG_FATAL,"sscop_retrieve invoked in state %s",
+	  state_name[dsc->state]);
+    if (rn != SSCOP_RN_UNKNOWN)
+	for (i = rn == SSCOP_RN_TOTAL ? dsc->vt_a : MOD24(rn+1);
+	  NORM_TX(dsc->vt_a) <= NORM_TX(i) && NORM_TX(i) < NORM_TX(dsc->vt_s);
+	  INC24(i)) {
+	    buf = queue_lookup(&dsc->tx_buf,i);
+	    if (buf) {
+		queue_remove(&dsc->tx_buf,buf);
+		if (dsc->ops->retr_ind)
+		    dsc->ops->retr_ind(dsc->user,buf->data,buf->length);
+		buffer_discard(buf);
+	    }
+	}
+    while ((buf = queue_get(&dsc->tx_q))) {
+	if (dsc->ops->retr_ind)
+	    dsc->ops->retr_ind(dsc->user,buf->data,buf->length);
+	buffer_discard(buf);
+    }
+    if (dsc->ops->retr_comp) dsc->ops->retr_comp(dsc->user);
+}
+
+
+void sscop_send(SSCOP_DSC *dsc,void *buffer,int size) /* 20.38 */
+{
+    BUFFER *buf;
+
+    if (dsc->state != sscop_ready && (dsc->state != sscop_outrec ||
+      dsc->clear_buffers)) return; /* 20.23 */
+    buf = buffer_create(size,0);
+    memcpy(buf->data,buffer,size);
+    queue_put(&dsc->tx_q,buf);
+    try_to_send(dsc);
+}
+
+
+void sscop_estab_req(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel)
+{
+    switch (dsc->state) {
+	case sscop_outdisc: /* 20.14 */
+	    STOP_TIMER(dsc->timer_cc);
+	    /* fall through */
+	case sscop_idle: /* 20.5 */
+	    clear_transmitter(dsc);
+	    dsc->clear_buffers = buf_rel;
+	    dsc->vt_cc = 1;
+	    INC8(dsc->vt_sq);
+	    initialize_vr_mr(dsc);
+	    send_pdu(dsc,SSCOP_BGN,uu_data,uu_length);
+	    START_TIMER(cc);
+	    next_state(dsc,sscop_outconn);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_FATAL,"sscop_estab_req invoked in state %s",
+      state_name[dsc->state]);
+}
+
+
+void sscop_estab_resp(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel)
+    /* 20.11 */
+{
+    if (dsc->state != sscop_inconn)
+	diag(COMPONENT,DIAG_FATAL,"sscop_estab_resp invoked in state %s",
+	  state_name[dsc->state]);
+    clear_transmitter(dsc);
+    dsc->clear_buffers = buf_rel;
+    initialize_vr_mr(dsc);
+    send_pdu(dsc,SSCOP_BGAK,uu_data,uu_length);
+    initialize_state_variables(dsc);
+    set_data_transfer_timers(dsc);
+    next_state(dsc,sscop_ready);
+    try_to_send(dsc); /* probably not ... */
+}
+
+
+void sscop_rel_req(SSCOP_DSC *dsc,void *uu_data,int uu_length)
+{
+    switch (dsc->state) {
+	case sscop_outrec: /* 20.24 */
+	    clear_receive_buffer(dsc);
+	    /* fall through */
+	case sscop_outconn: /* 20.9 */
+	case sscop_outres: /* 20.18 */
+	    STOP_TIMER(dsc->timer_cc);
+	    /* fall through */
+	case sscop_inres: /* 20.20 */
+	case sscop_recresp: /* 20.28 */
+	case sscop_inrec: /* 20.30 */
+	    dsc->vt_cc = 1;
+	    send_pdu(dsc,SSCOP_END,uu_data,uu_length);
+	    START_TIMER(cc);
+	    next_state(dsc,sscop_outdisc);
+	    return;
+	case sscop_inconn: /* 20.11 */
+	    send_pdu(dsc,SSCOP_BGREJ,uu_data,uu_length);
+	    next_state(dsc,sscop_idle);
+	    return;
+	case sscop_ready: /* 20.34 */
+	    reset_data_transfer_timers(dsc);
+	    dsc->vt_cc = 1;
+	    send_pdu(dsc,SSCOP_END,uu_data,uu_length);
+	    prepare_retrieval(dsc);
+	    START_TIMER(cc);
+	    next_state(dsc,sscop_outdisc);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_FATAL,"sscop_rel_req invoked in state %s",
+      state_name[dsc->state]);
+}
+
+
+void sscop_res_req(SSCOP_DSC *dsc,void *uu_data,int uu_length)
+{
+    switch (dsc->state) {
+	case sscop_outrec: /* 20.25 */
+	    STOP_TIMER(dsc->timer_cc);
+	    clear_receive_buffer(dsc);
+	    /* fall through */
+	case sscop_recresp: /* 20.29 */
+	    dsc->vt_cc = 1;
+	    INC8(dsc->vt_sq);
+	    initialize_vr_mr(dsc);
+	    send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
+	    clear_transmitter(dsc);
+	    START_TIMER(cc);
+	    next_state(dsc,sscop_outres);
+	    return;
+	case sscop_inrec: /* 20.30 */
+	    clear_transmitter(dsc);
+	    dsc->vt_cc = 1;
+	    INC8(dsc->vt_sq);
+	    initialize_vr_mr(dsc);
+	    send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
+	    START_TIMER(cc);
+	    next_state(dsc,sscop_outres);
+	    return;
+	case sscop_ready: /* 20.34 */
+	    reset_data_transfer_timers(dsc);
+	    dsc->vt_cc = 1;
+	    INC8(dsc->vt_sq);
+	    initialize_vr_mr(dsc);
+	    send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
+	    release_buffers(dsc);
+	    START_TIMER(cc);
+	    next_state(dsc,sscop_outres);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_FATAL,"sscop_res_req invoked in state %s",
+      state_name[dsc->state]);
+}
+
+
+void sscop_res_resp(SSCOP_DSC *dsc) /* 20.20 */
+{
+    if (dsc->state != sscop_inres)
+	diag(COMPONENT,DIAG_FATAL,"sscop_res_resp invoked in state %s",
+	  state_name[dsc->state]);
+    initialize_vr_mr(dsc);
+    send_pdu(dsc,SSCOP_RSAK,NULL,0);
+    clear_transmitter(dsc);
+    initialize_state_variables(dsc);
+    set_data_transfer_timers(dsc);
+    next_state(dsc,sscop_ready);
+    try_to_send(dsc); /* probably not ... */
+}
+
+
+void sscop_rec_resp(SSCOP_DSC *dsc)
+{
+    switch (dsc->state) {
+	case sscop_inrec: /* 20.30 */
+	    initialize_vr_mr(dsc);
+	    send_pdu(dsc,SSCOP_ERAK,NULL,0);
+	    /* fall through */
+	case sscop_recresp: /* 20.28 */
+	    if (!dsc->clear_buffers) clear_transmission_buffer(dsc);
+	    initialize_state_variables(dsc);
+	    set_data_transfer_timers(dsc);
+	    next_state(dsc,sscop_ready);
+	    try_to_send(dsc);
+	    return;
+	default:
+	    break;
+    }
+    diag(COMPONENT,DIAG_FATAL,"sscop_rec_resp invoked in state %s",
+      state_name[dsc->state]);
+}
+
+
+void sscop_unitdata(SSCOP_DSC *dsc,void *buffer,int size) /* 20.47-48 */
+{
+    send_pdu(dsc,SSCOP_UD,buffer,size);
+}
+
+
+void sscop_maa_data(SSCOP_DSC *dsc,void *buffer,int size) /* 20.47-48 */
+{
+    send_pdu(dsc,SSCOP_MD,buffer,size);
+}
+
+
+void start_sscop(SSCOP_DSC *dsc,SSCOP_USER_OPS *ops,void *user_data)
+{
+    dsc->ops = ops;
+    dsc->user = user_data;
+    dsc->vt_sq = dsc->vr_sq = 0; /* 20.5 */
+    dsc->clear_buffers = 1;
+    dsc->state = sscop_idle;
+    dsc->timer_cc = dsc->timer_poll = dsc->timer_noresp =
+      dsc->timer_keepalive = dsc->timer_idle = NULL;
+    queue_init(&dsc->rx_buf);
+    queue_init(&dsc->tx_q);
+    queue_init(&dsc->tx_buf);
+    queue_init(&dsc->rt_q);
+    dsc->last_bgn = dsc->last_end = dsc->last_rs = dsc->last_er = NULL;
+    dsc->cf_max_cc = SSCOP_CF_MaxCC;
+    dsc->cf_max_pd = SSCOP_CF_MaxPD;
+    dsc->cf_max_stat = SSCOP_CF_MaxSTAT;
+    dsc->cf_timer_cc = TIMER_CC;
+    dsc->cf_timer_poll = TIMER_POLL;
+    dsc->cf_timer_noresp = TIMER_NORESP;
+    dsc->cf_timer_keepalive = TIMER_KEEPALIVE;
+    dsc->cf_timer_idle = TIMER_IDLE;
+    dsc->list = NULL;
+}
+
+
+void stop_sscop(SSCOP_DSC *dsc)
+{
+    if (dsc->state != sscop_idle)
+	diag(COMPONENT,DIAG_WARN,"stopping dsc in state %s",
+	  state_name[dsc->state]);
+    dsc->state = sscop_idle; /* avoid send attempts */
+    STOP_TIMER(dsc->timer_cc);
+    STOP_TIMER(dsc->timer_poll);
+    STOP_TIMER(dsc->timer_noresp);
+    STOP_TIMER(dsc->timer_keepalive);
+    STOP_TIMER(dsc->timer_idle);
+    queue_clear(&dsc->rx_buf);
+    queue_clear(&dsc->tx_q);
+    queue_clear(&dsc->tx_buf);
+    queue_clear(&dsc->rt_q);
+    if (dsc->last_bgn) buffer_discard(dsc->last_bgn);
+    if (dsc->last_end) buffer_discard(dsc->last_end);
+    if (dsc->last_rs) buffer_discard(dsc->last_rs);
+    if (dsc->last_er) buffer_discard(dsc->last_er);
+    if (dsc->list) free(dsc->list);
+}
diff -ur --new-file old/atm/saal/sscop.h new/atm/saal/sscop.h
--- old/atm/saal/sscop.h	Thu Jan  1 01:00:00 1970
+++ new/atm/saal/sscop.h	Tue Oct 24 17:55:57 1995
@@ -0,0 +1,132 @@
+/* sscop.h - SSCOP (Q.2110) user interface */
+ 
+/* Written 1995 by Werner Almesberger, EPFL-LRC */
+ 
+
+#ifndef SSCOP_H
+#define SSCOP_H
+
+#include "atmd.h"
+
+
+typedef enum { sscop_idle,sscop_outconn,sscop_inconn,sscop_outdisc,
+  sscop_outres,sscop_inres,sscop_outrec,sscop_recresp,sscop_inrec,
+  sscop_ready } SSCOP_STATE;
+
+typedef struct {
+    SSCOP_STATE state;
+    struct _sscop_user_ops *ops;
+    void *user;
+    /* --- Configurable parameters ----------------------------------------- */
+    int cf_max_cc,cf_max_pd,cf_max_stat;
+    int cf_timer_cc,cf_timer_poll,cf_timer_noresp,cf_timer_keepalive,
+      cf_timer_idle;
+    /* --- SSCOP information, TX part -------------------------------------- */
+    int vt_s; /* send sequence number */
+    int vt_ps; /* poll send sequence number */
+    int vt_a; /* acknowledge sequence number */
+    int vt_pa; /* poll sequence number */
+    int vt_ms; /* maximum send sequence */
+    int vt_pd; /* SD PDUs between POLL PDUs */
+    int vt_cc; /* number of unacknowledged BSG, END, ER or RS PDUs */
+    int vt_sq; /* connection number */
+    /* --- SSCOP information, RX part -------------------------------------- */
+    int vr_r; /* receiver sequence number */
+    int vr_h; /* highest expected SD PDU */
+    int vr_mr; /* maximum acceptable (receiver) */
+    int vr_sq; /* connection number */
+    int vr_ps; /* non-Q.2110: keeps N(PS) received in last POLL for STAT */
+    /* Other variables */
+    int clear_buffers;
+    int credit;
+    /* Timers */
+    TIMER *timer_cc,*timer_poll,*timer_noresp,*timer_keepalive,*timer_idle;
+    /* Queues and buffers */
+    QUEUE tx_buf,tx_q,rx_buf,rt_q;
+    BUFFER *last_bgn,*last_end,*last_rs,*last_er; /* for retransmission */
+    /* Misc items */
+    int *list; /* STAT construction list */
+} SSCOP_DSC;
+
+
+/*
+ * Note: UU data of primitives carrying such is only available if
+ *  - the "user" flag is set (if available) and
+ *  - uu_data is non-NULL, and
+ *  - uu_length is non-zero
+ * in all other cases, uu_data must not be dereferenced.
+ *
+ * Note: the "ind" parameter in restart indicates whether the release primitive
+ * is an AA-RELEASE.indication (ind = 1) or to an AA-RELEASE.confirm (ind = 0).
+ */
+
+
+typedef struct _sscop_user_ops {
+    void (*estab_ind)(void *user_data,void *uu_data,int uu_length);
+	/* AA-ESTABLISH.indication */
+    void (*estab_conf)(void *user_data,void *uu_data,int uu_length);
+	/* AA-ESTABLISH.confirm */
+    void (*rel_ind)(void *user_data,void *uu_data,int uu_length,int user);
+	/* AA-RELEASE.indication */
+    void (*rel_conf)(void *user_data); /* AA-RELEASE.confirm */
+    void (*restart)(void *user_data,void *uu_data,int uu_length,int ind);
+	/* AA-RELEASE.indication or AA-RELEASE.confirm immediately followed by
+	   AA-ESTABLISH.indication */
+    void (*res_ind)(void *user_data,void *uu_data,int uu_length);
+	/* AA-RESYNC.indication */
+    void (*res_conf)(void *user_data); /* AA-RESYNC.confirm */
+    void (*rec_ind)(void *user_data); /* AA-RECOVER.indication */
+    void (*data_ind)(void *user_data,void *data,int length,int sn);
+	/* AA-DATA.indication */
+    void (*unitdata)(void *user_data,void *data,int length);
+	/* AA-UNITDATA.indication */
+    void (*retr_ind)(void *user_data,void *data,int length);
+	/* AA-RETRIEVE.indication */
+    void (*retr_comp)(void *user_data); /* AA-RETRIEVE_COMPLETE.indication */
+    void (*maa_data)(void *user_data,void *data,int length);
+	/* MAA-UNITDATA.indication */
+    int (*maa_error)(void *user_data,char code,int count);
+	/* MAA-ERROR.indication */
+    void (*cpcs_send)(void *user_data,void *data,int length);
+} SSCOP_USER_OPS;
+
+
+/* Attach/detach protocol */
+
+void start_sscop(SSCOP_DSC *dsc,SSCOP_USER_OPS *ops,void *user_data);
+void stop_sscop(SSCOP_DSC *dsc);
+
+/* Connection control */
+
+void sscop_estab_req(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel);
+    /* AA-ESTABLISH.request */
+void sscop_estab_resp(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel);
+    /* AA-ESTABLISH.response */
+void sscop_rel_req(SSCOP_DSC *dsc,void *uu_data,int uu_length);
+    /* AA-RELEASE.request */
+void sscop_res_req(SSCOP_DSC *dsc,void *uu_data,int uu_length);
+    /* AA-RESYNC.request */
+void sscop_res_resp(SSCOP_DSC *dsc); /* AA-RESYNC.response */
+void sscop_rec_resp(SSCOP_DSC *dsc); /* AA-RECOVER.response */
+
+/* Incoming PDU from lower layer */
+
+void sscop_pdu(SSCOP_DSC *dsc,void *msg,int size); /* CPCS-UNITDATA.request */
+
+/* Send data */
+
+void sscop_send(SSCOP_DSC *dsc,void *buffer,int size);
+    /* AA-DATA.request, 20.38 */
+void sscop_unitdata(SSCOP_DSC *dsc,void *buffer,int size);
+    /* AA-UNIDATA.request, 20.47 */
+void sscop_maa_data(SSCOP_DSC *dsc,void *buffer,int size);
+    /* MAA-UNITDATA.request, 20.47 */
+
+/* Retrieve unsent data */
+
+#define SSCOP_RN_UNKNOWN -2 /* -1 is (theoretically) a valid rn ... */
+#define SSCOP_RN_TOTAL   -3
+
+void sscop_retrieve(SSCOP_DSC *dsc,int rn); /* AA-RETRIEVE.request */
+
+#endif
diff -ur --new-file old/atm/sigd/Makefile new/atm/sigd/Makefile
--- old/atm/sigd/Makefile	Thu Jul 18 21:42:01 1996
+++ new/atm/sigd/Makefile	Wed Jul 31 14:40:52 1996
@@ -1,17 +1,13 @@
-LIBS=-latmd -lfl # lex may want  -ll  here
-INCLUDES=-I../qgen -I.
-OBJS=atmsigd.o io.o kernel.o mess.o proto.o q2931.o saal.o sap.o sscf.o \
-  sscop.o timeout.o q.out.o lex.yy.o y.tab.o
-SSCOP_OBJS=sscop.o
+LIBS=-L../saal -lsaal -latmd -lfl # lex may want  -ll  here
+INCLUDES=-I../qgen -I../saal -I.
+OBJS=atmsigd.o io.o kernel.o mess.o proto.o q2931.o sap.o timeout.o q.out.o \
+  lex.yy.o y.tab.o
 BOOTPGMS=atmsigd
-USRPGMS=svc #test doesn't work anymore
 TRASH=q.out.h q.out.o mess.c
 MANS=
 
 include ../Rules.make
-
-
-test:			$(SSCOP_OBJS)
+CFLAGS += $(STANDARDS)
 
 atmsigd:		$(OBJS)
 			$(CC) $(LDFLAGS) -o atmsigd $(OBJS) $(LDLIBS) $(LIBS)
@@ -24,8 +20,6 @@
 
 q.out.o:
 			ln -s ../qgen/q.out.o
-
-svc:			svc.o  # don't collapse compile and link phase
 
 depend:			fake_q.out.h
 
diff -ur --new-file old/atm/sigd/atmsigd.c new/atm/sigd/atmsigd.c
--- old/atm/sigd/atmsigd.c	Thu May  9 20:27:00 1996
+++ new/atm/sigd/atmsigd.c	Tue Jul 30 20:19:21 1996
@@ -230,8 +230,10 @@
 		break;
 	    case 'd':
 		set_verbosity(NULL,DIAG_DEBUG);
+		set_verbosity("QMSG",DIAG_INFO);
+		set_verbosity("SSCOP",DIAG_INFO);
 		debug = 1;
-		q_dump = 1;
+		/*q_dump = 1;*/
 		break;
 	    case 'l':
 		set_logfile(optarg);
@@ -246,8 +248,17 @@
 		usage(argv[0]);
 	}
     if (optind != argc) usage(argv[0]);
-    diag(COMPONENT,DIAG_INFO,"Linux ATM signaling UNI 3.x(red)/AAL5, version "
-      VERSION);
+    diag(COMPONENT,DIAG_INFO,"Linux ATM signaling "
+#ifdef UNI30
+      "UNI 3.0"
+#endif
+#ifdef UNI31
+      "UNI 3.1"
+#ifdef ALLOW_UNI30
+      "+3.0compat"
+#endif
+#endif
+      "/AAL5, version " VERSION);
     if (!(yyin = fopen(config_file,"r")))
 	diag(COMPONENT,DIAG_WARN,"%s not found. - Using defaults.",config_file);
     else if (yyparse()) {
diff -ur --new-file old/atm/sigd/io.c new/atm/sigd/io.c
--- old/atm/sigd/io.c	Thu Mar 14 13:58:07 1996
+++ new/atm/sigd/io.c	Wed Jul 31 15:40:56 1996
@@ -64,26 +64,20 @@
 
     size = read(kernel,buffer,KERNEL_BUFFER_SIZE);
     if (size < 0) {
-	perror("read kernel");
+	diag(COMPONENT,DIAG_ERROR,"read kernel: %s",strerror(errno));
 	return;
     }
-    if (size < KERNEL_BASE_LEN) {
+    if (size < KERNEL_BASE_LEN)
 	diag(COMPONENT,DIAG_FATAL,"kernel message too short (%d < %d)",size,
 	  KERNEL_BASE_LEN);
-	return;
-    }
-    if (size == KERNEL_BUFFER_SIZE) { /* gotcha ! */
+    if (size == KERNEL_BUFFER_SIZE) /* gotcha ! */
 	diag(COMPONENT,DIAG_FATAL,"kernel message too big (>= %d)",
 	  KERNEL_BUFFER_SIZE);
-	return;
-    }
     bllis = (size-KERNEL_BASE_LEN)/sizeof(struct atm_blli);
-    if (size != KERNEL_BASE_LEN+bllis*sizeof(struct atm_blli)) {
+    if (size != KERNEL_BASE_LEN+bllis*sizeof(struct atm_blli))
 	diag(COMPONENT,DIAG_FATAL,
 	  "kernel message has bad length (got %d, base is %d, incr is %d)",
 	  size,KERNEL_BASE_LEN,sizeof(struct atm_blli));
-	return;
-    }
     from_kernel((struct atmsvc_msg *) buffer,size);
 }
 
@@ -121,15 +115,21 @@
 
 static int open_signaling(void)
 {
+    struct atm_qos qos;
     int s;
 
     if ((s = socket(PF_ATMPVC,SOCK_DGRAM,ATM_AAL5)) < 0) {
 	perror("socket");
 	return -1;
     }
+    memset(&qos,0,sizeof(qos));
+    qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_UBR;
+    qos.rxtp.max_sdu = qos.txtp.max_sdu = MAX_Q_MSG;
+    if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) {
+	perror("setsockopt SO_ATMQOS");
+	return -1;
+    }
     signaling_pvc.sap_family = AF_ATMPVC;
-    signaling_pvc.sap_rxtp.class = signaling_pvc.sap_txtp.class = ATM_UBR;
-    signaling_pvc.sap_rxtp.max_sdu = signaling_pvc.sap_txtp.max_sdu = MAX_Q_MSG;
     if (bind(s,(struct sockaddr *) &signaling_pvc,sizeof(signaling_pvc)) < 0) {
 	perror("bind");
 	return -1;
diff -ur --new-file old/atm/sigd/kernel.c new/atm/sigd/kernel.c
--- old/atm/sigd/kernel.c	Fri May 31 19:57:47 1996
+++ new/atm/sigd/kernel.c	Tue Jul 30 21:25:05 1996
@@ -51,14 +51,13 @@
 				     this */
 #endif
     q_assign(&dsc,QF_sscs_type,0); /* unspecified - LANE wants this */
-    error = sap_encode(&dsc,sock->remote);
+    error = sap_encode(&dsc,sock->remote,&sock->qos);
     q_assign(&dsc,QF_bearer_class,16); /* BCOB-X */
     q_assign(&dsc,QF_traf_type,0); /* force presence - UNI 3.0 wants this */
     q_assign(&dsc,QF_upcc,0); /* p2p */
 #ifndef UNI30
     q_assign(&dsc,QF_qos_cs,Q2931_CS_ITU);
 #endif
-    q_assign(&dsc,QF_qos_cs,Q2931_CS_NET);
     q_assign(&dsc,QF_qos_fw,0); /* QOS 0 */
     q_assign(&dsc,QF_qos_bw,0);
     local = NULL;
@@ -96,7 +95,14 @@
     q_assign(&dsc,QF_msg_type,QMSG_CONNECT);
     q_assign(&dsc,QF_call_ref,sock->call_ref);
     if (sock->ep_ref >= 0) q_assign(&dsc,QF_ep_ref,sock->ep_ref);
-    /* @@@ lots of data */
+    q_assign(&dsc,QF_aal_type,5);
+#ifdef UNI30
+    q_assign(&dsc,QF_aal_mode,1); /* Message mode - LANE seems to really want
+				     this */
+#endif
+    q_assign(&dsc,QF_sscs_type,0); /* unspecified - LANE wants this */
+    q_assign(&dsc,QF_fw_max_sdu,sock->qos.rxtp.max_sdu);
+    q_assign(&dsc,QF_bw_max_sdu,sock->qos.txtp.max_sdu);
     if ((size = q_close(&dsc)) >= 0) to_signaling(q_buffer,size);
     return 0;
 }
@@ -131,6 +137,10 @@
     switch (msg->type) {
 	case as_bind: /* only in NULL state */
 	    if (sock) break;
+	    if (msg->aal != ATM_AAL5) {
+		SEND_ERROR(msg->vcc,-EINVAL);
+		return;
+	    }
 	    if (!*msg->svc.sas_addr.pub && !*msg->svc.sas_addr.prv)
 #ifdef BE_PICKY_ABOUT_BINDING_LOCAL_WILDCARD_ADDRESSES
 		if (local_addr[0].state != ls_same)
@@ -138,7 +148,7 @@
 		else
 #endif
 		send_kernel(msg->vcc,0,as_okay,0,NULL,NULL,
-		      &local_addr[0].addr);
+		      &local_addr[0].addr,NULL);
 	    else {
 		LOCAL_ADDR *walk;
 
@@ -147,36 +157,35 @@
 		      &msg->svc,0,0)) break;
 		if (walk->state == ls_unused)
 		    SEND_ERROR(msg->vcc,-EADDRNOTAVAIL);
-		else send_kernel(msg->vcc,0,as_okay,0,NULL,NULL,NULL);
+		else send_kernel(msg->vcc,0,as_okay,0,NULL,NULL,NULL,NULL);
 	    }
 	    return;
-	case as_establish: /* NULL or LISTENING */
-	    if (sock && sock->state != ss_listening) break;
-	    if (!sock) {
-		sock = new_sock(msg->vcc);
-		error = bind_check(sock,&msg->local);
-		if (!error) {
-		    sock->remote = sap_copy(&msg->svc);
-		    if (!sock->remote) error = -EINVAL;
-		}
-		if (error) {
-		    free_sock(sock);
-		    SEND_ERROR(msg->vcc,error);
-		    return;
-		}
-		sock->pvc.sap_txtp = sock->remote->sas_txtp;
-		sock->pvc.sap_rxtp = sock->remote->sas_rxtp;
-		sock->state = ss_connecting;
-		error = send_setup(sock);
-		if (error) {
-		    SEND_ERROR(msg->vcc,error);
-		    free_sock(sock);
-		    return;
-		}
-		START_TIMER(sock,T303);
-		new_state(sock,ss_connecting);
+	case as_connect: /* NULL state only */
+	    if (sock) break;
+	    sock = new_sock(msg->vcc);
+	    error = bind_check(sock,&msg->local);
+	    if (!error) {
+		sock->remote = sap_copy(&msg->svc);
+		if (!sock->remote) error = -EINVAL;
+	    }
+	    if (error) {
+		free_sock(sock);
+		SEND_ERROR(msg->vcc,error);
 		return;
 	    }
+	    sock->qos = msg->qos;
+	    sock->state = ss_connecting;
+	    error = send_setup(sock);
+	    if (error) {
+		SEND_ERROR(msg->vcc,error);
+		free_sock(sock);
+		return;
+	    }
+	    START_TIMER(sock,T303);
+	    new_state(sock,ss_connecting);
+	    return;
+	case as_accept: /* LISTENING only */
+	    if (sock->state != ss_listening) break;
 	    /* becomes INDICATED or ZOMBIE */
 	    if (!sock->listen) {
 		diag(COMPONENT,DIAG_WARN,
@@ -213,14 +222,15 @@
 
 		if (sock) break;
 		sap = sap_copy(&msg->svc);
-		if (lookup_sap(sap,NULL)) {
+		if (lookup_sap(sap,&msg->qos,NULL,NULL)) {
 		    free(sap);
 		    SEND_ERROR(msg->vcc,-EADDRINUSE);
 		    return;
 		}
 		sock = new_sock(msg->vcc);
 		sock->local = sap_copy(&msg->svc);
-		send_kernel(sock->id,0,as_okay,0,NULL,NULL,NULL);
+		sock->qos = msg->qos;
+		send_kernel(sock->id,0,as_okay,0,NULL,NULL,NULL,NULL);
 		sock->state = ss_listening;
 		return;
 	    }
diff -ur --new-file old/atm/sigd/proto.c new/atm/sigd/proto.c
--- old/atm/sigd/proto.c	Wed Apr 17 18:28:34 1996
+++ new/atm/sigd/proto.c	Tue Jul 30 18:10:59 1996
@@ -32,8 +32,8 @@
 	"CONN_REQ",	"IN_PROC",	"ACTIVE",	"REL_REQ",
 	"REL_IND" };
 
-const char *as_name[] = { "<invalid>","as_bind","as_establish","as_listen",
-  "as_okay","as_indicate","as_close" };
+const char *as_name[] = { "<invalid>","as_bind","as_connect","as_accept",
+  "as_listen","as_okay","as_error","as_indicate","as_close" };
 
 const Q2931_STATE state_map[] = { /* formatting aligned with STATE */
 	qs_null,	qs_null,	qs_null,	qs_call_init,
@@ -58,7 +58,7 @@
     sock = alloc_t(SOCKET);
     sock->state = ss_invalid;
     sock->pvc.sap_addr.vpi = sock->pvc.sap_addr.vci = 0;
-    sock->pvc.sap_txtp.class = sock->pvc.sap_rxtp.class = ATM_UBR;
+    sock->qos.txtp.traffic_class = sock->qos.rxtp.traffic_class = ATM_UBR;
     sock->id = id;
     sock->local = sock->remote = NULL;
     sock->error = 0;
@@ -107,14 +107,16 @@
 }
 
 
-SOCKET *lookup_sap(struct sockaddr_atmsvc *sap,struct sockaddr_atmsvc **res)
+SOCKET *lookup_sap(const struct sockaddr_atmsvc *sap,const struct atm_qos *qos,
+    struct sockaddr_atmsvc **res_sap,struct atm_qos *res_qos)
 {
     SOCKET *walk,*wildcard;
 
     wildcard = NULL;
     for (walk = sockets; walk; walk = walk->next)
 	if (walk->state == ss_listening) {
-	    if (walk->local && sap_compat(walk->local,sap,res)) return walk;
+	    if (walk->local && sap_compat(walk->local,sap,res_sap,&walk->qos,
+	      qos,res_qos)) return walk;
 	    if (!walk->local) wildcard = walk;
 	}
     return wildcard;
@@ -161,8 +163,9 @@
 
 
 void send_kernel(unsigned long vcc,unsigned long listen_vcc,
-  enum atmsvc_msg_type type,int reply,struct sockaddr_atmpvc *pvc,
-  struct sockaddr_atmsvc *svc,struct sockaddr_atmsvc *local)
+  enum atmsvc_msg_type type,int reply,const struct sockaddr_atmpvc *pvc,
+  const struct sockaddr_atmsvc *svc,const struct sockaddr_atmsvc *local,
+  const struct atm_qos *qos)
 {
     struct atmsvc_msg *msg;
     struct atm_blli *walk;
@@ -178,6 +181,8 @@
     msg->reply = reply;
     if (pvc) msg->pvc = *pvc;
     else memset(&msg->pvc,0,sizeof(msg->pvc));
+    if (qos) msg->qos = *qos;
+    else memset(&msg->qos,0,sizeof(msg->qos));
     if (!local) memset(&msg->local,0,sizeof(msg->local));
     else {
 	msg->local = *local;
@@ -243,7 +248,7 @@
 void send_close(SOCKET *sock)
 {
     if (sock->error == 1234) diag(COMPONENT,DIAG_ERROR,"BUG! BUG! BUG!");
-    SEND_ERROR(sock->id,sock->error);
+    send_kernel(sock->id,0L,as_close,sock->error,NULL,NULL,NULL,NULL);
     sock->error = 1234;
 }
 
diff -ur --new-file old/atm/sigd/proto.h new/atm/sigd/proto.h
--- old/atm/sigd/proto.h	Wed Apr 17 18:25:02 1996
+++ new/atm/sigd/proto.h	Tue Jul 30 18:10:49 1996
@@ -32,6 +32,7 @@
     unsigned long id;
     struct sockaddr_atmsvc *local; /* local SAP */
     struct sockaddr_atmsvc *remote; /* remote SAP */
+    struct atm_qos qos; /* QOS parameters */
     int error; /* error code for close */
     /* --- Q.93B/Q.2931 information ---------------------------------------- */
     Q2931_STATE q2931_state;
@@ -66,7 +67,8 @@
   (int) sizeof(struct atm_blli)+1)
 #define bllis2msg(n) (sizeof(struct atmsvc_msg)+sizeof(struct atm_blli)*((n)-1))
 
-#define SEND_ERROR(vcc,code) send_kernel(vcc,0L,as_close,code,NULL,NULL,NULL)
+#define SEND_ERROR(vcc,code) \
+  send_kernel(vcc,0L,as_error,code,NULL,NULL,NULL,NULL)
 
 
 void from_kernel(struct atmsvc_msg *msg,int size);
@@ -74,8 +76,9 @@
 
 void to_q2931(void *msg,int size);
 void send_kernel(unsigned long vcc,unsigned long listen_vcc,
-  enum atmsvc_msg_type type,int reply,struct sockaddr_atmpvc *pvc,
-  struct sockaddr_atmsvc *svc,struct sockaddr_atmsvc *local);
+  enum atmsvc_msg_type type,int reply,const struct sockaddr_atmpvc *pvc,
+  const struct sockaddr_atmsvc *svc,const struct sockaddr_atmsvc *local,
+  const struct atm_qos *qos);
 void from_net(void *msg,int size);
 void to_signaling(void *msg,int size);
 void saal_failure(void);
@@ -85,7 +88,8 @@
 SOCKET *new_sock(unsigned long id);
 void free_sock(SOCKET *sock);
 void new_state(SOCKET *sock,STATE state);
-SOCKET *lookup_sap(struct sockaddr_atmsvc *sap,struct sockaddr_atmsvc **res);
+SOCKET *lookup_sap(const struct sockaddr_atmsvc *sap,const struct atm_qos *qos,
+  struct sockaddr_atmsvc **res_sap,struct atm_qos *res_qos);
 void send_release(SOCKET *sock,unsigned char reason,...);
 void send_release_complete(unsigned long call_ref,unsigned char cause);
 
diff -ur --new-file old/atm/sigd/q2931.c new/atm/sigd/q2931.c
--- old/atm/sigd/q2931.c	Fri Jul 19 12:00:28 1996
+++ new/atm/sigd/q2931.c	Tue Jul 30 18:14:56 1996
@@ -59,12 +59,13 @@
 {
     SOCKET *sock,*this,**walk;
     struct sockaddr_atmsvc *sap,*new_sap;
+    struct atm_qos qos,new_qos;
     unsigned char buf[ATM_ESA_LEN];
     int len,i;
 
-    sap = sap_decode(&in_dsc);
+    sap = sap_decode(&in_dsc,&qos);
     new_sap = NULL;
-    sock = lookup_sap(sap,&new_sap);
+    sock = lookup_sap(sap,&qos,&new_sap,&new_qos);
     if (!sock || new_sap) free(sap);
     else new_sap = sap; /* @@@ should strip extra BLLIs */
     if (!sock) {
@@ -79,6 +80,7 @@
 	this->ep_ref = q_fetch(&in_dsc,QF_ep_ref);
     send_call_proceeding(this);
     this->local = new_sap;
+    this->qos = new_qos;
     /* if (sock->local) *this->local->sas_addr = sock->local->sas_addr; ??? */
     diag(COMPONENT,DIAG_DEBUG,"AAL type %ld",q_fetch(&in_dsc,QF_aal_type));
     len = q_read(&in_dsc,QF_cdpn_esa,(void *) &buf,sizeof(buf));
@@ -99,8 +101,6 @@
       this->pvc.sap_addr.vpi,this->pvc.sap_addr.vci);
     this->remote = alloc_t(struct sockaddr_atmsvc);
     memset(this->remote,0,sizeof(struct sockaddr_atmsvc));
-    this->pvc.sap_txtp = this->remote->sas_txtp = new_sap->sas_txtp;
-    this->pvc.sap_rxtp = this->remote->sas_rxtp = new_sap->sas_rxtp;
     if (q_present(&in_dsc,QF_cgpn)) { /* should handle E.164 too */
 	char buffer[MAX_ATM_ADDR_LEN+1];
 
@@ -112,7 +112,7 @@
 	  pretty) < 0) strcpy(buffer,"<invalid address>");
 	diag(COMPONENT,DIAG_DEBUG,"Incoming call from %s",buffer);
     }
-    send_kernel(0,sock->id,as_indicate,0,&this->pvc,this->remote,NULL);
+    send_kernel(0,sock->id,as_indicate,0,&this->pvc,this->remote,NULL,&new_qos);
     for (walk = &sock->listen; *walk; walk = &(*walk)->listen);
     *walk = this;
     diag(COMPONENT,DIAG_DEBUG,"SE vpi.vci=%d.%d",this->pvc.sap_addr.vpi,
@@ -306,7 +306,8 @@
 	    send_connect_ack(sock);
 	    /* @@@ fill in sock->remote */
 	    /* @@@ fill in traffic parameters */
-	    send_kernel(sock->id,0,as_okay,0,&sock->pvc,NULL,sock->local);
+	    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);
@@ -320,7 +321,7 @@
 	    }
 	    if (sock->state != ss_accepting) break;
 	    STOP_TIMER(sock);
-	    send_kernel(sock->id,0,as_okay,0,NULL,NULL,sock->local);
+	    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);
@@ -408,8 +409,6 @@
 		    case ss_connected:
 			diag(COMPONENT,DIAG_INFO,"Passive close (CR 0x%06X)",
 			  sock->call_ref);
-			sock->error = -sock->error;
-			    /* context is still around */
 			send_close(sock);
 			/* fall through */
 		    case ss_hold:
diff -ur --new-file old/atm/sigd/saal.c new/atm/sigd/saal.c
--- old/atm/sigd/saal.c	Wed Sep 27 15:14:45 1995
+++ new/atm/sigd/saal.c	Thu Jan  1 01:00:00 1970
@@ -1,13 +0,0 @@
-/* saal.c - SAAL = SSCF+SSCOP */
-
-/* Written 1995 by Werner Almesberger, EPFL-LRC */
-
-
-#include "sscop.h"
-#include "saal.h"
-
-
-void saal_pdu(SAAL_DSC *dsc,void *buffer,int length)
-{
-    sscop_pdu(&dsc->sscop,buffer,length);
-}
diff -ur --new-file old/atm/sigd/saal.h new/atm/sigd/saal.h
--- old/atm/sigd/saal.h	Wed Sep 27 11:07:23 1995
+++ new/atm/sigd/saal.h	Thu Jan  1 01:00:00 1970
@@ -1,24 +0,0 @@
-/* saal.h - SAAL user interface */
-
-/* Written 1995 by Werner Almesberger, EPFL-LRC */
-
-
-#ifndef SAAL_H
-#define SAAL_H
-
-#include "sscf.h"
-#include "sscop.h"
-
-
-#define SAAL_DSC SSCF_DSC
-#define SAAL_USER_OPS SSCF_USER_OPS
-#define start_saal start_sscf
-#define stop_saal stop_sscf
-#define saal_estab_req sscf_estab_req
-#define saal_rel_req sscf_rel_req
-#define saal_send sscf_send
-#define saal_unitdata sscf_unitdata
-
-void saal_pdu(SAAL_DSC *dsc,void *buffer,int length);
-
-#endif
diff -ur --new-file old/atm/sigd/sap.c new/atm/sigd/sap.c
--- old/atm/sigd/sap.c	Mon Jul 15 19:24:37 1996
+++ new/atm/sigd/sap.c	Mon Jul 29 17:27:25 1996
@@ -61,12 +61,14 @@
 static int class_compat(const struct atm_trafprm *tx,
   const struct atm_trafprm *rx)
 {
-    if (tx->class == ATM_NONE || tx->class == ATM_ANYCLASS) return 1;
-    if (rx->class == ATM_UBR || rx->class == ATM_ANYCLASS) return 1;
+    if (tx->traffic_class == ATM_NONE || tx->traffic_class == ATM_ANYCLASS)
+	return 1;
+    if (rx->traffic_class == ATM_UBR || rx->traffic_class == ATM_ANYCLASS)
+	return 1;
 	/* don't apply CAC to PCR */
-    if (tx->class != rx->class) return 0;
+    if (tx->traffic_class != rx->traffic_class) return 0;
 	/* ignore special cases like CBR to VBR for now */
-    switch (tx->class) {
+    switch (tx->traffic_class) {
 	case ATM_CBR:
 	    if (!rx->max_pcr || rx->max_pcr == ATM_MAX_PCR) return 1;
 	    return tx->min_pcr <= rx->max_pcr;
@@ -74,7 +76,7 @@
 	       bandwidth negotiation anyway. */
 	default:
 	    diag(COMPONENT,DIAG_ERROR,"unsupported traffic class %d\n",
-	      tx->class);
+	      tx->traffic_class);
 	    return 0;
     }
 }
@@ -151,47 +153,56 @@
 }
 
 
-static int do_sap_compat(const struct sockaddr_atmsvc *old,
-  const struct sockaddr_atmsvc *new,struct sockaddr_atmsvc *res)
+static int do_sap_compat(const struct sockaddr_atmsvc *old_sap,
+  const struct sockaddr_atmsvc *new_sap,const struct atm_qos *old_qos,
+  const struct atm_qos *new_qos,struct sockaddr_atmsvc *res_sap,
+  struct atm_qos *res_qos)
 {
-    if (old->sas_txtp.max_sdu && new->sas_rxtp.max_sdu &&
-      old->sas_txtp.max_sdu > new->sas_rxtp.max_sdu) return 0;
-    if (new->sas_txtp.max_sdu && old->sas_rxtp.max_sdu &&
-      new->sas_txtp.max_sdu > old->sas_rxtp.max_sdu) return 0;
-    if (!class_compat(&old->sas_txtp,&new->sas_rxtp) ||
-      !class_compat(&new->sas_txtp,&old->sas_rxtp)) return 0;
-    if (!bhli_compat(&old->sas_addr.bhli,&new->sas_addr.bhli,
-      res ? &res->sas_addr.bhli : NULL)) return 0;
-    return blli_compat(old->sas_addr.blli,new->sas_addr.blli,
-      res ? &res->sas_addr.blli : NULL,res ? (struct atm_blli *) (res+1) :
-      NULL);
+    if (res_qos) *res_qos = *new_qos;
+    if (old_qos->txtp.max_sdu && new_qos->rxtp.max_sdu &&
+      old_qos->txtp.max_sdu > new_qos->rxtp.max_sdu) return 0;
+    if (new_qos->txtp.max_sdu && old_qos->rxtp.max_sdu &&
+      new_qos->txtp.max_sdu > old_qos->rxtp.max_sdu) return 0;
+    if (!class_compat(&old_qos->txtp,&new_qos->rxtp) ||
+      !class_compat(&new_qos->txtp,&old_qos->rxtp)) return 0;
+    if (res_sap) *res_sap = *new_sap;
+    if (!bhli_compat(&old_sap->sas_addr.bhli,&new_sap->sas_addr.bhli,
+      res_sap ? &res_sap->sas_addr.bhli : NULL)) return 0;
+    return blli_compat(old_sap->sas_addr.blli,new_sap->sas_addr.blli,
+      res_sap ? &res_sap->sas_addr.blli : NULL,
+      res_sap ? (struct atm_blli *) (res_sap+1) : NULL);
 }
 
 
-int sap_compat(const struct sockaddr_atmsvc *old,
-  const struct sockaddr_atmsvc *new,struct sockaddr_atmsvc **out)
+int sap_compat(const struct sockaddr_atmsvc *old_sap,
+  const struct sockaddr_atmsvc *new_sap,struct sockaddr_atmsvc **out_sap,
+  const struct atm_qos *old_qos,const struct atm_qos *new_qos,
+  struct atm_qos *out_qos)
 {
-    struct sockaddr_atmsvc *res;
+    struct sockaddr_atmsvc *res_sap;
+    struct atm_qos res_qos;
     int compat;
 
-    if ((*old->sas_addr.prv || *old->sas_addr.pub) && !atm_equal(old,new,0,0))
-	return 0;
-    if (!out) res = NULL;
-    else res = alloc(sizeof(struct sockaddr_atmsvc)+sizeof(struct atm_blli));
-    compat = do_sap_compat(old,new,res);
-    if (out)
-	if (!compat) free(res);
+    if ((*old_sap->sas_addr.prv || *old_sap->sas_addr.pub) &&
+      !atm_equal(old_sap,new_sap,0,0)) return 0;
+    if (!out_sap) res_sap = NULL;
+    else res_sap = alloc(sizeof(struct sockaddr_atmsvc)+
+	  sizeof(struct atm_blli));
+    compat = do_sap_compat(old_sap,new_sap,old_qos,new_qos,res_sap,&res_qos);
+    if (out_sap)
+	if (!compat) free(res_sap);
 	else {
-	    *out = res;
-	    *res = *new;
-	    if (res->sas_addr.blli)
-		res->sas_addr.blli = (struct atm_blli *) (res+1);
+	    *out_sap = res_sap;
+	    if (res_sap->sas_addr.blli)
+		res_sap->sas_addr.blli = (struct atm_blli *) (res_sap+1);
 	}
+    if (out_qos && compat) *out_qos = res_qos;
     return compat;
 }
 
 
-int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *sap)
+int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *sap,
+  const struct atm_qos *qos)
 {
     struct atm_blli *blli;
     int pcr;
@@ -199,9 +210,10 @@
     if (!*sap->sas_addr.prv) return -EDESTADDRREQ;
     else q_write(dsc,QF_cdpn_esa,(void *) sap->sas_addr.prv,ATM_ESA_LEN);
 	/* improve @@@ */
-    switch (sap->sas_txtp.class) {
+    switch (qos->txtp.traffic_class) {
 	case ATM_NONE:
-	    if (sap->sas_rxtp.class == ATM_CBR) q_assign(dsc,QF_fw_pcr_01,0);
+	    if (qos->rxtp.traffic_class == ATM_CBR)
+		q_assign(dsc,QF_fw_pcr_01,0);
 	    break;
 	case ATM_UBR:
 	    diag(COMPONENT,DIAG_DEBUG,"UBR");
@@ -211,33 +223,35 @@
 	    break;
 	case ATM_CBR:
 	    /* here's a bit of policy: send the highest value we have */
-	    pcr = SELECT_TOP_PCR(sap->sas_txtp);
+	    pcr = SELECT_TOP_PCR(qos->txtp);
 	    diag(COMPONENT,DIAG_DEBUG,"CBR fwd %d (%d..%d)",pcr,
-	      sap->sas_txtp.min_pcr,sap->sas_txtp.max_pcr);
+	      qos->txtp.min_pcr,qos->txtp.max_pcr);
 	    q_assign(dsc,QF_fw_pcr_01,pcr);
 	    break;
         default:
-	    diag(COMPONENT,DIAG_ERROR,"bad TX class (%d)",sap->sas_txtp.class);
+	    diag(COMPONENT,DIAG_ERROR,"bad TX class (%d)",
+	      qos->txtp.traffic_class);
 	    return -EINVAL;
     }
-    switch (sap->sas_txtp.class == ATM_UBR ? ATM_NONE : sap->sas_rxtp.class) {
+    switch (qos->txtp.traffic_class == ATM_UBR ? ATM_NONE :
+      qos->rxtp.traffic_class) {
 	case ATM_NONE:
-	    if (sap->sas_txtp.class == ATM_CBR) q_assign(dsc,QF_bw_pcr_01,0);
+	    if (qos->txtp.traffic_class == ATM_CBR)
+		q_assign(dsc,QF_bw_pcr_01,0);
 	    break;
 	case ATM_CBR:
-	    pcr = SELECT_TOP_PCR(sap->sas_rxtp);
+	    pcr = SELECT_TOP_PCR(qos->rxtp);
 	    diag(COMPONENT,DIAG_DEBUG,"CBR bwd %d (%d..%d)",pcr,
-	      sap->sas_rxtp.min_pcr,sap->sas_rxtp.max_pcr);
+	      qos->rxtp.min_pcr,qos->rxtp.max_pcr);
 	    q_assign(dsc,QF_bw_pcr_01,pcr);
 	    break;
         default:
-	    diag(COMPONENT,DIAG_ERROR,"bad RX class (%d)",sap->sas_txtp.class);
+	    diag(COMPONENT,DIAG_ERROR,"bad RX class (%d)",
+	      qos->rxtp.traffic_class);
 	    return -EINVAL;
     }
-    if (sap->sas_txtp.max_sdu)
-	q_assign(dsc,QF_fw_max_sdu,sap->sas_txtp.max_sdu);
-    if (sap->sas_rxtp.max_sdu)
-	q_assign(dsc,QF_bw_max_sdu,sap->sas_rxtp.max_sdu);
+    if (qos->txtp.max_sdu) q_assign(dsc,QF_fw_max_sdu,qos->txtp.max_sdu);
+    if (qos->rxtp.max_sdu) q_assign(dsc,QF_bw_max_sdu,qos->rxtp.max_sdu);
     /* @@@ bearer class ? */
     /* @@@ QOS class ? */
     if (sap->sas_addr.bhli.hl_type != ATM_HL_NONE) {
@@ -325,7 +339,7 @@
 }
 
 
-struct sockaddr_atmsvc *sap_decode(Q_DSC *dsc)
+struct sockaddr_atmsvc *sap_decode(Q_DSC *dsc,struct atm_qos *qos)
 {
     struct sockaddr_atmsvc *sap;
     struct atm_blli *blli;
@@ -333,6 +347,7 @@
     sap = alloc(sizeof(struct sockaddr_atmsvc)+sizeof(struct atm_blli));
     blli = (struct atm_blli *) (sap+1);
     memset(sap,0,sizeof(struct sockaddr_atmsvc)+sizeof(struct atm_blli));
+    memset(qos,0,sizeof(struct atm_qos));
     sap->sas_family = AF_ATMSVC;
     if (q_present(dsc,QF_cdpn_esa)) /* @@@ handle E.164 too */
 	(void) q_read(dsc,QF_cdpn_esa,(void *) &sap->sas_addr.prv,ATM_ESA_LEN);
@@ -341,32 +356,32 @@
 	    diag(COMPONENT,DIAG_ERROR,"AAL type %d requested",
 	      q_fetch(dsc,QF_aal_type));
     if (q_present(dsc,QF_best_effort)) {
-	sap->sas_txtp.class = sap->sas_rxtp.class = ATM_UBR;
+	qos->txtp.traffic_class = qos->rxtp.traffic_class = ATM_UBR;
 	diag(COMPONENT,DIAG_DEBUG,"UBR");
     }
     else {
-	sap->sas_txtp.class = sap->sas_rxtp.class = ATM_CBR;
-	sap->sas_txtp.max_pcr = sap->sas_rxtp.max_pcr = 0;
+	qos->txtp.traffic_class = qos->rxtp.traffic_class = ATM_CBR;
+	qos->txtp.max_pcr = qos->rxtp.max_pcr = 0;
 	/* unbalanced decoding - always sets upper bound */
 	if (q_present(dsc,QF_fw_pcr_01)) {
-	    sap->sas_rxtp.min_pcr = 0;
-	    sap->sas_rxtp.max_pcr = q_fetch(dsc,QF_fw_pcr_01);
+	    qos->rxtp.min_pcr = 0;
+	    qos->rxtp.max_pcr = q_fetch(dsc,QF_fw_pcr_01);
 	}
 	if (q_present(dsc,QF_bw_pcr_01)) {
-	    sap->sas_txtp.min_pcr = 0;
-	    sap->sas_txtp.max_pcr = q_fetch(dsc,QF_bw_pcr_01);
+	    qos->txtp.min_pcr = 0;
+	    qos->txtp.max_pcr = q_fetch(dsc,QF_bw_pcr_01);
 	}
-	if (!sap->sas_txtp.max_pcr) sap->sas_txtp.class = ATM_NONE;
-	if (!sap->sas_rxtp.max_pcr) sap->sas_rxtp.class = ATM_NONE;
+	if (!qos->txtp.max_pcr) qos->txtp.traffic_class = ATM_NONE;
+	if (!qos->rxtp.max_pcr) qos->rxtp.traffic_class = ATM_NONE;
 	diag(COMPONENT,DIAG_DEBUG,"CBR fwd %d..%d bwd %d..%d",
-	  sap->sas_rxtp.min_pcr,sap->sas_rxtp.max_pcr,sap->sas_txtp.min_pcr,
-	  sap->sas_txtp.max_pcr);
+	  qos->rxtp.min_pcr,qos->rxtp.max_pcr,qos->txtp.min_pcr,
+	  qos->txtp.max_pcr);
 	/* SHOULD ... fail call if anything is missing ... @@@ */
     }
     if (q_present(dsc,QF_bw_max_sdu))
-	sap->sas_txtp.max_sdu = q_fetch(dsc,QF_bw_max_sdu);
+	qos->txtp.max_sdu = q_fetch(dsc,QF_bw_max_sdu);
     if (q_present(dsc,QF_fw_max_sdu))
-	sap->sas_rxtp.max_sdu = q_fetch(dsc,QF_fw_max_sdu);
+	qos->rxtp.max_sdu = q_fetch(dsc,QF_fw_max_sdu);
     if (q_present(dsc,QG_bhli)) {
 	sap->sas_addr.bhli.hl_type = q_fetch(dsc,QF_hli_type)+1;
 	switch (sap->sas_addr.bhli.hl_type) {
diff -ur --new-file old/atm/sigd/sap.h new/atm/sigd/sap.h
--- old/atm/sigd/sap.h	Wed Apr 17 19:06:49 1996
+++ new/atm/sigd/sap.h	Fri Jul 26 18:01:39 1996
@@ -11,9 +11,12 @@
 
 int sap_check_packing(const struct sockaddr_atmsvc *packed_sap,int len);
 struct sockaddr_atmsvc *sap_copy(const struct sockaddr_atmsvc *sap);
-int sap_compat(const struct sockaddr_atmsvc *old,
-  const struct sockaddr_atmsvc *new,struct sockaddr_atmsvc **out);
-int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *sap);
-struct sockaddr_atmsvc *sap_decode(Q_DSC *dsc);
+int sap_compat(const struct sockaddr_atmsvc *old_sap,
+  const struct sockaddr_atmsvc *new_sap,struct sockaddr_atmsvc **out_sap,
+  const struct atm_qos *old_qos,const struct atm_qos *new_qos,
+  struct atm_qos *out_qos);
+int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *sap,
+  const struct atm_qos *qos);
+struct sockaddr_atmsvc *sap_decode(Q_DSC *dsc,struct atm_qos *qos);
 
 #endif
diff -ur --new-file old/atm/sigd/sscf.c new/atm/sigd/sscf.c
--- old/atm/sigd/sscf.c	Fri Feb  9 17:36:05 1996
+++ new/atm/sigd/sscf.c	Thu Jan  1 01:00:00 1970
@@ -1,278 +0,0 @@
-/* sscf.c - SSCF (Q.2130) protocol */
-
-/* Written 1995 by Werner Almesberger, EPFL-LRC */
-
-
-#include "atmd.h"
-
-#include "sscop.h"
-#include "sscf.h"
-
-
-#define COMPONENT "SSCF"
-
-
-/* --- SSCOP configuration ------------------------------------------------- */
-
-
-#define SSCF_MaxCC		      4	/* give up after 4 retries */
-#define SSCF_MaxPD		     25	/* POLL after 25 SDs */
-#define SSCF_Timer_CC		1000000 /* 1 sec */
-#define SSCF_Timer_KEEPALIVE	2000000	/* 2 sec */
-#define SSCF_Timer_NORESP	7000000 /* 7 sec */
-#define SSCF_Timer_POLL		 750000 /* 750 ms */
-#define SSCF_Timer_IDLE	       15000000 /* 15 sec */
-
-
-static const char *state_name[] = { "1/2","2/2","4/10","3/4","2/5" };
-
-
-/* --- Helper function(s) -------------------------------------------------- */
-
-
-static void next_state(SSCF_DSC *dsc,SSCF_STATE state)
-{
-    diag(COMPONENT,DIAG_DEBUG,"entering state %s",state_name[state]);
-    dsc->state = state;
-}
-
-
-/* --- Invocation from SSCOP ----------------------------------------------- */
-
-
-static void sscf_estab_ind(void *user_data,void *uu_data,int uu_length)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state != sscf_11)
-	diag(COMPONENT,DIAG_FATAL,"sscf_estab_ind in state %s",
-	  state_name[dsc->state]);
-    next_state(dsc,sscf_410);
-    sscop_estab_resp(&dsc->sscop,NULL,0,1);
-    if (dsc->ops->estab_ind) dsc->ops->estab_ind(dsc->user,uu_data,uu_length);
-}
-
-
-static void sscf_estab_conf(void *user_data,
-  void *uu_data,int uu_length)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state != sscf_22)
-	diag(COMPONENT,DIAG_FATAL,"sscf_estab_conf in state %s",
-	  state_name[dsc->state]);
-    next_state(dsc,sscf_410);
-    if (dsc->ops->estab_conf)
-	dsc->ops->estab_conf(dsc->user,uu_data,uu_length);
-    
-}
-
-
-static void sscf_restart(void *user_data,void *uu_data,int uu_length,int ind)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if ((!ind && dsc->state != sscf_34) || (ind && (dsc->state == sscf_11 ||
-      dsc->state == sscf_34)))
-	diag(COMPONENT,DIAG_FATAL,"sscf_restart (ind = %d) in state %s",
-	  state_name[dsc->state],ind);
-    sscop_estab_resp(&dsc->sscop,NULL,0,1);
-    next_state(dsc,sscf_410);
-    if (dsc->ops->restart) dsc->ops->restart(dsc->user,uu_data,uu_length,ind);
-}
-
-
-static void sscf_rec_ind(void *user_data)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state != sscf_410)
-	diag(COMPONENT,DIAG_FATAL,"sscf_rec_ind in state %s",
-	  state_name[dsc->state]);
-    sscop_rec_resp(&dsc->sscop);
-    if (dsc->ops->estab_ind) dsc->ops->estab_ind(dsc->user,NULL,0);
-}
-
-
-static void sscf_rel_ind(void *user_data,void *uu_data,int uu_length,int user)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state == sscf_11 || dsc->state == sscf_34)
-	diag(COMPONENT,DIAG_FATAL,"sscf_rel_ind in state %s",
-	  state_name[dsc->state]);
-    next_state(dsc,sscf_11);
-    if (dsc->ops->rel_ind)
-	dsc->ops->rel_ind(dsc->user,user ? uu_data : NULL,uu_length);
-}
-
-
-static void sscf_rel_conf(void *user_data)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state != sscf_34)
-	diag(COMPONENT,DIAG_FATAL,"sscf_rel_conf in state %s",
-	  state_name[dsc->state]);
-    next_state(dsc,sscf_11);
-    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
-}
-
-
-static void sscf_data_ind(void *user_data,void *data,int length,int sn)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state != sscf_410)
-	diag(COMPONENT,DIAG_FATAL,"sscf_data_ind in state %s",
-	  state_name[dsc->state]);
-    if (dsc->ops->data_ind) dsc->ops->data_ind(dsc->user,data,length);
-    
-}
-
-
-static void sscf_res_ind(void *user_data,void *uu_data,int uu_length)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state != sscf_410)
-	diag(COMPONENT,DIAG_FATAL,"sscf_res_ind in state %s",
-	  state_name[dsc->state]);
-    sscop_res_resp(&dsc->sscop);
-    if (dsc->ops->estab_ind) dsc->ops->estab_ind(dsc->user,uu_data,uu_length);
-}
-
-
-static void sscf_res_conf(void *user_data)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->state != sscf_25)
-	diag(COMPONENT,DIAG_FATAL,"sscf_res_conf in state %s",
-	  state_name[dsc->state]);
-    next_state(dsc,sscf_410);
-    if (dsc->ops->estab_conf) dsc->ops->estab_conf(dsc->user,NULL,0);
-}
-
-
-static void sscf_unitdata_ind(void *user_data,void *data,int length)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->ops->unitdata) dsc->ops->unitdata(dsc->user,data,length);
-}
-
-
-static void sscf_cpcs_send(void *user_data,void *data,int length)
-{
-    SSCF_DSC *dsc = user_data;
-
-    if (dsc->ops->cpcs_send)
-	dsc->ops->cpcs_send(dsc->user,data,length);
-}
-
-
-static SSCOP_USER_OPS sscf_ops = {
-   sscf_estab_ind, *
-   sscf_estab_conf, *
-   sscf_rel_ind, *
-   sscf_rel_conf, *
-   sscf_restart,
-   sscf_res_ind,
-   sscf_res_conf,
-   sscf_rec_ind,
-   sscf_data_ind,
-   sscf_unitdata_ind,
-   NULL, /* no retr_ind */
-   NULL, /* no retr_comp */
-   NULL, /* no maa_data */
-   NULL, /* no maa_error */
-   sscf_cpcs_send
-};
-
-
-/* --- Invocation from user ------------------------------------------------ */
-
-
-void start_sscf(SSCF_DSC *dsc,SSCF_USER_OPS *ops,void *user_data)
-{
-    dsc->state = sscf_11;
-    dsc->ops = ops;
-    dsc->user = user_data;
-    start_sscop(&dsc->sscop,&sscf_ops,dsc);
-    dsc->sscop.cf_max_cc = SSCF_MaxCC;
-    dsc->sscop.cf_max_pd = SSCF_MaxPD;
-    dsc->sscop.cf_timer_cc = SSCF_Timer_CC;
-    dsc->sscop.cf_timer_poll = SSCF_Timer_POLL;
-    dsc->sscop.cf_timer_noresp = SSCF_Timer_NORESP;
-    dsc->sscop.cf_timer_keepalive = SSCF_Timer_KEEPALIVE;
-    dsc->sscop.cf_timer_idle = SSCF_Timer_IDLE;
-}
-
-
-void stop_sscf(SSCF_DSC *dsc)
-{
-    stop_sscop(&dsc->sscop);
-}
-
-
-void sscf_estab_req(SSCF_DSC *dsc,void *uu_data,int uu_length)
-{
-    switch (dsc->state) {
-	case sscf_11:
-	case sscf_34:
-	    next_state(dsc,sscf_22);
-	    sscop_estab_req(&dsc->sscop,uu_data,uu_length,1);
-	    return;
-	case sscf_410:
-	    next_state(dsc,sscf_25);
-	    sscop_res_req(&dsc->sscop,uu_data,uu_length);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_FATAL,"sscf_estab_req invoked in state %s",
-      state_name[dsc->state]);
-}
-
-
-void sscf_rel_req(SSCF_DSC *dsc,void *uu_data,int uu_length)
-{
-    switch (dsc->state) {
-	case sscf_11:
-	    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
-	    return;
-	case sscf_22:
-	case sscf_410:
-	case sscf_25:
-	    next_state(dsc,sscf_34);
-	    sscop_rel_req(&dsc->sscop,uu_data,uu_length);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_FATAL,"sscf_rel_req invoked in state %s",
-      state_name[dsc->state]);
-}
-
-
-void sscf_send(SSCF_DSC *dsc,void *data,int length)
-{
-    switch (dsc->state) {
-	case sscf_11:
-	    return;
-	case sscf_410:
-	    sscop_send(&dsc->sscop,data,length);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_WARN,"sscf_send invoked in state %s",
-      state_name[dsc->state]); /* make fatal later @@@ */
-}
-
-
-void sscf_unitdata(SSCF_DSC *dsc,void *data,int length)
-{
-    sscop_unitdata(&dsc->sscop,data,length);
-}
diff -ur --new-file old/atm/sigd/sscf.h new/atm/sigd/sscf.h
--- old/atm/sigd/sscf.h	Fri Oct  6 11:33:07 1995
+++ new/atm/sigd/sscf.h	Thu Jan  1 01:00:00 1970
@@ -1,56 +0,0 @@
-/* sscf.h - SSCF (Q.2130) user interface */
-
-/* Written 1995 by Werner Almesberger, EPFL-LRC */
-
-
-#ifndef SSCF_H
-#define SSCF_H
-
-#include "sscop.h"
-
-
-typedef enum { sscf_11,sscf_22,sscf_410,sscf_34,sscf_25 } SSCF_STATE;
-
-typedef struct {
-    SSCF_STATE state;
-    struct _sscf_user_ops *ops;
-    void *user;
-    SSCOP_DSC sscop;
-} SSCF_DSC;
-
-
-typedef struct _sscf_user_ops {
-    void (*estab_ind)(void *user_data,void *uu_data,int uu_length);
-	/* AAL-ESTABLISH.indication */
-    void (*estab_conf)(void *user_data,void *uu_data,int uu_length);
-	/* AAL-ESTABLISH.confirm */
-    void (*rel_ind)(void *user_data,void *uu_data,int uu_length);
-	/* AAL-RELEASE.indication */
-    void (*rel_conf)(void *user_data); /* AAL-RELEASE.confirm */
-    void (*restart)(void *user_data,void *uu_data,int uu_length,int ind);
-	/* AAL-RELEASE.indication or AAL-RELEASE.confirm immediately followed
-	   by AAL-ESTABLISH.indication */
-    void (*data_ind)(void *user_data,void *data,int length);
-	/* AAL-DATA.indication */
-    void (*unitdata)(void *user_data,void *data,int length);
-	/* AAL-UNITDATA.indication */
-    void (*cpcs_send)(void *user_data,void *data,int length);
-} SSCF_USER_OPS;
-
-
-/* Attach/detach protocol */
-
-void start_sscf(SSCF_DSC *dsc,SSCF_USER_OPS *ops,void *user_data);
-void stop_sscf(SSCF_DSC *dsc);
-
-/* Connection control */
-
-void sscf_estab_req(SSCF_DSC *dsc,void *uu_data,int uu_length);
-void sscf_rel_req(SSCF_DSC *dsc,void *uu_data,int uu_length);
-
-/* Send data */
-
-void sscf_send(SSCF_DSC *dsc,void *data,int length);
-void sscf_unitdata(SSCF_DSC *dsc,void *data,int length);
-
-#endif
diff -ur --new-file old/atm/sigd/sscop.c new/atm/sigd/sscop.c
--- old/atm/sigd/sscop.c	Fri May 31 15:44:11 1996
+++ new/atm/sigd/sscop.c	Thu Jan  1 01:00:00 1970
@@ -1,1995 +0,0 @@
-/* sscop.c - SSCOP (Q.2110) protocol */
- 
-/* Written 1995-1996 by Werner Almesberger, EPFL-LRC */
- 
-
-#if 1 /* debugging only */
-#include <stdio.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <netinet/in.h> /* for htonl, ntohl */
-
-#include "atmd.h"
-
-#include "sscop.h"
-
-
-/*
- * This is a quite exact translation of the ITU-T SSCOP SDL diagrams. They
- * are a pleasant example of how protocols _should_ be specified. It took me
- * less than a week to implement this.
- *
- * Calls back to the SSCOP user are always done last in a sequence of actions
- * to avoid reentrancy problems if the user invokes SSCOP from the callback
- * function. (Exception: data indications are delivered when needed. So you
- * shouldn't kill the protocol stack in the middle of this. Releasing the
- * call and such is fine, though.)
- *
- * Sequences of an AA-RELASE.indication/confirm immediately followed by a
- * AA-ESTABLISH.indication have been replaced by a new primitive called
- * "restart". This way, the SSCOP user is able to distinguish conditions where
- * SSCOP is in idle state after a callback from conditions where SSCOP wants to
- * continue (and where the user many not want to stop SSCOP).
- *
- * The entity receiving management information (e.g. maa_error) must not issue
- * any SSCOP primitives from that callback routine. Instead, actions must be
- * queued until SSCOP finishes processing of the current event.
- *
- * Comments of the type xx.yy are section or figure numbers in Q.2110
- *
- * KNOWN BUGS: The queue and buffer management stinks, especially the "buffer
- *	       cloning". All this ought to be rewritten to use skbuffs. Since
- *	       this will happen anyway if SSCOP ever gets integrated into the
- *	       kernel, we can safely ignore the problem for now.
- *
- *	       The lower layer is always assumed to be ready for sending. If
- *	       this is not the case (shouldn't happen), the PDU should be
- *	       discarded (or the process might be blocked for a few cycles).
- */
-
-
-#define COMPONENT "SSCOP"
-
-
-/*#define debug (void)*/
-
-/* Configurable SSCOP parameters */
-
-#define SSCOP_CF_MR	 30	/* buffer size */
-#define SSCOP_CF_MaxCC	 10	/* max timeouts */
-#define SSCOP_CF_MaxPD	100	/* SD/POLL ratio */
-#define SSCOP_CF_MaxSTAT 67	/* max elements in STAT PDU, 7.7 */
-
-#undef POLL_AFTER_RETRANSMISSION /* 20.38, #define this if needed */
-
-
-/* Timers */
-
-/*
- * Assumptions: RTT = 1 sec, remote needs about 1 sec to wake up. SSCF changes
- * all this anyway.
- */
-
-#define TIMER_CC	 2000000 /* 2 sec (== RTT+eps) */
-#define TIMER_POLL	 1000000 /* 1 sec (== RTT) */
-#define TIMER_NORESP	 7000000 /* 7 sec (keepalive+RTT+eps) */
-#define TIMER_KEEPALIVE  5000000 /* 5 sec (> poll && > RTT) */
-#define TIMER_IDLE	30000000 /* 30 sec (>> keepalive) */
-
-
-/* SSCOP PDU types, 7.1 */
-
-#define SSCOP_BGN	 1 /* Request Initialization */
-#define SSCOP_BGAK	 2 /* Request Acknowledgement */
-#define SSCOP_BGREJ	 7 /* Connection Reject */
-#define SSCOP_END	 3 /* Disconnect Command */
-#define SSCOP_ENDAK	 4 /* Disconnect Acknowledgement */
-#define SSCOP_RS	 5 /* Resynchronization Command */
-#define SSCOP_RSAK	 6 /* Resynchronization Acknowledgement */
-#define SSCOP_ER	 9 /* Recovery Command */
-#define SSCOP_ERAK	15 /* Recovery Acknowledgement */
-#define SSCOP_SD	 8 /* Sequence Connection-mode Data */
-#define SSCOP_POLL	10 /* Transmitter State Information with request ... */
-#define SSCOP_STAT	11 /* Solicited Receiver State Information */
-#define SSCOP_USTAT	12 /* Unsolicited Receiver State Information */
-#define SSCOP_UD	13 /* Unnumbered User Data */
-#define SSCOP_MD	14 /* Unnumbered Management Data */
-
-
-/* Trailer format macros */
-
-#define SSCOP_TRAIL(type,pad,n) (htonl((n) | ((type) << 24) | ((pad) << 30)))
-#define SSCOP_S_BIT 0x10000000
-#define SSCOP_TYPE(last) ((ntohl(last) >> 24) & 15)
-#define SSCOP_PAD(last) (ntohl(last) >> 30)
-#define SSCOP_N(last) (ntohl(last) & 0xffffff)
-#define SSCOP_S(last) (ntohl(last) & SSCOP_S_BIT)
-
-
-/* Some helper macros */
-
-#define START_TIMER(t) ({ STOP_TIMER(dsc->timer_##t); \
-  dsc->timer_##t = start_timer(dsc->cf_timer_##t, sscop_##t##_exp,dsc); })
-#define STOP_TIMER(t) ({ if (t) stop_timer(t); t = NULL; })
-#define MOD24(x) ((x) & ((1 << 24)-1))
-#define INC24(v) (v = MOD24(v+1))
-#define INC8(v) (v = (v+1) & 255)
-#define NORMALIZE(x,b) ( \
-    (b) < (1 << 23) ? \
-      (x) < (b)+(1 << 23) ? (x)+(1 << 24) : x : \
-      (x) < (b)-(1 << 23) ? (x)+(1 << 24) : x)
-#define NORM_RX(v) NORMALIZE((v),dsc->vr_r)
-#define NORM_TX(v) NORMALIZE((v),dsc->vt_a)
-
-
-/* Helper macros for PDU construction and decomposition */
-
-#define PDU_VARS \
-  unsigned char type; \
-  int length; \
-  int s,ps,r,mr,sq
-#define DECOMPOSE_PDU(dsc,msg,size) decompose_pdu(dsc,msg,size,&type, \
-  &length,&s,&ps,&r,&mr,&sq)
-#define PRINT_PDU(dsc,label,data) print_pdu(dsc,label,type,data,&length, \
-  &s,&ps,&r,&mr,&sq)
-
-
-static const char *pdu_name[] = { "???","BGN","BGAK","END","ENDAK","RS","RSAK",
-  "BGREJ","SD","ER","POLL","STAT","USTAT","UD","MD","ERAK" };
-
-static const char *state_name[] = { "Idle","OutConnPend","InConnPend",
-  "OutDiscPend","OutResyPend","InResyPend","OutRecPend","RecRespPend",
-  "InRecPend","DataTransReady" };
-
-
-static void maa_error(SSCOP_DSC *dsc,char code,int count)
-{
-    static const char *const msgs[] = {
-	"A:SD PDU","B:BGN PDU","C:BGAK PDU","D:BGREJ PDU","E:END PDU",
-	"F:ENDAK PDU","G:POLL PDU","H:STAT PDU","I:USTAT PDU","J:RS",
-	"K:RSAK PDU","L:ER","M:ERAK","O:VT(CC)>=MaxCC",
-	"P:Timer_NO_RESPONSE expiry","Q:SD or POLL, N(S) error",
-	"R:STAT N(PS) error","S:STAT N(R) or list elements error",
-	"T:USTAT N(R) or list elements error","U:PDU length violation",
-	"V:SD PDUs must be retransmitted","W:Lack of credit",
-	"X:Credit obtained","\0:Unknown error code" };
-    const char *const *walk;
-
-    if (dsc->ops->maa_error)
-	if (!dsc->ops->maa_error(dsc->user,code,count)) return;
-    for (walk = msgs; **walk; walk++)
-	if (**walk == code) break;
-    if (code != 'V')
-	diag(COMPONENT,DIAG_WARN,"layer management - error %c \"%s\"",code,
-	  (*walk)+2);
-    else diag(COMPONENT,DIAG_WARN,"layer management - error %c,%d \"%s\"",code,
-      count,(*walk)+2);
-}
-
-
-static void print_pdu(SSCOP_DSC *dsc,const char *label,unsigned char type,
-  void *data,const int *length,const int *s,const int *ps,const int *r,
-  const int *mr,const int *sq)
-{
-    int len;
-    int *list;
-
-    switch (type) {
-	case SSCOP_SD:
-	    diag(COMPONENT,DIAG_DEBUG,"%s SD(S=%d,len=%d)",label,*s,*length);
-	    break;
-	case SSCOP_POLL:
-	    diag(COMPONENT,DIAG_DEBUG,"%s POLL(PS=%d,S=%d)",label,*ps,*s);
-	    break;
-	case SSCOP_STAT:
-	    if (*length & 3) {
-		diag(COMPONENT,DIAG_WARN,"%s STAT PDU has wrong size (%d)",
-		  label,*length);
-		break;
-	    }
-	    len = *length/4;
-	    diag(COMPONENT,DIAG_DEBUG,"%s STAT(PS=%d,MR=%d,R=%d,items=%d:",
-	      label,*ps,*mr,*r,len);
-	    list = (int *) data;
-	    while (len > 1) {
-		diag(COMPONENT,DIAG_DEBUG,"%s    %d..%d",label,ntohl(list[0]),
-		  ntohl(list[1]));
-		list += 2;
-		len -= 2;
-	    }
-	    if (!len)
-		diag(COMPONENT,DIAG_DEBUG,"%s    <last is absent>)",label);
-	    else diag(COMPONENT,DIAG_DEBUG,"%s    <next is> %d)",label,
-		  ntohl(*list));
-	    break;
-	case SSCOP_USTAT:
-	    if (*length != 8) {
-		diag(COMPONENT,DIAG_WARN,"%s USTAT PDU has wrong size (%d)",
-		  label,*length);
-		break;
-	    }
-	    list = (int *) data;
-	    diag(COMPONENT,DIAG_DEBUG,"%s USTAT(MR=%d,R=%d,%d..%d)",label,*mr,
-	      *r,ntohl(list[0]),
-	      ntohl(list[1]));
-	    break;
-	case SSCOP_UD:
-	    diag(COMPONENT,DIAG_DEBUG,"%s UD(len=%d)",label,*length);
-	    break;
-	case SSCOP_MD:
-	    diag(COMPONENT,DIAG_DEBUG,"%s MD(len=%d)",label,*length);
-	    break;
-	case SSCOP_BGN:
-	    diag(COMPONENT,DIAG_DEBUG,"%s BGN(SQ=%d,MR=%d,len=%d)",label,*sq,
-	      *mr,*length);
-	    break;
-	case SSCOP_BGAK:
-	    diag(COMPONENT,DIAG_DEBUG,"%s BGAK(MR=%d,len=%d)",label,*mr,
-	      *length);
-	    break;
-	case SSCOP_BGREJ:
-	    diag(COMPONENT,DIAG_DEBUG,"%s BGREJ(len=%d)",label,*length);
-	    break;
-	case SSCOP_END:
-	    diag(COMPONENT,DIAG_DEBUG,"%s END(S=%d,len=%d)",label,*s,*length);
-	    break;
-	case SSCOP_ENDAK:
-	    diag(COMPONENT,DIAG_DEBUG,"%s ENDAK()",label);
-	    break;
-	case SSCOP_RS:
-	    diag(COMPONENT,DIAG_DEBUG,"%s RS(SQ=%d,MR=%d,len=%d)",label,*sq,
-	      *mr,*length);
-	    break;
-	case SSCOP_RSAK:
-	    diag(COMPONENT,DIAG_DEBUG,"%s RSAK(MR=%d)",label,*mr);
-	    break;
-	case SSCOP_ER:
-	    diag(COMPONENT,DIAG_DEBUG,"%s ER(MR=%d)",label,*mr);
-	    break;
-	case SSCOP_ERAK:
-	    diag(COMPONENT,DIAG_DEBUG,"%s ERAK(MR=%d)",label,*mr);
-	    break;
-	default:
-	    diag(COMPONENT,DIAG_ERROR,"%s unknown PDU type %d\n",label,type);
-    }
-}
-
-
-static int decompose_pdu(SSCOP_DSC *dsc,void *msg,int size,
-  unsigned char *type,int *length,int *s,int *ps,int *r,int *mr,int *sq)
-{
-    unsigned long *last;
-    unsigned char pad;
-    int n;
-
-/*
- * *length is undefined if PDU has no variable-length data part
- */
-
-    if (size < 4 || (size & 3)) {
-	diag(COMPONENT,DIAG_DEBUG,"invalid message length (%d)",size);
-	maa_error(dsc,'U',0);
-	return -1;
-    }
-    last = (unsigned long *) ((char *) msg+size-4);
-    *type = SSCOP_TYPE(*last);
-    pad = SSCOP_PAD(*last);
-    n = SSCOP_N(*last);
-    *length = size-4-pad;
-    switch (*type) {
-	case SSCOP_SD:
-	    *s = n;
-	    break;
-	case SSCOP_POLL:
-	    if (size != 8) {
-		diag(COMPONENT,DIAG_DEBUG,"POLL PDU has bad length (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *s = n;
-	    *ps = SSCOP_N(*(unsigned long *) msg);
-	    break;
-	case SSCOP_STAT:
-	    if (size < 12) {
-		diag(COMPONENT,DIAG_DEBUG,"STAT PDU too short (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    if (*length & 3) {
-		diag(COMPONENT,DIAG_DEBUG,"STAT PDU has bad length (%d)",
-		  length);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *r = n;
-	    *mr = SSCOP_N(last[-1]);
-	    *ps = SSCOP_N(last[-2]);
-	    *length -= 8;
-	    break;
-	case SSCOP_USTAT:
-	    if (size != 16) {
-		diag(COMPONENT,DIAG_DEBUG,"USTAT PDU has bad length (%d)",
-		  size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *r = n;
-	    *mr = SSCOP_N(last[-1]);
-	    *length -= 4;
-	    break;
-	case SSCOP_UD:
-	case SSCOP_MD:
-	    break;
-	case SSCOP_BGN:
-	    if (size < 8) {
-		diag(COMPONENT,DIAG_DEBUG,"BGN PDU too short (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *mr = n;
-	    *sq = SSCOP_N(last[-1]) & 0xff;
-	    *length -= 4;
-	    break;
-	case SSCOP_BGAK:
-	    if (size < 8) {
-		diag(COMPONENT,DIAG_DEBUG,"BGAK PDU too short (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *mr = n;
-	    *length -= 4;
-	    break;
-	case SSCOP_BGREJ:
-	    if (size < 8) {
-		diag(COMPONENT,DIAG_DEBUG,"BGREJ PDU too short (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *length -= 4;
-	    break;
-	case SSCOP_END:
-	    if (size < 8) {
-		diag(COMPONENT,DIAG_DEBUG,"END PDU too short (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *s = !!(ntohl(*last) & SSCOP_S_BIT);
-	    *length -= 4;
-	    break;
-	case SSCOP_ENDAK:
-	    if (size != 8) {
-		diag(COMPONENT,DIAG_DEBUG,"ENDAK PDU has bad length (%d)",
-		  size);
-		maa_error(dsc,'U',0);
-		if (size < 4) return -1; /* make it work with Fore */
-	    }
-	    break;
-	case SSCOP_RS:
-	    if (size < 8) {
-		diag(COMPONENT,DIAG_DEBUG,"RS PDU too short (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *mr = n;
-	    *sq = SSCOP_N(last[-1]) & 0xff;
-	    *length -= 4;
-	    break;
-	case SSCOP_RSAK:
-	    if (size != 8) {
-		diag(COMPONENT,DIAG_DEBUG,"RSAK PDU has bad length (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *mr = n;
-	    break;
-	case SSCOP_ER:
-	    if (size != 8) {
-		diag(COMPONENT,DIAG_DEBUG,"ER PDU has bad length (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *mr = n;
-	    *sq = SSCOP_N(last[-1]) & 0xff;
-	    break;
-	case SSCOP_ERAK:
-	    if (size != 8) {
-		diag(COMPONENT,DIAG_DEBUG,"ERAK PDU has bad length (%d)",size);
-		maa_error(dsc,'U',0);
-		return -1;
-	    }
-	    *mr = n;
-	    break;
-	default:
-	    diag(COMPONENT,DIAG_DEBUG,"unknown PDU type %d (0x%x)",*type,
-	      *type);
-	    return -1;
-    }
-    return 0;
-}
-
-
-static BUFFER *build_pdu(SSCOP_DSC *dsc,unsigned char type,void *data,
-  int num)
-{
-    BUFFER *buf;
-    unsigned long *trailer;
-    int pad;
-
-    buf = buffer_create(num+12,dsc->vt_s); /* space for trailers */
-    if (data && num) {
-	memset((char *) buf->data+(num & ~3),0,4); /* clear padding area */
-	memcpy((unsigned char *) buf->data,data,num);
-	pad = (4-(num & 3)) & 3;
-	trailer = (unsigned long *) ((char *) buf->data+num+pad);
-    }
-    else {
-	pad = 0;
-	trailer = (unsigned long *) buf->data;
-    }
-    diag(COMPONENT,DIAG_DEBUG,"generating %s PDU",pdu_name[type]);
-    switch (type) {
-	case SSCOP_BGN:
-	case SSCOP_RS:
-	case SSCOP_ER:
-	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vt_sq);
-	    *trailer++ = SSCOP_TRAIL(type,pad,dsc->vr_mr);
-	    break;
-	case SSCOP_BGAK:
-	case SSCOP_RSAK:
-	case SSCOP_ERAK:
-	    *trailer++ = SSCOP_TRAIL(0,0,0);
-	    *trailer++ = SSCOP_TRAIL(type,pad,dsc->vr_mr);
-	    break;
-	case SSCOP_END: /* S bit is indicated by "num" */
-	    *trailer++ = SSCOP_TRAIL(0,0,0);
-	    *trailer++ = SSCOP_TRAIL(type,pad,0) | (num ? htonl(SSCOP_S_BIT) :
-	      0);
-	    break;
-	case SSCOP_BGREJ:
-	case SSCOP_ENDAK:
-	    *trailer++ = SSCOP_TRAIL(0,0,0);
-	    *trailer++ = SSCOP_TRAIL(type,pad,0);
-	    break;
-	case SSCOP_POLL:
-	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vt_ps);
-	    /* fall through */
-	case SSCOP_SD:
-	    *trailer++ = SSCOP_TRAIL(type,pad,dsc->vt_s);
-	    break;
-	case SSCOP_STAT:
-	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vr_ps);
-	    /* fall through */
-	case SSCOP_USTAT:
-	    *trailer++ = SSCOP_TRAIL(0,0,dsc->vr_mr);
-	    *trailer++ = SSCOP_TRAIL(type,0,dsc->vr_r);
-	    break;
-	case SSCOP_UD:
-	case SSCOP_MD:
-	    *trailer++ = SSCOP_TRAIL(type,pad,0);
-	    break;
-	default:
-	    diag(COMPONENT,DIAG_FATAL,
-	      "requested construction of unknown PDU type %d",type);
-    }
-    buf->length = (char *) trailer-(char *) buf->data;
-    return buf;
-}
-
-
-static void dump_pdu(void *data,int len)
-{
-    int i;
-
-    if (!debug) return;
-    for (i = 0; i < len; i++) {
-	if (!(i & 15)) printf("%s%04X:",i ? "\n" : "",i);
-	printf(" %02X",((unsigned char *) data)[i]);
-    }
-    putchar('\n');
-}
-
-
-static void emit_pdu(SSCOP_DSC *dsc,BUFFER *buf)
-{
-    BUFFER **last;
-
-    dump_pdu(buf->data,buf->length);
-    {
-	PDU_VARS;
-
-	if (DECOMPOSE_PDU(dsc,buf->data,buf->length))
-	    diag(COMPONENT,DIAG_FATAL,"composed garbage");
-	PRINT_PDU(dsc,"SEND",buf->data);
-    }
-    if (dsc->ops->cpcs_send)
-	dsc->ops->cpcs_send(dsc->user,buf->data,buf->length);
-    switch (SSCOP_TYPE(*(unsigned long *) ((char *) buf->data+buf->length-4))) {
-	case SSCOP_BGN:
-	    last = &dsc->last_bgn;
-	    break;
-	case SSCOP_END:
-	    last = &dsc->last_end;
-	    break;
-	case SSCOP_RS:
-	    last = &dsc->last_rs;
-	    break;
-	case SSCOP_ER:
-	    last = &dsc->last_er;
-	    break;
-	default:
-	    buffer_discard(buf);
-	    return;
-    }
-    if (*last && *last != buf) {
-	buffer_discard(*last);
-	*last = NULL;
-    }
-    *last = buf;
-}
-
-
-static void resend_pdu(SSCOP_DSC *dsc,BUFFER *buf)
-{
-    if (!buf) diag(COMPONENT,DIAG_FATAL,"resend buffer is NULL");
-    emit_pdu(dsc,buf);
-}
-
-
-static void send_pdu(SSCOP_DSC *dsc,unsigned char type,void *data,int size)
-{
-    BUFFER *buf;
-
-    buf = build_pdu(dsc,type,data,size);
-    emit_pdu(dsc,buf);
-}
-
-
-static void next_state(SSCOP_DSC *dsc,SSCOP_STATE state)
-{
-    diag(COMPONENT,DIAG_DEBUG,"entering state %s (%d)",state_name[state],
-      (int) state);
-    dsc->state = state;
-}
-
-
-static void bad_pdu(SSCOP_DSC *dsc,unsigned char type)
-{
-		/*           111111 */
-		/* 0123456789012345 */
-    maa_error(dsc,"?BCxFJKDALGHIyzM"[type],0);
-}
-
-
-static void send_ustat(SSCOP_DSC *dsc,int first,int last)
-{
-    unsigned long range[2];
-
-    range[0] = htonl(first);
-    range[1] = htonl(last);
-    send_pdu(dsc,SSCOP_USTAT,range,8);
-}
-
-
-/* --- Utility functions --------------------------------------------------- */
-
-
-static void clear_receive_buffer(SSCOP_DSC *dsc)
-{
-    queue_clear(&dsc->rx_buf);
-}
-
-
-static void clear_transmission_buffer(SSCOP_DSC *dsc)
-{
-    queue_clear(&dsc->tx_buf);
-}
-
-
-static void clear_transmission_queue(SSCOP_DSC *dsc)
-{
-    queue_clear(&dsc->tx_q);
-}
-
-
-static void clear_retransmission_queue(SSCOP_DSC *dsc)
-{
-    queue_clear(&dsc->rt_q);
-}
-
-
-/* --- SSCOP subroutines --------------------------------------------------- */
-
-
-static void sscop_poll_exp(void *user);
-static void sscop_keepalive_exp(void *user);
-static void sscop_noresp_exp(void *user);
-
-
-static void release_buffers(SSCOP_DSC *dsc) /* 20.49 */
-{
-    clear_transmission_queue(dsc);
-    clear_transmission_buffer(dsc);
-    clear_retransmission_queue(dsc);
-    clear_receive_buffer(dsc);
-}
-
-
-static void clear_transmitter(SSCOP_DSC *dsc) /* 20.49 */
-{
-    if (!dsc->clear_buffers) {
-	clear_transmission_queue(dsc);
-	clear_transmission_buffer(dsc);
-    }
-}
-
-
-static void prepare_recovery(SSCOP_DSC *dsc) /* 20.49 */
-{
-    if (dsc->clear_buffers) {
-	clear_transmission_queue(dsc);
-	clear_transmission_buffer(dsc);
-    }
-    clear_retransmission_queue(dsc);
-}
-
-
-static void prepare_retrieval(SSCOP_DSC *dsc) /* 20.49 */
-{
-    prepare_recovery(dsc);
-    clear_receive_buffer(dsc);
-}
-
-
-static void deliver_data(SSCOP_DSC *dsc) /* 20.49 */
-{
-    BUFFER *buf;
-
-    if (!dsc->clear_buffers) {
-	while ((buf = queue_get(&dsc->rx_buf))) {
-	    if (dsc->ops->data_ind)
-		dsc->ops->data_ind(dsc->user,buf->data,buf->length,buf->key);
-	    buffer_discard(buf);
-	}
-    }
-    clear_receive_buffer(dsc);
-}
-
-
-static void initialize_state_variables(SSCOP_DSC *dsc) /* 20.49 */
-{
-    dsc->vt_s = dsc->vt_ps = dsc->vt_a = 0;
-    dsc->vt_pa = 1;
-    dsc->vt_pd = 0;
-    dsc->credit = 1;
-    dsc->vr_r = dsc->vr_h = 0;
-}
-
-
-static int detect_retransmission(SSCOP_DSC *dsc,int sq) /* 20.50 */
-{
-    if (dsc->vr_sq == sq) return 1;
-    dsc->vr_sq = sq;
-    return 0;
-}
-
-
-static void set_poll_timer(SSCOP_DSC *dsc) /* 20.50 */
-{
-    if (queue_peek(&dsc->tx_q) || dsc->vt_s != dsc->vt_a) START_TIMER(poll);
-    else START_TIMER(keepalive);
-}
-
-
-static void reset_data_transfer_timers(SSCOP_DSC *dsc) /* 20.51 */
-{
-    STOP_TIMER(dsc->timer_poll);
-    STOP_TIMER(dsc->timer_keepalive);
-    STOP_TIMER(dsc->timer_noresp);
-    STOP_TIMER(dsc->timer_idle);
-}
-
-
-static void set_data_transfer_timers(SSCOP_DSC *dsc) /* 20.51 */
-{
-    START_TIMER(poll);
-    START_TIMER(noresp);
-}
-
-
-static void initialize_vr_mr(SSCOP_DSC *dsc)
-{
-    dsc->vr_mr = MOD24(dsc->vr_h+SSCOP_CF_MR);
-}
-
-
-static void update_vr_mr(SSCOP_DSC *dsc)
-{
-    initialize_vr_mr(dsc);
-}
-
-
-/* --- Timer handlers ------------------------------------------------------ */
-
-
-static void sscop_cc_exp(void *user)
-{
-    SSCOP_DSC *dsc = user;
-
-    diag(COMPONENT,DIAG_DEBUG,"Timer CC has expired");
-    dsc->timer_cc = NULL;
-    switch (dsc->state) {
-	case sscop_outconn: /* 20.9 */
-	    if (dsc->vt_cc < dsc->cf_max_cc) {
-		dsc->vt_cc++;
-		resend_pdu(dsc,dsc->last_bgn);
-		START_TIMER(cc);
-		return;
-	    }
-	    maa_error(dsc,'O',0);
-	    send_pdu(dsc,SSCOP_END,NULL,1);
-	    next_state(dsc,sscop_idle);
-	    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
-	    return;
-	case sscop_outdisc: /* 20.15 */
-	    if (dsc->vt_cc < dsc->cf_max_cc) {
-		dsc->vt_cc++;
-		resend_pdu(dsc,dsc->last_end);
-		START_TIMER(cc);
-		return;
-	    }
-	    maa_error(dsc,'O',0);
-	    next_state(dsc,sscop_idle);
-	    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
-	    return;
-	case sscop_outres: /* 20.18 */
-	    if (dsc->vt_cc < dsc->cf_max_cc) {
-		dsc->vt_cc++;
-		resend_pdu(dsc,dsc->last_rs);
-		START_TIMER(cc);
-		return;
-	    }
-	    maa_error(dsc,'O',0);
-	    send_pdu(dsc,SSCOP_END,NULL,1);
-	    next_state(dsc,sscop_idle);
-	    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
-	    return;
-	case sscop_outrec: /* 20.24 */
-	    if (dsc->vt_cc < dsc->cf_max_cc) {
-		dsc->vt_cc++;
-		resend_pdu(dsc,dsc->last_er);
-		START_TIMER(cc);
-		return;
-	    }
-	    maa_error(dsc,'O',0);
-	    send_pdu(dsc,SSCOP_END,NULL,1);
-	    clear_receive_buffer(dsc);
-	    next_state(dsc,sscop_idle);
-	    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_FATAL,"Timer CC expired in state %s",
-      state_name[dsc->state]);
-}
-
-
-static void sscop_common_exp(void *user)
-{
-    SSCOP_DSC *dsc = user;
-
-    if (dsc->state != sscop_ready) {
-	diag(COMPONENT,DIAG_FATAL,"sscop_common_exp invoked in state %s",
-	  state_name[dsc->state]);
-	return;
-    }
-    INC24(dsc->vt_ps);
-    send_pdu(dsc,SSCOP_POLL,NULL,0);
-    dsc->vt_pd = 0;
-    set_poll_timer(dsc);
-}
-
-
-static void sscop_poll_exp(void *user)
-{
-    SSCOP_DSC *dsc = user;
-
-    diag(COMPONENT,DIAG_DEBUG,"Timer POLL has expired");
-    dsc->timer_poll = NULL;
-    sscop_common_exp(user);
-}
-
-
-static void sscop_keepalive_exp(void *user)
-{
-    SSCOP_DSC *dsc = user;
-
-    diag(COMPONENT,DIAG_DEBUG,"Timer KEEPALIVE has expired");
-    dsc->timer_keepalive = NULL;
-    sscop_common_exp(user);
-}
-
-
-static void sscop_idle_exp(void *user)
-{
-    SSCOP_DSC *dsc = user;
-
-    diag(COMPONENT,DIAG_DEBUG,"Timer IDLE has expired");
-    dsc->timer_idle = NULL;
-    START_TIMER(noresp);
-    sscop_poll_exp(user);
-}
-
-
-static void sscop_noresp_exp(void *user)
-{
-    SSCOP_DSC *dsc = user;
-
-    diag(COMPONENT,DIAG_DEBUG,"Timer NORESP has expired");
-    dsc->timer_noresp = NULL;
-    if (dsc->state != sscop_ready) {
-	diag(COMPONENT,DIAG_FATAL,"Timer NORESP expired in state %s",
-	  state_name[dsc->state]);
-	return;
-    }
-    reset_data_transfer_timers(dsc);
-    maa_error(dsc,'P',0);
-    send_pdu(dsc,SSCOP_END,NULL,1);
-    prepare_retrieval(dsc);
-    next_state(dsc,sscop_idle);
-    if (dsc->ops->rel_ind) dsc->ops->rel_ind(dsc->user,NULL,0,0);
-}
-
-
-/* --- Transmit engine ----------------------------------------------------- */
-
-
-static void try_to_send(SSCOP_DSC *dsc)
-{
-    BUFFER *buf,*buf2;
-
-    if (dsc->state != sscop_ready) return;
-    while (queue_peek(&dsc->rt_q) || NORM_TX(dsc->vt_s) <
-      NORM_TX(dsc->vt_ms) || dsc->timer_idle) {
-	buf = queue_get(&dsc->rt_q);
-	if (buf) {
-	    if (!(buf2 = queue_lookup(&dsc->tx_buf,buf->key)))
-		diag(COMPONENT,DIAG_FATAL,"didn't find PDU %d in TX buffer",
-		  buf->key);
-	    emit_pdu(dsc,buf);
-	    buf2->extra = dsc->vt_ps;
-#ifdef POLL_AFTER_RETRANSMISSION
-	    if (!queue_peek(dsc->rt_q))
-		/* fall through to goto */
-#endif
-	    goto B; /* sigh ... */
-	}
-	else {
-	    if (!queue_peek(&dsc->tx_q)) return;
-	    if (NORM_TX(dsc->vt_s) < NORM_TX(dsc->vt_ms)) {
-		buf = queue_get(&dsc->tx_q);
-		buf2 = build_pdu(dsc,SSCOP_SD,buf->data,buf->length);
-		buffer_discard(buf);
-		buf2->key = dsc->vt_s;
-		buf2->extra = dsc->vt_ps;
-		emit_pdu(dsc,buffer_clone(buf2));
-		queue_put(&dsc->tx_buf,buf2);
-		INC24(dsc->vt_s);
-	B:
-		/* B */
-		dsc->vt_pd++;
-		if (dsc->timer_poll) {
-		    if (dsc->vt_pd < dsc->cf_max_pd) continue;
-		}
-		else {
-		    if (!dsc->timer_idle) STOP_TIMER(dsc->timer_keepalive);
-		    else {
-			STOP_TIMER(dsc->timer_idle);
-			START_TIMER(noresp);
-		    }
-		    if (dsc->vt_pd < dsc->cf_max_pd) {
-			START_TIMER(poll);
-			continue;
-		    }
-		}
-	    }
-	    else {
-		STOP_TIMER(dsc->timer_idle);
-		START_TIMER(noresp);
-	    }
-	}
-	/* A */
-	INC24(dsc->vt_ps);
-	send_pdu(dsc,SSCOP_POLL,NULL,0);
-	dsc->vt_pd = 0;
-	START_TIMER(poll);
-    }
-}
-
-
-/* --- Incoming non-CC PDUs in Data Transfer Ready state ------------------- */
-
-
-static void start_error_recov(SSCOP_DSC *dsc,char code)
-{
-    reset_data_transfer_timers(dsc);
-    maa_error(dsc,code,0);
-    /* D */
-    dsc->vt_cc = 1;
-    INC8(dsc->vt_sq);
-    initialize_vr_mr(dsc);
-    send_pdu(dsc,SSCOP_ER,NULL,0);
-    prepare_recovery(dsc);
-    START_TIMER(cc);
-    next_state(dsc,sscop_outrec);
-}
-
-
-static void data_sd(SSCOP_DSC *dsc,int s,void *msg,int length) /* 20.38-39 */
-{
-    BUFFER *buf;
-
-    if (NORM_RX(s) >= NORM_RX(dsc->vr_mr)) {
-	if (NORM_RX(dsc->vr_h) >= NORM_RX(dsc->vr_mr)) {
-	    send_ustat(dsc,dsc->vr_h,dsc->vr_mr);
-	    dsc->vr_h = dsc->vr_mr;
-	}
-	return;
-    }
-    if (s == dsc->vr_r) {
-	if (dsc->ops->data_ind) dsc->ops->data_ind(dsc->user,msg,length,s);
-	if (s == dsc->vr_h) {
-	    dsc->vr_r = dsc->vr_h = MOD24(s+1);
-	    update_vr_mr(dsc);
-	    return;
-	}
-	while (1) {
-	    INC24(dsc->vr_r);
-	    buf = queue_lookup(&dsc->rx_buf,dsc->vr_r);
-	    if (!buf) break;
-	    queue_remove(&dsc->rx_buf,buf);
-	    if (dsc->ops->data_ind)
-		dsc->ops->data_ind(dsc->user,buf->data,buf->length,buf->key);
-	    buffer_discard(buf);
-	}
-	return;
-    }
-    buf = buffer_create(length,s);
-    memcpy(buf->data,msg,length);
-    if (s == dsc->vr_h) {
-	queue_put(&dsc->rx_buf,buf);
-	INC24(dsc->vr_h);
-	update_vr_mr(dsc);
-	return;
-    }
-    if (NORM_RX(dsc->vr_h) < NORM_RX(s)) {
-	queue_put(&dsc->rx_buf,buf);
-	send_ustat(dsc,dsc->vr_h,s);
-	dsc->vr_h = MOD24(s+1);
-	update_vr_mr(dsc);
-	return;
-    }
-    if (queue_lookup(&dsc->rx_buf,s)) queue_put(&dsc->rx_buf,buf);
-    else {
-	buffer_discard(buf);
-	start_error_recov(dsc,'Q');
-    }
-}
-
-
-/*
- * Some of the NORM_RXs in data_poll are certainly unnecessary. Maybe even
- * all of them could be removed ...
- */
-
-
-static void data_poll(SSCOP_DSC *dsc,int s) /* 20.41-42 */
-{
-    int curr,i;
-
-    if (!dsc->list) dsc->list = alloc(dsc->cf_max_stat*sizeof(int));
-    if (NORM_RX(dsc->vr_h) > NORM_RX(s)) {
-	start_error_recov(dsc,'Q');
-	return;
-    }
-    dsc->vr_h = NORM_RX(dsc->vr_mr) < NORM_RX(s) ? dsc->vr_mr : s;
-    update_vr_mr(dsc);
-    /* K */
-    curr = 0;
-    i = dsc->vr_r;
-    while (NORM_RX(i) < NORM_RX(dsc->vr_h)) {
-	if (queue_lookup(&dsc->rx_buf,i)) {
-	    INC24(i);
-	    continue;
-	}
-	dsc->list[curr++] = htonl(i);
-	if (curr >= dsc->cf_max_stat) {
-	    send_pdu(dsc,SSCOP_STAT,dsc->list,curr*4);
-	    curr = 0;
-	    dsc->list[curr++] = htonl(i);
-	}
-	do INC24(i);
-	while (i != dsc->vr_h && !queue_lookup(&dsc->rx_buf,i));
-	dsc->list[curr++] = htonl(i);
-    }
-    if (NORM_RX(i) > NORM_RX(dsc->vr_h)) dsc->list[curr++] = htonl(i);
-    send_pdu(dsc,SSCOP_STAT,dsc->list,curr*4);
-}
-
-
-static void data_ustat(SSCOP_DSC *dsc,int mr,int r,int e1,int e2) /* 20.43 */
-{
-    BUFFER *buf,*buf2;
-    int seq1,seq2,i;
-
-    if (NORM_TX(dsc->vt_a) <= NORM_TX(r) && NORM_TX(r) < NORM_TX(dsc->vt_s)) {
-	for (i = dsc->vt_a; i != r; INC24(i)) {
-	    buf = queue_lookup(&dsc->tx_buf,i);
-	    if (buf) {
-		queue_remove(&dsc->tx_buf,buf);
-		buffer_discard(buf);
-	    }
-	}
-	dsc->vt_a = r;
-	dsc->vt_ms = mr;
-	seq1 = e1;
-	seq2 = e2;
-	if (NORM_TX(dsc->vt_a) <= NORM_TX(seq1) && NORM_TX(seq1) <
-	  NORM_TX(seq2) && NORM_TX(seq2) < NORM_TX(dsc->vt_s)) {
-	    /* G */
-	    while ((buf = queue_lookup(&dsc->tx_buf,seq1))) {
-		buf2 = buffer_clone(buf);
-		queue_put(&dsc->rt_q,buf2);
-		INC24(seq1);
-		if (seq1 == seq2) {
-		    maa_error(dsc,'V',e2-e1);
-		    return;
-		}
-	    }
-	}
-    }
-    /* F */
-    start_error_recov(dsc,'T');
-}
-
-
-static void data_stat(SSCOP_DSC *dsc,int ps,int mr,int r,unsigned long *list,
-  int length) /* 20.44-46 */
-{
-    BUFFER *buf,*buf2;
-    unsigned long *curr,tmp;
-    int i,count,seq1,seq2;
-
-    if (NORM_TX(dsc->vt_pa) > NORM_TX(ps) || NORM_TX(ps) >
-      NORM_TX(dsc->vt_ps)) {
-	start_error_recov(dsc,'R');
-	return;
-    }
-    if (NORM_TX(dsc->vt_a) > NORM_TX(r) || NORM_TX(r) >
-      NORM_TX(dsc->vt_s)) {
-	/* H */
-	start_error_recov(dsc,'S');
-	return;
-    }
-    for (i = dsc->vt_a; i != r; INC24(i)) {
-	buf = queue_lookup(&dsc->tx_buf,i);
-	if (buf) {
-	    queue_remove(&dsc->tx_buf,buf);
-	    buffer_discard(buf);
-	}
-    }
-    dsc->vt_a = r;
-    dsc->vt_pa = ps;
-    dsc->vt_ms = mr;
-    i = length;
-    curr = list;
-    count = 0;
-    if (i > 1) {
-	tmp = *curr++;
-	seq1 = SSCOP_N(tmp);
-	i--;
-	if (NORM_TX(seq1) >= NORM_TX(dsc->vt_s)) {
-	    /* H */
-	    start_error_recov(dsc,'S');
-	    return;
-	}
-	/* I */
-	do {
-	    tmp = *curr++;
-	    seq2 = SSCOP_N(tmp);
-	    i--;
-	    if (NORM_TX(seq1) >= NORM_TX(seq2) ||
-	      NORM_TX(seq2) > NORM_TX(dsc->vt_s)) {
-		/* H */
-		start_error_recov(dsc,'S');
-		return;
-	    }
-	    do {
-		if (!(buf = queue_lookup(&dsc->tx_buf,seq1))) {
-		    /* H */
-		    start_error_recov(dsc,'S');
-		    return;
-		}
-		if (NORM_TX(buf->extra) < NORM_TX(ps) &&
-		  NORM_TX(ps) <= NORM_TX(dsc->vt_ps) &&
-		  !queue_lookup(&dsc->rt_q,seq1)) {
-		    buf2 = buffer_clone(buf);
-		    queue_put(&dsc->rt_q,buf2);
-		    count++;
-		}
-		INC24(seq1);
-	    }
-	    while (seq1 != seq2);
-	    /* J */
-	    if (!i) break;
-	    tmp = *curr++;
-	    seq2 = SSCOP_N(tmp);
-	    i--;
-	    if (NORM_TX(seq1) >= NORM_TX(seq2) ||
-	      NORM_TX(seq2) > NORM_TX(dsc->vt_s)) {
-		/* H */
-		start_error_recov(dsc,'S');
-		return;
-	    }
-	    do {
-		if (dsc->clear_buffers) {
-		    buf = queue_lookup(&dsc->tx_buf,seq1);
-		    if (buf) {
-			queue_remove(&dsc->tx_buf,buf);
-			buffer_discard(buf);
-		    }
-		}
-		INC24(seq1);
-	    }
-	    while (seq1 != seq2);
-	}
-	while (i);
-	maa_error(dsc,'V',count);
-    }
-    /* L */
-    if (dsc->credit != (NORM_TX(dsc->vt_s) < NORM_TX(dsc->vt_ms)))
-	maa_error(dsc,(dsc->credit = !dsc->credit) ? 'X' : 'W',0);
-    if (dsc->timer_poll) START_TIMER(noresp);
-    else if (!dsc->timer_idle) {
-	    STOP_TIMER(dsc->timer_keepalive);
-	    STOP_TIMER(dsc->timer_noresp);
-	    START_TIMER(idle);
-        }
-}
-
-
-/* --- Incoming PDUs ------------------------------------------------------- */
-
-
-/*
- * Returns 0 if the descriptor might conceivably be gone when returning,
- * 1 otherwise.
- */
- 
-static int handle_sscop_pdu(SSCOP_DSC *dsc,void *msg,int size)
-{
-    PDU_VARS;
-
-    dump_pdu(msg,size);
-    if (DECOMPOSE_PDU(dsc,msg,size)) return 1;
-    PRINT_PDU(dsc,"RECV",msg);
-    switch (type) {
-	case SSCOP_UD:
-	    if (dsc->ops->unitdata) dsc->ops->unitdata(dsc->user,msg,length);
-	    return 1;
-	case SSCOP_MD:
-	    if (dsc->ops->maa_data) dsc->ops->maa_data(dsc->user,msg,length);
-	    return 1;
-	default:
-	    break;
-    }
-    switch (dsc->state) {
-	case sscop_idle:
-	    switch (type) {
-		case SSCOP_BGREJ:
-		    bad_pdu(dsc,type);
-		    return 1;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			send_pdu(dsc,SSCOP_BGREJ,NULL,0); /* no SSCOP-UU */
-			return 1;
-		    }
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->estab_ind)
-			dsc->ops->estab_ind(dsc->user,msg,length);
-		    return 1;
-		case SSCOP_ENDAK:
-		    return 1;
-		case SSCOP_END:
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    return 1;
-		case SSCOP_ER:
-		case SSCOP_POLL:
-		case SSCOP_SD:
-		case SSCOP_BGAK:
-		case SSCOP_ERAK:
-		case SSCOP_STAT:
-		case SSCOP_USTAT:
-		case SSCOP_RS:
-		case SSCOP_RSAK:
-		    bad_pdu(dsc,type);
-		    send_pdu(dsc,SSCOP_END,NULL,1);
-		    return 1;
-		default:
-		    break;
-	    }
-	    break;
-	case sscop_outconn: /* 20.8-10 */
-	    switch (type) {
-		case SSCOP_ENDAK:
-		case SSCOP_SD:
-		case SSCOP_ERAK:
-		case SSCOP_END:
-		case SSCOP_STAT:
-		case SSCOP_USTAT:
-		case SSCOP_POLL:
-		case SSCOP_ER:
-		case SSCOP_RSAK:
-		case SSCOP_RS:
-		    return 1; /* ignore PDU */
-		case SSCOP_BGAK:
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    initialize_state_variables(dsc);
-		    set_data_transfer_timers(dsc);
-		    next_state(dsc,sscop_ready);
-		    if (dsc->ops->estab_conf)
-			dsc->ops->estab_conf(dsc->user,msg,length);
-		    return 1;
-		case SSCOP_BGREJ:
-		    STOP_TIMER(dsc->timer_cc);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,1);
-		    return 0;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) return 1;
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    initialize_vr_mr(dsc);
-		    send_pdu(dsc,SSCOP_BGAK,NULL,0);
-		    initialize_state_variables(dsc);
-		    set_data_transfer_timers(dsc);
-		    next_state(dsc,sscop_ready);
-		    if (dsc->ops->estab_conf)
-			dsc->ops->estab_conf(dsc->user,msg,length);
-		    return 1;
-		default:
-		    break;
-	    }
-	    break;
-	case sscop_inconn: /* 20.11-13 */
-	    switch (type) {
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) return 1;
-		    dsc->vt_ms = mr;
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,1);
-		    return 1;
-		case SSCOP_ER:
-		case SSCOP_BGAK:
-		case SSCOP_ERAK:
-		case SSCOP_RSAK:
-		case SSCOP_RS:
-		    bad_pdu(dsc,type);
-		    return 1;
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    bad_pdu(dsc,type);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,1);
-		    return 0;
-		case SSCOP_SD:
-		case SSCOP_USTAT:
-		case SSCOP_STAT:
-		case SSCOP_POLL:
-		    bad_pdu(dsc,type);
-		    send_pdu(dsc,SSCOP_END,NULL,1);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_END:
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,!s);
-		    return 0;
-		default:
-		    break;
-	    }
-	    break;
-	case sscop_outdisc: /* 20.14-16 */
-	    switch (type) {
-		case SSCOP_SD:
-		case SSCOP_BGAK:
-		case SSCOP_POLL:
-		case SSCOP_STAT:
-		case SSCOP_USTAT:
-		case SSCOP_ERAK:
-		case SSCOP_RS:
-		case SSCOP_RSAK:
-		case SSCOP_ER:
-		    return 1;
-		case SSCOP_END:
-		    STOP_TIMER(dsc->timer_cc);
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
-		    return 0;
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    STOP_TIMER(dsc->timer_cc);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_conf) dsc->ops->rel_conf(dsc->user);
-		    return 0;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			send_pdu(dsc,SSCOP_BGAK,NULL,0);
-			resend_pdu(dsc,dsc->last_end);
-			return 1;
-		    }
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,0);
-		    return 1;
-	    }
-	case sscop_outres: /* 20.17-19 */
-	    switch (type) {
-		case SSCOP_ER:
-		case SSCOP_POLL:
-		case SSCOP_STAT:
-		case SSCOP_USTAT:
-		case SSCOP_BGAK:
-		case SSCOP_ERAK:
-		case SSCOP_SD:
-		    return 1; /* ignore */
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			send_pdu(dsc,SSCOP_BGAK,NULL,0);
-			resend_pdu(dsc,dsc->last_rs);
-			return 1;
-		    }
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,1);
-		    return 1;
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    STOP_TIMER(dsc->timer_cc);
-		    bad_pdu(dsc,type);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_END:
-		    STOP_TIMER(dsc->timer_cc);
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,!s);
-		    return 0;
-		case SSCOP_RS:
-		    if (detect_retransmission(dsc,sq)) return 1;
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    initialize_vr_mr(dsc);
-		    send_pdu(dsc,SSCOP_RSAK,NULL,0);
-		    initialize_state_variables(dsc);
-		    set_data_transfer_timers(dsc);
-		    next_state(dsc,sscop_ready);
-		    if (dsc->ops->res_conf) dsc->ops->res_conf(dsc->user);
-		    return 1;
-		case SSCOP_RSAK:
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    initialize_state_variables(dsc);
-		    set_data_transfer_timers(dsc);
-		    next_state(dsc,sscop_ready);
-		    if (dsc->ops->res_conf) dsc->ops->res_conf(dsc->user);
-		    return 1;
-		default:
-		    break;
-	    }
-	    break;
-	case sscop_inres: /* 20.20-22 */
-	    switch (type) {
-		case SSCOP_SD:
-		case SSCOP_POLL:
-		case SSCOP_STAT:
-		case SSCOP_USTAT:
-		    send_pdu(dsc,SSCOP_END,NULL,1);
-		    /* fall through */
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    bad_pdu(dsc,type);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_END:
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,!s);
-		    return 0;
-		case SSCOP_ER:
-		case SSCOP_BGAK:
-		case SSCOP_ERAK:
-		case SSCOP_RSAK:
-		    bad_pdu(dsc,type);
-		    return 1;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,1);
-		    return 1;
-		case SSCOP_RS:
-		    if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
-		    return 1;
-		default:
-		    break;
-	    }
-	    break;
-	case sscop_outrec: /* 20.23-26 */
-	    switch (type) {
-		case SSCOP_BGAK:
-		case SSCOP_RSAK:
-		    bad_pdu(dsc,type);
-		    return 1;
-		case SSCOP_ERAK:
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_recresp);
-		    deliver_data(dsc);
-		    if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
-		    return 1;
-		case SSCOP_END:
-		    STOP_TIMER(dsc->timer_cc);
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    clear_receive_buffer(dsc);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,!s);
-		    return 0;
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    bad_pdu(dsc,type);
-		    STOP_TIMER(dsc->timer_cc);
-		    clear_receive_buffer(dsc);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_STAT:
-		case SSCOP_USTAT:
-		case SSCOP_POLL:
-		case SSCOP_SD:
-		    return 1;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    clear_receive_buffer(dsc);
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,1);
-		    return 1;
-		case SSCOP_ER:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    initialize_vr_mr(dsc);
-		    send_pdu(dsc,SSCOP_ERAK,NULL,0);
-		    deliver_data(dsc);
-		    next_state(dsc,sscop_recresp);
-		    if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
-		    return 1;
-		case SSCOP_RS:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    STOP_TIMER(dsc->timer_cc);
-		    dsc->vt_ms = mr;
-		    clear_receive_buffer(dsc);
-		    next_state(dsc,sscop_inres);
-		    if (dsc->ops->res_ind)
-			dsc->ops->res_ind(dsc->user,msg,length);
-		    return 0;
-		default:
-		    break;
-	    }
-	case sscop_recresp: /* 20.27-29 */
-	    switch (type) {
-		case SSCOP_BGAK:
-		case SSCOP_RSAK:
-		    bad_pdu(dsc,type);
-		    return 1;
-		case SSCOP_ERAK:
-		case SSCOP_SD:
-		case SSCOP_POLL:
-		    return 1;
-		case SSCOP_END:
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,!s);
-		    return 0;
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    bad_pdu(dsc,type);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_RS:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inres);
-		    if (dsc->ops->res_ind)
-			dsc->ops->res_ind(dsc->user,msg,length);
-		    return 1;
-		case SSCOP_ER:
-		    if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
-		    else send_pdu(dsc,SSCOP_ERAK,NULL,0);
-		    return 1;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,1);
-		    return 1;
-		case SSCOP_STAT:
-		case SSCOP_USTAT:
-		    bad_pdu(dsc,type);
-		    send_pdu(dsc,SSCOP_END,NULL,1);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		default:
-		    break;
-	    }
-	case sscop_inrec: /* 20.30-33 */
-	    switch (type) {
-		case SSCOP_END:
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,!s);
-		    return 0;
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    bad_pdu(dsc,type);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_USTAT:
-		case SSCOP_STAT:
-		case SSCOP_POLL:
-		case SSCOP_SD:
-		    bad_pdu(dsc,type);
-		    send_pdu(dsc,SSCOP_END,NULL,1);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_RSAK:
-		case SSCOP_BGAK:
-		case SSCOP_ERAK:
-		    bad_pdu(dsc,type);
-		    return 1;
-		case SSCOP_RS:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inres);
-		    if (dsc->ops->res_ind)
-			dsc->ops->res_ind(dsc->user,msg,length);
-		    return 1;
-		case SSCOP_ER:
-		    if (!detect_retransmission(dsc,sq)) bad_pdu(dsc,type);
-		    return 1;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			bad_pdu(dsc,type);
-			return 1;
-		    }
-		    dsc->vt_ms = mr;
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,1);
-		    return 1;
-		default:
-		    break;
-	    }
-	case sscop_ready: /* 20.34-46 */
-	    switch (type) {
-		case SSCOP_BGAK:
-		case SSCOP_ERAK:
-		case SSCOP_RSAK:
-		    return 1;
-		case SSCOP_ER:
-		    if (detect_retransmission(dsc,sq)) {
-			START_TIMER(noresp);
-			send_pdu(dsc,SSCOP_ERAK,NULL,0);
-			return 1;
-		    }
-		    reset_data_transfer_timers(dsc);
-		    dsc->vt_ms = mr;
-		    prepare_recovery(dsc);
-		    next_state(dsc,sscop_inrec);
-		    deliver_data(dsc);
-		    if (dsc->ops->rec_ind) dsc->ops->rec_ind(dsc->user);
-		    return 1;
-		case SSCOP_BGN:
-		    if (detect_retransmission(dsc,sq)) {
-			START_TIMER(noresp);
-			send_pdu(dsc,SSCOP_BGAK,NULL,0);
-			return 1;
-		    }
-		    reset_data_transfer_timers(dsc);
-		    dsc->vt_ms = mr;
-		    prepare_retrieval(dsc);
-		    next_state(dsc,sscop_inconn);
-		    if (dsc->ops->restart)
-			dsc->ops->restart(dsc->user,msg,length,1);
-		    return 1;
-		case SSCOP_ENDAK:
-		case SSCOP_BGREJ:
-		    reset_data_transfer_timers(dsc);
-		    bad_pdu(dsc,type);
-		    prepare_retrieval(dsc);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,NULL,0,0);
-		    return 0;
-		case SSCOP_RS:
-		    if (detect_retransmission(dsc,sq)) {
-			START_TIMER(noresp);
-			send_pdu(dsc,SSCOP_RSAK,NULL,0);
-			return 1;
-		    }
-		    reset_data_transfer_timers(dsc);
-		    dsc->vt_ms = mr;
-		    prepare_retrieval(dsc);
-		    next_state(dsc,sscop_inres);
-		    if (dsc->ops->res_ind)
-			dsc->ops->res_ind(dsc->user,msg,length);
-		    return 1;
-		case SSCOP_END:
-		    reset_data_transfer_timers(dsc);
-		    send_pdu(dsc,SSCOP_ENDAK,NULL,0);
-		    prepare_retrieval(dsc);
-		    next_state(dsc,sscop_idle);
-		    if (dsc->ops->rel_ind)
-			dsc->ops->rel_ind(dsc->user,msg,length,!s);
-		    return 0;
-		case SSCOP_SD:
-		    data_sd(dsc,s,msg,length);
-		    return 1;
-		case SSCOP_POLL:
-		    dsc->vr_ps = ps; /* store */
-		    data_poll(dsc,s);
-		    return 1;
-		case SSCOP_USTAT:
-		    data_ustat(dsc,mr,r,SSCOP_N(((unsigned long *) msg)[0]),
-		      SSCOP_N(((unsigned long *) msg)[1]));
-		    return 1;
-		case SSCOP_STAT:
-		    data_stat(dsc,ps,mr,r,msg,length/4);
-		    return 1;
-		default:
-		    break;
-	    }
-    }
-    return 1;
-}
-
-
-void sscop_pdu(SSCOP_DSC *dsc,void *msg,int size)
-{
-    if (handle_sscop_pdu(dsc,msg,size))
-	if (dsc->state == sscop_ready) try_to_send(dsc);
-}
-
-
-/* --- From SSCOP user ----------------------------------------------------- */
-
-
-void sscop_retrieve(SSCOP_DSC *dsc,int rn)
-{
-    BUFFER *buf;
-    int i;
-
-    if (dsc->state != sscop_idle && dsc->state != sscop_inconn &&
-      dsc->state != sscop_outdisc && dsc->state != sscop_inres &&
-      dsc->state != sscop_recresp && dsc->state != sscop_inrec)
-	diag(COMPONENT,DIAG_FATAL,"sscop_retrieve invoked in state %s",
-	  state_name[dsc->state]);
-    if (rn != SSCOP_RN_UNKNOWN)
-	for (i = rn == SSCOP_RN_TOTAL ? dsc->vt_a : MOD24(rn+1);
-	  NORM_TX(dsc->vt_a) <= NORM_TX(i) && NORM_TX(i) < NORM_TX(dsc->vt_s);
-	  INC24(i)) {
-	    buf = queue_lookup(&dsc->tx_buf,i);
-	    if (buf) {
-		queue_remove(&dsc->tx_buf,buf);
-		if (dsc->ops->retr_ind)
-		    dsc->ops->retr_ind(dsc->user,buf->data,buf->length);
-		buffer_discard(buf);
-	    }
-	}
-    while ((buf = queue_get(&dsc->tx_q))) {
-	if (dsc->ops->retr_ind)
-	    dsc->ops->retr_ind(dsc->user,buf->data,buf->length);
-	buffer_discard(buf);
-    }
-    if (dsc->ops->retr_comp) dsc->ops->retr_comp(dsc->user);
-}
-
-
-void sscop_send(SSCOP_DSC *dsc,void *buffer,int size) /* 20.38 */
-{
-    BUFFER *buf;
-
-    if (dsc->state != sscop_ready && (dsc->state != sscop_outrec ||
-      dsc->clear_buffers)) return; /* 20.23 */
-    buf = buffer_create(size,0);
-    memcpy(buf->data,buffer,size);
-    queue_put(&dsc->tx_q,buf);
-    try_to_send(dsc);
-}
-
-
-void sscop_estab_req(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel)
-{
-    switch (dsc->state) {
-	case sscop_outdisc: /* 20.14 */
-	    STOP_TIMER(dsc->timer_cc);
-	    /* fall through */
-	case sscop_idle: /* 20.5 */
-	    clear_transmitter(dsc);
-	    dsc->clear_buffers = buf_rel;
-	    dsc->vt_cc = 1;
-	    INC8(dsc->vt_sq);
-	    initialize_vr_mr(dsc);
-	    send_pdu(dsc,SSCOP_BGN,uu_data,uu_length);
-	    START_TIMER(cc);
-	    next_state(dsc,sscop_outconn);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_FATAL,"sscop_estab_req invoked in state %s",
-      state_name[dsc->state]);
-}
-
-
-void sscop_estab_resp(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel)
-    /* 20.11 */
-{
-    if (dsc->state != sscop_inconn)
-	diag(COMPONENT,DIAG_FATAL,"sscop_estab_resp invoked in state %s",
-	  state_name[dsc->state]);
-    clear_transmitter(dsc);
-    dsc->clear_buffers = buf_rel;
-    initialize_vr_mr(dsc);
-    send_pdu(dsc,SSCOP_BGAK,uu_data,uu_length);
-    initialize_state_variables(dsc);
-    set_data_transfer_timers(dsc);
-    next_state(dsc,sscop_ready);
-    try_to_send(dsc); /* probably not ... */
-}
-
-
-void sscop_rel_req(SSCOP_DSC *dsc,void *uu_data,int uu_length)
-{
-    switch (dsc->state) {
-	case sscop_outrec: /* 20.24 */
-	    clear_receive_buffer(dsc);
-	    /* fall through */
-	case sscop_outconn: /* 20.9 */
-	case sscop_outres: /* 20.18 */
-	    STOP_TIMER(dsc->timer_cc);
-	    /* fall through */
-	case sscop_inres: /* 20.20 */
-	case sscop_recresp: /* 20.28 */
-	case sscop_inrec: /* 20.30 */
-	    dsc->vt_cc = 1;
-	    send_pdu(dsc,SSCOP_END,uu_data,uu_length);
-	    START_TIMER(cc);
-	    next_state(dsc,sscop_outdisc);
-	    return;
-	case sscop_inconn: /* 20.11 */
-	    send_pdu(dsc,SSCOP_BGREJ,uu_data,uu_length);
-	    next_state(dsc,sscop_idle);
-	    return;
-	case sscop_ready: /* 20.34 */
-	    reset_data_transfer_timers(dsc);
-	    dsc->vt_cc = 1;
-	    send_pdu(dsc,SSCOP_END,uu_data,uu_length);
-	    prepare_retrieval(dsc);
-	    START_TIMER(cc);
-	    next_state(dsc,sscop_outdisc);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_FATAL,"sscop_rel_req invoked in state %s",
-      state_name[dsc->state]);
-}
-
-
-void sscop_res_req(SSCOP_DSC *dsc,void *uu_data,int uu_length)
-{
-    switch (dsc->state) {
-	case sscop_outrec: /* 20.25 */
-	    STOP_TIMER(dsc->timer_cc);
-	    clear_receive_buffer(dsc);
-	    /* fall through */
-	case sscop_recresp: /* 20.29 */
-	    dsc->vt_cc = 1;
-	    INC8(dsc->vt_sq);
-	    initialize_vr_mr(dsc);
-	    send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
-	    clear_transmitter(dsc);
-	    START_TIMER(cc);
-	    next_state(dsc,sscop_outres);
-	    return;
-	case sscop_inrec: /* 20.30 */
-	    clear_transmitter(dsc);
-	    dsc->vt_cc = 1;
-	    INC8(dsc->vt_sq);
-	    initialize_vr_mr(dsc);
-	    send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
-	    START_TIMER(cc);
-	    next_state(dsc,sscop_outres);
-	    return;
-	case sscop_ready: /* 20.34 */
-	    reset_data_transfer_timers(dsc);
-	    dsc->vt_cc = 1;
-	    INC8(dsc->vt_sq);
-	    initialize_vr_mr(dsc);
-	    send_pdu(dsc,SSCOP_RS,uu_data,uu_length);
-	    release_buffers(dsc);
-	    START_TIMER(cc);
-	    next_state(dsc,sscop_outres);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_FATAL,"sscop_res_req invoked in state %s",
-      state_name[dsc->state]);
-}
-
-
-void sscop_res_resp(SSCOP_DSC *dsc) /* 20.20 */
-{
-    if (dsc->state != sscop_inres)
-	diag(COMPONENT,DIAG_FATAL,"sscop_res_resp invoked in state %s",
-	  state_name[dsc->state]);
-    initialize_vr_mr(dsc);
-    send_pdu(dsc,SSCOP_RSAK,NULL,0);
-    clear_transmitter(dsc);
-    initialize_state_variables(dsc);
-    set_data_transfer_timers(dsc);
-    next_state(dsc,sscop_ready);
-    try_to_send(dsc); /* probably not ... */
-}
-
-
-void sscop_rec_resp(SSCOP_DSC *dsc)
-{
-    switch (dsc->state) {
-	case sscop_inrec: /* 20.30 */
-	    initialize_vr_mr(dsc);
-	    send_pdu(dsc,SSCOP_ERAK,NULL,0);
-	    /* fall through */
-	case sscop_recresp: /* 20.28 */
-	    if (!dsc->clear_buffers) clear_transmission_buffer(dsc);
-	    initialize_state_variables(dsc);
-	    set_data_transfer_timers(dsc);
-	    next_state(dsc,sscop_ready);
-	    try_to_send(dsc);
-	    return;
-	default:
-	    break;
-    }
-    diag(COMPONENT,DIAG_FATAL,"sscop_rec_resp invoked in state %s",
-      state_name[dsc->state]);
-}
-
-
-void sscop_unitdata(SSCOP_DSC *dsc,void *buffer,int size) /* 20.47-48 */
-{
-    send_pdu(dsc,SSCOP_UD,buffer,size);
-}
-
-
-void sscop_maa_data(SSCOP_DSC *dsc,void *buffer,int size) /* 20.47-48 */
-{
-    send_pdu(dsc,SSCOP_MD,buffer,size);
-}
-
-
-void start_sscop(SSCOP_DSC *dsc,SSCOP_USER_OPS *ops,void *user_data)
-{
-    dsc->ops = ops;
-    dsc->user = user_data;
-    dsc->vt_sq = dsc->vr_sq = 0; /* 20.5 */
-    dsc->clear_buffers = 1;
-    dsc->state = sscop_idle;
-    dsc->timer_cc = dsc->timer_poll = dsc->timer_noresp =
-      dsc->timer_keepalive = dsc->timer_idle = NULL;
-    queue_init(&dsc->rx_buf);
-    queue_init(&dsc->tx_q);
-    queue_init(&dsc->tx_buf);
-    queue_init(&dsc->rt_q);
-    dsc->last_bgn = dsc->last_end = dsc->last_rs = dsc->last_er = NULL;
-    dsc->cf_max_cc = SSCOP_CF_MaxCC;
-    dsc->cf_max_pd = SSCOP_CF_MaxPD;
-    dsc->cf_max_stat = SSCOP_CF_MaxSTAT;
-    dsc->cf_timer_cc = TIMER_CC;
-    dsc->cf_timer_poll = TIMER_POLL;
-    dsc->cf_timer_noresp = TIMER_NORESP;
-    dsc->cf_timer_keepalive = TIMER_KEEPALIVE;
-    dsc->cf_timer_idle = TIMER_IDLE;
-    dsc->list = NULL;
-}
-
-
-void stop_sscop(SSCOP_DSC *dsc)
-{
-    if (dsc->state != sscop_idle)
-	diag(COMPONENT,DIAG_WARN,"stopping dsc in state %s",
-	  state_name[dsc->state]);
-    dsc->state = sscop_idle; /* avoid send attempts */
-    STOP_TIMER(dsc->timer_cc);
-    STOP_TIMER(dsc->timer_poll);
-    STOP_TIMER(dsc->timer_noresp);
-    STOP_TIMER(dsc->timer_keepalive);
-    STOP_TIMER(dsc->timer_idle);
-    queue_clear(&dsc->rx_buf);
-    queue_clear(&dsc->tx_q);
-    queue_clear(&dsc->tx_buf);
-    queue_clear(&dsc->rt_q);
-    if (dsc->last_bgn) buffer_discard(dsc->last_bgn);
-    if (dsc->last_end) buffer_discard(dsc->last_end);
-    if (dsc->last_rs) buffer_discard(dsc->last_rs);
-    if (dsc->last_er) buffer_discard(dsc->last_er);
-    if (dsc->list) free(dsc->list);
-}
diff -ur --new-file old/atm/sigd/sscop.h new/atm/sigd/sscop.h
--- old/atm/sigd/sscop.h	Tue Oct 24 17:55:57 1995
+++ new/atm/sigd/sscop.h	Thu Jan  1 01:00:00 1970
@@ -1,132 +0,0 @@
-/* sscop.h - SSCOP (Q.2110) user interface */
- 
-/* Written 1995 by Werner Almesberger, EPFL-LRC */
- 
-
-#ifndef SSCOP_H
-#define SSCOP_H
-
-#include "atmd.h"
-
-
-typedef enum { sscop_idle,sscop_outconn,sscop_inconn,sscop_outdisc,
-  sscop_outres,sscop_inres,sscop_outrec,sscop_recresp,sscop_inrec,
-  sscop_ready } SSCOP_STATE;
-
-typedef struct {
-    SSCOP_STATE state;
-    struct _sscop_user_ops *ops;
-    void *user;
-    /* --- Configurable parameters ----------------------------------------- */
-    int cf_max_cc,cf_max_pd,cf_max_stat;
-    int cf_timer_cc,cf_timer_poll,cf_timer_noresp,cf_timer_keepalive,
-      cf_timer_idle;
-    /* --- SSCOP information, TX part -------------------------------------- */
-    int vt_s; /* send sequence number */
-    int vt_ps; /* poll send sequence number */
-    int vt_a; /* acknowledge sequence number */
-    int vt_pa; /* poll sequence number */
-    int vt_ms; /* maximum send sequence */
-    int vt_pd; /* SD PDUs between POLL PDUs */
-    int vt_cc; /* number of unacknowledged BSG, END, ER or RS PDUs */
-    int vt_sq; /* connection number */
-    /* --- SSCOP information, RX part -------------------------------------- */
-    int vr_r; /* receiver sequence number */
-    int vr_h; /* highest expected SD PDU */
-    int vr_mr; /* maximum acceptable (receiver) */
-    int vr_sq; /* connection number */
-    int vr_ps; /* non-Q.2110: keeps N(PS) received in last POLL for STAT */
-    /* Other variables */
-    int clear_buffers;
-    int credit;
-    /* Timers */
-    TIMER *timer_cc,*timer_poll,*timer_noresp,*timer_keepalive,*timer_idle;
-    /* Queues and buffers */
-    QUEUE tx_buf,tx_q,rx_buf,rt_q;
-    BUFFER *last_bgn,*last_end,*last_rs,*last_er; /* for retransmission */
-    /* Misc items */
-    int *list; /* STAT construction list */
-} SSCOP_DSC;
-
-
-/*
- * Note: UU data of primitives carrying such is only available if
- *  - the "user" flag is set (if available) and
- *  - uu_data is non-NULL, and
- *  - uu_length is non-zero
- * in all other cases, uu_data must not be dereferenced.
- *
- * Note: the "ind" parameter in restart indicates whether the release primitive
- * is an AA-RELEASE.indication (ind = 1) or to an AA-RELEASE.confirm (ind = 0).
- */
-
-
-typedef struct _sscop_user_ops {
-    void (*estab_ind)(void *user_data,void *uu_data,int uu_length);
-	/* AA-ESTABLISH.indication */
-    void (*estab_conf)(void *user_data,void *uu_data,int uu_length);
-	/* AA-ESTABLISH.confirm */
-    void (*rel_ind)(void *user_data,void *uu_data,int uu_length,int user);
-	/* AA-RELEASE.indication */
-    void (*rel_conf)(void *user_data); /* AA-RELEASE.confirm */
-    void (*restart)(void *user_data,void *uu_data,int uu_length,int ind);
-	/* AA-RELEASE.indication or AA-RELEASE.confirm immediately followed by
-	   AA-ESTABLISH.indication */
-    void (*res_ind)(void *user_data,void *uu_data,int uu_length);
-	/* AA-RESYNC.indication */
-    void (*res_conf)(void *user_data); /* AA-RESYNC.confirm */
-    void (*rec_ind)(void *user_data); /* AA-RECOVER.indication */
-    void (*data_ind)(void *user_data,void *data,int length,int sn);
-	/* AA-DATA.indication */
-    void (*unitdata)(void *user_data,void *data,int length);
-	/* AA-UNITDATA.indication */
-    void (*retr_ind)(void *user_data,void *data,int length);
-	/* AA-RETRIEVE.indication */
-    void (*retr_comp)(void *user_data); /* AA-RETRIEVE_COMPLETE.indication */
-    void (*maa_data)(void *user_data,void *data,int length);
-	/* MAA-UNITDATA.indication */
-    int (*maa_error)(void *user_data,char code,int count);
-	/* MAA-ERROR.indication */
-    void (*cpcs_send)(void *user_data,void *data,int length);
-} SSCOP_USER_OPS;
-
-
-/* Attach/detach protocol */
-
-void start_sscop(SSCOP_DSC *dsc,SSCOP_USER_OPS *ops,void *user_data);
-void stop_sscop(SSCOP_DSC *dsc);
-
-/* Connection control */
-
-void sscop_estab_req(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel);
-    /* AA-ESTABLISH.request */
-void sscop_estab_resp(SSCOP_DSC *dsc,void *uu_data,int uu_length,int buf_rel);
-    /* AA-ESTABLISH.response */
-void sscop_rel_req(SSCOP_DSC *dsc,void *uu_data,int uu_length);
-    /* AA-RELEASE.request */
-void sscop_res_req(SSCOP_DSC *dsc,void *uu_data,int uu_length);
-    /* AA-RESYNC.request */
-void sscop_res_resp(SSCOP_DSC *dsc); /* AA-RESYNC.response */
-void sscop_rec_resp(SSCOP_DSC *dsc); /* AA-RECOVER.response */
-
-/* Incoming PDU from lower layer */
-
-void sscop_pdu(SSCOP_DSC *dsc,void *msg,int size); /* CPCS-UNITDATA.request */
-
-/* Send data */
-
-void sscop_send(SSCOP_DSC *dsc,void *buffer,int size);
-    /* AA-DATA.request, 20.38 */
-void sscop_unitdata(SSCOP_DSC *dsc,void *buffer,int size);
-    /* AA-UNIDATA.request, 20.47 */
-void sscop_maa_data(SSCOP_DSC *dsc,void *buffer,int size);
-    /* MAA-UNITDATA.request, 20.47 */
-
-/* Retrieve unsent data */
-
-#define SSCOP_RN_UNKNOWN -2 /* -1 is (theoretically) a valid rn ... */
-#define SSCOP_RN_TOTAL   -3
-
-void sscop_retrieve(SSCOP_DSC *dsc,int rn); /* AA-RETRIEVE.request */
-
-#endif
diff -ur --new-file old/atm/sigd/svc.c new/atm/sigd/svc.c
--- old/atm/sigd/svc.c	Fri Mar 15 14:17:52 1996
+++ new/atm/sigd/svc.c	Thu Jan  1 01:00:00 1970
@@ -1,97 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/atm.h>
-
-#include "atm.h"
-
-
-static void usage(const char *name)
-{
-    fprintf(stderr,"usage: %s [ <atm_addr> ]\n",name);
-    exit(1);
-}
-
-
-int main(int argc,char **argv)
-{
-    struct sockaddr_atmsvc addr;
-    struct atm_blli blli;
-    char buffer[MAX_ATM_ADDR_LEN+1];
-    char *here;
-    int s,passive,len;
-int l2_proto;
-    if ((s = socket(PF_ATMSVC,SOCK_DGRAM,ATM_AAL5)) < 0) {
-	perror("socket");
-	return 1;
-    }
-if (argc == 1) l2_proto = 1;
-else {
-l2_proto = atoi(argv[1]);
-argc--;
-argv++;
-}
-    memset(&addr,0,sizeof(addr));
-    memset(&blli,0,sizeof(blli));
-    addr.sas_family = AF_ATMSVC;
-    addr.sas_family = AF_ATMSVC;
-    addr.sas_addr.blli = &blli;
-    addr.sas_txtp.class = addr.sas_rxtp.class = ATM_UBR;
-    blli.l2_proto = l2_proto;
-    blli.next = NULL;
-    if (bind(s,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
-	perror("bind");
-	return 1;
-    }
-    if (!(passive = argc == 1)) {
-	if (argc != 2) usage(argv[0]);
-	if (text2atm(argv[1],(struct sockaddr *) &addr,sizeof(addr),T2A_NAME)
-	  < 0) {
-	    fprintf(stderr,"%s: invalid ATM address\n",argv[1]);
-	    return 1;
-	}
-    }
-    addr.sas_addr.blli = &blli;
-    addr.sas_txtp.class = addr.sas_rxtp.class = ATM_UBR;
-    here = strrchr(argv[0],'/');
-    if (!passive) {
-	if (connect(s,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
-	    perror("connect");
-	    return 1;
-	}
-    }
-    else {
-	if (listen(s,0) < 0) {
-	    perror("listen");
-	    return 1;
-	}
-	if ((s = accept(s,NULL,0)) < 0) { /* loses listening socket dsc */
-	    perror("accept");
-	    return 1;
-	}
-    }
-    len = sizeof(addr);
-    if (getsockname(s,(struct sockaddr *) &addr,&len) < 0)
-	perror("getsockname");
-    else {
-	if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &addr,
-	  A2T_NAME | A2T_PRETTY) < 0) strcpy(buffer,"<invalid>");
-	printf("Local: %s\n",buffer);
-    }
-    fflush(stdout);
-    len = sizeof(addr);
-    if (getpeername(s,(struct sockaddr *) &addr,&len) < 0)
-	perror("getpeername");
-    else {
-	if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &addr,
-	  A2T_NAME | A2T_PRETTY) < 0) strcpy(buffer,"<invalid>");
-	printf("Remote: %s\n",buffer);
-    }
-    printf("Press [Enter] to continue ");
-    fflush(stdout);
-    (void) getchar();
-    return 0;
-}
diff -ur --new-file old/atm/test/ttcp.c new/atm/test/ttcp.c
--- old/atm/test/ttcp.c	Thu Jun  6 18:47:44 1996
+++ new/atm/test/ttcp.c	Wed Jul 31 18:47:25 1996
@@ -178,6 +178,7 @@
 {
     struct timeval td;
     unsigned long addr_tmp;
+    const char *port_name;
     int c;
     double mbps;
 int no_check = 0;
@@ -227,7 +228,7 @@
 	    sinkmode = !sinkmode;
 	    break;
 	  case 'p':
-	    port = atoi(optarg);
+	    port_name = optarg;
 	    break;
 	  case 'P':
 	    pcr = atoi(optarg);
@@ -263,7 +264,22 @@
 	    goto usage;
 	}
     }
-    
+
+    if (port_name)
+	if (atm) goto usage;
+	else {
+	    struct servent *se;
+
+	    se = getservbyname(port_name,udp ? "udp" : "tcp");
+	    if (se) port = ntohs(se->s_port);
+	    else {
+		const char *end;
+
+		port = strtoul(port_name,&end,0);
+		if (*end) goto usage;
+	    }
+	}     
+
     host = argv[optind];
     if (atm && arequipa) {
 	fprintf(stderr,"-Q must not be used with -a\n");
@@ -369,19 +385,27 @@
     /* set socket buffer size */
 #if defined(SO_SNDBUF) || defined(SO_RCVBUF)
     if (sockbufsize) {
+int l;
 	if (trans) {
 	    /* set send socket buffer if we are transmitting */    
 	    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
 			   sizeof sockbufsize) < 0)
 		err("setsockopt: sndbuf");
+l = sizeof sockbufsize;
+if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, &l) < 0)
+perror("grr");
 	    mes("sndbuf");
 	} else {
 	    /* set receive socket buffer if we are receiving */    
 	    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &sockbufsize,
 			   sizeof sockbufsize) < 0)
 		err("setsockopt: rcvbuf");
+l = sizeof sockbufsize;
+if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &sockbufsize, &l) < 0)
+perror("grr");
 	    mes("rcvbuf");
 	}
+printf("sbs=%d\n",sockbufsize);
     }
 #endif
 
@@ -421,6 +445,7 @@
 		    err("setsockopt: nodelay");
 		mes("nodelay");
 	    }
+	    if (atm) sleep(1); /* grr ... */
 #endif
 	} else if (!arequipa || !udp) {
 	    /* otherwise, we are the server and