diff -ur --new-file old/atm/CHANGES new/atm/CHANGES --- old/atm/CHANGES Thu Apr 22 19:56:37 1999 +++ new/atm/CHANGES Tue May 18 01:30:50 1999 @@ -1,3 +1,35 @@ +Version 0.56 to 0.57 (18-MAY-1999) +==================== + +Bug fixes +--------- + + - atmarpd complained "invalid control msg type" when changing interface + characteristics (reported by Soo-Khim Ho) + - sch_atm didn't compile without CLIP (reported by Zhu Qun Ying) + - LANE & MPOA: plugged a few memory leaks after failure to atm_charge (Heikki + Vatiainen) + - mpcd could not be killed if MPS's address was not known (fixed by Heikki + Vatiainen) + - nicstar: fixed the sleeping in interrupt issue (Rui Prior) + +New features +------------ + + - drivers for the Madge "Ambassador" and "Horizon [Ultra]" NICs, also known + as Collage PCI 155 Server, 25, and 155 Client (by Giuliano Procida) + +Other changes +------------- + + - MPOA code cleanup and debugging printks are now conditional (Heikki + Vatiainen) + - removed both led/USAGE files + - removed the old atmsigd (was in atm/sig.old/) + - nicstar driver now works around TSQ bug on older chips (77201) (by Rui + Prior, with the detective work by Jay Talbott) + + Version 0.55 to 0.56 (22-APR-1999) ==================== diff -ur --new-file old/atm/Makefile new/atm/Makefile --- old/atm/Makefile Tue Feb 9 17:19:03 1999 +++ new/atm/Makefile Tue May 18 01:19:36 1999 @@ -3,7 +3,7 @@ # "maint" must appear after "qgen" DIRS=lib test debug qgen saal sigd maint arpd ilmid aqd man led led.new lane \ - mpoad switch sigd.old # extra + mpoad switch # extra all: for n in $(DIRS); do $(MAKE) -C $$n || exit; done diff -ur --new-file old/atm/README new/atm/README --- old/atm/README Thu Apr 22 17:38:31 1999 +++ new/atm/README Tue May 18 01:19:53 1999 @@ -1,4 +1,4 @@ -ATM on Linux, release 0.56 (alpha) by Werner Almesberger, EPFL ICA +ATM on Linux, release 0.57 (alpha) by Werner Almesberger, EPFL ICA ============================================== Werner.Almesberger@epfl.ch This is experimental software. There are known major bugs and certainly @@ -11,7 +11,7 @@ The kernel patch is relative to the "standard" 2.2.1 kernel. -Please see http://ica1www.epfl.ch/linux-atm/info.html for a list of +Please see http://icawww1.epfl.ch/linux-atm/info.html for a list of features supported by ATM on Linux. For usage and installation instructions, please read the file USAGE or diff -ur --new-file old/atm/USAGE new/atm/USAGE --- old/atm/USAGE Thu Apr 22 20:02:00 1999 +++ new/atm/USAGE Tue May 18 01:30:05 1999 @@ -1,10 +1,15 @@ -Usage instructions - ATM on Linux, release 0.56 -------------------------------------------------- +7 + +%:Usage instructions - ATM on Linux, release 0.57 +%:------------------------------------------------- +%: +%: For updates of ATM on Linux, please check the Web page at http://ica1www.epfl.ch/linux-atm/ - WARNING +%: WARNING +%: This is experimental software. There are known major bugs and certainly even many more yet unknown problems. Internal and external interfaces are @@ -17,7 +22,7 @@ In order to install this package, you need - the package itself - ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.56.tar.gz + ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.57.tar.gz - the Linux kernel, version 2.2.1, e.g. from ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.2.1.tar.gz - Perl, version 4 or 5 @@ -33,7 +38,7 @@ all the files listed above there. Then extract the ATM on Linux distribution: -tar xfz atm-0.56.tar.gz +tar xfz atm-0.57.tar.gz and the kernel source: @@ -49,7 +54,6 @@ atm/ Documentation in ASCII format, kernel patch, top-level Makefile, and distribution scripts atm/sigd/ UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: atmsigd - atm/sigd.old/ Older version: atmsigd.old atm/saal/ Signaling AAL library (SSCOP, SSCF, and SAAL) atm/qgen/ Q.2931-style message handling atm/ilmid/ ILMI address registration demon: ilmid diff -ur --new-file old/atm/VERSION new/atm/VERSION --- old/atm/VERSION Thu Apr 22 17:38:19 1999 +++ new/atm/VERSION Tue May 18 00:39:42 1999 @@ -1 +1 @@ -0.56 +0.57 diff -ur --new-file old/atm/arpd/io.c new/atm/arpd/io.c --- old/atm/arpd/io.c Tue Jul 28 15:52:17 1998 +++ new/atm/arpd/io.c Fri Apr 23 12:11:42 1999 @@ -1,6 +1,6 @@ /* io.c - I/O operations */ -/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ +/* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */ #include @@ -104,6 +104,7 @@ break; case act_change: itf_change(ctrl.itf_num); + break; default: diag(COMPONENT,DIAG_ERROR,"invalid control msg type 0x%x", ctrl.type); diff -ur --new-file old/atm/arpd/itf.c new/atm/arpd/itf.c --- old/atm/arpd/itf.c Sat Apr 11 12:13:44 1998 +++ new/atm/arpd/itf.c Fri Apr 23 12:11:36 1999 @@ -1,6 +1,6 @@ /* itf.c - IP interface registry */ -/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ +/* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */ #include @@ -94,7 +94,7 @@ diag(COMPONENT,DIAG_DEBUG,"ITF DOWN %d",number); itf = lookup_itf(number); if (!itf) { - diag(COMPONENT,DIAG_ERROR,"no such interface"); + diag(COMPONENT,DIAG_ERROR,"no such interface (%d)",number); return; } itf_bring_down(itf); @@ -111,7 +111,7 @@ diag(COMPONENT,DIAG_DEBUG,"ITF CHANGE %d",number); itf = lookup_itf(number); if (!itf) { - diag(COMPONENT,DIAG_ERROR,"no such interface"); + diag(COMPONENT,DIAG_ERROR,"no such interface (%d)",number); return; } if (ip_itf_info(number,&local_ip,&netmask,&mtu) < 0) { diff -ur --new-file old/atm/atm.patch new/atm/atm.patch --- old/atm/atm.patch Thu Apr 22 20:01:48 1999 +++ new/atm/atm.patch Tue May 18 01:29:48 1999 @@ -20,8 +20,8 @@ if [ -f FS_MODULES ]; then inst_mod FS_MODULES fs; fi; \ if [ -f NLS_MODULES ]; then inst_mod NLS_MODULES fs; fi; \ --- ref/Documentation/Configure.help Wed Jan 20 20:05:32 1999 -+++ work/Documentation/Configure.help Tue Mar 30 21:19:57 1999 -@@ -3460,6 +3460,179 @@ ++++ work/Documentation/Configure.help Tue May 18 01:13:20 1999 +@@ -3460,6 +3460,217 @@ This is a backward compatibility option, choose Y for now. This option will be removed soon. @@ -198,16 +198,54 @@ + 25 and for 155 Mbps, including IDT cards and the Fore ForeRunnerLE + series. + ++Madge Ambassador (Collage PCI 155 Server) ++CONFIG_ATM_AMBASSADOR ++ This is a driver for ATMizer based ATM card produced by Madge ++ Networks Ltd. Say Y (or M to compile as a module named ambassador.o) ++ here if you have one of these cards. ++ ++Enable debugging messages ++CONFIG_ATM_AMBASSADOR_DEBUG ++ Somewhat useful debugging messages are available. The choice of ++ messages is controlled by a bitmap. This may be specified as a ++ module argument (kernel command line argument as well?), changed ++ dynamically using an ioctl (not yet) or changed by sending the ++ string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file ++ drivers/atm/ambassador.h for the meanings of the bits in the mask. ++ ++ When active, these messages can have a significant impact on the ++ speed of the driver, and the size of your syslog files! When ++ inactive, they will have only a modest impact on performance. ++ ++Madge Horizon [Ultra] (Collage PCI 25 and Collage PCI 155 Client) ++CONFIG_ATM_HORIZON ++ This is a driver for the Horizon chipset ATM adapter cards once ++ produced by Madge Networks Ltd. Say Y (or M to compile as a module ++ named horizon.o) here if you have one of these cards. ++ ++Enable debugging messages ++CONFIG_ATM_HORIZON_DEBUG ++ Somewhat useful debugging messages are available. The choice of ++ messages is controlled by a bitmap. This may be specified as a ++ module argument (kernel command line argument as well?), changed ++ dynamically using an ioctl (not yet) or changed by sending the ++ string "Dxxxx" to VCI 1023 (where x is a hex digit). See the file ++ drivers/atm/horizon.h for the meanings of the bits in the mask. ++ ++ When active, these messages can have a significant impact on the ++ speed of the driver, and the size of your syslog files! When ++ inactive, they will have only a modest impact on performance. ++ SCSI support? CONFIG_SCSI If you want to use a SCSI hard disk, SCSI tape drive, SCSI CDROM or --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/Documentation/atm.txt Tue Feb 9 15:49:57 1999 ++++ work/Documentation/atm.txt Mon May 3 02:18:18 1999 @@ -0,0 +1,4 @@ +In order to use anything but the most primitive functions of ATM, +several user-mode programs are required to assist the kernel. These +programs and related material can be found via the ATM on Linux Web -+page at http://lrcwww.epfl.ch/linux-atm/ ++page at http://icawww1.epfl.ch/linux-atm/ --- ref/arch/i386/config.in Wed Jan 20 19:18:53 1999 +++ work/arch/i386/config.in Tue Feb 9 15:49:57 1999 @@ -139,6 +139,9 @@ @@ -256,8 +294,8 @@ ifeq ($(CONFIG_AP1000),y) --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/Config.in Tue Feb 9 15:49:57 1999 -@@ -0,0 +1,34 @@ ++++ work/drivers/atm/Config.in Tue May 18 01:13:20 1999 +@@ -0,0 +1,42 @@ +# +# ATM device configuration +# @@ -290,11 +328,19 @@ +# if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then +# bool ' Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n +# fi -+ tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR y ++ tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR ++ tristate 'Madge Ambassador (Collage PCI 155 Server)' CONFIG_ATM_AMBASSADOR ++ if [ "$CONFIG_ATM_AMBASSADOR" != "n" ]; then ++ bool ' Enable debugging messages' CONFIG_ATM_AMBASSADOR_DEBUG ++ fi ++ tristate 'Madge Horizon [Ultra] (Collage PCI 25 and Collage PCI 155 Client)' CONFIG_ATM_HORIZON y ++ if [ "$CONFIG_ATM_HORIZON" != "n" ]; then ++ bool ' Enable debugging messages' CONFIG_ATM_HORIZON_DEBUG ++ fi +fi --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/Makefile Tue Feb 9 15:49:57 1999 -@@ -0,0 +1,57 @@ ++++ work/drivers/atm/Makefile Tue May 18 01:13:20 1999 +@@ -0,0 +1,73 @@ +# File: drivers/atm/Makefile +# +# Makefile for the Linux network (ATM) device drivers. @@ -341,6 +387,22 @@ + endif +endif + ++ifeq ($(CONFIG_ATM_HORIZON),y) ++L_OBJS += horizon.o ++else ++ ifeq ($(CONFIG_ATM_HORIZON),m) ++ M_OBJS += horizon.o ++ endif ++endif ++ ++ifeq ($(CONFIG_ATM_AMBASSADOR),y) ++L_OBJS += ambassador.o ++else ++ ifeq ($(CONFIG_ATM_AMBASSADOR),m) ++ M_OBJS += ambassador.o ++ endif ++endif ++ +ifeq ($(CONFIG_ATM_TCP),y) +L_OBJS += atmtcp.o +else @@ -353,2778 +415,11786 @@ + +include $(TOPDIR)/Rules.make --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/atmdev_init.c Tue Feb 9 15:49:57 1999 -@@ -0,0 +1,48 @@ -+/* drivers/atm/atmdev_init.c - ATM device driver initialization */ -+ -+/* Written 1995-1997 by Werner Almesberger, EPFL LRC */ -+ ++++ work/drivers/atm/ambassador.c Fri May 7 15:42:50 1999 +@@ -0,0 +1,2650 @@ ++/* ++ Madge Ambassador ATM Adapter driver. ++ Copyright (C) 1995-1999 Madge Networks Ltd. + -+#include -+#include ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ++ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian ++ system and in the file COPYING in the Linux kernel source. ++*/ + -+#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_NICSTAR -+extern int nicstar_detect(void); -+#endif ++/* ++ IMPORTANT NOTE: Madge Networks does not license the microcode for ++ this driver under the GPL. See the .data file for the licence. ++*/ + ++/* * dedicated to the memory of Graham Gordon 1971-1998 * */ + -+__initfunc(int atmdev_init(void)) -+{ -+ int devs; ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ 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_NICSTAR -+ devs += nicstar_detect(); -+#endif -+ return devs; ++#include ++#include ++ ++#include "ambassador.h" ++ ++#define maintainer_string "Giuliano Procida at Madge Networks " ++#define description_string "Madge ATM Ambassador driver" ++#define version_string "1.1" ++ ++static inline void __init show_version (void) { ++ printk ("%s version %s\n", description_string, version_string); +} ---- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/atmtcp.c Tue Feb 9 15:49:57 1999 -@@ -0,0 +1,339 @@ -+/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */ + -+/* Written 1997,1998 by Werner Almesberger, EPFL LRC/ICA */ ++/* ++ ++ Theory of Operation ++ ++ I Hardware, detection, initialisation and shutdown. ++ ++ 1. Supported Hardware ++ ++ This driver is for the PCI ATMizer-based Ambassador card (except ++ very early versions). It is not suitable for the similar EISA "TR7" ++ card. Commercially, both cards are known as Collage Server ATM ++ adapters. ++ ++ The loader supports image transfer to the card, image start and few ++ other miscellaneous commands. ++ ++ Only AAL5 is supported with vpi = 0 and vci in the range 0 to 1023. ++ ++ The cards are big-endian. ++ ++ 2. Detection ++ ++ Standard PCI stuff, the early cards are detected and rejected. ++ ++ 3. Initialisation ++ ++ The cards are reset and the self-test results are checked. The ++ microcode image is then transferred and started. This waits for a ++ pointer to a descriptor containing details of the host-based queues ++ and buffers and various parameters etc. Once they are processed ++ normal operations may begin. The BIA is read using a microcode ++ command. ++ ++ 4. Shutdown ++ ++ This may be accomplished either by a card reset or via the microcode ++ shutdown command. Further investigation required. ++ ++ 5. Persistent state ++ ++ The card reset does not affect PCI configuration (good) or the ++ contents of several other "shared run-time registers" (bad) which ++ include doorbell and interrupt control as well as EEPROM and PCI ++ control. The driver must be careful when modifying these registers ++ not to touch bits it does not use and to undo any changes at exit. ++ ++ II Driver software ++ ++ 0. Generalities ++ ++ The adapter is quite intelligent (fast) and has a simple interface ++ (few features). VPI is always zero, 1024 VCIs are supported. There ++ is limited cell rate support. UBR channels can be kept and ABR ++ (explicit rate, bu not EFCI) is supported. There is no CBR or VBR ++ support. ++ ++ 1. Driver <-> Adapter Communication ++ ++ Apart from the basic loader commands, the driver communicates ++ through three entities: the command queue (CQ), the transmit queue ++ pair (TXQ) and the receive queue pairs (RXQ). These three entities ++ are set up by the host and passed to the microcode just after it has ++ been started. ++ ++ All queues are host-based circular queues. They are contiguous and ++ (due to hardware limitations) have some restrictions as to their ++ locations in (bus) memory. They are of the "full means the same as ++ empty so don't do that" variety since the adapter uses pointers ++ internally. ++ ++ The queue pairs work as follows: one queue is for supply to the ++ adapter, items in it are pending and are owned by the adapter; the ++ other is the for return from the adapter, items in it have been ++ dealt with by the adapter. The host adds items to the supply (TX ++ descriptors and free RX buffer descriptors) and removes items from ++ the return (TX and RX completions). The adapter deals with out of ++ order completions. ++ ++ Interrupts (card to host) and the doorbell (host to card) are used ++ for signalling. ++ ++ 1. CQ ++ ++ This is to communicate "open VC", "close VC", "get stats" etc. to ++ the adapter. At most one command is retired every millisecond by the ++ card. There is no out of order completion or notification. The ++ driver needs to check the return code of the command, waiting as ++ appropriate. ++ ++ 2. TXQ ++ ++ TX supply items are of variable length (scatter gather support) and ++ so the queue items are (more or less) pointers to the real thing. ++ Each TX supply item contains a unique, host-supplied handle (the skb ++ bus address seems most sensible as this works for Alphas as well, ++ there is no need to do any endian conversions on the handles). ++ ++ TX return items consist of just the handles above. ++ ++ 3. RXQ (up to 4 of these with different lengths and buffer sizes) ++ ++ RX supply items consist of a unique, host-supplied handle (the skb ++ bus address again) and a pointer to the buffer data area. ++ ++ RX return items consist of the handle above, the VC, length and a ++ status word. This just screams "oh so easy" doesn't it? + ++ Note on RX pool sizes: ++ ++ Each pool should have enough buffers to handle a back-to-back stream ++ of minimum sized frames on a single VC. For example: ++ ++ frame spacing = 3us (about right) ++ ++ delay = IRQ lat + RX handling + RX buffer replenish = 20 (us) (a guess) ++ ++ min number of buffers for one VC = 1 + delay/spacing (buffers) + -+#include -+#include -+#include -+#include -+#include "../../net/atm/tunable.h" /* @@@ fix this */ -+#include "../../net/atm/protocols.h" /* @@@ fix this */ ++ delay/spacing = latency = (20+2)/3 = 7 (buffers) (rounding up) ++ ++ The 20us delay assumes that there is no need to touch disk; if we ++ need touch disk to get buffers we are going to drop frames anyway. ++ ++ In fact, each pool should have enough buffers to support the ++ simultaneous reassembly of a separate frame on each VC and cope with ++ the case in which large frames arrive with round robin cell arrivals ++ on each VC. ++ ++ Only one frame can complete at each cell arrival, so if "n" VCs are ++ open, the worst case is to have them all complete frames together ++ followed by all starting new frames together. ++ ++ min number of buffers = n + delay/spacing ++ ++ These are the extreme requirements, however, they are "n+k" for some ++ "k" so we have only the constant to choose. This is the argument ++ rx_lats which current defaults at 3. ++ ++ Actually, "n ? n+k : 0" is better and this is what is implemented. ++ ++ 4. Driver locking ++ ++ Simple spinlocks are used around the TX and RX queue mechanisms. ++ Anyone with a faster, working method is welcome to implement it. ++ ++ The adapter command queue is protected with a spinlock. We always ++ wait for commands to complete. ++ ++ A more complex form of locking is used around parts of the VC open ++ and close functions. There are three reasons for a lock: 1. we need ++ to do atomic rate reservation and release (not used yet), 2. Opening ++ sometimes involves two adapter commands which must not be separated ++ by another command on the same VC, 3. the changes in RX pool size ++ must be atomic. The lock needs to work over context switches, so we ++ use a semaphore. ++ ++ III Hardware Features and Microcode Bugs ++ ++ 1. Byte Ordering ++ ++ *%^"$&%^$*&^"$(%^$#&^%$(&#%$*(&^#%!"!"!*! ++ ++ 2. Memory access ++ ++ All structures that are not accessed using DMA must be 4-byte ++ aligned (not a problem) and must not cross 4MB boundaries. ++ ++ There is a DMA memory hole at E0000000-E00000FF (groan). ++ ++ TX fragments (DMA read) must not cross 4MB boundaries (would be 16MB ++ but for a hardware bug). ++ ++ RX buffers (DMA write) must not cross 16MB boundaries and must ++ include spare trailing bytes up to the next 4-byte boundary; they ++ will be written with rubbish. ++ ++ The PLX likes to prefetch; if reading up to 4 u32 past the end of ++ each TX fragment is not a problem, then TX can be made to go a ++ little faster by passing a flag at init that disables a prefetch ++ workaround. We do not pass this flag. ++ ++ Now we: ++ . Note that skb_alloc rounds up size to a 16byte boundary. ++ . Ensure all areas must not traverse 4MB boundaries. ++ . Ensure all areas must not start at a E00000xx bus address. ++ (I cannot be certain, but this may always hold with Linux) ++ . Make all failures cause a loud message. ++ . Discard non-conforming SKBs (causes TX failure or RX fill delay). ++ . Discard non-conforming TX fragment descriptors (the TX fails). ++ In the future we could: ++ . Allow RX areas that traverse 4MB (but not 16MB) boundaries. ++ . Segment TX areas into some/more fragments, when necessary. ++ . Relax checks for non-DMA items (ignore hole). ++ . Give scatter-gather (iovec) requirements using ???. (?) ++ ++ 3. VC close is broken (only for new microcode) ++ ++ The VC close adapter microcode command fails to do anything if any ++ frames have been received on the VC but none have been transmitted. ++ Frames continue to be reassembled and passed (with IRQ) to the ++ driver. ++ ++ IV To Do List ++ ++ . Fix bugs! ++ ++ . Timer code may be broken. ++ ++ . Deal with buggy VC close (somehow) in microcode 12. ++ ++ . Handle interrupted and/or non-blocking writes - is this a job for ++ the protocol layer? ++ ++ . Add code to break up TX fragments when they span 4MB boundaries. ++ ++ . Add SUNI phy layer (need to know where SUNI lives on card). ++ ++ . Implement a tx_alloc fn to (a) satisfy TX alignment etc. and (b) ++ leave extra headroom space for Ambassador TX descriptors. ++ ++ . Understand these elements of struct atm_vcc: recvq (proto?), ++ sleep, callback, listenq, backlog_quota, reply and user_back. ++ ++ . Adjust TX/RX skb allocation to favour IP with LANE/CLIP (configurable). ++ ++ . Impose a TX-pending limit (2?) on each VC, help avoid TX q overflow. ++ ++ . Decide whether RX buffer recycling is or can be made completely safe; ++ turn it back on. It looks like Werner is going to axe this. ++ ++ . Implement QoS changes on open VCs (involves extracting parts of VC open ++ and close into separate functions and using them to make changes). ++ ++ . Hack on command queue so that someone can issue multiple commands and wait ++ on the last one (OR only "no-op" or "wait" commands are waited for). ++ ++ . Eliminate need for while-schedule around do_command. ++ ++*/ + ++/********** microcode **********/ + -+#define PRIV(dev) ((struct atmtcp_dev_data *) ((dev)->dev_data)) ++#ifdef AMB_NEW_MICROCODE ++#define UCODE(x) "atmsar12" "." #x ++#else ++#define UCODE(x) "atmsar11" "." #x ++#endif + ++static const u32 __initdata ucode_start = ++#include UCODE(start) ++; + -+struct atmtcp_dev_data { -+ struct atm_vcc *vcc; /* control VCC; NULL if detached */ -+ int persist; /* non-zero if persistent */ ++static const region __initdata ucode_regions[] = { ++#include UCODE(regions) ++ { 0, 0 } +}; + ++static const u32 __initdata ucode_data[] = { ++#include UCODE(data) ++ 0xdeadbeef ++}; + -+#define DEV_LABEL "atmtcp" ++/********** globals **********/ + -+#define MAX_VPI_BITS 8 /* simplifies life */ -+#define MAX_VCI_BITS 16 ++static amb_dev * amb_devs = NULL; ++static struct timer_list housekeeping; + ++static unsigned short debug = 0; ++static unsigned int cmds = 8; ++static unsigned int txs = 32; ++static unsigned int rxs[NUM_RX_POOLS] = { 64, 64, 64, 64 }; ++static unsigned int rxs_bs[NUM_RX_POOLS] = { 2500, 5000, 10000, 20000 }; ++static unsigned int rx_lats = 7; ++static unsigned char pci_lat = 0; + -+static void atmtcp_v_dev_close(struct atm_dev *dev) -+{ -+ MOD_DEC_USE_COUNT; ++/********** access to adapter **********/ ++ ++static inline void wr_mem (const amb_dev * dev, u32 * addr, u32 data) { ++ u32 be = cpu_to_be32 (data); ++ PRINTD (DBG_FLOW|DBG_REGS, "wr: %p <- %08x b[%08x]", addr, data, be); ++#ifdef AMB_MMIO ++ dev->membase[addr - (u32 *) 0] = be; ++#else ++ outl (be, dev->iobase + (addr - (u32 *) 0) * sizeof(u32)); ++#endif +} + ++static inline u32 rd_mem (const amb_dev * dev, u32 * addr) { ++#ifdef AMB_MMIO ++ u32 be = dev->membase[addr - (u32 *) 0]; ++#else ++ u32 be = inl (dev->iobase + (addr - (u32 *) 0) * sizeof(u32)); ++#endif ++ u32 data = be32_to_cpu (be); ++ PRINTD (DBG_FLOW|DBG_REGS, "rd: %p -> %08x b[%08x]", addr, data, be); ++ return data; ++} ++ ++/********** dump routines **********/ ++ ++static inline void dump_registers (const amb_dev * dev) { ++#ifdef DEBUG_AMBASSADOR ++ // u32 * i; ++ // PRINTD (DBG_REGS, "mailboxes: "); ++ // for (i = (u32 *) 0x40; i < (u32 *) 0x60; ++i) ++ // PRINTD (DBG_REGS, "%08x ", rd_mem (dev, i)); ++ PRINTD (DBG_REGS, "doorb %08x", rd_mem (dev, (u32 *) 0x60)); ++ PRINTD (DBG_REGS, "irqev %08x", rd_mem (dev, (u32 *) 0x64)); ++ PRINTD (DBG_REGS, "irqen %08x", rd_mem (dev, (u32 *) 0x68)); ++ PRINTD (DBG_REGS, "reset %08x", rd_mem (dev, (u32 *) 0x6c)); ++#else ++ (void) dev; ++#endif ++ return; ++} + -+static int atmtcp_v_open(struct atm_vcc *vcc,short vpi,int vci) -+{ -+ int error; ++static inline void dump_loader_block (volatile loader_block * lb) { ++#ifdef DEBUG_AMBASSADOR ++ unsigned int i; ++ PRINTDB (DBG_LOAD, "lb @ %p; res: %d, cmd: %d, pay:", ++ lb, be32_to_cpu (lb->result), be32_to_cpu (lb->command)); ++ for (i = 0; i < MAX_COMMAND_DATA; ++i) ++ PRINTDM (DBG_LOAD, " %08x", be32_to_cpu (lb->payload.data[i])); ++ PRINTDE (DBG_LOAD, ", vld: %08x", be32_to_cpu (lb->valid)); ++#else ++ (void) lb; ++#endif ++ return; ++} + -+ 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; -+ vcc->flags |= ATM_VF_ADDR | ATM_VF_READY; -+ return 0; ++static inline void dump_command (command * cmd) { ++#ifdef DEBUG_AMBASSADOR ++ unsigned int i; ++ PRINTDB (DBG_CMD, "cmd @ %p, req: %08x, pars:", ++ cmd, /*be32_to_cpu*/ (cmd->request)); ++ for (i = 0; i < 3; ++i) ++ PRINTDM (DBG_CMD, " %08x", /*be32_to_cpu*/ (cmd->args.par[i])); ++ PRINTDE (DBG_CMD, ""); ++#else ++ (void) cmd; ++#endif ++ return; ++} ++ ++static inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) { ++#ifdef DEBUG_AMBASSADOR ++ unsigned int i; ++ unsigned char * data = skb->data; ++ PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc); ++ for (i=0; ilen && i < 256;i++) ++ PRINTDM (DBG_DATA, "%02x ", data[i]); ++ PRINTDE (DBG_DATA,""); ++#else ++ (void) prefix; ++ (void) vc; ++ (void) skb; ++#endif ++ return; ++} ++ ++/********** check memory areas for use by Ambassador **********/ ++ ++/* see limitations under Hardware Features */ ++ ++static inline int check_area (void * start, size_t length) { ++ // assumes length > 0 ++ const u32 fourmegmask = (-1)<<22; ++ const u32 twofivesixmask = (-1)<<8; ++ const u32 starthole = 0xE0000000; ++ u32 startaddress = virt_to_bus (start); ++ u32 lastaddress = startaddress+length-1; ++ if ((startaddress ^ lastaddress) & fourmegmask || ++ (startaddress & twofivesixmask) == starthole) { ++ PRINTK (KERN_ERR, "check_area failure: [%x,%x] - mail maintainer!", ++ startaddress, lastaddress); ++ return -1; ++ } else { ++ return 0; ++ } +} + ++/********** free an skb (as per ATM device driver documentation) **********/ + -+static void atmtcp_v_close(struct atm_vcc *vcc) -+{ -+ vcc->flags &= ~(ATM_VF_READY | ATM_VF_ADDR); ++static inline void amb_kfree_skb (struct sk_buff * skb) { ++ if (ATM_SKB(skb)->vcc->pop) { ++ ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb); ++ } else { ++ dev_kfree_skb (skb); ++ } +} + ++/********** TX completion **********/ + -+static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) -+{ -+ struct atm_cirange ci; -+ struct atm_vcc *vcc; ++static inline void tx_complete (amb_dev * dev, tx_out * tx) { ++ tx_simple * tx_descr = bus_to_virt (tx->handle); ++ struct sk_buff * skb = tx_descr->skb; ++ ++ PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx); ++ ++ // VC layer stats ++ ATM_SKB(skb)->vcc->stats->tx++; ++ ++ // free the descriptor ++ kfree (tx_descr); ++ ++ // free the skb ++ amb_kfree_skb (skb); ++ ++ dev->stats.tx_ok++; ++ return; ++} + -+ if (cmd != ATM_SETCIRANGE) return -EINVAL; -+ if (copy_from_user(&ci,(void *) arg,sizeof(ci))) return -EFAULT; -+ 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; -+ for (vcc = dev->vccs; vcc; vcc = vcc->next) -+ if ((vcc->vpi >> ci.vpi_bits) || -+ (vcc->vci >> ci.vci_bits)) return -EBUSY; -+ dev->ci_range = ci; -+ return 0; ++/********** RX completion **********/ ++ ++static void rx_complete (amb_dev * dev, rx_out * rx) { ++ struct sk_buff * skb = bus_to_virt (rx->handle); ++ u16 vc = be16_to_cpu (rx->vc); ++ // unused: u16 lec_id = be16_to_cpu (rx->lec_id); ++ u16 status = be16_to_cpu (rx->status); ++ u16 rx_len = be16_to_cpu (rx->length); ++ ++ PRINTD (DBG_FLOW|DBG_RX, "rx_complete %p %p (len=%hu)", dev, rx, rx_len); ++ ++ // XXX move this in and add to VC stats ??? ++ if (!status) { ++ struct atm_vcc * atm_vcc = dev->rxer[vc]; ++ dev->stats.rx.ok++; ++ ++ if (atm_vcc) { ++ ++ if (rx_len <= atm_vcc->qos.rxtp.max_sdu) { ++ ++ if (atm_charge (atm_vcc, skb->truesize)) { ++ ++ // prepare socket buffer ++ ATM_SKB(skb)->vcc = atm_vcc; ++ skb_put (skb, rx_len); ++ ++ dump_skb ("<<<", vc, skb); ++ ++ // VC layer stats ++ atm_vcc->stats->rx++; ++ skb->stamp = xtime; ++ // end of our responsability ++ atm_vcc->push (atm_vcc, skb); ++ return; ++ ++ } else { ++ // someone fix this (message), please! ++ PRINTD (DBG_INFO|DBG_RX, "dropped thanks to atm_charge (vc %hu)", vc); ++ // drop stats incremented in atm_charge ++ } ++ ++ } else { ++ PRINTK (KERN_INFO, "dropped over-size frame"); ++ // should we count this? ++ atm_vcc->stats->rx_drop++; ++ } ++ ++ } else { ++ PRINTD (DBG_WARN|DBG_RX, "got frame but RX closed for channel %hu", vc); ++ // this is an adapter bug, only in new version of microcode ++ } ++ ++ } else { ++ dev->stats.rx.error++; ++ if (status & CRC_ERR) ++ dev->stats.rx.badcrc++; ++ if (status & LEN_ERR) ++ dev->stats.rx.toolong++; ++ if (status & ABORT_ERR) ++ dev->stats.rx.aborted++; ++ if (status & UNUSED_ERR) ++ dev->stats.rx.unused++; ++ } ++ ++ dev_kfree_skb (skb); ++ return; +} + ++/* ++ ++ Note on queue handling. ++ ++ Here "give" and "take" refer to queue entries and a queue (pair) ++ rather than frames to or from the host or adapter. Empty frame ++ buffers are given to the RX queue pair and returned unused or ++ containing RX frames. TX frames (well, pointers to TX fragment ++ lists) are given to the TX queue pair, completions are returned. ++ ++*/ + -+static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb) -+{ -+ struct atmtcp_dev_data *dev_data; -+ struct atm_vcc *out_vcc; -+ struct sk_buff *new_skb; -+ struct atmtcp_hdr *hdr; -+ int size; -+ -+ dev_data = PRIV(vcc->dev); -+ if (dev_data) out_vcc = dev_data->vcc; -+ if (!dev_data || !out_vcc) { -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ if (dev_data) return 0; -+ vcc->stats->tx_err++; -+ return -ENOLINK; -+ } -+ size = skb->len+sizeof(struct atmtcp_hdr); -+ if (!atm_charge(out_vcc,atm_pdu2truesize(size))) new_skb = NULL; -+ else { -+ new_skb = alloc_skb(size,GFP_ATOMIC); -+ if (!new_skb) -+ atm_return(out_vcc,atm_pdu2truesize(size)); -+ } -+ if (!new_skb) { -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ vcc->stats->tx_err++; -+ return -ENOBUFS; -+ } -+ hdr = (void *) skb_put(new_skb,sizeof(struct atmtcp_hdr)); -+ hdr->vpi = htons(vcc->vpi); -+ hdr->vci = htons(vcc->vci); -+ hdr->length = htonl(skb->len); -+ memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ out_vcc->push(out_vcc,new_skb); -+ return 0; -+} ++/********** command queue **********/ + ++// I really don't like this, but it's the best I can do at the moment + -+static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page) -+{ -+ struct atmtcp_dev_data *dev_data = PRIV(dev); ++// also, the callers are responsible for byte order as the microcode ++// sometimes does 16-bit accesses (yuk yuk yuk) + -+ if (*pos) return 0; -+ if (!dev_data->persist) return sprintf(page,"ephemeral\n"); -+ return sprintf(page,"persistent, %sconnected\n", -+ dev_data->vcc ? "" : "dis"); ++static int command_do (amb_dev * dev, command * cmd) { ++ volatile amb_cq * cq = &dev->cq; ++ command * my_slot; ++ unsigned long timeout; ++ ++ PRINTD (DBG_FLOW|DBG_CMD, "command_do %p", dev); ++ ++ if (test_bit (dead, &dev->flags)) ++ return 0; ++ ++ spin_lock (&cq->lock); ++ ++ // if not full... ++ if (cq->pending < cq->maximum) { ++ // remember my slot for later ++ my_slot = cq->in; ++ PRINTD (DBG_CMD, "command in slot %p", my_slot); ++ ++ dump_command (cmd); ++ ++ // copy command in ++ *cq->in = *cmd; ++ cq->pending++; ++ cq->in = NEXTQ (cq->in, cq->start, cq->limit); ++ ++ // mail the command ++ wr_mem (dev, &mem->mb.adapter.cmd_address, virt_to_bus (cq->in)); ++ ++ // prepare to wait for cq->pending milliseconds ++ // effectively one centisecond on i386 ++ timeout = (cq->pending*HZ+999)/1000; ++ ++ if (cq->pending > cq->high) ++ cq->high = cq->pending; ++ spin_unlock (&cq->lock); ++ ++ while (timeout) { ++ // go to sleep ++ // PRINTD (DBG_CMD, "wait: sleeping %lu for command", timeout); ++ timeout = schedule_timeout (timeout); ++ // woken up by timeout or signal ++ } ++ ++ // wait for my slot to be reached (all waiters are here or above, until...) ++ while (cq->out != my_slot) { ++ PRINTD (DBG_CMD, "wait: command slot (now at %p)", cq->out); ++ schedule(); ++ } ++ ++ // wait on my slot (... one gets to its slot, and... ) ++ while (cq->out->request != cpu_to_be32 (SRB_COMPLETE)) { ++ PRINTD (DBG_CMD, "wait: command slot completion"); ++ schedule(); ++ } ++ ++ PRINTD (DBG_CMD, "command complete"); ++ // update queue (... moves the queue along to the next slot) ++ spin_lock (&cq->lock); ++ cq->pending--; ++ // copy command out ++ *cmd = *cq->out; ++ cq->out = NEXTQ (cq->out, cq->start, cq->limit); ++ spin_unlock (&cq->lock); ++ ++ return 0; ++ } else { ++ spin_unlock (&cq->lock); ++ return -EAGAIN; ++ } ++ +} + ++/********** TX queue pair **********/ + -+static void atmtcp_c_close(struct atm_vcc *vcc) -+{ -+ struct atm_dev *atmtcp_dev; -+ struct atmtcp_dev_data *dev_data; -+ -+ atmtcp_dev = (struct atm_dev *) vcc->dev_data; -+ dev_data = PRIV(atmtcp_dev); -+ dev_data->vcc = NULL; -+ if (dev_data->persist) return; -+ kfree(dev_data); -+ shutdown_atm_dev(atmtcp_dev); -+ vcc->dev_data = NULL; -+} -+ ++static inline int tx_give (amb_dev * dev, tx_in * tx) { ++ amb_txq * txq = &dev->txq; ++ unsigned long flags; ++ ++ PRINTD (DBG_FLOW|DBG_TX, "tx_give %p", dev); + -+static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) -+{ -+ struct atm_dev *dev; -+ struct atmtcp_hdr *hdr; -+ struct atm_vcc *out_vcc; -+ struct sk_buff *new_skb; ++ if (test_bit (dead, &dev->flags)) ++ return 0; ++ ++ spin_lock_irqsave (&txq->lock, flags); ++ ++ if (txq->pending < txq->maximum) { ++ PRINTD (DBG_TX, "TX in slot %p", txq->in.ptr); + -+ if (!skb->len) return 0; -+ dev = vcc->dev_data; -+ hdr = (void *) skb->data; -+ for (out_vcc = dev->vccs; out_vcc; out_vcc = out_vcc->next) -+ if (out_vcc->vpi == ntohs(hdr->vpi) && -+ out_vcc->vci == ntohs(hdr->vci) && -+ out_vcc->qos.rxtp.traffic_class != ATM_NONE) -+ break; -+ if (!out_vcc) { -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ vcc->stats->tx_err++; -+ return 0; -+ } -+ skb_pull(skb,sizeof(struct atmtcp_hdr)); -+ if (!atm_charge(out_vcc,atm_pdu2truesize(skb->len))) new_skb = NULL; -+ else { -+ new_skb = alloc_skb(skb->len,GFP_KERNEL); -+ if (!new_skb) atm_return(out_vcc,atm_pdu2truesize(skb->len)); -+ } -+ if (!new_skb) { -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ return -ENOBUFS; -+ } -+ memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ out_vcc->push(out_vcc,new_skb); -+ return 0; ++ *txq->in.ptr = *tx; ++ txq->pending++; ++ txq->in.ptr = NEXTQ (txq->in.ptr, txq->in.start, txq->in.limit); ++ // hand over the TX and ring the bell ++ wr_mem (dev, &mem->mb.adapter.tx_address, virt_to_bus (txq->in.ptr)); ++ wr_mem (dev, &mem->doorbell, TX_FRAME); ++ ++ if (txq->pending > txq->high) ++ txq->high = txq->pending; ++ spin_unlock_irqrestore (&txq->lock, flags); ++ return 0; ++ } else { ++ txq->filled++; ++ spin_unlock_irqrestore (&txq->lock, flags); ++ return -EAGAIN; ++ } +} + ++static inline int tx_take (amb_dev * dev) { ++ amb_txq * txq = &dev->txq; ++ unsigned long flags; ++ ++ PRINTD (DBG_FLOW|DBG_TX, "tx_take %p", dev); ++ ++ spin_lock_irqsave (&txq->lock, flags); ++ ++ if (txq->pending && txq->out.ptr->handle) { ++ // deal with TX completion ++ tx_complete (dev, txq->out.ptr); ++ // mark unused again ++ txq->out.ptr->handle = 0; ++ // remove item ++ txq->pending--; ++ txq->out.ptr = NEXTQ (txq->out.ptr, txq->out.start, txq->out.limit); ++ ++ spin_unlock_irqrestore (&txq->lock, flags); ++ return 0; ++ } else { ++ ++ spin_unlock_irqrestore (&dev->lock, flags); ++ return -1; ++ } ++} + -+/* -+ * Device operations for the virtual ATM devices created by ATMTCP. -+ */ -+ -+ -+static struct atmdev_ops atmtcp_v_dev_ops = { -+ atmtcp_v_dev_close, -+ atmtcp_v_open, -+ atmtcp_v_close, -+ atmtcp_v_ioctl, -+ NULL, /* no getsockopt */ -+ NULL, /* no setsockopt */ -+ atmtcp_v_send, -+ NULL, /* no direct writes */ -+ NULL, /* no send_oam */ -+ NULL, /* no phy_put */ -+ NULL, /* no phy_get */ -+ NULL, /* no feedback */ -+ NULL, /* no change_qos */ -+ NULL, /* no free_rx_skb */ -+ atmtcp_v_proc /* proc_read */ -+}; -+ -+ -+/* -+ * Device operations for the ATMTCP control device. -+ */ -+ ++/********** RX queue pairs **********/ + -+static struct atmdev_ops atmtcp_c_dev_ops = { -+ NULL, /* no dev_close */ -+ NULL, /* no open */ -+ atmtcp_c_close, -+ NULL, /* no ioctl */ -+ NULL, /* no getsockopt */ -+ NULL, /* no setsockopt */ -+ atmtcp_c_send, -+ NULL, /* no sg_send */ -+ NULL, /* no send_oam */ -+ NULL, /* no phy_put */ -+ NULL, /* no phy_get */ -+ NULL, /* no feedback */ -+ NULL, /* no change_qos */ -+ NULL, /* no free_rx_skb */ -+ NULL /* no proc_read */ -+}; ++static inline int rx_give (amb_dev * dev, rx_in * rx, unsigned char pool) { ++ amb_rxq * rxq = &dev->rxq[pool]; ++ unsigned long flags; ++ ++ PRINTD (DBG_FLOW|DBG_RX, "rx_give %p[%hu]", dev, pool); ++ ++ spin_lock_irqsave (&rxq->lock, flags); ++ ++ if (rxq->pending < rxq->maximum) { ++ PRINTD (DBG_RX, "RX in slot %p", rxq->in.ptr); + ++ *rxq->in.ptr = *rx; ++ rxq->pending++; ++ rxq->in.ptr = NEXTQ (rxq->in.ptr, rxq->in.start, rxq->in.limit); ++ // hand over the RX buffer ++ wr_mem (dev, &mem->mb.adapter.rx_address[pool], virt_to_bus (rxq->in.ptr)); ++ ++ spin_unlock_irqrestore (&rxq->lock, flags); ++ return 0; ++ } else { ++ spin_unlock_irqrestore (&rxq->lock, flags); ++ return -1; ++ } ++} + -+static struct atm_dev atmtcp_control_dev = { -+ &atmtcp_c_dev_ops, -+ NULL, /* no PHY */ -+ "atmtcp", /* 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 */ -+}; ++static inline int rx_take (amb_dev * dev, unsigned char pool) { ++ amb_rxq * rxq = &dev->rxq[pool]; ++ unsigned long flags; ++ ++ PRINTD (DBG_FLOW|DBG_RX, "rx_take %p[%hu]", dev, pool); ++ ++ spin_lock_irqsave (&rxq->lock, flags); ++ ++ if (rxq->pending && (rxq->out.ptr->status || rxq->out.ptr->length)) { ++ // deal with RX completion ++ rx_complete (dev, rxq->out.ptr); ++ // mark unused again ++ rxq->out.ptr->status = 0; ++ rxq->out.ptr->length = 0; ++ // remove item ++ rxq->pending--; ++ rxq->out.ptr = NEXTQ (rxq->out.ptr, rxq->out.start, rxq->out.limit); ++ ++ if (rxq->pending < rxq->low) ++ rxq->low = rxq->pending; ++ spin_unlock_irqrestore (&rxq->lock, flags); ++ return 0; ++ } else { ++ if (!rxq->pending && rxq->buffers_wanted) ++ rxq->emptied++; ++ spin_unlock_irqrestore (&rxq->lock, flags); ++ return -1; ++ } ++} + ++/********** RX Pool handling **********/ + -+static int atmtcp_create(int itf,int persist,struct atm_dev **result) -+{ -+ struct atmtcp_dev_data *dev_data; -+ struct atm_dev *dev; ++/* pre: buffers_wanted = 0, post: pending = 0 */ ++static inline void drain_rx_pool (amb_dev * dev, unsigned char pool) { ++ amb_rxq * rxq = &dev->rxq[pool]; ++ ++ PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pool %p %hu", dev, pool); ++ ++ if (test_bit (dead, &dev->flags)) ++ return; ++ ++ /* we are not quite like the fill pool routines as we cannot just ++ remove one buffer, we have to remove all of them, but we might as ++ well pretend... */ ++ if (rxq->pending > rxq->buffers_wanted) { ++ command cmd; ++ cmd.request = cpu_to_be32 (SRB_FLUSH_BUFFER_Q); ++ cmd.args.flush.flags = cpu_to_be16 (pool << SRB_POOL_SHIFT); ++ while (command_do (dev, &cmd)) ++ schedule(); ++ /* the pool may also be emptied via the interrupt handler */ ++ while (rxq->pending > rxq->buffers_wanted) ++ if (rx_take (dev, pool)) ++ schedule(); ++ } ++ ++ return; ++} + -+ dev_data = kmalloc(sizeof(*dev_data),GFP_KERNEL); -+ if (!dev_data) return -ENOMEM; -+ dev = atm_dev_register(DEV_LABEL,&atmtcp_v_dev_ops,itf,0); -+ if (!dev) { -+ kfree(dev_data); -+ return itf == -1 ? -ENOMEM : -EBUSY; -+ } -+ MOD_INC_USE_COUNT; -+ dev->ci_range.vpi_bits = MAX_VPI_BITS; -+ dev->ci_range.vci_bits = MAX_VCI_BITS; -+ PRIV(dev) = dev_data; -+ PRIV(dev)->vcc = NULL; -+ PRIV(dev)->persist = persist; -+ if (result) *result = dev; -+ return 0; ++#ifdef MODULE ++static void drain_rx_pools (amb_dev * dev) { ++ unsigned char pool; ++ ++ PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pools %p", dev); ++ ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ drain_rx_pool (dev, pool); ++ ++ return; +} ++#endif + ++static inline void fill_rx_pool (amb_dev * dev, unsigned char pool, int priority) { ++ rx_in rx; ++ amb_rxq * rxq; ++ ++ PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pool %p %hu %x", dev, pool, priority); ++ ++ if (test_bit (dead, &dev->flags)) ++ return; ++ ++ rxq = &dev->rxq[pool]; ++ while (rxq->pending < rxq->maximum && rxq->pending < rxq->buffers_wanted) { ++ ++ struct sk_buff * skb = alloc_skb (rxq->buffer_size + RX_FUDGE, priority); ++ if (!skb) { ++ PRINTD (DBG_SKB|DBG_POOL, "failed to allocate skb for RX pool %hu", pool); ++ return; ++ } ++ if (check_area (skb->data, skb->truesize)) { ++ dev_kfree_skb (skb); ++ return; ++ } ++ // cast needed as there is no %? for pointer differences ++ PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li", ++ skb, skb->head, (long) (skb->end - skb->head)); ++ rx.handle = virt_to_bus (skb); ++ rx.host_address = cpu_to_be32 (virt_to_bus (skb->data)); ++ if (rx_give (dev, &rx, pool)) ++ dev_kfree_skb (skb); ++ ++ } ++ ++ return; ++} + -+int atmtcp_attach(struct atm_vcc *vcc,int itf) -+{ -+ struct atm_dev *dev; ++// top up all RX pools (also called as a bottom half) ++static void fill_rx_pools (amb_dev * dev) { ++ unsigned char pool; ++ ++ PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pools %p", dev); ++ ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ fill_rx_pool (dev, pool, GFP_ATOMIC); ++ ++ return; ++} + -+ dev = NULL; -+ if (itf != -1) dev = atm_find_dev(itf); -+ if (dev) { -+ if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; -+ if (PRIV(dev)->vcc) return -EBUSY; -+ } -+ else { -+ int error; ++/********** enable host interrupts **********/ + -+ error = atmtcp_create(itf,0,&dev); -+ if (error) return error; -+ } -+ PRIV(dev)->vcc = vcc; -+ bind_vcc(vcc,&atmtcp_control_dev); -+ vcc->flags |= ATM_VF_READY | ATM_VF_META; -+ vcc->dev_data = dev; -+ (void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */ -+ vcc->stats = &atmtcp_control_dev.stats.aal5; -+ return dev->number; ++static inline void interrupts_on (amb_dev * dev) { ++ wr_mem (dev, &mem->interrupt_control, ++ rd_mem (dev, &mem->interrupt_control) ++ | AMB_INTERRUPT_BITS); +} + ++/********** disable host interrupts **********/ + -+int atmtcp_create_persistent(int itf) -+{ -+ return atmtcp_create(itf,1,NULL); ++static inline void interrupts_off (amb_dev * dev) { ++ wr_mem (dev, &mem->interrupt_control, ++ rd_mem (dev, &mem->interrupt_control) ++ &~ AMB_INTERRUPT_BITS); +} + ++/********** interrupt handling **********/ + -+int atmtcp_remove_persistent(int itf) -+{ -+ struct atm_dev *dev; -+ struct atmtcp_dev_data *dev_data; ++static void interrupt_handler (int irq, void * dev_id, struct pt_regs * pt_regs) { ++ amb_dev * dev = amb_devs; ++ unsigned int irq_ok; ++ unsigned int irq_ok_old; ++ (void) pt_regs; ++ ++ PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id); ++ ++ if (!dev_id) { ++ PRINTD (DBG_IRQ|DBG_ERR, "irq with NULL dev_id: %d", irq); ++ return; ++ } ++ // Did one of our cards generate the interrupt? ++ while (dev) { ++ if (dev == dev_id) ++ break; ++ dev = dev->prev; ++ } ++ if (!dev) { ++ PRINTD (DBG_IRQ, "irq not for me: %d", irq); ++ return; ++ } ++ if (irq != dev->irq) { ++ PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq); ++ return; ++ } ++ ++ // definitely for us ++ irq_ok = 0; ++ irq_ok_old = -1; ++ ++ // perhaps disable interrupts? (disabled at PIC by Linux) ++ // interrupts_off (dev); ++ ++ while (irq_ok_old != irq_ok && irq_ok < 100) { ++ unsigned char pool; ++ u32 ints = rd_mem (dev, &mem->interrupt); ++ wr_mem (dev, &mem->interrupt, -1); ++ PRINTD (DBG_IRQ, "FYI: interrupt was %08x, work %u", ints, irq_ok); ++ irq_ok_old = irq_ok; ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ while (!rx_take (dev, pool)) ++ ++irq_ok; ++ while (!tx_take (dev)) ++ ++irq_ok; ++ } ++ ++ if (irq_ok) { ++#if 0 ++ queue_task (&dev->bh, &tq_immediate); ++ mark_bh (IMMEDIATE_BH); ++#else ++ fill_rx_pools (dev); ++#endif + -+ dev = atm_find_dev(itf); -+ if (!dev) return -ENODEV; -+ if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; -+ dev_data = PRIV(dev); -+ if (!dev_data->persist) return 0; -+ dev_data->persist = 0; -+ if (PRIV(dev)->vcc) return 0; -+ kfree(dev_data); -+ shutdown_atm_dev(dev); -+ return 0; ++ PRINTD (DBG_IRQ, "work done: %u", irq_ok); ++ } else { ++ PRINTD (DBG_IRQ|DBG_WARN, "no work done"); ++ } ++ ++ // perhaps re-enable interrupts? (re-enabled at PIC by Linux) ++ // interrupts_on (dev); ++ PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id); ++ return; ++} ++ ++/********** don't panic... yeah, right **********/ ++ ++static void dont_panic (amb_dev * dev) { ++ amb_cq * cq = &dev->cq; ++ amb_txq * txq; ++ amb_rxq * rxq; ++ command * cmd; ++ tx_in * tx; ++ tx_simple * tx_descr; ++ unsigned char pool; ++ rx_in * rx; ++ ++ unsigned long flags; ++ save_flags (flags); ++ cli(); ++ ++ PRINTK (KERN_INFO, "don't panic - putting adapter into reset"); ++ wr_mem (dev, &mem->reset_control, rd_mem (dev, &mem->reset_control) | AMB_RESET); ++ ++ PRINTK (KERN_INFO, "marking all commands complete"); ++ for (cmd = cq->start; cmd < cq->limit; ++cmd) ++ cmd->request = cpu_to_be32 (SRB_COMPLETE); ++ ++ PRINTK (KERN_INFO, "completing all TXs"); ++ txq = &dev->txq; ++ tx = txq->in.ptr; ++ while (txq->pending--) { ++ if (tx == txq->in.start) ++ tx = txq->in.limit; ++ --tx; ++ tx_descr = bus_to_virt (be32_to_cpu (tx->tx_descr_addr)); ++ amb_kfree_skb (tx_descr->skb); ++ kfree (tx_descr); ++ } ++ ++ PRINTK (KERN_INFO, "freeing all RX buffers"); ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) { ++ rxq = &dev->rxq[pool]; ++ rx = rxq->in.ptr; ++ while (rxq->pending--) { ++ if (rx == rxq->in.start) ++ rx = rxq->in.limit; ++ --rx; ++ dev_kfree_skb (bus_to_virt (rx->handle)); ++ } ++ } ++ ++ PRINTK (KERN_INFO, "don't panic over - close all VCs and rmmod"); ++ set_bit (dead, &dev->flags); ++ restore_flags (flags); ++ return; +} + -+ -+#ifdef MODULE -+ -+int init_module(void) -+{ -+ atm_tcp_ops.attach = atmtcp_attach; -+ atm_tcp_ops.create_persistent = atmtcp_create_persistent; -+ atm_tcp_ops.remove_persistent = atmtcp_remove_persistent; -+ return 0; ++/********** make rate (not quite as much fun as Horizon) **********/ ++ ++static unsigned int make_rate (unsigned int rate, rounding r, ++ u16 * bits, unsigned int * actual) { ++ unsigned char exp; ++ unsigned int man; ++ ++ PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate); ++ ++ // rates in cells per second, ITU format (nasty 16bit fp) ++ // given 5-bit e and 9-bit m: ++ // rate = EITHER (1+m/2^9)*2^e OR 0 ++ // bits = EITHER 1<<14 | e<<9 | m OR 0 ++ // (bit 15 is "reserved", bit 14 "non-zero") ++ // smallest rate is 0 (special representation) ++ // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1) ++ // simple algorithm: ++ // find position of top bit, this gives e ++ // remove top bit and shift (rounding if feeling clever) by 9-e ++ ++ // XXX fix me to work with larger ints ++ ++ // ucode bug: please don't set bit 14! 0 not representable ++ ++ if (rate) { ++ // non-zero rate ++ ++ exp = 31; ++ man = rate; ++ ++ // invariant: rate = man*2^(exp-31) ++ while (!(man & (1<<31))) { ++ exp = exp - 1; ++ man = man<<1; ++ } ++ ++ // man has top bit set ++ // rate = (2^31+(man-2^31))*2^(exp-31) ++ // rate = (1+(man-2^31)/2^31)*2^exp ++ man = man<<1; ++ // rate = (1+man/2^32)*2^exp ++ ++ // exp is in the range 0 to 31, man is in the range 0 to 2^32-1 ++ // time to lose significance... we want m in the range 0 to 2^9-1 ++ // rounding presents a minor problem... we first decide which way ++ // we are rounding (based on given rounding direction and the bits ++ // of the mantissa that are to be discarded). ++ ++ switch (r) { ++ case round_down: { ++ // just truncate ++ man = man>>(32-9); ++ break; ++ } ++ case round_up: { ++ // check all bits that we are discarding ++ if (man & (-1>>9)) { ++ man = (man>>(32-9)) + 1; ++ if (man == (1<<9)) { ++ // check for round up outside of range ++ if (exp == 31) { ++ return -EINVAL; ++ } else { ++ man = 0; ++ exp += 1; ++ } ++ } ++ } else { ++ man = (man>>(32-9)); ++ } ++ break; ++ } ++ case round_nearest: { ++ // check msb that we are discarding ++ if (man & (1<<(32-9-1))) { ++ man = (man>>(32-9)) + 1; ++ // if rounding up would go out of range, just stay at top ++ if (man == (1<<9)) { ++ if (exp == 31) { ++ man -= 1; ++ } else { ++ man = 0; ++ exp += 1; ++ } ++ } ++ } else { ++ man = (man>>(32-9)); ++ } ++ break; ++ } ++ } ++ ++ } else { ++ // zero rate ++ ++ switch (r) { ++ case round_up: { ++ exp = 0; ++ man = 0; ++ break; ++ } ++ case round_down: { ++ return -EINVAL; ++ break; ++ } ++ case round_nearest: { ++ exp = 0; ++ man = 0; ++ break; ++ } ++ } ++ ++ } ++ ++ PRINTD (DBG_QOS, "rate: man=%u, exp=%hu", man, exp); ++ ++ if (bits) ++ *bits = /* (1<<14) | */ (exp<<9) | man; ++ ++ if (actual) ++ *actual = (exp >= 9) ++ ? (1 << exp) + (man << (exp-9)) ++ : (1 << exp) + ((man + (1<<(9-exp-1))) >> (9-exp)); ++ ++ return 0; +} + -+void cleanup_module(void) -+{ -+} ++/********** Linux ATM Operations **********/ + -+#else ++// some are not yet implemented while others do not make sense for ++// this device + -+struct atm_tcp_ops atm_tcp_ops = { -+ atmtcp_attach, /* attach */ -+ atmtcp_create_persistent, /* create_persistent */ -+ atmtcp_remove_persistent /* remove_persistent */ -+}; ++/********** Open a VC **********/ + ++static int amb_open (struct atm_vcc * atm_vcc, short vpi, int vci) { ++ int error; ++ ++ struct atm_qos * qos; ++ struct atm_trafprm * txtp; ++ struct atm_trafprm * rxtp; ++ u16 tx_rate_bits; ++ u16 tx_vc_bits; ++ u16 tx_frame_bits; ++ // int pcr; ++ ++ amb_dev * dev = AMB_DEV(atm_vcc->dev); ++ amb_vcc * vcc; ++ unsigned char pool = -1; // compiler warning ++ ++ PRINTD (DBG_FLOW|DBG_VCC, "amb_open %x %x", vpi, vci); ++ ++ // UNSPEC is deprecated, remove this code eventually ++#if defined ATM_VPI_UNSPEC ++ if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) { ++ PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)"); ++ return -EINVAL; ++ } +#endif -+ ---- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/eni.c Wed Apr 21 17:36:48 1999 -@@ -0,0 +1,2259 @@ -+/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ -+ -+/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ -+ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include /* for xtime */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "tonga.h" -+#include "midway.h" -+#include "suni.h" -+#include "eni.h" -+ -+ -+/* -+ * TODO: -+ * -+ * Show stoppers -+ * none -+ * -+ * Minor -+ * - OAM support -+ * - fix bugs listed below -+ */ -+ -+/* -+ * KNOWN BUGS: -+ * -+ * - may run into JK-JK bug and deadlock -+ * - should allocate UBR channel first -+ * - buffer space allocation algorithm is stupid -+ * (RX: should be maxSDU+maxdelay*rate -+ * TX: should be maxSDU+min(maxSDU,maxdelay*rate) ) -+ * - doesn't support OAM cells -+ * - eni_put_free may hang if not putting memory fragments that _complete_ -+ * 2^n block (never happens in real life, though) -+ * - keeps IRQ even if initialization fails -+ */ -+ -+ ++ ++ // deal with possibly wildcarded VCs ++ error = atm_find_ci (atm_vcc, &vpi, &vci); ++ if (error) { ++ PRINTD (DBG_WARN|DBG_VCC, "atm_find_ci failed!"); ++ return error; ++ } ++ PRINTD (DBG_VCC, "atm_find_ci gives %x %x", vpi, vci); ++ ++ if (!(0 <= vpi && vpi < (1<qos; ++ ++ if (qos->aal != ATM_AAL5) { ++ PRINTD (DBG_QOS, "AAL not supported"); ++ return -EINVAL; ++ } ++ ++ // traffic parameters ++ ++ PRINTD (DBG_QOS, "TX:"); ++ txtp = &qos->txtp; ++ if (txtp->traffic_class != ATM_NONE) { ++ switch (txtp->traffic_class) { ++ case ATM_UBR: { ++ // we take "the PCR" as a rate-cap ++ int pcr = atm_pcr_goal (txtp); ++ if (!pcr) { ++ // no rate cap ++ tx_rate_bits = 0; ++ tx_vc_bits = TX_UBR; ++ tx_frame_bits = TX_FRAME_NOTCAP; ++ } else { ++ rounding r; ++ if (pcr < 0) { ++ r = round_down; ++ pcr = -pcr; ++ } else { ++ r = round_up; ++ } ++ error = make_rate (pcr, r, &tx_rate_bits, 0); ++ tx_vc_bits = TX_UBR_CAPPED; ++ tx_frame_bits = TX_FRAME_CAPPED; ++ } ++ break; ++ } +#if 0 -+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) -+#else -+#define DPRINTK(format,args...) ++ case ATM_ABR: { ++ pcr = atm_pcr_goal (txtp); ++ PRINTD (DBG_QOS, "pcr goal = %d", pcr); ++ break; ++ } +#endif -+ -+ -+#ifndef CONFIG_ATM_ENI_TUNE_BURST -+#define CONFIG_ATM_ENI_BURST_TX_8W -+#define CONFIG_ATM_ENI_BURST_RX_4W ++ default: { ++ // PRINTD (DBG_QOS, "request for non-UBR/ABR denied"); ++ PRINTD (DBG_QOS, "request for non-UBR denied"); ++ return -EINVAL; ++ } ++ } ++ PRINTD (DBG_QOS, "tx_rate_bits=%hx, tx_vc_bits=%hx", ++ tx_rate_bits, tx_vc_bits); ++ } ++ ++ PRINTD (DBG_QOS, "RX:"); ++ rxtp = &qos->rxtp; ++ if (rxtp->traffic_class == ATM_NONE) { ++ // do nothing ++ } else { ++ // choose an RX pool (arranged in increasing size) ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ if ((unsigned int) rxtp->max_sdu <= dev->rxq[pool].buffer_size) { ++ PRINTD (DBG_VCC|DBG_QOS|DBG_POOL, "chose pool %hu (max_sdu %u <= %u)", ++ pool, rxtp->max_sdu, dev->rxq[pool].buffer_size); ++ break; ++ } ++ if (pool == NUM_RX_POOLS) { ++ PRINTD (DBG_WARN|DBG_VCC|DBG_QOS|DBG_POOL, ++ "no pool suitable for VC (RX max_sdu %d is too large)", ++ rxtp->max_sdu); ++ return -EINVAL; ++ } ++ ++ switch (rxtp->traffic_class) { ++ case ATM_UBR: { ++ break; ++ } ++#if 0 ++ case ATM_ABR: { ++ pcr = atm_pcr_goal (rxtp); ++ PRINTD (DBG_QOS, "pcr goal = %d", pcr); ++ break; ++ } +#endif ++ default: { ++ // PRINTD (DBG_QOS, "request for non-UBR/ABR denied"); ++ PRINTD (DBG_QOS, "request for non-UBR denied"); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ // get space for our vcc stuff ++ vcc = kmalloc (sizeof(amb_vcc), GFP_KERNEL); ++ if (!vcc) { ++ PRINTK (KERN_ERR, "out of memory!"); ++ return -ENOMEM; ++ } ++ atm_vcc->dev_data = (void *) vcc; ++ ++ // no failures beyond this point ++ ++ // we are not really "immediately before allocating the connection ++ // identifier in hardware", but it will just have to do! ++ atm_vcc->flags |= ATM_VF_ADDR; ++ ++ if (txtp->traffic_class != ATM_NONE) { ++ command cmd; ++ ++ vcc->tx_frame_bits = tx_frame_bits; ++ ++ down (&dev->vcc_sf); ++ if (dev->rxer[vci]) { ++ // RXer on the channel already, just modify rate... ++ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); ++ cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0 ++ // only lower 16 bits used (BE nightmare continues) ++ cmd.args.modify_rate.rate = cpu_to_be16 (tx_rate_bits); ++ while (command_do (dev, &cmd)) ++ schedule(); ++ // ... and TX flags, preserving the RX pool ++ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); ++ cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 ++ // only lower 16 bits used (BE nightmare continues) ++ cmd.args.modify_flags.flags = cpu_to_be16 ++ ((AMB_VCC(dev->rxer[vci])->rx_info.pool << SRB_POOL_SHIFT) | tx_vc_bits); ++ while (command_do (dev, &cmd)) ++ schedule(); ++ } else { ++ // no RXer on the channel, just open (with pool zero) ++ cmd.request = cpu_to_be32 (SRB_OPEN_VC); ++ cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0 ++ // only lower 16 bits used (BE nightmare continues) ++ cmd.args.open.flags = cpu_to_be16 (tx_vc_bits); ++ cmd.args.open.rate = cpu_to_be16 (tx_rate_bits); ++ while (command_do (dev, &cmd)) ++ schedule(); ++ } ++ dev->txer[vci].tx_present = 1; ++ up (&dev->vcc_sf); ++ } ++ ++ if (rxtp->traffic_class != ATM_NONE) { ++ command cmd; ++ ++ vcc->rx_info.pool = pool; ++ ++ down (&dev->vcc_sf); ++ ++ /* grow RX buffer pool */ ++ if (!dev->rxq[pool].buffers_wanted) ++ dev->rxq[pool].buffers_wanted = rx_lats; ++ dev->rxq[pool].buffers_wanted += 1; ++ fill_rx_pool (dev, pool, GFP_KERNEL); ++ ++ if (dev->txer[vci].tx_present) { ++ // TXer on the channel already ++ // switch (from pool zero) to this pool, preserving the TX bits ++ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); ++ cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 ++ // only lower 16 bits used (BE nightmare continues) ++ cmd.args.modify_flags.flags = cpu_to_be16 ++ ((pool << SRB_POOL_SHIFT) | dev->txer[vci].tx_vc_bits); ++ } else { ++ // no TXer on the channel, open the VC (with no rate info) ++ cmd.request = cpu_to_be32 (SRB_OPEN_VC); ++ cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0 ++ // only lower 16 bits used in the next two (BE nightmare continues) ++ cmd.args.open.flags = cpu_to_be16 (pool << SRB_POOL_SHIFT); ++ cmd.args.open.rate = cpu_to_be16 (0); ++ } ++ while (command_do (dev, &cmd)) ++ schedule(); ++ // this link allows RX frames through ++ dev->rxer[vci] = atm_vcc; ++ up (&dev->vcc_sf); ++ } ++ ++ // set elements of vcc ++ atm_vcc->vpi = vpi; // 0 ++ atm_vcc->vci = vci; ++ ++ // indicate readiness ++ atm_vcc->flags |= ATM_VF_READY; ++ ++ MOD_INC_USE_COUNT; ++ return 0; ++} + ++/********** Close a VC **********/ + -+#ifndef CONFIG_ATM_ENI_DEBUG -+ -+ -+#define NULLCHECK(x) -+ -+#define EVENT(s,a,b) -+ ++static void amb_close (struct atm_vcc * atm_vcc) { ++ amb_dev * dev = AMB_DEV (atm_vcc->dev); ++ amb_vcc * vcc = AMB_VCC (atm_vcc); ++ u16 vci = atm_vcc->vci; ++ ++ PRINTD (DBG_VCC|DBG_FLOW, "amb_close"); ++ ++ // indicate unreadiness ++ atm_vcc->flags &= ~ATM_VF_READY; ++ ++ // disable TXing ++ if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) { ++ command cmd; ++ ++ down (&dev->vcc_sf); ++ if (dev->rxer[vci]) { ++ // RXer still on the channel, just modify rate... XXX not really needed ++ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); ++ cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0 ++ // only lower 16 bits used (BE nightmare continues) ++ cmd.args.modify_rate.rate = cpu_to_be16 (0); ++ // ... and clear TX rate flags (XXX to stop RM cell output?), preserving RX pool ++ } else { ++ // no RXer on the channel, close channel ++ cmd.request = cpu_to_be32 (SRB_CLOSE_VC); ++ cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0 ++ } ++ dev->txer[vci].tx_present = 0; ++ while (command_do (dev, &cmd)) ++ schedule(); ++ up (&dev->vcc_sf); ++ } ++ ++ // disable RXing ++ if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) { ++ command cmd; ++ ++ // this is (the?) one reason why we need the amb_vcc struct ++ unsigned char pool = vcc->rx_info.pool; ++ ++ down (&dev->vcc_sf); ++ if (dev->txer[vci].tx_present) { ++ // TXer still on the channel, just go to pool zero XXX not really needed ++ cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); ++ cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 ++ // only lower 16 bits used (BE nightmare continues) ++ cmd.args.modify_flags.flags = cpu_to_be16 (dev->txer[vci].tx_vc_bits); ++ } else { ++ // no TXer on the channel, close the VC ++ cmd.request = cpu_to_be32 (SRB_CLOSE_VC); ++ cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0 ++ } ++ // forget the rxer - no more skbs will be pushed ++ if (atm_vcc != dev->rxer[vci]) ++ PRINTK (KERN_ERR, "%s vcc=%p rxer[vci]=%p", ++ "arghhh! we're going to die!", ++ vcc, dev->rxer[vci]); ++ dev->rxer[vci] = 0; ++ while (command_do (dev, &cmd)) ++ schedule(); ++ ++ /* shrink RX buffer pool */ ++ dev->rxq[pool].buffers_wanted -= 1; ++ if (dev->rxq[pool].buffers_wanted == rx_lats) { ++ dev->rxq[pool].buffers_wanted = 0; ++ drain_rx_pool (dev, pool); ++ } + -+static void event_dump(void) -+{ ++ up (&dev->vcc_sf); ++ } ++ ++ // free our structure ++ kfree (vcc); ++ ++ // say the VPI/VCI is free again ++ atm_vcc->flags &= ~ATM_VF_ADDR; ++ MOD_DEC_USE_COUNT; +} + ++/********** DebugIoctl **********/ + -+#else ++#if 0 ++static int amb_ioctl (struct atm_dev * dev, unsigned int cmd, void * arg) { ++ unsigned short newdebug; ++ if (cmd == AMB_SETDEBUG) { ++ if (copy_from_user (&newdebug, arg, sizeof(newdebug))) { ++ // moan ++ return -EFAULT; ++ } else { ++ debug = newdebug; ++ return 0; ++ } ++ } else if (cmd == AMB_DONTPANIC) { ++ dont_panic (dev); ++ } else { ++ // moan ++ return -EINVAL; ++ } ++} ++#endif + ++/********** Set socket options for a VC **********/ + -+/* -+ * NULL pointer checking -+ */ ++// int amb_getsockopt (struct atm_vcc * atm_vcc, int level, int optname, void * optval, int optlen); + -+#define NULLCHECK(x) \ -+ if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x)) ++/********** Set socket options for a VC **********/ + -+/* -+ * Very extensive activity logging. Greatly improves bug detection speed but -+ * costs a few Mbps if enabled. -+ */ ++// int amb_setsockopt (struct atm_vcc * atm_vcc, int level, int optname, void * optval, int optlen); + -+#define EV 64 ++/********** Send **********/ + -+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 int amb_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) { ++ amb_dev * dev = AMB_DEV(atm_vcc->dev); ++ amb_vcc * vcc = AMB_VCC(atm_vcc); ++ u16 vc = atm_vcc->vci; ++ unsigned int tx_len = skb->len; ++ unsigned char * tx_data = skb->data; ++ tx_simple * tx_descr; ++ tx_in tx; ++ ++ if (test_bit (dead, &dev->flags)) ++ return -EIO; ++ ++ PRINTD (DBG_FLOW|DBG_TX, "amb_send vc %x data %p len %u", ++ vc, tx_data, tx_len); ++ ++ dump_skb (">>>", vc, skb); ++ ++ if (!dev->txer[vc].tx_present) { ++ PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", vc); ++ return -EBADFD; ++ } ++ ++ // this is a driver private field so we have to set it ourselves, ++ // despite the fact that we are _required_ to use it to check for a ++ // pop function ++ ATM_SKB(skb)->vcc = atm_vcc; ++ ++ if (skb->len > (size_t) atm_vcc->qos.txtp.max_sdu) { ++ PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping..."); ++ return -EIO; ++ } ++ ++ if (check_area (skb->data, skb->len)) { ++ atm_vcc->stats->tx_err++; ++ return -ENOMEM; // ? ++ } ++ ++ // allocate memory for fragments ++ tx_descr = kmalloc (sizeof(tx_simple), GFP_KERNEL); ++ if (!tx_descr) { ++ PRINTK (KERN_ERR, "could not allocate TX descriptor"); ++ return -ENOMEM; ++ } ++ if (check_area (tx_descr, sizeof(tx_simple))) { ++ kfree (tx_descr); ++ return -ENOMEM; ++ } ++ PRINTD (DBG_TX, "fragment list allocated at %p", tx_descr); ++ ++ tx_descr->skb = skb; ++ ++ tx_descr->tx_frag.bytes = cpu_to_be32 (tx_len); ++ tx_descr->tx_frag.address = cpu_to_be32 (virt_to_bus (tx_data)); ++ ++ tx_descr->tx_frag_end.handle = virt_to_bus (tx_descr); ++ tx_descr->tx_frag_end.vc = 0; ++ tx_descr->tx_frag_end.next_descriptor_length = 0; ++ tx_descr->tx_frag_end.next_descriptor = 0; ++#ifdef AMB_NEW_MICROCODE ++ tx_descr->tx_frag_end.cpcs_uu = 0; ++ tx_descr->tx_frag_end.cpi = 0; ++ tx_descr->tx_frag_end.pad = 0; ++#endif ++ ++ tx.vc = cpu_to_be16 (vcc->tx_frame_bits | vc); ++ tx.tx_descr_length = cpu_to_be16 (sizeof(tx_frag)+sizeof(tx_frag_end)); ++ tx.tx_descr_addr = cpu_to_be32 (virt_to_bus (&tx_descr->tx_frag)); ++ ++#ifdef DEBUG_AMBASSADOR ++ /* wey-hey! */ ++ if (vc == 1023) { ++ unsigned int i; ++ unsigned short d = 0; ++ char * s = skb->data; ++ switch (*s++) { ++ case 'D': { ++ for (i = 0; i < 4; ++i) { ++ d = (d<<4) | ((*s <= '9') ? (*s - '0') : (*s - 'a' + 10)); ++ ++s; ++ } ++ PRINTK (KERN_INFO, "debug bitmap is now %hx", debug = d); ++ break; ++ } ++ case 'R': { ++ if (*s++ == 'e' && *s++ == 's' && *s++ == 'e' && *s++ == 't') ++ dont_panic (dev); ++ break; ++ } ++ default: { ++ break; ++ } ++ } ++ } ++#endif ++ ++ while (tx_give (dev, &tx)) ++ schedule(); ++ return 0; ++} ++ ++/********** Scatter Gather Send Capability **********/ ++ ++static int amb_sg_send (struct atm_vcc * atm_vcc, ++ unsigned long start, ++ unsigned long size) { ++ PRINTD (DBG_FLOW|DBG_VCC, "amb_sg_send: never"); ++ return 0; ++ if (atm_vcc->qos.aal == ATM_AAL5) { ++ PRINTD (DBG_FLOW|DBG_VCC, "amb_sg_send: yes"); ++ return 1; ++ } else { ++ PRINTD (DBG_FLOW|DBG_VCC, "amb_sg_send: no"); ++ return 0; ++ } ++ PRINTD (DBG_FLOW|DBG_VCC, "amb_sg_send: always"); ++ return 1; +} + ++/********** Send OAM **********/ + -+static void event_dump(void) -+{ -+ int n,i; ++// static int amb_send_oam (struct atm_vcc * atm_vcc, void * cell, int flags); + -+ 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]); -+ } -+} ++/********** Feedback to Driver **********/ + ++// void amb_feedback (struct atm_vcc * atm_vcc, struct sk_buff * skb, ++// unsigned long start, unsigned long dest, int len); + -+#endif /* CONFIG_ATM_ENI_DEBUG */ ++/********** Change QoS on a VC **********/ + ++// int amb_change_qos (struct atm_vcc * atm_vcc, struct atm_qos * qos, int flags); + -+/* -+ * 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 -+ */ ++/********** Free RX Socket Buffer **********/ + -+#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) ++#if 0 ++static void amb_free_rx_skb (struct atm_vcc * atm_vcc, struct sk_buff * skb) { ++ amb_dev * dev = AMB_DEV (atm_vcc->dev); ++ amb_vcc * vcc = AMB_VCC (atm_vcc); ++ unsigned char pool = vcc->rx_info.pool; ++ rx_in rx; ++ ++ // This may be unsafe for various reasons that I cannot really guess ++ // at. However, I note that the ATM layer calls kfree_skb rather ++ // than dev_kfree_skb at this point so we are least covered as far ++ // as buffer locking goes. There may be bugs if pcap clones RX skbs. ++ ++ PRINTD (DBG_FLOW|DBG_SKB, "amb_rx_free skb %p (atm_vcc %p, vcc %p)", ++ skb, atm_vcc, vcc); ++ ++ rx.handle = virt_to_bus (skb); ++ rx.host_address = cpu_to_be32 (virt_to_bus (skb->data)); ++ ++ skb->data = skb->head; ++ skb->tail = skb->head; ++ skb->len = 0; ++ ++ if (!rx_give (dev, &rx, pool)) { ++ // success ++ PRINTD (DBG_SKB|DBG_POOL, "recycled skb for pool %hu", pool); ++ return; ++ } ++ ++ // just do what the ATM layer would have done ++ kfree_skb (skb); ++ ++ return; ++} ++#endif + ++/********** Proc File Output **********/ + -+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 int amb_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) { ++ amb_dev * dev = AMB_DEV (atm_dev); ++ int left = *pos; ++ unsigned char pool; ++ ++ PRINTD (DBG_FLOW, "amb_proc_read"); ++ ++ /* more diagnostics here? */ ++ ++ if (!left--) { ++ amb_stats * s = &dev->stats; ++ return sprintf (page, ++ "frames: TX OK %lu, RX OK %lu, RX bad %lu " ++ "(CRC %lu, long %lu, aborted %lu, unused %lu).\n", ++ s->tx_ok, s->rx.ok, s->rx.error, ++ s->rx.badcrc, s->rx.toolong, ++ s->rx.aborted, s->rx.unused); ++ } ++ ++ if (!left--) { ++ amb_cq * c = &dev->cq; ++ return sprintf (page, "cmd queue [cur/hi/max]: %u/%u/%u. ", ++ c->pending, c->high, c->maximum); ++ } ++ ++ if (!left--) { ++ amb_txq * t = &dev->txq; ++ return sprintf (page, "TX queue [cur/max high full]: %u/%u %u %u.\n", ++ t->pending, t->maximum, t->high, t->filled); ++ } ++ ++ if (!left--) { ++ unsigned int count = sprintf (page, "RX queues [cur/max/req low empty]:"); ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) { ++ amb_rxq * r = &dev->rxq[pool]; ++ count += sprintf (page+count, " %u/%u/%u %u %u", ++ r->pending, r->maximum, r->buffers_wanted, r->low, r->emptied); ++ } ++ count += sprintf (page+count, ".\n"); ++ return count; ++ } ++ ++ if (!left--) { ++ unsigned int count = sprintf (page, "RX buffer sizes:"); ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) { ++ amb_rxq * r = &dev->rxq[pool]; ++ count += sprintf (page+count, " %u", r->buffer_size); ++ } ++ count += sprintf (page+count, ".\n"); ++ return count; ++ } ++ ++#if 0 ++ if (!left--) { ++ // suni block etc? ++ } ++#endif ++ ++ return 0; ++} + -+static struct atm_dev *eni_boards = NULL; ++/********** Operation Structure **********/ + -+static u32 *zeroes = NULL; /* aligned "magic" zeroes */ ++static const struct atmdev_ops amb_ops = { ++ NULL, // no amb_dev_close ++ amb_open, ++ amb_close, ++ NULL, // no amb_ioctl, ++ NULL, // amb_getsockopt, ++ NULL, // amb_setsockopt, ++ amb_send, ++ amb_sg_send, ++ NULL, // no send_oam - not in fact used yet ++ NULL, // no amb_phy_put - not needed in this driver ++ NULL, // no amb_phy_get - not needed in this driver ++ NULL, // no feedback - feedback to the driver! ++ NULL, // no amb_change_qos ++ NULL, // amb_free_rx_skb not used until checked by someone else ++ amb_proc_read ++}; ++ ++/********** housekeeping **********/ ++ ++static inline void set_timer (struct timer_list * timer, unsigned long delay) { ++ timer->expires = jiffies + delay; ++ add_timer (timer); ++ return; ++} ++ ++static void do_housekeeping (unsigned long arg) { ++ amb_dev * dev = amb_devs; ++ // data is set to zero at module unload ++ (void) arg; ++ ++ if (housekeeping.data) { ++ while (dev) { ++ ++ // could collect device-specific (not driver/atm-linux) stats here ++ ++ // last resort refill once every ten seconds ++ fill_rx_pools (dev); ++ ++ dev = dev->prev; ++ } ++ set_timer (&housekeeping, 10*HZ); ++ } ++ ++ return; ++} + ++/********** creation of communication queues **********/ + -+/*-------------------------------- utilities --------------------------------*/ ++static int create_queues (amb_dev * dev, unsigned int cmds, unsigned int txs, ++ unsigned int * rxs, unsigned int * rx_buffer_sizes) { ++ unsigned char pool; ++ size_t total = 0; ++ void * memory; ++ void * limit; ++ ++ PRINTD (DBG_FLOW, "create_queues %p", dev); ++ ++ total += cmds * sizeof(command); ++ ++ total += txs * (sizeof(tx_in) + sizeof(tx_out)); ++ ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ total += rxs[pool] * (sizeof(rx_in) + sizeof(rx_out)); ++ ++ memory = kmalloc (total, GFP_KERNEL); ++ if (!memory) { ++ PRINTK (KERN_ERR, "could not allocate queues"); ++ return -ENOMEM; ++ } ++ if (check_area (memory, total)) { ++ PRINTK (KERN_ERR, "queues allocated in nasty area"); ++ kfree (memory); ++ return -ENOMEM; ++ } ++ ++ limit = memory + total; ++ PRINTD (DBG_INIT, "queues from %p to %p", memory, limit); ++ ++ PRINTD (DBG_CMD, "command queue at %p", memory); ++ ++ { ++ command * cmd = memory; ++ amb_cq * cq = &dev->cq; ++ ++ cq->pending = 0; ++ cq->high = 0; ++ cq->maximum = cmds - 1; ++ ++ cq->start = cmd; ++ cq->in = cmd; ++ cq->out = cmd; ++ cq->limit = cmd + cmds; ++ ++ memory = cq->limit; ++ } ++ ++ PRINTD (DBG_TX, "TX queue pair at %p", memory); ++ ++ { ++ tx_in * in = memory; ++ tx_out * out; ++ amb_txq * txq = &dev->txq; ++ ++ txq->pending = 0; ++ txq->high = 0; ++ txq->filled = 0; ++ txq->maximum = txs - 1; ++ ++ txq->in.start = in; ++ txq->in.ptr = in; ++ txq->in.limit = in + txs; ++ ++ memory = txq->in.limit; ++ out = memory; ++ ++ txq->out.start = out; ++ txq->out.ptr = out; ++ txq->out.limit = out + txs; ++ ++ memory = txq->out.limit; ++ } ++ ++ PRINTD (DBG_RX, "RX queue pairs at %p", memory); ++ ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) { ++ rx_in * in = memory; ++ rx_out * out; ++ amb_rxq * rxq = &dev->rxq[pool]; ++ ++ rxq->buffer_size = rx_buffer_sizes[pool]; ++ rxq->buffers_wanted = 0; ++ ++ rxq->pending = 0; ++ rxq->low = rxs[pool] - 1; ++ rxq->emptied = 0; ++ rxq->maximum = rxs[pool] - 1; ++ ++ rxq->in.start = in; ++ rxq->in.ptr = in; ++ rxq->in.limit = in + rxs[pool]; ++ ++ memory = rxq->in.limit; ++ out = memory; ++ ++ rxq->out.start = out; ++ rxq->out.ptr = out; ++ rxq->out.limit = out + rxs[pool]; ++ ++ memory = rxq->out.limit; ++ } ++ ++ if (memory == limit) { ++ return 0; ++ } else { ++ PRINTK (KERN_ERR, "bad queue alloc %p != %p (tell maintainer)", memory, limit); ++ kfree (limit - total); ++ return -ENOMEM; ++ } ++ ++} + ++/********** destruction of communication queues **********/ + -+static void dump_mem(struct eni_dev *eni_dev) -+{ -+ int i; ++static void destroy_queues (amb_dev * dev) { ++ // all queues assumed empty ++ void * memory = dev->cq.start; ++ // includes txq.in, txq.out, rxq[].in and rxq[].out ++ ++ PRINTD (DBG_FLOW, "destroy_queues %p", dev); ++ ++ PRINTD (DBG_INIT, "freeing queues at %p", memory); ++ kfree (memory); ++ ++ return; ++} + -+ 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); ++/********** basic loader commands and error handling **********/ ++ ++static int do_loader_command (const amb_dev * dev, loader_command cmd, ++ volatile loader_block * lb) { ++ ++ // centisecond timeouts - guessing away here ++ unsigned int command_timeouts [] = { ++ [host_memory_test] = 15, ++ [read_adapter_memory] = 2, ++ [write_adapter_memory] = 2, ++ [adapter_start] = 50, ++ [get_version_number] = 10, ++ [interrupt_host] = 1, ++ [flash_erase_sector] = 1, ++ [adap_download_block] = 1, ++ [adap_erase_flash] = 1, ++ [adap_run_in_iram] = 1, ++ [adap_end_download] = 1 ++ }; ++ ++ unsigned int command_successes [] = { ++ [host_memory_test] = COMMAND_PASSED_TEST, ++ [read_adapter_memory] = COMMAND_READ_DATA_OK, ++ [write_adapter_memory] = COMMAND_WRITE_DATA_OK, ++ [adapter_start] = COMMAND_COMPLETE, ++ [get_version_number] = COMMAND_COMPLETE, ++ [interrupt_host] = COMMAND_COMPLETE, ++ [flash_erase_sector] = COMMAND_COMPLETE, ++ [adap_download_block] = COMMAND_COMPLETE, ++ [adap_erase_flash] = COMMAND_COMPLETE, ++ [adap_run_in_iram] = COMMAND_COMPLETE, ++ [adap_end_download] = COMMAND_COMPLETE ++ }; ++ ++ int decode_loader_error (u32 result) { ++ int res; ++ const char * msg; ++ switch (result) { ++ case BAD_COMMAND: ++ res = -EINVAL; ++ msg = "bad command"; ++ break; ++ case COMMAND_IN_PROGRESS: ++ res = -ETIMEDOUT; ++ msg = "command in progress"; ++ break; ++ case COMMAND_PASSED_TEST: ++ res = 0; ++ msg = "command passed test"; ++ break; ++ case COMMAND_FAILED_TEST: ++ res = -EIO; ++ msg = "command failed test"; ++ break; ++ case COMMAND_READ_DATA_OK: ++ res = 0; ++ msg = "command read data ok"; ++ break; ++ case COMMAND_READ_BAD_ADDRESS: ++ res = -EINVAL; ++ msg = "command read bad address"; ++ break; ++ case COMMAND_WRITE_DATA_OK: ++ res = 0; ++ msg = "command write data ok"; ++ break; ++ case COMMAND_WRITE_BAD_ADDRESS: ++ res = -EINVAL; ++ msg = "command write bad address"; ++ break; ++ case COMMAND_WRITE_FLASH_FAILURE: ++ res = -EIO; ++ msg = "command write flash failure"; ++ break; ++ case COMMAND_COMPLETE: ++ res = 0; ++ msg = "command complete"; ++ break; ++ case COMMAND_FLASH_ERASE_FAILURE: ++ res = -EIO; ++ msg = "command flash erase failure"; ++ break; ++ case COMMAND_WRITE_BAD_DATA: ++ res = -EINVAL; ++ msg = "command write bad data"; ++ break; ++ default: ++ res = -EINVAL; ++ msg = "unknown error"; ++ PRINTD (DBG_LOAD|DBG_ERR, "decode_loader_error got %d=%x !", ++ result, result); ++ break; ++ } ++ if (res) ++ PRINTK (KERN_ERR, "%s", msg); ++ return res; ++ } ++ ++ unsigned long timeout; ++ ++ PRINTD (DBG_FLOW|DBG_LOAD, "do_loader_command"); ++ ++ /* do a command ++ ++ Set the return value to zero, set the command type and set the ++ valid entry to the right magic value. The payload is already ++ correctly byte-ordered so we leave it alone. Hit the doorbell ++ with the bus address of this structure. ++ ++ */ ++ ++ lb->result = 0; ++ lb->command = cpu_to_be32 (cmd); ++ lb->valid = cpu_to_be32 (DMA_VALID); ++ // dump_loader_block (lb); ++ wr_mem (dev, &mem->doorbell, virt_to_bus (lb)); ++ ++ timeout = command_timeouts[cmd] * HZ/100; ++ ++ while (!lb->result || lb->result == be32_to_cpu (COMMAND_IN_PROGRESS)) ++ if (timeout) { ++ timeout = schedule_timeout (timeout); ++ } else { ++ PRINTD (DBG_LOAD|DBG_ERR, "command %d timed out", cmd); ++ dump_registers (dev); ++ dump_loader_block (lb); ++ return -ETIMEDOUT; ++ } ++ ++ if (cmd == adapter_start) { ++ // wait for start command to acknowledge... ++ timeout = HZ/10; ++ while (rd_mem (dev, &mem->doorbell)) ++ if (timeout) { ++ timeout = schedule_timeout (timeout); ++ } else { ++ PRINTD (DBG_LOAD|DBG_ERR, "start command did not clear doorbell, res=%08x", ++ be32_to_cpu (lb->result)); ++ dump_registers (dev); ++ return -ETIMEDOUT; ++ } ++ return 0; ++ } else { ++ return decode_loader_error (be32_to_cpu (lb->result)); ++ } ++ ++#if 0 ++ if ((res != COMMAND_PASSED_TEST) && ++ (res != COMMAND_READ_DATA_OK) && ++ (res != COMMAND_WRITE_DATA_OK) && ++ (res != COMMAND_COMPLETE)) { ++ PRINTD (DBG_LOAD|DBG_ERR"startup cmd %d failed with error %08x", ++ cmd, res); ++ dump_registers (dev); ++ return -EIO; ++ } ++#endif +} + ++/* loader: determine loader version */ + -+static void dump(struct atm_dev *dev) -+{ -+ struct eni_dev *eni_dev; ++static int get_loader_version (const amb_dev * dev, u32 * version) { ++ loader_block lb; ++ int res; ++ ++ PRINTD (DBG_FLOW|DBG_LOAD, "get_loader_version"); ++ ++ res = do_loader_command (dev, get_version_number, &lb); ++ if (res) ++ return res; ++ if (version) ++ *version = be32_to_cpu (lb.payload.version); ++ return 0; ++} + -+ int i; ++/* loader: read or verify memory data blocks */ ++ ++static int loader_write (const amb_dev * dev, const u32 * data, ++ u32 address, unsigned int count) { ++ unsigned int i; ++ loader_block lb; ++ transfer_block * tb = &lb.payload.transfer; ++ ++ PRINTD (DBG_FLOW|DBG_LOAD, "loader_write"); ++ ++ if (count > MAX_TRANSFER_DATA) ++ return -EINVAL; ++ tb->address = cpu_to_be32 (address); ++ tb->count = cpu_to_be32 (count); ++ for (i = 0; i < count; ++i) ++ tb->data[i] = cpu_to_be32 (data[i]); ++ return do_loader_command (dev, write_adapter_memory, &lb); ++} + -+ 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%p: %ld\n",i, -+ 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%p: %ld\n",i, -+ ENI_VCC(eni_dev->rx_map[i])->recv, -+ ENI_VCC(eni_dev->rx_map[i])->words*4); -+ printk(KERN_NOTICE "----\n"); ++static int loader_verify (const amb_dev * dev, const u32 * data, ++ u32 address, unsigned int count) { ++ unsigned int i; ++ loader_block lb; ++ transfer_block * tb = &lb.payload.transfer; ++ int res; ++ ++ PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify"); ++ ++ if (count > MAX_TRANSFER_DATA) ++ return -EINVAL; ++ tb->address = cpu_to_be32 (address); ++ tb->count = cpu_to_be32 (count); ++ res = do_loader_command (dev, read_adapter_memory, &lb); ++ if (!res) ++ for (i = 0; i < count; ++i) ++ if (tb->data[i] != cpu_to_be32 (data[i])) { ++ res = -EINVAL; ++ break; ++ } ++ return res; +} + ++static int loader_start (const amb_dev * dev, u32 address) { ++ loader_block lb; ++ ++ PRINTD (DBG_FLOW|DBG_LOAD, "loader_start"); ++ ++ lb.payload.start = cpu_to_be32 (address); ++ return do_loader_command (dev, adapter_start, &lb); ++} + -+static void eni_put_free(struct eni_dev *eni_dev,unsigned long start, -+ unsigned long size) -+{ -+ struct eni_free *list; -+ int len,order; ++/********** reset card **********/ + -+ 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 int amb_reset (amb_dev * dev, int diags) { ++ u32 word; ++ ++ PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset"); ++ ++ word = rd_mem (dev, &mem->reset_control); ++#if 0 ++ // clear all interrupts just in case ++ wr_mem (dev, &mem->interrupt, -1); ++#endif ++ // put card into reset state ++ wr_mem (dev, &mem->reset_control, word | AMB_RESET); ++ // wait a short while ++ udelay (10); ++ // clear self-test done flag ++ wr_mem (dev, &mem->mb.loader.ready, 0); ++ // take card out of reset state ++ wr_mem (dev, &mem->reset_control, word &~ AMB_RESET); ++ ++ if (diags) { ++ unsigned long timeout; ++ // 4.2 second wait ++ timeout = HZ*42/10; ++ while (timeout) ++ timeout = schedule_timeout (timeout); ++ // half second time-out ++ timeout = HZ/2; ++ while (!rd_mem (dev, &mem->mb.loader.ready)) ++ if (timeout) { ++ timeout = schedule_timeout (timeout); ++ } else { ++ PRINTD (DBG_LOAD|DBG_ERR, "reset timed out"); ++ return -ETIMEDOUT; ++ } ++ ++ // get results of self-test ++ word = rd_mem (dev, &mem->mb.loader.result); ++ if (word & SELF_TEST_FAILURE) { ++ void sf (const char * msg) { ++ PRINTK (KERN_ERR, "self-test failed: %s", msg); ++ } ++ if (word & GPINT_TST_FAILURE) ++ sf ("interrupt"); ++ if (word & SUNI_DATA_PATTERN_FAILURE) ++ sf ("SUNI data pattern"); ++ if (word & SUNI_DATA_BITS_FAILURE) ++ sf ("SUNI data bits"); ++ if (word & SUNI_UTOPIA_FAILURE) ++ sf ("SUNI UTOPIA interface"); ++ if (word & SUNI_FIFO_FAILURE) ++ sf ("SUNI cell buffer FIFO"); ++ if (word & SRAM_FAILURE) ++ sf ("bad SRAM"); ++ // better return value? ++ return -EIO; ++ } ++ ++ } ++ return 0; +} + ++/********** transfer and start the microcode **********/ + -+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; ++static int ucode_init (amb_dev * dev) { ++ unsigned int i = 0; ++ unsigned int total = 0; ++ const u32 * pointer = ucode_data; ++ u32 address; ++ unsigned int count; ++ int res; ++ ++ PRINTD (DBG_FLOW|DBG_LOAD, "ucode_init"); ++ ++ while (address = ucode_regions[i].start, ++ count = ucode_regions[i].count) { ++ PRINTD (DBG_LOAD, "starting region (%x, %u)", address, count); ++ while (count) { ++ unsigned int words; ++ if (count <= MAX_TRANSFER_DATA) ++ words = count; ++ else ++ words = MAX_TRANSFER_DATA; ++ total += words; ++ res = loader_write (dev, pointer, address, words); ++ if (res) ++ return res; ++ res = loader_verify (dev, pointer, address, words); ++ if (res) ++ return res; ++ count -= words; ++ address += sizeof(u32) * words; ++ pointer += words; ++ } ++ i += 1; ++ } ++ if (*pointer == 0xdeadbeef) { ++ return loader_start (dev, ucode_start); ++ } else { ++ // cast needed as there is no %? for pointer differnces ++ PRINTD (DBG_LOAD|DBG_ERR, ++ "offset=%li, *pointer=%x, address=%x, total=%u", ++ (long) (pointer - ucode_data), *pointer, address, total); ++ PRINTK (KERN_ERR, "incorrect microcode data"); ++ return -ENOMEM; ++ } ++} + -+ 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; ++/********** give adapter parameters **********/ ++ ++static int amb_talk (amb_dev * dev) { ++ adap_talk_block a; ++ unsigned char pool; ++ unsigned long timeout; ++ ++ inline u32 x (void * addr) { ++ return cpu_to_be32 (virt_to_bus (addr)); ++ } ++ ++ PRINTD (DBG_FLOW, "amb_talk %p", dev); ++ ++ a.command_start = x (dev->cq.start); ++ a.command_end = x (dev->cq.limit); ++ a.tx_start = x (dev->txq.in.start); ++ a.tx_end = x (dev->txq.in.limit); ++ a.txcom_start = x (dev->txq.out.start); ++ a.txcom_end = x (dev->txq.out.limit); ++ ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) { ++ // the other "a" items are set up by the adapter ++ a.rec_struct[pool].buffer_start = x (dev->rxq[pool].in.start); ++ a.rec_struct[pool].buffer_end = x (dev->rxq[pool].in.limit); ++ a.rec_struct[pool].rx_start = x (dev->rxq[pool].out.start); ++ a.rec_struct[pool].rx_end = x (dev->rxq[pool].out.limit); ++ a.rec_struct[pool].buffer_size = cpu_to_be32 (dev->rxq[pool].buffer_size); ++ } ++ ++#ifdef AMB_NEW_MICROCODE ++ // disable fast PLX prefetching ++ a.init_flags = 0; ++#endif ++ ++ // pass the structure ++ wr_mem (dev, &mem->doorbell, virt_to_bus (&a)); ++ ++ // 2.2 second wait (must not touch doorbell during 2 second DMA test) ++ timeout = HZ*22/10; ++ while (timeout) ++ timeout = schedule_timeout (timeout); ++ // give the adapter another half second? ++ timeout = HZ/2; ++ while (rd_mem (dev, &mem->doorbell)) ++ if (timeout) { ++ timeout = schedule_timeout (timeout); ++ } else { ++ PRINTD (DBG_INIT|DBG_ERR, "adapter init timed out"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; +} + ++// get microcode version ++static void amb_ucode_version (amb_dev * dev) { ++ u32 major; ++ u32 minor; ++ command cmd; ++ cmd.request = cpu_to_be32 (SRB_GET_VERSION); ++ while (command_do (dev, &cmd)) ++ schedule(); ++ major = be32_to_cpu (cmd.args.version.major); ++ minor = be32_to_cpu (cmd.args.version.minor); ++ PRINTK (KERN_INFO, "microcode version is %u.%u", major, minor); ++} ++ ++// get end station address ++static void amb_esi (amb_dev * dev, u8 * esi) { ++ u32 lower4; ++ u16 upper2; ++ command cmd; ++ ++ // swap bits within byte to get Ethernet ordering ++ u8 bit_swap (u8 byte) { ++ const u8 swap[] = { ++ 0x0, 0x8, 0x4, 0xc, ++ 0x2, 0xa, 0x6, 0xe, ++ 0x1, 0x9, 0x5, 0xd, ++ 0x3, 0xb, 0x7, 0xf ++ }; ++ return ((swap[byte & 0xf]<<4) | swap[byte>>4]); ++ } + -+static void eni_free_mem(struct eni_dev *eni_dev,unsigned long start, -+ unsigned long size) -+{ -+ struct eni_free *list; -+ int len,i,order; ++ cmd.request = cpu_to_be32 (SRB_GET_BIA); ++ while (command_do (dev, &cmd)) ++ schedule(); ++ lower4 = be32_to_cpu (cmd.args.bia.lower4); ++ upper2 = be32_to_cpu (cmd.args.bia.upper2); ++ PRINTD (DBG_LOAD, "BIA: lower4: %08x, upper2 %04x", lower4, upper2); + -+ 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 &= ~(unsigned long) (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);*/ ++ if (esi) { ++ unsigned int i; ++ ++ PRINTDB (DBG_INIT, "ESI:"); ++ for (i = 0; i < ESI_LEN; ++i) { ++ if (i < 4) ++ esi[i] = bit_swap (lower4>>(8*i)); ++ else ++ esi[i] = bit_swap (upper2>>(8*(i-4))); ++ PRINTDM (DBG_INIT, " %02x", esi[i]); ++ } ++ ++ PRINTDE (DBG_INIT, ""); ++ } ++ ++ return; +} + ++static int amb_init (amb_dev * dev) { ++ u32 version; ++ ++ /* enable adapter doorbell */ ++ wr_mem (dev, &mem->interrupt_control, ++ rd_mem (dev, &mem->interrupt_control) ++ | AMB_DOORBELL_BITS); ++ ++ if (amb_reset (dev, 1)) { ++ PRINTK (KERN_ERR, "card reset failed!"); ++ } else if (get_loader_version (dev, &version)) { ++ PRINTK (KERN_INFO, "failed to get loader version"); ++ } else { ++ PRINTK (KERN_INFO, "loader version is %08x", version); ++ ++ if (ucode_init (dev)) { ++ PRINTK (KERN_ERR, "microcode failure"); ++ } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) { ++ PRINTK (KERN_ERR, "failed to get memory for queues"); ++ } else { ++ ++ if (amb_talk (dev)) { ++ PRINTK (KERN_ERR, "adapter did not accept queues"); ++ } else { ++ ++ amb_ucode_version (dev); ++ return 0; ++ } /* amb_talk */ ++ ++ destroy_queues (dev); ++ } /* create_queues, ucode_init */ ++ ++ } /* get_loader_version, amb_reset */ ++ ++ return -1; ++} + -+/*----------------------------------- RX ------------------------------------*/ ++static int __init amb_probe (void) { ++ struct pci_dev * pci_dev; ++ int devs; ++ ++ void do_pci_device (void) { ++ amb_dev * dev; ++ ++ // read resources from PCI configuration space ++ u32 * membase = bus_to_virt ++ (pci_dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK); ++ u32 iobase = pci_dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; ++ u8 irq = pci_dev->irq; ++ ++ // check IO region ++ if (check_region (iobase, AMB_EXTENT)) { ++ PRINTK (KERN_ERR, "IO range already in use!"); ++ return; ++ } ++ ++ dev = kmalloc (sizeof(amb_dev), GFP_KERNEL); ++ if (!dev) { ++ // perhaps we should be nice: deregister all adapters and abort? ++ PRINTK (KERN_ERR, "out of memory!"); ++ return; ++ } ++ memset (dev, 0, sizeof(amb_dev)); ++ ++ // set up known dev items straight away ++ dev->pci_dev = pci_dev; ++ ++ dev->iobase = iobase; ++ dev->irq = irq; ++ dev->membase = membase; ++ ++ // flags (currently only dead) ++ dev->flags = 0; ++ ++ // Allocate cell rates (fibre) ++ // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53 ++ // to be really pedantic, this should be ATM_OC3c_PCR ++ dev->tx_avail = ATM_OC3_PCR; ++ dev->rx_avail = ATM_OC3_PCR; ++ ++#if 0 ++ // initialise bottom half ++ dev->bh.next = 0; ++ dev->bh.sync = 0; ++ dev->bh.routine = (void (*)(void *)) fill_rx_pools; ++ dev->bh.data = dev; ++#endif ++ ++ // semaphore for txer/rxer modifications - we cannot use a ++ // spinlock as the critical region needs to process switches ++ dev->vcc_sf = MUTEX; ++ // queue manipulation spinlocks; we want atomic reads and ++ // writes to the queue descriptors (handles IRQ and SMP) ++ // consider replacing "int pending" -> "atomic_t available" ++ // => problem related to who gets to move queue pointers ++ spin_lock_init (&dev->cq.lock); ++ spin_lock_init (&dev->txq.lock); ++ { ++ unsigned char pool; ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ spin_lock_init (&dev->rxq[pool].lock); ++ } ++ ++ // grab (but share) IRQ and install handler ++ if (request_irq (irq, interrupt_handler, SA_SHIRQ, DEV_LABEL, dev)) { ++ PRINTK (KERN_ERR, "request IRQ failed!"); ++ // free_irq is at "endif" ++ } else { ++ ++ unsigned char lat; ++ ++ // reserve IO region ++ request_region (iobase, AMB_EXTENT, DEV_LABEL); ++ ++ PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at IO %x, IRQ %u, MEM %p", ++ iobase, irq, membase); ++ ++ // enable bus master accesses ++ pci_set_master (pci_dev); + ++ // frobnicate latency (upwards, usually) ++ pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat); ++ if (pci_lat) { ++ PRINTD (DBG_INIT, "%s PCI latency timer from %hu to %hu", ++ "changing", lat, pci_lat); ++ pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, pci_lat); ++ } else if (lat < MIN_PCI_LATENCY) { ++ PRINTK (KERN_INFO, "%s PCI latency timer from %hu to %hu", ++ "increasing", lat, MIN_PCI_LATENCY); ++ pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY); ++ } ++ ++ if (amb_init (dev)) { ++ PRINTK (KERN_ERR, "adapter initialisation failure"); ++ } else if (!(dev->atm_dev = atm_dev_register (DEV_LABEL, &amb_ops, -1, 0))) { ++ PRINTD (DBG_ERR, "failed to register Madge ATM adapter"); ++ } else { ++ ++ PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p", ++ dev->atm_dev->number, dev, dev->atm_dev); ++ dev->atm_dev->dev_data = (void *) dev; ++ ++ // register our address ++ amb_esi (dev, dev->atm_dev->esi); ++ ++ // 0 bits for vpi, 10 bits for vci ++ dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS; ++ dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS; ++ ++ fill_rx_pools (dev); ++ ++ /* enable host interrupts */ ++ interrupts_on (dev); ++ ++ // update count and linked list ++ ++devs; ++ dev->prev = amb_devs; ++ amb_devs = dev; ++ // success ++ return; ++ ++ // not currently reached ++ atm_dev_deregister (dev->atm_dev); ++ } /* atm_dev_register, amb_init */ ++ ++ release_region (iobase, AMB_EXTENT); ++ free_irq (irq, dev); ++ } /* request_region, request_irq */ ++ ++ kfree (dev); ++ } /* kmalloc, end-of-fn */ ++ ++ PRINTD (DBG_FLOW, "amb_probe"); ++ ++ if (!pci_present()) ++ return 0; ++ ++ devs = 0; ++ pci_dev = NULL; ++ while ((pci_dev = pci_find_device ++ (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR, pci_dev) ++ )) ++ do_pci_device(); ++ ++ pci_dev = NULL; ++ while ((pci_dev = pci_find_device ++ (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD, pci_dev) ++ )) ++ PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card"); ++ ++ return devs; ++} + -+#define ENI_VCC_NOS ((struct atm_vcc *) 1) ++static void __init amb_check_args (void) { ++ unsigned char pool; ++ unsigned int max_rx_size; ++ ++#ifdef DEBUG_AMBASSADOR ++ PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK); ++#else ++ if (debug) ++ PRINTK (KERN_NOTICE, "no debugging support"); ++#endif ++ ++ if (cmds < MIN_QUEUE_SIZE) ++ PRINTK (KERN_NOTICE, "cmds has been raised to %u", ++ cmds = MIN_QUEUE_SIZE); ++ ++ if (txs < MIN_QUEUE_SIZE) ++ PRINTK (KERN_NOTICE, "txs has been raised to %u", ++ txs = MIN_QUEUE_SIZE); ++ ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ if (rxs[pool] < MIN_QUEUE_SIZE) ++ PRINTK (KERN_NOTICE, "rxs[%hu] has been raised to %u", ++ pool, rxs[pool] = MIN_QUEUE_SIZE); ++ ++ // buffers sizes should be greater than zero and strictly increasing ++ max_rx_size = 0; ++ for (pool = 0; pool < NUM_RX_POOLS; ++pool) ++ if (rxs_bs[pool] <= max_rx_size) ++ PRINTK (KERN_NOTICE, "useless pool (rxs_bs[%hu] = %u)", ++ pool, rxs_bs[pool]); ++ else ++ max_rx_size = rxs_bs[pool]; ++ ++ if (rx_lats < MIN_RX_BUFFERS) ++ PRINTK (KERN_NOTICE, "rx_lats has been raised to %u", ++ rx_lats = MIN_RX_BUFFERS); ++ ++ return; ++} + ++/********** module stuff **********/ + -+static void rx_ident_err(struct atm_vcc *vcc) -+{ -+ struct atm_dev *dev; -+ struct eni_vcc *eni_vcc; ++#ifdef MODULE ++EXPORT_NO_SYMBOLS; + -+ dev = vcc->dev; -+ /* immediately halt adapter */ -+ writel(readl(ENI_DEV(dev)->reg+MID_MC_S) & ~(MID_DMA_ENABLE | -+ MID_TX_ENABLE | MID_RX_ENABLE),ENI_DEV(dev)->reg+MID_MC_S); -+ /* 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%lx, rx pos 0x%lx, descr value " -+ "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos, -+ readl(eni_vcc->recv+eni_vcc->descr)); -+ printk(KERN_ALERT " last 0x%p, servicing %d\n",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); ++MODULE_AUTHOR(maintainer_string); ++MODULE_DESCRIPTION(description_string); ++MODULE_PARM(debug, "h"); ++MODULE_PARM(cmds, "i"); ++MODULE_PARM(txs, "i"); ++MODULE_PARM(rxs, __MODULE_STRING(NUM_RX_POOLS) "i"); ++MODULE_PARM(rxs_bs, __MODULE_STRING(NUM_RX_POOLS) "i"); ++MODULE_PARM(rx_lats, "i"); ++MODULE_PARM(pci_lat, "b"); ++MODULE_PARM_DESC(debug, "debug bitmap, see .h file"); ++MODULE_PARM_DESC(cmds, "number of command queue entries"); ++MODULE_PARM_DESC(txs, "number of TX queue entries"); ++MODULE_PARM_DESC(rxs, "number of RX queue entries [" __MODULE_STRING(NUM_RX_POOLS) "]"); ++MODULE_PARM_DESC(rxs_bs, "size of RX buffers [" __MODULE_STRING(NUM_RX_POOLS) "]"); ++MODULE_PARM_DESC(rx_lats, "number of extra buffers to cope with RX latencies"); ++MODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles"); ++ ++/********** module entry **********/ ++ ++int init_module (void) { ++ int devs; ++ ++ PRINTD (DBG_FLOW|DBG_INIT, "init_module"); ++ ++ // sanity check - cast needed as printk does not support %Zu ++ if (sizeof(amb_mem) != 4*16 + 4*12) { ++ PRINTK (KERN_ERR, "Fix amb_mem (is %lu words).", ++ (unsigned long) sizeof(amb_mem)); ++ return -ENOMEM; ++ } ++ ++ show_version(); ++ ++ // check arguments ++ amb_check_args(); ++ ++ // get the juice ++ devs = amb_probe(); ++ ++ if (devs) { ++ init_timer (&housekeeping); ++ housekeeping.function = do_housekeeping; ++ // paranoia ++ housekeeping.data = 1; ++ set_timer (&housekeeping, 0); ++ } else { ++ PRINTK (KERN_INFO, "no (usable) adapters found"); ++ } ++ ++ return devs ? 0 : -ENODEV; +} + ++/********** module exit **********/ + -+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; -+ u32 dma_rd,dma_wr; -+ u32 dma[RX_DMA_BUF*2]; -+ unsigned long paddr,here; -+ int i,j; ++void cleanup_module (void) { ++ amb_dev * dev; ++ ++ PRINTD (DBG_FLOW|DBG_INIT, "cleanup_module"); ++ ++ // paranoia ++ housekeeping.data = 0; ++ del_timer (&housekeeping); ++ ++ while (amb_devs) { ++ dev = amb_devs; ++ amb_devs = dev->prev; ++ ++ PRINTD (DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev); ++ // the drain should not be necessary ++ drain_rx_pools (dev); ++ amb_reset (dev, 0); ++ interrupts_off (dev); ++ destroy_queues (dev); ++ atm_dev_deregister (dev->atm_dev); ++ free_irq (dev->irq, dev); ++ release_region (dev->iobase, AMB_EXTENT); ++ kfree (dev); ++ } ++ ++ return; ++} + -+ 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); -+ ENI_PRV_SIZE(skb) = size+skip; -+ /* PDU plus descriptor */ -+ ATM_SKB(skb)->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; ++#else + -+ 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; ++/********** monolithic entry **********/ ++ ++int __init amb_detect (void) { ++ int devs; ++ ++ PRINTD (DBG_FLOW|DBG_INIT, "init_module"); ++ ++ // sanity check - cast needed as printk does not support %Zu ++ if (sizeof(amb_mem) != 4*16 + 4*12) { ++ PRINTK (KERN_ERR, "Fix amb_mem (is %lu words).", ++ (unsigned long) sizeof(amb_mem)); ++ return 0; ++ } ++ ++ show_version(); ++ ++ // check arguments ++ amb_check_args(); ++ ++ // get the juice ++ devs = amb_probe(); ++ ++ if (devs) { ++ init_timer (&housekeeping); ++ housekeeping.function = do_housekeeping; ++ // paranoia ++ housekeeping.data = 1; ++ set_timer (&housekeeping, 0); ++ } else { ++ PRINTK (KERN_INFO, "no (usable) adapters found"); ++ } ++ ++ return devs; ++} + -+ 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++] = virt_to_bus((void *) paddr); -+ paddr += init << 2; -+ words -= init; -+ } -+#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */ -+ if (words & ~15) { -+ dma[j++] = MID_DT_16W | ((words >> 4) << -+ MID_DMA_COUNT_SHIFT) | (vcc->vci << -+ MID_DMA_VCI_SHIFT); -+ dma[j++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~15) << 2; -+ words &= 15; -+ } +#endif -+#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */ -+ if (words & ~7) { -+ dma[j++] = MID_DT_8W | ((words >> 3) << -+ MID_DMA_COUNT_SHIFT) | (vcc->vci << -+ MID_DMA_VCI_SHIFT); -+ dma[j++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~7) << 2; -+ words &= 7; -+ } -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */ -+ if (words & ~3) { -+ dma[j++] = MID_DT_4W | ((words >> 2) << -+ MID_DMA_COUNT_SHIFT) | (vcc->vci << -+ MID_DMA_VCI_SHIFT); -+ dma[j++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~3) << 2; -+ words &= 3; -+ } -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */ -+ if (words & ~1) { -+ dma[j++] = MID_DT_2W | ((words >> 1) << -+ MID_DMA_COUNT_SHIFT) | (vcc->vci << -+ MID_DMA_VCI_SHIFT); -+ dma[j++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~1) << 2; -+ words &= 1; -+ } +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/ambassador.h Fri May 7 15:42:50 1999 +@@ -0,0 +1,687 @@ ++/* ++ Madge Ambassador ATM Adapter driver. ++ Copyright (C) 1995-1999 Madge Networks Ltd. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian ++ system and in the file COPYING in the Linux kernel source. ++*/ ++ ++/* ++ IMPORTANT NOTE: Madge Networks does not license the microcode for ++ this driver under the GPL. See the .data file for the licence. ++*/ ++ ++#ifndef AMBASSADOR_H ++#define AMBASSADOR_H ++ ++#ifdef CONFIG_ATM_AMBASSADOR_DEBUG ++#define DEBUG_AMBASSADOR +#endif -+ if (words) { -+ dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) -+ | (vcc->vci << MID_DMA_VCI_SHIFT); -+ dma[j++] = virt_to_bus((void *) 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); -+ return -1; -+ } -+ dma[j-2] |= MID_DMA_END; -+ j = j >> 1; -+ dma_wr = readl(eni_dev->reg+MID_DMA_WR_RX); -+ dma_rd = readl(eni_dev->reg+MID_DMA_RD_RX); -+ /* -+ * Can I move the dma_wr pointer by 2j+1 positions without overwriting -+ * data that hasn't been read (position of dma_rd) yet ? -+ */ -+ 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); -+ return -1; -+ } -+ for (i = 0; i < j; i++) { -+ writel(dma[i*2],eni_dev->rx_dma+dma_wr*2); -+ writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*2+1); -+ dma_wr = (dma_wr+1) & (NR_DMA_RX-1); -+ } -+ if (skb) { -+ ENI_PRV_POS(skb) = eni_vcc->descr+size+1; -+ skb_queue_tail(&eni_dev->rx_queue,skb); -+eni_vcc->last = skb; -+rx_enqueued++; -+ } -+ eni_vcc->descr = here; -+ writel(dma_wr,eni_dev->reg+MID_DMA_WR_RX); -+ return 0; -+} + ++#define DEV_LABEL "amb" + -+static void discard(struct atm_vcc *vcc,unsigned long size) -+{ -+ struct eni_vcc *eni_vcc; ++#ifndef PCI_VENDOR_ID_MADGE ++#define PCI_VENDOR_ID_MADGE 0x10B6 ++#endif ++#ifndef PCI_VENDOR_ID_MADGE_AMBASSADOR ++#define PCI_DEVICE_ID_MADGE_AMBASSADOR 0x1001 ++#endif ++#ifndef PCI_VENDOR_ID_MADGE_AMBASSADOR_BAD ++#define PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD 0x1002 ++#endif ++ ++// diagnostic output ++ ++#define PRINTK(severity,format,args...) \ ++ printk(severity DEV_LABEL ": " format "\n" , ## args) ++ ++#ifdef DEBUG_AMBASSADOR ++ ++#define DBG_ERR 0x0001 ++#define DBG_WARN 0x0002 ++#define DBG_INFO 0x0004 ++#define DBG_INIT 0x0008 ++#define DBG_LOAD 0x0010 ++#define DBG_VCC 0x0020 ++#define DBG_QOS 0x0040 ++#define DBG_CMD 0x0080 ++#define DBG_TX 0x0100 ++#define DBG_RX 0x0200 ++#define DBG_SKB 0x0400 ++#define DBG_POOL 0x0800 ++#define DBG_IRQ 0x1000 ++#define DBG_FLOW 0x2000 ++#define DBG_REGS 0x4000 ++#define DBG_DATA 0x8000 ++#define DBG_MASK 0xffff ++ ++/* the ## prevents the annoying double expansion of the macro arguments */ ++/* KERN_INFO is used since KERN_DEBUG often does not make it to the console */ ++#define PRINTDB(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format , ## args) : 1 ) ++#define PRINTDM(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (format , ## args) : 1 ) ++#define PRINTDE(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (format "\n" , ## args) : 1 ) ++#define PRINTD(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format "\n" , ## args) : 1 ) + -+ 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_PRV_POS(eni_vcc->last) += size+1; -+ else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1); -+} ++#else + ++#define PRINTD(bits,format,args...) ++#define PRINTDB(bits,format,args...) ++#define PRINTDM(bits,format,args...) ++#define PRINTDE(bits,format,args...) ++ ++#endif ++ ++#define PRINTDD(bits,format,args...) ++#define PRINTDDB(sec,fmt,args...) ++#define PRINTDDM(sec,fmt,args...) ++#define PRINTDDE(sec,fmt,args...) ++ ++// tunable values (?) ++ ++/* MUST be powers of two -- why ? */ ++#define COM_Q_ENTRIES 8 ++#define TX_Q_ENTRIES 32 ++#define RX_Q_ENTRIES 64 ++ ++// fixed values ++ ++// guessing ++#define AMB_EXTENT 0x80 ++ ++// Minimum allowed size for an Ambassador queue ++#define MIN_QUEUE_SIZE 2 ++ ++// Ambassador microcode allows 1 to 4 pools, we use 4 (simpler) ++#define NUM_RX_POOLS 4 ++ ++// minimum RX buffers required to cope with replenishing delay ++#define MIN_RX_BUFFERS 1 ++ ++// RX buffer tailroom to cope with writing too much ++#define RX_FUDGE 3 ++ ++// minimum PCI latency we will tolerate (32 IS TOO SMALL) ++#define MIN_PCI_LATENCY 64 // 255 ++ ++// VCs supported by card (VPI always 0) ++#define NUM_VPI_BITS 0 ++#define NUM_VCI_BITS 10 ++#define NUM_VCS 1024 ++ ++/* The status field bits defined so far. */ ++#define RX_ERR 0x8000 // always present if there is an error (hmm) ++#define CRC_ERR 0x4000 // AAL5 CRC error ++#define LEN_ERR 0x2000 // overlength frame ++#define ABORT_ERR 0x1000 // zero length field in received frame ++#define UNUSED_ERR 0x0800 // buffer returned unused ++ ++// Adaptor commands ++ ++#define SRB_OPEN_VC 0 ++/* par_0: dwordswap(VC_number) */ ++/* par_1: dwordswap(flags<<16) or wordswap(flags)*/ ++/* flags: */ ++ ++/* LANE: 0x0004 */ ++/* NOT_UBR: 0x0008 */ ++/* ABR: 0x0010 */ ++ ++/* RxPool0: 0x0000 */ ++/* RxPool1: 0x0020 */ ++/* RxPool2: 0x0040 */ ++/* RxPool3: 0x0060 */ ++ ++/* par_2: dwordswap(fp_rate<<16) or wordswap(fp_rate) */ ++ ++#define SRB_CLOSE_VC 1 ++/* par_0: dwordswap(VC_number) */ ++ ++#define SRB_GET_BIA 2 ++/* returns */ ++/* par_0: dwordswap(half BIA) */ ++/* par_1: dwordswap(half BIA) */ ++ ++#define SRB_GET_SUNI_STATS 3 ++/* par_0: dwordswap(physical_host_address) */ ++ ++#define SRB_SET_BITS_8 4 ++#define SRB_SET_BITS_16 5 ++#define SRB_SET_BITS_32 6 ++#define SRB_CLEAR_BITS_8 7 ++#define SRB_CLEAR_BITS_16 8 ++#define SRB_CLEAR_BITS_32 9 ++/* par_0: dwordswap(ATMizer address) */ ++/* par_1: dwordswap(mask) */ ++ ++#define SRB_SET_8 10 ++#define SRB_SET_16 11 ++#define SRB_SET_32 12 ++/* par_0: dwordswap(ATMizer address) */ ++/* par_1: dwordswap(data) */ ++ ++#define SRB_GET_32 13 ++/* par_0: dwordswap(ATMizer address) */ ++/* returns */ ++/* par_1: dwordswap(ATMizer data) */ ++ ++#define SRB_GET_VERSION 14 ++/* returns */ ++/* par_0: dwordswap(Major Version) */ ++/* par_1: dwordswap(Minor Version) */ ++ ++#define SRB_FLUSH_BUFFER_Q 15 ++/* Only flags to define which buffer pool; all others must be zero */ ++/* par_0: dwordswap(flags<<16) or wordswap(flags)*/ ++ ++#define SRB_GET_DMA_SPEEDS 16 ++/* returns */ ++/* par_0: dwordswap(Read speed (bytes/sec)) */ ++/* par_1: dwordswap(Write speed (bytes/sec)) */ ++ ++#define SRB_MODIFY_VC_RATE 17 ++/* par_0: dwordswap(VC_number) */ ++/* par_1: dwordswap(fp_rate<<16) or wordswap(fp_rate) */ ++ ++#define SRB_MODIFY_VC_FLAGS 18 ++/* par_0: dwordswap(VC_number) */ ++/* par_1: dwordswap(flags<<16) or wordswap(flags)*/ ++ ++/* flags: */ ++ ++/* LANE: 0x0004 */ ++/* NOT_UBR: 0x0008 */ ++/* ABR: 0x0010 */ ++ ++/* RxPool0: 0x0000 */ ++/* RxPool1: 0x0020 */ ++/* RxPool2: 0x0040 */ ++/* RxPool3: 0x0060 */ ++ ++#define SRB_POOL_SHIFT 5 ++ ++#define SRB_STOP_TASKING 19 ++#define SRB_START_TASKING 20 ++#define SRB_SHUT_DOWN 21 ++#define MAX_SRB 21 ++ ++#define SRB_COMPLETE 0xffffffff ++ ++#define TX_FRAME 0x80000000 ++ ++// number of types of SRB MUST be a power of two -- why? ++#define NUM_OF_SRB 32 ++ ++// number of bits of period info for rate ++#define MAX_RATE_BITS 6 ++ ++#define TX_UBR 0x0000 ++#define TX_UBR_CAPPED 0x0008 ++#define TX_ABR 0x0018 ++#define TX_FRAME_NOTCAP 0x0000 ++#define TX_FRAME_CAPPED 0x8000 ++ ++#define FP_155_RATE 0x24b1 ++#define FP_25_RATE 0x1f9d ++ ++#define AMB_RESET 0x40 ++ ++/* #define VERSION_NUMBER 0x01000000 // initial release */ ++/* #define VERSION_NUMBER 0x01010000 // fixed startup probs PLX MB0 not cleared */ ++/* #define VERSION_NUMBER 0x01020000 // changed SUNI reset timings; allowed r/w onchip */ ++ ++/* #define VERSION_NUMBER 0x01030000 // clear local doorbell int reg on reset */ ++/* #define VERSION_NUMBER 0x01040000 // PLX bug work around version PLUS */ ++/* remove race conditions on basic interface */ ++/* indicate to the host that diagnostics */ ++/* have finished; if failed, how and what */ ++/* failed */ ++/* fix host memory test to fix PLX bug */ ++/* allow flash upgrade and BIA upgrade directly */ ++/* */ ++#define VERSION_NUMBER 0x01050025 /* Jason's first hacked version. */ ++/* Change in download algorithm */ ++ ++#define DMA_VALID 0xb728e149 /* completely random */ ++ ++#define FLASH_BASE 0xa0c00000 ++#define FLASH_SIZE 0x00020000 /* 128K */ ++#define BIA_BASE (FLASH_BASE+0x0001c000) /* Flash Sector 7 */ ++#define BIA_ADDRESS ((void *)0xa0c1c000) ++#define PLX_BASE 0xe0000000 ++ ++typedef enum { ++ host_memory_test = 1, ++ read_adapter_memory, ++ write_adapter_memory, ++ adapter_start, ++ get_version_number, ++ interrupt_host, ++ flash_erase_sector, ++ adap_download_block = 0x20, ++ adap_erase_flash, ++ adap_run_in_iram, ++ adap_end_download ++} loader_command; ++ ++#define BAD_COMMAND (-1) ++#define COMMAND_IN_PROGRESS 1 ++#define COMMAND_PASSED_TEST 2 ++#define COMMAND_FAILED_TEST 3 ++#define COMMAND_READ_DATA_OK 4 ++#define COMMAND_READ_BAD_ADDRESS 5 ++#define COMMAND_WRITE_DATA_OK 6 ++#define COMMAND_WRITE_BAD_ADDRESS 7 ++#define COMMAND_WRITE_FLASH_FAILURE 8 ++#define COMMAND_COMPLETE 9 ++#define COMMAND_FLASH_ERASE_FAILURE 10 ++#define COMMAND_WRITE_BAD_DATA 11 ++ ++/* bit fields for mailbox[0] return values */ ++ ++#define GPINT_TST_FAILURE 0x00000001 ++#define SUNI_DATA_PATTERN_FAILURE 0x00000002 ++#define SUNI_DATA_BITS_FAILURE 0x00000004 ++#define SUNI_UTOPIA_FAILURE 0x00000008 ++#define SUNI_FIFO_FAILURE 0x00000010 ++#define SRAM_FAILURE 0x00000020 ++#define SELF_TEST_FAILURE 0x0000003f ++ ++/* mailbox[1] = 0 in progress, -1 on completion */ ++/* mailbox[2] = current test 00 00 test(8 bit) phase(8 bit) */ ++/* mailbox[3] = last failure, 00 00 test(8 bit) phase(8 bit) */ ++/* mailbox[4],mailbox[5],mailbox[6] random failure values */ ++ ++/* PLX/etc. memory map including command structure */ ++ ++/* These registers may also be memory mapped in PCI memory */ ++ ++#define UNUSED_LOADER_MAILBOXES 6 ++ ++typedef struct { ++ u32 stuff[16]; ++ union { ++ struct { ++ u32 result; ++ u32 ready; ++ u32 stuff[UNUSED_LOADER_MAILBOXES]; ++ } loader; ++ struct { ++ u32 cmd_address; ++ u32 tx_address; ++ u32 rx_address[NUM_RX_POOLS]; ++ u32 gen_counter; ++ u32 spare; ++ } adapter; ++ } mb; ++ u32 doorbell; ++ u32 interrupt; ++ u32 interrupt_control; ++ u32 reset_control; ++} amb_mem; ++ ++#define mem ((amb_mem *)0) ++ ++/* IRQ (card to host) and doorbell (host to card) enable bits */ ++#define AMB_INTERRUPT_BITS 0x00030000 ++#define AMB_DOORBELL_BITS 0x00000300 ++ ++/* loader commands */ ++ ++#define MAX_COMMAND_DATA 13 ++#define MAX_TRANSFER_DATA 11 ++ ++typedef struct { ++ u32 address; ++ u32 count; ++ u32 data[MAX_TRANSFER_DATA]; ++} transfer_block; ++ ++typedef struct { ++ u32 result; ++ u32 command; ++ union { ++ transfer_block transfer; ++ u32 version; ++ u32 start; ++ u32 data[MAX_COMMAND_DATA]; ++ } payload; ++ u32 valid; ++} loader_block; ++ ++/* command queue */ ++ ++/* Again all data are BIG ENDIAN */ ++ ++typedef struct { ++ union { ++ struct { ++ u32 vc; ++ u32 flags; ++ u32 rate; ++ } open; ++ struct { ++ u32 vc; ++ u32 rate; ++ } modify_rate; ++ struct { ++ u32 vc; ++ u32 flags; ++ } modify_flags; ++ struct { ++ u32 vc; ++ } close; ++ struct { ++ u32 lower4; ++ u32 upper2; ++ } bia; ++ struct { ++ u32 address; ++ } suni; ++ struct { ++ u32 major; ++ u32 minor; ++ } version; ++ struct { ++ u32 read; ++ u32 write; ++ } speed; ++ struct { ++ u32 flags; ++ } flush; ++ struct { ++ u32 address; ++ u32 data; ++ } memory; ++ u32 par[3]; ++ } args; ++ u32 request; ++} command; ++ ++/* transmit queues and associated structures */ ++ ++/* The hosts transmit structure. All BIG ENDIAN; host address ++ restricted to first 1GByte, but address passed to the card must ++ have the top MS bit or'ed in. -- check this */ ++ ++/* TX is described by 1+ tx_frags followed by a tx_frag_end */ ++ ++typedef struct { ++ u32 bytes; ++ u32 address; ++} tx_frag; ++ ++/* apart from handle the fields here are for the adapter to play with ++ and should be set to zero */ ++ ++typedef struct { ++ u32 handle; ++ // really? what about a BE host? ++ u16 vc; ++ u16 next_descriptor_length; ++ u32 next_descriptor; ++#ifdef AMB_NEW_MICROCODE ++ // BE host? ++ u8 cpcs_uu; ++ u8 cpi; ++ u16 pad; ++#endif ++} tx_frag_end; ++ ++typedef struct { ++ tx_frag tx_frag; ++ tx_frag_end tx_frag_end; ++ struct sk_buff * skb; ++} tx_simple; + -+/* -+ * TODO: should check whether direct copies (without DMA setup, dequeuing on -+ * interrupt, etc.) aren't much faster for AAL0 -+ */ ++#if 0 ++typedef union { ++ tx_frag fragment; ++ tx_frag_end end_of_list; ++} tx_descr; ++#endif ++ ++/* this "points" to the sequence of fragments and trailer */ ++ ++typedef struct { ++ // what about a BE host? ++ u16 vc; ++ u16 tx_descr_length; ++ u32 tx_descr_addr; ++} tx_in; ++ ++/* handle is the handle from tx_in */ ++ ++typedef struct { ++ u32 handle; ++} tx_out; ++ ++/* receive frame structure */ ++ ++/* All BIG ENDIAN; handle is as passed from host; length is zero for ++ aborted frames, and frames with errors. Header is actually VC ++ number, lec-id is NOT yet supported. */ ++ ++typedef struct { ++ u32 handle; ++ // what about a BE host? ++ u16 vc; ++ u16 lec_id; // unused ++ u16 status; ++ u16 length; ++} rx_out; ++ ++/* buffer supply structure */ ++ ++typedef struct { ++ u32 handle; ++ u32 host_address; ++} rx_in; ++ ++/* This first structure is the area in host memory where the adapter ++ writes its pointer values. These pointer values are BIG ENDIAN and ++ reside in the same 4MB 'page' as this structure. The host gives the ++ adapter the address of this block by sending a doorbell interrupt ++ to the adapter after downloading the code and setting it going. The ++ addresses have the top 10 bits set to 1010000010b -- really? ++ ++ The host must initialise these before handing the block to the ++ adapter. */ + -+static int rx_aal0(struct atm_vcc *vcc) -+{ -+ struct eni_vcc *eni_vcc; -+ unsigned long descr; -+ unsigned long length; -+ struct sk_buff *skb; ++typedef struct { ++ u32 command_start; /* SRB commands completions */ ++ u32 command_end; /* SRB commands completions */ ++ u32 tx_start; ++ u32 tx_end; ++ u32 txcom_start; /* tx completions */ ++ u32 txcom_end; /* tx completions */ ++ struct { ++ u32 buffer_start; ++ u32 buffer_end; ++ u32 buffer_q_get; ++ u32 buffer_q_end; ++ u32 buffer_aptr; ++ u32 rx_start; /* rx completions */ ++ u32 rx_end; ++ u32 rx_ptr; ++ u32 buffer_size; /* size of host buffer */ ++ } rec_struct[NUM_RX_POOLS]; ++#ifdef AMB_NEW_MICROCODE ++ // BE? ++ u16 init_flags; ++ u16 talk_block_spare; ++#endif ++} adap_talk_block; + -+ DPRINTK(">rx_aal0\n"); -+ eni_vcc = ENI_VCC(vcc); -+ descr = readl(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 || !atm_charge(vcc,atm_pdu2truesize(length))) skb = NULL; -+ else { -+ skb = alloc_skb(length,GFP_ATOMIC); -+ if (!skb) atm_return(vcc,atm_pdu2truesize(length)); -+ } -+ if (!skb) { -+ discard(vcc,length >> 2); -+ return 0; -+ } -+ skb_put(skb,length); -+ skb->stamp = 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; -+} ++/* This structure must be kept in line with the vcr image in sarmain.h ++ ++ This is the structure in the host filled in by the adapter by ++ GET_SUNI_STATS */ + ++typedef struct { ++ // what about a BE host? ++ u8 racp_chcs; ++ u8 racp_uhcs; ++ u16 spare; ++ u32 racp_rcell; ++ u32 tacp_tcell; ++ u32 flags; ++ u32 dropped_cells; ++ u32 dropped_frames; ++} suni_stats; ++ ++typedef enum { ++ dead ++} amb_flags; ++ ++#define NEXTQ(current,start,limit) \ ++ ( (current)+1 < (limit) ? (current)+1 : (start) ) ++ ++typedef struct { ++ spinlock_t lock; ++ unsigned int pending; ++ unsigned int high; ++ unsigned int maximum; // size - 1 (q implementation) ++ command * start; ++ command * in; ++ command * out; ++ command * limit; ++} amb_cq; ++ ++typedef struct { ++ spinlock_t lock; ++ unsigned int pending; ++ unsigned int high; ++ unsigned int filled; ++ unsigned int maximum; // size - 1 (q implementation) ++ struct { ++ tx_in * start; ++ tx_in * ptr; ++ tx_in * limit; ++ } in; ++ struct { ++ tx_out * start; ++ tx_out * ptr; ++ tx_out * limit; ++ } out; ++} amb_txq; ++ ++typedef struct { ++ spinlock_t lock; ++ unsigned int pending; ++ unsigned int low; ++ unsigned int emptied; ++ unsigned int maximum; // size - 1 (q implementation) ++ struct { ++ rx_in * start; ++ rx_in * ptr; ++ rx_in * limit; ++ } in; ++ struct { ++ rx_out * start; ++ rx_out * ptr; ++ rx_out * limit; ++ } out; ++ unsigned int buffers_wanted; ++ unsigned int buffer_size; ++} amb_rxq; ++ ++typedef struct { ++ unsigned long tx_ok; ++ struct { ++ unsigned long ok; ++ unsigned long error; ++ unsigned long badcrc; ++ unsigned long toolong; ++ unsigned long aborted; ++ unsigned long unused; ++ } rx; ++} amb_stats; ++ ++// a single struct pointed to by atm_vcc->dev_data ++ ++typedef struct { ++ u8 tx_vc_bits:7; ++ u8 tx_present:1; ++} amb_tx_info; ++ ++typedef struct { ++ unsigned char pool; ++} amb_rx_info; ++ ++typedef struct { ++ amb_rx_info rx_info; ++ u16 tx_frame_bits; ++ unsigned int tx_rate; ++ unsigned int rx_rate; ++} amb_vcc; ++ ++struct amb_dev { ++ u8 irq; ++ u8 flags; ++ u32 iobase; ++ u32 * membase; + -+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; ++#if 0 ++ struct tq_struct bh; ++#endif ++ ++ amb_cq cq; ++ amb_txq txq; ++ amb_rxq rxq[NUM_RX_POOLS]; ++ ++ struct semaphore vcc_sf; ++ amb_tx_info txer[NUM_VCS]; ++ struct atm_vcc * rxer[NUM_VCS]; ++ unsigned int tx_avail; ++ unsigned int rx_avail; ++ ++ amb_stats stats; ++ ++ struct atm_dev * atm_dev; ++ struct pci_dev * pci_dev; ++ struct amb_dev * prev; ++}; + -+ EVENT("rx_aal5\n",0,0); -+ DPRINTK(">rx_aal5\n"); -+ eni_vcc = ENI_VCC(vcc); -+ descr = readl(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%lx)\n",descr,0); -+ DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", -+ vcc->dev->number); -+ size = 0; -+ } -+ else { -+ static unsigned long silence = 0; ++typedef struct amb_dev amb_dev; + -+ if (time_after(jiffies, silence) || silence == 0) { -+ printk(KERN_WARNING DEV_LABEL "(itf %d): " -+ "discarding PDU(s) with CRC error\n", -+ vcc->dev->number); -+ silence = (jiffies+2*HZ)|1; -+ } -+ size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); -+ EVENT("CRC error (descr=0x%lx,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 = readl(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); -+ length = eff = 0; -+ vcc->stats->rx_err++; -+ } -+ } -+ if (!eff || !atm_charge(vcc,atm_pdu2truesize(eff << 2))) skb = NULL; -+ else { -+ skb = alloc_skb(eff << 2,GFP_ATOMIC); -+ 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); -+ atm_return(vcc,atm_pdu2truesize(eff << 2)); -+ } -+ } -+ if (!skb) { -+ discard(vcc,size); -+ return 0; -+ } -+ skb_put(skb,length); -+ DPRINTK("got len %ld\n",length); -+ if (do_rx_dma(vcc,skb,1,size,eff)) return 1; -+ eni_vcc->rxing++; -+ return 0; -+} ++#define AMB_DEV(atm_dev) ((amb_dev *) (atm_dev)->dev_data) ++#define AMB_VCC(atm_vcc) ((amb_vcc *) (atm_vcc)->dev_data) + ++/* the microcode */ + -+static inline int rx_vcc(struct atm_vcc *vcc) -+{ -+ u32 *vci_dsc; -+ struct eni_vcc *eni_vcc; -+ unsigned long tmp; ++typedef struct { ++ u32 start; ++ unsigned int count; ++} region; + -+ 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 = (readl(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 %d\n",ENI_VCC(vcc)->descr, -+ ((readl(vci_dsc+1) & MID_VCI_DESCR) >> -+ MID_VCI_DESCR_SHIFT)); -+ if (ENI_VCC(vcc)->rx(vcc)) return 1; -+ } -+ /* clear IN_SERVICE flag */ -+ writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc); -+ /* -+ * 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 = (readl(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 %d\n",ENI_VCC(vcc)->descr, -+ ((readl(vci_dsc+1) & MID_VCI_DESCR) >> -+ MID_VCI_DESCR_SHIFT)); -+ if (ENI_VCC(vcc)->rx(vcc)) return 1; -+ } -+ return 0; -+} ++extern const region ucode_regions[]; ++extern const u32 ucode_data[]; ++extern const u32 ucode_start; + ++/* rate rounding */ + -+static void poll_rx(struct atm_dev *dev) -+{ -+ struct eni_dev *eni_dev; -+ struct atm_vcc *curr; ++typedef enum { ++ round_up, ++ round_down, ++ round_nearest ++} rounding; + -+ eni_dev = ENI_DEV(dev); -+ while ((curr = eni_dev->fast)) { -+ EVENT("poll_rx.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_rx.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--; -+ } -+} ++#endif +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/atmdev_init.c Tue May 18 01:13:20 1999 +@@ -0,0 +1,60 @@ ++/* drivers/atm/atmdev_init.c - ATM device driver initialization */ ++ ++/* Written 1995-1997 by Werner Almesberger, EPFL LRC */ ++ + ++#include ++#include + -+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 (readl(eni_dev->reg+MID_SERV_WRITE) != eni_dev->serv_read) { -+ vci = readl(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++; -+ } -+} ++#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_NICSTAR ++extern int nicstar_detect(void); ++#endif ++#ifdef CONFIG_ATM_AMBASSADOR ++extern int amb_detect(void); ++#endif ++#ifdef CONFIG_ATM_HORIZON ++extern int hrz_detect(void); ++#endif + + -+static void dequeue_rx(struct atm_dev *dev) ++__initfunc(int atmdev_init(void)) +{ -+ struct eni_dev *eni_dev; -+ struct eni_vcc *eni_vcc; -+ struct atm_vcc *vcc; -+ struct sk_buff *skb; -+ u32 *vci_dsc; -+ int first; ++ int devs; + -+ 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=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb), -+ ENI_PRV_POS(skb)); -+rx_dequeued++; -+ vcc = ATM_SKB(skb)->vcc; -+ eni_vcc = ENI_VCC(vcc); -+ first = 0; -+ vci_dsc = eni_dev->vci+(vcc->vci << 2); -+ if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb), -+ (readl(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 = ENI_PRV_POS(skb) & (eni_vcc->words-1); -+ if (!skb->len) kfree_skb(skb); -+ else { -+ EVENT("pushing (len=%ld)\n",skb->len,0); -+ if (vcc->qos.aal == ATM_AAL0) -+ *(unsigned long *) skb->data = -+ ntohl(*(unsigned long *) skb->data); -+ memset(skb->cb,0,sizeof(struct eni_skb_prv)); -+ vcc->push(vcc,skb); -+ pushed++; -+ } -+ vcc->stats->rx++; -+ } -+ wake_up(&eni_dev->rx_wait); ++ 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_NICSTAR ++ devs += nicstar_detect(); ++#endif ++#ifdef CONFIG_ATM_AMBASSADOR ++ devs += amb_detect(); ++#endif ++#ifdef CONFIG_ATM_HORIZON ++ devs += hrz_detect(); ++#endif ++ return devs; +} +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/atmsar11.data Tue Mar 16 19:13:42 1999 +@@ -0,0 +1,2063 @@ ++/* ++ Madge Ambassador ATM Adapter microcode. ++ Copyright (C) 1995-1999 Madge Networks Ltd. ++ ++ This is provided here for your convenience only. ++ ++ No restrictions are placed on its use, so long as this file remains ++ unchanged. ++ ++ You may not make, use or re-distribute modified versions of this code. ++*/ ++ ++ 0x401a6800, ++ 0x00000000, ++ 0x335b007c, ++ 0x13600005, ++ 0x335b1000, ++ 0x3c1aa0c0, ++ 0x375a0180, ++ 0x03400008, ++ 0x00000000, ++ 0x1760fffb, ++ 0x335b4000, ++ 0x401a7000, ++ 0x13600003, ++ 0x241b0fc0, ++ 0xaf9b4500, ++ 0x25080008, ++ 0x03400008, ++ 0x42000010, ++ 0x8f810c90, ++ 0x32220002, ++ 0x10400003, ++ 0x3c03a0d1, ++ 0x2463f810, ++ 0x0060f809, ++ 0x24210001, ++ 0x1000001a, ++ 0xaf810c90, ++ 0x82020011, ++ 0xaf900c48, ++ 0x0441000a, ++ 0x34420080, ++ 0x967d0002, ++ 0x96020012, ++ 0x00000000, ++ 0x105d0011, ++ 0x00000000, ++ 0x04110161, ++ 0xa6620002, ++ 0x1000000d, ++ 0xae62000c, ++ 0x34848000, ++ 0xa2020011, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f834c00, ++ 0x00000000, ++ 0xaf830fec, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x00041400, ++ 0x0440fff7, ++ 0x00000000, ++ 0xaf80460c, ++ 0x8e100008, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f834c00, ++ 0x4900001d, ++ 0xaf830fec, ++ 0x8f820cbc, ++ 0x8f9d0c4c, ++ 0x24420001, ++ 0x97be0000, ++ 0xaf820cbc, ++ 0x13c00009, ++ 0xaca200d8, ++ 0xa7a00000, ++ 0x3c0100d1, ++ 0x003e0825, ++ 0x9422002c, ++ 0x0411013f, ++ 0xa4220002, ++ 0xac22000c, ++ 0xac200010, ++ 0x8f9e0c54, ++ 0x27bd0002, ++ 0x17be0002, ++ 0x8ca200c0, ++ 0x8f9d0c50, ++ 0x8f970fc8, ++ 0xaf9d0c4c, ++ 0x12e20005, ++ 0x87804002, ++ 0x3c02a0d1, ++ 0x2442f94c, ++ 0x0040f809, ++ 0x00000000, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4500ffdc, ++ 0x8e11000c, ++ 0x3c1300d1, ++ 0x00111102, ++ 0x2c430400, ++ 0x1060ffb9, ++ 0x00021180, ++ 0x02629821, ++ 0x8e76003c, ++ 0x32220008, ++ 0x1440ffb7, ++ 0x8e770034, ++ 0x8e750030, ++ 0x3c03cfb0, ++ 0x16c00003, ++ 0x02d5102b, ++ 0x041100be, ++ 0x00000000, ++ 0x1040ffa6, ++ 0x00701826, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f824c00, ++ 0xaf974c00, ++ 0xaf820fec, ++ 0xac760010, ++ 0x02609021, ++ 0x32220002, ++ 0x10400007, ++ 0x8f944a00, ++ 0x9602003a, ++ 0x34840004, ++ 0x14400003, ++ 0xaf820fbc, ++ 0x3c029000, ++ 0xaf820fbc, ++ 0x8e100008, ++ 0x32943f00, ++ 0x8e11000c, ++ 0x2694ff00, ++ 0x12800073, ++ 0x3c1300d1, ++ 0x49010071, ++ 0x32370008, ++ 0x16e0006f, ++ 0x00111102, ++ 0x2c430400, ++ 0x1060006c, ++ 0x0002b980, ++ 0x00041740, ++ 0x0440003a, ++ 0x02779821, ++ 0x12720023, ++ 0x26d60030, ++ 0xae56003c, ++ 0x8e76003c, ++ 0x8e770034, ++ 0x8e750030, ++ 0x3c03cfb0, ++ 0x16c00003, ++ 0x02d5102b, ++ 0x04110091, ++ 0x00000000, ++ 0x10400060, ++ 0x2e821000, ++ 0x14400009, ++ 0x00701826, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f824c00, ++ 0xaf974c00, ++ 0xac760010, ++ 0xae420034, ++ 0x1000ffd0, ++ 0xaf80460c, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x3c03cfb0, ++ 0x00701826, ++ 0xae460034, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f824c00, ++ 0xaf974c00, ++ 0xaf820fec, ++ 0xac760010, ++ 0x1000ffc3, ++ 0xaf80460c, ++ 0x02d5102b, ++ 0x10400042, ++ 0x3c17cfb0, ++ 0x2e821000, ++ 0x14400006, ++ 0x02f0b826, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xaef60010, ++ 0x1000ffb8, ++ 0xaf80460c, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f824c00, ++ 0xaf864c00, ++ 0xaef60010, ++ 0xaf820fec, ++ 0x1000ffae, ++ 0xaf80460c, ++ 0x3084fffb, ++ 0x8e570038, ++ 0x3242ffc0, ++ 0x00021182, ++ 0xa7820fb8, ++ 0xaf970fb4, ++ 0x865d002a, ++ 0x865e0008, ++ 0xa79d0fba, ++ 0x279d0f18, ++ 0x33de0060, ++ 0x03bee821, ++ 0x001ef0c2, ++ 0x03bee821, ++ 0x8f970c58, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f834c00, ++ 0x8fa2001c, ++ 0x12e30003, ++ 0x3c030c40, ++ 0x3c1ec000, ++ 0xaf9e0fbc, ++ 0xac620fb4, ++ 0x8fa30018, ++ 0x2442000c, ++ 0x14430002, ++ 0xaf80460c, ++ 0x8fa20014, ++ 0xae40003c, ++ 0xafa2001c, ++ 0x8e76003c, ++ 0x8e770034, ++ 0x8e750030, ++ 0x3c03cfb0, ++ 0x16c00003, ++ 0x02d5102b, ++ 0x0411003c, ++ 0x00000000, ++ 0x00701826, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xaca500e4, ++ 0x10400032, ++ 0xaf974c00, ++ 0x1000ff7f, ++ 0xac760010, ++ 0x00041740, ++ 0x04400007, ++ 0x26d60030, ++ 0xae56003c, ++ 0x00e0f809, ++ 0x03e03821, ++ 0xaf80460c, ++ 0x1000ff39, ++ 0xae460034, ++ 0x8e570038, ++ 0x3242ffc0, ++ 0x00021182, ++ 0xa7820fb8, ++ 0xaf970fb4, ++ 0x8f970c58, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x12e60003, ++ 0x3c030c40, ++ 0x3c02c000, ++ 0xaf820fbc, ++ 0x865d002a, ++ 0x865e0008, ++ 0xa79d0fba, ++ 0x279d0f18, ++ 0x33de0060, ++ 0x03bee821, ++ 0x001ef0c2, ++ 0x03bee821, ++ 0x8fa2001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f974c00, ++ 0xac620fb4, ++ 0x3084fffb, ++ 0x8fa30018, ++ 0x2442000c, ++ 0x14430002, ++ 0xaf80460c, ++ 0x8fa20014, ++ 0xae40003c, ++ 0xafa2001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xaca500e4, ++ 0x1000ff13, ++ 0xaf974c00, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x1000ff0f, ++ 0x00000000, ++ 0x1040005b, ++ 0x867e0008, ++ 0x279d0f18, ++ 0x33de0060, ++ 0x03bee821, ++ 0x001e10c2, ++ 0x03a2e821, ++ 0x8fb70008, ++ 0x8fa2000c, ++ 0x8ef60004, ++ 0x12e20028, ++ 0x86620008, ++ 0x82030010, ++ 0x00021740, ++ 0x04410019, ++ 0x24630001, ++ 0x10600017, ++ 0x3c02d1b0, ++ 0x00501026, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f9e4c00, ++ 0xac560010, ++ 0x26d6fffe, ++ 0x86020010, ++ 0x3c03cfb0, ++ 0x34632000, ++ 0xa662002a, ++ 0x8ee20000, ++ 0x26f70008, ++ 0xae620038, ++ 0x8fa20020, ++ 0xafb70008, ++ 0x2417ffff, ++ 0x02c2a821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xaf9e4c00, ++ 0x03e00008, ++ 0xae750030, ++ 0x8ee20000, ++ 0x26f70008, ++ 0xae620038, ++ 0x8fa20020, ++ 0xafb70008, ++ 0x2417ffff, ++ 0xa677002a, ++ 0x02c2a821, ++ 0x3c03cfb0, ++ 0x03e00008, ++ 0xae750030, ++ 0x001e18c2, ++ 0x00651821, ++ 0x8c6300c8, ++ 0x8fa20010, ++ 0x00000000, ++ 0x0062b023, ++ 0x1ec00003, ++ 0x8fa10004, ++ 0x12c0001b, ++ 0x0022b023, ++ 0x2ec30041, ++ 0x14600002, ++ 0x3c150040, ++ 0x24160040, ++ 0x00161e80, ++ 0x00031882, ++ 0x00751825, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f954c00, ++ 0x001eb840, ++ 0x00771821, ++ 0xac624d00, ++ 0x00561021, ++ 0x14410002, ++ 0x27830d00, ++ 0x8fa20000, ++ 0x02e3b821, ++ 0xafa20010, ++ 0x02d71821, ++ 0xafa3000c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8ef60004, ++ 0x1000ffb5, ++ 0xaf954c00, ++ 0x3c16dead, ++ 0xae76003c, ++ 0xae600038, ++ 0x26d5ffff, ++ 0x00001021, ++ 0x03e00008, ++ 0xae750030, ++ 0x2c430ab2, ++ 0x10600005, ++ 0x2c4324b2, ++ 0x10000004, ++ 0x24020ab2, ++ 0x10000002, ++ 0x240224b1, ++ 0x1060fffd, ++ 0x304301ff, ++ 0x00031840, ++ 0x3c1da0d1, ++ 0x27bdd6cc, ++ 0x007d1821, ++ 0x94630000, ++ 0x0002ea42, ++ 0x00031c00, ++ 0x27bdfffb, ++ 0x03e00008, ++ 0x03a31006, ++ 0x24030fc0, ++ 0xaf834500, ++ 0x10000002, ++ 0x01206021, ++ 0x3c0ccfb0, ++ 0x11e00056, ++ 0x01896026, ++ 0x85fe0000, ++ 0x00000000, ++ 0x13c00047, ++ 0x3c02cfb0, ++ 0x07c0002d, ++ 0x001e1f80, ++ 0x04610034, ++ 0x001e1fc0, ++ 0x04600009, ++ 0x3c02d3b0, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f864c00, ++ 0x8f990fec, ++ 0x1000000b, ++ 0xaf994c00, ++ 0x01e27826, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f864c00, ++ 0xaf994c00, ++ 0xadef2010, ++ 0x3c02d3b0, ++ 0x01e27826, ++ 0x8f820fc0, ++ 0x8f830fc4, ++ 0xaf824d00, ++ 0x8de20004, ++ 0xa5e00000, ++ 0xac620000, ++ 0x8c620000, ++ 0x24020380, ++ 0xaf824d00, ++ 0x8f824d00, ++ 0x8f820f14, ++ 0x24630004, ++ 0x14620002, ++ 0x2419ffff, ++ 0x8f830f10, ++ 0xaca500e4, ++ 0xaf830fc4, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f824c80, ++ 0x1000001f, ++ 0xade2003c, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xa5e00000, ++ 0x8f864c00, ++ 0x15800022, ++ 0xaf8f4540, ++ 0x10000017, ++ 0x01e27826, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f864c00, ++ 0xaf994c00, ++ 0xadef2010, ++ 0x3c02cfb0, ++ 0x01e27826, ++ 0xa5e00000, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x10000007, ++ 0x8f994c00, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f864c00, ++ 0x8f990fec, ++ 0x1580000a, ++ 0xaf8f4500, ++ 0x00007821, ++ 0x10000014, ++ 0xaf190014, ++ 0x00e0f809, ++ 0x03e03821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x1180fff8, ++ 0x8f864c00, ++ 0x85220000, ++ 0x01207821, ++ 0x0440000a, ++ 0x8d290008, ++ 0x130b0004, ++ 0x000c1602, ++ 0xaf190014, ++ 0x8d790014, ++ 0x0160c021, ++ 0xaf994c00, ++ 0xad8e4010, ++ 0x3042003f, ++ 0x01c27021, ++ 0x00041780, ++ 0x0440018b, ++ 0x8f824a00, ++ 0x30818000, ++ 0x30420004, ++ 0x1440ff8d, ++ 0x8d4b0000, ++ 0x1020000c, ++ 0x30847fff, ++ 0x8f820c48, ++ 0x0120f021, ++ 0x24430034, ++ 0x8c5d000c, ++ 0x24420004, ++ 0xafdd000c, ++ 0x1462fffc, ++ 0x27de0004, ++ 0xa5210000, ++ 0x1000ff82, ++ 0x25080008, ++ 0x11600058, ++ 0x00000000, ++ 0x857d0008, ++ 0x8d63000c, ++ 0x9562000a, ++ 0x8d410004, ++ 0x07a10026, ++ 0x00621821, ++ 0xa563000a, ++ 0x00031c02, ++ 0x041101a0, ++ 0x000318c0, ++ 0x001d16c0, ++ 0x0441001f, ++ 0x27a20080, ++ 0x00021cc0, ++ 0x0461000e, ++ 0x0040e821, ++ 0x27bd0080, ++ 0x95620000, ++ 0x95630002, ++ 0x3442000c, ++ 0xad22000c, ++ 0x24020100, ++ 0xa5220010, ++ 0x9562002c, ++ 0xa5230014, ++ 0xa5220012, ++ 0xa5200016, ++ 0x34028000, ++ 0xa5220000, ++ 0xa57d0008, ++ 0x07a0000c, ++ 0x8f820c4c, ++ 0x8f830c50, ++ 0x2441ffe8, ++ 0x0023f02b, ++ 0x13c00002, ++ 0x00201021, ++ 0x24420400, ++ 0x945e0000, ++ 0x2441fffe, ++ 0x17c0fff9, ++ 0xad620010, ++ 0xa44b0000, ++ 0x142b001c, ++ 0xad400000, ++ 0xad400004, ++ 0x254a0008, ++ 0x3142007f, ++ 0x1440000e, ++ 0x00041780, ++ 0x04410003, ++ 0x8f820fe0, ++ 0x10000006, ++ 0x34840001, ++ 0x34840002, ++ 0x24420008, ++ 0x34421000, ++ 0x38421000, ++ 0xaf820fe0, ++ 0x354a0100, ++ 0x394a0100, ++ 0x39420080, ++ 0xaf820fe4, ++ 0x001d14c0, ++ 0x04410003, ++ 0x33a2efff, ++ 0x1000ff3c, ++ 0xa5620008, ++ 0x07a0009f, ++ 0x33a2fffe, ++ 0x10000021, ++ 0xa5620008, ++ 0x8d620024, ++ 0x001d1cc0, ++ 0x04610004, ++ 0xad420000, ++ 0x33a3efff, ++ 0x1000ff31, ++ 0xa5630008, ++ 0x07a00005, ++ 0x33a3fffe, ++ 0xa5630008, ++ 0x8d4b0000, ++ 0x1000ffaa, ++ 0x00000000, ++ 0x1000008e, ++ 0x25080008, ++ 0x254a0008, ++ 0x3142007f, ++ 0x1440000e, ++ 0x00041780, ++ 0x04410003, ++ 0x8f820fe0, ++ 0x10000006, ++ 0x34840001, ++ 0x34840002, ++ 0x24420008, ++ 0x34421000, ++ 0x38421000, ++ 0xaf820fe0, ++ 0x354a0100, ++ 0x394a0100, ++ 0x39420080, ++ 0xaf820fe4, ++ 0x11000003, ++ 0x8d4b0000, ++ 0x1000ff93, ++ 0x2508fff8, ++ 0x8f820fd8, ++ 0x8f830fdc, ++ 0x8f810fd4, ++ 0x1062001d, ++ 0x24620008, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f8c4c00, ++ 0x847f0000, ++ 0x3c1e00d1, ++ 0x33fd03ff, ++ 0x001d5980, ++ 0x017e5821, ++ 0x857e0008, ++ 0x001de900, ++ 0x001e0f00, ++ 0x03e1f825, ++ 0x07e00003, ++ 0xaf820fdc, ++ 0x879e0ca0, ++ 0x278b0c98, ++ 0x07c10042, ++ 0x3c020840, ++ 0x3c01f7b0, ++ 0x8d620020, ++ 0x00230826, ++ 0xac220000, ++ 0x8c620004, ++ 0x94630002, ++ 0x2442fff8, ++ 0x00431021, ++ 0x1000004e, ++ 0xad620020, ++ 0x8f820fd0, ++ 0x87830ca0, ++ 0x14220007, ++ 0x278b0c98, ++ 0x41000051, ++ 0x3c018000, ++ 0xaca100e0, ++ 0x8ca100c4, ++ 0x00000000, ++ 0x1022004c, ++ 0x0022e823, ++ 0x8f9f0f0c, ++ 0x07a10002, ++ 0xaf810fd4, ++ 0x03e2e823, ++ 0x2fa30041, ++ 0x14600002, ++ 0x3c1e0040, ++ 0x241d0040, ++ 0x001d1e80, ++ 0x00031882, ++ 0x007e1825, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f8c4c00, ++ 0xac624cc0, ++ 0x005d1021, ++ 0x145f0002, ++ 0x27830cc0, ++ 0x8f820f08, ++ 0x03a3f021, ++ 0xaf820fd0, ++ 0xaf9e0fd8, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x1000ffc3, ++ 0x24620008, ++ 0x8d63000c, ++ 0x8d7d0010, ++ 0xa563000a, ++ 0x13a00002, ++ 0x00031c02, ++ 0xa7a00000, ++ 0x000318c0, ++ 0x041100ef, ++ 0x00681821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f820c44, ++ 0x8f830c40, ++ 0xad620010, ++ 0xa5630004, ++ 0xa5630006, ++ 0x10000021, ++ 0xaf8c4c00, ++ 0xa57d0000, ++ 0x8c7d0004, ++ 0x94630002, ++ 0xac5d4c40, ++ 0x27a20008, ++ 0xad620018, ++ 0x03a3e821, ++ 0x27bdfff4, ++ 0xad7d001c, ++ 0x27bd0004, ++ 0xad7d0020, ++ 0x37c18001, ++ 0x001e17c0, ++ 0x0441ffe0, ++ 0xa5610008, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f820c44, ++ 0x8f830c40, ++ 0xad620010, ++ 0xa5630004, ++ 0xa5630006, ++ 0x8f820fd8, ++ 0x8f830fdc, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x1462ff95, ++ 0x24620008, ++ 0xaf8c4c00, ++ 0x87830ca0, ++ 0x278b0c98, ++ 0x0461fe97, ++ 0x00041700, ++ 0x04400005, ++ 0x95620000, ++ 0x11780006, ++ 0x00000000, ++ 0xaf0e0010, ++ 0xa70d0004, ++ 0x3084fff7, ++ 0x956d0004, ++ 0x8d6e0010, ++ 0x25adffd0, ++ 0x05a1fe8f, ++ 0xad22000c, ++ 0x3c0cffb0, ++ 0x01896026, ++ 0x000d1822, ++ 0x25ad0030, ++ 0x8d7e0018, ++ 0x8d61001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x103e0036, ++ 0x8f9d4c00, ++ 0x3c010840, ++ 0xac3e4c40, ++ 0x27de0008, ++ 0x11a00017, ++ 0xad7e0018, ++ 0x000df600, ++ 0x019e6025, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xad8e4010, ++ 0x8f8d0c40, ++ 0x957e0006, ++ 0x8f8e0c44, ++ 0x03cdf021, ++ 0xa57e0006, ++ 0x000cf782, ++ 0x000c0e02, ++ 0x03c1f021, ++ 0x001e0f80, ++ 0x000c6200, ++ 0x000c6202, ++ 0x01816025, ++ 0x33de003c, ++ 0x019e6021, ++ 0x34010001, ++ 0x10000008, ++ 0xa5210000, ++ 0x957e0006, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f8d0c40, ++ 0x8f8e0c44, ++ 0x03cdf021, ++ 0xa57e0006, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x01a3f02b, ++ 0x17c00008, ++ 0x0003f600, ++ 0x01a36823, ++ 0x019e6025, ++ 0x01896026, ++ 0x4d01fff7, ++ 0x00000000, ++ 0x1000fe58, ++ 0xaf9d4c00, ++ 0x8d7e0018, ++ 0x8d61001c, ++ 0x00000000, ++ 0x143effce, ++ 0x006d1823, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x2c610008, ++ 0x10200017, ++ 0x95610008, ++ 0x00000000, ++ 0x0001ff80, ++ 0x07e0000b, ++ 0x34210002, ++ 0x006d1821, ++ 0x00031e00, ++ 0x01836025, ++ 0x01896026, ++ 0x240d002c, ++ 0xa5610008, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x1000fe40, ++ 0xaf9d4c00, ++ 0x3c1f0c40, ++ 0xaffe4fa8, ++ 0x3021fffd, ++ 0xa5610008, ++ 0x3c0cd3cf, ++ 0x358ce000, ++ 0x10000008, ++ 0x34030002, ++ 0x3c1f0c40, ++ 0xaffe4fa8, ++ 0x11a0fff9, ++ 0x000df600, ++ 0x34030003, ++ 0x019e6025, ++ 0x01896026, ++ 0x34840008, ++ 0x34420002, ++ 0xad22000c, ++ 0x95620006, ++ 0xa5230000, ++ 0xad220038, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x857e0008, ++ 0x8f820fa8, ++ 0x97830fac, ++ 0xad220004, ++ 0x33c17fff, ++ 0xad600010, ++ 0xa5610008, ++ 0x1060fe20, ++ 0xaf9d4c00, ++ 0xa57e0008, ++ 0x00031900, ++ 0x30633ff0, ++ 0xa5630000, ++ 0x8f820fb0, ++ 0x3c030840, ++ 0xac624c40, ++ 0x24430008, ++ 0xad630018, ++ 0x97830fae, ++ 0x2442fff4, ++ 0x00621821, ++ 0xad63001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f8d0c40, ++ 0x8f830c44, ++ 0xa56d0004, ++ 0xa56d0006, ++ 0xad630010, ++ 0x1000fe0a, ++ 0xaf9d4c00, ++ 0x8f820fe0, ++ 0x00040fc0, ++ 0x8c430000, ++ 0x0421001b, ++ 0x8f9f0fe4, ++ 0x8c5d0004, ++ 0xac400004, ++ 0x1060000e, ++ 0xac400000, ++ 0x00000000, ++ 0x94620028, ++ 0x00000000, ++ 0x005f1020, ++ 0x8c410004, ++ 0x00000000, ++ 0x10200003, ++ 0xac430004, ++ 0x10000002, ++ 0xac230024, ++ 0xac430000, ++ 0x17a3fff4, ++ 0x8c630024, ++ 0x8f820fe0, ++ 0x3bff0080, ++ 0x24420008, ++ 0x34421000, ++ 0x38421000, ++ 0xaf820fe0, ++ 0xaf9f0fe4, ++ 0x1000fe57, ++ 0x3084fffe, ++ 0x10600010, ++ 0x00000000, ++ 0x947d0028, ++ 0x00000000, ++ 0x03bfe820, ++ 0x8fa10004, ++ 0xafa30004, ++ 0x10200003, ++ 0x8c5e0004, ++ 0x10000002, ++ 0xac230024, ++ 0xafa30000, ++ 0x8c610024, ++ 0x17c3fe48, ++ 0xac410000, ++ 0xac400004, ++ 0xac400000, ++ 0x1000fe44, ++ 0x3084fffd, ++ 0x2c620100, ++ 0x1440000e, ++ 0x006a1021, ++ 0x3143007f, ++ 0x01431823, ++ 0x00431823, ++ 0x3062007f, ++ 0xa5620028, ++ 0x00621823, ++ 0x00031902, ++ 0x8f820fe0, ++ 0x2463fff8, ++ 0x00621821, ++ 0x34631000, ++ 0x10000003, ++ 0x38631000, ++ 0x34430100, ++ 0x38630100, ++ 0x8c620004, ++ 0x00000000, ++ 0x10400003, ++ 0xac6b0004, ++ 0x03e00008, ++ 0xac4b0024, ++ 0x03e00008, ++ 0xac6b0000, ++ 0x00000002, ++ 0xa0d0e000, ++ 0x00000000, ++ 0x00001000, ++ 0x00000006, ++ 0x00000008, ++ 0x00000000, ++ 0x00000008, ++ 0x00000002, ++ 0xa0d0d648, ++ 0x00000000, ++ 0x00000888, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x24313200, ++ 0x24313200, ++ 0x24313200, ++ 0x00000000, ++ 0x244d4352, ++ 0x2420436f, ++ 0x70797269, ++ 0x67687420, ++ 0x28632920, ++ 0x4d616467, ++ 0x65204e65, ++ 0x74776f72, ++ 0x6b73204c, ++ 0x74642031, ++ 0x3939352e, ++ 0x20416c6c, ++ 0x20726967, ++ 0x68747320, ++ 0x72657365, ++ 0x72766564, ++ 0x2e004d61, ++ 0x64676520, ++ 0x416d6261, ++ 0x73736164, ++ 0x6f722076, ++ 0x312e3031, ++ 0x00000000, ++ 0x00000001, ++ 0x00000001, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0xfff04000, ++ 0x00000000, ++ 0x0c343e2d, ++ 0x00000000, ++ 0x3c1ca0d1, ++ 0x279c5638, ++ 0x3c1da0d1, ++ 0x27bddfd0, ++ 0x3c08a0d1, ++ 0x2508dfd0, ++ 0xaf878008, ++ 0x0c343c13, ++ 0x00000000, ++ 0x24040003, ++ 0x0097000d, ++ 0x3c08bfc0, ++ 0x35080230, ++ 0x8d080000, ++ 0x00000000, ++ 0x01000008, ++ 0x00000000, ++ 0x27bdffd0, ++ 0xafbf001c, ++ 0xafb10018, ++ 0xafb00014, ++ 0x3c11fff0, ++ 0x00008021, ++ 0x3c180056, ++ 0x37183b79, ++ 0x26190200, ++ 0x17200002, ++ 0x0319001a, ++ 0x0007000d, ++ 0x2401ffff, ++ 0x17210005, ++ 0x00000000, ++ 0x3c018000, ++ 0x17010002, ++ 0x00000000, ++ 0x0006000d, ++ 0x00001012, ++ 0x00101840, ++ 0x3c05a0d1, ++ 0x24a5d6cc, ++ 0x00a32021, ++ 0xa4820000, ++ 0x26100001, ++ 0x2a010200, ++ 0x1420ffea, ++ 0x00000000, ++ 0x3c06a0d1, ++ 0x24c6f9e4, ++ 0x3c07a0d1, ++ 0x24e7d648, ++ 0xace60000, ++ 0x3c08a0d1, ++ 0x2508fb14, ++ 0xace80004, ++ 0x3c09a0d1, ++ 0x2529fc94, ++ 0xace90008, ++ 0x3c0aa0d1, ++ 0x254afcd4, ++ 0xacea000c, ++ 0x3c0ba0d1, ++ 0x256bfba8, ++ 0xaceb0010, ++ 0x3c0ca0d1, ++ 0x258cfbc4, ++ 0xacec0014, ++ 0x3c0da0d1, ++ 0x25adfbe0, ++ 0xaced0018, ++ 0x3c0ea0d1, ++ 0x25cefbfc, ++ 0xacee001c, ++ 0x3c0fa0d1, ++ 0x25effc18, ++ 0xacef0020, ++ 0x3c18a0d1, ++ 0x2718fc34, ++ 0xacf80024, ++ 0x3c19a0d1, ++ 0x2739fc50, ++ 0xacf90028, ++ 0x3c02a0d1, ++ 0x2442fc60, ++ 0xace2002c, ++ 0x3c03a0d1, ++ 0x2463fc70, ++ 0xace30030, ++ 0x3c04a0d1, ++ 0x2484fc80, ++ 0xace40034, ++ 0x3c05a0d1, ++ 0x24a5fcb4, ++ 0xace50038, ++ 0x3c06a0d1, ++ 0x24c6fe08, ++ 0xace6003c, ++ 0x3c08a0d1, ++ 0x2508fe90, ++ 0xace80040, ++ 0x3c09a0d1, ++ 0x2529fa38, ++ 0xace90044, ++ 0x3c0aa0d1, ++ 0x254afa74, ++ 0xacea0048, ++ 0x24100013, ++ 0x3c0ba0d1, ++ 0x256bf9d8, ++ 0x00106080, ++ 0x3c0ea0d1, ++ 0x25ced648, ++ 0x01cc6821, ++ 0xadab0000, ++ 0x26100001, ++ 0x2a010020, ++ 0x1420fff6, ++ 0x00000000, ++ 0x8f988000, ++ 0x00000000, ++ 0xaf000100, ++ 0x8f828000, ++ 0x241903ff, ++ 0xa4590202, ++ 0x00008021, ++ 0x8f868000, ++ 0x24030fff, ++ 0x00102040, ++ 0x24c70380, ++ 0x00e42821, ++ 0xa4a30000, ++ 0x26100001, ++ 0x2a010008, ++ 0x1420fff7, ++ 0x00000000, ++ 0x8f898000, ++ 0x34089c40, ++ 0xad2803a0, ++ 0x8f8b8000, ++ 0x3c0a00ff, ++ 0x354affff, ++ 0xad6a03a4, ++ 0x00008021, ++ 0x8f8f8000, ++ 0x240c0fff, ++ 0x00106840, ++ 0x25f80300, ++ 0x030d7021, ++ 0xa5cc0000, ++ 0x26100001, ++ 0x2a010008, ++ 0x1420fff7, ++ 0x00000000, ++ 0x8f828000, ++ 0x34199c40, ++ 0xac590320, ++ 0x8f848000, ++ 0x3c0300ff, ++ 0x3463ffff, ++ 0xac830324, ++ 0x8f868000, ++ 0x240502ff, ++ 0xa4c50202, ++ 0x3c08a0c0, ++ 0x35080180, ++ 0x3c09a0d1, ++ 0x2529d5b8, ++ 0x250a0028, ++ 0x8d0b0000, ++ 0x8d0c0004, ++ 0xad2b0000, ++ 0xad2c0004, ++ 0x25080008, ++ 0x150afffa, ++ 0x25290008, ++ 0x40026000, ++ 0x00000000, ++ 0xafa20028, ++ 0x24030022, ++ 0x3c04a0e0, ++ 0x34840014, ++ 0xac830000, ++ 0x8fa50028, ++ 0x00000000, ++ 0x34a61001, ++ 0x00c01021, ++ 0xafa60028, ++ 0x3c07ffbf, ++ 0x34e7ffff, ++ 0x00c73824, ++ 0x00e01021, ++ 0xafa70028, ++ 0x40876000, ++ 0x00000000, ++ 0x3c080002, ++ 0x3508d890, ++ 0x3c09fffe, ++ 0x35290130, ++ 0xad280000, ++ 0x8faa0028, ++ 0x3c0bf000, ++ 0x014b5825, ++ 0x01601021, ++ 0xafab0028, ++ 0x01606021, ++ 0x408c6000, ++ 0x00000000, ++ 0x00008021, ++ 0x00107080, ++ 0x022e7821, ++ 0xade00000, ++ 0x26100001, ++ 0x2a010400, ++ 0x1420fffa, ++ 0x00000000, ++ 0x24180001, ++ 0x3c19a0e8, ++ 0xaf380000, ++ 0x24020011, ++ 0x3c03a0f0, ++ 0x34630017, ++ 0xa0620000, ++ 0x3c04f0eb, ++ 0x34840070, ++ 0x3c05fff0, ++ 0x34a54a00, ++ 0xaca40000, ++ 0x3c06fceb, ++ 0x34c60070, ++ 0xaca60000, ++ 0x3c07fff0, ++ 0x34e74700, ++ 0xace00000, ++ 0x00008021, ++ 0x3c08fff0, ++ 0x35080fc0, ++ 0x3c09fff0, ++ 0x35294500, ++ 0xad280000, ++ 0x26100001, ++ 0x2a010004, ++ 0x1420fff8, ++ 0x00000000, ++ 0x00008021, ++ 0x3c0adead, ++ 0x00105980, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xac2a003c, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xac200030, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xac200038, ++ 0x240dffff, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xac2d0014, ++ 0x00107100, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xa42e0000, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xa4200004, ++ 0x24180020, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xa4380008, ++ 0x3c0100d1, ++ 0x002b0821, ++ 0xac200010, ++ 0x26100001, ++ 0x2a010400, ++ 0x1420ffe0, ++ 0x00000000, ++ 0x00008021, ++ 0x001018c0, ++ 0x3c05a0d1, ++ 0x24a5e000, ++ 0x00a32021, ++ 0xac800000, ++ 0x3c07a0d1, ++ 0x24e7e000, ++ 0x24e80004, ++ 0x01033021, ++ 0xacc00000, ++ 0x26100001, ++ 0x2a010009, ++ 0x1420fff3, ++ 0x00000000, ++ 0x24090380, ++ 0x3c0afff0, ++ 0x354a4d00, ++ 0xad490000, ++ 0x3c0ca080, ++ 0x358c009c, ++ 0xad800000, ++ 0x3c0da080, ++ 0x35ad00a0, ++ 0xada00000, ++ 0x3c0e1100, ++ 0x3c0fa080, ++ 0x35ef00a8, ++ 0xadee0000, ++ 0x41010003, ++ 0x00000000, ++ 0x4100ffff, ++ 0x00000000, ++ 0x3c18a080, ++ 0x371800e0, ++ 0x8f190000, ++ 0x3c01a0d1, ++ 0xac39d6c8, ++ 0x0c343d43, ++ 0x03202021, ++ 0x8fb00014, ++ 0x8fbf001c, ++ 0x8fb10018, ++ 0x03e00008, ++ 0x27bd0030, ++ 0x0080b821, ++ 0x3c1cfff0, ++ 0xa3800c84, ++ 0xa3800c88, ++ 0x8f904400, ++ 0x00002021, ++ 0xaf800cbc, ++ 0x240200a8, ++ 0x27830f00, ++ 0x2c5d0040, ++ 0x17a0000c, ++ 0x3c1dffb0, ++ 0x03a3e826, ++ 0xafb74000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x2442ffc0, ++ 0x24630040, ++ 0x1000fff3, ++ 0x26f70040, ++ 0x1040000d, ++ 0x00000000, ++ 0x0002ee00, ++ 0x3c010040, ++ 0x03a1e825, ++ 0x3c01fff0, ++ 0x03a1e826, ++ 0x03a3e826, ++ 0xafb74000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x3c05a080, ++ 0x8f820f08, ++ 0x00000000, ++ 0xaf820fd4, ++ 0xaf820fd0, ++ 0xaca200c4, ++ 0x8f820f10, ++ 0x00000000, ++ 0x00021d82, ++ 0xaf830fc0, ++ 0x00031d80, ++ 0x00431023, ++ 0x3c01a080, ++ 0x00411025, ++ 0xaf820fc4, ++ 0xaf820f10, ++ 0x8f820f14, ++ 0x00000000, ++ 0x00431023, ++ 0x3c01a080, ++ 0x00411025, ++ 0xaf820f14, ++ 0x24030003, ++ 0x279d0f18, ++ 0x24be00c8, ++ 0x27810d00, ++ 0x8fa20000, ++ 0x00000000, ++ 0xafa20010, ++ 0xafc20000, ++ 0xafa10008, ++ 0xafa1000c, ++ 0x8fa20014, ++ 0x00000000, ++ 0xafa2001c, ++ 0x27bd0024, ++ 0x27de0004, ++ 0x24210040, ++ 0x1460fff3, ++ 0x2463ffff, ++ 0x8f820f00, ++ 0x00000000, ++ 0xaf820fc8, ++ 0xaca200c0, ++ 0x27820800, ++ 0x2403000f, ++ 0xac400000, ++ 0x24420004, ++ 0x1460fffd, ++ 0x2463ffff, ++ 0x8f830fc0, ++ 0x00000000, ++ 0xaf834d00, ++ 0x8f834d00, ++ 0x8f830f14, ++ 0x8f820f10, ++ 0x2463fffc, ++ 0xac400000, ++ 0x1443fffe, ++ 0x24420004, ++ 0x24020380, ++ 0xaf824d00, ++ 0x279d0f18, ++ 0x27a10090, ++ 0x8fa20014, ++ 0x8fa30018, ++ 0x00000000, ++ 0x00621823, ++ 0x2c7f0040, ++ 0x17e00009, ++ 0x3c1f0040, ++ 0x37ff0800, ++ 0x03a0f021, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xafe20000, ++ 0x24420040, ++ 0x1000fff6, ++ 0x2463ffc0, ++ 0x10600006, ++ 0x37ff0800, ++ 0x00031e00, ++ 0x03e3f825, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xafe20000, ++ 0x27bd0024, ++ 0x17a1ffe8, ++ 0x00000000, ++ 0x00003821, ++ 0x8fc20014, ++ 0x8fc30018, ++ 0x00000000, ++ 0x00621823, ++ 0x2c7f0040, ++ 0x13e00004, ++ 0x3c1f0040, ++ 0x00030e00, ++ 0x10000002, ++ 0x03e1f825, ++ 0x24030040, ++ 0x37ff0800, ++ 0x241e03e7, ++ 0x00000821, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xafe20000, ++ 0x00230821, ++ 0x4900fffb, ++ 0x00000000, ++ 0x87804002, ++ 0x17c0fff8, ++ 0x27deffff, ++ 0x14e00004, ++ 0x34e74000, ++ 0x03e7f825, ++ 0x1000fff0, ++ 0xaf810c60, ++ 0xaf810c5c, ++ 0x3c01a0d1, ++ 0x8c22d6c8, ++ 0x00000000, ++ 0x3c01a080, ++ 0xac2200e0, ++ 0x3c01a080, ++ 0x8c2000e0, ++ 0xaf800fb4, ++ 0xa7800fb8, ++ 0xa7800fba, ++ 0xa7800fbc, ++ 0xa7800fbe, ++ 0x27820cc0, ++ 0xaf820fdc, ++ 0xaf820fd8, ++ 0x3c02a0d1, ++ 0x2442dacc, ++ 0xaf820c4c, ++ 0xaf820c50, ++ 0x24420400, ++ 0xaf820c54, ++ 0x2402001e, ++ 0x3c03fff0, ++ 0x247d0040, ++ 0xac7d0008, ++ 0x03a01821, ++ 0x1440fffc, ++ 0x2442ffff, ++ 0x3c1dfff0, ++ 0xac7d0008, ++ 0x3c02c704, ++ 0x3442dd7b, ++ 0xaf820c58, ++ 0x3c070000, ++ 0x24e70158, ++ 0x08343fa9, ++ 0x00000000, ++ 0x8e620038, ++ 0x00000000, ++ 0x14400005, ++ 0x8f830c94, ++ 0x12a00022, ++ 0x24630001, ++ 0x10000020, ++ 0xaf830c94, ++ 0xaf820fb4, ++ 0x3262ffc0, ++ 0x00021182, ++ 0x8663002a, ++ 0xa7820fb8, ++ 0x3c02a000, ++ 0xaf820fbc, ++ 0xa7830fba, ++ 0x867e0008, ++ 0x279d0f18, ++ 0x33de0060, ++ 0x03bee821, ++ 0x001ef0c2, ++ 0x03bee821, ++ 0x8fa2001c, ++ 0x3c030c40, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f974c00, ++ 0xac620fb4, ++ 0x8fa30018, ++ 0x2442000c, ++ 0x14430003, ++ 0x00000000, ++ 0x8fa20014, ++ 0x00000000, ++ 0xafa2001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0xaca500e4, ++ 0xaf974c00, ++ 0x03e00008, ++ 0xae60003c, ++ 0x3c0da0d1, ++ 0x25add500, ++ 0x11a00021, ++ 0x00000000, ++ 0x8da90000, ++ 0x00000000, ++ 0x1120001d, ++ 0x00000000, ++ 0x8daa0004, ++ 0x8dab0008, ++ 0x8dac000c, ++ 0x00094740, ++ 0x05010004, ++ 0x00000000, ++ 0x3c08a0d1, ++ 0x2508d638, ++ 0x01485021, ++ 0x00094780, ++ 0x05010007, ++ 0x00000000, ++ 0x1180000d, ++ 0x00000000, ++ 0xad400000, ++ 0x254a0004, ++ 0x1000fffb, ++ 0x258cfffc, ++ 0x11800007, ++ 0x00000000, ++ 0x8d6e0000, ++ 0x256b0004, ++ 0xad4e0000, ++ 0x254a0004, ++ 0x1000fff9, ++ 0x258cfffc, ++ 0x1000ffe1, ++ 0x25ad0010, ++ 0x03e00008, ++ 0x00000000, ++ 0x3c021040, ++ 0xac574ff0, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x8f820ffc, ++ 0x00000000, ++ 0x3042001f, ++ 0x00021080, ++ 0x3c17a0d1, ++ 0x02e2b821, ++ 0x26f7d648, ++ 0x8ef70000, ++ 0x00000000, ++ 0x02e00008, ++ 0x00000000, ++ 0x2402ffff, ++ 0xaf820ffc, ++ 0x8f970fc8, ++ 0x3c021040, ++ 0xac570ff0, ++ 0x8f820f04, ++ 0x26f70010, ++ 0x16e20004, ++ 0xaf970fc8, ++ 0x8f970f00, ++ 0x00000000, ++ 0xaf970fc8, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x03e00008, ++ 0x00000000, ++ 0x3c1fa0d1, ++ 0x27fff02c, ++ 0x1000ffed, ++ 0x8f970ff0, ++ 0x3c0200d1, ++ 0x32f703ff, ++ 0x0017b980, ++ 0x02e2b825, ++ 0xaee0003c, ++ 0x2402ffff, ++ 0xaee20030, ++ 0xaee20014, ++ 0x97830ff4, ++ 0x97820ff8, ++ 0x3c1d0000, ++ 0x27bd0698, ++ 0xa6e30008, ++ 0xa6e20002, ++ 0xaf9f0fe8, ++ 0x03a0f809, ++ 0xa6e2002c, ++ 0x8f9f0fe8, ++ 0x1000ffd9, ++ 0xaee2000c, ++ 0x8f970ff0, ++ 0x3c0200d1, ++ 0x32f703ff, ++ 0x0017b980, ++ 0x02e2b825, ++ 0x97820ff4, ++ 0x3c030000, ++ 0x24630698, ++ 0xa6e20002, ++ 0xaf9f0fe8, ++ 0x0060f809, ++ 0xa6e2002c, ++ 0x8f9f0fe8, ++ 0x1000ffca, ++ 0xaee2000c, ++ 0x8f970ff0, ++ 0x3c0200d1, ++ 0x32f703ff, ++ 0x0017b980, ++ 0x02e2b825, ++ 0x97820ff4, ++ 0x00000000, ++ 0x96e30008, ++ 0xa6e20008, ++ 0x00431026, ++ 0x30420060, ++ 0x1040ffbd, ++ 0x8ee2003c, ++ 0xaee0003c, ++ 0x1040ffba, ++ 0x3c028800, ++ 0xaf820fbc, ++ 0x8ee20038, ++ 0xaee00038, ++ 0x30630060, ++ 0x279d0f18, ++ 0x03a3e821, ++ 0x000318c2, ++ 0x03a3e821, ++ 0x8fa3001c, ++ 0x1040ffaf, ++ 0xaf820fb4, ++ 0x3c020c40, ++ 0xac430fb4, ++ 0x8fa20018, ++ 0x2463000c, ++ 0x14430003, ++ 0x00000000, ++ 0x8fa30014, ++ 0x00000000, ++ 0xafa3001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x1000ffa2, ++ 0x00000000, ++ 0x8f970ff0, ++ 0x3c0200d1, ++ 0xa7970fb8, ++ 0x0017b980, ++ 0x32f7ffc0, ++ 0x02e2b821, ++ 0xaee00030, ++ 0x3c02dead, ++ 0x8ee3003c, ++ 0xaee2003c, ++ 0x8ee20038, ++ 0x1060ff95, ++ 0xaee00038, ++ 0x3c038800, ++ 0xaf830fbc, ++ 0x86e30008, ++ 0x27970f18, ++ 0x30630060, ++ 0x02e3b821, ++ 0x000318c2, ++ 0x02e3b821, ++ 0x8ee3001c, ++ 0x1040ff8a, ++ 0xaf820fb4, ++ 0x3c020c40, ++ 0xac430fb4, ++ 0x8ee20018, ++ 0x2463000c, ++ 0x14430003, ++ 0x00000000, ++ 0x8ee30014, ++ 0x00000000, ++ 0xaee3001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x1000ff7d, ++ 0x00000000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x90410000, ++ 0x00000000, ++ 0x00370825, ++ 0x1000ff76, ++ 0xa0410000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x94410000, ++ 0x00000000, ++ 0x00370825, ++ 0x1000ff6f, ++ 0xa4410000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x8c410000, ++ 0x00000000, ++ 0x00370825, ++ 0x1000ff68, ++ 0xac410000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x90410000, ++ 0x02e0b827, ++ 0x00370824, ++ 0x1000ff61, ++ 0xa0410000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x94410000, ++ 0x02e0b827, ++ 0x00370824, ++ 0x1000ff5a, ++ 0xa4410000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x8c410000, ++ 0x02e0b827, ++ 0x00370824, ++ 0x1000ff53, ++ 0xac410000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x1000ff4f, ++ 0xa0570000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x1000ff4b, ++ 0xa4570000, ++ 0x8f820ff0, ++ 0x8f970ff4, ++ 0x1000ff47, ++ 0xac570000, ++ 0x8f820ff0, ++ 0x00000000, ++ 0x8c420000, ++ 0x1000ff42, ++ 0xaf820ff4, ++ 0x3c01a0c2, ++ 0x8c22c000, ++ 0x00000000, ++ 0xaf820ff0, ++ 0x3c01a0c2, ++ 0x8c22c004, ++ 0x1000ff3a, ++ 0xaf820ff4, ++ 0x3c01a0d1, ++ 0x8c22d5ac, ++ 0x00000000, ++ 0xaf820ff0, ++ 0x3c01a0d1, ++ 0x8c22d5b0, ++ 0x1000ff32, ++ 0xaf820ff4, ++ 0x3c02a0f0, ++ 0xac400000, ++ 0x90570153, ++ 0x00000000, ++ 0xa3970c80, ++ 0x90570157, ++ 0x00000000, ++ 0xa3970c81, ++ 0x9057015b, ++ 0x00000000, ++ 0xa3970c87, ++ 0x9057015f, ++ 0x00000000, ++ 0xa3970c86, ++ 0x90570163, ++ 0x00000000, ++ 0x32f70007, ++ 0xa3970c85, ++ 0x90570193, ++ 0x00000000, ++ 0xa3970c8b, ++ 0x90570197, ++ 0x00000000, ++ 0xa3970c8a, ++ 0x9057019b, ++ 0x00000000, ++ 0x32f70007, ++ 0xa3970c89, ++ 0x9057000b, ++ 0x00000000, ++ 0x32f700e0, ++ 0x00170942, ++ 0x90570047, ++ 0x00000000, ++ 0x32f70078, ++ 0x00370825, ++ 0x90570067, ++ 0x00000000, ++ 0x32f7000f, ++ 0x0017b9c0, ++ 0x00370825, ++ 0x905700c7, ++ 0x00000000, ++ 0x32f7002f, ++ 0x0017bac0, ++ 0x00370825, ++ 0x90570147, ++ 0x00000000, ++ 0x32f7001e, ++ 0x0017bc00, ++ 0x00370825, ++ 0x90570183, ++ 0x00000000, ++ 0x32f70060, ++ 0x0017bc00, ++ 0x00370825, ++ 0xaf810c8c, ++ 0x3c021840, ++ 0x8f970fc8, ++ 0x00000000, ++ 0x8f970ff0, ++ 0x00000000, ++ 0xac570c80, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x3c02a0d1, ++ 0x2442f998, ++ 0xaf800c90, ++ 0xaf800c94, ++ 0x00400008, ++ 0x00000000, ++ 0x87970ff0, ++ 0x3c1300d1, ++ 0xa6770008, ++ 0x3c030000, ++ 0x24630520, ++ 0xaf9f0fe8, ++ 0x0060f809, ++ 0x24020001, ++ 0x8f9f0fe8, ++ 0x1040feda, ++ 0x97970ff0, ++ 0x27830f18, ++ 0x00771821, ++ 0x0017b8c2, ++ 0x02e3b821, ++ 0x3c028800, ++ 0xaf820fbc, ++ 0x8e620038, ++ 0xa7800fb8, ++ 0xaf820fb4, ++ 0x8ee3001c, ++ 0x3c020c40, ++ 0xac430fb4, ++ 0x8ee20018, ++ 0x2463000c, ++ 0x14430004, ++ 0xaee3001c, ++ 0x8ee30014, ++ 0x00000000, ++ 0xaee3001c, ++ 0x4d01ffff, ++ 0x00000000, ++ 0x1000ffdf, ++ 0x00000000, ++ 0x8f820c5c, ++ 0x8f830c60, ++ 0xaf820ff0, ++ 0x1000febe, ++ 0xaf830ff4, ++ 0x23890800, ++ 0x01201821, ++ 0x2402000f, ++ 0x206c0040, ++ 0xac6c0008, ++ 0x01801821, ++ 0x1440fffc, ++ 0x2042ffff, ++ 0xac690008, ++ 0x278b0c98, ++ 0xa5600000, ++ 0x2403ffff, ++ 0xad630014, ++ 0x34020001, ++ 0x34420020, ++ 0xa5620008, ++ 0x278a0e00, ++ 0x01401021, ++ 0x00001821, ++ 0xac400000, ++ 0x24630004, ++ 0x2c6c0100, ++ 0x1580fffc, ++ 0x24420004, ++ 0x3c02a0d1, ++ 0x2442e000, ++ 0xaf820fe0, ++ 0x3c1800d1, ++ 0x01206021, ++ 0x00006821, ++ 0x00007821, ++ 0x00005821, ++ 0x00004021, ++ 0x40026000, ++ 0x00000000, ++ 0x34424001, ++ 0x40826000, ++ 0x3c020000, ++ 0x244206f8, ++ 0x00400008, ++ 0x00000000, +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/atmsar11.regions Thu Mar 11 15:12:37 1999 +@@ -0,0 +1,3 @@ ++ { 0x00000080, 993, }, ++ { 0xa0d0d500, 80, }, ++ { 0xa0d0f000, 978, }, +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/atmsar11.start Thu Mar 11 15:12:38 1999 +@@ -0,0 +1 @@ ++ 0xa0d0f000 +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/atmtcp.c Tue Feb 9 15:49:57 1999 +@@ -0,0 +1,339 @@ ++/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */ + ++/* Written 1997,1998 by Werner Almesberger, EPFL LRC/ICA */ + -+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 */ -+ if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <= -+ MID_MAX_BUF_SIZE) -+ size = MID_MAX_BUF_SIZE; -+ eni_vcc->recv = (u32 *) eni_alloc_mem(eni_dev,&size); -+ DPRINTK("rx at 0x%p\n",eni_vcc->recv); -+ eni_vcc->words = size >> 2; -+ if (!eni_vcc->recv) return -ENOBUFS; -+ eni_vcc->rx = vcc->qos.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; ++#include ++#include ++#include ++#include ++#include "../../net/atm/tunable.h" /* @@@ fix this */ ++#include "../../net/atm/protocols.h" /* @@@ fix this */ ++ ++ ++#define PRIV(dev) ((struct atmtcp_dev_data *) ((dev)->dev_data)) ++ ++ ++struct atmtcp_dev_data { ++ struct atm_vcc *vcc; /* control VCC; NULL if detached */ ++ int persist; /* non-zero if persistent */ ++}; ++ ++ ++#define DEV_LABEL "atmtcp" ++ ++#define MAX_VPI_BITS 8 /* simplifies life */ ++#define MAX_VCI_BITS 16 ++ ++ ++static void atmtcp_v_dev_close(struct atm_dev *dev) ++{ ++ MOD_DEC_USE_COUNT; +} + + -+static int open_rx_second(struct atm_vcc *vcc) ++static int atmtcp_v_open(struct atm_vcc *vcc,short vpi,int vci) +{ -+ u32 *here; -+ struct eni_dev *eni_dev; -+ struct eni_vcc *eni_vcc; -+ unsigned long size; -+ int order; ++ int error; + -+ 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; -+ writel(0,here+1); /* descr, read = 0 */ -+ writel(0,here+2); /* 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 */ -+ writel(((vcc->qos.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),here); ++ 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; ++ vcc->flags |= ATM_VF_ADDR | ATM_VF_READY; + return 0; +} + + -+static void close_rx(struct atm_vcc *vcc) ++static void atmtcp_v_close(struct atm_vcc *vcc) +{ -+ u32 *here; -+ struct eni_dev *eni_dev; -+ struct eni_vcc *eni_vcc; -+ unsigned long flags; -+ u32 tmp; ++ vcc->flags &= ~(ATM_VF_READY | ATM_VF_ADDR); ++} + -+ 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 */ -+ writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH << -+ MID_VCI_MODE_SHIFT),here); -+ /* wait for receiver to become idle */ -+ udelay(27); -+ /* discard pending cell */ -+ writel(readl(here) & ~MID_VCI_IN_SERVICE,here); -+ /* 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%x\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 atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) ++{ ++ struct atm_cirange ci; ++ struct atm_vcc *vcc; ++ ++ if (cmd != ATM_SETCIRANGE) return -EINVAL; ++ if (copy_from_user(&ci,(void *) arg,sizeof(ci))) return -EFAULT; ++ 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; ++ for (vcc = dev->vccs; vcc; vcc = vcc->next) ++ if ((vcc->vpi >> ci.vpi_bits) || ++ (vcc->vci >> ci.vci_bits)) return -EBUSY; ++ dev->ci_range = ci; ++ return 0; +} + + -+static int start_rx(struct atm_dev *dev) ++static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb) +{ -+ struct eni_dev *eni_dev; ++ struct atmtcp_dev_data *dev_data; ++ struct atm_vcc *out_vcc; ++ struct sk_buff *new_skb; ++ struct atmtcp_hdr *hdr; ++ int size; + -+ 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; ++ dev_data = PRIV(vcc->dev); ++ if (dev_data) out_vcc = dev_data->vcc; ++ if (!dev_data || !out_vcc) { ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ if (dev_data) return 0; ++ vcc->stats->tx_err++; ++ return -ENOLINK; + } -+ 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 = readl(eni_dev->reg+MID_SERV_WRITE); -+ writel(0,eni_dev->reg+MID_DMA_WR_RX); ++ size = skb->len+sizeof(struct atmtcp_hdr); ++ if (!atm_charge(out_vcc,atm_pdu2truesize(size))) new_skb = NULL; ++ else { ++ new_skb = alloc_skb(size,GFP_ATOMIC); ++ if (!new_skb) ++ atm_return(out_vcc,atm_pdu2truesize(size)); ++ } ++ if (!new_skb) { ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ vcc->stats->tx_err++; ++ return -ENOBUFS; ++ } ++ hdr = (void *) skb_put(new_skb,sizeof(struct atmtcp_hdr)); ++ hdr->vpi = htons(vcc->vpi); ++ hdr->vci = htons(vcc->vci); ++ hdr->length = htonl(skb->len); ++ memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ out_vcc->push(out_vcc,new_skb); + return 0; +} + + -+/*----------------------------------- TX ------------------------------------*/ ++static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page) ++{ ++ struct atmtcp_dev_data *dev_data = PRIV(dev); + ++ if (*pos) return 0; ++ if (!dev_data->persist) return sprintf(page,"ephemeral\n"); ++ return sprintf(page,"persistent, %sconnected\n", ++ dev_data->vcc ? "" : "dis"); ++} + -+enum enq_res { enq_ok,enq_next,enq_jam }; + ++static void atmtcp_c_close(struct atm_vcc *vcc) ++{ ++ struct atm_dev *atmtcp_dev; ++ struct atmtcp_dev_data *dev_data; + -+static inline void put_dma(int chan,u32 *dma,int *j,unsigned long paddr, -+ u32 size) ++ atmtcp_dev = (struct atm_dev *) vcc->dev_data; ++ dev_data = PRIV(atmtcp_dev); ++ dev_data->vcc = NULL; ++ if (dev_data->persist) return; ++ kfree(dev_data); ++ shutdown_atm_dev(atmtcp_dev); ++ vcc->dev_data = NULL; ++} ++ ++ ++static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) +{ -+ u32 init,words; ++ struct atm_dev *dev; ++ struct atmtcp_hdr *hdr; ++ struct atm_vcc *out_vcc; ++ struct sk_buff *new_skb; + -+ DPRINTK("put_dma: 0x%lx+0x%x\n",paddr,size); -+ EVENT("put_dma: 0x%lx+0x%lx\n",paddr,size); -+#if 0 /* don't complain anymore */ -+ if (paddr & 3) -+ printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr); -+ if (size & 3) -+ printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size); -+#endif -+ if (paddr & 3) { -+ init = 4-(paddr & 3); -+ if (init > size || size < 7) init = size; -+ DPRINTK("put_dma: %lx DMA: %d/%d bytes\n",paddr,init,size); -+ dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) | -+ (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); -+ paddr += init; -+ size -= init; -+ } -+ words = size >> 2; -+ size &= 3; -+ if (words && (paddr & 31)) { -+ init = 8-((paddr & 31) >> 2); -+ if (init > words) init = words; -+ DPRINTK("put_dma: %lx DMA: %d/%d words\n",paddr,init,words); -+ dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | -+ (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); -+ paddr += init << 2; -+ words -= init; -+ } -+#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */ -+ if (words & ~15) { -+ DPRINTK("put_dma: %lx DMA: %d*16/%d words\n",paddr,words >> 4, -+ words); -+ dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT) -+ | (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~15) << 2; -+ words &= 15; -+ } -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */ -+ if (words & ~7) { -+ DPRINTK("put_dma: %lx DMA: %d*8/%d words\n",paddr,words >> 3, -+ words); -+ dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT) -+ | (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~7) << 2; -+ words &= 7; -+ } -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */ -+ if (words & ~3) { -+ DPRINTK("put_dma: %lx DMA: %d*4/%d words\n",paddr,words >> 2, -+ words); -+ dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT) -+ | (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~3) << 2; -+ words &= 3; -+ } -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */ -+ if (words & ~1) { -+ DPRINTK("put_dma: %lx DMA: %d*2/%d words\n",paddr,words >> 1, -+ words); -+ dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT) -+ | (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); -+ paddr += (words & ~1) << 2; -+ words &= 1; -+ } -+#endif -+ if (words) { -+ DPRINTK("put_dma: %lx DMA: %d words\n",paddr,words); -+ dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) | -+ (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); -+ paddr += words << 2; ++ if (!skb->len) return 0; ++ dev = vcc->dev_data; ++ hdr = (void *) skb->data; ++ for (out_vcc = dev->vccs; out_vcc; out_vcc = out_vcc->next) ++ if (out_vcc->vpi == ntohs(hdr->vpi) && ++ out_vcc->vci == ntohs(hdr->vci) && ++ out_vcc->qos.rxtp.traffic_class != ATM_NONE) ++ break; ++ if (!out_vcc) { ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ vcc->stats->tx_err++; ++ return 0; + } -+ if (size) { -+ DPRINTK("put_dma: %lx DMA: %d bytes\n",paddr,size); -+ dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) | -+ (chan << MID_DMA_CHAN_SHIFT); -+ dma[(*j)++] = virt_to_bus((void *) paddr); ++ skb_pull(skb,sizeof(struct atmtcp_hdr)); ++ if (!atm_charge(out_vcc,atm_pdu2truesize(skb->len))) new_skb = NULL; ++ else { ++ new_skb = alloc_skb(skb->len,GFP_KERNEL); ++ if (!new_skb) atm_return(out_vcc,atm_pdu2truesize(skb->len)); ++ } ++ if (!new_skb) { ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ return -ENOBUFS; + } ++ memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ out_vcc->push(out_vcc,new_skb); ++ return 0; +} + + -+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; -+ u32 dma_rd,dma_wr; -+ u32 size; /* in words */ -+ int aal5,dma_size,i,j; ++/* ++ * Device operations for the virtual ATM devices created by ATMTCP. ++ */ + -+ DPRINTK(">do_tx\n"); -+ NULLCHECK(skb); -+ EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len); -+ vcc = ATM_SKB(skb)->vcc; -+ NULLCHECK(vcc); -+ eni_dev = ENI_DEV(vcc->dev); -+ NULLCHECK(eni_dev); -+ eni_vcc = ENI_VCC(vcc); -+ tx = eni_vcc->tx; -+ NULLCHECK(tx); -+#if 0 /* Enable this for testing with the "align" program */ -+ { -+ unsigned int hack = *((char *) skb->data)-'0'; + -+ if (hack < 8) { -+ skb->data += hack; -+ skb->len -= hack; -+ } -+ } -+#endif -+#if 0 /* should work now */ -+ 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); -+#endif -+ /* -+ * 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 :-) -+ */ ++static struct atmdev_ops atmtcp_v_dev_ops = { ++ atmtcp_v_dev_close, ++ atmtcp_v_open, ++ atmtcp_v_close, ++ atmtcp_v_ioctl, ++ NULL, /* no getsockopt */ ++ NULL, /* no setsockopt */ ++ atmtcp_v_send, ++ NULL, /* no direct writes */ ++ NULL, /* no send_oam */ ++ NULL, /* no phy_put */ ++ NULL, /* no phy_get */ ++ NULL, /* no feedback */ ++ NULL, /* no change_qos */ ++ NULL, /* no free_rx_skb */ ++ atmtcp_v_proc /* proc_read */ ++}; + -+ /* check space in buffer */ -+ if (!(aal5 = vcc->qos.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 */ -+ } -+ /* -+ * Can I move tx_pos by size bytes without getting closer than TX_GAP -+ * to the read pointer ? TX_GAP means to leave some space for what -+ * the manual calls "too close". -+ */ -+ if (!NEPMOK(tx->tx_pos,size+TX_GAP, -+ readl(eni_dev->reg+MID_TX_RDPTR(tx->index)),tx->words)) { -+ DPRINTK(DEV_LABEL "(itf %d): TX full (size %ld)\n", -+ vcc->dev->number,size); -+ return enq_next; -+ } -+ /* check DMA */ -+ dma_wr = readl(eni_dev->reg+MID_DMA_WR_TX); -+ dma_rd = readl(eni_dev->reg+MID_DMA_RD_TX); -+ dma_size = 3; /* JK for descriptor and final fill, plus final size -+ mis-alignment fix */ -+DPRINTK("iovcnt = %d\n",ATM_SKB(skb)->iovcnt); -+ if (!ATM_SKB(skb)->iovcnt) dma_size += 5; -+ else dma_size += 5*ATM_SKB(skb)->iovcnt; -+ if (dma_size > TX_DMA_BUF) { -+ printk(KERN_CRIT 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; -+ eni_dev->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 (!ATM_SKB(skb)->iovcnt) -+ if (aal5) -+ put_dma(tx->index,eni_dev->dma,&j, -+ (unsigned long) skb->data,skb->len); -+ else put_dma(tx->index,eni_dev->dma,&j, -+ (unsigned long) skb->data+4,skb->len-4); -+ else { -+DPRINTK("doing direct send\n"); -+ for (i = 0; i < ATM_SKB(skb)->iovcnt; i++) -+ put_dma(tx->index,eni_dev->dma,&j,(unsigned long) -+ ((struct iovec *) skb->data)[i].iov_base, -+ ((struct iovec *) skb->data)[i].iov_len); -+ } -+ if (skb->len & 3) -+ put_dma(tx->index,eni_dev->dma,&j, -+ (unsigned long) zeroes,4-(skb->len & 3)); -+ /* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */ -+ eni_dev->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)) | -+ (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0); -+ DPRINTK("size: %ld, len:%d\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++) { -+ writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*2); -+ writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*2+1); -+ dma_wr = (dma_wr+1) & (NR_DMA_TX-1); -+ } -+ ENI_PRV_POS(skb) = tx->tx_pos; -+ ENI_PRV_SIZE(skb) = 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); -+ writel(dma_wr,eni_dev->reg+MID_DMA_WR_TX); -+ skb_queue_tail(&eni_dev->tx_queue,skb); -+queued++; -+ return enq_ok; -+} + ++/* ++ * Device operations for the ATMTCP control device. ++ */ + -+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 struct atmdev_ops atmtcp_c_dev_ops = { ++ NULL, /* no dev_close */ ++ NULL, /* no open */ ++ atmtcp_c_close, ++ NULL, /* no ioctl */ ++ NULL, /* no getsockopt */ ++ NULL, /* no setsockopt */ ++ atmtcp_c_send, ++ NULL, /* no sg_send */ ++ NULL, /* no send_oam */ ++ NULL, /* no phy_put */ ++ NULL, /* no phy_get */ ++ NULL, /* no feedback */ ++ NULL, /* no change_qos */ ++ NULL, /* no free_rx_skb */ ++ NULL /* no proc_read */ ++}; + + -+static void dequeue_tx(struct atm_dev *dev) ++static struct atm_dev atmtcp_control_dev = { ++ &atmtcp_c_dev_ops, ++ NULL, /* no PHY */ ++ "atmtcp", /* 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 */ ++}; ++ ++ ++static int atmtcp_create(int itf,int persist,struct atm_dev **result) +{ -+ struct eni_dev *eni_dev; -+ struct atm_vcc *vcc; -+ struct sk_buff *skb; -+ struct eni_tx *tx; ++ struct atmtcp_dev_data *dev_data; ++ struct atm_dev *dev; + -+ NULLCHECK(dev); -+ eni_dev = ENI_DEV(dev); -+ NULLCHECK(eni_dev); -+ while ((skb = skb_dequeue(&eni_dev->tx_queue))) { -+ vcc = ATM_SKB(skb)->vcc; -+ NULLCHECK(vcc); -+ tx = ENI_VCC(vcc)->tx; -+ NULLCHECK(ENI_VCC(vcc)->tx); -+ DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb), -+ readl(eni_dev->reg+MID_TX_DESCRSTART(tx->index))); -+ if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) == -+ readl(eni_dev->reg+MID_TX_DESCRSTART(tx->index))) { -+ skb_queue_head(&eni_dev->tx_queue,skb); -+ break; -+ } -+ ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb); -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ vcc->stats->tx++; -+ wake_up(&eni_dev->tx_wait); -+dma_complete++; ++ dev_data = kmalloc(sizeof(*dev_data),GFP_KERNEL); ++ if (!dev_data) return -ENOMEM; ++ dev = atm_dev_register(DEV_LABEL,&atmtcp_v_dev_ops,itf,0); ++ if (!dev) { ++ kfree(dev_data); ++ return itf == -1 ? -ENOMEM : -EBUSY; + } ++ MOD_INC_USE_COUNT; ++ dev->ci_range.vpi_bits = MAX_VPI_BITS; ++ dev->ci_range.vci_bits = MAX_VCI_BITS; ++ PRIV(dev) = dev_data; ++ PRIV(dev)->vcc = NULL; ++ PRIV(dev)->persist = persist; ++ if (result) *result = dev; ++ return 0; +} + + -+static struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr) ++int atmtcp_attach(struct atm_vcc *vcc,int itf) +{ -+ int i; ++ struct atm_dev *dev; + -+ for (i = !ubr; i < NR_CHAN; i++) -+ if (!eni_dev->tx[i].send) return eni_dev->tx+i; -+ return NULL; -+} -+ -+ -+static int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre, -+ int *res,int unlimited) -+{ -+ static const int pre_div[] = { 4,16,128,2048 }; -+ /* 2^(((x+2)^2-(x+2))/2+1) */ -+ -+ if (unlimited) *pre = *res = 0; ++ dev = NULL; ++ if (itf != -1) dev = atm_find_dev(itf); ++ if (dev) { ++ if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; ++ if (PRIV(dev)->vcc) return -EBUSY; ++ } + else { -+ if (*pcr > 0) { -+ int div; -+ -+ for (*pre = 0; *pre < 3; (*pre)++) -+ if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break; -+ div = pre_div[*pre]**pcr; -+ DPRINTK("min div %d\n",div); -+ *res = TS_CLOCK/div-1; -+ } -+ else { -+ int div; ++ int error; + -+ if (!*pcr) *pcr = eni_dev->tx_bw+reserved; -+ for (*pre = 3; *pre >= 0; (*pre)--) -+ if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break; -+ if (*pre < 3) (*pre)++; /* else fail later */ -+ div = pre_div[*pre]*-*pcr; -+ 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; ++ error = atmtcp_create(itf,0,&dev); ++ if (error) return error; + } -+ *pcr = TS_CLOCK/pre_div[*pre]/(*res+1); -+ DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res); -+ return 0; ++ PRIV(dev)->vcc = vcc; ++ bind_vcc(vcc,&atmtcp_control_dev); ++ vcc->flags |= ATM_VF_READY | ATM_VF_META; ++ vcc->dev_data = dev; ++ (void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */ ++ vcc->stats = &atmtcp_control_dev.stats.aal5; ++ return dev->number; +} + + -+static int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp, -+ int set_rsv,int set_shp) ++int atmtcp_create_persistent(int itf) +{ -+ struct eni_dev *eni_dev = ENI_DEV(vcc->dev); -+ struct eni_vcc *eni_vcc = ENI_VCC(vcc); -+ struct eni_tx *tx; -+ unsigned long size,mem; -+ int rate,ubr,unlimited,new_tx; -+ int pre,res,order; -+ int error; -+ -+ rate = atm_pcr_goal(txtp); -+ ubr = txtp->traffic_class == ATM_UBR; -+ unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR || -+ rate >= ATM_OC3_PCR); -+ if (!unlimited) { -+ size = txtp->max_sdu*3; /* @@@ improve */ -+ if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <= -+ MID_MAX_BUF_SIZE) -+ size = MID_MAX_BUF_SIZE; -+ } -+ else { -+ if (eni_dev->ubr) { -+ eni_vcc->tx = eni_dev->ubr; -+ txtp->pcr = ATM_OC3_PCR; -+ return 0; -+ } -+ size = UBR_BUFFER; -+ } -+ new_tx = !eni_vcc->tx; -+ mem = 0; /* for gcc */ -+ if (!new_tx) tx = eni_vcc->tx; -+ else { -+ mem = eni_alloc_mem(eni_dev,&size); -+ if (!mem) return -ENOBUFS; -+ tx = alloc_tx(eni_dev,unlimited); -+ if (!tx) { -+ eni_free_mem(eni_dev,mem,size); -+ return -EBUSY; -+ } -+ DPRINTK("got chan %d\n",tx->index); -+ tx->reserved = tx->shaping = 0; -+ tx->send = (u32 *) mem; -+ tx->words = size >> 2; -+ skb_queue_head_init(&tx->backlog); -+ for (order = 0; size > (1 << (order+10)); order++); -+ writel((order << MID_SIZE_SHIFT) | -+ ((tx->send-eni_dev->ram) >> MID_LOC_SKIP), -+ eni_dev->reg+MID_TX_PLACE(tx->index)); -+ tx->tx_pos = readl(eni_dev->reg+MID_TX_DESCRSTART(tx->index)) & -+ MID_DESCR_START; -+ } -+ error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited); -+ if (!error && txtp->min_pcr > rate) error = -EINVAL; -+ if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR && -+ txtp->max_pcr < rate) error = -EINVAL; -+ if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved) -+ error = -EINVAL; -+ if (!error && set_rsv && !set_shp && rate < tx->shaping) -+ error = -EINVAL; -+ if (!error && !set_rsv && rate > tx->reserved && !ubr) -+ error = -EINVAL; -+ if (error) { -+ if (new_tx) { -+ tx->send = NULL; -+ eni_free_mem(eni_dev,mem,size); -+ } -+ return error; -+ } -+ txtp->pcr = rate; -+ if (set_rsv && !ubr) { -+ eni_dev->tx_bw += tx->reserved; -+ tx->reserved = rate; -+ eni_dev->tx_bw -= rate; -+ } -+ if (set_shp || (unlimited && new_tx)) { -+ if (unlimited && new_tx) eni_dev->ubr = tx; -+ tx->prescaler = pre; -+ tx->resolution = res; -+ tx->shaping = rate; -+ } -+ if (set_shp) eni_vcc->tx = tx; -+ DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping); -+ return 0; ++ return atmtcp_create(itf,1,NULL); +} + + -+static int open_tx_first(struct atm_vcc *vcc) ++int atmtcp_remove_persistent(int itf) +{ -+ ENI_VCC(vcc)->tx = NULL; -+ if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; -+ ENI_VCC(vcc)->txing = 0; -+ return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1); -+} -+ ++ struct atm_dev *dev; ++ struct atmtcp_dev_data *dev_data; + -+static int open_tx_second(struct atm_vcc *vcc) -+{ -+ return 0; /* nothing to do */ ++ dev = atm_find_dev(itf); ++ if (!dev) return -ENODEV; ++ if (dev->ops != &atmtcp_v_dev_ops) return -EMEDIUMTYPE; ++ dev_data = PRIV(dev); ++ if (!dev_data->persist) return 0; ++ dev_data->persist = 0; ++ if (PRIV(dev)->vcc) return 0; ++ kfree(dev_data); ++ shutdown_atm_dev(dev); ++ return 0; +} + + -+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); -+ } -+ /* -+ * Looping a few times in here is probably far cheaper than keeping -+ * track of TX completions all the time, so let's poll a bit ... -+ */ -+ while (readl(eni_dev->reg+MID_TX_RDPTR(eni_vcc->tx->index)) != -+ readl(eni_dev->reg+MID_TX_DESCRSTART(eni_vcc->tx->index))) -+ schedule(); -+ 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->reserved; -+ } -+ eni_vcc->tx = NULL; -+} -+ ++#ifdef MODULE + -+static int start_tx(struct atm_dev *dev) ++int init_module(void) +{ -+ 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); -+ writel(0,eni_dev->reg+MID_DMA_WR_TX); -+ for (i = 0; i < NR_CHAN; i++) { -+ eni_dev->tx[i].send = NULL; -+ eni_dev->tx[i].index = i; -+ } ++ atm_tcp_ops.attach = atmtcp_attach; ++ atm_tcp_ops.create_persistent = atmtcp_create_persistent; ++ atm_tcp_ops.remove_persistent = atmtcp_remove_persistent; + return 0; +} + -+ -+/*--------------------------------- common ----------------------------------*/ -+ -+ -+#if 0 /* may become useful again when tuning things */ -+ -+static void foo(void) ++void cleanup_module(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); +} + -+#endif -+ ++#else + -+static void misc_int(struct atm_dev *dev,unsigned long reason) -+{ -+ struct eni_dev *eni_dev; ++struct atm_tcp_ops atm_tcp_ops = { ++ atmtcp_attach, /* attach */ ++ atmtcp_create_persistent, /* create_persistent */ ++ atmtcp_remove_persistent /* remove_persistent */ ++}; + -+ DPRINTK(">misc_int\n"); -+ eni_dev = ENI_DEV(dev); -+ if (reason & MID_STAT_OVFL) { -+ EVENT("stat overflow\n",0,0); -+ eni_dev->lost += readl(eni_dev->reg+MID_STAT) & MID_OVFL_TRASH; -+ } -+ if (reason & MID_SUNI_INT) { -+ EVENT("SUNI int\n",0,0); -+ dev->phy->interrupt(dev); -+#if 0 -+ foo(); +#endif -+ } -+ if (reason & MID_DMA_ERR_ACK) { -+ printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " -+ "error\n",dev->number); -+ EVENT("---dump ends here---\n",0,0); -+ printk(KERN_NOTICE "---recent events---\n"); -+ event_dump(); -+ } -+ 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(); -+ } -+} ++ +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/eni.c Wed Apr 21 17:36:48 1999 +@@ -0,0 +1,2259 @@ ++/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ ++ ++/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ ++ + ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for xtime */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+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; ++#include "tonga.h" ++#include "midway.h" ++#include "suni.h" ++#include "eni.h" + -+ DPRINTK(">eni_int\n"); -+ dev = dev_id; -+ eni_dev = ENI_DEV(dev); -+ while ((reason = readl(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_DMA_ERR_ACK | -+ MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) { -+ EVENT("misc interrupt\n",0,0); -+ misc_int(dev,reason); -+ } -+ } -+} + ++/* ++ * TODO: ++ * ++ * Show stoppers ++ * none ++ * ++ * Minor ++ * - OAM support ++ * - fix bugs listed below ++ */ + -+/*--------------------------------- entries ---------------------------------*/ ++/* ++ * KNOWN BUGS: ++ * ++ * - may run into JK-JK bug and deadlock ++ * - should allocate UBR channel first ++ * - buffer space allocation algorithm is stupid ++ * (RX: should be maxSDU+maxdelay*rate ++ * TX: should be maxSDU+min(maxSDU,maxdelay*rate) ) ++ * - doesn't support OAM cells ++ * - eni_put_free may hang if not putting memory fragments that _complete_ ++ * 2^n block (never happens in real life, though) ++ * - keeps IRQ even if initialization fails ++ */ + + -+static const char *media_name[] __initdata = { -+ "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 */ -+}; ++#if 0 ++#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) ++#else ++#define DPRINTK(format,args...) ++#endif + + -+#define SET_SEPROM \ -+ ({ if (!error && !pci_error) { \ -+ pci_error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \ -+ udelay(10); /* 10 usecs */ \ -+ } }) -+#define GET_SEPROM \ -+ ({ if (!error && !pci_error) { \ -+ pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \ -+ udelay(10); /* 10 usecs */ \ -+ } }) ++#ifndef CONFIG_ATM_ENI_TUNE_BURST ++#define CONFIG_ATM_ENI_BURST_TX_8W ++#define CONFIG_ATM_ENI_BURST_RX_4W ++#endif + + -+__initfunc(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; ++#ifndef CONFIG_ATM_ENI_DEBUG + -+ 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 " -+ "(0x%02x)\n",dev->number,pci_error); -+ error = -EIO; -+ } -+ return error; -+} + ++#define NULLCHECK(x) + -+#undef SET_SEPROM -+#undef GET_SEPROM ++#define EVENT(s,a,b) + + -+__initfunc(static int get_esi_fpga(struct atm_dev *dev,unsigned long base)) ++static void event_dump(void) +{ -+ 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] = readl(&eprom->mac+((i & ~3) | (3-(i & 3)))); -+ return 0; +} + + -+__initfunc(static int eni_init(struct atm_dev *dev)) -+{ -+ struct midway_eprom *eprom; -+ struct eni_dev *eni_dev; -+ struct pci_dev *pci_dev; -+ unsigned int real_base,base; -+ unsigned char revision; -+ int error,i,last; ++#else + -+ DPRINTK(">eni_init\n"); -+ dev->ci_range.vpi_bits = 0; -+ dev->ci_range.vci_bits = NR_VCI_LD; -+ dev->link_rate = ATM_OC3_PCR; -+ eni_dev = ENI_DEV(dev); -+ pci_dev = eni_dev->pci_dev; -+ real_base = pci_dev->base_address[0] & MEM_VALID; /* strip flags */ -+ eni_dev->irq = pci_dev->irq; -+ error = pci_read_config_byte(pci_dev,PCI_REVISION_ID,&revision); -+ if (error) { -+ printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%02x\n", -+ dev->number,error); -+ return -EINVAL; -+ } -+ if ((error = pci_write_config_word(pci_dev,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 " -+ "(0x%02x)\n",dev->number,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) ioremap_nocache(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 (readl(&eprom->magic) != ENI155_MAGIC) { -+ printk("\n"); -+ printk(KERN_ERR KERN_ERR DEV_LABEL "(itf %d): bad " -+ "magic - expected 0x%X, got 0x%X\n",dev->number, -+ ENI155_MAGIC,readl(&eprom->magic)); -+ return -EINVAL; -+ } -+ } -+ eni_dev->phy = (u32 *) (base+PHY_BASE); -+ eni_dev->reg = (u32 *) (base+REG_BASE); -+ eni_dev->ram = (u32 *) (base+RAM_BASE); -+ last = (MAP_MAX_SIZE-RAM_BASE)/4; -+ for (i = last-RAM_INCREMENT; i >= 0; -+ i -= RAM_INCREMENT) { -+ writel(0x55555555,eni_dev->ram+i); -+ if (readl(eni_dev->ram+i) != 0x55555555) last = i; -+ else { -+ writel(0xAAAAAAAA,eni_dev->ram+i); -+ if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i; -+ else writel(i,eni_dev->ram+i); -+ } -+ } -+ for (i = 0; i < last; i += RAM_INCREMENT) -+ if (readl(eni_dev->ram+i) != i) break; -+ eni_dev->mem = i << 2; -+ memset_io(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 (!(readl(eni_dev->reg+MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) { -+ printk(")\n"); -+ printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%0x\n", -+ dev->number,readl(eni_dev->reg+MID_RES_ID_MCON)); -+ return -EINVAL; ++ ++/* ++ * 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]); + } -+ 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, -+ readl(eni_dev->reg+MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA", -+ media_name[readl(eni_dev->reg+MID_RES_ID_MCON) & DAUGTHER_ID]); -+ return suni_init(dev); +} + + -+__initfunc(static int eni_start(struct atm_dev *dev)) ++#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; ++ ++static u32 *zeroes = NULL; /* aligned "magic" zeroes */ ++ ++ ++/*-------------------------------- 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; -+ u32 *buf; -+ unsigned long buffer_mem; -+ int error; + -+ DPRINTK(">eni_start\n"); ++ int i; ++ + eni_dev = ENI_DEV(dev); -+ if (request_irq(eni_dev->irq,&eni_int,SA_SHIRQ,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 = pci_write_config_word(eni_dev->pci_dev,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 (0x%02x)\n",dev->number,error); -+ return error; -+ } -+ if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL, -+ END_SWAP_DMA))) { -+ printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap " -+ "(0x%02x)\n",dev->number,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%p,rx 0x%p, tx 0x%p,srv 0x%p,buf 0x%p\n", -+ eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma,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; ++ 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%p: %ld\n",i, ++ 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%p: %ld\n",i, ++ 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 = 0; -+ eni_put_free(eni_dev,(unsigned long) buf,buffer_mem); -+ memset_io(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->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 &= ~(unsigned long) (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 void rx_ident_err(struct atm_vcc *vcc) ++{ ++ struct atm_dev *dev; ++ struct eni_vcc *eni_vcc; ++ ++ dev = vcc->dev; ++ /* immediately halt adapter */ ++ writel(readl(ENI_DEV(dev)->reg+MID_MC_S) & ~(MID_DMA_ENABLE | ++ MID_TX_ENABLE | MID_RX_ENABLE),ENI_DEV(dev)->reg+MID_MC_S); ++ /* 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%lx, rx pos 0x%lx, descr value " ++ "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos, ++ readl(eni_vcc->recv+eni_vcc->descr)); ++ printk(KERN_ALERT " last 0x%p, servicing %d\n",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; ++ u32 dma_rd,dma_wr; ++ u32 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); ++ ENI_PRV_SIZE(skb) = size+skip; ++ /* PDU plus descriptor */ ++ ATM_SKB(skb)->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++] = virt_to_bus((void *) paddr); ++ paddr += init << 2; ++ words -= init; ++ } ++#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */ ++ if (words & ~15) { ++ dma[j++] = MID_DT_16W | ((words >> 4) << ++ MID_DMA_COUNT_SHIFT) | (vcc->vci << ++ MID_DMA_VCI_SHIFT); ++ dma[j++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~15) << 2; ++ words &= 15; ++ } ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */ ++ if (words & ~7) { ++ dma[j++] = MID_DT_8W | ((words >> 3) << ++ MID_DMA_COUNT_SHIFT) | (vcc->vci << ++ MID_DMA_VCI_SHIFT); ++ dma[j++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~7) << 2; ++ words &= 7; ++ } ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */ ++ if (words & ~3) { ++ dma[j++] = MID_DT_4W | ((words >> 2) << ++ MID_DMA_COUNT_SHIFT) | (vcc->vci << ++ MID_DMA_VCI_SHIFT); ++ dma[j++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~3) << 2; ++ words &= 3; ++ } ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */ ++ if (words & ~1) { ++ dma[j++] = MID_DT_2W | ((words >> 1) << ++ MID_DMA_COUNT_SHIFT) | (vcc->vci << ++ MID_DMA_VCI_SHIFT); ++ dma[j++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~1) << 2; ++ words &= 1; ++ } ++#endif ++ if (words) { ++ dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) ++ | (vcc->vci << MID_DMA_VCI_SHIFT); ++ dma[j++] = virt_to_bus((void *) 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); ++ return -1; ++ } ++ dma[j-2] |= MID_DMA_END; ++ j = j >> 1; ++ dma_wr = readl(eni_dev->reg+MID_DMA_WR_RX); ++ dma_rd = readl(eni_dev->reg+MID_DMA_RD_RX); ++ /* ++ * Can I move the dma_wr pointer by 2j+1 positions without overwriting ++ * data that hasn't been read (position of dma_rd) yet ? + */ -+ writel(0xffffffff,eni_dev->reg+MID_IE); -+ 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; -+ writel(readl(eni_dev->reg+MID_MC_S) | (1 << MID_INT_SEL_SHIFT) | -+ MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE, -+ eni_dev->reg+MID_MC_S); -+ /* Tonga uses SBus INTReq1 */ -+ (void) readl(eni_dev->reg+MID_ISA); /* clear Midway interrupts */ -+ return 0; ++ 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); ++ return -1; ++ } ++ for (i = 0; i < j; i++) { ++ writel(dma[i*2],eni_dev->rx_dma+dma_wr*2); ++ writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*2+1); ++ dma_wr = (dma_wr+1) & (NR_DMA_RX-1); ++ } ++ if (skb) { ++ ENI_PRV_POS(skb) = eni_vcc->descr+size+1; ++ skb_queue_tail(&eni_dev->rx_queue,skb); ++eni_vcc->last = skb; ++rx_enqueued++; ++ } ++ eni_vcc->descr = here; ++ writel(dma_wr,eni_dev->reg+MID_DMA_WR_RX); ++ 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_PRV_POS(eni_vcc->last) += 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 = readl(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 || !atm_charge(vcc,atm_pdu2truesize(length))) skb = NULL; ++ else { ++ skb = alloc_skb(length,GFP_ATOMIC); ++ if (!skb) atm_return(vcc,atm_pdu2truesize(length)); ++ } ++ if (!skb) { ++ discard(vcc,length >> 2); ++ return 0; ++ } ++ skb_put(skb,length); ++ skb->stamp = 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 = readl(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%lx)\n",descr,0); ++ DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", ++ vcc->dev->number); ++ size = 0; ++ } ++ else { ++ static unsigned long silence = 0; ++ ++ if (time_after(jiffies, silence) || silence == 0) { ++ printk(KERN_WARNING DEV_LABEL "(itf %d): " ++ "discarding PDU(s) with CRC error\n", ++ vcc->dev->number); ++ silence = (jiffies+2*HZ)|1; ++ } ++ size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); ++ EVENT("CRC error (descr=0x%lx,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 = readl(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); ++ length = eff = 0; ++ vcc->stats->rx_err++; ++ } ++ } ++ if (!eff || !atm_charge(vcc,atm_pdu2truesize(eff << 2))) skb = NULL; ++ else { ++ skb = alloc_skb(eff << 2,GFP_ATOMIC); ++ 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); ++ atm_return(vcc,atm_pdu2truesize(eff << 2)); ++ } ++ } ++ if (!skb) { ++ discard(vcc,size); ++ return 0; ++ } ++ skb_put(skb,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) ++{ ++ u32 *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 = (readl(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 %d\n",ENI_VCC(vcc)->descr, ++ ((readl(vci_dsc+1) & MID_VCI_DESCR) >> ++ MID_VCI_DESCR_SHIFT)); ++ if (ENI_VCC(vcc)->rx(vcc)) return 1; ++ } ++ /* clear IN_SERVICE flag */ ++ writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc); ++ /* ++ * 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 = (readl(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 %d\n",ENI_VCC(vcc)->descr, ++ ((readl(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_rx.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_rx.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 (readl(eni_dev->reg+MID_SERV_WRITE) != eni_dev->serv_read) { ++ vci = readl(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; ++ u32 *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=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb), ++ ENI_PRV_POS(skb)); ++rx_dequeued++; ++ vcc = ATM_SKB(skb)->vcc; ++ eni_vcc = ENI_VCC(vcc); ++ first = 0; ++ vci_dsc = eni_dev->vci+(vcc->vci << 2); ++ if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb), ++ (readl(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 = ENI_PRV_POS(skb) & (eni_vcc->words-1); ++ if (!skb->len) kfree_skb(skb); ++ else { ++ EVENT("pushing (len=%ld)\n",skb->len,0); ++ if (vcc->qos.aal == ATM_AAL0) ++ *(unsigned long *) skb->data = ++ ntohl(*(unsigned long *) skb->data); ++ memset(skb->cb,0,sizeof(struct eni_skb_prv)); ++ 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 */ ++ if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <= ++ MID_MAX_BUF_SIZE) ++ size = MID_MAX_BUF_SIZE; ++ eni_vcc->recv = (u32 *) eni_alloc_mem(eni_dev,&size); ++ DPRINTK("rx at 0x%p\n",eni_vcc->recv); ++ eni_vcc->words = size >> 2; ++ if (!eni_vcc->recv) return -ENOBUFS; ++ eni_vcc->rx = vcc->qos.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) ++{ ++ u32 *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; ++ writel(0,here+1); /* descr, read = 0 */ ++ writel(0,here+2); /* 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 */ ++ writel(((vcc->qos.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),here); ++ return 0; ++} ++ ++ ++static void close_rx(struct atm_vcc *vcc) ++{ ++ u32 *here; ++ struct eni_dev *eni_dev; ++ struct eni_vcc *eni_vcc; ++ unsigned long flags; ++ u32 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 */ ++ writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH << ++ MID_VCI_MODE_SHIFT),here); ++ /* wait for receiver to become idle */ ++ udelay(27); ++ /* discard pending cell */ ++ writel(readl(here) & ~MID_VCI_IN_SERVICE,here); ++ /* 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%x\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 = readl(eni_dev->reg+MID_SERV_WRITE); ++ writel(0,eni_dev->reg+MID_DMA_WR_RX); ++ return 0; ++} ++ ++ ++/*----------------------------------- TX ------------------------------------*/ ++ ++ ++enum enq_res { enq_ok,enq_next,enq_jam }; ++ ++ ++static inline void put_dma(int chan,u32 *dma,int *j,unsigned long paddr, ++ u32 size) ++{ ++ u32 init,words; ++ ++ DPRINTK("put_dma: 0x%lx+0x%x\n",paddr,size); ++ EVENT("put_dma: 0x%lx+0x%lx\n",paddr,size); ++#if 0 /* don't complain anymore */ ++ if (paddr & 3) ++ printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr); ++ if (size & 3) ++ printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size); ++#endif ++ if (paddr & 3) { ++ init = 4-(paddr & 3); ++ if (init > size || size < 7) init = size; ++ DPRINTK("put_dma: %lx DMA: %d/%d bytes\n",paddr,init,size); ++ dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) | ++ (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) paddr); ++ paddr += init; ++ size -= init; ++ } ++ words = size >> 2; ++ size &= 3; ++ if (words && (paddr & 31)) { ++ init = 8-((paddr & 31) >> 2); ++ if (init > words) init = words; ++ DPRINTK("put_dma: %lx DMA: %d/%d words\n",paddr,init,words); ++ dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | ++ (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) paddr); ++ paddr += init << 2; ++ words -= init; ++ } ++#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */ ++ if (words & ~15) { ++ DPRINTK("put_dma: %lx DMA: %d*16/%d words\n",paddr,words >> 4, ++ words); ++ dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT) ++ | (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~15) << 2; ++ words &= 15; ++ } ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */ ++ if (words & ~7) { ++ DPRINTK("put_dma: %lx DMA: %d*8/%d words\n",paddr,words >> 3, ++ words); ++ dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT) ++ | (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~7) << 2; ++ words &= 7; ++ } ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */ ++ if (words & ~3) { ++ DPRINTK("put_dma: %lx DMA: %d*4/%d words\n",paddr,words >> 2, ++ words); ++ dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT) ++ | (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~3) << 2; ++ words &= 3; ++ } ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */ ++ if (words & ~1) { ++ DPRINTK("put_dma: %lx DMA: %d*2/%d words\n",paddr,words >> 1, ++ words); ++ dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT) ++ | (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) paddr); ++ paddr += (words & ~1) << 2; ++ words &= 1; ++ } ++#endif ++ if (words) { ++ DPRINTK("put_dma: %lx DMA: %d words\n",paddr,words); ++ dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) | ++ (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) paddr); ++ paddr += words << 2; ++ } ++ if (size) { ++ DPRINTK("put_dma: %lx DMA: %d bytes\n",paddr,size); ++ dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) | ++ (chan << MID_DMA_CHAN_SHIFT); ++ dma[(*j)++] = virt_to_bus((void *) 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; ++ u32 dma_rd,dma_wr; ++ u32 size; /* in words */ ++ int aal5,dma_size,i,j; ++ ++ DPRINTK(">do_tx\n"); ++ NULLCHECK(skb); ++ EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len); ++ vcc = ATM_SKB(skb)->vcc; ++ NULLCHECK(vcc); ++ eni_dev = ENI_DEV(vcc->dev); ++ NULLCHECK(eni_dev); ++ eni_vcc = ENI_VCC(vcc); ++ tx = eni_vcc->tx; ++ NULLCHECK(tx); ++#if 0 /* Enable this for testing with the "align" program */ ++ { ++ unsigned int hack = *((char *) skb->data)-'0'; ++ ++ if (hack < 8) { ++ skb->data += hack; ++ skb->len -= hack; ++ } ++ } ++#endif ++#if 0 /* should work now */ ++ 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); ++#endif ++ /* ++ * 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->qos.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 */ ++ } ++ /* ++ * Can I move tx_pos by size bytes without getting closer than TX_GAP ++ * to the read pointer ? TX_GAP means to leave some space for what ++ * the manual calls "too close". ++ */ ++ if (!NEPMOK(tx->tx_pos,size+TX_GAP, ++ readl(eni_dev->reg+MID_TX_RDPTR(tx->index)),tx->words)) { ++ DPRINTK(DEV_LABEL "(itf %d): TX full (size %ld)\n", ++ vcc->dev->number,size); ++ return enq_next; ++ } ++ /* check DMA */ ++ dma_wr = readl(eni_dev->reg+MID_DMA_WR_TX); ++ dma_rd = readl(eni_dev->reg+MID_DMA_RD_TX); ++ dma_size = 3; /* JK for descriptor and final fill, plus final size ++ mis-alignment fix */ ++DPRINTK("iovcnt = %d\n",ATM_SKB(skb)->iovcnt); ++ if (!ATM_SKB(skb)->iovcnt) dma_size += 5; ++ else dma_size += 5*ATM_SKB(skb)->iovcnt; ++ if (dma_size > TX_DMA_BUF) { ++ printk(KERN_CRIT 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; ++ eni_dev->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 (!ATM_SKB(skb)->iovcnt) ++ if (aal5) ++ put_dma(tx->index,eni_dev->dma,&j, ++ (unsigned long) skb->data,skb->len); ++ else put_dma(tx->index,eni_dev->dma,&j, ++ (unsigned long) skb->data+4,skb->len-4); ++ else { ++DPRINTK("doing direct send\n"); ++ for (i = 0; i < ATM_SKB(skb)->iovcnt; i++) ++ put_dma(tx->index,eni_dev->dma,&j,(unsigned long) ++ ((struct iovec *) skb->data)[i].iov_base, ++ ((struct iovec *) skb->data)[i].iov_len); ++ } ++ if (skb->len & 3) ++ put_dma(tx->index,eni_dev->dma,&j, ++ (unsigned long) zeroes,4-(skb->len & 3)); ++ /* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */ ++ eni_dev->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)) | ++ (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0); ++ DPRINTK("size: %ld, len:%d\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++) { ++ writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*2); ++ writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*2+1); ++ dma_wr = (dma_wr+1) & (NR_DMA_TX-1); ++ } ++ ENI_PRV_POS(skb) = tx->tx_pos; ++ ENI_PRV_SIZE(skb) = 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); ++ writel(dma_wr,eni_dev->reg+MID_DMA_WR_TX); ++ 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 = ATM_SKB(skb)->vcc; ++ NULLCHECK(vcc); ++ tx = ENI_VCC(vcc)->tx; ++ NULLCHECK(ENI_VCC(vcc)->tx); ++ DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb), ++ readl(eni_dev->reg+MID_TX_DESCRSTART(tx->index))); ++ if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) == ++ readl(eni_dev->reg+MID_TX_DESCRSTART(tx->index))) { ++ skb_queue_head(&eni_dev->tx_queue,skb); ++ break; ++ } ++ ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb); ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ vcc->stats->tx++; ++ wake_up(&eni_dev->tx_wait); ++dma_complete++; ++ } ++} ++ ++ ++static struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr) ++{ ++ int i; ++ ++ for (i = !ubr; i < NR_CHAN; i++) ++ if (!eni_dev->tx[i].send) return eni_dev->tx+i; ++ return NULL; ++} ++ ++ ++static int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre, ++ int *res,int unlimited) ++{ ++ static const int pre_div[] = { 4,16,128,2048 }; ++ /* 2^(((x+2)^2-(x+2))/2+1) */ ++ ++ if (unlimited) *pre = *res = 0; ++ else { ++ if (*pcr > 0) { ++ int div; ++ ++ for (*pre = 0; *pre < 3; (*pre)++) ++ if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break; ++ div = pre_div[*pre]**pcr; ++ DPRINTK("min div %d\n",div); ++ *res = TS_CLOCK/div-1; ++ } ++ else { ++ int div; ++ ++ if (!*pcr) *pcr = eni_dev->tx_bw+reserved; ++ for (*pre = 3; *pre >= 0; (*pre)--) ++ if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break; ++ if (*pre < 3) (*pre)++; /* else fail later */ ++ div = pre_div[*pre]*-*pcr; ++ 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; ++ } ++ *pcr = TS_CLOCK/pre_div[*pre]/(*res+1); ++ DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res); ++ return 0; ++} ++ ++ ++static int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp, ++ int set_rsv,int set_shp) ++{ ++ struct eni_dev *eni_dev = ENI_DEV(vcc->dev); ++ struct eni_vcc *eni_vcc = ENI_VCC(vcc); ++ struct eni_tx *tx; ++ unsigned long size,mem; ++ int rate,ubr,unlimited,new_tx; ++ int pre,res,order; ++ int error; ++ ++ rate = atm_pcr_goal(txtp); ++ ubr = txtp->traffic_class == ATM_UBR; ++ unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR || ++ rate >= ATM_OC3_PCR); ++ if (!unlimited) { ++ size = txtp->max_sdu*3; /* @@@ improve */ ++ if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <= ++ MID_MAX_BUF_SIZE) ++ size = MID_MAX_BUF_SIZE; ++ } ++ else { ++ if (eni_dev->ubr) { ++ eni_vcc->tx = eni_dev->ubr; ++ txtp->pcr = ATM_OC3_PCR; ++ return 0; ++ } ++ size = UBR_BUFFER; ++ } ++ new_tx = !eni_vcc->tx; ++ mem = 0; /* for gcc */ ++ if (!new_tx) tx = eni_vcc->tx; ++ else { ++ mem = eni_alloc_mem(eni_dev,&size); ++ if (!mem) return -ENOBUFS; ++ tx = alloc_tx(eni_dev,unlimited); ++ if (!tx) { ++ eni_free_mem(eni_dev,mem,size); ++ return -EBUSY; ++ } ++ DPRINTK("got chan %d\n",tx->index); ++ tx->reserved = tx->shaping = 0; ++ tx->send = (u32 *) mem; ++ tx->words = size >> 2; ++ skb_queue_head_init(&tx->backlog); ++ for (order = 0; size > (1 << (order+10)); order++); ++ writel((order << MID_SIZE_SHIFT) | ++ ((tx->send-eni_dev->ram) >> MID_LOC_SKIP), ++ eni_dev->reg+MID_TX_PLACE(tx->index)); ++ tx->tx_pos = readl(eni_dev->reg+MID_TX_DESCRSTART(tx->index)) & ++ MID_DESCR_START; ++ } ++ error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited); ++ if (!error && txtp->min_pcr > rate) error = -EINVAL; ++ if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR && ++ txtp->max_pcr < rate) error = -EINVAL; ++ if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved) ++ error = -EINVAL; ++ if (!error && set_rsv && !set_shp && rate < tx->shaping) ++ error = -EINVAL; ++ if (!error && !set_rsv && rate > tx->reserved && !ubr) ++ error = -EINVAL; ++ if (error) { ++ if (new_tx) { ++ tx->send = NULL; ++ eni_free_mem(eni_dev,mem,size); ++ } ++ return error; ++ } ++ txtp->pcr = rate; ++ if (set_rsv && !ubr) { ++ eni_dev->tx_bw += tx->reserved; ++ tx->reserved = rate; ++ eni_dev->tx_bw -= rate; ++ } ++ if (set_shp || (unlimited && new_tx)) { ++ if (unlimited && new_tx) eni_dev->ubr = tx; ++ tx->prescaler = pre; ++ tx->resolution = res; ++ tx->shaping = rate; ++ } ++ if (set_shp) eni_vcc->tx = tx; ++ DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping); ++ return 0; ++} ++ ++ ++static int open_tx_first(struct atm_vcc *vcc) ++{ ++ ENI_VCC(vcc)->tx = NULL; ++ if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; ++ ENI_VCC(vcc)->txing = 0; ++ return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1); ++} ++ ++ ++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); ++ } ++ /* ++ * Looping a few times in here is probably far cheaper than keeping ++ * track of TX completions all the time, so let's poll a bit ... ++ */ ++ while (readl(eni_dev->reg+MID_TX_RDPTR(eni_vcc->tx->index)) != ++ readl(eni_dev->reg+MID_TX_DESCRSTART(eni_vcc->tx->index))) ++ schedule(); ++ 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->reserved; ++ } ++ 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); ++ writel(0,eni_dev->reg+MID_DMA_WR_TX); ++ for (i = 0; i < NR_CHAN; i++) { ++ eni_dev->tx[i].send = NULL; ++ eni_dev->tx[i].index = i; ++ } ++ return 0; ++} ++ ++ ++/*--------------------------------- common ----------------------------------*/ ++ ++ ++#if 0 /* may become useful again when tuning things */ ++ ++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); ++} ++ ++#endif ++ ++ ++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 += readl(eni_dev->reg+MID_STAT) & MID_OVFL_TRASH; ++ } ++ if (reason & MID_SUNI_INT) { ++ EVENT("SUNI int\n",0,0); ++ dev->phy->interrupt(dev); ++#if 0 ++ foo(); ++#endif ++ } ++ if (reason & MID_DMA_ERR_ACK) { ++ printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " ++ "error\n",dev->number); ++ EVENT("---dump ends here---\n",0,0); ++ printk(KERN_NOTICE "---recent events---\n"); ++ event_dump(); ++ } ++ 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 = readl(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_DMA_ERR_ACK | ++ MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) { ++ EVENT("misc interrupt\n",0,0); ++ misc_int(dev,reason); ++ } ++ } ++} ++ ++ ++/*--------------------------------- entries ---------------------------------*/ ++ ++ ++static const char *media_name[] __initdata = { ++ "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 = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \ ++ udelay(10); /* 10 usecs */ \ ++ } }) ++#define GET_SEPROM \ ++ ({ if (!error && !pci_error) { \ ++ pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \ ++ udelay(10); /* 10 usecs */ \ ++ } }) ++ ++ ++__initfunc(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 " ++ "(0x%02x)\n",dev->number,pci_error); ++ error = -EIO; ++ } ++ return error; ++} ++ ++ ++#undef SET_SEPROM ++#undef GET_SEPROM ++ ++ ++__initfunc(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] = readl(&eprom->mac+((i & ~3) | (3-(i & 3)))); ++ return 0; ++} ++ ++ ++__initfunc(static int eni_init(struct atm_dev *dev)) ++{ ++ struct midway_eprom *eprom; ++ struct eni_dev *eni_dev; ++ struct pci_dev *pci_dev; ++ unsigned int real_base,base; ++ 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; ++ dev->link_rate = ATM_OC3_PCR; ++ eni_dev = ENI_DEV(dev); ++ pci_dev = eni_dev->pci_dev; ++ real_base = pci_dev->base_address[0] & MEM_VALID; /* strip flags */ ++ eni_dev->irq = pci_dev->irq; ++ error = pci_read_config_byte(pci_dev,PCI_REVISION_ID,&revision); ++ if (error) { ++ printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%02x\n", ++ dev->number,error); ++ return -EINVAL; ++ } ++ if ((error = pci_write_config_word(pci_dev,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 " ++ "(0x%02x)\n",dev->number,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) ioremap_nocache(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 (readl(&eprom->magic) != ENI155_MAGIC) { ++ printk("\n"); ++ printk(KERN_ERR KERN_ERR DEV_LABEL "(itf %d): bad " ++ "magic - expected 0x%X, got 0x%X\n",dev->number, ++ ENI155_MAGIC,readl(&eprom->magic)); ++ return -EINVAL; ++ } ++ } ++ eni_dev->phy = (u32 *) (base+PHY_BASE); ++ eni_dev->reg = (u32 *) (base+REG_BASE); ++ eni_dev->ram = (u32 *) (base+RAM_BASE); ++ last = (MAP_MAX_SIZE-RAM_BASE)/4; ++ for (i = last-RAM_INCREMENT; i >= 0; ++ i -= RAM_INCREMENT) { ++ writel(0x55555555,eni_dev->ram+i); ++ if (readl(eni_dev->ram+i) != 0x55555555) last = i; ++ else { ++ writel(0xAAAAAAAA,eni_dev->ram+i); ++ if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i; ++ else writel(i,eni_dev->ram+i); ++ } ++ } ++ for (i = 0; i < last; i += RAM_INCREMENT) ++ if (readl(eni_dev->ram+i) != i) break; ++ eni_dev->mem = i << 2; ++ memset_io(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 (!(readl(eni_dev->reg+MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) { ++ printk(")\n"); ++ printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%0x\n", ++ dev->number,readl(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, ++ readl(eni_dev->reg+MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA", ++ media_name[readl(eni_dev->reg+MID_RES_ID_MCON) & DAUGTHER_ID]); ++ return suni_init(dev); ++} ++ ++ ++__initfunc(static int eni_start(struct atm_dev *dev)) ++{ ++ struct eni_dev *eni_dev; ++ u32 *buf; ++ unsigned long buffer_mem; ++ int error; ++ ++ DPRINTK(">eni_start\n"); ++ eni_dev = ENI_DEV(dev); ++ if (request_irq(eni_dev->irq,&eni_int,SA_SHIRQ,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 = pci_write_config_word(eni_dev->pci_dev,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 (0x%02x)\n",dev->number,error); ++ return error; ++ } ++ if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL, ++ END_SWAP_DMA))) { ++ printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap " ++ "(0x%02x)\n",dev->number,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%p,rx 0x%p, tx 0x%p,srv 0x%p,buf 0x%p\n", ++ eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma,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_io(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) ++ */ ++ writel(0xffffffff,eni_dev->reg+MID_IE); ++ 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; ++ writel(readl(eni_dev->reg+MID_MC_S) | (1 << MID_INT_SEL_SHIFT) | ++ MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE, ++ eni_dev->reg+MID_MC_S); ++ /* Tonga uses SBus INTReq1 */ ++ (void) readl(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); ++ error = get_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->qos.aal != ATM_AAL0 && vcc->qos.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_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs) ++{ ++ struct eni_dev *eni_dev = ENI_DEV(vcc->dev); ++ struct eni_tx *tx = ENI_VCC(vcc)->tx; ++ struct sk_buff *skb; ++ unsigned long flags; ++ int error,rate,rsv,shp; ++ ++ if (qos->txtp.traffic_class == ATM_NONE) return 0; ++ if (tx == eni_dev->ubr) return -EBADFD; ++ rate = atm_pcr_goal(&qos->txtp); ++ if (rate < 0) rate = -rate; ++ rsv = shp = 0; ++ if ((flgs & ATM_MF_DEC_RSV) && rate && rate < tx->reserved) rsv = 1; ++ if ((flgs & ATM_MF_INC_RSV) && (!rate || rate > tx->reserved)) rsv = 1; ++ if ((flgs & ATM_MF_DEC_SHP) && rate && rate < tx->shaping) shp = 1; ++ if ((flgs & ATM_MF_INC_SHP) && (!rate || rate > tx->shaping)) shp = 1; ++ if (!rsv && !shp) return 0; ++ error = reserve_or_set_tx(vcc,&qos->txtp,rsv,shp); ++ if (error) return error; ++ if (shp && !(flgs & ATM_MF_IMMED)) return 0; ++ /* ++ * Walk through the send buffer and patch the rate information in all ++ * segmentation buffer descriptors of this VCC. ++ */ ++ save_flags(flags); ++ cli(); ++ for (skb = eni_dev->tx_queue.next; skb != ++ (struct sk_buff *) &eni_dev->tx_queue; skb = skb->next) { ++ u32 *dsc; ++ ++ if (ATM_SKB(skb)->vcc != vcc) continue; ++ dsc = tx->send+ENI_PRV_POS(skb); ++ *dsc = (*dsc & ~(MID_SEG_RATE | MID_SEG_PR)) | ++ (tx->prescaler << MID_SEG_PR_SHIFT) | ++ (tx->resolution << MID_SEG_RATE_SHIFT); ++ } ++ restore_flags(flags); ++ return 0; ++} ++ ++ ++static int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) ++{ ++ if (cmd == ENI_MEMDUMP) { ++ printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d " ++ "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number); ++ dump(dev); ++ return 0; ++ } ++ if (cmd == ATM_SETCIRANGE) { ++ struct atm_cirange ci; ++ ++ if (copy_from_user(&ci,(void *) arg,sizeof(struct atm_cirange))) ++ return -EFAULT; ++ 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, ++ void *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 = { sizeof(int),0,sizeof(int),0,0,0 }; ++static const struct atm_buffconst bcrx = { sizeof(int),0,sizeof(int),0,0,0 }; ++ ++#endif ++ if (level == SOL_AAL && (optname == SO_BCTXOPT || ++ optname == SO_BCRXOPT)) ++ return copy_to_user(optval,optname == SO_BCTXOPT ? &bctx : ++ &bcrx,sizeof(struct atm_buffconst)) ? -EFAULT : 0; ++ return -EINVAL; ++} ++ ++ ++static int eni_setsockopt(struct atm_vcc *vcc,int level,int optname, ++ void *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) { ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ if (!skb) { ++ printk(KERN_CRIT "!skb in eni_send ?\n"); ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ if (vcc->qos.aal == ATM_AAL0) { ++ if (skb->len != ATM_CELL_SIZE-1) { ++ if (vcc->pop) vcc->pop(vcc,skb); ++ else dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ *(u32 *) skb->data = htonl(*(u32 *) skb->data); ++ } ++submitted++; ++ ATM_SKB(skb)->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->qos.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) ++{ ++ writel(value,ENI_DEV(dev)->phy+addr); ++} ++ ++ ++ ++static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr) ++{ ++ return readl(ENI_DEV(dev)->phy+addr); ++} ++ ++ ++static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) ++{ ++ static const char *signal[] = { "LOST","unknown","okay" }; ++ struct eni_dev *eni_dev = ENI_DEV(dev); ++ struct atm_vcc *vcc; ++ int left,i; ++ ++ left = *pos; ++ if (!left) ++ return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, " ++ "%d cps remaining\n",dev->number,signal[(int) dev->signal], ++ eni_dev->mem >> 10,eni_dev->tx_bw); ++ left--; ++ if (!left) ++ return sprintf(page,"Bursts: TX" ++#if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \ ++ !defined(CONFIG_ATM_ENI_BURST_TX_8W) && \ ++ !defined(CONFIG_ATM_ENI_BURST_TX_4W) && \ ++ !defined(CONFIG_ATM_ENI_BURST_TX_2W) ++ " none" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_TX_16W ++ " 16W" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_TX_8W ++ " 8W" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_TX_4W ++ " 4W" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_TX_2W ++ " 2W" ++#endif ++ ", RX" ++#if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \ ++ !defined(CONFIG_ATM_ENI_BURST_RX_8W) && \ ++ !defined(CONFIG_ATM_ENI_BURST_RX_4W) && \ ++ !defined(CONFIG_ATM_ENI_BURST_RX_2W) ++ " none" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_RX_16W ++ " 16W" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_RX_8W ++ " 8W" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_RX_4W ++ " 4W" ++#endif ++#ifdef CONFIG_ATM_ENI_BURST_RX_2W ++ " 2W" ++#endif ++#ifndef CONFIG_ATM_ENI_TUNE_BURST ++ " (default)" ++#endif ++ "\n"); ++ for (i = 0; i < NR_CHAN; i++) { ++ struct eni_tx *tx = eni_dev->tx+i; ++ ++ if (!tx->send) continue; ++ if (--left) continue; ++ return sprintf(page,"tx[%d]: 0x%06x-0x%06lx (%6ld bytes), " ++ "rsv %d cps, shp %d cps%s\n",i,(tx->send-eni_dev->ram)*4, ++ (tx->send-eni_dev->ram+tx->words)*4-1,tx->words*4, ++ tx->reserved,tx->shaping, ++ tx == eni_dev->ubr ? " (UBR)" : ""); ++ } ++ for (vcc = dev->vccs; vcc; vcc = vcc->next) { ++ struct eni_vcc *eni_vcc = ENI_VCC(vcc); ++ int length; ++ ++ if (--left) continue; ++ length = sprintf(page,"vcc %4d: ",vcc->vci); ++ if (eni_vcc->rx) { ++ length += sprintf(page+length,"0x%06x-0x%06lx " ++ "(%6ld bytes)",(eni_vcc->recv-eni_dev->ram)*4, ++ (eni_vcc->recv-eni_dev->ram+eni_vcc->words)*4-1, ++ eni_vcc->words*4); ++ if (eni_vcc->tx) length += sprintf(page+length,", "); ++ } ++ if (eni_vcc->tx) ++ length += sprintf(page+length,"tx[%d]", ++ eni_vcc->tx->index); ++ page[length] = '\n'; ++ return length+1; ++ } ++ for (i = 0; i < eni_dev->free_len; i++) { ++ struct eni_free *fe = eni_dev->free_list+i; ++ unsigned long offset; ++ ++ if (--left) continue; ++ offset = (unsigned long) eni_dev->ram+eni_dev->base_diff; ++ return sprintf(page,"free 0x%06lx-0x%06lx (%6d bytes)\n", ++ fe->start-offset,fe->start-offset+(1 << fe->order)-1, ++ 1 << fe->order); ++ } ++ return 0; ++} ++ ++ ++static const struct atmdev_ops ops = { ++ NULL, /* no dev_close */ ++ eni_open, ++ eni_close, ++ eni_ioctl, ++ eni_getsockopt, ++ eni_setsockopt, ++ eni_send, ++ eni_sg_send, ++ NULL, /* no send_oam */ ++ eni_phy_put, ++ eni_phy_get, ++ NULL, /* no feedback */ ++ eni_change_qos, /* no change_qos */ ++ NULL, /* no free_rx_skb */ ++ eni_proc_read ++}; ++ ++ ++__initfunc(int eni_detect(void)) ++{ ++ struct atm_dev *dev; ++ struct eni_dev *eni_dev; ++ int devs,type; ++ ++ 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++) { ++ struct pci_dev *pci_dev; ++ ++ pci_dev = NULL; ++ while ((pci_dev = pci_find_device(PCI_VENDOR_ID_EF,type ? ++ PCI_DEVICE_ID_EF_ATM_ASIC : PCI_DEVICE_ID_EF_ATM_FPGA, ++ pci_dev))) { ++ if (!devs) { ++ zeroes = kmalloc(4,GFP_KERNEL); ++ if (!zeroes) { ++ kfree(eni_dev); ++ return -ENOMEM; ++ } ++ } ++ dev = atm_dev_register(DEV_LABEL,&ops,-1,0); ++ if (!dev) break; ++ eni_dev->pci_dev = pci_dev; ++ 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; ++ devs++; ++ eni_dev = (struct eni_dev *) kmalloc(sizeof(struct ++ eni_dev),GFP_KERNEL); ++ if (!eni_dev) break; ++ } ++ } ++ kfree(eni_dev); ++ if (!devs && zeroes) { ++ kfree(zeroes); ++ zeroes = NULL; ++ } ++ return devs; ++} ++ ++ ++#ifdef MODULE ++ ++int init_module(void) ++{ ++ if (!eni_detect()) { ++ printk(KERN_ERR 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 Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/eni.h Thu Apr 22 19:46:09 1999 +@@ -0,0 +1,115 @@ ++/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */ ++ ++/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ ++ ++ ++#ifndef DRIVER_ATM_ENI_H ++#define DRIVER_ATM_ENI_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 8 /* burst and skip a few things */ ++#define TX_DMA_BUF 100 /* should be enough for 64 kB */ ++ ++ ++struct eni_free { ++ unsigned long start; /* counting in bytes */ ++ int order; ++}; ++ ++struct eni_tx { ++ u32 *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 reserved; /* reserved peak cell rate */ ++ int shaping; /* shaped 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 */ ++ u32 *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 */ ++ u32 *phy; /* PHY interface chip registers */ ++ u32 *reg; /* register base */ ++ u32 *ram; /* RAM base */ ++ u32 *vci; /* VCI table */ ++ u32 *rx_dma; /* RX DMA queue */ ++ u32 *tx_dma; /* TX DMA queue */ ++ u32 *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 */ ++ u32 dma[TX_DMA_BUF*2]; /* DMA request scratch area */ ++ /*-------------------------------- RX part */ ++ u32 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 */ ++ struct pci_dev *pci_dev; /* PCI stuff */ ++}; ++ ++ ++#define ENI_DEV(d) ((struct eni_dev *) (d)->dev_data) ++#define ENI_VCC(d) ((struct eni_vcc *) (d)->dev_data) ++ ++ ++struct eni_skb_prv { ++ struct atm_skb_data _; /* reserved */ ++ unsigned long pos; /* position of next descriptor */ ++ int size; /* PDU size in reassembly buffer */ ++}; ++ ++#define ENI_PRV_SIZE(skb) (((struct eni_skb_prv *) (skb)->cb)->size) ++#define ENI_PRV_POS(skb) (((struct eni_skb_prv *) (skb)->cb)->pos) ++ ++#endif +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/horizon.c Fri May 7 15:42:50 1999 +@@ -0,0 +1,3065 @@ ++/* ++ Madge Horizon ATM Adapter driver. ++ Copyright (C) 1995-1999 Madge Networks Ltd. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian ++ system and in the file COPYING in the Linux kernel source. ++*/ ++ ++/* ++ IMPORTANT NOTE: Madge Networks no longer makes the adapters ++ supported by this driver and makes no commitment to maintain it. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "horizon.h" ++ ++#define maintainer_string "Giuliano Procida at Madge Networks " ++#define description_string "Madge ATM Horizon [Ultra] driver" ++#define version_string "1.1" ++ ++static void __init show_version (void) { ++ printk ("%s version %s\n", description_string, version_string); ++} ++ ++/* ++ ++ CREDITS ++ ++ Driver and documentation by: ++ ++ Chris Aston Madge Networks ++ Giuliano Procida Madge Networks ++ Simon Benham Madge Networks ++ Simon Johnson Madge Networks ++ Various Others Madge Networks ++ ++ Some inspiration taken from other drivers by: ++ ++ Alexandru Cucos UTBv ++ Kari Mettinen University of Helsinki ++ Werner Almesberger EPFL LRC ++ ++ Theory of Operation ++ ++ I Hardware, detection, initialisation and shutdown. ++ ++ 1. Supported Hardware ++ ++ This driver should handle all variants of the PCI Madge ATM adapters ++ with the Horizon chipset. These are all PCI cards supporting PIO, BM ++ DMA and a form of MMIO (registers only, not internal RAM). ++ ++ The driver is only known to work with SONET and UTP Horizon Ultra ++ cards at 155Mb/s. However, code is in place to deal with both the ++ original Horizon and 35Mb/s. ++ ++ There are two revisions of the Horizon ASIC: the original and the ++ Ultra. Details of hardware bugs are in section III. ++ ++ The ASIC version can be distinguished by chip markings but is NOT ++ indicated by the PCI revision (all adapters seem to have PCI rev 1). ++ ++ I believe that: ++ ++ Horizon => Collage 25 PCI Adapter (UTP and STP) ++ Horizon Ultra => Collage 155 PCI Client (UTP or SONET) ++ Ambassador x => Collage 155 PCI Server (completely different) ++ ++ Horizon (25Mb/s) is fitted with UTP and STP connectors. It seems to ++ have a Madge B154 plus glue logic serializer. I have also found a ++ really ancient version of this with slightly different glue. It ++ comes with the revision 0 (140-025-01) ASIC. ++ ++ Horizon Ultra (155Mb/s) is fitted with either a Pulse Medialink ++ output (UTP) or an HP HFBR 5205 output (SONET). It has either ++ Madge's SAMBA framer or a SUNI-lite device (early versions). It ++ comes with the revision 1 (140-027-01) ASIC. ++ ++ 2. Detection ++ ++ All Horizon-based cards present with the same PCI Vendor and Device ++ IDs. The standard Linux 2.2 PCI API is used to locate any cards and ++ to enable bus-mastering (with appropriate latency). ++ ++ ATM_LAYER_STATUS in the control register distinguishes between the ++ two possible physical layers (25 and 155). It is not clear whether ++ the 155 cards can also operate at 25Mbps. We rely on the fact that a ++ card operates at 155 if and only if it has the newer Horizon Ultra ++ ASIC. ++ ++ For 155 cards the two possible framers are probed for and then set ++ up for loop-timing. ++ ++ 3. Initialisation ++ ++ The card is reset and then put into a know state. The physical layer ++ is configured for normal operation at the appropriate speed; in the ++ case of the 155 cards, the framer is initialised with line-based ++ timing; the internal RAM is zeroed and the allocation of buffers for ++ RX and TX is made; the Burnt In Address is read and copied to the ++ ATM ESI; various policy settings for RX (VPI bits, unknown VCs, oam ++ cells) are made. Ideally all policy items should be configurable at ++ module load (if not actually on-demand), however, only the vpi vs ++ vci bit allocation can be specified at insmod. ++ ++ 4. Shutdown ++ ++ This is in response to module_cleaup. No VCs are in use and the card ++ should be idle; it is reset. ++ ++ II Driver software (as it should be) ++ ++ 0. Traffic Parameters ++ ++ The traffic classes (not an enumeration) are currently: ATM_NONE (no ++ traffic), ATM_UBR, ATM_CBR, ATM_VBR and ATM_ABR, ATM_ANYCLASS ++ (compatible with everything). Together with (perhaps only some of) ++ the following items they make up the traffic specification. ++ ++ struct atm_trafprm { ++ unsigned char traffic_class; traffic class (ATM_UBR, ...) ++ int max_pcr; maximum PCR in cells per second ++ int pcr; desired 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 ++ }; ++ ++ Note that these denote bandwidth available not bandwidth used; the ++ possibilities according to ATMF are: ++ ++ Real Time (cdv and max CDT given) ++ ++ CBR(pcr) pcr bandwidth always available ++ rtVBR(pcr,scr,mbs) scr bandwidth always available, upto pcr at mbs too ++ ++ Non Real Time ++ ++ nrtVBR(pcr,scr,mbs) scr bandwidth always available, upto pcr at mbs too ++ UBR() ++ ABR(mcr,pcr) mcr bandwidth always available, upto pcr (depending) too ++ ++ mbs is max burst size (bucket) ++ pcr and scr have associated cdvt values ++ mcr is like scr but has no cdtv ++ cdtv may differ at each hop ++ ++ Some of the above items are qos items (as opposed to traffic ++ parameters). We have nothing to do with qos. All except ABR can have ++ their traffic parameters converted to GCRA parameters. The GCRA may ++ be implemented as a (real-number) leaky bucket. The GCRA can be used ++ in complicated ways by switches and in simpler ways by end-stations. ++ It can be used both to filter incoming cells and shape out-going ++ cells. ++ ++ ATM Linux actually supports: ++ ++ ATM_NONE() (no traffic in this direction) ++ ATM_UBR(max_frame_size) ++ ATM_CBR(max/min_pcr, max_cdv, max_frame_size) ++ ++ 0 or ATM_MAX_PCR are used to indicate maximum available PCR ++ ++ A traffic specification consists of the AAL type and separate ++ traffic specifications for either direction. In ATM Linux it is: ++ ++ struct atm_qos { ++ struct atm_trafprm txtp; ++ struct atm_trafprm rxtp; ++ unsigned char aal; ++ }; ++ ++ AAL types are: ++ ++ ATM_NO_AAL AAL not specified ++ ATM_AAL0 "raw" ATM cells ++ ATM_AAL1 AAL1 (CBR) ++ ATM_AAL2 AAL2 (VBR) ++ ATM_AAL34 AAL3/4 (data) ++ ATM_AAL5 AAL5 (data) ++ ATM_SAAL signaling AAL ++ ++ The Horizon has support for AAL frame types: 0, 3/4 and 5. However, ++ it does not implement AAL 3/4 SAR and it has a different notion of ++ "raw cell" to ATM Linux's (48 bytes vs. 52 bytes) so neither are ++ supported by this driver. ++ ++ The Horizon has (TX) support for ABR (including UBR), VBR and CBR. ++ Each TX channel has a bucket (containing up to 31 cell units) and ++ two timers (PCR and SCR) associated with it that can be used to ++ govern cell emissions and host notification (in the case of ++ ABR). The timers may either be disabled or may be set to any of 240 ++ values (determined by the clock crystal, a fixed (?) per-device ++ divider, a configurable divider and a configurable timer preload ++ value). ++ ++ At the moment only UBR and CBR are supported by the driver. This is ++ due to my not understanding ATM Linux VBR or Horizon's VBR support. ++ ++ 1. TX (TX channel setup and TX transfer) ++ ++ The TX half of the driver owns the TX Horizon registers. The TX ++ component in the IRQ handler is the BM completion handler. This can ++ only be entered when tx_busy is true (enforced by hardware). The ++ other TX component can only be entered when tx_busy is false ++ (enforced by driver). So TX is single-threaded. ++ ++ Apart from a minor optimisation to not re-select the last channel, ++ the TX send component works as follows: ++ ++ Atomic test and set tx_busy until we succeed; we should implement ++ some sort of timeout so that tx_busy will never be stuck at true. ++ ++ If no TX channel is setup for this VC we wait for an idle one (if ++ necessary) and set it up. ++ ++ At this point we have a TX channel ready for use. We wait for enough ++ buffers to become available then start a TX transmit (set the TX ++ descriptor, schedule transfer, exit). ++ ++ The IRQ component handles TX completion (stats, free buffer, tx_busy ++ unset, exit). We also re-schedule further transfers for the same ++ frame if needed. ++ ++ TX setup in more detail: ++ ++ TX open is a nop, the relevant information is held in the hrz_vcc ++ (vcc->dev_data) structure and is "cached" on the card. ++ ++ TX close gets the TX lock and clears the channel from the "cache". ++ ++ 2. RX (Data Available and RX transfer) ++ ++ The RX half of the driver owns the RX registers. There are two RX ++ components in the IRQ handler: the data available handler deals with ++ fresh data that has arrived on the card, the BM completion handler ++ is very similar to the TX completion handler. The data available ++ handler grabs the rx_lock and it is only released once the data has ++ been discarded or completely transferred to the host. The BM ++ completion handler only runs when the lock is held; the data ++ available handler is locked out over the same period. ++ ++ Data available on the card triggers an interrupt. If the data is not ++ suitable for out existing RX channels or we cannot allocate a buffer ++ it is flushed. Otherwise an RX receive is scheduled. Multiple RX ++ transfers may be scheduled for the same frame. ++ ++ RX setup in more detail: ++ ++ RX open... ++ RX close... ++ ++ III Hardware Bugs ++ ++ 0. Byte vs Word addressing of adapter RAM. ++ ++ A design feature; see the .h file (especially the memory map). ++ ++ 1. Bus Master Data Transfers (original Horizon only, fixed in Ultra) ++ ++ The host must not start a transmit direction transfer at a ++ non-four-byte boundary in host memory. Instead the host should ++ perform a byte, or a two byte, or one byte followed by two byte ++ transfer in order to start the rest of the transfer on a four byte ++ boundary. RX is OK. ++ ++ Simultaneous transmit and receive direction bus master transfers are ++ not allowed. ++ ++ The simplest solution to these two is to always do PIO (never DMA) ++ in the TX direction on the original Horizon. More complicated ++ solutions are likely to hurt my brain. ++ ++ 3. Loss of buffer on close VC ++ ++ When a VC is being closed, the buffer associated with it is not ++ returned to the pool. The host must store the reference to this ++ buffer and when opening a new VC then give it to that new VC. ++ ++ The host intervention currently consists of stacking such a buffer ++ pointer at VC close and checking the stack at VC open. ++ ++ 4. Failure to close a VC ++ ++ If a VC is currently receiving a frame then closing the VC may fail ++ and the frame continues to be received. ++ ++ The solution is to make sure any received frames are flushed when ++ ready. This is currently done just before the solution to 3. ++ ++ 5. PCI bus (original Horizon only, fixed in Ultra) ++ ++ Reading from the data port prior to initialisation will hang the PCI ++ bus. Just don't do that then! We don't. ++ ++ IV To Do List ++ ++ . Timer code may be broken. ++ ++ . Allow users to specify buffer allocation split for TX and RX. ++ ++ . Deal once and for all with buggy VC close. ++ ++ . Handle interrupted and/or non-blocking operations. ++ ++ . Change some macros to functions and move from .h to .c. ++ ++ . Try to limit the number of TX frames each VC may have queued, in ++ order to reduce the chances of TX buffer exhaustion. ++ ++ . Implement VBR (bucket and timers not understood) and ABR (need to ++ do RM cells manually); also no Linux support for either. ++ ++ . Implement QoS changes on open VCs (involves extracting parts of VC open ++ and close into separate functions and using them to make changes). ++ ++*/ ++ ++/********** globals **********/ ++ ++static hrz_dev * hrz_devs = NULL; ++static struct timer_list housekeeping; ++ ++static unsigned short debug = 0; ++static unsigned short vpi_bits = 0; ++static unsigned short max_tx_size = 9000; ++static unsigned short max_rx_size = 9000; ++static unsigned char pci_lat = 0; ++ ++/********** access functions **********/ ++ ++/* Read / Write Horizon registers */ ++static inline void wr_regl (const hrz_dev * dev, unsigned char reg, u32 data) { ++ outl (cpu_to_le32 (data), dev->iobase + reg); ++} ++ ++static inline u32 rd_regl (const hrz_dev * dev, unsigned char reg) { ++ return le32_to_cpu (inl (dev->iobase + reg)); ++} ++ ++static inline void wr_regw (const hrz_dev * dev, unsigned char reg, u16 data) { ++ outw (cpu_to_le16 (data), dev->iobase + reg); ++} ++ ++static inline u16 rd_regw (const hrz_dev * dev, unsigned char reg) { ++ return le16_to_cpu (inw (dev->iobase + reg)); ++} ++ ++static inline void wrs_regb (const hrz_dev * dev, unsigned char reg, void * addr, u32 len) { ++ outsb (dev->iobase + reg, addr, len); ++} ++ ++static inline void rds_regb (const hrz_dev * dev, unsigned char reg, void * addr, u32 len) { ++ insb (dev->iobase + reg, addr, len); ++} ++ ++/* Read / Write to a given address in Horizon buffer memory. */ ++// Interrupts must be disabled between the address register and data port ++// accesses as these must form an atomic operation. ++ ++static inline void wr_mem (const hrz_dev * dev, HDW * addr, u32 data) { ++ // wr_regl (dev, MEM_WR_ADDR_REG_OFF, (u32) addr); ++ wr_regl (dev, MEM_WR_ADDR_REG_OFF, (addr - (HDW *) 0) * sizeof(HDW)); ++ wr_regl (dev, MEMORY_PORT_OFF, data); ++} ++ ++static inline u32 rd_mem (const hrz_dev * dev, HDW * addr) { ++ // wr_regl (dev, MEM_RD_ADDR_REG_OFF, (u32) addr); ++ wr_regl (dev, MEM_RD_ADDR_REG_OFF, (addr - (HDW *) 0) * sizeof(HDW)); ++ return rd_regl(dev, MEMORY_PORT_OFF); ++} ++ ++static inline void wr_framer (const hrz_dev * dev, u32 addr, u32 data) { ++ wr_regl (dev, MEM_WR_ADDR_REG_OFF, (u32) addr | 0x80000000); ++ wr_regl (dev, MEMORY_PORT_OFF, data); ++} ++ ++static inline u32 rd_framer (const hrz_dev * dev, u32 addr) { ++ wr_regl (dev, MEM_RD_ADDR_REG_OFF, (u32) addr | 0x80000000); ++ return rd_regl (dev, MEMORY_PORT_OFF); ++} ++ ++/********** specialised access functions **********/ ++ ++/* RX */ ++ ++static inline void FLUSH_RX_CHANNEL (hrz_dev * dev, u16 channel) { ++ wr_regw (dev, RX_CHANNEL_PORT_OFF, FLUSH_CHANNEL | channel); ++ return; ++} ++ ++static inline void WAIT_FLUSH_RX_COMPLETE (hrz_dev * dev) { ++ while (rd_regw (dev, RX_CHANNEL_PORT_OFF) & FLUSH_CHANNEL) ++ ; ++ return; ++} ++ ++static inline void SELECT_RX_CHANNEL (hrz_dev * dev, u16 channel) { ++ wr_regw (dev, RX_CHANNEL_PORT_OFF, channel); ++ return; ++} ++ ++static inline void WAIT_UPDATE_COMPLETE (hrz_dev * dev) { ++ while (rd_regw (dev, RX_CHANNEL_PORT_OFF) & RX_CHANNEL_UPDATE_IN_PROGRESS) ++ ; ++ return; ++} ++ ++/* TX */ ++ ++static inline void SELECT_TX_CHANNEL (hrz_dev * dev, u16 tx_channel) { ++ wr_regl (dev, TX_CHANNEL_PORT_OFF, tx_channel); ++ return; ++} ++ ++/* Update or query one configuration parameter of a particular channel. */ ++ ++static inline void update_tx_channel_config (hrz_dev * dev, short chan, u8 mode, u16 value) { ++ wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF, ++ chan * TX_CHANNEL_CONFIG_MULT | mode); ++ wr_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF, value); ++ return; ++} ++ ++static inline u16 query_tx_channel_config (hrz_dev * dev, short chan, u8 mode) { ++ wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF, ++ chan * TX_CHANNEL_CONFIG_MULT | mode); ++ return rd_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF); ++} ++ ++/********** dump functions **********/ ++ ++static inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) { ++#ifdef DEBUG_HORIZON ++ unsigned int i; ++ unsigned char * data = skb->data; ++ PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc); ++ for (i=0; ilen && i < 256;i++) ++ PRINTDM (DBG_DATA, "%02x ", data[i]); ++ PRINTDE (DBG_DATA,""); ++#else ++ (void) prefix; ++ (void) vc; ++ (void) skb; ++#endif ++ return; ++} ++ ++static inline void dump_regs (hrz_dev * dev) { ++#ifdef DEBUG_HORIZON ++ PRINTD (DBG_REGS, "CONTROL 0: %#x", rd_regl (dev, CONTROL_0_REG)); ++ PRINTD (DBG_REGS, "RX CONFIG: %#x", rd_regw (dev, RX_CONFIG_OFF)); ++ PRINTD (DBG_REGS, "TX CONFIG: %#x", rd_regw (dev, TX_CONFIG_OFF)); ++ PRINTD (DBG_REGS, "TX STATUS: %#x", rd_regw (dev, TX_STATUS_OFF)); ++ PRINTD (DBG_REGS, "IRQ ENBLE: %#x", rd_regl (dev, INT_ENABLE_REG_OFF)); ++ PRINTD (DBG_REGS, "IRQ SORCE: %#x", rd_regl (dev, INT_SOURCE_REG_OFF)); ++#else ++ (void) dev; ++#endif ++ return; ++} ++ ++static inline void dump_framer (hrz_dev * dev) { ++#ifdef DEBUG_HORIZON ++ unsigned int i; ++ PRINTDB (DBG_REGS, "framer registers:"); ++ for (i = 0; i < 0x10; ++i) ++ PRINTDM (DBG_REGS, " %02x", rd_framer (dev, i)); ++ PRINTDE (DBG_REGS,""); ++#else ++ (void) dev; ++#endif ++ return; ++} ++ ++/********** VPI/VCI <-> (RX) channel conversions **********/ ++ ++/* RX channels are 10 bit integers, these fns are quite paranoid */ ++ ++static inline int channel_to_vpci (const u16 channel, short * vpi, int * vci) { ++ unsigned short vci_bits = 10 - vpi_bits; ++ if ((channel & RX_CHANNEL_MASK) == channel) { ++ *vci = channel & ((~0)<> vci_bits; ++ return channel ? 0 : -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++static inline int vpci_to_channel (u16 * channel, const short vpi, const int vci) { ++ unsigned short vci_bits = 10 - vpi_bits; ++ if (0 <= vpi && vpi < 1<>RX_Q_ENTRY_CHANNEL_SHIFT) & RX_CHANNEL_MASK; ++} ++ ++/* Cell Transmit Rate Values ++ * ++ * the cell transmit rate (cells per sec) can be set to a variety of ++ * different values by specifying two parameters: a timer preload from ++ * 1 to 16 (stored as 0 to 15) and a clock divider (2 to the power of ++ * an exponent from 0 to 14; the special value 15 disables the timer). ++ * ++ * cellrate = baserate / (preload * 2^divider) ++ * ++ * The maximum cell rate that can be specified is therefore just the ++ * base rate. Halving the preload is equivalent to adding 1 to the ++ * divider and so values 1 to 8 of the preload are redundant except ++ * in the case of a maximal divider (14). ++ * ++ * Given a desired cell rate, an algorithm to determine the preload ++ * and divider is: ++ * ++ * a) x = baserate / cellrate, want p * 2^d = x (as far as possible) ++ * b) if x > 16 * 2^14 then set p = 16, d = 14 (min rate), done ++ * if x <= 16 then set p = x, d = 0 (high rates), done ++ * c) now have 16 < x <= 2^18, or 1 < x/16 <= 2^14 and we want to ++ * know n such that 2^(n-1) < x/16 <= 2^n, so slide a bit until ++ * we find the range (n will be between 1 and 14), set d = n ++ * d) Also have 8 < x/2^n <= 16, so set p nearest x/2^n ++ * ++ * The algorithm used below is a minor variant of the above. ++ * ++ * The base rate is derived from the oscillator frequency (Hz) using a ++ * fixed divider: ++ * ++ * baserate = freq / 32 in the case of some Unknown Card ++ * baserate = freq / 8 in the case of the Horizon 25 ++ * baserate = freq / 8 in the case of the Horizon Ultra 155 ++ * ++ * The Horizon cards have oscillators and base rates as follows: ++ * ++ * Card Oscillator Base Rate ++ * Unknown Card 33 MHz 1.03125 MHz (33 MHz = PCI freq) ++ * Horizon 25 32 MHz 4 MHz ++ * Horizon Ultra 155 40 MHz 5 MHz ++ * ++ * The following defines give the base rates in Hz. These were ++ * previously a factor of 100 larger, no doubt someone was using ++ * cps*100. ++ */ ++ ++#define BR_UKN 1031250l ++#define BR_HRZ 4000000l ++#define BR_ULT 5000000l ++ ++// d is an exponent ++#define CR_MIND 0 ++#define CR_MAXD 14 ++ ++// p ranges from 1 to a power of 2 ++#define CR_MAXPEXP 4 ++ ++static int make_rate (const hrz_dev * dev, u32 c, rounding r, ++ u16 * bits, unsigned int * actual) { ++ ++ // note: rounding the rate down means rounding 'p' up ++ ++ const unsigned long br = test_bit (ultra, &dev->flags) ? BR_ULT : BR_HRZ; ++ ++ u32 div = CR_MIND; ++ u32 pre; ++ ++ // local fn to build the timer bits ++ inline int set_cr (void) { ++ // paranoia ++ if (div > CR_MAXD || (!pre) || pre > 1<> 1; ++ ++br_exp; ++ } ++ // (br >>br_exp) <rx_descs[channel]; ++ ++ PRINTD (DBG_FLOW, "hrz_open_rx %x", channel); ++ ++ spin_lock_irqsave (&dev->mem_lock, flags); ++ channel_type = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK; ++ spin_unlock_irqrestore (&dev->mem_lock, flags); ++ ++ // very serious error, should never occur ++ if (channel_type != RX_CHANNEL_DISABLED) { ++ PRINTD (DBG_ERR|DBG_VCC, "RX channel for VC already open"); ++ return -EBUSY; // clean up? ++ } ++ ++ // Give back spare buffer ++ if (dev->noof_spare_buffers) { ++ buf_ptr = dev->spare_buffers[--dev->noof_spare_buffers]; ++ PRINTD (DBG_VCC, "using a spare buffer: %u", buf_ptr); ++ // should never occur ++ if (buf_ptr == RX_CHANNEL_DISABLED || buf_ptr == RX_CHANNEL_IDLE) { ++ // but easy to recover from ++ PRINTD (DBG_ERR|DBG_VCC, "bad spare buffer pointer, using IDLE"); ++ buf_ptr = RX_CHANNEL_IDLE; ++ } ++ } else { ++ PRINTD (DBG_VCC, "using IDLE buffer pointer"); ++ } ++ ++ // Channel is currently disabled so change its status to idle ++ ++ // do we really need to save the flags again? ++ spin_lock_irqsave (&dev->mem_lock, flags); ++ ++ wr_mem (dev, &rx_desc->wr_buf_type, ++ buf_ptr | CHANNEL_TYPE_AAL5 | FIRST_CELL_OF_AAL5_FRAME); ++ if (buf_ptr != RX_CHANNEL_IDLE) ++ wr_mem (dev, &rx_desc->rd_buf_type, buf_ptr); ++ ++ spin_unlock_irqrestore (&dev->mem_lock, flags); ++ ++ // rxer->rate = make_rate (qos->peak_cells); ++ ++ PRINTD (DBG_FLOW, "hrz_open_rx ok"); ++ ++ return 0; ++} ++ ++#if 0 ++/********** change vc rate for a given vc **********/ ++ ++static void hrz_change_vc_qos (ATM_RXER * rxer, MAAL_QOS * qos) { ++ rxer->rate = make_rate (qos->peak_cells); ++} ++#endif ++ ++/********** free an skb (as per ATM device driver documentation) **********/ ++ ++static inline void hrz_kfree_skb (struct sk_buff * skb) { ++ if (ATM_SKB(skb)->vcc->pop) { ++ ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb); ++ } else { ++ dev_kfree_skb (skb); ++ } ++} ++ ++/********** cancel listen on a VC **********/ ++ ++static void hrz_close_rx (hrz_dev * dev, u16 vc) { ++ unsigned long flags; ++ ++ u32 value; ++ ++ u32 r1, r2; ++ ++ rx_ch_desc * rx_desc = &memmap->rx_descs[vc]; ++ ++ int was_idle = 0; ++ ++ spin_lock_irqsave (&dev->mem_lock, flags); ++ value = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK; ++ spin_unlock_irqrestore (&dev->mem_lock, flags); ++ ++ if (value == RX_CHANNEL_DISABLED) { ++ // I suppose this could happen once we deal with _NONE traffic properly ++ PRINTD (DBG_VCC, "closing VC: RX channel %u already disabled", vc); ++ return; ++ } ++ if (value == RX_CHANNEL_IDLE) ++ was_idle = 1; ++ ++ spin_lock_irqsave (&dev->mem_lock, flags); ++ ++ for (;;) { ++ wr_mem (dev, &rx_desc->wr_buf_type, RX_CHANNEL_DISABLED); ++ ++ if ((rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK) == RX_CHANNEL_DISABLED) ++ break; ++ ++ was_idle = 0; ++ } ++ ++ if (was_idle) { ++ spin_unlock_irqrestore (&dev->mem_lock, flags); ++ return; ++ } ++ ++ WAIT_FLUSH_RX_COMPLETE(dev); ++ ++ // XXX Is this all really necessary? We can rely on the rx_data_av ++ // handler to discard frames that remain queued for delivery. If the ++ // worry is that immediately reopening the channel (perhaps by a ++ // different process) may cause some data to be mis-delivered then ++ // there may still be a simpler solution (such as busy-waiting on ++ // rx_busy once the channel is disabled or before a new one is ++ // opened - does this leave any holes?). Arguably setting up and ++ // tearing down the TX and RX halves of each virtual circuit could ++ // most safely be done within ?x_busy protected regions. ++ ++ // OK, current changes are that Simon's marker is disabled and we DO ++ // look for NULL rxer elsewhere. The code here seems flush frames ++ // and then remember the last dead cell belonging to the channel ++ // just disabled - the cell gets relinked at the next vc_open. ++ // However, when all VCs are closed or only a few opened there are a ++ // handful of buffers that are unusable. ++ ++ // Does anyone feel like documenting spare_buffers properly? ++ // Does anyone feel like fixing this in a nicer way? ++ ++ // Flush any data which is left in the channel ++ for (;;) { ++ // Change the rx channel port to something different to the RX ++ // channel we are trying to close to force Horizon to flush the rx ++ // channel read and write pointers. ++ ++ u16 other = vc^(RX_CHANS/2); ++ ++ SELECT_RX_CHANNEL (dev, other); ++ WAIT_UPDATE_COMPLETE (dev); ++ ++ r1 = rd_mem (dev, &rx_desc->rd_buf_type); ++ ++ // Select this RX channel. Flush doesn't seem to work unless we ++ // select an RX channel before hand ++ ++ SELECT_RX_CHANNEL (dev, vc); ++ WAIT_UPDATE_COMPLETE (dev); ++ ++ // Attempt to flush a frame on this RX channel ++ ++ FLUSH_RX_CHANNEL (dev, vc); ++ WAIT_FLUSH_RX_COMPLETE (dev); ++ ++ // Force Horizon to flush rx channel read and write pointers as before ++ ++ SELECT_RX_CHANNEL (dev, other); ++ WAIT_UPDATE_COMPLETE (dev); ++ ++ r2 = rd_mem (dev, &rx_desc->rd_buf_type); ++ ++ PRINTD (DBG_VCC|DBG_RX, "r1 = %u, r2 = %u", r1, r2); ++ ++ if (r1 == r2) { ++ dev->spare_buffers[dev->noof_spare_buffers++] = (u16)r1; ++ break; ++ } ++ } ++ ++#if 0 ++ { ++ rx_q_entry * wr_ptr = &memmap->rx_q_entries[rd_regw (dev, RX_QUEUE_WR_PTR_OFF)]; ++ rx_q_entry * rd_ptr = dev->rx_q_entry; ++ ++ PRINTD (DBG_VCC|DBG_RX, "rd_ptr = %u, wr_ptr = %u", rd_ptr, wr_ptr); ++ ++ while (rd_ptr != wr_ptr) { ++ u32 x = rd_mem (dev, (HDW *) rd_ptr); ++ ++ if (vc == rx_q_entry_to_rx_channel (x)) { ++ x |= SIMONS_DODGEY_MARKER; ++ ++ PRINTD (DBG_RX|DBG_VCC|DBG_WARN, "marking a frame as dodgey"); ++ ++ wr_mem (dev, (HDW *) rd_ptr, x); ++ } ++ ++ if (rd_ptr == dev->rx_q_wrap) ++ rd_ptr = dev->rx_q_reset; ++ else ++ rd_ptr++; ++ } ++ } ++#endif ++ ++ spin_unlock_irqrestore (&dev->mem_lock, flags); ++ ++ return; ++} ++ ++/********** schedule RX transfers **********/ ++ ++// Note on tail recursion: a GCC developer said that it is not likely ++// to be fixed soon, so do not define TAILRECUSRIONWORKS unless you ++// are sure it does as you may otherwise overflow the kernel stack. ++ ++// giving this fn a return value would help GCC, alledgedly ++ ++static void rx_schedule (hrz_dev * dev, int irq) { ++ unsigned int rx_bytes; ++ ++ int pio_instead; ++#ifndef TAILRECURSIONWORKS ++ do { ++#endif ++ pio_instead = 0; ++ ++ // bytes waiting for RX transfer ++ rx_bytes = dev->rx_bytes; ++ ++#if 0 ++ spin_count = 0; ++ while (rd_regl (dev, MASTER_RX_COUNT_REG_OFF)) { ++ PRINTD (DBG_RX|DBG_WARN, "RX error: other PCI Bus Master RX still in progress!"); ++ if (++spin_count > 10) { ++ PRINTD (DBG_RX|DBG_ERR, "spun out waiting PCI Bus Master RX completion"); ++ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0); ++ clear_bit (rx_busy, &dev->flags); ++ hrz_kfree_skb (dev->rx_skb); ++ return; ++ } ++ } ++#endif ++ ++ // this code follows the TX code but (at the moment) there is only ++ // one region - the skb itself. I don't know if this will change, ++ // but it doesn't hurt to have the code here, disabled. ++ ++ if (rx_bytes) { ++ // start next transfer within same region ++ if (rx_bytes <= MAX_PIO_COUNT) { ++ PRINTD (DBG_RX|DBG_BUS, "(pio)"); ++ pio_instead = 1; ++ } ++ if (rx_bytes <= MAX_TRANSFER_COUNT) { ++ PRINTD (DBG_RX|DBG_BUS, "(simple or last multi)"); ++ dev->rx_bytes = 0; ++ } else { ++ PRINTD (DBG_RX|DBG_BUS, "(continuing multi)"); ++ dev->rx_bytes = rx_bytes - MAX_TRANSFER_COUNT; ++ rx_bytes = MAX_TRANSFER_COUNT; ++ } ++ } else { ++ // rx_bytes == 0 -- we're between regions ++ // regions remaining to transfer ++#if 0 ++ unsigned int rx_regions = dev->rx_regions; ++#else ++ unsigned int rx_regions = 0; ++#endif ++ ++ if (rx_regions) { ++#if 0 ++ // start a new region ++ dev->rx_addr = dev->rx_iovec->iov_base; ++ rx_bytes = dev->rx_iovec->iov_len; ++ ++dev->rx_iovec; ++ dev->rx_regions = rx_regions - 1; ++ ++ if (rx_bytes <= MAX_PIO_COUNT) { ++ PRINTD (DBG_RX|DBG_BUS, "(pio)"); ++ pio_instead = 1; ++ } ++ if (rx_bytes <= MAX_TRANSFER_COUNT) { ++ PRINTD (DBG_RX|DBG_BUS, "(full region)"); ++ dev->rx_bytes = 0; ++ } else { ++ PRINTD (DBG_RX|DBG_BUS, "(start multi region)"); ++ dev->rx_bytes = rx_bytes - MAX_TRANSFER_COUNT; ++ rx_bytes = MAX_TRANSFER_COUNT; ++ } ++#endif ++ } else { ++ // rx_regions == 0 ++ // that's all folks - end of frame ++ struct sk_buff * skb = dev->rx_skb; ++ // dev->rx_iovec = 0; ++ ++ FLUSH_RX_CHANNEL (dev, dev->rx_channel); ++ ++ dump_skb ("<<<", dev->rx_channel, skb); ++ ++ PRINTD (DBG_RX|DBG_SKB, "push %p %u", skb->data, skb->len); ++ ++ { ++ struct atm_vcc * vcc = ATM_SKB(skb)->vcc; ++ // VC layer stats ++ vcc->stats->rx++; ++ skb->stamp = xtime; ++ // end of our responsability ++ vcc->push (vcc, skb); ++ } ++ } ++ } ++ ++ // note: writing RX_COUNT clears any interrupt condition ++ if (rx_bytes) { ++ if (pio_instead) { ++ if (irq) ++ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0); ++ rds_regb (dev, DATA_PORT_OFF, dev->rx_addr, rx_bytes); ++ } else { ++ wr_regl (dev, MASTER_RX_ADDR_REG_OFF, virt_to_bus (dev->rx_addr)); ++ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, rx_bytes); ++ } ++ dev->rx_addr += rx_bytes; ++ } else { ++ if (irq) ++ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0); ++ // allow another RX thread to start ++ YELLOW_LED_ON(dev); ++ clear_bit (rx_busy, &dev->flags); ++ PRINTD (DBG_RX, "cleared rx_busy for dev %p", dev); ++ } ++ ++#ifdef TAILRECURSIONWORKS ++ // and we all bless optimised tail calls ++ if (pio_instead) ++ rx_schedule (dev, 0); ++ return; ++#else ++ // grrrrrrr! ++ irq = 0; ++ } while (pio_instead); ++ return; ++#endif ++} ++ ++/********** handle RX bus master complete events **********/ ++ ++static inline void rx_bus_master_complete_handler (hrz_dev * dev) { ++ if (test_bit (rx_busy, &dev->flags)) { ++ rx_schedule (dev, 1); ++ } else { ++ PRINTD (DBG_RX|DBG_ERR, "unexpected RX bus master completion"); ++ // clear interrupt condition on adapter ++ wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0); ++ } ++ return; ++} ++ ++/********** (queue to) become the next TX thread **********/ ++ ++static inline int tx_hold (hrz_dev * dev) { ++ while (test_and_set_bit (tx_busy, &dev->flags)) { ++ PRINTD (DBG_TX, "sleeping at tx lock %p %u", dev, dev->flags); ++ interruptible_sleep_on (&dev->tx_queue); ++ PRINTD (DBG_TX, "woken at tx lock %p %u", dev, dev->flags); ++ if (signal_pending (current)) ++ return -1; ++ } ++ PRINTD (DBG_TX, "set tx_busy for dev %p", dev); ++ return 0; ++} ++ ++/********** allow another TX thread to start **********/ ++ ++static inline void tx_release (hrz_dev * dev) { ++ clear_bit (tx_busy, &dev->flags); ++ PRINTD (DBG_TX, "cleared tx_busy for dev %p", dev); ++ wake_up_interruptible (&dev->tx_queue); ++} ++ ++/********** schedule TX transfers **********/ ++ ++static void tx_schedule (hrz_dev * const dev, int irq) { ++ unsigned int tx_bytes; ++ ++ int append_desc = 0; ++ ++ int pio_instead; ++#ifndef TAILRECURSIONWORKS ++ do { ++#endif ++ pio_instead = 0; ++ // bytes in current region waiting for TX transfer ++ tx_bytes = dev->tx_bytes; ++ ++#if 0 ++ spin_count = 0; ++ while (rd_regl (dev, MASTER_TX_COUNT_REG_OFF)) { ++ PRINTD (DBG_TX|DBG_WARN, "TX error: other PCI Bus Master TX still in progress!"); ++ if (++spin_count > 10) { ++ PRINTD (DBG_TX|DBG_ERR, "spun out waiting PCI Bus Master TX completion"); ++ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0); ++ tx_release (dev); ++ hrz_kfree_skb (dev->tx_skb); ++ return; ++ } ++ } ++#endif ++ ++ if (tx_bytes) { ++ // start next transfer within same region ++ if (!test_bit (ultra, &dev->flags) || tx_bytes <= MAX_PIO_COUNT) { ++ PRINTD (DBG_TX|DBG_BUS, "(pio)"); ++ pio_instead = 1; ++ } ++ if (tx_bytes <= MAX_TRANSFER_COUNT) { ++ PRINTD (DBG_TX|DBG_BUS, "(simple or last multi)"); ++ if (!dev->tx_iovec) { ++ // end of last region ++ append_desc = 1; ++ } ++ dev->tx_bytes = 0; ++ } else { ++ PRINTD (DBG_TX|DBG_BUS, "(continuing multi)"); ++ dev->tx_bytes = tx_bytes - MAX_TRANSFER_COUNT; ++ tx_bytes = MAX_TRANSFER_COUNT; ++ } ++ } else { ++ // tx_bytes == 0 -- we're between regions ++ // regions remaining to transfer ++ unsigned int tx_regions = dev->tx_regions; ++ ++ if (tx_regions) { ++ // start a new region ++ dev->tx_addr = dev->tx_iovec->iov_base; ++ tx_bytes = dev->tx_iovec->iov_len; ++ ++dev->tx_iovec; ++ dev->tx_regions = tx_regions - 1; ++ ++ if (!test_bit (ultra, &dev->flags) || tx_bytes <= MAX_PIO_COUNT) { ++ PRINTD (DBG_TX|DBG_BUS, "(pio)"); ++ pio_instead = 1; ++ } ++ if (tx_bytes <= MAX_TRANSFER_COUNT) { ++ PRINTD (DBG_TX|DBG_BUS, "(full region)"); ++ dev->tx_bytes = 0; ++ } else { ++ PRINTD (DBG_TX|DBG_BUS, "(start multi region)"); ++ dev->tx_bytes = tx_bytes - MAX_TRANSFER_COUNT; ++ tx_bytes = MAX_TRANSFER_COUNT; ++ } ++ } else { ++ // tx_regions == 0 ++ // that's all folks - end of frame ++ struct sk_buff * skb = dev->tx_skb; ++ dev->tx_iovec = 0; ++ ++ // VC layer stats ++ ATM_SKB(skb)->vcc->stats->tx++; ++ ++ // free the skb ++ hrz_kfree_skb (skb); ++ } ++ } ++ ++ // note: writing TX_COUNT clears any interrupt condition ++ if (tx_bytes) { ++ if (pio_instead) { ++ if (irq) ++ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0); ++ wrs_regb (dev, DATA_PORT_OFF, dev->tx_addr, tx_bytes); ++ if (append_desc) ++ wr_regl (dev, TX_DESCRIPTOR_PORT_OFF, cpu_to_be32 (dev->tx_skb->len)); ++ } else { ++ wr_regl (dev, MASTER_TX_ADDR_REG_OFF, virt_to_bus (dev->tx_addr)); ++ if (append_desc) ++ wr_regl (dev, TX_DESCRIPTOR_REG_OFF, cpu_to_be32 (dev->tx_skb->len)); ++ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, ++ append_desc ++ ? tx_bytes | MASTER_TX_AUTO_APPEND_DESC ++ : tx_bytes); ++ } ++ dev->tx_addr += tx_bytes; ++ } else { ++ if (irq) ++ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0); ++ YELLOW_LED_ON(dev); ++ tx_release (dev); ++ } ++ ++#ifdef TAILRECURSIONWORKS ++ // and we all bless optimised tail calls ++ if (pio_instead) ++ tx_schedule (dev, 0); ++ return; ++#else ++ // grrrrrrr! ++ irq = 0; ++ } while (pio_instead); ++ return; ++#endif +} + ++/********** handle TX bus master complete events **********/ ++ ++static inline void tx_bus_master_complete_handler (hrz_dev * dev) { ++ if (test_bit (tx_busy, &dev->flags)) { ++ tx_schedule (dev, 1); ++ } else { ++ PRINTD (DBG_TX|DBG_ERR, "unexpected TX bus master completion"); ++ // clear interrupt condition on adapter ++ wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0); ++ } ++ return; ++} + -+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();*/ ++/********** move RX Q pointer to next item in circular buffer **********/ ++ ++// called only from IRQ sub-handler ++static inline u32 rx_queue_entry_next (hrz_dev * dev) { ++ u32 rx_queue_entry; ++ spin_lock (&dev->mem_lock); ++ rx_queue_entry = rd_mem (dev, &dev->rx_q_entry->entry); ++ if (dev->rx_q_entry == dev->rx_q_wrap) ++ dev->rx_q_entry = dev->rx_q_reset; ++ else ++ dev->rx_q_entry++; ++ wr_regw (dev, RX_QUEUE_RD_PTR_OFF, dev->rx_q_entry - dev->rx_q_reset); ++ spin_unlock (&dev->mem_lock); ++ return rx_queue_entry; +} + ++/********** handle RX disabled by device **********/ + -+static int get_ci(struct atm_vcc *vcc,short *vpi,int *vci) -+{ -+ struct atm_vcc *walk; ++static inline void rx_disabled_handler (hrz_dev * dev) { ++ wr_regw (dev, RX_CONFIG_OFF, rd_regw (dev, RX_CONFIG_OFF) | RX_ENABLE); ++ // count me please ++ PRINTK (KERN_WARNING, "RX was disabled!"); ++} + -+ 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; ++/********** handle RX data received by device **********/ ++ ++// called from IRQ handler ++static inline void rx_data_av_handler (hrz_dev * dev) { ++ u32 rx_queue_entry; ++ u32 rx_queue_entry_flags; ++ u16 rx_len; ++ u16 rx_channel; ++ ++ PRINTD (DBG_FLOW, "hrz_data_av_handler"); ++ ++ // try to grab rx lock (not possible during RX bus mastering) ++ if (test_and_set_bit (rx_busy, &dev->flags)) { ++ PRINTD (DBG_RX, "locked out of rx lock"); ++ return; ++ } ++ PRINTD (DBG_RX, "set rx_busy for dev %p", dev); ++ // lock is cleared if we fail now, o/w after bus master completion ++ ++ YELLOW_LED_OFF(dev); ++ ++ rx_queue_entry = rx_queue_entry_next (dev); ++ ++ rx_len = rx_q_entry_to_length (rx_queue_entry); ++ rx_channel = rx_q_entry_to_rx_channel (rx_queue_entry); ++ ++ WAIT_FLUSH_RX_COMPLETE (dev); ++ ++ SELECT_RX_CHANNEL (dev, rx_channel); ++ ++ PRINTD (DBG_RX, "rx_queue_entry is: %#x", rx_queue_entry); ++ rx_queue_entry_flags = rx_queue_entry & (RX_CRC_32_OK|RX_COMPLETE_FRAME|SIMONS_DODGEY_MARKER); ++ ++ if (!rx_len) { ++ // (at least) bus-mastering breaks if we try to handle a ++ // zero-length frame, besides AAL5 does not support them ++ PRINTK (KERN_ERR, "zero-length frame!"); ++ rx_queue_entry_flags &= ~RX_COMPLETE_FRAME; ++ } ++ ++ if (rx_queue_entry_flags & SIMONS_DODGEY_MARKER) { ++ PRINTD (DBG_RX|DBG_ERR, "Simon's marker detected!"); ++ } ++ if (rx_queue_entry_flags == (RX_CRC_32_OK | RX_COMPLETE_FRAME)) { ++ struct atm_vcc * atm_vcc; ++ ++ PRINTD (DBG_RX, "got a frame on rx_channel %x len %u", rx_channel, rx_len); ++ ++ atm_vcc = dev->rxer[rx_channel]; ++ // if no vcc is assigned to this channel, we should drop the frame ++ // (is this what SIMONS etc. was trying to achieve?) ++ ++ if (atm_vcc) { ++ ++ if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) { ++ ++ if (rx_len <= atm_vcc->qos.rxtp.max_sdu) { ++ ++ // If everyone has to call atm_pdu2... why isn't it part of ++ // atm_charge? B'cos some people already have skb->truesize! ++ if (atm_charge (atm_vcc, atm_pdu2truesize (rx_len))) { ++ ++ struct sk_buff * skb = alloc_skb (rx_len, GFP_ATOMIC); ++ if (skb) { ++ // remember this so we can push it later ++ dev->rx_skb = skb; ++ // remember this so we can flush it later ++ dev->rx_channel = rx_channel; ++ ++ // prepare socket buffer ++ skb_put (skb, rx_len); ++ ATM_SKB(skb)->vcc = atm_vcc; ++ ++ // simple transfer ++ // dev->rx_regions = 0; ++ // dev->rx_iovec = 0; ++ dev->rx_bytes = rx_len; ++ dev->rx_addr = skb->data; ++ PRINTD (DBG_RX, "RX start simple transfer (addr %p, len %d)", ++ skb->data, rx_len); ++ ++ // do the business ++ rx_schedule (dev, 0); ++ return; ++ ++ } else { ++ PRINTD (DBG_SKB|DBG_WARN, "failed to get skb"); ++ atm_vcc->stats->rx_drop++; ++ } ++ ++ } else { ++ // someone fix this (message), please! ++ PRINTD (DBG_INFO, "dropped thanks to atm_charge"); ++ // drop stats incremented in atm_charge ++ } ++ ++ } else { ++ PRINTK (KERN_INFO, "frame received on TX-only VC %x", rx_channel); ++ // do we count this? + } -+ 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; ++ ++ } else { ++ PRINTK (KERN_WARNING, "dropped over-size frame"); ++ // do we count this? ++ } ++ ++ } else { ++ PRINTD (DBG_WARN|DBG_VCC|DBG_RX, "no VCC for this frame (VC closed)"); ++ // do we count this? ++ } ++ ++ } else { ++ // Wait update complete ? SPONG ++ } ++ ++ // RX was aborted ++ YELLOW_LED_ON(dev); ++ ++ FLUSH_RX_CHANNEL (dev,rx_channel); ++ clear_bit (rx_busy, &dev->flags); ++ ++ return; +} + ++/********** interrupt handler **********/ + -+static int eni_open(struct atm_vcc *vcc,short vpi,int vci) -+{ -+ struct eni_dev *eni_dev; -+ struct eni_vcc *eni_vcc; -+ int error; ++static void interrupt_handler (int irq, void * dev_id, struct pt_regs * pt_regs) { ++ hrz_dev * dev = hrz_devs; ++ u32 int_source; ++ unsigned int irq_ok; ++ (void) pt_regs; ++ ++ PRINTD (DBG_FLOW, "interrupt_handler: %p", dev_id); ++ ++ if (!dev_id) { ++ PRINTD (DBG_IRQ|DBG_ERR, "irq with NULL dev_id: %d", irq); ++ return; ++ } ++ // Did one of our cards generate the interrupt? ++ while (dev) { ++ if (dev == dev_id) ++ break; ++ dev = dev->prev; ++ } ++ if (!dev) { ++ PRINTD (DBG_IRQ, "irq not for me: %d", irq); ++ return; ++ } ++ if (irq != dev->irq) { ++ PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq); ++ return; ++ } ++ ++ // definitely for us ++ irq_ok = 0; ++ while ((int_source = rd_regl (dev, INT_SOURCE_REG_OFF) ++ & INTERESTING_INTERRUPTS)) { ++ // In the interests of fairness, the (inline) handlers below are ++ // called in sequence and without immediate return to the head of ++ // the while loop. This is only of issue for slow hosts (or when ++ // debugging messages are on). Really slow hosts may find a fast ++ // sender keeps them permanently in the IRQ handler. :( ++ ++ // (only an issue for slow hosts) RX completion goes before ++ // rx_data_av as the former implies rx_busy and so the latter ++ // would just abort. If it reschedules another transfer ++ // (continuing the same frame) then it will not clear rx_busy. ++ ++ // (only an issue for slow hosts) TX completion goes before RX ++ // data available as it is a much shorter routine - there is the ++ // chance that any further transfers it schedules will be complete ++ // by the time of the return to the head of the while loop ++ ++ if (int_source & RX_BUS_MASTER_COMPLETE) { ++ ++irq_ok; ++ PRINTD (DBG_IRQ|DBG_BUS|DBG_RX, "rx_bus_master_complete asserted"); ++ rx_bus_master_complete_handler (dev); ++ } ++ if (int_source & TX_BUS_MASTER_COMPLETE) { ++ ++irq_ok; ++ PRINTD (DBG_IRQ|DBG_BUS|DBG_TX, "tx_bus_master_complete asserted"); ++ tx_bus_master_complete_handler (dev); ++ } ++ if (int_source & RX_DATA_AV) { ++ ++irq_ok; ++ PRINTD (DBG_IRQ|DBG_RX, "rx_data_av asserted"); ++ rx_data_av_handler (dev); ++ } ++ } ++ if (irq_ok) { ++ PRINTD (DBG_IRQ, "work done: %u", irq_ok); ++ } else { ++ PRINTD (DBG_IRQ|DBG_WARN, "spurious interrupt source: %#x", int_source); ++ } ++ ++ PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id); ++} + -+ 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); -+ error = get_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->qos.aal != ATM_AAL0 && vcc->qos.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; ++/********** housekeeping **********/ ++ ++static void set_timer (struct timer_list * timer, unsigned int delay) { ++ timer->expires = jiffies + delay; ++ add_timer (timer); ++ return; ++} ++ ++static void do_housekeeping (unsigned long arg) { ++ // just stats at the moment ++ hrz_dev * dev = hrz_devs; ++ (void) arg; ++ // data is set to zero at module unload ++ if (housekeeping.data) { ++ while (dev) { ++ // collect device-specific (not driver/atm-linux) stats here ++ dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF); ++ dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF); ++ dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF); ++ dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF); ++ dev = dev->prev; ++ } ++ set_timer (&housekeeping, HZ/10); ++ } ++ return; +} + ++/********** find an idle channel for TX and set it up **********/ + -+static int eni_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs) -+{ -+ struct eni_dev *eni_dev = ENI_DEV(vcc->dev); -+ struct eni_tx *tx = ENI_VCC(vcc)->tx; -+ struct sk_buff *skb; -+ unsigned long flags; -+ int error,rate,rsv,shp; ++// called with tx_busy set ++static inline short setup_idle_tx_channel (hrz_dev * dev, hrz_vcc * vcc) { ++ unsigned short idle_channels; ++ short tx_channel = -1; ++ unsigned int spin_count; ++ PRINTD (DBG_FLOW|DBG_TX, "setup_idle_tx_channel %p", dev); ++ ++ // better would be to fail immediately, the caller can then decide whether ++ // to wait or drop (depending on whether this is UBR etc.) ++ spin_count = 0; ++ while (!(idle_channels = rd_regw (dev, TX_STATUS_OFF) & IDLE_CHANNELS_MASK)) { ++ PRINTD (DBG_TX|DBG_WARN, "waiting for idle TX channel"); ++ // delay a bit here ++ if (++spin_count > 100) { ++ PRINTD (DBG_TX|DBG_ERR, "spun out waiting for idle TX channel"); ++ return -EBUSY; ++ } ++ } ++ ++ // got an idle channel ++ { ++ // tx_idle ensures we look for idle channels in RR order ++ int chan = dev->tx_idle; ++ ++ int keep_going = 1; ++ while (keep_going) { ++ if (idle_channels & (1<tx_idle = chan; ++ } ++ ++ // set up the channel we found ++ { ++ // Initialise the cell header in the transmit channel descriptor ++ // a.k.a. prepare the channel and remember that we have done so. ++ ++ tx_ch_desc * tx_desc = &memmap->tx_descs[tx_channel]; ++ u16 rd_ptr; ++ u16 wr_ptr; ++ u16 channel = vcc->channel; ++ ++ unsigned long flags; ++ spin_lock_irqsave (&dev->mem_lock, flags); ++ ++ // Update the transmit channel record. ++ dev->tx_channel_record[tx_channel] = channel; ++ ++ // xBR channel ++ update_tx_channel_config (dev, tx_channel, RATE_TYPE_ACCESS, ++ vcc->tx_xbr_bits); ++ ++ // Update the PCR counter preload value etc. ++ update_tx_channel_config (dev, tx_channel, PCR_TIMER_ACCESS, ++ vcc->tx_pcr_bits); + -+ if (qos->txtp.traffic_class == ATM_NONE) return 0; -+ if (tx == eni_dev->ubr) return -EBADFD; -+ rate = atm_pcr_goal(&qos->txtp); -+ if (rate < 0) rate = -rate; -+ rsv = shp = 0; -+ if ((flgs & ATM_MF_DEC_RSV) && rate && rate < tx->reserved) rsv = 1; -+ if ((flgs & ATM_MF_INC_RSV) && (!rate || rate > tx->reserved)) rsv = 1; -+ if ((flgs & ATM_MF_DEC_SHP) && rate && rate < tx->shaping) shp = 1; -+ if ((flgs & ATM_MF_INC_SHP) && (!rate || rate > tx->shaping)) shp = 1; -+ if (!rsv && !shp) return 0; -+ error = reserve_or_set_tx(vcc,&qos->txtp,rsv,shp); -+ if (error) return error; -+ if (shp && !(flgs & ATM_MF_IMMED)) return 0; -+ /* -+ * Walk through the send buffer and patch the rate information in all -+ * segmentation buffer descriptors of this VCC. -+ */ -+ save_flags(flags); -+ cli(); -+ for (skb = eni_dev->tx_queue.next; skb != -+ (struct sk_buff *) &eni_dev->tx_queue; skb = skb->next) { -+ u32 *dsc; ++#if 0 ++ if (a vbr channel) { ++ // SCR timer ++ update_tx_channel_config (dev, tx_channel, SCR_TIMER_ACCESS, ++ vcc->tx_scr_bits); ++ ++ // Bucket size... ++ update_tx_channel_config (dev, tx_channel, BUCKET_CAPACITY_ACCESS, ++ vcc->tx_bucket_bits); ++ ++ // ... and fullness ++ update_tx_channel_config (dev, tx_channel, BUCKET_FULLNESS_ACCESS, ++ vcc->tx_bucket_bits); ++ } ++#endif + -+ if (ATM_SKB(skb)->vcc != vcc) continue; -+ dsc = tx->send+ENI_PRV_POS(skb); -+ *dsc = (*dsc & ~(MID_SEG_RATE | MID_SEG_PR)) | -+ (tx->prescaler << MID_SEG_PR_SHIFT) | -+ (tx->resolution << MID_SEG_RATE_SHIFT); -+ } -+ restore_flags(flags); -+ return 0; ++ // Initialise the read and write buffer pointers ++ rd_ptr = rd_mem (dev, &tx_desc->rd_buf_type) & BUFFER_PTR_MASK; ++ wr_ptr = rd_mem (dev, &tx_desc->wr_buf_type) & BUFFER_PTR_MASK; ++ ++ // idle TX channels should have identical pointers ++ if (rd_ptr != wr_ptr) { ++ PRINTD (DBG_TX|DBG_ERR, "TX buffer pointers are broken!"); ++ // spin_unlock... return -E... ++ // I wonder if gcc would get rid of one of the pointer aliases ++ } ++ PRINTD (DBG_TX, "TX buffer pointers are: rd %x, wr %x.", ++ rd_ptr, wr_ptr); ++ ++ switch (vcc->aal) { ++ case aal0: ++ PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal0"); ++ rd_ptr |= CHANNEL_TYPE_RAW_CELLS; ++ wr_ptr |= CHANNEL_TYPE_RAW_CELLS; ++ break; ++ case aal34: ++ PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal34"); ++ rd_ptr |= CHANNEL_TYPE_AAL3_4; ++ wr_ptr |= CHANNEL_TYPE_AAL3_4; ++ break; ++ case aal5: ++ rd_ptr |= CHANNEL_TYPE_AAL5; ++ wr_ptr |= CHANNEL_TYPE_AAL5; ++ // Initialise the CRC ++ wr_mem (dev, &tx_desc->partial_crc, INITIAL_CRC); ++ break; ++ } ++ ++ wr_mem (dev, &tx_desc->rd_buf_type, rd_ptr); ++ wr_mem (dev, &tx_desc->wr_buf_type, wr_ptr); ++ ++ // Write the Cell Header ++ // Payload Type, CLP and GFC would go here if non-zero ++ wr_mem (dev, &tx_desc->cell_header, channel); ++ ++ spin_unlock_irqrestore (&dev->mem_lock, flags); ++ } ++ ++ return tx_channel; ++} ++ ++/********** send a frame **********/ ++ ++static int hrz_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) { ++ unsigned int spin_count; ++ int free_buffers; ++ hrz_dev * dev = HRZ_DEV(atm_vcc->dev); ++ hrz_vcc * vcc = HRZ_VCC(atm_vcc); ++ u16 channel = vcc->channel; ++ ++ u32 buffers_required; ++ ++ /* signed for error return */ ++ short tx_channel; ++ ++ PRINTD (DBG_FLOW|DBG_TX, "hrz_send vc %x data %p len %u", ++ channel, skb->data, skb->len); ++ ++ dump_skb (">>>", channel, skb); ++ ++ if (atm_vcc->qos.txtp.traffic_class == ATM_NONE) { ++ PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", channel); ++ hrz_kfree_skb (skb); ++ return -EIO; ++ } ++ ++ // don't understand this ++ ATM_SKB(skb)->vcc = atm_vcc; ++ ++ if (skb->len > atm_vcc->qos.txtp.max_sdu) { ++ PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping..."); ++ hrz_kfree_skb (skb); ++ return -EIO; ++ } ++ ++ if (!channel) { ++ PRINTD (DBG_ERR|DBG_TX, "attempt to transmit on zero (rx_)channel"); ++ return -EIO; ++ } ++ ++#if 0 ++ { ++ // where would be a better place for this? housekeeping? ++ u16 status; ++ pci_read_config_word (dev->pci_dev, PCI_STATUS, &status); ++ if (status & PCI_STATUS_REC_MASTER_ABORT) { ++ PRINTD (DBG_BUS|DBG_ERR, "Clearing PCI Master Abort (and cleaning up)"); ++ status &= ~PCI_STATUS_REC_MASTER_ABORT; ++ pci_write_config_word (dev->pci_dev, PCI_STATUS, status); ++ if (test_bit (tx_busy, &dev->flags)) { ++ hrz_kfree_skb (dev->tx_skb); ++ tx_release (dev); ++ } ++ } ++ } ++#endif ++ ++#ifdef DEBUG_HORIZON ++ /* wey-hey! */ ++ if (channel == 1023) { ++ unsigned int i; ++ unsigned short d = 0; ++ char * s = skb->data; ++ if (*s++ == 'D') { ++ for (i = 0; i < 4; ++i) { ++ d = (d<<4) | ((*s <= '9') ? (*s - '0') : (*s - 'a' + 10)); ++ ++s; ++ } ++ PRINTK (KERN_INFO, "debug bitmap is now %hx", debug = d); ++ } ++ } ++#endif ++ ++ // wait until TX is free and grab lock ++ if (tx_hold (dev)) ++ return -ERESTARTSYS; ++ ++ // Wait for enough space to be available in transmit buffer memory. ++ ++ // should be number of cells needed + 2 (according to hardware docs) ++ // = ((framelen+8)+47) / 48 + 2 ++ // = (framelen+7) / 48 + 3, hmm... faster to put addition inside XXX ++ buffers_required = (skb->len+(ATM_AAL5_TRAILER-1)) / ATM_CELL_PAYLOAD + 3; ++ ++ // replace with timer and sleep, add dev->tx_buffers_queue (max 1 entry) ++ spin_count = 0; ++ while ((free_buffers = rd_regw (dev, TX_FREE_BUFFER_COUNT_OFF)) < buffers_required) { ++ PRINTD (DBG_TX, "waiting for free TX buffers, got %d of %d", ++ free_buffers, buffers_required); ++ // what is the appropriate delay? implement a timeout? (depending on line speed?) ++ // mdelay (1); ++ // what happens if kill (current_pid, SIGKILL) ? ++ schedule(); ++ if (++spin_count > 1000) { ++ PRINTD (DBG_TX|DBG_ERR, "spun out waiting for tx buffers, got %d of %d", ++ free_buffers, buffers_required); ++ tx_release (dev); ++ return -ERESTARTSYS; ++ } ++ } ++ ++ // Select a channel to transmit the frame on. ++ if (channel == dev->last_vc) { ++ PRINTD (DBG_TX, "last vc hack: hit"); ++ tx_channel = dev->tx_last; ++ } else { ++ PRINTD (DBG_TX, "last vc hack: miss"); ++ // Are we currently transmitting this VC on one of the channels? ++ for (tx_channel = 0; tx_channel < TX_CHANS; ++tx_channel) ++ if (dev->tx_channel_record[tx_channel] == channel) { ++ PRINTD (DBG_TX, "vc already on channel: hit"); ++ break; ++ } ++ if (tx_channel == TX_CHANS) { ++ PRINTD (DBG_TX, "vc already on channel: miss"); ++ // Find and set up an idle channel. ++ tx_channel = setup_idle_tx_channel (dev, vcc); ++ if (tx_channel < 0) { ++ PRINTD (DBG_TX|DBG_ERR, "failed to get channel"); ++ tx_release (dev); ++ return tx_channel; ++ } ++ } ++ ++ PRINTD (DBG_TX, "got channel"); ++ SELECT_TX_CHANNEL(dev, tx_channel); ++ ++ dev->last_vc = channel; ++ dev->tx_last = tx_channel; ++ } ++ ++ PRINTD (DBG_TX, "using channel %u", tx_channel); ++ ++ YELLOW_LED_OFF(dev); ++ ++ // TX start transfer ++ ++ { ++ unsigned int tx_len = skb->len; ++ unsigned int tx_iovcnt = ATM_SKB(skb)->iovcnt; ++ // remember this so we can free it later ++ dev->tx_skb = skb; ++ ++ if (tx_iovcnt) { ++ // scatter gather transfer ++ dev->tx_regions = tx_iovcnt; ++ dev->tx_iovec = (struct iovec *) skb->data; ++ dev->tx_bytes = 0; ++ PRINTD (DBG_TX|DBG_BUS, "TX start scatter-gather transfer (iovec %p, len %d)", ++ skb->data, tx_len); ++ } else { ++ // simple transfer ++ dev->tx_regions = 0; ++ dev->tx_iovec = 0; ++ dev->tx_bytes = tx_len; ++ dev->tx_addr = skb->data; ++ PRINTD (DBG_TX|DBG_BUS, "TX start simple transfer (addr %p, len %d)", ++ skb->data, tx_len); ++ } ++ ++ // and do the business ++ tx_schedule (dev, 0); ++ ++ } ++ ++ return 0; +} + ++/********** reset a card **********/ + -+static int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) -+{ -+ if (cmd == ENI_MEMDUMP) { -+ printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d " -+ "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number); -+ dump(dev); -+ return 0; -+ } -+ if (cmd == ATM_SETCIRANGE) { -+ struct atm_cirange ci; -+ -+ if (copy_from_user(&ci,(void *) arg,sizeof(struct atm_cirange))) -+ return -EFAULT; -+ 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 void __init hrz_reset_card (const hrz_dev * dev) { ++ u32 control_0_reg = rd_regl (dev, CONTROL_0_REG); ++ ++ // why not set RESET_HORIZON to one and wait for the card to ++ // reassert that bit as zero? Like so: ++ control_0_reg = control_0_reg & RESET_HORIZON; ++ wr_regl (dev, CONTROL_0_REG, control_0_reg); ++ while (control_0_reg & RESET_HORIZON) ++ control_0_reg = rd_regl (dev, CONTROL_0_REG); ++ ++ // old reset code retained: ++ wr_regl (dev, CONTROL_0_REG, control_0_reg | ++ RESET_ATM | RESET_RX | RESET_TX | RESET_HOST); ++ // just guessing here ++ udelay (1000); ++ ++ wr_regl (dev, CONTROL_0_REG, control_0_reg); +} + ++/********** shutdown a card **********/ + -+static int eni_getsockopt(struct atm_vcc *vcc,int level,int optname, -+ void *optval,int optlen) -+{ -+#ifdef CONFIG_MMU_HACKS ++static void hrz_shutdown (const hrz_dev * dev) { ++ hrz_reset_card (dev); ++ ++ GREEN_LED_OFF(dev); ++} + -+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 }; ++/********** read the burnt in address **********/ + -+#else ++static u16 __init read_bia (const hrz_dev * dev, u16 addr) { ++ ++ u32 ctrl = rd_regl (dev, CONTROL_0_REG); ++ ++ inline void WRITE_IT_WAIT (void) { ++ wr_regl (dev, CONTROL_0_REG, ctrl); ++ udelay (5); ++ } ++ ++ inline void CLOCK_IT (void) { ++ // DI must be valid around rising SK edge ++ ctrl &= ~SEEPROM_SK; ++ WRITE_IT_WAIT(); ++ ctrl |= SEEPROM_SK; ++ WRITE_IT_WAIT(); ++ } ++ ++ const unsigned int addr_bits = 6; ++ const unsigned int data_bits = 16; ++ ++ unsigned int i; ++ ++ u16 res; ++ ++ ctrl &= ~(SEEPROM_CS | SEEPROM_SK | SEEPROM_DI); ++ WRITE_IT_WAIT(); ++ ++ // wake Serial EEPROM and send 110 (READ) command ++ ctrl |= (SEEPROM_CS | SEEPROM_DI); ++ CLOCK_IT(); ++ ++ ctrl |= SEEPROM_DI; ++ CLOCK_IT(); ++ ++ ctrl &= ~SEEPROM_DI; ++ CLOCK_IT(); ++ ++ for (i=0; i> 1; ++ ++ CLOCK_IT(); ++ ++ if (rd_regl (dev, CONTROL_0_REG) & SEEPROM_DO) ++ res |= (1 << (data_bits-1)); ++ } ++ ++ ctrl &= ~(SEEPROM_SK | SEEPROM_CS); ++ WRITE_IT_WAIT(); ++ ++ return res; ++} + -+static const struct atm_buffconst bctx = { sizeof(int),0,sizeof(int),0,0,0 }; -+static const struct atm_buffconst bcrx = { sizeof(int),0,sizeof(int),0,0,0 }; ++/********** initialise a card **********/ + -+#endif -+ if (level == SOL_AAL && (optname == SO_BCTXOPT || -+ optname == SO_BCRXOPT)) -+ return copy_to_user(optval,optname == SO_BCTXOPT ? &bctx : -+ &bcrx,sizeof(struct atm_buffconst)) ? -EFAULT : 0; -+ return -EINVAL; ++static int __init hrz_init (hrz_dev * dev) { ++ int onefivefive; ++ ++ u16 chan; ++ ++ int buff_count; ++ ++ HDW * mem; ++ ++ cell_buf * tx_desc; ++ cell_buf * rx_desc; ++ ++ u32 ctrl; ++ ++ ctrl = rd_regl (dev, CONTROL_0_REG); ++ PRINTD (DBG_INFO, "ctrl0reg is %#x", ctrl); ++ onefivefive = ctrl & ATM_LAYER_STATUS; ++ ++ if (onefivefive) ++ printk (DEV_LABEL ": Horizon Ultra (at 155.52 MBps)"); ++ else ++ printk (DEV_LABEL ": Horizon (at 25 MBps)"); ++ ++ printk (":"); ++ // Reset the card to get everything in a known state ++ ++ printk (" reset"); ++ hrz_reset_card (dev); ++ ++ // Clear all the buffer memory ++ ++ printk (" clearing memory"); ++ ++ for (mem = (HDW *) memmap; mem < (HDW *) (memmap + 1); ++mem) ++ wr_mem (dev, mem, 0); ++ ++ printk (" tx channels"); ++ ++ // All transmit eight channels are set up as AAL5 ABR channels with ++ // a 16us cell spacing. Why? ++ ++ // Channel 0 gets the free buffer at 100h, channel 1 gets the free ++ // buffer at 110h etc. ++ ++ for (chan = 0; chan < TX_CHANS; ++chan) { ++ tx_ch_desc * tx_desc = &memmap->tx_descs[chan]; ++ cell_buf * buf = &memmap->inittxbufs[chan]; ++ ++ // initialise the read and write buffer pointers ++ wr_mem (dev, &tx_desc->rd_buf_type, BUF_PTR(buf)); ++ wr_mem (dev, &tx_desc->wr_buf_type, BUF_PTR(buf)); ++ ++ // set the status of the initial buffers to empty ++ wr_mem (dev, &buf->next, BUFF_STATUS_EMPTY); ++ } ++ ++ // Use space bufn3 at the moment for tx buffers ++ ++ printk (" tx buffers"); ++ ++ tx_desc = memmap->bufn3; ++ ++ wr_mem (dev, &memmap->txfreebufstart.next, BUF_PTR(tx_desc) | BUFF_STATUS_EMPTY); ++ ++ for (buff_count = 0; buff_count < BUFN3_SIZE-1; buff_count++) { ++ wr_mem (dev, &tx_desc->next, BUF_PTR(tx_desc+1) | BUFF_STATUS_EMPTY); ++ tx_desc++; ++ } ++ ++ wr_mem (dev, &tx_desc->next, BUF_PTR(&memmap->txfreebufend) | BUFF_STATUS_EMPTY); ++ ++ // Initialise the transmit free buffer count ++ wr_regw (dev, TX_FREE_BUFFER_COUNT_OFF, BUFN3_SIZE); ++ ++ printk (" rx channels"); ++ ++ // Initialise all of the receive channels to be AAL5 disabled with ++ // an interrupt threshold of 0 ++ ++ for (chan = 0; chan < RX_CHANS; ++chan) { ++ rx_ch_desc * rx_desc = &memmap->rx_descs[chan]; ++ ++ wr_mem (dev, &rx_desc->wr_buf_type, CHANNEL_TYPE_AAL5 | RX_CHANNEL_DISABLED); ++ } ++ ++ printk (" rx buffers"); ++ ++ // Use space bufn4 at the moment for rx buffers ++ ++ rx_desc = memmap->bufn4; ++ ++ wr_mem (dev, &memmap->rxfreebufstart.next, BUF_PTR(rx_desc) | BUFF_STATUS_EMPTY); ++ ++ for (buff_count = 0; buff_count < BUFN4_SIZE-1; buff_count++) { ++ wr_mem (dev, &rx_desc->next, BUF_PTR(rx_desc+1) | BUFF_STATUS_EMPTY); ++ ++ rx_desc++; ++ } ++ ++ wr_mem (dev, &rx_desc->next, BUF_PTR(&memmap->rxfreebufend) | BUFF_STATUS_EMPTY); ++ ++ // Initialise the receive free buffer count ++ wr_regw (dev, RX_FREE_BUFFER_COUNT_OFF, BUFN4_SIZE); ++ ++ // Initialize Horizons registers ++ ++ // TX config ++ wr_regw (dev, TX_CONFIG_OFF, ++ ABR_ROUND_ROBIN | TX_NORMAL_OPERATION | DRVR_DRVRBAR_ENABLE); ++ ++ // RX config. Use 10-x VC bits, x VP bits, non user cells in channel 0. ++ wr_regw (dev, RX_CONFIG_OFF, ++ DISCARD_UNUSED_VPI_VCI_BITS_SET | NON_USER_CELLS_IN_ONE_CHANNEL | vpi_bits); ++ ++ // RX line config ++ wr_regw (dev, RX_LINE_CONFIG_OFF, ++ LOCK_DETECT_ENABLE | FREQUENCY_DETECT_ENABLE | GXTALOUT_SELECT_DIV4); ++ ++ // Set the max AAL5 cell count to be just enough to contain the ++ // largest AAL5 frame that the user wants to receive ++ wr_regw (dev, MAX_AAL5_CELL_COUNT_OFF, ++ (max_rx_size + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD); ++ ++ // Enable receive ++ wr_regw (dev, RX_CONFIG_OFF, rd_regw (dev, RX_CONFIG_OFF) | RX_ENABLE); ++ ++ printk (" control"); ++ ++ // Drive the OE of the LEDs then turn the green LED on ++ ctrl |= GREEN_LED_OE | YELLOW_LED_OE | GREEN_LED | YELLOW_LED; ++ wr_regl (dev, CONTROL_0_REG, ctrl); ++ ++ // Test for a 155-capable card ++ ++ if (onefivefive) { ++ // Select 155 mode... make this a choice (or: how do we detect ++ // external line speed and switch?) ++ ctrl |= ATM_LAYER_SELECT; ++ wr_regl (dev, CONTROL_0_REG, ctrl); ++ ++ // test SUNI-lite vs SAMBA ++ ++ // Register 0x00 in the SUNI will have some of bits 3-7 set, and ++ // they will always be zero for the SAMBA. Ha! Bloody hardware ++ // engineers. It'll never work. ++ ++ if (rd_framer (dev, 0) & 0x00f0) { ++ // SUNI ++ printk (" SUNI"); ++ ++ // Reset, just in case ++ wr_framer (dev, 0x00, 0x0080); ++ wr_framer (dev, 0x00, 0x0000); ++ ++ // Configure transmit FIFO ++ wr_framer (dev, 0x63, rd_framer (dev, 0x63) | 0x0002); ++ ++ // Set line timed mode ++ wr_framer (dev, 0x05, rd_framer (dev, 0x05) | 0x0001); ++ } else { ++ // SAMBA ++ printk (" SAMBA"); ++ ++ // Reset, just in case ++ wr_framer (dev, 0, rd_framer (dev, 0) | 0x0001); ++ wr_framer (dev, 0, rd_framer (dev, 0) &~ 0x0001); ++ ++ // Turn off diagnostic loopback and enable line-timed mode ++ wr_framer (dev, 0, 0x0002); ++ ++ // Turn on transmit outputs ++ wr_framer (dev, 2, 0x0B80); ++ } ++ } else { ++ // Select 25 mode ++ ctrl &= ~ATM_LAYER_SELECT; ++ ++ // Madge B154 setup ++ // none required? ++ } ++ ++ printk (" LEDs"); ++ ++ GREEN_LED_ON(dev); ++ YELLOW_LED_ON(dev); ++ ++ printk (" ESI="); ++ ++ { ++ u16 b = 0; ++ int i; ++ u8 * esi = dev->atm_dev->esi; ++ ++ // in the card I have, EEPROM ++ // addresses 0, 1, 2 contain 0 ++ // addresess 5, 6 etc. contain ffff ++ // NB: Madge prefix is 00 00 f6 (which is 00 00 6f in Ethernet bit order) ++ // the read_bia routine gets the BIA in Ethernet bit order ++ ++ for (i=0; i < ESI_LEN; ++i) { ++ if (i % 2 == 0) ++ b = read_bia (dev, i/2 + 2); ++ else ++ b = b >> 8; ++ esi[i] = b & 0xFF; ++ printk ("%02x", esi[i]); ++ } ++ } ++ ++ // Enable RX_Q and ?X_COMPLETE interrupts only ++ wr_regl (dev, INT_ENABLE_REG_OFF, INTERESTING_INTERRUPTS); ++ printk (" IRQ on"); ++ ++ printk (".\n"); ++ ++ return onefivefive; +} + ++/********** check max_sdu **********/ + -+static int eni_setsockopt(struct atm_vcc *vcc,int level,int optname, -+ void *optval,int optlen) -+{ ++static int check_max_sdu (hrz_aal aal, struct atm_trafprm * tp, unsigned int max_frame_size) { ++ PRINTD (DBG_FLOW|DBG_QOS, "check_max_sdu"); ++ ++ switch (aal) { ++ case aal0: ++ if (!(tp->max_sdu)) { ++ PRINTD (DBG_QOS, "defaulting max_sdu"); ++ tp->max_sdu = ATM_AAL0_SDU; ++ } else if (tp->max_sdu != ATM_AAL0_SDU) { ++ PRINTD (DBG_QOS|DBG_ERR, "rejecting max_sdu"); + return -EINVAL; ++ } ++ break; ++ case aal34: ++ if (tp->max_sdu == 0 || tp->max_sdu > ATM_MAX_AAL34_PDU) { ++ PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default"); ++ tp->max_sdu = ATM_MAX_AAL34_PDU; ++ } ++ break; ++ case aal5: ++ if (tp->max_sdu == 0 || tp->max_sdu > max_frame_size) { ++ PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default"); ++ tp->max_sdu = max_frame_size; ++ } ++ break; ++ } ++ return 0; +} + ++/********** check pcr **********/ + -+static int eni_send(struct atm_vcc *vcc,struct sk_buff *skb) -+{ -+ unsigned long flags; ++// something like this should be part of ATM Linux ++static int atm_pcr_check (struct atm_trafprm * tp, unsigned int pcr) { ++ // we are assuming non-UBR, and non-special values of pcr ++ if (tp->min_pcr == ATM_MAX_PCR) ++ PRINTD (DBG_QOS, "luser gave min_pcr = ATM_MAX_PCR"); ++ else if (tp->min_pcr < 0) ++ PRINTD (DBG_QOS, "luser gave negative min_pcr"); ++ else if (tp->min_pcr && tp->min_pcr > pcr) ++ PRINTD (DBG_QOS, "pcr less than min_pcr"); ++ else ++ // !! max_pcr = UNSPEC (0) is equivalent to max_pcr = MAX (-1) ++ // easier to #define ATM_MAX_PCR 0 and have all rates unsigned? ++ // [this would get rid of next two conditionals] ++ if ((0) && tp->max_pcr == ATM_MAX_PCR) ++ PRINTD (DBG_QOS, "luser gave max_pcr = ATM_MAX_PCR"); ++ else if ((tp->max_pcr != ATM_MAX_PCR) && tp->max_pcr < 0) ++ PRINTD (DBG_QOS, "luser gave negative max_pcr"); ++ else if (tp->max_pcr && tp->max_pcr != ATM_MAX_PCR && tp->max_pcr < pcr) ++ PRINTD (DBG_QOS, "pcr greater than max_pcr"); ++ else { ++ // each limit unspecified or not violated ++ PRINTD (DBG_QOS, "xBR(pcr) OK"); ++ return 0; ++ } ++ PRINTD (DBG_QOS, "pcr=%u, tp: min_pcr=%d, pcr=%d, max_pcr=%d", ++ pcr, tp->min_pcr, tp->pcr, tp->max_pcr); ++ return -EINVAL; ++} + -+ DPRINTK(">eni_send\n"); -+ if (!ENI_VCC(vcc)->tx) { -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ return -EINVAL; ++/********** open VC **********/ ++ ++static int hrz_open (struct atm_vcc * atm_vcc, short vpi, int vci) { ++ int error; ++ u16 channel; ++ ++ struct atm_qos * qos; ++ struct atm_trafprm * txtp; ++ struct atm_trafprm * rxtp; ++ ++ hrz_dev * dev = HRZ_DEV(atm_vcc->dev); ++ hrz_vcc vcc; ++ hrz_vcc * vccp; // allocated late ++ PRINTD (DBG_FLOW|DBG_VCC, "hrz_open %x %x", vpi, vci); ++ ++ // UNSPEC is deprecated, remove this code eventually ++#if defined ATM_VPI_UNSPEC ++ if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) { ++ PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)"); ++ return -EINVAL; ++ } ++#endif ++ ++ // deal with possibly wildcarded VCs ++ error = atm_find_ci (atm_vcc, &vpi, &vci); ++ if (error) { ++ PRINTD (DBG_WARN|DBG_VCC, "atm_find_ci failed!"); ++ return error; ++ } ++ PRINTD (DBG_VCC, "atm_find_ci gives %x %x", vpi, vci); ++ ++ error = vpci_to_channel (&channel, vpi, vci); ++ if (error) { ++ PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci); ++ return error; ++ } ++ ++ vcc.channel = channel; ++ // max speed for the moment ++ vcc.tx_rate = 0x0; ++ ++ qos = &atm_vcc->qos; ++ ++ // check AAL and remember it ++ switch (qos->aal) { ++ case ATM_AAL0: ++ // we would if it were 48 bytes and not 52! ++ PRINTD (DBG_QOS|DBG_VCC, "AAL0"); ++ vcc.aal = aal0; ++ break; ++ case ATM_AAL34: ++ // we would if I knew how do the SAR! ++ PRINTD (DBG_QOS|DBG_VCC, "AAL3/4"); ++ vcc.aal = aal34; ++ break; ++ case ATM_AAL5: ++ PRINTD (DBG_QOS|DBG_VCC, "AAL5"); ++ vcc.aal = aal5; ++ break; ++ default: ++ PRINTD (DBG_QOS|DBG_VCC, "Bad AAL!"); ++ return -EINVAL; ++ break; ++ } ++ ++ // TX traffic parameters ++ ++ // there are two, interrelated problems here: 1. the reservation of ++ // PCR is not a binary choice, we are given bounds and/or a ++ // desirable value; 2. the device is only capable of certain values, ++ // most of which are not integers. It is almost certainly acceptable ++ // to be off by a maximum of 1 to 10 cps. ++ ++ // Pragmatic choice: always store an integral PCR as that which has ++ // been allocated, even if we allocate a little (or a lot) less, ++ // after rounding. The actual allocation depends on what we can ++ // manage with our rate selection algorithm. The rate selection ++ // algorithm is given an integral PCR and a tolerance and told ++ // whether it should round the value up or down if the tolerance is ++ // exceeded; it returns: a) the actual rate selected (rounded up to ++ // the nearest integer), b) a bit pattern to feed to the timer ++ // register, and c) a failure value if no applicable rate exists. ++ ++ // Part of the job is done by atm_pcr_goal which gives us a PCR ++ // specification which says: EITHER grab the maximum available PCR ++ // (and perhaps a lower bound which we musn't pass), OR grab this ++ // amount, rounding down if you have to (and perhaps a lower bound ++ // which we musn't pass) OR grab this amount, rounding up if you ++ // have to (and perhaps an upper bound which we musn't pass). If any ++ // bounds ARE passed we fail. Note that rounding is only rounding to ++ // match device limitations, we do not round down to satisfy ++ // bandwidth availability even if this would not violate any given ++ // lower bound. ++ ++ // Note: telephony = 64kb/s = 48 byte cell payload @ 500/3 cells/s ++ // (say) so this is not even a binary fixpoint cell rate (but this ++ // device can do it). To avoid this sort of hassle we use a ++ // tolerance parameter (currently fixed at 10 cps). ++ ++ PRINTD (DBG_QOS, "TX:"); ++ ++ txtp = &qos->txtp; ++ ++ // set up defaults for no traffic ++ vcc.tx_rate = 0; ++ // who knows what would actually happen if you try and send on this? ++ vcc.tx_xbr_bits = IDLE_RATE_TYPE; ++ vcc.tx_pcr_bits = CLOCK_DISABLE; ++#if 0 ++ vcc.tx_scr_bits = CLOCK_DISABLE; ++ vcc.tx_bucket_bits = 0; ++#endif ++ ++ if (txtp->traffic_class != ATM_NONE) { ++ error = check_max_sdu (vcc.aal, txtp, max_tx_size); ++ if (error) { ++ PRINTD (DBG_QOS, "TX max_sdu check failed"); ++ return error; ++ } ++ ++ switch (txtp->traffic_class) { ++ case ATM_UBR: { ++ // we take "the PCR" as a rate-cap ++ // not reserved ++ vcc.tx_rate = 0; ++ make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, 0); ++ vcc.tx_xbr_bits = ABR_RATE_TYPE; ++ break; ++ } ++#if 0 ++ case ATM_ABR: { ++ // reserve min, allow up to max ++ vcc.tx_rate = 0; // ? ++ make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, 0); ++ vcc.tx_xbr_bits = ABR_RATE_TYPE; ++ break; ++ } ++#endif ++ case ATM_CBR: { ++ int pcr = atm_pcr_goal (txtp); ++ rounding r; ++ if (!pcr) { ++ // down vs. up, remaining bandwidth vs. unlimited bandwidth!! ++ // should really have: once someone gets unlimited bandwidth ++ // that no more non-UBR channels can be opened until the ++ // unlimited one closes?? For the moment, round_down means ++ // greedy people actually get something and not nothing ++ r = round_down; ++ // slight race (no locking) here so we may get -EAGAIN ++ // later; the greedy bastards would deserve it :) ++ PRINTD (DBG_QOS, "snatching all remaining TX bandwidth"); ++ pcr = dev->tx_avail; ++ } else if (pcr < 0) { ++ r = round_down; ++ pcr = -pcr; ++ } else { ++ r = round_up; + } -+ if (!skb) { -+ printk(KERN_CRIT "!skb in eni_send ?\n"); -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ return -EINVAL; ++ error = make_rate_with_tolerance (dev, pcr, r, 10, ++ &vcc.tx_pcr_bits, &vcc.tx_rate); ++ if (error) { ++ PRINTD (DBG_QOS, "could not make rate from TX PCR"); ++ return error; + } -+ if (vcc->qos.aal == ATM_AAL0) { -+ if (skb->len != ATM_CELL_SIZE-1) { -+ if (vcc->pop) vcc->pop(vcc,skb); -+ else dev_kfree_skb(skb); -+ return -EINVAL; -+ } -+ *(u32 *) skb->data = htonl(*(u32 *) skb->data); ++ // not really clear what further checking is needed ++ error = atm_pcr_check (txtp, vcc.tx_rate); ++ if (error) { ++ PRINTD (DBG_QOS, "TX PCR failed consistency check"); ++ return error; + } -+submitted++; -+ ATM_SKB(skb)->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++; ++ vcc.tx_xbr_bits = CBR_RATE_TYPE; ++ break; ++ } ++#if 0 ++ case ATM_VBR: { ++ int pcr = atm_pcr_goal (txtp); ++ // int scr = atm_scr_goal (txtp); ++ int scr = pcr/2; // just for fun ++ unsigned int mbs = 60; // just for fun ++ rounding pr; ++ rounding sr; ++ unsigned int bucket; ++ if (!pcr) { ++ pr = round_nearest; ++ pcr = 1<<30; ++ } else if (pcr < 0) { ++ pr = round_down; ++ pcr = -pcr; ++ } else { ++ pr = round_up; ++ } ++ error = make_rate_with_tolerance (dev, pcr, pr, 10, ++ &vcc.tx_pcr_bits, 0); ++ if (!scr) { ++ // see comments for PCR with CBR above ++ sr = round_down; ++ // slight race (no locking) here so we may get -EAGAIN ++ // later; the greedy bastards would deserve it :) ++ PRINTD (DBG_QOS, "snatching all remaining TX bandwidth"); ++ scr = dev->tx_avail; ++ } else if (scr < 0) { ++ sr = round_down; ++ scr = -scr; ++ } else { ++ sr = round_up; ++ } ++ error = make_rate_with_tolerance (dev, scr, sr, 10, ++ &vcc.tx_scr_bits, &vcc.tx_rate); ++ if (error) { ++ PRINTD (DBG_QOS, "could not make rate from TX SCR"); ++ return error; ++ } ++ // not really clear what further checking is needed ++ // error = atm_scr_check (txtp, vcc.tx_rate); ++ if (error) { ++ PRINTD (DBG_QOS, "TX SCR failed consistency check"); ++ return error; ++ } ++ // bucket calculations (from a piece of paper...) cell bucket ++ // capacity must be largest integer smaller than m(p-s)/p + 1 ++ // where m = max burst size, p = pcr, s = scr ++ bucket = mbs*(pcr-scr)/pcr; ++ if (bucket*pcr != mbs*(pcr-scr)) ++ bucket += 1; ++ if (bucket > BUCKET_MAX_SIZE) { ++ PRINTD (DBG_QOS, "shrinking bucket from %u to %u", ++ bucket, BUCKET_MAX_SIZE); ++ bucket = BUCKET_MAX_SIZE; ++ } ++ vcc.tx_xbr_bits = VBR_RATE_TYPE; ++ vcc.tx_bucket_bits = bucket; ++ break; ++ } ++#endif ++ default: { ++ PRINTD (DBG_QOS, "unsupported TX traffic class"); ++ return -EINVAL; ++ break; ++ } ++ } ++ } ++ ++ // RX traffic parameters ++ ++ PRINTD (DBG_QOS, "RX:"); ++ ++ rxtp = &qos->rxtp; ++ ++ // set up defaults for no traffic ++ vcc.rx_rate = 0; ++ ++ if (rxtp->traffic_class != ATM_NONE) { ++ error = check_max_sdu (vcc.aal, rxtp, max_rx_size); ++ if (error) { ++ PRINTD (DBG_QOS, "RX max_sdu check failed"); ++ return error; ++ } ++ switch (rxtp->traffic_class) { ++ case ATM_UBR: { ++ // not reserved ++ break; ++ } ++#if 0 ++ case ATM_ABR: { ++ // reserve min ++ vcc.rx_rate = 0; // ? ++ break; ++ } ++#endif ++ case ATM_CBR: { ++ int pcr = atm_pcr_goal (rxtp); ++ if (!pcr) { ++ // slight race (no locking) here so we may get -EAGAIN ++ // later; the greedy bastards would deserve it :) ++ PRINTD (DBG_QOS, "snatching all remaining RX bandwidth"); ++ pcr = dev->tx_avail; ++ } else if (pcr < 0) { ++ pcr = -pcr; ++ } ++ vcc.rx_rate = pcr; ++ // not really clear what further checking is needed ++ error = atm_pcr_check (rxtp, vcc.rx_rate); ++ if (error) { ++ PRINTD (DBG_QOS, "RX PCR failed consistency check"); ++ return error; + } -+ restore_flags(flags); -+ return 0; ++ break; ++ } ++ case ATM_VBR: { ++ // int scr = atm_scr_goal (txtp); ++ int scr = 1<<16; // just for fun ++ if (!scr) { ++ // slight race (no locking) here so we may get -EAGAIN ++ // later; the greedy bastards would deserve it :) ++ PRINTD (DBG_QOS, "snatching all remaining RX bandwidth"); ++ scr = dev->tx_avail; ++ } else if (scr < 0) { ++ scr = -scr; ++ } ++ vcc.rx_rate = scr; ++ // not really clear what further checking is needed ++ // error = atm_scr_check (rxtp, vcc.rx_rate); ++ if (error) { ++ PRINTD (DBG_QOS, "RX SCR failed consistency check"); ++ return error; ++ } ++ break; ++ } ++ default: { ++ PRINTD (DBG_QOS, "unsupported RX traffic class"); ++ return -EINVAL; ++ break; ++ } ++ } ++ } ++ ++ ++ // late abort useful for diagnostics ++ if (vcc.aal != aal5) { ++ PRINTD (DBG_QOS, "AAL not supported"); ++ return -EINVAL; ++ } ++ ++ // get space for our vcc stuff and copy parameters into it ++ vccp = kmalloc (sizeof(hrz_vcc), GFP_KERNEL); ++ if (!vccp) { ++ PRINTK (KERN_ERR, "out of memory!"); ++ return -ENOMEM; ++ } ++ *vccp = vcc; ++ ++ // clear error and grab cell rate resource lock ++ error = 0; ++ spin_lock (&dev->rates_lock); ++ ++ if (vcc.tx_rate > dev->tx_avail) { ++ PRINTD (DBG_QOS, "not enough TX PCR left"); ++ error = -EAGAIN; ++ } ++ ++ if (vcc.rx_rate > dev->rx_avail) { ++ PRINTD (DBG_QOS, "not enough RX PCR left"); ++ error = -EAGAIN; ++ } ++ ++ if (!error) { ++ // really consume cell rates ++ dev->tx_avail -= vcc.tx_rate; ++ dev->rx_avail -= vcc.rx_rate; ++ PRINTD (DBG_QOS|DBG_VCC, "reserving %u TX PCR and %u RX PCR", ++ vcc.tx_rate, vcc.rx_rate); ++ } ++ ++ // release lock and exit on error ++ spin_unlock (&dev->rates_lock); ++ if (error) { ++ PRINTD (DBG_QOS|DBG_VCC, "insufficient cell rate resources"); ++ kfree (vccp); ++ return error; ++ } ++ ++ // this is "immediately before allocating the connection identifier ++ // in hardware" - so long as the next call does not fail :) ++ atm_vcc->flags |= ATM_VF_ADDR; ++ ++ // any errors here are very serious and should never occur ++ ++ if (rxtp->traffic_class != ATM_NONE) { ++ if (dev->rxer[channel]) { ++ PRINTD (DBG_ERR|DBG_VCC, "VC already open for RX"); ++ return -EBUSY; ++ } ++ error = hrz_open_rx (dev, channel); ++ if (error) { ++ kfree (vccp); ++ return error; ++ } ++ // this link allows RX frames through ++ dev->rxer[channel] = atm_vcc; ++ } ++ ++ // success, set elements of atm_vcc ++ atm_vcc->vpi = vpi; ++ atm_vcc->vci = vci; ++ atm_vcc->dev_data = (void *) vccp; ++ ++ // indicate readiness ++ atm_vcc->flags |= ATM_VF_READY; ++ ++ MOD_INC_USE_COUNT; ++ return 0; +} + ++/********** close VC **********/ + -+static int eni_sg_send(struct atm_vcc *vcc,unsigned long start, -+ unsigned long size) -+{ -+ return vcc->qos.aal == ATM_AAL5 && !((start | size) & 3); -+ /* don't tolerate misalignment */ -+} ++static void hrz_close (struct atm_vcc * atm_vcc) { ++ hrz_dev * dev = HRZ_DEV(atm_vcc->dev); ++ hrz_vcc * vcc = HRZ_VCC(atm_vcc); ++ u16 channel = vcc->channel; ++ PRINTD (DBG_VCC|DBG_FLOW, "hrz_close"); ++ ++ // indicate unreadiness ++ atm_vcc->flags &= ~ATM_VF_READY; + ++ if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) { ++ unsigned int i; ++ ++ // let any TX on this channel that has started complete ++ // no restart, just keep trying ++ while (tx_hold (dev)) ++ ; ++ // remove record of any tx_channel having been setup for this channel ++ for (i = 0; i < TX_CHANS; ++i) ++ if (dev->tx_channel_record[i] == channel) { ++ dev->tx_channel_record[i] = -1; ++ break; ++ } ++ if (dev->last_vc == channel) ++ dev->tx_last = -1; ++ tx_release (dev); ++ } + -+static void eni_phy_put(struct atm_dev *dev,unsigned char value, -+ unsigned long addr) -+{ -+ writel(value,ENI_DEV(dev)->phy+addr); ++ if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) { ++ // disable RXing - it tries quite hard ++ hrz_close_rx (dev, channel); ++ // forget the vcc - no more skbs will be pushed ++ if (atm_vcc != dev->rxer[channel]) ++ PRINTK (KERN_ERR, "%s atm_vcc=%p rxer[channel]=%p", ++ "arghhh! we're going to die!", ++ atm_vcc, dev->rxer[channel]); ++ dev->rxer[channel] = 0; ++ } ++ ++ // atomically release our rate reservation ++ spin_lock (&dev->rates_lock); ++ PRINTD (DBG_QOS|DBG_VCC, "releasing %u TX PCR and %u RX PCR", ++ vcc->tx_rate, vcc->rx_rate); ++ dev->tx_avail += vcc->tx_rate; ++ dev->rx_avail += vcc->rx_rate; ++ spin_unlock (&dev->rates_lock); ++ ++ // free our structure ++ kfree (vcc); ++ // say the VPI/VCI is free again ++ atm_vcc->flags &= ~ATM_VF_ADDR; ++ MOD_DEC_USE_COUNT; ++} ++ ++static int hrz_getsockopt (struct atm_vcc * atm_vcc, int level, int optname, ++ void *optval, int optlen) { ++ hrz_dev * dev = HRZ_DEV(atm_vcc->dev); ++ PRINTD (DBG_FLOW|DBG_VCC, "hrz_getsockopt"); ++ switch (level) { ++ case SOL_SOCKET: ++ switch (optname) { ++ case SO_BCTXOPT: ++ // return the right thing ++ break; ++ case SO_BCRXOPT: ++ // return the right thing ++ break; ++ default: ++ return -ENOPROTOOPT; ++ break; ++ }; ++ break; ++ } ++ return -EINVAL; +} + ++static int hrz_setsockopt (struct atm_vcc * atm_vcc, int level, int optname, ++ void *optval, int optlen) { ++ hrz_dev * dev = HRZ_DEV(atm_vcc->dev); ++ PRINTD (DBG_FLOW|DBG_VCC, "hrz_setsockopt"); ++ switch (level) { ++ case SOL_SOCKET: ++ switch (optname) { ++ case SO_BCTXOPT: ++ // not settable ++ break; ++ case SO_BCRXOPT: ++ // not settable ++ break; ++ default: ++ return -ENOPROTOOPT; ++ break; ++ }; ++ break; ++ } ++ return -EINVAL; ++} + ++static int hrz_sg_send (struct atm_vcc * atm_vcc, ++ unsigned long start, ++ unsigned long size) { ++ if (atm_vcc->qos.aal == ATM_AAL5) { ++ PRINTD (DBG_FLOW|DBG_VCC, "hrz_sg_send: yes"); ++ return 1; ++ } else { ++ PRINTD (DBG_FLOW|DBG_VCC, "hrz_sg_send: no"); ++ return 0; ++ } ++} + -+static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr) -+{ -+ return readl(ENI_DEV(dev)->phy+addr); ++#if 0 ++static int hrz_ioctl (struct atm_dev * atm_dev, unsigned int cmd, void *arg) { ++ hrz_dev * dev = HRZ_DEV(atm_dev); ++ PRINTD (DBG_FLOW, "hrz_ioctl"); ++ return -1; +} + ++unsigned char hrz_phy_get (struct atm_dev * atm_dev, unsigned long addr) { ++ hrz_dev * dev = HRZ_DEV(atm_dev); ++ PRINTD (DBG_FLOW, "hrz_phy_get"); ++ return 0; ++} + -+static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) -+{ -+ static const char *signal[] = { "LOST","unknown","okay" }; -+ struct eni_dev *eni_dev = ENI_DEV(dev); -+ struct atm_vcc *vcc; -+ int left,i; ++static void hrz_phy_put (struct atm_dev * atm_dev, unsigned char value, ++ unsigned long addr) { ++ hrz_dev * dev = HRZ_DEV(atm_dev); ++ PRINTD (DBG_FLOW, "hrz_phy_put"); ++} + -+ left = *pos; -+ if (!left) -+ return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, " -+ "%d cps remaining\n",dev->number,signal[(int) dev->signal], -+ eni_dev->mem >> 10,eni_dev->tx_bw); -+ left--; -+ if (!left) -+ return sprintf(page,"Bursts: TX" -+#if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \ -+ !defined(CONFIG_ATM_ENI_BURST_TX_8W) && \ -+ !defined(CONFIG_ATM_ENI_BURST_TX_4W) && \ -+ !defined(CONFIG_ATM_ENI_BURST_TX_2W) -+ " none" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_TX_16W -+ " 16W" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_TX_8W -+ " 8W" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_TX_4W -+ " 4W" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_TX_2W -+ " 2W" -+#endif -+ ", RX" -+#if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \ -+ !defined(CONFIG_ATM_ENI_BURST_RX_8W) && \ -+ !defined(CONFIG_ATM_ENI_BURST_RX_4W) && \ -+ !defined(CONFIG_ATM_ENI_BURST_RX_2W) -+ " none" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_RX_16W -+ " 16W" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_RX_8W -+ " 8W" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_RX_4W -+ " 4W" -+#endif -+#ifdef CONFIG_ATM_ENI_BURST_RX_2W -+ " 2W" -+#endif -+#ifndef CONFIG_ATM_ENI_TUNE_BURST -+ " (default)" ++static int hrz_change_qos (struct atm_vcc * atm_vcc, struct atm_qos *qos, int flgs) { ++ hrz_dev * dev = HRZ_DEV(vcc->dev); ++ PRINTD (DBG_FLOW, "hrz_change_qos"); ++ return -1; ++} +#endif -+ "\n"); -+ for (i = 0; i < NR_CHAN; i++) { -+ struct eni_tx *tx = eni_dev->tx+i; -+ -+ if (!tx->send) continue; -+ if (--left) continue; -+ return sprintf(page,"tx[%d]: 0x%06x-0x%06lx (%6ld bytes), " -+ "rsv %d cps, shp %d cps%s\n",i,(tx->send-eni_dev->ram)*4, -+ (tx->send-eni_dev->ram+tx->words)*4-1,tx->words*4, -+ tx->reserved,tx->shaping, -+ tx == eni_dev->ubr ? " (UBR)" : ""); -+ } -+ for (vcc = dev->vccs; vcc; vcc = vcc->next) { -+ struct eni_vcc *eni_vcc = ENI_VCC(vcc); -+ int length; + -+ if (--left) continue; -+ length = sprintf(page,"vcc %4d: ",vcc->vci); -+ if (eni_vcc->rx) { -+ length += sprintf(page+length,"0x%06x-0x%06lx " -+ "(%6ld bytes)",(eni_vcc->recv-eni_dev->ram)*4, -+ (eni_vcc->recv-eni_dev->ram+eni_vcc->words)*4-1, -+ eni_vcc->words*4); -+ if (eni_vcc->tx) length += sprintf(page+length,", "); -+ } -+ if (eni_vcc->tx) -+ length += sprintf(page+length,"tx[%d]", -+ eni_vcc->tx->index); -+ page[length] = '\n'; -+ return length+1; -+ } -+ for (i = 0; i < eni_dev->free_len; i++) { -+ struct eni_free *fe = eni_dev->free_list+i; -+ unsigned long offset; ++/********** proc file contents **********/ + -+ if (--left) continue; -+ offset = (unsigned long) eni_dev->ram+eni_dev->base_diff; -+ return sprintf(page,"free 0x%06lx-0x%06lx (%6d bytes)\n", -+ fe->start-offset,fe->start-offset+(1 << fe->order)-1, -+ 1 << fe->order); -+ } -+ return 0; ++static int hrz_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) { ++ hrz_dev * dev = HRZ_DEV(atm_dev); ++ int left = *pos; ++ PRINTD (DBG_FLOW, "hrz_proc_read"); ++ ++ /* more diagnostics here? */ ++ ++#if 0 ++ { ++ // VBR temporary diags ++ unsigned int i; ++ for (i = 0; i < TX_CHANS; ++i) { ++ if (!left--) ++ return sprintf (page, "bucket %u: %u/%u\n", i, ++ query_tx_channel_config (dev, i, BUCKET_FULLNESS_ACCESS), ++ query_tx_channel_config (dev, i, BUCKET_CAPACITY_ACCESS)); ++ } ++ } ++#endif ++ ++ if (!left--) ++ return sprintf (page, ++ "cells: TX %lu, RX %lu, HEC errors %lu, unassigned %lu.\n", ++ dev->tx_cell_count, dev->rx_cell_count, ++ dev->hec_error_count, dev->unassigned_cell_count); ++ ++ if (!left--) ++ return sprintf (page, ++ "free cell buffers: TX %hu, RX %hu+%hu.\n", ++ rd_regw (dev, TX_FREE_BUFFER_COUNT_OFF), ++ rd_regw (dev, RX_FREE_BUFFER_COUNT_OFF), ++ dev->noof_spare_buffers); ++ ++ if (!left--) ++ return sprintf (page, ++ "cps remaining: TX %u, RX %u\n", ++ dev->tx_avail, dev->rx_avail); ++ ++ return 0; +} + -+ -+static const struct atmdev_ops ops = { -+ NULL, /* no dev_close */ -+ eni_open, -+ eni_close, -+ eni_ioctl, -+ eni_getsockopt, -+ eni_setsockopt, -+ eni_send, -+ eni_sg_send, -+ NULL, /* no send_oam */ -+ eni_phy_put, -+ eni_phy_get, -+ NULL, /* no feedback */ -+ eni_change_qos, /* no change_qos */ -+ NULL, /* no free_rx_skb */ -+ eni_proc_read -+}; -+ -+ -+__initfunc(int eni_detect(void)) -+{ -+ struct atm_dev *dev; -+ struct eni_dev *eni_dev; -+ int devs,type; -+ -+ 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++) { -+ struct pci_dev *pci_dev; -+ -+ pci_dev = NULL; -+ while ((pci_dev = pci_find_device(PCI_VENDOR_ID_EF,type ? -+ PCI_DEVICE_ID_EF_ATM_ASIC : PCI_DEVICE_ID_EF_ATM_FPGA, -+ pci_dev))) { -+ if (!devs) { -+ zeroes = kmalloc(4,GFP_KERNEL); -+ if (!zeroes) { -+ kfree(eni_dev); -+ return -ENOMEM; -+ } -+ } -+ dev = atm_dev_register(DEV_LABEL,&ops,-1,0); -+ if (!dev) break; -+ eni_dev->pci_dev = pci_dev; -+ 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; -+ devs++; -+ eni_dev = (struct eni_dev *) kmalloc(sizeof(struct -+ eni_dev),GFP_KERNEL); -+ if (!eni_dev) break; -+ } ++static const struct atmdev_ops hrz_ops = { ++ NULL, // no hrz_dev_close ++ hrz_open, ++ hrz_close, ++ NULL, // no hrz_ioctl ++ hrz_getsockopt, ++ hrz_setsockopt, ++ hrz_send, ++ hrz_sg_send, ++ NULL, // no send_oam - not in fact used yet ++ NULL, // no hrz_phy_put - not needed in this driver ++ NULL, // no hrz_phy_get - not needed in this driver ++ NULL, // no feedback - feedback to the driver! ++ NULL, // no hrz_change_qos ++ NULL, // no free_rx_skb ++ hrz_proc_read ++}; ++ ++static int __init hrz_probe (void) { ++ struct pci_dev * pci_dev; ++ int devs; ++ ++ PRINTD (DBG_FLOW, "hrz_probe"); ++ ++ if (!pci_present()) ++ return 0; ++ ++ devs = 0; ++ pci_dev = NULL; ++ while ((pci_dev = pci_find_device ++ (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, pci_dev) ++ )) { ++ hrz_dev * dev; ++ ++ // adapter slot free, read resources from PCI configuration space ++ u32 iobase = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; ++ u32 * membase = bus_to_virt ++ (pci_dev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK); ++ u8 irq = pci_dev->irq; ++ ++ // check IO region ++ if (check_region (iobase, HRZ_IO_EXTENT)) { ++ PRINTD (DBG_WARN, "IO range already in use"); ++ continue; ++ } ++ ++ dev = kmalloc (sizeof(hrz_dev), GFP_KERNEL); ++ if (!dev) { ++ // perhaps we should be nice: deregister all adapters and abort? ++ PRINTD (DBG_ERR, "out of memory"); ++ continue; ++ } ++ ++ memset (dev, 0, sizeof(hrz_dev)); ++ ++ // grab IRQ and install handler - move this someplace more sensible ++ if (request_irq (irq, ++ interrupt_handler, ++ SA_SHIRQ, /* irqflags guess */ ++ DEV_LABEL, /* name guess */ ++ dev)) { ++ PRINTD (DBG_WARN, "request IRQ failed!"); ++ // free_irq is at "endif" ++ } else { ++ ++ PRINTD (DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p", ++ iobase, irq, membase); ++ ++ dev->atm_dev = atm_dev_register (DEV_LABEL, &hrz_ops, -1, 0); ++ if (!(dev->atm_dev)) { ++ PRINTD (DBG_ERR, "failed to register Madge ATM adapter"); ++ } else { ++ unsigned char lat; ++ ++ PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p", ++ dev->atm_dev->number, dev, dev->atm_dev); ++ dev->atm_dev->dev_data = (void *) dev; ++ dev->pci_dev = pci_dev; ++ ++ /* XXX DEV_LABEL is a guess */ ++ request_region (iobase, HRZ_IO_EXTENT, DEV_LABEL); ++ ++ // enable bus master accesses ++ pci_set_master (pci_dev); ++ ++ // frobnicate latency (upwards, usually) ++ pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat); ++ if (pci_lat) { ++ PRINTD (DBG_INFO, "%s PCI latency timer from %hu to %hu", ++ "changing", lat, pci_lat); ++ pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, pci_lat); ++ } else if (lat < MIN_PCI_LATENCY) { ++ PRINTK (KERN_INFO, "%s PCI latency timer from %hu to %hu", ++ "increasing", lat, MIN_PCI_LATENCY); ++ pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY); + } -+ kfree(eni_dev); -+ if (!devs && zeroes) { -+ kfree(zeroes); -+ zeroes = NULL; ++ ++ dev->iobase = iobase; ++ dev->irq = irq; ++ dev->membase = membase; ++ ++ dev->rx_q_entry = dev->rx_q_reset = &memmap->rx_q_entries[0]; ++ dev->rx_q_wrap = &memmap->rx_q_entries[RX_CHANS-1]; ++ ++ // these next three are performance hacks ++ dev->last_vc = -1; ++ dev->tx_last = -1; ++ dev->tx_idle = 0; ++ ++ dev->tx_regions = 0; ++ dev->tx_bytes = 0; ++ dev->tx_skb = 0; ++ dev->tx_iovec = 0; ++ ++ dev->tx_cell_count = 0; ++ dev->rx_cell_count = 0; ++ dev->hec_error_count = 0; ++ dev->unassigned_cell_count = 0; ++ ++ dev->noof_spare_buffers = 0; ++ ++ { ++ unsigned int i; ++ for (i = 0; i < TX_CHANS; ++i) ++ dev->tx_channel_record[i] = -1; + } -+ return devs; ++ ++ dev->flags = 0; ++ ++ // Allocate cell rates and remember ASIC version ++ // Fibre: ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53 ++ // Copper: (WRONG) we want 6 into the above, close to 25Mb/s ++ // Copper: (plagarise!) 25600000/8/270*260/53 - n/53 ++ ++ if (hrz_init (dev)) { ++ // to be really pedantic, this should be ATM_OC3c_PCR ++ dev->tx_avail = ATM_OC3_PCR; ++ dev->rx_avail = ATM_OC3_PCR; ++ set_bit (ultra, &dev->flags); // NOT "|= ultra" ! ++ } else { ++ dev->tx_avail = ((25600000/8)*26)/(27*53); ++ dev->rx_avail = ((25600000/8)*26)/(27*53); ++ PRINTD (DBG_WARN, "Buggy ASIC: no TX bus-mastering."); ++ } ++ ++ // rate changes spinlock ++ spin_lock_init (&dev->rate_lock); ++ ++ // on-board memory access spinlock; we want atomic reads and ++ // writes to adapter memory (handles IRQ and SMP) ++ spin_lock_init (&dev->mem_lock); ++ ++ dev->tx_queue = 0; ++ ++ // vpi in 0..4, vci in 6..10 ++ dev->atm_dev->ci_range.vpi_bits = vpi_bits; ++ dev->atm_dev->ci_range.vci_bits = 10-vpi_bits; ++ ++ // update count and linked list ++ ++devs; ++ dev->prev = hrz_devs; ++ hrz_devs = dev; ++ // success ++ continue; ++ ++ /* not currently reached */ ++ atm_dev_deregister (dev->atm_dev); ++ } /* atm_dev_register */ ++ free_irq (irq, dev); ++ } /* request_irq */ ++ kfree (dev); ++ } /* kmalloc and while */ ++ return devs; +} + ++static void __init hrz_check_args (void) { ++#ifdef DEBUG_HORIZON ++ PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK); ++#else ++ if (debug) ++ PRINTK (KERN_NOTICE, "no debug support in this image"); ++#endif ++ ++ if (vpi_bits > HRZ_MAX_VPI) ++ PRINTK (KERN_ERR, "vpi_bits has been limited to %hu", ++ vpi_bits = HRZ_MAX_VPI); ++ ++ if (max_tx_size > TX_AAL5_LIMIT) ++ PRINTK (KERN_NOTICE, "max_tx_size has been limited to %hu", ++ max_tx_size = TX_AAL5_LIMIT); ++ ++ if (max_rx_size > RX_AAL5_LIMIT) ++ PRINTK (KERN_NOTICE, "max_rx_size has been limited to %hu", ++ max_rx_size = RX_AAL5_LIMIT); ++ ++ return; ++} + +#ifdef MODULE ++EXPORT_NO_SYMBOLS; + -+int init_module(void) -+{ -+ if (!eni_detect()) { -+ printk(KERN_ERR DEV_LABEL ": no adapter found\n"); -+ return -ENXIO; -+ } -+ MOD_INC_USE_COUNT; -+ return 0; ++MODULE_AUTHOR(maintainer_string); ++MODULE_DESCRIPTION(description_string); ++MODULE_PARM(debug, "h"); ++MODULE_PARM(vpi_bits, "h"); ++MODULE_PARM(max_tx_size, "h"); ++MODULE_PARM(max_rx_size, "h"); ++MODULE_PARM(pci_lat, "b"); ++MODULE_PARM_DESC(debug, "debug bitmap, see .h file"); ++MODULE_PARM_DESC(vpi_bits, "number of bits (0..4) to allocate to VPIs"); ++MODULE_PARM_DESC(max_tx_size, "maximum size of TX AAL5 frames"); ++MODULE_PARM_DESC(max_rx_size, "maximum size of RX AAL5 frames"); ++MODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles"); ++ ++/********** module entry **********/ ++ ++int init_module (void) { ++ int devs; ++ ++ // sanity check - cast is needed since printk does not support %Zu ++ if (sizeof(struct MEMMAP) != 128*1024/4) { ++ PRINTK (KERN_ERR, "Fix struct MEMMAP (is %lu fakewords).", ++ (unsigned long) sizeof(struct MEMMAP)); ++ return -ENOMEM; ++ } ++ ++ show_version(); ++ ++ // check arguments ++ hrz_check_args(); ++ ++ // get the juice ++ devs = hrz_probe(); ++ ++ if (devs) { ++ init_timer (&housekeeping); ++ housekeeping.function = do_housekeeping; ++ // paranoia ++ housekeeping.data = 1; ++ set_timer (&housekeeping, 0); ++ } else { ++ PRINTK (KERN_ERR, "no (usable) adapters found"); ++ } ++ ++ return devs ? 0 : -ENODEV; +} + ++/********** module exit **********/ + -+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 ? :-) -+ */ ++void cleanup_module (void) { ++ hrz_dev * dev; ++ PRINTD (DBG_FLOW, "cleanup_module"); ++ ++ // paranoia ++ housekeeping.data = 0; ++ del_timer (&housekeeping); ++ ++ while (hrz_devs) { ++ dev = hrz_devs; ++ hrz_devs = dev->prev; ++ ++ PRINTD (DBG_INFO, "closing %p (atm_dev = %p)", dev, dev->atm_dev); ++ hrz_shutdown (dev); ++ atm_dev_deregister (dev->atm_dev); ++ free_irq (dev->irq, dev); ++ release_region (dev->iobase, HRZ_IO_EXTENT); ++ kfree (dev); ++ } ++ ++ return; ++} ++ ++#else ++ ++/********** monolithic entry **********/ ++ ++int __init hrz_detect (void) { ++ int devs; ++ ++ // sanity check - cast is needed since printk does not support %Zu ++ if (sizeof(struct MEMMAP) != 128*1024/4) { ++ PRINTK (KERN_ERR, "Fix struct MEMMAP (is %lu fakewords).", ++ (unsigned long) sizeof(struct MEMMAP)); ++ return 0; ++ } ++ ++ show_version(); ++ ++ // what about command line arguments? ++ // check arguments ++ hrz_check_args(); ++ ++ // get the juice ++ devs = hrz_probe(); ++ ++ if (devs) { ++ init_timer (&housekeeping); ++ housekeeping.function = do_housekeeping; ++ // paranoia ++ housekeeping.data = 1; ++ set_timer (&housekeeping, 0); ++ } else { ++ PRINTK (KERN_ERR, "no (usable) adapters found"); ++ } ++ ++ return devs; +} + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/eni.h Thu Apr 22 19:46:09 1999 -@@ -0,0 +1,115 @@ -+/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */ -+ -+/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ -+ -+ -+#ifndef DRIVER_ATM_ENI_H -+#define DRIVER_ATM_ENI_H ++++ work/drivers/atm/horizon.h Fri May 7 15:42:50 1999 +@@ -0,0 +1,506 @@ ++/* ++ Madge Horizon ATM Adapter driver. ++ Copyright (C) 1995-1999 Madge Networks Ltd. + -+#include -+#include -+#include -+#include -+#include -+#include ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + -+#include "midway.h" ++ The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian ++ system and in the file COPYING in the Linux kernel source. ++*/ + ++/* ++ IMPORTANT NOTE: Madge Networks no longer makes the adapters ++ supported by this driver and makes no commitment to maintain it. ++*/ + -+#define KERNEL_OFFSET 0xC0000000 /* kernel 0x0 is at phys 0xC0000000 */ -+#define DEV_LABEL "eni" ++/* too many macros - change to inline functions */ + -+#define UBR_BUFFER (128*1024) /* UBR buffer size */ ++#ifndef DRIVER_ATM_HORIZON_H ++#define DRIVER_ATM_HORIZON_H + -+#define RX_DMA_BUF 8 /* burst and skip a few things */ -+#define TX_DMA_BUF 100 /* should be enough for 64 kB */ ++#ifdef CONFIG_ATM_HORIZON_DEBUG ++#define DEBUG_HORIZON ++#endif + ++#define DEV_LABEL "hrz" ++ ++#ifndef PCI_VENDOR_ID_MADGE ++#define PCI_VENDOR_ID_MADGE 0x10B6 ++#endif ++#ifndef PCI_VENDOR_ID_MADGE_HORIZON ++#define PCI_DEVICE_ID_MADGE_HORIZON 0x1000 ++#endif ++ ++// diagnostic output ++ ++#define PRINTK(severity,format,args...) \ ++ printk(severity DEV_LABEL ": " format "\n" , ## args) ++ ++#ifdef DEBUG_HORIZON ++ ++#define DBG_ERR 0x0001 ++#define DBG_WARN 0x0002 ++#define DBG_INFO 0x0004 ++#define DBG_VCC 0x0008 ++#define DBG_QOS 0x0010 ++#define DBG_TX 0x0020 ++#define DBG_RX 0x0040 ++#define DBG_SKB 0x0080 ++#define DBG_IRQ 0x0100 ++#define DBG_FLOW 0x0200 ++#define DBG_BUS 0x0400 ++#define DBG_REGS 0x0800 ++#define DBG_DATA 0x1000 ++#define DBG_MASK 0x1fff ++ ++/* the ## prevents the annoying double expansion of the macro arguments */ ++/* KERN_INFO is used since KERN_DEBUG often does not make it to the console */ ++#define PRINTDB(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format , ## args) : 1 ) ++#define PRINTDM(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (format , ## args) : 1 ) ++#define PRINTDE(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (format "\n" , ## args) : 1 ) ++#define PRINTD(bits,format,args...) \ ++ ( (debug & (bits)) ? printk (KERN_INFO DEV_LABEL ": " format "\n" , ## args) : 1 ) + -+struct eni_free { -+ unsigned long start; /* counting in bytes */ -+ int order; -+}; ++#else + -+struct eni_tx { -+ u32 *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 reserved; /* reserved peak cell rate */ -+ int shaping; /* shaped peak cell rate */ -+ struct sk_buff_head backlog; /* queue of waiting TX buffers */ -+}; ++#define PRINTD(bits,format,args...) ++#define PRINTDB(bits,format,args...) ++#define PRINTDM(bits,format,args...) ++#define PRINTDE(bits,format,args...) ++ ++#endif ++ ++#define PRINTDD(sec,fmt,args...) ++#define PRINTDDB(sec,fmt,args...) ++#define PRINTDDM(sec,fmt,args...) ++#define PRINTDDE(sec,fmt,args...) ++ ++// fixed constants ++ ++#define SPARE_BUFFER_POOL_SIZE MAX_VCS ++#define HRZ_MAX_VPI 4 ++#define MIN_PCI_LATENCY 48 // 24 IS TOO SMALL ++ ++/* Horizon specific bits */ ++/* Register offsets */ ++ ++#define HRZ_IO_EXTENT 0x80 ++ ++#define DATA_PORT_OFF 0x00 ++#define TX_CHANNEL_PORT_OFF 0x04 ++#define TX_DESCRIPTOR_PORT_OFF 0x08 ++#define MEMORY_PORT_OFF 0x0C ++#define MEM_WR_ADDR_REG_OFF 0x14 ++#define MEM_RD_ADDR_REG_OFF 0x18 ++#define CONTROL_0_REG 0x1C ++#define INT_SOURCE_REG_OFF 0x20 ++#define INT_ENABLE_REG_OFF 0x24 ++#define MASTER_RX_ADDR_REG_OFF 0x28 ++#define MASTER_RX_COUNT_REG_OFF 0x2C ++#define MASTER_TX_ADDR_REG_OFF 0x30 ++#define MASTER_TX_COUNT_REG_OFF 0x34 ++#define TX_DESCRIPTOR_REG_OFF 0x38 ++#define TX_CHANNEL_CONFIG_COMMAND_OFF 0x40 ++#define TX_CHANNEL_CONFIG_DATA_OFF 0x44 ++#define TX_FREE_BUFFER_COUNT_OFF 0x48 ++#define RX_FREE_BUFFER_COUNT_OFF 0x4C ++#define TX_CONFIG_OFF 0x50 ++#define TX_STATUS_OFF 0x54 ++#define RX_CONFIG_OFF 0x58 ++#define RX_LINE_CONFIG_OFF 0x5C ++#define RX_QUEUE_RD_PTR_OFF 0x60 ++#define RX_QUEUE_WR_PTR_OFF 0x64 ++#define MAX_AAL5_CELL_COUNT_OFF 0x68 ++#define RX_CHANNEL_PORT_OFF 0x6C ++#define TX_CELL_COUNT_OFF 0x70 ++#define RX_CELL_COUNT_OFF 0x74 ++#define HEC_ERROR_COUNT_OFF 0x78 ++#define UNASSIGNED_CELL_COUNT_OFF 0x7C ++ ++/* Register bit definitions */ ++ ++/* Control 0 register */ ++ ++#define SEEPROM_DO 0x00000001 ++#define SEEPROM_DI 0x00000002 ++#define SEEPROM_SK 0x00000004 ++#define SEEPROM_CS 0x00000008 ++#define DEBUG_BIT_0 0x00000010 ++#define DEBUG_BIT_1 0x00000020 ++#define DEBUG_BIT_2 0x00000040 ++// RESERVED 0x00000080 ++#define DEBUG_BIT_0_OE 0x00000100 ++#define DEBUG_BIT_1_OE 0x00000200 ++#define DEBUG_BIT_2_OE 0x00000400 ++// RESERVED 0x00000800 ++#define DEBUG_BIT_0_STATE 0x00001000 ++#define DEBUG_BIT_1_STATE 0x00002000 ++#define DEBUG_BIT_2_STATE 0x00004000 ++// RESERVED 0x00008000 ++#define GENERAL_BIT_0 0x00010000 ++#define GENERAL_BIT_1 0x00020000 ++#define GENERAL_BIT_2 0x00040000 ++#define GENERAL_BIT_3 0x00080000 ++#define RESET_HORIZON 0x00100000 ++#define RESET_ATM 0x00200000 ++#define RESET_RX 0x00400000 ++#define RESET_TX 0x00800000 ++#define RESET_HOST 0x01000000 ++// RESERVED 0x02000000 ++#define TARGET_RETRY_DISABLE 0x04000000 ++#define ATM_LAYER_SELECT 0x08000000 ++#define ATM_LAYER_STATUS 0x10000000 ++// RESERVED 0xE0000000 ++ ++/* Interrupt source and enable registers */ ++ ++#define RX_DATA_AV 0x00000001 ++#define RX_DISABLED 0x00000002 ++#define TIMING_MARKER 0x00000004 ++#define FORCED 0x00000008 ++#define RX_BUS_MASTER_COMPLETE 0x00000010 ++#define TX_BUS_MASTER_COMPLETE 0x00000020 ++#define ABR_TX_CELL_COUNT_INT 0x00000040 ++#define DEBUG_INT 0x00000080 ++// RESERVED 0xFFFFFF00 ++ ++/* PIO and Bus Mastering */ ++ ++#define MAX_PIO_COUNT 0x000000ff // 255 - make tunable? ++// 8188 is a hard limit for bus mastering ++#define MAX_TRANSFER_COUNT 0x00001ffc // 8188 ++#define MASTER_TX_AUTO_APPEND_DESC 0x80000000 ++ ++/* TX channel config command port */ ++ ++#define PCR_TIMER_ACCESS 0x0000 ++#define SCR_TIMER_ACCESS 0x0001 ++#define BUCKET_CAPACITY_ACCESS 0x0002 ++#define BUCKET_FULLNESS_ACCESS 0x0003 ++#define RATE_TYPE_ACCESS 0x0004 ++// UNUSED 0x00F8 ++#define TX_CHANNEL_CONFIG_MULT 0x0100 ++// UNUSED 0xF800 ++#define BUCKET_MAX_SIZE 0x003f ++ ++/* TX channel config data port */ ++ ++#define CLOCK_SELECT_SHIFT 4 ++#define CLOCK_DISABLE 0x00ff ++ ++#define IDLE_RATE_TYPE 0x0 ++#define ABR_RATE_TYPE 0x1 ++#define VBR_RATE_TYPE 0x2 ++#define CBR_RATE_TYPE 0x3 ++ ++/* TX config register */ ++ ++#define DRVR_DRVRBAR_ENABLE 0x0001 ++#define TXCLK_MUX_SELECT_RCLK 0x0002 ++#define TRANSMIT_TIMING_MARKER 0x0004 ++#define LOOPBACK_TIMING_MARKER 0x0008 ++#define TX_TEST_MODE_16MHz 0x0000 ++#define TX_TEST_MODE_8MHz 0x0010 ++#define TX_TEST_MODE_5_33MHz 0x0020 ++#define TX_TEST_MODE_4MHz 0x0030 ++#define TX_TEST_MODE_3_2MHz 0x0040 ++#define TX_TEST_MODE_2_66MHz 0x0050 ++#define TX_TEST_MODE_2_29MHz 0x0060 ++#define TX_NORMAL_OPERATION 0x0070 ++#define ABR_ROUND_ROBIN 0x0080 ++ ++/* TX status register */ ++ ++#define IDLE_CHANNELS_MASK 0x00FF ++#define ABR_CELL_COUNT_REACHED_MULT 0x0100 ++#define ABR_CELL_COUNT_REACHED_MASK 0xFF ++ ++/* RX config register */ ++ ++#define NON_USER_CELLS_IN_ONE_CHANNEL 0x0008 ++#define RX_ENABLE 0x0010 ++#define IGNORE_UNUSED_VPI_VCI_BITS_SET 0x0000 ++#define NON_USER_UNUSED_VPI_VCI_BITS_SET 0x0020 ++#define DISCARD_UNUSED_VPI_VCI_BITS_SET 0x0040 ++ ++/* RX line config register */ ++ ++#define SIGNAL_LOSS 0x0001 ++#define FREQUENCY_DETECT_ERROR 0x0002 ++#define LOCK_DETECT_ERROR 0x0004 ++#define SELECT_INTERNAL_LOOPBACK 0x0008 ++#define LOCK_DETECT_ENABLE 0x0010 ++#define FREQUENCY_DETECT_ENABLE 0x0020 ++#define USER_FRAQ 0x0040 ++#define GXTALOUT_SELECT_DIV4 0x0080 ++#define GXTALOUT_SELECT_NO_GATING 0x0100 ++#define TIMING_MARKER_RECEIVED 0x0200 ++ ++/* RX channel port */ ++ ++#define RX_CHANNEL_MASK 0x03FF ++// UNUSED 0x3C00 ++#define FLUSH_CHANNEL 0x4000 ++#define RX_CHANNEL_UPDATE_IN_PROGRESS 0x8000 ++ ++/* Receive queue entry */ ++ ++#define RX_Q_ENTRY_LENGTH_MASK 0x0000FFFF ++#define RX_Q_ENTRY_CHANNEL_SHIFT 16 ++#define SIMONS_DODGEY_MARKER 0x08000000 ++#define RX_CONGESTION_EXPERIENCED 0x10000000 ++#define RX_CRC_10_OK 0x20000000 ++#define RX_CRC_32_OK 0x40000000 ++#define RX_COMPLETE_FRAME 0x80000000 ++ ++/* Offsets and constants for use with the buffer memory */ ++ ++/* Buffer pointers and channel types */ ++ ++#define BUFFER_PTR_MASK 0x0000FFFF ++#define RX_INT_THRESHOLD_MULT 0x00010000 ++#define RX_INT_THRESHOLD_MASK 0x07FF ++#define INT_EVERY_N_CELLS 0x08000000 ++#define CONGESTION_EXPERIENCED 0x10000000 ++#define FIRST_CELL_OF_AAL5_FRAME 0x20000000 ++#define CHANNEL_TYPE_AAL5 0x00000000 ++#define CHANNEL_TYPE_RAW_CELLS 0x40000000 ++#define CHANNEL_TYPE_AAL3_4 0x80000000 ++ ++/* Buffer status stuff */ ++ ++#define BUFF_STATUS_MASK 0x00030000 ++#define BUFF_STATUS_EMPTY 0x00000000 ++#define BUFF_STATUS_CELL_AV 0x00010000 ++#define BUFF_STATUS_LAST_CELL_AV 0x00020000 ++ ++/* Transmit channel stuff */ ++ ++/* Receive channel stuff */ ++ ++#define RX_CHANNEL_DISABLED 0x00000000 ++#define RX_CHANNEL_IDLE 0x00000001 ++ ++/* General things */ ++ ++#define INITIAL_CRC 0xFFFFFFFF ++ ++// A Horizon u32, a byte! Really nasty. Horizon pointers are (32 bit) ++// word addresses and so standard C pointer operations break (as they ++// assume byte addresses); so we pretend that Horizon words (and word ++// pointers) are bytes (and byte pointers) for the purposes of having ++// a memory map that works. ++ ++typedef u8 HDW; ++ ++typedef struct cell_buf { ++ HDW payload[12]; ++ HDW next; ++ HDW cell_count; // AAL5 rx bufs ++ HDW res; ++ union { ++ HDW partial_crc; // AAL5 rx bufs ++ HDW cell_header; // RAW bufs ++ } u; ++} cell_buf; ++ ++typedef struct tx_ch_desc { ++ HDW rd_buf_type; ++ HDW wr_buf_type; ++ HDW partial_crc; ++ HDW cell_header; ++} tx_ch_desc; ++ ++typedef struct rx_ch_desc { ++ HDW wr_buf_type; ++ HDW rd_buf_type; ++} rx_ch_desc; ++ ++typedef struct rx_q_entry { ++ HDW entry; ++} rx_q_entry; ++ ++#define TX_CHANS 8 ++#define RX_CHANS 1024 ++#define RX_QS 1024 ++#define MAX_VCS RX_CHANS ++ ++/* Horizon buffer memory map */ ++ ++// TX Channel Descriptors 2 ++// TX Initial Buffers 8 // TX_CHANS ++#define BUFN1_SIZE 118 // (126 - TX_CHANS) ++// RX/TX Start/End Buffers 4 ++#define BUFN2_SIZE 124 ++// RX Queue Entries 64 ++#define BUFN3_SIZE 192 ++// RX Channel Descriptors 128 ++#define BUFN4_SIZE 1408 ++// TOTAL cell_buff chunks 2048 ++ ++// cell_buf bufs[2048]; ++// HDW dws[32768]; ++ ++typedef struct MEMMAP { ++ tx_ch_desc tx_descs[TX_CHANS]; // 8 * 4 = 32 , 0x0020 ++ cell_buf inittxbufs[TX_CHANS]; // these are really ++ cell_buf bufn1[BUFN1_SIZE]; // part of this pool ++ cell_buf txfreebufstart; ++ cell_buf txfreebufend; ++ cell_buf rxfreebufstart; ++ cell_buf rxfreebufend; // 8+118+1+1+1+1+124 = 254 ++ cell_buf bufn2[BUFN2_SIZE]; // 16 * 254 = 4064 , 0x1000 ++ rx_q_entry rx_q_entries[RX_QS]; // 1 * 1024 = 1024 , 0x1400 ++ cell_buf bufn3[BUFN3_SIZE]; // 16 * 192 = 3072 , 0x2000 ++ rx_ch_desc rx_descs[MAX_VCS]; // 2 * 1024 = 2048 , 0x2800 ++ cell_buf bufn4[BUFN4_SIZE]; // 16 * 1408 = 22528 , 0x8000 ++} MEMMAP; ++ ++#define memmap ((MEMMAP *)0) ++ ++#define BUF_PTR(cbptr) ((cbptr) - (cell_buf *) 0) ++ ++/* end horizon specific bits */ ++ ++typedef enum { ++ aal0, ++ aal34, ++ aal5 ++} hrz_aal; ++ ++typedef enum { ++ tx_busy, ++ rx_busy, ++ ultra ++} hrz_flags; ++ ++// a single struct pointed to by atm_vcc->dev_data ++ ++typedef struct { ++ unsigned int tx_rate; ++ unsigned int rx_rate; ++ u16 channel; ++ u16 tx_xbr_bits; ++ u16 tx_pcr_bits; ++#if 0 ++ u16 tx_scr_bits; ++ u16 tx_bucket_bits; ++#endif ++ hrz_aal aal; ++} hrz_vcc; + -+struct eni_vcc { -+ int (*rx)(struct atm_vcc *vcc); /* RX function, NULL if none */ -+ u32 *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 hrz_dev { ++ ++ u32 iobase; ++ u32 * membase; + -+struct eni_dev { -+ /*-------------------------------- base pointers into Midway address -+ space */ -+ u32 *phy; /* PHY interface chip registers */ -+ u32 *reg; /* register base */ -+ u32 *ram; /* RAM base */ -+ u32 *vci; /* VCI table */ -+ u32 *rx_dma; /* RX DMA queue */ -+ u32 *tx_dma; /* TX DMA queue */ -+ u32 *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 */ -+ u32 dma[TX_DMA_BUF*2]; /* DMA request scratch area */ -+ /*-------------------------------- RX part */ -+ u32 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 */ -+ struct pci_dev *pci_dev; /* PCI stuff */ -+}; ++ struct sk_buff * rx_skb; // skb being RXed ++ unsigned int rx_bytes; // bytes remaining to RX within region ++ void * rx_addr; // addr to send bytes to (for PIO) ++ unsigned int rx_channel; // channel that the skb is going out on ++ ++ struct sk_buff * tx_skb; // skb being TXed ++ unsigned int tx_bytes; // bytes remaining to TX within region ++ void * tx_addr; // addr to send bytes from (for PIO) ++ struct iovec * tx_iovec; // remaining regions ++ unsigned int tx_regions; // number of remaining regions ++ ++ spinlock_t mem_lock; ++ struct wait_queue * tx_queue; ++ ++ u8 irq; ++ u8 flags; ++ u8 tx_last; ++ u8 tx_idle; ++ ++ rx_q_entry * rx_q_reset; ++ rx_q_entry * rx_q_entry; ++ rx_q_entry * rx_q_wrap; + ++ struct atm_dev * atm_dev; + -+#define ENI_DEV(d) ((struct eni_dev *) (d)->dev_data) -+#define ENI_VCC(d) ((struct eni_vcc *) (d)->dev_data) ++ u32 last_vc; ++ ++ int noof_spare_buffers; ++ u16 spare_buffers[SPARE_BUFFER_POOL_SIZE]; + ++ u16 tx_channel_record[TX_CHANS]; + -+struct eni_skb_prv { -+ struct atm_skb_data _; /* reserved */ -+ unsigned long pos; /* position of next descriptor */ -+ int size; /* PDU size in reassembly buffer */ ++ // this is what we follow when we get incoming data ++ u32 txer[MAX_VCS/32]; ++ struct atm_vcc * rxer[MAX_VCS]; ++ ++ // cell rate allocation ++ spinlock_t rate_lock; ++ unsigned int rx_avail; ++ unsigned int tx_avail; ++ ++ // dev stats ++ unsigned long tx_cell_count; ++ unsigned long rx_cell_count; ++ unsigned long hec_error_count; ++ unsigned long unassigned_cell_count; ++ ++ struct pci_dev * pci_dev; ++ struct hrz_dev * prev; +}; + -+#define ENI_PRV_SIZE(skb) (((struct eni_skb_prv *) (skb)->cb)->size) -+#define ENI_PRV_POS(skb) (((struct eni_skb_prv *) (skb)->cb)->pos) ++typedef struct hrz_dev hrz_dev; + -+#endif ++/* macros for use later */ ++ ++#define INTERESTING_INTERRUPTS \ ++ (RX_DATA_AV | RX_DISABLED | TX_BUS_MASTER_COMPLETE | RX_BUS_MASTER_COMPLETE) ++ ++// 190 cells by default (192 TX buffers - 2 elbow room, see docs) ++#define TX_AAL5_LIMIT (190*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER) // 9112 ++ ++// Have enough RX buffers (unless we allow other buffer splits) ++#define RX_AAL5_LIMIT ATM_MAX_AAL5_PDU ++ ++/* multi-statement macro protector */ ++#define DW(x) do{ x } while(0) ++ ++#define HRZ_DEV(atm_dev) ((hrz_dev *) (atm_dev)->dev_data) ++#define HRZ_VCC(atm_vcc) ((hrz_vcc *) (atm_vcc)->dev_data) ++ ++/* Turn the LEDs on and off */ ++// The LEDs bits are upside down in that setting the bit in the debug ++// register will turn the appropriate LED off. ++ ++#define YELLOW_LED DEBUG_BIT_0 ++#define GREEN_LED DEBUG_BIT_1 ++#define YELLOW_LED_OE DEBUG_BIT_0_OE ++#define GREEN_LED_OE DEBUG_BIT_1_OE ++ ++#define GREEN_LED_OFF(dev) \ ++ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) | GREEN_LED) ++#define GREEN_LED_ON(dev) \ ++ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) &~ GREEN_LED) ++#define YELLOW_LED_OFF(dev) \ ++ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) | YELLOW_LED) ++#define YELLOW_LED_ON(dev) \ ++ wr_regl (dev, CONTROL_0_REG, rd_regl (dev, CONTROL_0_REG) &~ YELLOW_LED) ++ ++typedef enum { ++ round_up, ++ round_down, ++ round_nearest ++} rounding; ++ ++#endif /* DRIVER_ATM_HORIZON_H */ --- /dev/null Tue Jan 1 05:00:00 1980 +++ work/drivers/atm/midway.h Tue Feb 9 15:49:57 1999 @@ -0,0 +1,265 @@ @@ -4143,8 +13213,8 @@ + +#endif /* _LINUX_NICSTAR_H_ */ --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/nicstar.c Wed Apr 21 18:37:17 1999 -@@ -0,0 +1,2949 @@ ++++ work/drivers/atm/nicstar.c Tue May 18 01:02:06 1999 +@@ -0,0 +1,2999 @@ +/****************************************************************************** + * + * nicstar.c @@ -4187,6 +13257,7 @@ +#include +#include +#include ++#include +#include +#include +#include "nicstar.h" @@ -5957,9 +15028,8 @@ + scq = card->scq0; + } + -+ if (push_scqe(card, vc, scq, &scqe, skb) != 0) /* Timeout pushing the TBD */ ++ if (push_scqe(card, vc, scq, &scqe, skb) != 0) + { -+ printk("nicstar%d: Timeout pushing TBD.\n", card->index); + vcc->stats->tx_err++; + dev_kfree_skb(skb); + return -EIO; @@ -5983,13 +15053,20 @@ + + if (scq->tail == scq->next) + { ++ if (in_interrupt()) { ++ printk("nicstar%d: Error pushing TBD.\n", card->index); ++ return 1; ++ } ++ + save_flags(flags); cli(); + scq->full = 1; + interruptible_sleep_on_timeout(&scq->scqfull_waitq, SCQFULL_TIMEOUT); + restore_flags(flags); + -+ if (scq->full) ++ if (scq->full) { ++ printk("nicstar%d: Timeout pushing TBD.\n", card->index); + return 1; ++ } + } + *scq->next = *tbd; + index = (int) (scq->next - scq->base); @@ -6017,6 +15094,13 @@ + { + if (scq->tail == scq->next) + { ++ if (in_interrupt()) { ++ data = (u32) virt_to_bus(scq->next); ++ ns_write_sram(card, scq->scd, &data, 1); ++ printk("nicstar%d: Error pushing TSR.\n", card->index); ++ return 0; ++ } ++ + save_flags(flags); cli(); + scq->full = 1; + interruptible_sleep_on_timeout(&scq->scqfull_waitq, SCQFULL_TIMEOUT); @@ -6049,7 +15133,7 @@ + scq->tbd_count = 0; + } + else -+ PRINTK("nicstar%d: Could not write TSI.\n", card->index); ++ PRINTK("nicstar%d: Timeout pushing TSR.\n", card->index); + } + + data = (u32) virt_to_bus(scq->next); @@ -6064,12 +15148,35 @@ +{ + u32 scdi; + scq_info *scq; -+ ns_tsi *previous; ++ ns_tsi *previous, *one_ahead, *two_ahead; ++ int serviced_entries; /* flag indicating at least on entry was serviced */ + -+ if (ns_tsi_isempty(card->tsq.next)) -+ return; -+ while (!ns_tsi_isempty(card->tsq.next)) ++ serviced_entries = 0; ++ ++ if (card->tsq.next == card->tsq.last) ++ one_ahead = card->tsq.base; ++ else ++ one_ahead = card->tsq.next + 1; ++ ++ if (one_ahead == card->tsq.last) ++ two_ahead = card->tsq.base; ++ else ++ two_ahead = one_ahead + 1; ++ ++ while (!ns_tsi_isempty(card->tsq.next) || !ns_tsi_isempty(one_ahead) || ++ !ns_tsi_isempty(two_ahead)) ++ /* At most two empty, as stated in the 77201 errata */ + { ++ serviced_entries = 1; ++ ++ /* Skip the one or two possible empty entries */ ++ while (ns_tsi_isempty(card->tsq.next)) { ++ if (card->tsq.next == card->tsq.last) ++ card->tsq.next = card->tsq.base; ++ else ++ card->tsq.next++; ++ } ++ + if (!ns_tsi_tmrof(card->tsq.next)) + { + scdi = ns_tsi_getscdindex(card->tsq.next); @@ -6097,9 +15204,22 @@ + card->tsq.next = card->tsq.base; + else + card->tsq.next++; ++ ++ if (card->tsq.next == card->tsq.last) ++ one_ahead = card->tsq.base; ++ else ++ one_ahead = card->tsq.next + 1; ++ ++ if (one_ahead == card->tsq.last) ++ two_ahead = card->tsq.base; ++ else ++ two_ahead = one_ahead + 1; ++ } ++ ++ if (serviced_entries) { ++ writel((((u32) previous) - ((u32) card->tsq.base)), ++ card->membase + TSQH); + } -+ writel((((u32) previous) - ((u32) card->tsq.base)), -+ card->membase + TSQH); +} + + @@ -14662,7 +23782,7 @@ +}; +#endif /* _ATMLEC_H_ */ --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmmpc.h Tue Feb 9 18:09:24 1999 ++++ work/include/linux/atmmpc.h Tue May 18 00:41:41 1999 @@ -0,0 +1,124 @@ +#ifndef _ATMMPC_H_ +#define _ATMMPC_H_ @@ -14722,7 +23842,7 @@ + struct mpc_parameters params; + } content; + struct atm_qos qos; -+} k_message; ++}; + +struct llc_snap_hdr { /* RFC 1483 LLC/SNAP encapsulation for routed IP PDUs */ + uint8_t dsap; /* Destination Service Access Point (0xAA) */ @@ -16033,8 +25153,8 @@ +} +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/Makefile Thu Apr 22 19:19:28 1999 -@@ -0,0 +1,64 @@ ++++ work/net/atm/Makefile Thu May 6 10:50:28 1999 +@@ -0,0 +1,68 @@ +# +# Makefile for the ATM Protocol Families. +# @@ -16069,6 +25189,10 @@ +NEED_IPCOM = ipcommon.o +endif + ++ifeq ($(CONFIG_NET_SCH_ATM),y) ++NEED_IPCOM = ipcommon.o ++endif ++ +O_OBJS += $(NEED_IPCOM) + +ifeq ($(CONFIG_PROC_FS),y) @@ -18165,8 +27289,8 @@ +EXPORT_SYMBOL(atm_find_ci); +EXPORT_SYMBOL(atm_pcr_goal); --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/lec.c Thu Feb 18 16:36:00 1999 -@@ -0,0 +1,2080 @@ ++++ work/net/atm/lec.c Tue May 18 00:41:41 1999 +@@ -0,0 +1,2092 @@ +/* + * lec.c: Lan Emulation driver + * Marko Kiiskila carnil@cs.tut.fi @@ -18282,7 +27406,10 @@ + mesg->content.normal.flag = *(skb->nh.raw + BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) & TOPOLOGY_CHANGE; + + priv = (struct lec_priv *)dev->priv; -+ if (atm_charge(priv->lecd, skb2->truesize) == 0) return; ++ if (atm_charge(priv->lecd, skb2->truesize) == 0) { ++ kfree_skb(skb2); ++ return; ++ } + skb_queue_tail(&priv->lecd->recvq, skb2); + wake_up(&priv->lecd->sleep); + } @@ -18596,7 +27723,10 @@ + if (skb2 == NULL) break; + skb2->len = sizeof(struct atmlec_msg); + memcpy(skb2->data, mesg, sizeof(struct atmlec_msg)); -+ if (atm_charge(priv->lecd, skb2->truesize) == 0) break; ++ if (atm_charge(priv->lecd, skb2->truesize) == 0) { ++ kfree_skb(skb2); ++ break; ++ } + skb_queue_tail(&priv->lecd->recvq, skb2); + wake_up(&priv->lecd->sleep); + } @@ -18703,15 +27833,21 @@ + if (atm_addr) + memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN); + -+ if (atm_charge(priv->lecd, skb->truesize) == 0) ++ if (atm_charge(priv->lecd, skb->truesize) == 0) { + DPRINTK("lec: send_to_lecd, atm_charge()\n"); ++ kfree_skb(skb); ++ return -1; ++ } + skb_queue_tail(&priv->lecd->recvq, skb); + wake_up(&priv->lecd->sleep); + + if (data != NULL) { + DPRINTK("lec: about to send %d bytes of data\n", data->len); -+ if (atm_charge(priv->lecd, data->truesize) == 0) -+ printk("lec: send_to_lecd, atm_charge()\n"); ++ if (atm_charge(priv->lecd, data->truesize) == 0) { ++ printk("lec: send_to_lecd, atm_charge\n"); ++ kfree_skb(data); ++ return -1; ++ } + skb_queue_tail(&priv->lecd->recvq, data); + wake_up(&priv->lecd->sleep); + } @@ -20517,8 +29653,8 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpc.c Tue Feb 9 17:15:25 1999 -@@ -0,0 +1,1515 @@ ++++ work/net/atm/mpc.c Tue May 18 00:41:41 1999 +@@ -0,0 +1,1480 @@ +#include +#include +#include @@ -20555,7 +29691,7 @@ + * mpc.c: Implementation of MPOA client kernel part + */ + -+#if 1 ++#if 0 +#define dprintk printk /* debug */ +#else +#define dprintk(format,args...) @@ -20817,7 +29953,7 @@ +static void start_mpc(struct mpoa_client *mpc, struct device *dev) +{ + -+ printk("mpoa: (%s) start_mpc:\n", mpc->dev->name); ++ dprintk("mpoa: (%s) start_mpc:\n", mpc->dev->name); + if (dev->hard_start_xmit == NULL) { + printk("mpoa: (%s) start_mpc: dev->hard_start_xmit == NULL, not starting\n", + dev->name); @@ -20832,14 +29968,14 @@ +static void stop_mpc(struct mpoa_client *mpc) +{ + -+ printk("mpoa: (%s) stop_mpc:", mpc->dev->name); ++ dprintk("mpoa: (%s) stop_mpc:", mpc->dev->name); + + /* Lets not nullify lec device's dev->hard_start_xmit */ + if (mpc->dev->hard_start_xmit != mpc_send_packet) { -+ printk(" mpc already stopped, not fatal\n"); ++ dprintk(" mpc already stopped, not fatal\n"); + return; + } -+ printk("\n"); ++ dprintk("\n"); + mpc->dev->hard_start_xmit = mpc->old_hard_start_xmit; + mpc->old_hard_start_xmit = NULL; + /* close_shortcuts(mpc); ??? FIXME */ @@ -20921,7 +30057,7 @@ + } + mpoa_device_type = *tlvs++; + number_of_mps_macs = *tlvs++; -+ printk("mpoa: (%s) MPOA device type '%s', ", dev->name, mpoa_device_type_string(mpoa_device_type)); ++ dprintk("mpoa: (%s) MPOA device type '%s', ", dev->name, mpoa_device_type_string(mpoa_device_type)); + if (mpoa_device_type == MPS_AND_MPC && + length < (42 + number_of_mps_macs*ETH_ALEN)) { /* :) */ + printk("\nmpoa: (%s) lane2_assoc_ind: short MPOA Device Type TLV\n", @@ -20935,7 +30071,7 @@ + continue; + } + if (mpoa_device_type != MPS && mpoa_device_type != MPS_AND_MPC) { -+ printk("ignoring non-MPS device\n"); ++ dprintk("ignoring non-MPS device\n"); + if (mpoa_device_type == MPC) tlvs += 20; + continue; /* we are only interested in MPSs */ + } @@ -20943,7 +30079,7 @@ + printk("\nmpoa: (%s) lane2_assoc_ind: MPS_AND_MPC has zero MACs\n", dev->name); + continue; /* someone should read the spec */ + } -+ printk("this MPS has %d MAC addresses\n", number_of_mps_macs); ++ dprintk("this MPS has %d MAC addresses\n", number_of_mps_macs); + + /* ok, now we can go and tell our daemon the control address of MPS */ + send_set_mps_ctrl_addr(tlvs, mpc); @@ -21015,11 +30151,11 @@ + return 1; + } + if (mpc->in_ops->cache_hit(entry, mpc) != OPEN){ /* threshold not exceeded or VCC not ready */ -+ ddprintk("mpoa: (%s) send_via_shortcut() cache_hit() returns != OPEN\n", mpc->dev->name); ++ ddprintk("mpoa: (%s) send_via_shortcut: cache_hit: returns != OPEN\n", mpc->dev->name); + return 1; + } + -+ ddprintk("mpoa: (%s) send_via_shortcut() using shortcut\n", mpc->dev->name); ++ ddprintk("mpoa: (%s) send_via_shortcut: using shortcut\n", mpc->dev->name); + /* MPOA spec A.1.4, MPOA client must decrement IP ttl at least by one */ + if (iph->ttl <= 1) { + ddprintk("mpoa: (%s) send_via_shortcut: IP ttl = %u, using LANE\n", mpc->dev->name, iph->ttl); @@ -21030,7 +30166,7 @@ + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + + if (entry->ctrl_info.tag != 0) { -+ ddprintk("mpoa: (%s) send_via_shortcut() adding tag 0x%x\n", mpc->dev->name, entry->ctrl_info.tag); ++ ddprintk("mpoa: (%s) send_via_shortcut: adding tag 0x%x\n", mpc->dev->name, entry->ctrl_info.tag); + tagged_llc_snap_hdr.tag = entry->ctrl_info.tag; + skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ + skb_push(skb, sizeof(tagged_llc_snap_hdr)); /* add LLC/SNAP header */ @@ -21113,11 +30249,11 @@ + return -EINVAL; + } + ip = (unsigned char*)&in_entry->ctrl_info.in_dst_ip; -+ printk("mpoa: (%s) mpc_vcc_attach() attaching ingress SVC, entry = %u.%u.%u.%u\n", ++ printk("mpoa: (%s) mpc_vcc_attach: attaching ingress SVC, entry = %u.%u.%u.%u\n", + mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + in_entry->shortcut = vcc; + } else { -+ printk("mpoa: (%s) mpc_vcc_attach() attaching egress SVC\n", mpc->dev->name); ++ printk("mpoa: (%s) mpc_vcc_attach: attaching egress SVC\n", mpc->dev->name); + } + + vcc->proto_data = mpc->dev; @@ -21141,17 +30277,17 @@ + return; + } + -+ printk("mpoa: (%s) mpc_vcc_close:\n", dev->name); ++ dprintk("mpoa: (%s) mpc_vcc_close:\n", dev->name); + in_entry = mpc->in_ops->search_by_vcc(vcc, mpc); + if (in_entry) { + unsigned char *ip = (unsigned char *)&in_entry->ctrl_info.in_dst_ip; -+ dprintk("mpoa: (%s) mpoa_vcc_close, ingress SVC closed ip = %u.%u.%u.%u\n", ++ dprintk("mpoa: (%s) mpc_vcc_close: ingress SVC closed ip = %u.%u.%u.%u\n", + mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + in_entry->shortcut = NULL; + } + eg_entry = mpc->eg_ops->search_by_vcc(vcc, mpc); + if (eg_entry) { -+ dprintk("mpoa: (%s) mpoa_vcc_close, egress SVC closed\n", mpc->dev->name); ++ dprintk("mpoa: (%s) mpc_vcc_close: egress SVC closed\n", mpc->dev->name); + eg_entry->shortcut = NULL; + } + @@ -21170,16 +30306,16 @@ + uint32_t tag; + char *tmp; + -+ ddprintk("mpoa: (%s) mpc_push()\n", dev->name); ++ ddprintk("mpoa: (%s) mpc_push:\n", dev->name); + if (skb == NULL) { -+ printk("mpoa: (%s) mpc_push() null skb, closing VCC\n", dev->name); ++ dprintk("mpoa: (%s) mpc_push: null skb, closing VCC\n", dev->name); + mpc_vcc_close(vcc, dev); + return; + } + + skb->dev = dev; + if (memcmp(skb->data, &llc_snap_mpoa_ctrl, sizeof(struct llc_snap_hdr)) == 0) { -+ printk("mpoa: (%s) mpc_push() control packet arrived\n", dev->name); ++ dprintk("mpoa: (%s) mpc_push: control packet arrived\n", dev->name); + skb_queue_tail(&vcc->recvq, skb); /* Pass control packets to daemon */ + wake_up(&vcc->sleep); + return; @@ -21195,15 +30331,15 @@ + } + + if (memcmp(skb->data, &llc_snap_mpoa_data_tagged, sizeof(struct llc_snap_hdr)) == 0) { /* MPOA tagged data */ -+ ddprintk("mpoa: (%s) mpc_push() tagged data packet arrived\n", dev->name); ++ ddprintk("mpoa: (%s) mpc_push: tagged data packet arrived\n", dev->name); + + } else if (memcmp(skb->data, &llc_snap_mpoa_data, sizeof(struct llc_snap_hdr)) == 0) { /* MPOA data */ -+ printk("mpoa: (%s) mpc_push() non-tagged data packet arrived\n", dev->name); -+ printk(" mpc_push() non-tagged data unsupported, purging\n"); ++ printk("mpoa: (%s) mpc_push: non-tagged data packet arrived\n", dev->name); ++ printk(" mpc_push: non-tagged data unsupported, purging\n"); + kfree_skb(skb); + return; + } else { -+ printk("mpoa:(%s) mpc_push() garbage arrived, purging\n", dev->name); ++ printk("mpoa: (%s) mpc_push: garbage arrived, purging\n", dev->name); + kfree_skb(skb); + return; + } @@ -21294,7 +30430,7 @@ + + mpc = find_mpc_by_itfnum(arg); + if (mpc == NULL) { -+ printk("mpoa: mpoad_attach: allocating new mpc for itf %d\n", arg); ++ dprintk("mpoa: mpoad_attach: allocating new mpc for itf %d\n", arg); + mpc = alloc_mpc(); + mpc->dev_num = arg; + mpc->dev = find_lec_by_itfnum(arg); /* NULL if there was no lec */ @@ -21334,20 +30470,13 @@ + +static void send_set_mps_ctrl_addr(char *addr, struct mpoa_client *mpc) +{ -+ struct sk_buff *skb; -+ struct k_message *mesg; ++ struct k_message mesg; + -+ skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); -+ if (skb == NULL) { -+ printk("mpoa: (%s) lane2_assoc_ind: alloc_skb()\n", mpc->dev->name); -+ return; -+ } + memcpy (mpc->mps_ctrl_addr, addr, ATM_ESA_LEN); + -+ mesg = (struct k_message *)skb->data; -+ mesg->type = SET_MPS_CTRL_ADDR; -+ memcpy(mesg->MPS_ctrl, addr, ATM_ESA_LEN); -+ msg_to_mpoad(mesg, mpc); ++ mesg.type = SET_MPS_CTRL_ADDR; ++ memcpy(mesg.MPS_ctrl, addr, ATM_ESA_LEN); ++ msg_to_mpoad(&mesg, mpc); + + return; +} @@ -21410,54 +30539,54 @@ + printk("mpoa: msg_from_mpoad: no mpc found\n"); + return 0; + } -+ printk("mpoa: (%s) msg_from_mpoad:", (mpc->dev) ? mpc->dev->name : ""); ++ dprintk("mpoa: (%s) msg_from_mpoad:", (mpc->dev) ? mpc->dev->name : ""); + switch(mesg->type) { + case MPOA_RES_REPLY_RCVD: -+ printk(" mpoa_res_reply_rcvd.\n"); ++ dprintk(" mpoa_res_reply_rcvd\n"); + MPOA_res_reply_rcvd(mesg, mpc); + break; + case MPOA_TRIGGER_RCVD: -+ printk(" mpoa_trigger_rcvd.\n"); ++ dprintk(" mpoa_trigger_rcvd\n"); + MPOA_trigger_rcvd(mesg, mpc); + break; + case INGRESS_PURGE_RCVD: -+ printk(" nhrp_purge_rcvd.\n"); ++ dprintk(" nhrp_purge_rcvd\n"); + ingress_purge_rcvd(mesg, mpc); + break; + case EGRESS_PURGE_RCVD: -+ printk(" egress_purge_reply_rcvd.\n"); ++ dprintk(" egress_purge_reply_rcvd\n"); + egress_purge_rcvd(mesg, mpc); + break; + case MPS_DEATH: -+ printk(" mps_death.\n"); ++ dprintk(" mps_death\n"); + mps_death(mesg, mpc); + break; + case CACHE_IMPOS_RCVD: -+ printk(" cache_impos_rcvd.\n"); ++ dprintk(" cache_impos_rcvd\n"); + MPOA_cache_impos_rcvd(mesg, mpc); + break; + case SET_MPC_CTRL_ADDR: -+ printk(" set_mpc_ctrl_addr\n"); ++ dprintk(" set_mpc_ctrl_addr\n"); + set_mpc_ctrl_addr_rcvd(mesg, mpc); + break; + case SET_MPS_MAC_ADDR: -+ printk(" set_mps_mac_addr\n"); ++ dprintk(" set_mps_mac_addr\n"); + set_mps_mac_addr_rcvd(mesg, mpc); + break; + case CLEAN_UP_AND_EXIT: -+ printk(" clean_up_and_exit\n"); -+ clean_up(mesg, mpc, DIE); -+ break; ++ dprintk(" clean_up_and_exit\n"); ++ clean_up(mesg, mpc, DIE); ++ break; + case RELOAD: -+ printk(" reload\n"); ++ dprintk(" reload\n"); + clean_up(mesg, mpc, RELOAD); + break; + case SET_MPC_PARAMS: -+ printk(" set_mpc_params\n"); ++ dprintk(" set_mpc_params\n"); + mpc->parameters = mesg->content.params; + break; + default: -+ printk(" unknown message %d\n", mesg->type); ++ dprintk(" unknown message %d\n", mesg->type); + break; + } + kfree_skb(skb); @@ -21470,7 +30599,7 @@ + struct sk_buff *skb; + + if (mpc == NULL || !mpc->mpoad_vcc) { -+ printk("mpoa: msg_to_mpoad: mesg %d to a non-existant mpoad\n", mesg->type); ++ printk("mpoa: msg_to_mpoad: mesg %d to a non-existent mpoad\n", mesg->type); + return -ENXIO; + } + @@ -21481,6 +30610,7 @@ + memcpy(skb->data, mesg, sizeof(struct k_message)); + if (atm_charge(mpc->mpoad_vcc, skb->truesize) == 0) { + printk("mpoa: msg_to_mpoad: atm_charge failed\n"); ++ kfree_skb(skb); + return -ENOMEM; + } + @@ -21508,7 +30638,7 @@ + priv->lane2_ops->associate_indicator = lane2_assoc_ind; + mpc = find_mpc_by_itfnum(priv->itfnum); + if (mpc == NULL) { -+ printk("mpoa: mpoa_event_listener: allocating new mpc for %s\n", ++ dprintk("mpoa: mpoa_event_listener: allocating new mpc for %s\n", + dev->name); + mpc = alloc_mpc(); + if (mpc == NULL) { @@ -21518,14 +30648,14 @@ + } + mpc->dev_num = priv->itfnum; + mpc->dev = dev; -+ printk("mpoa: (%s) was initialized\n", dev->name); ++ dprintk("mpoa: (%s) was initialized\n", dev->name); + break; + case NETDEV_UNREGISTER: + /* the lec device was deallocated */ + mpc = find_mpc_by_lec(dev); + if (mpc == NULL) + break; -+ printk("mpoa: device (%s) was deallocated\n", dev->name); ++ dprintk("mpoa: device (%s) was deallocated\n", dev->name); + stop_mpc(mpc); + mpc->dev = NULL; + break; @@ -21645,7 +30775,7 @@ + dprintk("mpoa: (%s) MPOA_res_reply_rcvd: ip %d.%d.%d.%d\n", client->dev->name, ip[0], ip[1], ip[2], ip[3]); + ddprintk("mpoa: (%s) MPOA_res_reply_rcvd() entry = %p", client->dev->name, entry); + if(entry == NULL){ -+ printk("\nmpoa: (%s): ARGH, received res. reply for an entry that doesn't exist.\n", client->dev->name); ++ printk("\nmpoa: (%s) ARGH, received res. reply for an entry that doesn't exist.\n", client->dev->name); + return; + } + ddprintk(" entry_state = %d ", entry->entry_state); @@ -21673,38 +30803,6 @@ + } + + check_qos_and_open_shortcut(msg, client, entry); -+#if 0 -+ -+ /* entry->shortcut == NULL so we need to get a VC. First check for candicates -+ * in egress cache. If none found, ask daemon to create one. -+ */ -+ eg_entry = client->eg_ops->search_by_src_ip(dst_ip, client); -+ if (eg_entry != NULL && eg_entry->shortcut) { -+ /* FIXME: Is it enough to just look at the max_pcr-value? */ -+ if(eg_entry->shortcut->qos.txtp.traffic_class == ATM_UBR || (eg_entry->shortcut->qos.txtp.traffic_class == ATM_CBR && eg_entry->shortcut->qos.txtp.max_pcr > 0)){ -+ entry->shortcut = eg_entry->shortcut; -+ ip = (unsigned char *)&dst_ip; -+ dprintk("mpoa: (%s) using egress SVC to reach %d.%d.%d.%d\n", client->dev->name, ip[0], ip[1], ip[2], ip[3]); -+ } -+ } -+ -+ /* It is possible that there was an egress entry with no valid shortcut */ -+ if (entry->shortcut == NULL) { -+ msg->type = OPEN_INGRESS_SVC; -+ qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); -+ if (qos != NULL && qos->qos.txtp.traffic_class & msg->qos.txtp.traffic_class) { -+ msg->qos = qos->qos; -+ printk("mpoa: (%s) MPOA_res_reply_rcvd: trying to get a CBR shortcut\n", -+ client->dev->name); -+ } -+ else{ -+ if(qos)printk("mpoa: (%s) requested qos-parameters not supported by the other end\n",client->dev->name); -+ memset(&msg->qos,0,sizeof(struct atm_qos)); -+ } -+ msg_to_mpoad(msg, client); -+ } -+#endif -+ + entry->entry_state = INGRESS_RESOLVED; + + return; @@ -21719,12 +30817,12 @@ + + in_cache_entry *entry = mpc->in_ops->search_with_mask(dst_ip, mpc, mask); + if( entry == NULL ){ -+ printk("mpoa: (%s): ingress_purge_rcvd: recieved a purge for an entry that doesn't exist, ", mpc->dev->name); ++ printk("mpoa: (%s) ingress_purge_rcvd: recieved a purge for an entry that doesn't exist, ", mpc->dev->name); + printk("ip = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); + return; + } + while(entry != NULL){ -+ printk("mpoa: (%s) ingress_purge_rcvd: removing an ingress entry, ip = %u.%u.%u.%u\n" , ++ dprintk("mpoa: (%s) ingress_purge_rcvd: removing an ingress entry, ip = %u.%u.%u.%u\n" , + mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + mpc->in_ops->cache_remove(entry, mpc); + entry = mpc->in_ops->search_with_mask(dst_ip, mpc, mask); @@ -21739,7 +30837,7 @@ + eg_cache_entry *entry = mpc->eg_ops->search_by_cache_id(cache_id, mpc); + + if( entry == NULL ){ -+ printk("mpoa: (%s): egress_purge_rcvd: recieved a purge reply for an entry that doesn't exist\n", mpc->dev->name); ++ printk("mpoa: (%s) egress_purge_rcvd: received a purge reply for an entry that doesn't exist\n", mpc->dev->name); + return; + } + @@ -21755,7 +30853,7 @@ + struct k_message *purge_msg; + struct sk_buff *skb; + -+ dprintk("mpoa: purge_egress_shortcut:, entering\n"); ++ dprintk("mpoa: purge_egress_shortcut: entering\n"); + if (vcc == NULL) { + printk("mpoa: purge_egress_shortcut: vcc == NULL\n"); + return; @@ -21776,12 +30874,13 @@ + + if (atm_charge(vcc, skb->truesize) == 0) { + printk("mpoa: purge_egress_shortcut: atm_charge failed\n"); ++ kfree_skb(skb); + return; + } + + skb_queue_tail(&vcc->recvq, skb); + wake_up(&vcc->sleep); -+ dprintk("mpoa: purge_egress_shortcut:, exiting:\n"); ++ dprintk("mpoa: purge_egress_shortcut: exiting:\n"); + + return; +} @@ -21796,7 +30895,7 @@ + unsigned long flags; + eg_cache_entry *entry; + -+ printk("mpoa: (%s) mps_death:\n", mpc->dev->name); ++ dprintk("mpoa: (%s) mps_death:\n", mpc->dev->name); + + if(memcmp(msg->MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN)){ + printk("mpoa: (%s) mps_death: wrong MPS\n", mpc->dev->name); @@ -21827,7 +30926,7 @@ + eg_cache_entry *entry = mpc->eg_ops->search_by_cache_id(msg->content.eg_info.cache_id, mpc); + + holding_time = msg->content.eg_info.holding_time; -+ printk("mpoa: (%s) MPOA_cache_impos_rcvd: entry = %p, holding_time = %u\n", ++ dprintk("mpoa: (%s) MPOA_cache_impos_rcvd: entry = %p, holding_time = %u\n", + mpc->dev->name, entry, holding_time); + if(entry == NULL && holding_time) { + mpc->eg_ops->new_entry(msg, mpc); @@ -21861,11 +30960,11 @@ + memcpy(&tlv[7], mesg->MPS_ctrl, ATM_ESA_LEN); /* MPC ctrl ATM addr */ + memcpy(mpc->our_ctrl_addr, mesg->MPS_ctrl, ATM_ESA_LEN); + -+ printk("mpoa: (%s) setting MPC ctrl ATM address to ", ++ dprintk("mpoa: (%s) setting MPC ctrl ATM address to ", + (mpc->dev) ? mpc->dev->name : ""); + for (i = 7; i < sizeof(tlv); i++) -+ printk("%02x ", tlv[i]); -+ printk("\n"); ++ dprintk("%02x ", tlv[i]); ++ dprintk("\n"); + + if (mpc->dev) { + priv = (struct lec_priv *)mpc->dev->priv; @@ -21881,8 +30980,10 @@ +} + +static void set_mps_mac_addr_rcvd(struct k_message *msg, struct mpoa_client *client){ ++ + if(client->number_of_mps_macs) + kfree(client->mps_macs); ++ client->number_of_mps_macs = 0; + client->mps_macs = kmalloc(ETH_ALEN,GFP_KERNEL); + if (client->mps_macs == NULL) { + printk("mpoa: set_mps_mac_addr_rcvd: out of memory\n"); @@ -21908,7 +31009,7 @@ + entry = mpc->eg_cache; + while(entry != NULL){ + msg->content.eg_info = entry->ctrl_info; -+ printk("mpoa: cache_id %u\n", entry->ctrl_info.cache_id); ++ dprintk("mpoa: cache_id %u\n", entry->ctrl_info.cache_id); + msg_to_mpoad(msg, mpc); + entry = entry->next; + } @@ -22035,7 +31136,7 @@ +} +#endif /* MODULE */ --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpc.h Thu Apr 22 19:46:47 1999 ++++ work/net/atm/mpc.h Tue May 18 01:14:52 1999 @@ -0,0 +1,65 @@ +#ifndef _MPC_H_ +#define _MPC_H_ @@ -22103,7 +31204,7 @@ + +#endif /* _MPC_H_ */ --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpoa_caches.c Tue Feb 9 15:49:58 1999 ++++ work/net/atm/mpoa_caches.c Tue May 18 00:41:41 1999 @@ -0,0 +1,557 @@ +#include +#include @@ -22198,11 +31299,11 @@ + in_cache_entry* entry = kmalloc(sizeof(in_cache_entry), GFP_KERNEL); + + if (entry == NULL) { -+ printk("mpoa: mpoa_caches.c: new_in_cache_entry() out of memory\n"); ++ printk("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n"); + return NULL; + } + -+ printk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); ++ dprintk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); + memset(entry,0,sizeof(in_cache_entry)); + + dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: about to lock\n"); @@ -22258,7 +31359,7 @@ + entry->entry_state == INGRESS_INVALID){ + unsigned char *ip = (unsigned char *)&entry->ctrl_info.in_dst_ip; + -+ printk("mpoa: (%s) mpoa_caches.c: threshold exceeded for ip %u.%u.%u.%u, sending MPOA res req\n", mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); ++ dprintk("mpoa: (%s) mpoa_caches.c: threshold exceeded for ip %u.%u.%u.%u, sending MPOA res req\n", mpc->dev->name, ip[0], ip[1], ip[2], ip[3]); + entry->entry_state = INGRESS_RESOLVING; + msg.type = SND_MPOA_RES_RQST; + memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN ); @@ -22281,12 +31382,12 @@ + if (vcc == NULL) + return; + -+ printk("mpoa: mpoa_caches.c: close_unused_egress_vcc:\n"); ++ dprintk("mpoa: mpoa_caches.c: close_unused_egress_vcc:\n"); + if (mpc->eg_ops->search_by_vcc(vcc, mpc) != NULL) + return; /* entry still in use */ + + atm_async_release_vcc(vcc, -EPIPE); /* nobody uses this VCC anymore, close it */ -+ printk("mpoa: mpoa_caches.c: close_unused_egress_vcc, closed one:\n"); ++ dprintk("mpoa: mpoa_caches.c: close_unused_egress_vcc, closed one:\n"); + + return; +} @@ -22306,7 +31407,7 @@ + + vcc = entry->shortcut; + ip = (unsigned char *)&entry->ctrl_info.in_dst_ip; -+ printk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %u.%u.%u.%u\n",ip[0], ip[1], ip[2], ip[3]); ++ dprintk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %u.%u.%u.%u\n",ip[0], ip[1], ip[2], ip[3]); + + if (entry->prev != NULL) + entry->prev->next = entry->next; @@ -22346,7 +31447,7 @@ + if((now.tv_sec - entry->tv.tv_sec) + > entry->ctrl_info.holding_time){ + ip = (unsigned char*)&entry->ctrl_info.in_dst_ip; -+ printk("mpoa: mpoa_caches.c: holding time expired, ip = %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); ++ dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + in_cache_remove(entry, client); + } + entry = next_entry; @@ -22518,12 +31619,12 @@ + if (vcc == NULL) + return; + -+ printk("mpoa: mpoa_caches.c: close_unused_ingress_vcc:\n"); ++ dprintk("mpoa: mpoa_caches.c: close_unused_ingress_vcc:\n"); + if (mpc->in_ops->search_by_vcc(vcc, mpc) != NULL) + return; /* entry still in use */ + + atm_async_release_vcc(vcc, -EPIPE); /* nobody uses this VCC anymore, close it */ -+ printk("mpoa: mpoa_caches.c: close_unused_ingress_vcc:, closed one\n"); ++ dprintk("mpoa: mpoa_caches.c: close_unused_ingress_vcc:, closed one\n"); + + return; +} @@ -22539,7 +31640,7 @@ + return 0; + + vcc = entry->shortcut; -+ printk("mpoa: mpoa_caches.c: removing an egress entry.\n"); ++ dprintk("mpoa: mpoa_caches.c: removing an egress entry.\n"); + if (entry->prev != NULL) + entry->prev->next = entry->next; + else @@ -22565,12 +31666,12 @@ + eg_cache_entry *entry = kmalloc(sizeof(eg_cache_entry), GFP_KERNEL); + + if (entry == NULL) { -+ printk("mpoa: mpoa_caches.c: new_eg_cache_entry() out of memory\n"); ++ printk("mpoa: mpoa_caches.c: new_eg_cache_entry: out of memory\n"); + return NULL; + } + + ip = (unsigned char *)&msg->content.eg_info.eg_dst_ip; -+ printk("mpoa: mpoa_caches.c: adding an egress entry, ip = %d.%d.%d.%d, this should be our IP\n", ip[0], ip[1], ip[2], ip[3]); ++ dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %d.%d.%d.%d, this should be our IP\n", ip[0], ip[1], ip[2], ip[3]); + memset(entry, 0, sizeof(eg_cache_entry)); + + dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: about to lock\n"); @@ -22587,9 +31688,9 @@ + entry->ctrl_info = msg->content.eg_info; + do_gettimeofday(&(entry->tv)); + entry->entry_state = EGRESS_RESOLVED; -+ printk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry->ctrl_info.cache_id)); ++ dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry->ctrl_info.cache_id)); + ip = (unsigned char *)&entry->ctrl_info.mps_ip; -+ printk("mpoa: mpoa_caches.c: mps_ip = %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); ++ dprintk("mpoa: mpoa_caches.c: mps_ip = %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + return entry; +} + @@ -22618,7 +31719,7 @@ + > entry->ctrl_info.holding_time){ + msg.type = SND_EGRESS_PURGE; + msg.content.eg_info = entry->ctrl_info; -+ printk("mpoa: mpoa_caches.c: egress_cache:holding time expired, cache_id = %lu.\n",ntohl(entry->ctrl_info.cache_id)); ++ dprintk("mpoa: mpoa_caches.c: egress_cache: holding time expired, cache_id = %lu.\n",ntohl(entry->ctrl_info.cache_id)); + msg_to_mpoad(&msg, client); + eg_cache_remove(entry, client); + } @@ -22663,7 +31764,7 @@ + return; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpoa_caches.h Thu Apr 22 19:46:47 1999 ++++ work/net/atm/mpoa_caches.h Tue May 18 01:14:52 1999 @@ -0,0 +1,90 @@ +#ifndef MPOA_CACHES_H +#define MPOA_CACHES_H @@ -22756,8 +31857,8 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpoa_proc.c Tue Feb 9 17:15:25 1999 -@@ -0,0 +1,395 @@ ++++ work/net/atm/mpoa_proc.c Tue May 18 00:41:41 1999 +@@ -0,0 +1,391 @@ +#include + +#ifdef CONFIG_PROC_FS @@ -23017,7 +32118,7 @@ +static int parse_qos(const char *buff, int len) +{ + /* possible lines look like this -+ * add 130.230.54.142 tx=max_pcr,max_cdv,max_sdu rx=max_pcr,max_cdv,max_sdu ++ * add 130.230.54.142 tx=max_pcr,max_sdu rx=max_pcr,max_sdu + */ + + int pos, i; @@ -23060,7 +32161,7 @@ + if(tmp == NULL) return 0; + tmp += 3; + prev = tmp; -+ for( i = 0; i < 2; i++){ ++ for( i = 0; i < 1; i++){ + tmp = strchr(prev, ','); + if (tmp == NULL) return 0; + memset(temp, '\0', 256); @@ -23076,8 +32177,7 @@ + value[i] = (int)simple_strtoul(temp, NULL, 0); + qos.txtp.traffic_class = ATM_CBR; + qos.txtp.max_pcr = value[0]; -+ qos.txtp.max_cdv = value[1]; -+ qos.txtp.max_sdu = value[2]; ++ qos.txtp.max_sdu = value[1]; + + /* next receive values */ + tmp = strstr(buff, "rx="); @@ -23090,7 +32190,7 @@ + } else { + tmp += 3; + prev = tmp; -+ for( i = 0; i < 2; i++){ ++ for( i = 0; i < 1; i++){ + tmp = strchr(prev, ','); + if (tmp == NULL) return 0; + memset(temp, '\0', 256); @@ -23106,16 +32206,13 @@ + value[i] = (int)simple_strtoul(temp, NULL, 0); + qos.rxtp.traffic_class = ATM_CBR; + qos.rxtp.max_pcr = value[0]; -+ qos.rxtp.max_cdv = value[1]; -+ qos.rxtp.max_sdu = value[2]; ++ qos.rxtp.max_sdu = value[1]; + } + qos.aal = ATM_AAL5; -+ dprintk("mpoa: mpoa_proc.c: parse_qos(): setting qos paramameters to tx=%d,%d,%d rx=%d,%d,%d\n", ++ dprintk("mpoa: mpoa_proc.c: parse_qos(): setting qos paramameters to tx=%d,%d rx=%d,%d\n", + qos.txtp.max_pcr, -+ qos.txtp.max_cdv, + qos.txtp.max_sdu, + qos.rxtp.max_pcr, -+ qos.rxtp.max_cdv, + qos.rxtp.max_sdu + ); + diff -ur --new-file old/atm/doc/usage.tex new/atm/doc/usage.tex --- old/atm/doc/usage.tex Thu Apr 22 17:39:04 1999 +++ new/atm/doc/usage.tex Tue May 18 01:20:52 1999 @@ -1,7 +1,7 @@ -%%def%:= +7%def%:= %:\begin{verbatim} -%:Usage instructions - ATM on Linux, release 0.56 +%:Usage instructions - ATM on Linux, release 0.57 %:------------------------------------------------- %: %:\end{verbatim} @@ -38,7 +38,7 @@ \title{ATM on Linux \\ User's guide \\ - Release 0.56 (alpha)} + Release 0.57 (alpha)} \author{Werner Almesberger \\ {\tt Werner.Almesberger@epfl.ch} \\ \\ @@ -82,7 +82,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.56.tar.gz} + \url{ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.57.tar.gz} \item the Linux kernel, version 2.2.1, e.g. from \url{ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.2.1.tar.gz} \item Perl, version 4 or 5 @@ -99,7 +99,7 @@ distribution: \begin{verbatim} -tar xfz atm-0.56.tar.gz +tar xfz atm-0.57.tar.gz \end{verbatim} and the kernel source: @@ -122,7 +122,6 @@ \name{Makefile}, and distribution scripts \item[\path{atm/sigd/}] UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: \name{atmsigd} - \item[\path{atm/sigd.old/}] Older version: \name{atmsigd.old} \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} diff -ur --new-file old/atm/doc/usage.txt new/atm/doc/usage.txt --- old/atm/doc/usage.txt Thu Apr 22 20:02:00 1999 +++ new/atm/doc/usage.txt Tue May 18 01:30:05 1999 @@ -1,10 +1,15 @@ -Usage instructions - ATM on Linux, release 0.56 -------------------------------------------------- +7 + +%:Usage instructions - ATM on Linux, release 0.57 +%:------------------------------------------------- +%: +%: For updates of ATM on Linux, please check the Web page at http://ica1www.epfl.ch/linux-atm/ - WARNING +%: WARNING +%: This is experimental software. There are known major bugs and certainly even many more yet unknown problems. Internal and external interfaces are @@ -17,7 +22,7 @@ In order to install this package, you need - the package itself - ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.56.tar.gz + ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.57.tar.gz - the Linux kernel, version 2.2.1, e.g. from ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.2.1.tar.gz - Perl, version 4 or 5 @@ -33,7 +38,7 @@ all the files listed above there. Then extract the ATM on Linux distribution: -tar xfz atm-0.56.tar.gz +tar xfz atm-0.57.tar.gz and the kernel source: @@ -49,7 +54,6 @@ atm/ Documentation in ASCII format, kernel patch, top-level Makefile, and distribution scripts atm/sigd/ UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: atmsigd - atm/sigd.old/ Older version: atmsigd.old atm/saal/ Signaling AAL library (SSCOP, SSCF, and SAAL) atm/qgen/ Q.2931-style message handling atm/ilmid/ ILMI address registration demon: ilmid diff -ur --new-file old/atm/led/USAGE new/atm/led/USAGE --- old/atm/led/USAGE Wed Nov 19 12:59:15 1997 +++ new/atm/led/USAGE Thu Jan 1 01:00:00 1970 @@ -1,76 +0,0 @@ -/* - * Marko Kiiskila carnil@cs.tut.fi - * - * Tampere University of Technology - Telecommunications Laboratory - * - */ - -LAN Emulation Package -===================== -Lan Emulation Daemon (LED) Zeppelin & kernel driver. Description -of LAN-E can be found from ATM Forum's specification "LAN Emulation -Over ATM - Version 1.0". -ftp://ftp.atmforum.com/pub/specs/af-lane-0021.000.ps.Z - -Usage: -====== -In order to use LE one has to compile kernel with LANE support. - -zeppelin [-c LECS_address]|[-s LES_address] [-e esi] [-n VLAN_name] - [-m msg_mask] [-l listen_address] - -LECS_address: ATM address of Lan Emulation Configuration Server, if not -set, Well-Known server address is used. - -LES_address: ATM address of Lan Emulation Server, can be used to -bypass configuration phase in joining emulated Lan i.e LECS address -is not used. - -Esi: Mac address to use when communicating in Emulated LAN - -VLAN_name: Name of the virtual LAN to which joining is requested. - -msg_mask: Sometimes one wants to know more what is happening in LE -daemon e.g. when nothing works. This is a hexadecimal long value -setting global message mask. 0 = No messages, ffff = All messages. -Purpose of different mask values is shown in source file g_event.h. - -listen_address: ATM address that zeppelin uses in signalling. - -e.g. -> zeppelin -c coral_lecs -n tut-lane1 & -> ifconfig lec0 viulu.lane.tut.fi netmask 255.255.255.0 -> route add -net 193.166.165.0 -> netstat -nr - -Suggested way of running LAN-E is via two scripts: -/etc/rc.d/rc.atm: -----8<----- -#/bin/sh -/usr/local/bin/atmsigd -b -/usr/local/bin/ilmid -b -sleep 2 -/usr/local/bin/zeppelin.sh & -/sbin/ifconfig lec0 viulu.lane.tut.fi netmask 255.255.255.0 -route add -net 193.166.165.0 -----8<----- - -/usr/local/bin/zeppelin.sh: -----8<----- -#/bin/sh - -while [ 1 ]; do - /usr/local/bin/zeppelin -c coral_lecs -m 3 - sleep 30 -done -----8<----- - -Wrapper scripts aren't really needed anymore. This is because demon -code is becoming more stable and won't (hopefully) crash :) - -Known limitations: ------------------- -Supports only 802.3 type of Emulated LANs. -Tested only with DIX Ethernet data frame. -Possibility for only one VLAN interface - diff -ur --new-file old/atm/led.new/USAGE new/atm/led.new/USAGE --- old/atm/led.new/USAGE Thu Feb 18 19:05:58 1999 +++ new/atm/led.new/USAGE Thu Jan 1 01:00:00 1970 @@ -1,76 +0,0 @@ -/* - * Marko Kiiskila carnil@cs.tut.fi - * - * Tampere University of Technology - Telecommunications Laboratory - * - */ - -LAN Emulation Package -===================== -Lan Emulation Daemon (LED) Zeppelin & kernel driver. Description -of LAN-E can be found from ATM Forum's specification "LAN Emulation -Over ATM - Version 1.0". -ftp://ftp.atmforum.com/pub/specs/af-lane-0021.000.ps.Z - -Usage: -====== -In order to use LE one has to compile kernel with LANE support. - -zeppelin [-c LECS_address]|[-s LES_address] [-e esi] [-n VLAN_name] - [-m msg_mask] [-l listen_address] - -LECS_address: ATM address of Lan Emulation Configuration Server, if not -set, Well-Known server address is used. - -LES_address: ATM address of Lan Emulation Server, can be used to -bypass configuration phase in joining emulated Lan i.e LECS address -is not used. - -Esi: Mac address to use when communicating in Emulated LAN - -VLAN_name: Name of the virtual LAN to which joining is requested. - -msg_mask: Sometimes one wants to know more what is happening in LE -daemon e.g. when nothing works. This is a hexadecimal long value -setting global message mask. 0 = No messages, ffff = All messages. -Purpose of different mask values is shown in source file g_event.h. - -listen_address: ATM address that zeppelin uses in signalling. - -e.g. -> zeppelin -c coral_lecs -n tut-lane1 & -> ifconfig lec0 viulu.lane.tut.fi netmask 255.255.255.0 -> route add -net 193.166.165.0 -> netstat -nr - -Suggested way of running LAN-E is via two scripts: -/etc/rc.d/rc.atm: -----8<----- -#/bin/sh -/usr/local/bin/atmsigd -b -/usr/local/bin/ilmid -b -sleep 2 -/usr/local/bin/zeppelin.sh & -/sbin/ifconfig lec0 viulu.lane.tut.fi netmask 255.255.255.0 -route add -net 193.166.165.0 -----8<----- - -/usr/local/bin/zeppelin.sh: -----8<----- -#/bin/sh - -while [ 1 ]; do - /usr/local/bin/zeppelin -c coral_lecs -m 3 - sleep 30 -done -----8<----- - -Wrapper scripts aren't really needed anymore. This is because demon -code is becoming more stable and won't (hopefully) crash :) - -Known limitations: ------------------- -Supports only 802.3 type of Emulated LANs. -Tested only with DIX Ethernet data frame. -Possibility for only one VLAN interface - diff -ur --new-file old/atm/mkdist new/atm/mkdist --- old/atm/mkdist Fri Feb 19 17:06:52 1999 +++ new/atm/mkdist Tue May 18 00:39:37 1999 @@ -20,13 +20,6 @@ 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/doc/README.CLP \ atm/man/Makefile atm/man/qos.7 atm/man/sap.7 \ - atm/sigd.old/Makefile atm/sigd.old/atmsigd.c atm/sigd.old/cfg.l \ - atm/sigd.old/cfg.y atm/sigd.old/io.h atm/sigd.old/io.c \ - atm/sigd.old/kernel.c atm/sigd.old/proto.h atm/sigd.old/proto.c \ - atm/sigd.old/uni.c atm/sigd.old/timeout.c atm/sigd.old/timeout.h \ - atm/sigd.old/sap.h atm/sigd.old/sap.c atm/sigd.old/trace.c \ - atm/sigd.old/trace.h atm/sigd.old/policy.h atm/sigd.old/policy.c \ - atm/sigd.old/mkmess.pl atm/sigd.old/atmsigd.8 atm/sigd.old/atmsigd.conf.4 \ atm/sigd/README \ atm/sigd/Makefile atm/sigd/atmsigd.c atm/sigd/cfg.l \ atm/sigd/cfg.y atm/sigd/io.h atm/sigd/io.c \ @@ -89,7 +82,7 @@ atm/lib/sap2text.c atm/lib/text2sap.c atm/lib/sapequal.c atm/lib/misc.c \ atm/lib/atmres.h atm/lib/ans.c atm/lib/rtf2e164_cc.pl \ atm/lib/stdint.h atm/lib/unix.c atm/lib/text2ip.c \ - atm/led/USAGE atm/led/COPYRIGHT.DEC atm/led/COPYRIGHT.TUT \ + atm/led/COPYRIGHT.DEC atm/led/COPYRIGHT.TUT \ atm/led/lec.h atm/led/lec_arp.h atm/led/lec_ctrl.h atm/led/emask.h \ atm/led/le_disp.h atm/led/g_event.h \ atm/led/addr_reg.h atm/led/af_lane.h atm/led/lane_atm.h atm/led/cm.h \ @@ -101,7 +94,7 @@ atm/led/address.c atm/led/conn.c atm/led/main.c atm/led/kernel_itf.c \ atm/led/lane2.h \ atm/led/Makefile atm/led/zeppelin.8 \ - atm/led.new/USAGE atm/led.new/COPYRIGHT.TUT atm/led.new/Makefile \ + atm/led.new/COPYRIGHT.TUT atm/led.new/Makefile \ atm/led.new/address.c atm/led.new/address.h atm/led.new/conn.c \ atm/led.new/conn.h atm/led.new/display.c atm/led.new/display.h \ atm/led.new/frame_defs.h atm/led.new/frames.c atm/led.new/frames.h \ diff -ur --new-file old/atm/mkpatch new/atm/mkpatch --- old/atm/mkpatch Fri Feb 19 18:00:40 1999 +++ new/atm/mkpatch Tue May 18 01:29:42 1999 @@ -17,10 +17,17 @@ #--- drivers/atm -------------------------------------------------------------- drivers/atm/Config.in drivers/atm/Makefile +drivers/atm/ambassador.c +drivers/atm/ambassador.h drivers/atm/atmdev_init.c +drivers/atm/atmsar11.data +drivers/atm/atmsar11.regions +drivers/atm/atmsar11.start drivers/atm/atmtcp.c drivers/atm/eni.c drivers/atm/eni.h +drivers/atm/horizon.c +drivers/atm/horizon.h drivers/atm/midway.h drivers/atm/nicstar.h drivers/atm/nicstar.c @@ -86,7 +93,7 @@ net/atm/common.h net/atm/ipcommon.c net/atm/ipcommon.h -net/atm/misc.c +net/atm/atm_misc.c net/atm/lec.c net/atm/lec.h net/atm/lec_arpc.h diff -ur --new-file old/atm/mpoad/CHANGELOG new/atm/mpoad/CHANGELOG --- old/atm/mpoad/CHANGELOG Sat Dec 5 01:43:15 1998 +++ new/atm/mpoad/CHANGELOG Tue May 18 00:33:34 1999 @@ -1,3 +1,16 @@ +Changes from version 0.55 +========================= +o mpoa_proc.c: CDV is no more requred for CBR entries +o mpc.c: two small skb_buff leaks fixed +o kernel: number of debug messages reduced + +o k_interf.c: mpcd could not be killed if MPS's address was not known + +Changes from version 0.52 +========================= +o mpoa_proc.c: when setting QoS values for shortcuts, receive values + can now be specified with just "... rx=tx" + Changes from version 0.50 ========================= o lecs.c: new file. mpcd can now ask LECS for configuration info diff -ur --new-file old/atm/mpoad/k_interf.c new/atm/mpoad/k_interf.c --- old/atm/mpoad/k_interf.c Sat Dec 5 01:43:15 1998 +++ new/atm/mpoad/k_interf.c Tue May 18 00:34:39 1999 @@ -67,6 +67,11 @@ } dprintf("mpcd: k_interf.c: message from kernel: "); + if (msg.type == DIE) { + dprintf(" die\n"); + exit(0); + } + /* do nothing if MPS's control ATM address is not known */ if (msg.type != SET_MPS_CTRL_ADDR && !mpc_control.mps_ctrl_addr_set) return 0; @@ -105,10 +110,6 @@ get_own_ip_addr(mpc_control.INTERFACE_NUMBER), msg.content.eg_info.cache_id); return 1; - break; - case DIE: - dprintf(" die.\n"); - exit(0); break; case OPEN_INGRESS_SVC: dprintf(" open_ingress_svc\n"); diff -ur --new-file old/atm/sigd/atmsigd.conf.4 new/atm/sigd/atmsigd.conf.4 --- old/atm/sigd/atmsigd.conf.4 Thu Mar 25 15:44:20 1999 +++ new/atm/sigd/atmsigd.conf.4 Wed Apr 28 14:12:28 1999 @@ -1,4 +1,4 @@ -.TH ATMSIGD.CONF 4 "March 25, 1999" "Linux" "File Formats" +.TH ATMSIGD.CONF 4 "April 28, 1999" "Linux" "File Formats" .SH NAME atmsigd.conf \- configuration file for the ATM signaling demon .SH SYNOPSIS @@ -100,7 +100,7 @@ curly braces: \fBvpci\fP (corresponds to \fBsig vpci\fP), \fBmode\fP (corresponds to \fBsig mode\fP), \fBqos\fP (corresponds to \fBvc qos\fP), \fBroute\fP, and \fBdefault\fP. The last two options determine how -outbound call are routed. The \fBroute\fP option is followed by an address +outbound calls are routed. The \fBroute\fP option is followed by an address in the format used for addresses in \fBpolicy\fP. If no route entry matches the called party number of an outbound call, the entry marked with \fBdefault\fP is selected. diff -ur --new-file old/atm/sigd.old/Makefile new/atm/sigd.old/Makefile --- old/atm/sigd.old/Makefile Thu Apr 22 17:29:40 1999 +++ new/atm/sigd.old/Makefile Thu Jan 1 01:00:00 1970 @@ -1,39 +0,0 @@ -LIBS=-L../saal -lsaal -latmd -lfl # lex may want -ll here -LIBDEPS=../saal/libsaal.a ../lib/libatmd.a -INCLUDES=-I../qgen -I../saal -I. -OBJS=atmsigd.o io.o kernel.o mess.o proto.o uni.o sap.o timeout.o trace.o \ - policy.o lex.yy.o y.tab.o -EXTOBJS=../qgen/q.out.o ../qgen/qd.dump.o -#BOOTPGMS=atmsigd # don't install anymore -PGMS=atmsigd.old -TRASH=mess.c q.out.h -#MAN4=atmsigd.conf.4 # don't install anymore -#MAN8=atmsigd.8 # don't install anymore - -include ../Rules.make -CFLAGS_NOWARN += $(STANDARDS) - -atmsigd.old: $(OBJS) $(EXTOBJS) - $(CC) $(LDFLAGS) -o atmsigd.old $(OBJS) $(EXTOBJS) \ - $(LDLIBS) $(LIBS) - -lex.yy.c: cfg.l y.tab.h ../lib/atm.h - $(LEX) cfg.l - -y.tab.c y.tab.h: cfg.y ../lib/atmd.h proto.h io.h - $(YACC) -d cfg.y - -mess.c: ../qgen/uni.h mkmess.pl - perl ./mkmess.pl <../qgen/uni.h >mess.c - -# -# The following hack makes sure that "make depend" finds q.out.h and is -# happy with it. Once qgen has been built, there will be ../qgen/q.out.h, -# which is first in the include file search path and therefore gets -# included. An second "make depend" will also use the right file. -# - -depend: fake_q.out.h - -fake_q.out.h: - echo "! This must not compile" >q.out.h diff -ur --new-file old/atm/sigd.old/atmsigd.8 new/atm/sigd.old/atmsigd.8 --- old/atm/sigd.old/atmsigd.8 Thu Aug 20 18:04:23 1998 +++ new/atm/sigd.old/atmsigd.8 Thu Jan 1 01:00:00 1970 @@ -1,105 +0,0 @@ -.TH ATMSIGD 8 "June 25, 1998" "Linux" "Maintenance Commands" -.SH NAME -atmsigd \- ATM signaling demon -.SH SYNOPSIS -.B atmsigd -.RB [ \-b ] -.RB [ \-c\ \fIconfig_file\fP ] -.RB [ \-d ] -.RB [ \-D\ \fIdump_dir\fP ] -.RB [ \-l\ \fIlogfile\fP ] -.RB [ \-m\ \fImode\fP ] -.RB [ \-n ] -.RB [ \-q\ \fIqos\fP ] -.RB [ \-t\ \fItrace_length\fP ] -.RB [ [\fIitf\fP.]\fIvpi\fP.\fIvci\fP -.RB [ \fIinput\ output\fP ] ] -.SH DESCRIPTION -\fBatmsigd\fP implements the ATM UNI signaling protocol. Requests to -establish, accept, or close ATM SVCs are sent from the kernel (using a -comparably simple protocol) to the signaling demon, which then performs -the dialog with the network. -.P -Note that \fBatmsigd\fP is not able to accept or establish connections -until the local ATM address of the interface is configured by \fBilmid\fP -or manually using \fBatmaddr\fP. -.P -The default signaling VC (interface 0, VPI 0, VCI 5) can be overridden on -the command line by specifying a different PVC address. -.P -When overriding the default VC, optionally a pair of named pipes to use for -communicating with the user of signaling can be specified. Normally, the -kernel is the user of signaling and \fBatmsigd\fP opens a special socket for -communication with it. -.P -If \fBatmsigd\fP is killed, all system calls requiring interaction with it -will return with an error and set \fBerrno\fP to \fBEUNATCH\fP. -.SH OPTIONS -.IP \fB\-b\fP -Run in background (i.e. in a forked child process) after initializing. -.IP \fB\-c\ \fIconfig_file\fP -Use the specified configuration file instead of \fB/etc/atmsigd.conf\fP -If an option is specified in the configuration file and on the command -line, the command line has priority. -.IP \fB\-d\fP -Enables (lots of) debugging output. By default, \fBatmsigd\fP is comparably -quiet. -.IP \fB\-D\ \fIdump_dir\fP -Specifies the directory to which \fBatmsigd\fP will write status and trace -dumps. If tracing is not yet enabled, the trace size is automatically -set to a (small) default value. -.IP \fB\-l\ \fIlogfile\fP -Write diagnostic messages to the specified file. The special name -\fBsyslog\fP is used to send diagnostics to the system logger, \fBstderr\fP -is used to send diagnostics to standard error. If \fB\-l\fP is absent, the -setting in \fBatmsigd.conf\fP is used. If \fBatmsigd\fP doesn't specify a -destination either, messages are written to standard error. -.IP \fB\-m\ \fImode\fP -Set the mode of operation. The following modes are available: \fBuser\fP for -the user side (the default), \fBnetwork\fP for the network side (useful if you -have two PCs but no switch), and \fBswitch\fP for operation with a signaling -relay in a switch. -.IP \fB\-n\fP -Prints addresses in numeric format only, i.e. no address to name translation -is attempted. -.IP \fB\-q\ \fIqos\fP -Configures the signaling VC to use the specified quality of service (see -qos(7) for the syntax). -By default, UBR at link speed is used on the signaling VC. -.IP \fB\-t\ \fItrace_length\fP -Enables tracing and sets the number -of entries that should be kept in the trace buffer. -.SH FILES -.PD 0 -.TP 25 -.B /etc/atmsigd.conf -default configuration file -.TP 25 -.B /var/tmp/atmsigd.\fIpid\fB.status.\fIversion\fP -default location of status dumps -.TP 25 -.B /var/tmp/atmsigd.\fIpid\fB.trace.\fIversion\fP -default location of signaling trace dumps -.PD -.SH DEBUGGING -When receiving a \fBSIGUSR1\fP signals, \fBatmsigd\fP dumps the list of all -internal socket descriptors. With \fBSIGUSR2\fP, it dumps the contents of -the trace buffer. If a dump directory was set, dumps are written to files -called \fBatmsigd.\fP\fIpid\fP\fB.status.\fP\fInumber\fP and -\fBatmsigd.\fP\fIpid\fP\fB.trace.\fP\fInumber\fP, respectively, with -\fInumber\fP -starting at zero and being incremented for every dump. If no dump directory -is set, dumps are written to standard error. -.P -Dumps are also generated whenever \fBatmsigd\fP detects a fatal error and -terminates. No attempt is made to catch signals like \fBSIGSEGV\fP. -.SH BUGS -The generation of traces is a comparably slow -process which may already take several seconds for only 100 trace entries. -To generate a trace dump, \fBatmsigd\fP therefore forks a child process that -runs in parallel to the signaling demon. -.SH AUTHOR -Werner Almesberger, EPFL ICA -.SH "SEE ALSO" -atmaddr(8), atmsigd.conf(4), ilmid(8), qos(7) -.\"{{{}}} diff -ur --new-file old/atm/sigd.old/atmsigd.c new/atm/sigd.old/atmsigd.c --- old/atm/sigd.old/atmsigd.c Thu Apr 22 16:47:13 1999 +++ new/atm/sigd.old/atmsigd.c Thu Jan 1 01:00:00 1970 @@ -1,462 +0,0 @@ -/* atmsigd.c - ATM signaling demon */ - -/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atm.h" -#include "atmd.h" -#include "qlib.h" - -#include "io.h" -#include "proto.h" -#include "saal.h" -#include "trace.h" - - -#define COMPONENT "SIGD" -#define CONFIG_FILE "/etc/atmsigd.conf" - - -extern int yyparse(void); -extern FILE *yyin; - -int debug = 0; -int pretty = A2T_PRETTY | A2T_NAME | A2T_LOCAL; -int sig_pcr = -1; /* obsolete */ -const char *sig_qos = NULL; -const char *dump_dir = NULL; - -SIGNALING_MODE mode = sm_user; -int net = 0; -int allocate_ci = 1; - - -/* ------------------------------ SAAL relays ------------------------------ */ - - -static SAAL_DSC saal; - - -static void q_estab_conf(void *user_data,void *uu_data,int uu_length) -{ - saal_okay(); -} - - -static void q_rel_ind(void *user_data,void *uu_data,int uu_length) -{ - saal_failure(); - saal_estab_req(&saal,NULL,0); -} - - -static void q_restart(void *user_data,void *uu_data,int uu_length,int ind) -{ - saal_failure(); - if (!ind) saal_okay(); /* actually, ind should probably never be zero */ -} - - -void from_net(void *buffer,int size) -{ - saal_pdu(&saal,buffer,size); -} - - -void to_signaling(void *msg,int size) -{ - trace_uni("TO NETWORK",msg,size); - diag(COMPONENT,DIAG_DEBUG,"TO SAAL: %s (0x%02x) CR 0x%06x (%d bytes)", - mid2name(((unsigned char *) msg)[5]),((unsigned char *) msg)[5], - (((unsigned char *) msg)[2] << 16) | (((unsigned char *) msg)[3] << 8) | - ((unsigned char *) msg)[4],size); - saal_send(&saal,msg,size); -} - - -static void q_data_ind(void *user_data,void *data,int length) -{ - trace_uni("FROM NETWORK",data,length); - to_uni(data,length); -} - - -static void q_cpcs_send(void *user_data,void *data,int length) -{ - to_net(data,length); -} - - -static SAAL_USER_OPS ops = { - NULL, /* no q_estab_ind - 5.5.6.9 says 5.5.6.11 and 5.5.6.11 says "may" */ - q_estab_conf, - q_rel_ind, - NULL, /* no q_rel_conf - what to do ? */ - q_restart, - q_data_ind, - NULL, /* no q_unitdata */ - q_cpcs_send -}; - - -/* -------------------------------- signals -------------------------------- */ - - -static volatile int got_usr1 = 0,got_usr2 = 0; - - -static void dump_addr(FILE *file,const char *label,struct sockaddr_atmsvc *addr) -{ - char buffer[MAX_ATM_ADDR_LEN+1]; - int i; - - if (!atmsvc_addr_in_use(*addr)) return; - fprintf(file," %s ",label); - if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) addr,A2T_NAME | - A2T_PRETTY | A2T_LOCAL) >= 0) fprintf(file,"%s\n",buffer); - else { - fprintf(file,"\n"); - } -} - - -static void dump_sap(FILE *file,const char *label,struct atm_sap *sap) -{ - char buffer[MAX_ATM_SAP_LEN+1]; - int i; - - fprintf(file," %s ",label); - if (sap2text(buffer,MAX_ATM_SAP_LEN+1,sap,S2T_NAME | S2T_LOCAL) >= 0) - fprintf(file,"%s\n",buffer); - else { - fprintf(file,"\n"); - } -} - - -static void dump_status(FILE *file,const char *banner) -{ - SOCKET *walk; - - if (sockets) fprintf(file,"%s\n\n",banner); - for (walk = sockets; walk; walk = walk->next) { - fprintf(file,"0x%lx: %s, CR 0x%06lX, PVC %d.%d.%d\n",walk->id, - state_name[walk->state],walk->call_ref,walk->pvc.sap_addr.itf, - walk->pvc.sap_addr.vpi,walk->pvc.sap_addr.vci); - dump_addr(file,"local ",&walk->local); - dump_addr(file,"remote",&walk->remote); - dump_sap(file,"sap",&walk->sap); - } -} - - -static void dump_trace(FILE *file,const char *banner) -{ - static int busy = 0; - char *trace; - - if (busy++) abort(); - trace = get_trace(); - if (trace) { - fprintf(file,"%s\n\n",banner); - fprintf(file,"%s",trace); - } - busy--; -} - - -void poll_signals(void) -{ - static int status_num = 0,trace_num = 0; - char path[PATH_MAX+1]; - FILE *file; - - if (got_usr1) { - got_usr1 = 0; - if (!dump_dir) file = stderr; - else { - sprintf(path,"atmsigd.%d.status.%d",getpid(),status_num++); - if ((file = fopen(path,"w"))) - diag(COMPONENT,DIAG_INFO,"Dumping to %s",path); - else { - perror(path); - file = stderr; - } - } - dump_status(file,"Status dump (on SIGUSR1)"); - if (file != stderr) (void) fclose(file); - } - if (got_usr2) { - pid_t pid; - - got_usr2 = 0; - if (!dump_dir) file = stderr; - else { - sprintf(path,"atmsigd.%d.trace.%d",getpid(),trace_num++); - if ((file = fopen(path,"w"))) - diag(COMPONENT,DIAG_INFO,"Dumping to %s",path); - else { - perror(path); - file = stderr; - } - } - if (!(pid = fork())) - dump_trace(file,"Message trace (on SIGUSR2)"); - else if (pid < 0) perror("fork"); - if (file != stderr) (void) fclose(file); - if (!pid) exit(0); - } -} - - -static void handle_signal(int sig) -{ - switch (sig) { - case SIGUSR1: - got_usr1 = 1; - break; - case SIGUSR2: - got_usr2 = 1; - break; - default: - break; - } -} - - -static void setup_signals(void) -{ - struct sigaction act; - - (void) signal(SIGCHLD,SIG_IGN); /* reap children automatially */ - act.sa_handler = handle_signal; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - if (sigaction(SIGUSR1,&act,NULL) < 0) { - perror("sigaction"); - exit(1); - } - if (sigaction(SIGUSR2,&act,NULL) < 0) { - perror("sigaction"); - exit(1); - } -} - - -/* ------------------------------- main ... ------------------------------- */ - - -static void trace_on_exit(int status,void *dummy) -{ - char path[PATH_MAX+1]; - FILE *file; - - if (!status) return; - if (!dump_dir) file = stderr; - else { - sprintf(path,"atmsigd.%d.trace.exit",getpid()); - if (!(file = fopen(path,"w"))) { - perror(path); - file = stderr; - } - } - dump_trace(file,"Message trace (after error exit)"); - if (file != stderr) (void) fclose(file); -} - - -static void usage(const char *name) -{ - fprintf(stderr,"usage: %s [ -b ] [ -c config_file ] [ -d ] " - "[ -D dump_dir ]\n" - " [ -l logfile ] [ -n ] [ -m user|network|switch ] [ -q qos ]\n" - " [ -t trace_length ] [ [itf.]vpi.vci [ socket_path ] ]\n",name); - exit(1); -} - - -int main(int argc,char **argv) -{ - const char *config_file; - char *end; - int c,background; - - set_application("atmsigd"); - config_file = CONFIG_FILE; - dump_dir = NULL; - background = 0; - memset(&signaling_pvc,0,sizeof(signaling_pvc)); - signaling_pvc.sap_addr.vci = 5; - /* 1st pass to get the -c option */ - while ((c = getopt(argc,argv,"Abc:dD:l:m:nNP:q:t:")) != EOF) - if (c == 'c') config_file = optarg; - if (!(yyin = fopen(config_file,"r"))) - diag(COMPONENT,DIAG_WARN,"%s not found. - Using defaults.",config_file); - else if (yyparse()) - diag(COMPONENT,DIAG_FATAL,"Error in config file. - Aborting."); - /* process all other options but -c */ - optind = 0; - while ((c = getopt(argc,argv,"Abc:dD:l:m:nNP:q:t:")) != EOF) - switch (c) { - case 'A': - allocate_ci = 0; - break; - case 'b': - background = 1; - break; - case 'c': - /* already handled */ - break; - case 'd': - set_verbosity(NULL,DIAG_DEBUG); - set_verbosity("QMSG",DIAG_INFO); - set_verbosity("SSCOP",DIAG_INFO); - debug = 1; - /*q_dump = 1;*/ - break; - case 'D': - dump_dir = optarg; - if (!trace_size) trace_size = DEFAULT_TRACE_SIZE; - break; - case 'l': - set_logfile(optarg); - break; - case 'm': - if (!strcmp(optarg,"user")) mode = sm_user; - else if (!strcmp(optarg,"network")) mode = sm_net; - else if (!strcmp(optarg,"switch")) mode = sm_switch; - else usage(argv[0]); - break; - case 'n': - pretty = A2T_PRETTY; - break; - case 'N': - net = 1; - break; - case 'q': - if (sig_pcr != -1) usage(argv[0]); - sig_qos = optarg; - break; - case 'P': /* obsolete */ - if (sig_qos) usage(argv[0]); - sig_pcr = strtol(optarg,&end,0); - if (*end) usage(argv[0]); - break; - case 't': - trace_size = strtol(optarg,&end,0); - if (*end) usage(argv[0]); - break; - default: - usage(argv[0]); - } - switch (mode) { - case sm_unknown: - if (net) { - if (allocate_ci) { - mode = sm_net; - diag(COMPONENT,DIAG_WARN,"option -N is obsolete, " - "please use -m network instead"); - } - else { - mode = sm_switch; - diag(COMPONENT,DIAG_WARN,"options -N -A are obsolete, " - "please use -m switch instead"); - } - } - else if (allocate_ci) mode = sm_user; - else usage(argv[0]); - break; - case sm_user: - allocate_ci = 1; /* should be ignored anyway */ - net = 0; - break; - case sm_net: - allocate_ci = 1; - net = 1; - break; - case sm_switch: - allocate_ci = 0; - net = 1; - break; - default: - abort(); - } - if (optind < argc) { - if (text2atm(argv[optind],(struct sockaddr *) &signaling_pvc, - sizeof(signaling_pvc),T2A_PVC) < 0) - diag(COMPONENT,DIAG_FATAL,"text2atm \"%s\": failed",argv[optind]); - optind++; - if (optind == argc-1) { - open_unix(argv[optind]); - optind++; - } - } - if (optind != argc) usage(argv[0]); - diag(COMPONENT,DIAG_INFO,"Linux ATM signaling " -#ifdef UNI30 - "UNI 3.0" -#endif -#ifdef UNI31 - "UNI 3.1" -#ifdef ALLOW_UNI30 - "+3.0compat" -#endif -#endif -#ifdef UNI40 - "UNI 4.0" -#ifdef Q2963_1 - "+Q.2963.1" -#endif -#endif - "/AAL5, version " VERSION); - if (dump_dir) - if (chdir(dump_dir) < 0) - diag(COMPONENT,DIAG_ERROR,"chdir %s: %s",dump_dir,strerror(errno)); - diag(COMPONENT,DIAG_INFO,"Acting as %s", - mode == sm_user ? "USER side" : - mode == sm_net ? "NETWORK side" : "SWITCH"); - if (open_all()) return 1; - init_current_time(); - if (mode != sm_switch) init_addr(); - q_start(); - start_saal(&saal,&ops,NULL, -#ifdef UNI30 - sscop_qsaal1 -#else - sscop_q2110 -#endif - ); - saal_estab_req(&saal,NULL,0); - setup_signals(); - if (background) { - pid_t pid; - - pid = fork(); - if (pid < 0) - diag(COMPONENT,DIAG_FATAL,"fork: %s",strerror(errno)); - if (pid) { - diag(COMPONENT,DIAG_DEBUG,"Backgrounding (PID %d)",pid); - exit(0); - } - } - (void) on_exit(trace_on_exit,NULL); - poll_loop(); - close_all(); - stop_saal(&saal); - return 0; -} diff -ur --new-file old/atm/sigd.old/atmsigd.conf.4 new/atm/sigd.old/atmsigd.conf.4 --- old/atm/sigd.old/atmsigd.conf.4 Thu Aug 20 18:04:24 1998 +++ new/atm/sigd.old/atmsigd.conf.4 Thu Jan 1 01:00:00 1970 @@ -1,113 +0,0 @@ -.TH ATMSIGD.CONF 4 "June 25, 1998" "Linux" "File Formats" -.SH NAME -atmsigd.conf \- configuration file for the ATM signaling demon -.SH SYNOPSIS -.B /etc/atmsigd.conf -.SH DESCRIPTION -\fBatmsigd.conf\fP contains configuration data for \fBatmsigd\fP. -\fBatmsigd\fP reads \fBatmsigd.conf\fP after parsing the command -line options, before connecting to the ATM network. -.P -Configuration parameters are arranged in functional groups. In order to -set a parameter, the name of the group, the name of the parameter, and -the parameter value(s) have to be specified, e.g. -.nf -.sp - sig level debug -.sp -.fi -decreases the logging threshold for messages related to signaling to the -\fBdebug\fP level. The following options are recognized: -.IP \fBdebug\ dump\ \fIpath\fP -Specifies the directory to which \fBatmsigd\fP will write status and trace -dumps. If tracing is not yet enabled, the trace size is automatically -set to a (small) default value. -.IP \fBdebug\ level\ \fIlevel\fP -Sets the default debug level to \fIlevel\fP. \fIlevel\fP can be any of -\fBdebug\fP, \fBinfo\fP, \fBwarn\fP, \fBerror\fP, and \fBfatal\fP. Only -messages with the same or a higher priority than the debug level are printed. -Note that -the command-line option \fB\-d\fP generates even more output (e.g. hexdumps -of all packets passing between \fBatmsigd\fP and the network) than -\fBdebug level debug\fP. -.IP \fBdebug\ log\ \fIpath\fP -Specifies the file to which \fBatmsigd\fP writes logging messages. When -using the special file name \fBsyslog\fP, messages are send to the -system logger instead. Log messages are written to standard output if no log -file is specified. Writing to standard output can also be explicitly requested -by using the special file name \fBstderr\fP. -.IP \fBdebug\ trace\ \fP[\fInumber\fP] -Enables tracing and optionally sets the number of entries that should be -kept in the trace buffer. A (small) default is used if the number is -omitted. -.IP \fBio\ level\ \fIlevel\fP -Sets the debug level for IO-related messages to \fIlevel\fP. -.IP \fBio\ qos\ \fIqos\fP -Configures the signaling VC to use the specified QOS (see qos(7) for the -syntax). By default, UBR at link speed is used on the signaling VC. -.IP \fBio\ vc\ \fP[\fIitf\fB.\fP]\fIvpi\fB.\fIvci\fP -Uses the specified VC for signaling messages instead of the usual 0.0.5. -.IP \fBsaal\ level\ \fIlevel\fP -Sets the debug level for messages related to SAAL (i.e. SSCF and SSCOP) to -\fIlevel\fP. -.IP \fBsig\ level\ \fIlevel\fP -Sets the debug level for messages related to signaling (Q.2931 or ISP) to -\fIlevel\fP. -.IP \fBsig\ mode\ \fImode\fP -Set the mode of operation. The following modes are available: \fBuser\fP for -the user side, \fBnetwork\fP for the network side, and \fBswitch\fP for -operation in a switch. The default behaviour is \fBuser\fP. -.IP \fBsig\ uni30\fP -Use UNI 3.0 signaling. This option is not implemented. The signaling mode -must be configured at compile time. -.IP \fBsig\ uni31\fP -Use UNI 3.1 signaling. This option is not implemented. The signaling mode -must be configured at compile time. -.IP \fBsig\ vpci\ \fIvpci\fB\ itf\ \fIitf\fP -Sets up a very simplistic type of routing. All calls with VPCI values -equal to or greater than \fIvpci\fP will be routed to \fIitf\fP, and their -VPI values will be set to the signaled VPCI minus \fIvpci\fP. Multiple -\fBsig vpci\fP entries can be used to support an arbitrary number of -interfaces. -Example: with \fBsig vpci 4 itf 1\fP, a call signaled for -VPCI/VCI 0.x is routed to 0.0.x, a call signaled for 6.y is routed to -1.2.y, etc. -.IP \fBpolicy\ level\ \fIlevel\fP -Sets the debug level for messages related to policy decisions to \fIlevel\fP. -.IP \fBpolicy\ \fIdecision\ direction\ address\fP -Takes the specified \fIdecision\fP for calls from or to \fIaddress\fP. -\fIdecision\fP can be either \fBallow\fP or \fBreject\fP. \fIdirection\fP is -either \fBfrom\fP or \fBto\fP. The \fIaddress\fP may be wildcarded by -prepending a slash and the number of significant bits (NSAP) or digits (E.164). -The rules are searched in the order in which they appear in \fBatmsigd.conf\fP -until the first match. If no rule matches, the call is allowed. -.P -When setting multiple parameters in the same group, the group name doesn't -have to be repeated if it is followed by the parameters in curly braces. -Example: -.nf -.sp - debug { - level warn - dump /var/tmp - log syslog - trace 100 - } -.sp -.fi -.P -Line breaks can be inserted in \fBatmsigd.conf\fP wherever spaces or tabs -are allowed. Everything between a `#' and the end of the line is considered -a comment. The `#' character cannot be escaped. -.P -If an option is specified in \fBatmsigd.conf\fP and on the command -line, the command line has priority. -.COMPATIBILITY -For historical reasons, synonyms (e.g. abbreviated or long forms) exist for -most keywords. Future versions of \fBatmsigd\fP will only recognize the -syntax described on this man page. -.SH AUTHOR -Werner Almesberger, EPFL ICA -.SH "SEE ALSO" -atmsigd(8), qos(7), syslogd(8) -.\"{{{}}} diff -ur --new-file old/atm/sigd.old/cfg.l new/atm/sigd.old/cfg.l --- old/atm/sigd.old/cfg.l Thu Aug 20 18:04:24 1998 +++ new/atm/sigd.old/cfg.l Thu Jan 1 01:00:00 1970 @@ -1,100 +0,0 @@ -%{ -/* cfg.l - configuration language */ - -/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ - - -#include -#include -#include -#include - -#include "atm.h" - -#include "y.tab.h" - - -static int lineno = 1; -static int token; /* f@#%ing flex doesn't grok return after BEGIN */ - - -void yyerror(const char *s); -void yywarn(const char *s); - -%} - -%s N -%s P - -%% - BEGIN(N); - -level return TOK_LEVEL; -debug return TOK_DEBUG; -info return TOK_INFO; -warn return TOK_WARN; -error return TOK_ERROR; -fatal return TOK_FATAL; -sig return TOK_SIG; -uni30 return TOK_UNI30; -uni31 return TOK_UNI31; -uni40 return TOK_UNI40; -[qQ].2963.1 return TOK_Q2963_1; -mode return TOK_MODE; -user return TOK_USER; -network return TOK_NET; -switch return TOK_SWITCH; -saal return TOK_SAAL; -vc return TOK_VC; -io return TOK_IO; -itf return TOK_ITF; -vpci return TOK_VPCI; -pcr return TOK_PCR; -policy return TOK_POLICY; -allow return TOK_ALLOW; -reject return TOK_REJECT; -dump { BEGIN(P); - token = TOK_DUMP_DIR; } -log { BEGIN(P); - token = TOK_LOGFILE; } -qos { BEGIN(P); /* syntacticly close to a path */ - token = TOK_QOS; } -from { BEGIN(P); /* syntacticly close to a path */ - token = TOK_FROM; } -to { BEGIN(P); /* syntacticly close to a path */ - token = TOK_TO; } -trace return TOK_TRACE; -[0-9]+ { char *end; - yylval.num = strtoul(yytext,&end,10); - if (*end) yyerror("invalid number"); - return TOK_NUMBER; } -[0-9]+\.[0-9]+(\.[0-9]+)? { - if (text2atm(yytext,(struct sockaddr *) &yylval.pvc, - sizeof(yylval.pvc),T2A_PVC) < 0) - yyerror("invalid signaling channel"); - return TOK_PVC; - } -

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