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