diff -ur --new-file old/linux/CREDITS new/linux/CREDITS --- old/linux/CREDITS Mon Aug 12 12:33:48 1996 +++ new/linux/CREDITS Fri Nov 1 12:04:07 1996 @@ -53,7 +53,8 @@ S: Calgary, Alberta, Canada N: Ralf Baechle -E: ralf@waldorf-gmbh.de +E: ralf@gnu.ai.mit.edu +P: 1024/AF7B30C1 CF 97 C2 CC 6D AE A7 FE C8 BA 9C FC 88 DE 32 C3 D: Linux/MIPS port D: Linux/68k hacker S: Hauptstrasse 19 @@ -388,10 +389,10 @@ D: Debugging: SCSI code; Cyclades serial driver; APM driver D: Debugging: XFree86 Mach 32 server, accelerated server code -N: Juergen Fischer -E: fischer@server.et-inf.fho-emden.de +N: Jürgen Fischer +E: fischer@et-inf.fho-emden.de (=?iso-8859-1?q?J=FCrgen?= Fischer) D: Author of Adaptec AHA-152x scsi driver -S: Schulstrasse 18 +S: Schulstraße 18 S: 26506 Norden S: Germany @@ -532,9 +533,7 @@ N: Kai Harrekilde-Petersen E: khp@dolphinics.no -W: http://www.dolphinics.no/~khp -P: 1024/D2CDF895 F6 33 E0 4E 01 17 85 8C 4F 7F 1F F8 14 E7 86 1D -D: ftape-HOWTO, i82078 fdc detection code, ftape maintainer. +D: Original author of the ftape-HOWTO, i82078 fdc detection code. S: Peder Holters vei 13 S: 1168 Oslo S: Norway @@ -668,7 +667,7 @@ S: USA N: Bernhard Kaindl -E: bartelt@computerhaus.at +E: edv@bartelt.via.at D: Author of a menu based configuration tool, kmenu, which D: is the predecessor of 'make menuconfig' and 'make xconfig'. S: Tallak 95 @@ -804,10 +803,10 @@ S: Canada K2H 6S3 N: Warner Losh -E: imp@boulder.parcplace.com -D: Provided OI/OB for Linux, general hacker -S: 4909 Pearl East Circle, Suite 200 -S: Boulder, Colorado 80303 +E: imp@village.org +D: Linux/MIPS Deskstation support, Provided OI/OB for Linux +S: 8786 Niwot Rd +S: Niwot, CO 80503 S: USA N: H.J. Lu @@ -927,7 +926,7 @@ S: USA N: William (Bill) Metzenthen -E: billm@jacobi.maths.monash.edu.au +E: billm@suburbia.net D: Author of the FPU emulator. D: Minor kernel hacker for other lost causes (Hercules mono, etc). S: 22 Parker Street @@ -1089,13 +1088,12 @@ D: The Linux Support Team Erlangen N: Daniel Quinlan -E: Daniel.Quinlan@linux.org -W: http://www.bucknell.edu/~quinlan +E: quinlan@pathname.com +W: http://www.pathname.com/~quinlan/ D: FSSTND coordinator; FHS editor D: random Linux documentation, patches, and hacks -S: Box C-5083 -S: Bucknell University -S: Lewisburg, Pennsylvania 17837 +S: 4390 Albany Dr. #41A +S: San Jose, California 95129 S: USA N: Eric S. Raymond @@ -1273,11 +1271,11 @@ S: USA N: Leo Spiekman -E: spiekman@et.tudelft.nl -W: http://dutettk.et.tudelft.nl/~spiekman +E: leo@netlabs.net +W: http://www.netlabs.net/hp/leo/ D: Optics Storage 8000AT cdrom driver -S: Utrecht -S: The Netherlands +S: Cliffwood, NJ 07721 +S: USA N: Henrik Storner E: storner@osiris.ping.dk @@ -1409,6 +1407,10 @@ N: Jeffrey A. Uphoff E: juphoff@nrao.edu E: jeff.uphoff@linux.org +P: 1024/9ED505C5 D7 BB CA AA 10 45 40 1B 16 19 0A C0 38 A0 3E CB +D: Linux Security/Alert mailing lists' moderator/maintainer. +D: NSM (rpc.statd) developer. +D: PAM S/Key module developer. D: 'dip' contributor. D: AIPS port, astronomical community support. S: National Radio Astronomy Observatory @@ -1561,6 +1563,7 @@ N: Leonard N. Zubkoff E: lnz@dandelion.com +W: http://www.dandelion.com/Linux/ D: BusLogic SCSI driver D: Miscellaneous kernel fixes S: 3078 Sulphur Spring Court diff -ur --new-file old/linux/Documentation/Changes new/linux/Documentation/Changes --- old/linux/Documentation/Changes Mon Aug 19 08:45:51 1996 +++ new/linux/Documentation/Changes Thu Oct 17 15:20:29 1996 @@ -1,7 +1,7 @@ Intro ===== -This document contains a list of the latest releases of the most +This document contains a list of the latest stable releases of the most important packages for Linux as well as instructions for newcomers to the 2.0.x series of kernels. By glancing through it, you should be able to find out what you need to upgrade in order to successfully run @@ -20,20 +20,21 @@ Para aquellos que prefieran una version en castellano de este documento, consultad la traduccion de Alfredo Sanjuan en -http://slug.ctv.es/~alfredo/Cambios.html. +http://slug.ctv.es/~alfredo/Cambios.html (Spanish translation). Akik magyarul szeretnenek olvasni az uj kernellel kapcsolatos valtozasokrol, az alabbi cimen megtalaljak Nyitrai Tamas forditasat: -http://www.datanet.hu/generations/linux/newkernel.html. +http://www.datanet.hu/generations/linux/newkernel.html (Hungarian +translation). - Tamas also maintains a version of this file in English at -http://www.datanet.hu/generations/linux/Changes.html. + Tamas also maintains a version of this file at +http://www.datanet.hu/generations/linux/Changes.html (English version). For people who prefer Japanese (thanks to Mitsuhiro Kojima): Kono bunshou no nihongo ban wa http://jf.gee.kyoto-u.ac.jp/JF/v2.0/Changes-2.0.html ni arimasu. -Last updated: August 18, 1996. +Last updated: October 12, 1996. Current Author: Chris Ricker (gt1355b@prism.gatech.edu). Current Releases @@ -43,8 +44,8 @@ - PPP daemon 2.2.0f - Dynamic linker (ld.so) 1.7.14 - GNU CC 2.7.2 -- Binutils 2.6.0.14 -- Linux C Library Stable: 5.2.18, Beta: 5.3.12 +- Binutils 2.7.0.3 +- Linux C Library Stable: 5.2.18, Beta: 5.3.12 / 5.4.7 - Linux C++ Library 2.7.1.4 - Termcap 2.0.8 - Procps 1.01 @@ -72,6 +73,15 @@ distributions like Caldera). If you're running one of these, edit /etc/sysconfig/network-scripts/ifup-lo, changing the line `route add -net $(IPADDR)' to `route add -net 127.0.0.0' and you should be fine. +The new Red Hat 4.0 (Colgate) distribution does not have this problem. + + People have also reported problems due to the naming of the dummy +network interface driver. If the dummy driver is compiled into the +kernel, its name is "dummy." If the dummy driver is compiled as a +module, its name is "dummy0." Furthermore, more than one dummy driver +can be loaded if compiled as a module. Each subsequent loading of the +driver adds a new dummy interface whose name is incremented by one +("dummy1," "dummy2," etc.). Booting Changes =============== @@ -94,6 +104,14 @@ this option in their lilo.config file. Comment it out and re-run lilo if you need ramdisks. + The definition of SIOCSARP in /usr/include/linux/sockios.h was +changed. This means bootpd has to be re-compiled in order to work. + + The kernel reboot method is now, by default, a cold reboot so that +the kernel will work on systems that don't support other methods. If +you want to be able to do a warm reboot, add a reboot=warm option to +lilo.conf. + The Linux C Library =================== @@ -116,7 +134,9 @@ Also, the libc-5.3.x line has a known security hole relating to rlogin. Libc-5.3.12 fixes this, so if you're going to run an -experimental libc, be sure to upgrade to 5.3.12. +experimental libc, be sure to upgrade to 5.3.12. Libc-5.4.7 is +currently available as well, but it has a few problems of its own (a +few missing symbols, for example, that will break existing apps). If you're getting an error message that is something to the effect of @@ -152,7 +172,7 @@ On the other hand, if you're using a gcc patched for Pentium optimization and are getting these errors, downgrade to a standard GNU -gcc before assuming your hardware (or the kernel) is to blame.. +gcc before assuming your hardware (or the kernel) is to blame. On a related note, if you get random OOPses that don't seem to be related to anything and you have a motherboard with APM support, try @@ -194,6 +214,15 @@ about going completely modular and then forget to compile ext2fs support and ide/SCSI drive support into your kernel ;-). +Kernel messages +=============== + + Kernel messages without a specific log level use the kernel's +default log level. In 1.2 kernels, the default log level was 6 +(information), while in 2.0.x kernels it is 4 (warning). Adjust your +configuration of syslogd appropriately (or edit printk.c in the kernel +source ;-). + PPP driver ========== @@ -349,8 +378,10 @@ Frame relay support for Linux is now available as well. Currently, only Sangoma cards are supported, but the interface is such that others will be as drivers become available. To use this, grab -ftp://ftp.invlogic.com/linux/frad-0.15.tgz and -ftp://ftp.invlogic.com/linux/routing.tgz. +ftp://linux.invlogic.com/pub/fr/frad-0.15.tgz (soon to be +frad-0.20.tgz). Another package of interest is +ftp://linux.invlogic.com/pub/routing/routing.tgz (which allows Linux to +make routing decisions based on packet source). Networking ========== @@ -547,7 +578,7 @@ e2fsprogs 1.02 will work with the latest kernels, but it cannot be compiled on them. If you need (or want) to compile your own copy, you'll need to get the latest version, currently available at -ftp://tsx-11.mit.edu/pub/linux/packages/ext2fs/e2fsprogs-1.04.tar.gz. +ftp://tsx-11.mit.edu/pub/linux/packages/ext2fs/e2fsprogs-1.06.tar.gz. How to know the version of the installed programs ************************************************* @@ -571,9 +602,9 @@ Binutils ======== -ftp://sunsite.unc.edu/pub/Linux/GCC/binutils-2.6.0.14.bin.tar.gz +ftp://sunsite.unc.edu/pub/Linux/GCC/binutils-2.7.0.3.bin.tar.gz Installation notes: -ftp://sunsite.unc.edu/pub/Linux/GCC/release.binutils-2.6.0.14 +ftp://sunsite.unc.edu/pub/Linux/GCC/release.binutils-2.7.0.3 GNU CC ====== @@ -678,11 +709,14 @@ ftp://ftp.redhat.com/pub/current/i386/updates/2.0-kernel/ and its mirrors. - For others, especially those of you running Slackware 3.0, David -Bourgin has put together a Slackware-compatible package of everything + For others, David Bourgin has put together a package of everything necessary to quickly and easily upgrade to 2.0.x. See ftp://ftp.wsc.com/pub/freeware/linux/update.linux/ for more information -and the files. +and the files. This package also includes many bug-fixes, including +one for a recently discovered bug in sendmail-8.7.5 (just look at +/pub/freeware/linux/update.linux/updat2-0.addon1/sendmail-8.7.5a.tar.gz +if you only need the bug fix). There's also an alternate lightweight +termcap in the same directory that works well for many people. Please send info about any other packages that 2.0.x "broke" or about any new features of 2.0.x that require extra or new packages for use to diff -ur --new-file old/linux/Documentation/Configure.help new/linux/Documentation/Configure.help --- old/linux/Documentation/Configure.help Sat Nov 16 00:48:03 1996 +++ new/linux/Documentation/Configure.help Sat Nov 16 00:48:28 1996 @@ -3,6 +3,13 @@ # This version of the Linux kernel configuration help texts # corresponds to the kernel versions 2.0.x. # +# International versions of this file available on the WWW: +# - http://jf.gee.kyoto-u.ac.jp/JF/JF-ftp/euc/Configure.help.euc +# is a Japanese translation, maintained by Tetsuyasu YAMADA +# (tetsu@cauchy.nslab.ntt.jp). +# - http://nevod.perm.su/service/linux/doc/kernel/Configure.help +# is a Russian translation, maintained by kaf@linux.nevod.perm.su. +# # Information about what a kernel is, what it does, how to patch and # compile it and much more is contained in the Kernel-HOWTO, available # via ftp (user: anonymous) from sunsite.unc.edu in the directory @@ -107,18 +114,15 @@ Loop device support CONFIG_BLK_DEV_LOOP - Enabling this option will allow you to mount a file as a file system. - This is useful if you want to check an ISO9660 file system before - burning the CD, or want to use floppy images without first writing - them to floppy. - This option also allows one to mount a filesystem with encryption. - To use these features, you need a recent version of mount, such as - the one found at ftp.win.tue.nl:/pub/linux/util/mount-2.5X.tar.gz. - If you want to use encryption, you might also be interested in the - (old) DES package ftp.funet.fi:/pub/OS/Linux/BETA/loop/des.1.tar.gz. - Note that this loop device has nothing to do with the loopback - device used for network connections from the machine to itself. - Most users will answer N here. + Enabling this option will allow you to mount a file as a file + system. This is useful if you want to check an ISO9660 file system + before burning the CD, or want to use floppy images without first + writing them to floppy. This option also allows one to mount a + filesystem with encryption. To use these features, you need a + recent version of mount (check the file Documentation/Changes for + location and latest version). Note that this loop device has + nothing to do with the loopback device used for network connections + from the machine to itself. Most users will answer N here. Enhanced IDE/MFM/RLL disk/cdrom/tape support CONFIG_BLK_DEV_IDE @@ -202,22 +206,23 @@ "SiS" chipset. Unfortunately, it has a number of rather nasty design flaws that can cause severe data corruption under many common conditions. Say Y here to include code which tries to automatically - detect and correct the problems under Linux. - This option also enables access to the secondary IDE ports in some - CMD640 based systems. This driver will work automatically in PCI - based systems (most new systems have PCI slots). But if your system - uses VESA local bus (VLB) instead of PCI, you must also supply a - kernel boot parameter to enable the CMD640 bugfix/support: - "ide0=cmd640_vlb" The CMD640 chip is also used on add-in cards by - Acculogic, and on the "CSA-6400E PCI to IDE controller" that some - people have. If unsure, say Y. + detect and correct the problems under Linux. This option also + enables access to the secondary IDE ports in some CMD640 based + systems. This driver will work automatically in PCI based systems + (most new systems have PCI slots). But if your system uses VESA + local bus (VLB) instead of PCI, you must also supply a kernel boot + parameter to enable the CMD640 bugfix/support: "ide0=cmd640_vlb" The + CMD640 chip is also used on add-in cards by Acculogic, and on the + "CSA-6400E PCI to IDE controller" that some people have. For + details, read Documentation/ide.txt. If unsure, say Y. CMD640 enhanced support CONFIG_BLK_DEV_CMD640_ENHANCED - This option includes support for setting/autotuning PIO modes - and prefetch on CMD640 IDE interfaces. If you have a CMD640 IDE - interface and your BIOS does not already do this for you, then say Y - here. Otherwise say N. + This option includes support for setting/autotuning PIO modes and + prefetch on CMD640 IDE interfaces. For details, read + Documentation/ide.txt. If you have a CMD640 IDE interface and your + BIOS does not already do this for you, then say Y here. Otherwise + say N. RZ1000 chipset bugfix/support CONFIG_BLK_DEV_RZ1000 @@ -267,12 +272,13 @@ of the UMC-8672, and permits faster I/O speeds to be set as well. See the Documentation/ide.txt and umc8672.c files for more info. -ALI M1439/M1445 support +ALI M14xx support CONFIG_BLK_DEV_ALI14XX This driver is enabled at runtime using the "ide0=ali14xx" kernel boot parameter. It enables support for the secondary IDE interface - of the chipset, and permits faster I/O speeds to be set as well. - See the Documentation/ide.txt and ali14xx.c files for more info. + of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster + I/O speeds to be set as well. See the Documentation/ide.txt and + ali14xx.c files for more info. PROMISE DC4030 support (EXPERIMENTAL) CONFIG_BLK_DEV_PROMISE @@ -551,15 +557,14 @@ certain BIOSes if your computer uses a PCI bus system. This is recommended; say Y. -Intel 430FX (Triton) chipset DMA support +Intel 82371 PIIX (Triton I/II) DMA support CONFIG_BLK_DEV_TRITON If your PCI system uses an IDE harddrive (as opposed to SCSI, say) and includes the Intel 430FX PCI Triton chipset, you will want to enable this option to allow use of bus-mastering DMA data transfers. - Read the comments at the beginning of drivers/block/triton.c. The - hdparm utility can be obtained via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/. It is safe - to say Y. + Read the comments at the beginning of drivers/block/triton.c. Check + the file Documentation/Changes for location and latest version of + the hdparm utility. It is safe to say Y to this question. System V IPC CONFIG_SYSVIPC @@ -583,56 +588,55 @@ executables used across different architectures and operating systems. This option will enable your kernel to run ELF binaries and enlarge it by about 2kB. ELF support under Linux is quickly - replacing the traditional Linux a.out format because it is portable - (this does *not* mean that you will be able to run executables from - different architectures or operating systems!) and makes building - run-time libraries very easy. Many new executables are distributed - solely in ELF format. You definitely want to say Y here. Information - about ELF is on the WWW at + replacing the traditional Linux a.out formats (QMAGIC and ZMAGIC) + because it is portable (this does *not* mean that you will be able + to run executables from different architectures or operating + systems!) and makes building run-time libraries very easy. Many new + executables are distributed solely in ELF format. You definitely + want to say Y here. Information about ELF is on the WWW at http://www.sjc.ox.ac.uk/users/barlow/elf-howto.html (To browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, netscape or Mosaic). If you find that after upgrading to Linux kernel 1.3 and saying Y here, you still can't run any ELF binaries (they just crash), then you'll have to - install the newest ELF runtime libraries, including ld.so (available - via ftp (user: anonymous) from - tsx-11.mit.edu:/pub/linux/packages/GCC). Also note that ELF binary - support was broken in kernel versions 1.3.0 - 1.3.2. Either use a - newer 1.3 kernel or one of the stable 1.2 versions. If you want to - compile this as a module ( = code which can be inserted in and - removed from the running kernel whenever you want), say M here and - read Documentation/modules.txt. Saying M or N here is dangerous - because some crucial programs on your system might be in ELF - format. + install the newest ELF runtime libraries, including ld.so (check the + file Documentation/Changes for location and latest version). If you + want to compile this as a module ( = code which can be inserted in + and removed from the running kernel whenever you want), say M here + and read Documentation/modules.txt. Saying M or N here is dangerous + because some crucial programs on your system might be in ELF format. Compile kernel as ELF - if your GCC is ELF-GCC CONFIG_KERNEL_ELF The gcc version 2.7.0 and newer produces the new ELF binary format - as default. If you have such a compiler (try "gcc -v"), say Y - here, otherwise N. + as default. If you have such a compiler (try "gcc -v"), say Y here, + otherwise N. It is possible, albeit almost pointless, to compile the kernel in - a.out format even if your compiler produces ELF as default. For - that, you would have to say N here and change the variables LD and - CC in the toplevel Makefile. Similarly, if your compiler produces - a.out by default but is able to produce ELF, you can compile the - kernel in ELF by saying Y here and editing the variables CC - and LD in the toplevel Makefile. + a.out (i.e. QMAGIC) format even if your compiler produces ELF as + default. For that, you would have to say N here and change the + variables LD and CC in the toplevel Makefile. Similarly, if your + compiler produces a.out by default but is able to produce ELF, you + can compile the kernel in ELF by saying Y here and editing the + variables CC and LD in the toplevel Makefile. Kernel support for A.OUT binaries CONFIG_BINFMT_AOUT - A.OUT (Assembler.OUTput) format is a format for libraries and - executables used in the earliest versions of UNIX. Linux used this - format until it was replaced with the ELF format. - As more and more programs are converted to ELF, the use for A.OUT + A.out (Assembler.OUTput) is a set of formats for libraries and + executables used in the earliest versions of UNIX. Linux used the + a.out formats QMAGIC and ZMAGIC until they were replaced with the + ELF format. + As more and more programs are converted to ELF, the use for a.out will gradually diminish. If you disable this option it will reduce your kernel by one page. This is not much and by itself does not - warrant removing support. However its removal is a good idea when - you wish to ensure that absolutely none of your programs will use - this older executable format. If you don't know what to answer at - this point then answer Y. You may answer M for module support and - later load the module when you find a program which needs a.out - format. Saying M or N here is dangerous, because some crucial - programs on your system might be in A.OUT format. + warrant removing support. However its removal is a good idea if you + wish to ensure that absolutely none of your programs will use this + older executable format. If you don't know what to answer at this + point then answer Y. If someone told you "You need a kernel with + QMAGIC support" then you'll have to say Y here. You may answer M + to compile a.out support as a module and later load the module when + you want to use a program or library in a.out format. Saying M or N + here is dangerous though, because some crucial programs on your + system might still be in A.OUT format. Kernel support for JAVA binaries CONFIG_BINFMT_JAVA @@ -654,7 +658,7 @@ warrant removing support. However its removal is a good idea if you do not have the JDK installed. If you don't know what to answer at this point then answer Y. You may answer M for module support and - later load the module when you install the JDK or find a interesting + later load the module when you install the JDK or find an interesting Java program that you can't live without. Processor type @@ -697,7 +701,7 @@ opposed to mips-linux-gcc or mips-linuxelf-gcc), say Y here, otherwise N. Most MIPS machines use little-endian code, but it might be necessary to run older Mips systems, such as the Sony News and - MIPS RC3xxx in big endian mode. + MIPS RC3xxx, in big endian mode. Enable loadable module support CONFIG_MODULES @@ -715,14 +719,13 @@ kernel. Enabling this option makes it possible, and safe, to use the same modules even after compiling a new kernel; this requires the program modprobe. All the software needed for module support is in - the modules package in sunsite.unc.edu:/pub/Linux/kernel, available - via ftp (user: anonymous) to be extracted with "tar xzvf filename". - NOTE: if you say Y here but don't have the program genksyms (which - is also contained in the above mentioned modules package), then the - building of your kernel will fail. - If you are going to use modules that are generated from non-kernel - sources, you would benefit from this option. Otherwise it's not that - important. So, N ought to be a safe bet. + the modules package (check the file Documentation/Changes for + location and latest version). NOTE: if you say Y here but don't + have the program genksyms (which is also contained in the above + mentioned modules package), then the building of your kernel will + fail. If you are going to use modules that are generated from + non-kernel sources, you would benefit from this option. Otherwise + it's not that important. So, N ought to be a safe bet. Kernel daemon support CONFIG_KERNELD @@ -812,11 +815,11 @@ CONFIG_IP_MULTICAST This is code for addressing several networked computers at once, enlarging your kernel by about 2 kB. If you are using gated, the - daemon that updates your computer's routing tables, and are using - RIP2 or OSPF you will need to have this option compiled in. You also - need multicasting if you intend to participate in the MBONE, a high - bandwidth network on top of the internet which carries audio and - video broadcasts. More information about the MBONE is on the WWW at + daemon that updates your computer's routing tables, you will need to + have this option compiled in. You also need multicasting if you + intend to participate in the MBONE, a high bandwidth network on top + of the internet which carries audio and video broadcasts. More + information about the MBONE is on the WWW at http://www.best.com/~prince/techinfo/mbone.html (to browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, netscape or Mosaic). Information about the @@ -838,19 +841,20 @@ about 2kB. You may need to read the FIREWALL-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you will need the - ipfwadm tool (available via ftp (user: anonymous) from ftp.xos.nl) - to allow selective blocking of internet traffic based - on type, origin and destination. You need to enable IP firewalling - in order to be able to use IP masquerading (i.e. local computers can - chat with an outside host, but that outside host is made to think - that it is talking to the firewall box. Makes the local network - completely invisible and avoids the need to allocate valid IP host - addresses for the machines on the local net) or to use the IP packet - accounting to see what is using all your network bandwidth. - This option is also needed when you want to enable the transparent - proxying support (makes the computers on the local network think - they're talking to a remote computer, while in reality the traffic - is redirected by your Linux firewall to a local proxy server). + ipfwadm tool (check the file Documentation/Changes for location and + latest version) to allow selective blocking of internet traffic + based on type, origin and destination. You need to enable IP + firewalling in order to be able to use IP masquerading (i.e. local + computers can chat with an outside host, but that outside host is + made to think that it is talking to the firewall box. Makes the + local network completely invisible and avoids the need to allocate + valid IP host addresses for the machines on the local net) or to use + the IP packet accounting to see what is using all your network + bandwidth. This option is also needed when you want to enable the + transparent proxying support (makes the computers on the local + network think they're talking to a remote computer, while in reality + the traffic is redirected by your Linux firewall to a local proxy + server). IP: accounting CONFIG_IP_ACCT @@ -861,7 +865,8 @@ firewalling. The data is accessible with "cat /proc/net/ip_acct", so you want to say Y to the /proc filesystem below, if you say Y here. To specify what exactly should be recorded, you need the tool - ipfwadm (available from ftp.xos.nl if you don't have a copy already). + ipfwadm (check the file Documentation/Changes for location and + latest version). IP: tunneling CONFIG_NET_IPIP @@ -975,17 +980,25 @@ Reverse ARP CONFIG_INET_RARP - Since you asked: if there's a diskless machine on your local network - that know its hardware ethernet address but doesn't know its IP - address upon startup, it can send out a Reverse Address Resolution - Protocol request to find out its own IP address. If you want your - Linux box to be able to *answer* such requests, say Y here; you'd - have to run the program rarp ("man rarp") on your box. Superior - solutions to the same problem are given by the protocols BOOTP and - DHCP. If you want to compile RARP support as a module ( = code which - can be inserted in and removed from the running kernel whenever you - want), say M here and read Documentation/modules.txt. If you don't - understand a word, say N and rest in peace. + Since you asked: if there are (usually diskless or portable) + machines on your local network that know their hardware ethernet + addresses but don't know their IP addresses upon startup, they can + send out a Reverse Address Resolution Protocol (RARP) request to + find out their own IP addresses. Diskless Sun 3 machines use this + procedure at boot time. If you want your Linux box to be able to + *answer* such requests, say Y here; you'd have to run the program + rarp ("man rarp") on your box. If you actually want to use a + diskless Sun 3 machine as an Xterminal to Linux, say Y here and + fetch Linux-Xkernel from + ftp://sunsite.unc.edu/pub/Linux/system/Network/boot.net/. Superior + solutions to the problem of booting and configuring machines over a + net connection are given by the protocol BOOTP and its successor + DHCP. See the DHCP FAQ + http://web.syr.edu/~jmwobus/comfaqs/dhcp.faq.html for details. If + you want to compile RARP support as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. If you don't + understand a word of the above, say N and rest in peace. Assume subnets are local CONFIG_INET_SNARL @@ -1295,6 +1308,13 @@ extended debugging may create certain race conditions itself. Enable this ONLY if you suspect problems with the driver. +IDT 77201 (NICStAR) +CONFIG_ATM_NICSTAR + Driver for the IDT 77201 (NICStAR) ATM adapter. Written by Matt Welsh + and Stuart Daniel . Note that this driver + currently only works with native ATM (i.e. not with IP over ATM or + LANE). + SCSI support? CONFIG_SCSI If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or @@ -1346,16 +1366,19 @@ SCSI generic support CONFIG_CHR_DEV_SG If you want to use SCSI scanners, synthesizers or CD-writers or just - about anything having "SCSI" in its name other than disks, CDROMs or - tapes, say Y here. Those won't be supported by the kernel directly, - so you need some additional software which knows how to talk to - these things using the SCSI protocol. Chances are that you'll have - to write that software yourself, so have a look at the SCSI-HOWTO - and at the SCSI-Programming-HOWTO, both available via ftp (user: - anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to - compile this as a module ( = code which can be inserted in and - removed from the running kernel whenever you want), say M here and - read Documentation/modules.txt and Documentation/scsi.txt. + about anything having "SCSI" in its name other than harddisks, + CDROMs or tapes, say Y here. Those won't be supported by the kernel + directly, so you need some additional software which knows how to + talk to these devices using the SCSI protocol. For CD-writers, you + would need the program cdwrite, available via ftp (user: anonymous) + from sunsite.unc.edu:/pub/Linux/utils/disk-management; for other + devices, it's possible that you'll have to write the driver software + yourself, so have a look at the SCSI-HOWTO and at the + SCSI-Programming-HOWTO, both available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this + as a module ( = code which can be inserted in and removed from the + running kernel whenever you want), say M here and read + Documentation/modules.txt and Documentation/scsi.txt. Probe all LUNs on each SCSI device CONFIG_SCSI_MULTI_LUN @@ -1467,9 +1490,9 @@ CONFIG_SCSI_EATA_PIO This driver supports all EATA-PIO protocol compliant SCSI Host Adaptors like the DPT PM2001 and the PM2012A. EATA-DMA compliant - HBAs can also use this driver but are discouraged from doing so, - since this driver only supports harddisks and lacks numerous - features. You might want to have a look at the SCSI-HOWTO, + host adaptors could also use this driver but are discouraged from + doing so, since this driver only supports harddisks and lacks + numerous features. You might want to have a look at the SCSI-HOWTO, available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the @@ -1595,10 +1618,10 @@ not allow targets to disconnect CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT This option is only provided for safety if you suspect some scsi - device of yours to not support properly this feature. In that case, - you would say Y here. In general however, to not allow targets to - disconnect is not reasonable if there is more than 1 device on a - scsi bus. The normal answer therefore is N. + device of yours to not support properly the target-disconnect + feature. In that case, you would say Y here. In general however, to + not allow targets to disconnect is not reasonable if there is more + than 1 device on a scsi bus. The normal answer therefore is N. enable tagged command queuing CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE @@ -1610,8 +1633,8 @@ asks the driver to use up to 4 concurrent tagged commands for target 2 of controller 0. See the file drivers/scsi/README.ncr53c8xx for more information. - WARNING! tagged queue support requires to allow targets to - disconnect (see above). + WARNING! If you say Y here, then you have to say N to "not allow + targets to disconnect", above. The safe answer therefore is N. The normal answer therefore is Y. @@ -1834,7 +1857,8 @@ module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. If you want to use more than one dummy - device at a time, you need to compile it as a module. + device at a time, you need to compile it as a module. Instead of + 'dummy', it will they will then be called 'dummy0', 'dummy1' etc. SLIP (serial line) support CONFIG_SLIP @@ -2046,9 +2070,9 @@ low-cost way to connect to a remote internet access provider or to form a private wide area network. The one physical line from your box to the local "switch" (i.e. the entry point to the frame relay - network) can carry several logical point-to-point connections to - other computers connected to the frame relay network. For a general - explanation of the protocol, check out + network, usually at the phone company) can carry several logical + point-to-point connections to other computers connected to the frame + relay network. For a general explanation of the protocol, check out http://frame-relay.indiana.edu/4000/4000index.html on the WWW. (To browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, netscape or Mosaic.) To use frame @@ -2074,7 +2098,7 @@ Sangoma S502A FRAD support CONFIG_SDLA - Say Y here if you need a driver for the Sangoma S502A, S502E and + Say Y here if you need a driver for the Sangoma S502A, S502E, and S508 Frame Relay Access Devices. These are multi-protocol cards, but only frame relay is supported by the driver at this time. Please read Documentation/framerelay.txt. This driver is also @@ -2837,7 +2861,7 @@ This is the driver for the 'DOLPHIN' drive with a 34-pin Sony compatible interface. It also works with the Lasermate CR328A. If you have one of those, say Y. This driver does not work for the - Optics Storage 8001 drive; use the IDE-ATAPI CDROM driver for this + Optics Storage 8001 drive; use the IDE-ATAPI CDROM driver for that one. Sanyo CDR-H94A CDROM support @@ -2877,19 +2901,22 @@ CONFIG_QUOTA If you say Y here, you will be able to set per user limits for disk usage (also called diskquotas). Currently, it works only for the - ext2 filesystem; you need the software available via ftp (user: - anonymous) in - ftp.funet.fi:/pub/Linux/PEOPLE/Linus/subsystems/quota/ in order to - use it. Probably this is only useful for multi user systems. If - unsure, say N. + ext2 filesystem. You need additional software in order to use quota + support; check the file Documentation/Changes for that. Probably the + quota support is only useful for multi user systems. If unsure, say + N. Mandatory lock support CONFIG_LOCK_MANDATORY - Mandatory locking is used by some System 5 style database - applications. To use this option safely you must have newer NFS - daemons, new samba, new netatalk, new mars-nwe and other file - servers. At the time of writing none of these are available. Unless - you need this feature say N. + File locking is a system designed to prevent that several processes + write to the same file at the same time, causing data + corruption. Mandatory file locking is more secure than the usual + algorithm and is used by some Unix System 5 style database + applications. For details, read Documentation/mandatory.txt. To use + this option safely you must have newer NFS daemons, new samba, new + netatalk, new mars-nwe and other file servers. At the time of + writing none of these are available. So it's safest to say N here + unless you really know that you need this feature. Minix fs support CONFIG_MINIX_FS @@ -3075,33 +3102,35 @@ BOOTP support CONFIG_RNFS_BOOTP If you want your Linux box to mount its whole root filesystem from - some other computer over the net via NFS and you want the address - of your computer to be discovered automatically using the BOOTP - protocol (a special protocol designed for doing this job), say Y - here. In case the boot ROM of your network card was designed for - booting Linux and does BOOTP itself, providing all necessary - information on the kernel command line, you can say N here. - If unsure, say Y. Note that in case you want to use BOOTP, a BOOTP + some other computer over the net via NFS and you want the IP address + of your computer to be discovered automatically at boot time using + the BOOTP protocol (a special protocol designed for doing this job), + say Y here. In case the boot ROM of your network card was designed + for booting Linux and does BOOTP itself, providing all necessary + information on the kernel command line, you can say N here. If + unsure, say Y. Note that in case you want to use BOOTP, a BOOTP server must be operating on your network. Read Documentation/nfsroot.txt for details. RARP support CONFIG_RNFS_RARP If you want your Linux box to mount its whole root filesystem from - some other computer over the net via NFS and you want the address - of your computer to be discovered automatically using the RARP - protocol (an older protocol which is being obsoleted by BOOTP), say - Y here. Note that in case you want to use RARP, a RARP server must be - operating on your network. Read Documentation/nfsroot.txt for - details. + some other computer over the net via NFS and you want the IP address + of your computer to be discovered automatically at boot time using + the RARP protocol (an older protocol which is being obsoleted by + BOOTP), say Y here. Note that in case you want to use RARP, a RARP + server must be operating on your network. Read + Documentation/nfsroot.txt for details. ISO9660 cdrom filesystem support CONFIG_ISO9660_FS This is the standard filesystem used on CDROMs. It was previously known as "High Sierra Filesystem" and is called "hsfs" on other Unix - systems. If you have a CDROM drive and want to do more with it than - just listen to audio CDs and watch its LEDs, say Y (and read the - CDROM-HOWTO, available via ftp (user: anonymous) from + systems. The so-called Rock-Ridge extensions which allow for long + Unix filenames are also supported by this driver. If you have a + CDROM drive and want to do more with it than just listen to audio + CDs and watch its LEDs, say Y (and read the CDROM-HOWTO, available + via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your kernel by about 27 kB; otherwise say N. If you want to compile this as a module ( = code which can be inserted in and removed from the @@ -3468,18 +3497,21 @@ Advanced Power Management CONFIG_APM APM is a BIOS specification for saving power using several different - techniques. This is mostly useful for battery powered laptops with APM - compliant BIOSes. Specifically, the time will be reset after a USER - RESUME operation, the /proc/apm device will provide battery status - information, and user-space programs will receive notification of APM - "events" (e.g., battery status change). This driver does not spin down - disk drives (see hdparm(8) for that); and it doesn't turn off - VESA-compliant "green" monitors. This driver does not support the TI - 4000M TravelMate and the ACER 486/DX4/75 because they don't have - compliant BIOSes. Many "green" desktop machines also don't have - compliant BIOSes, and this driver will cause those machines to panic - during the boot phase (typically, these machines are using a data - segment of 0040, which is reserved for the Linux kernel). If you don't + techniques. This is mostly useful for battery powered laptops with + APM compliant BIOSes. Specifically, the time will be reset after a + USER RESUME operation, the /proc/apm device will provide battery + status information, and user-space programs will receive + notification of APM "events" (e.g., battery status change). This + driver does not spin down disk drives (see hdparm(8) for that); and + it doesn't turn off VESA-compliant "green" monitors. This driver + does not support the TI 4000M TravelMate and the ACER 486/DX4/75 + because they don't have compliant BIOSes. Many "green" desktop + machines also don't have compliant BIOSes, and this driver will + cause those machines to panic during the boot phase (typically, + these machines are using a data segment of 0040, which is reserved + for the Linux kernel). If you get random kernel OOPSes that don't + seem to be related to anything and you have a motherboard with APM + support, try disabling/enabling this option. Generally, if you don't have a battery in your machine, there isn't much point in using this driver. @@ -3591,12 +3623,16 @@ Berkshire Products PC Watchdog card CONFIG_PCWATCHDOG - This is the driver for the Berkshire Products PC Watchdog card. This card - simply watches your kernel to make sure it doesn't freeze, and if it does, - it resets your computer after a certain amount of time. This driver is - like the WDT501 driver but for different hardware. The PC watchdog cards - can be ordered from http://www.berkprod.com. Some example rc.local files - are available from ftp.bitgate.com. + This is the driver for the Berkshire Products PC Watchdog card. + This card simply watches your kernel to make sure it doesn't freeze, + and if it does, it resets your computer after a certain amount of + time. This driver is like the WDT501 driver but for different + hardware. The PC watchdog cards can be ordered from + http://www.berkprod.com. Some example rc.local files are available + from ftp.bitgate.com. This driver is also available as a module ( = + code which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read Documentation/modules.txt. Most people will say N. Enhanced Real Time Clock Support CONFIG_RTC @@ -3952,7 +3988,7 @@ # LocalWords: Bernd informatik rwth aachen uae affs multihosting bytecode java # LocalWords: applets applet JDK ncsa cabi SNI Alphatronix readme LANs scarab # LocalWords: winsock RNIS caltech OSPF honour Honouring Mbit Localtalk DEFRAG -# LocalWords: Camtec Berkshire +# LocalWords: localtalk download Packetwin Baycom baycom interwork ascii JNT # LocalWords: Camtec proxying indyramp defragment defragmented UDP FAS FASXX # LocalWords: FastSCSI SIO FDC qlogicfas QLogic qlogicisp setbaycom ife ee LJ # LocalWords: ethz ch Travelmates ProAudioSpectrum ProAudio SoundMan SB SBPro @@ -3962,4 +3998,5 @@ # LocalWords: Soundscape SSCAPE TRIX MediaTrix PnP Maui dsp midixx EIA getty # LocalWords: mgetty sendfax gert greenie muc lowlevel Lasermate LanManager io # LocalWords: OOPSes trackball binghamton mobileip ncr IOMAPPED settags ns ser -# LocalWords: setsync NEGO MPARITY +# LocalWords: setsync NEGO MPARITY autotuning prefetch PIIX cdwrite utils rc +# LocalWords: PCWATCHDOG berkprod bitgate diff -ur --new-file old/linux/Documentation/IO-mapping.txt new/linux/Documentation/IO-mapping.txt --- old/linux/Documentation/IO-mapping.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/IO-mapping.txt Mon Sep 30 15:05:45 1996 @@ -0,0 +1,202 @@ + +[ This is a mail-message in response to a query on IO mapping, thus the + strange format for a "document" ] + +The aha1542 is a bus-master device, and your patch makes the driver give the +controller the physical address of the buffers, which is correct on x86 +(because all bus master devices see the physical memory mappings directly). + +However, on many setups, there are actually _three_ different ways of looking +at memory addresses, and in this case we actually want the third, the +so-called "bus address". + +Essentially, the three ways of addressing memory are (this is "real memory", +ie normal RAM, see later about other details): + + - CPU untranslated. This is the "physical" address, ie physical address + 0 is what the CPU sees when it drives zeroes on the memory bus. + + - CPU translated address. This is the "virtual" address, and is + completely internal to the CPU itself with the CPU doing the appropriate + translations into "CPU untranslated". + + - bus address. This is the address of memory as seen by OTHER devices, + not the CPU. Now, in theory there could be many different bus + addresses, with each device seeing memory in some device-specific way, but + happily most hardware designers aren't actually actively trying to make + things any more complex than necessary, so you can assume that all + external hardware sees the memory the same way. + +Now, on normal PC's the bus address is exactly the same as the physical +address, and things are very simple indeed. However, they are that simple +because the memory and the devices share the same address space, and that is +not generally necessarily true on other PCI/ISA setups. + +Now, just as an example, on the PReP (PowerPC Reference Platform), the +CPU sees a memory map something like this (this is from memory): + + 0-2GB "real memory" + 2GB-3GB "system IO" (ie inb/out type accesses on x86) + 3GB-4GB "IO memory" (ie shared memory over the IO bus) + +Now, that looks simple enough. However, when you look at the same thing from +the viewpoint of the devices, you have the reverse, and the physical memory +address 0 actually shows up as address 2GB for any IO master. + +So when the CPU wants any bus master to write to physical memory 0, it +has to give the master address 0x80000000 as the memory address. + +So, for example, depending on how the kernel is actually mapped on the +PPC, you can end up with a setup like this: + + physical address: 0 + virtual address: 0xC0000000 + bus address: 0x80000000 + +where all the addresses actually point to the same thing, it's just seen +through different translations.. + +Similarly, on the alpha, the normal translation is + + physical address: 0 + virtual address: 0xfffffc0000000000 + bus address: 0x40000000 + +(but there are also alpha's where the physical address and the bus address +are the same). + +Anyway, the way to look up all these translations, you do + + #include + + phys_addr = virt_to_phys(virt_addr); + virt_addr = phys_to_virt(phys_addr); + bus_addr = virt_to_bus(virt_addr); + virt_addr = bus_to_virt(bus_addr); + +Now, when do you need these? + +You want the _virtual_ address when you are actually going to access that +pointer from the kernel. So you can have something like this: + + /* + * this is the hardware "mailbox" we use to communicate with + * the controller. The controller sees this directly. + */ + struct mailbox { + __u32 status; + __u32 bufstart; + __u32 buflen; + .. + } mbox; + + unsigned char * retbuffer; + + /* get the address from the controller */ + retbuffer = bus_to_virt(mbox.bufstart); + switch (retbuffer[0]) { + case STATUS_OK: + ... + +on the other hand, you want the bus address when you have a buffer that +you want to give to the controller: + + /* ask the controller to read the sense status into "sense_buffer" */ + mbox.bufstart = virt_to_bus(&sense_buffer); + mbox.buflen = sizeof(sense_buffer); + mbox.status = 0; + notify_controller(&mbox); + +And you generally _never_ want to use the physical address, because you can't +use that from the CPU (the CPU only uses translated virtual addresses), and +you can't use it from the bus master. + +So why do we care about the physical address at all? We do need the physical +address in some cases, it's just not very often in normal code. The physical +address is needed if you use memory mappings, for example, because the +"remap_page_range()" mm function wants the physical address of the memory to +be remapped (the memory management layer doesn't know about devices outside +the CPU, so it shouldn't need to know about "bus addresses" etc). + +NOTE NOTE NOTE! The above is only one part of the whole equation. The above +only talks about "real memory", ie CPU memory, ie RAM. + +There is a completely different type of memory too, and that's the "shared +memory" on the PCI or ISA bus. That's generally not RAM (although in the case +of a video graphics card it can be normal DRAM that is just used for a frame +buffer), but can be things like a packet buffer in a network card etc. + +This memory is called "PCI memory" or "shared memory" or "IO memory" or +whatever, and there is only one way to access it: the readb/writeb and +related functions. You should never take the address of such memory, because +there is really nothing you can do with such an address: it's not +conceptually in the same memory space as "real memory" at all, so you cannot +just dereference a pointer. (Sadly, on x86 it _is_ in the same memory space, +so on x86 it actually works to just deference a pointer, but it's not +portable). + +For such memory, you can do things like + + - reading: + /* + * read first 32 bits from ISA memory at 0xC0000, aka + * C000:0000 in DOS terms + */ + unsigned int signature = readl(0xC0000); + + - remapping and writing: + /* + * remap framebuffer PCI memory area at 0xFC000000, + * size 1MB, so that we can access it: We can directly + * access only the 640k-1MB area, so anything else + * has to be remapped. + */ + char * baseptr = ioremap(0xFC000000, 1024*1024); + + /* write a 'A' to the offset 10 of the area */ + writeb('A',baseptr+10); + + /* unmap when we unload the driver */ + iounmap(baseptr); + + - copying and clearing: + /* get the 6-byte ethernet address at ISA address E000:0040 */ + memcpy_fromio(kernel_buffer, 0xE0040, 6); + /* write a packet to the driver */ + memcpy_toio(0xE1000, skb->data, skb->len); + /* clear the frame buffer */ + memset_io(0xA0000, 0, 0x10000); + +Ok, that just about covers the basics of accessing IO portably. Questions? +Comments? You may think that all the above is overly complex, but one day you +might find yourself with a 500MHz alpha in front of you, and then you'll be +happy that your driver works ;) + +Note that kernel versions 2.0.x (and earlier) mistakenly called the +ioremap() function "vremap()". ioremap() is the proper name, but I +didn't think straight when I wrote it originally. People who have to +support both can do something like: + + /* support old naming sillyness */ + #if LINUX_VERSION_CODE < 0x020100 + #define ioremap vremap + #define iounmap vfree + #endif + +at the top of their source files, and then they can use the right names +even on 2.0.x systems. + +And the above sounds worse than it really is. Most real drivers really +don't do all that complex things (or rather: the complexity is not so +much in the actual IO accesses as in error handling and timeouts etc). +It's generally not hard to fix drivers, and in many cases the code +actually looks better afterwards: + + unsigned long signature = *(unsigned int *) 0xC0000; + vs + unsigned long signature = readl(0xC0000); + +I think the second version actually is more readable, no? + + Linus + diff -ur --new-file old/linux/Documentation/cdrom/ide-cd new/linux/Documentation/cdrom/ide-cd --- old/linux/Documentation/cdrom/ide-cd Mon Jul 8 12:01:29 1996 +++ new/linux/Documentation/cdrom/ide-cd Wed Sep 25 10:12:11 1996 @@ -182,6 +182,10 @@ If this is set, the driver will never attempt to lock the door of the drive. +CDROM_NBLOCKS_BUFFER + This sets the size of the buffer to be used for a CDROMREADAUDIO + ioctl. The default is 8. + TEST This presently enables an additional ioctl which enables a user-mode program to execute an arbitrary packet command. See the source for @@ -348,6 +352,14 @@ bug. +f. Data corruption. + + - Random data corruption was occasionally observed with the Hitachi + CDR-7730 cdrom. If you experience data corruption, using "hdx=slow" + as a command line parameter may work around the problem, at the + expense of low system performance. + + 6. cdload.c ----------- @@ -366,6 +378,7 @@ #include #include #include +#include int @@ -396,10 +409,10 @@ } /* load */ - status = ioctl (fd, CDROMLOADFROMSLOT, x_slot); + status = ioctl (fd, CDROM_SELECT_DISC, x_slot); if (status != 0) { fprintf (stderr, - "%s: CDROMLOADFROMSLOT ioctl failed for `%s': %s\n", + "%s: CDROM_SELECT_DISC ioctl failed for `%s': %s\n", program, device, strerror (errno)); exit (1); } diff -ur --new-file old/linux/Documentation/devices.tex new/linux/Documentation/devices.tex --- old/linux/Documentation/devices.tex Wed Jul 10 12:11:40 1996 +++ new/linux/Documentation/devices.tex Mon Sep 2 14:18:25 1996 @@ -1104,7 +1104,7 @@ commercial interface by P\&E. \begin{devicelist} -\major{54}{Electrocardiognosis Holter serial card} +\major{54}{}{char }{Electrocardiognosis Holter serial card} \minor{0}{/dev/holter0}{First Holter port} \minor{1}{/dev/holter1}{Second Holter port} \minor{2}{/dev/holter2}{Third Holter port} diff -ur --new-file old/linux/Documentation/digiboard.txt new/linux/Documentation/digiboard.txt --- old/linux/Documentation/digiboard.txt Thu Aug 1 14:31:04 1996 +++ new/linux/Documentation/digiboard.txt Fri Sep 20 16:00:33 1996 @@ -60,7 +60,7 @@ for c in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do name=`expr $boardnum \* 16 + $c` - mknod /dev/cud$name c $DIGI_CUMAJOR $name + mknod /dev/cud$name c $DIGICU_MAJOR $name mknod /dev/ttyD$name c $DIGI_MAJOR $name done boardnum=`expr $boardnum + 1` diff -ur --new-file old/linux/Documentation/ide.txt new/linux/Documentation/ide.txt --- old/linux/Documentation/ide.txt Wed Aug 7 11:31:22 1996 +++ new/linux/Documentation/ide.txt Wed Sep 25 10:12:11 1996 @@ -255,6 +255,8 @@ Not fully supported by all chipset types, and quite likely to cause trouble with older/odd IDE drives. + "hdx=slow" : insert a huge pause after each access to the data + port. Should be used only as a last resort. "idebus=xx" : inform IDE driver of VESA/PCI bus speed in Mhz, where "xx" is between 20 and 66 inclusive, diff -ur --new-file old/linux/Documentation/networking/arcnet.txt new/linux/Documentation/networking/arcnet.txt --- old/linux/Documentation/networking/arcnet.txt Mon May 27 13:22:44 1996 +++ new/linux/Documentation/networking/arcnet.txt Wed Sep 11 16:57:12 1996 @@ -117,12 +117,9 @@ Loadable Module Support ----------------------- -Configure and rebuild Linux. When asked, answer 'n' to "arcnet support" if +Configure and rebuild Linux. When asked, answer 'm' to "arcnet support" if you want to use the loadable module. -Actually, with Linux 1.3.24 and higher, you should answer 'm' to build the -module. - make config make dep make clean @@ -130,15 +127,16 @@ make modules If you're using a loadable module, you need to use insmod to load it, and -you need to specify various characteristics of your card on the command -line. For example: +you can specify various characteristics of your card on the command +line. (In recent versions of the driver, autoprobing is much more reliable +and works as a module, so most of this is now unnecessary.) + +For example: cd /usr/src/linux/modules - insmod arcnet.o io=0x300 irqnum=2 shmem=0xd0000 -You can also add a num=1, num=2 etc for additional arcnet cards that will -use arc1, arc2 etc for their device names (instead of the default, arc0). + insmod arcnet.o io=0x300 irq=2 shmem=0xd0000 -** NEWS FLASH! Starting with 2.30 ALPHA, the ARCnet driver can autoprobe - even as a module. So "insmod arcnet.o" by itself should work. +You can name the device using something like "device=arc1" (for a second +card) or "device=eth0" (for weird compatibility reasons) if you like. Using the Driver @@ -154,6 +152,19 @@ By the way, be sure to change all references from "eth0" to "arc0" in the HOWTOs. Remember that ARCnet isn't a "true" ethernet, and the device name is DIFFERENT. + + +Multiple Cards in One Computer +------------------------------ + +Linux has pretty good support for this now, but since I've been busy, the +ARCnet driver has somewhat suffered in this respect. For now, the easiest +way to use multiple ARCnet cards is to build it as a loadable module and +then do something like this: + insmod -o arc0 arcnet + insmod -o arc1 arcnet device=arc1 +(Note that in the first line, the default is device=arc0, but it doesn't +hurt if you want to add it for consistency.) How do I get it to work with...? diff -ur --new-file old/linux/Documentation/oops-tracing.txt new/linux/Documentation/oops-tracing.txt --- old/linux/Documentation/oops-tracing.txt Sat May 11 09:08:34 1996 +++ new/linux/Documentation/oops-tracing.txt Wed Sep 11 16:57:13 1996 @@ -70,3 +70,81 @@ Linus + +--------------------------------------------------------------------------- +Notes on Oops tracing with klogd: + +In order to help Linus and the other kernel developers there has been +substantial support incorporated into klogd for processing protection +faults. In order to have full support for address resolution at least +version 1.3-pl3 of the sysklogd package should be used. + +When a protection fault occurs the klogd daemon automatically +translates important addresses in the kernel log messages to their +symbolic equivalents. This translated kernel message is then +forwarded through whatever reporting mechanism klogd is using. The +protection fault message can be simply cut out of the message files +and forwarded to the kernel developers. + +Two types of address resolution are performed by klogd. The first is +static translation and the second is dynamic translation. Static +translation uses the System.map file in much the same manner that +ksymoops does. In order to do static translation the klogd daemon +must be able to find a system map file at daemon initialization time. +See the klogd man page for information on how klogd searches for map +files. + +Dynamic address translation is important when kernel loadable modules +are being used. Since memory for kernel modules is allocated from the +kernel's dynamic memory pools there are no fixed locations for either +the start of the module or for functions and symbols in the module. + +The kernel supports system calls which allow a program to determine +which modules are loaded and their location in memory. Using these +system calls the klogd daemon builds a symbol table which can be used +to debug a protection fault which occurs in a loadable kernel module. + +At the very minimum klogd will provide the name of the module which +generated the protection fault. There may be additional symbolic +information available if the developer of the loadable module chose to +export symbol information from the module. + +Since the kernel module environment can be dynamic there must be a +mechanism for notifying the klogd daemon when a change in module +environment occurs. There are command line options available which +allow klogd to signal the currently executing daemon that symbol +information should be refreshed. See the klogd manual page for more +information. + +A patch is included with the sysklogd distribution which modifies the +modules-2.0.0 package to automatically signal klogd whenever a module +is loaded or unloaded. Applying this patch provides essentially +seamless support for debugging protection faults which occur with +kernel loadable modules. + +The following is an example of a protection fault in a loadable module +processed by klogd: +--------------------------------------------------------------------------- +Aug 29 09:51:01 blizard kernel: Unable to handle kernel paging request at virtual address f15e97cc +Aug 29 09:51:01 blizard kernel: current->tss.cr3 = 0062d000, %cr3 = 0062d000 +Aug 29 09:51:01 blizard kernel: *pde = 00000000 +Aug 29 09:51:01 blizard kernel: Oops: 0002 +Aug 29 09:51:01 blizard kernel: CPU: 0 +Aug 29 09:51:01 blizard kernel: EIP: 0010:[oops:_oops+16/3868] +Aug 29 09:51:01 blizard kernel: EFLAGS: 00010212 +Aug 29 09:51:01 blizard kernel: eax: 315e97cc ebx: 003a6f80 ecx: 001be77b edx: 00237c0c +Aug 29 09:51:01 blizard kernel: esi: 00000000 edi: bffffdb3 ebp: 00589f90 esp: 00589f8c +Aug 29 09:51:01 blizard kernel: ds: 0018 es: 0018 fs: 002b gs: 002b ss: 0018 +Aug 29 09:51:01 blizard kernel: Process oops_test (pid: 3374, process nr: 21, stackpage=00589000) +Aug 29 09:51:01 blizard kernel: Stack: 315e97cc 00589f98 0100b0b4 bffffed4 0012e38e 00240c64 003a6f80 00000001 +Aug 29 09:51:01 blizard kernel: 00000000 00237810 bfffff00 0010a7fa 00000003 00000001 00000000 bfffff00 +Aug 29 09:51:01 blizard kernel: bffffdb3 bffffed4 ffffffda 0000002b 0007002b 0000002b 0000002b 00000036 +Aug 29 09:51:01 blizard kernel: Call Trace: [oops:_oops_ioctl+48/80] [_sys_ioctl+254/272] [_system_call+82/128] +Aug 29 09:51:01 blizard kernel: Code: c7 00 05 00 00 00 eb 08 90 90 90 90 90 90 90 90 89 ec 5d c3 +--------------------------------------------------------------------------- + +Dr. G.W. Wettstein Oncology Research Div. Computing Facility +Roger Maris Cancer Center INTERNET: greg@wind.rmcc.com +820 4th St. N. +Fargo, ND 58122 +Phone: 701-234-7556 diff -ur --new-file old/linux/MAINTAINERS new/linux/MAINTAINERS --- old/linux/MAINTAINERS Mon Aug 12 12:44:46 1996 +++ new/linux/MAINTAINERS Tue Oct 29 14:17:25 1996 @@ -106,10 +106,10 @@ S: Maintained APM DRIVER -P: Rik Faith & Stephen Rothwell -M: faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au -L: linux-laptop@vger.rutgers.edu -S: Maintained +P: Rik Faith & Stephen Rothwell +M: faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au +L: linux-laptop@vger.rutgers.edu +S: Maintained APPLETALK NETWORK LAYER P: Alan Cox & University Of Michigan @@ -166,10 +166,10 @@ S: Maintained FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit) -P: Rik Faith -M: faith@cs.unc.edu -L: linux-scsi@vger.rutgers.edu -S: Odd fixes (e.g., new signatures) +P: Rik Faith +M: faith@cs.unc.edu +L: linux-scsi@vger.rutgers.edu +S: Odd fixes (e.g., new signatures) SCSI TAPE DRIVER P: Kai Mäkisara @@ -178,9 +178,8 @@ S: Maintained FTAPE/QIC-117: -P: Kai Harrekilde-Petersen -M: khp@dolphinics.no -W: http://www.dolphinics.no/~khp/ftape.html +P: Claus-Justus Heine +M: claus@momo.math.rwth-aachen.de L: linux-tape@vger.rutgers.edu S: Maintained @@ -246,10 +245,10 @@ S: Maintained STARMODE RADIO IP (STRIP) PROTOCOL DRIVER -P: Stuart Cheshire -M: cheshire@cs.stanford.edu -W: http://mosquitonet.Stanford.EDU/strip.html -S: Maintained +P: Stuart Cheshire +M: cheshire@cs.stanford.edu +W: http://mosquitonet.Stanford.EDU/strip.html +S: Maintained SMB FILESYSTEM: P: Volker Lendecke @@ -334,6 +333,12 @@ P: Eberhard Moenkeberg M: emoenke@gwdg.de L: linux-kernel@vger.rutgers.edu +S: Maintained + +FPU EMULATOR +P: Bill Metzenthen +M: billm@suburbia.net +W: http://suburbia.net/~billm/floating-point/emulator/ S: Maintained CREDITS FILE diff -ur --new-file old/linux/Makefile new/linux/Makefile --- old/linux/Makefile Sat Nov 16 00:48:02 1996 +++ new/linux/Makefile Sat Nov 16 00:48:28 1996 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 0 -SUBLEVEL = 14 +SUBLEVEL = 25 ARCH = i386 @@ -24,9 +24,10 @@ TOPDIR := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi) HPATH = $(TOPDIR)/include +FINDHPATH = $(HPATH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net HOSTCC =gcc -I$(HPATH) -HOSTCFLAGS = +HOSTCFLAGS =-O2 -fomit-frame-pointer CROSS_COMPILE = @@ -38,7 +39,6 @@ NM =$(CROSS_COMPILE)nm STRIP =$(CROSS_COMPILE)strip MAKE =make -AWK =gawk all: do-it-all @@ -332,7 +332,7 @@ rm -f .menuconfig .menuconfig.log rm -f include/asm rm -f .depend `find . -name .depend -print` - rm -f .hdepend + rm -f .hdepend scripts/mkdep rm -f $(TOPDIR)/include/linux/modversions.h rm -f $(TOPDIR)/include/linux/modules/* @@ -349,8 +349,9 @@ sums: find . -type f -print | sort | xargs sum > .SUMS -dep-files: archdep .hdepend include/linux/version.h - $(AWK) -f scripts/depend.awk init/*.c > .tmpdepend +dep-files: scripts/mkdep archdep include/linux/version.h + scripts/mkdep init/*.c > .tmpdepend + scripts/mkdep `find $(FINDHPATH) -follow -name \*.h ! -name modversions.h -print` > .hdepend set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i fastdep; done mv .tmpdepend .depend @@ -388,7 +389,5 @@ # This generates dependencies for the .h files. # -.hdepend: dummy - rm -f $@ - $(AWK) -f scripts/depend.awk `find $(HPATH) -name \*.h ! -name modversions.h -print` > .$@ - mv .$@ $@ +scripts/mkdep: scripts/mkdep.c + $(HOSTCC) $(HOSTCFLAGS) -o scripts/mkdep scripts/mkdep.c diff -ur --new-file old/linux/Rules.make new/linux/Rules.make --- old/linux/Rules.make Fri Jun 7 15:28:45 1996 +++ new/linux/Rules.make Fri Sep 20 16:00:33 1996 @@ -83,7 +83,7 @@ # fastdep: dummy if [ -n "$(wildcard *.[chS])" ]; then \ - $(AWK) -f $(TOPDIR)/scripts/depend.awk *.[chS] > .depend; fi + $(TOPDIR)/scripts/mkdep *.[chS] > .depend; fi ifdef ALL_SUB_DIRS set -e; for i in $(ALL_SUB_DIRS); do $(MAKE) -C $$i fastdep; done endif diff -ur --new-file old/linux/arch/alpha/defconfig new/linux/arch/alpha/defconfig --- old/linux/arch/alpha/defconfig Sun Aug 4 13:20:22 1996 +++ new/linux/arch/alpha/defconfig Wed Oct 30 02:42:40 1996 @@ -162,6 +162,7 @@ # CONFIG_DGRS is not set # CONFIG_NET_POCKET is not set # CONFIG_TR is not set +# CONFIG_FDDI is not set # CONFIG_ARCNET is not set # diff -ur --new-file old/linux/arch/alpha/kernel/ptrace.c new/linux/arch/alpha/kernel/ptrace.c --- old/linux/arch/alpha/kernel/ptrace.c Fri May 3 14:55:57 1996 +++ new/linux/arch/alpha/kernel/ptrace.c Wed Sep 11 16:57:13 1996 @@ -266,7 +266,7 @@ struct vm_area_struct * vma; addr &= PAGE_MASK; - vma = find_vma(tsk,addr); + vma = find_vma(tsk->mm,addr); if (!vma) return NULL; if (vma->vm_start <= addr) diff -ur --new-file old/linux/arch/alpha/mm/fault.c new/linux/arch/alpha/mm/fault.c --- old/linux/arch/alpha/mm/fault.c Sat Jul 27 08:49:05 1996 +++ new/linux/arch/alpha/mm/fault.c Wed Sep 11 16:57:13 1996 @@ -58,8 +58,11 @@ struct pt_regs regs) { struct vm_area_struct * vma; + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; - vma = find_vma(current, address); + down(&mm->mmap_sem); + vma = find_vma(mm, address); if (!vma) goto bad_area; if (vma->vm_start <= address) @@ -85,6 +88,7 @@ goto bad_area; } handle_mm_fault(vma, address, cause > 0); + up(&mm->mmap_sem); return; /* @@ -92,11 +96,12 @@ * Fix it, but check if it's kernel or user first.. */ bad_area: + up(&mm->mmap_sem); if (user_mode(®s)) { printk("%s: memory violation at pc=%08lx rp=%08lx (bad address = %08lx)\n", - current->comm, regs.pc, regs.r26, address); + tsk->comm, regs.pc, regs.r26, address); die_if_kernel("oops", ®s, cause); - force_sig(SIGSEGV, current); + force_sig(SIGSEGV, tsk); return; } /* diff -ur --new-file old/linux/arch/i386/boot/compressed/Makefile new/linux/arch/i386/boot/compressed/Makefile --- old/linux/arch/i386/boot/compressed/Makefile Wed Jul 17 13:59:14 1996 +++ new/linux/arch/i386/boot/compressed/Makefile Sun Sep 8 18:50:20 1996 @@ -58,7 +58,7 @@ $(ENCAPS) $(TARGET) piggy.o $$tmppiggy.gz $(INPUT_DATA) $(INPUT_LEN); \ else \ echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ - ld -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ + $(LD) -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ fi; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk else diff -ur --new-file old/linux/arch/i386/defconfig new/linux/arch/i386/defconfig --- old/linux/arch/i386/defconfig Mon Aug 5 07:41:50 1996 +++ new/linux/arch/i386/defconfig Wed Oct 30 02:42:40 1996 @@ -112,6 +112,7 @@ # CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set # CONFIG_TR is not set +# CONFIG_FDDI is not set # CONFIG_ARCNET is not set # diff -ur --new-file old/linux/arch/i386/kernel/Makefile new/linux/arch/i386/kernel/Makefile --- old/linux/arch/i386/kernel/Makefile Wed Feb 7 07:55:34 1996 +++ new/linux/arch/i386/kernel/Makefile Wed Sep 11 16:57:13 1996 @@ -11,7 +11,6 @@ # $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s ifdef SMP - .S.o: $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o else diff -ur --new-file old/linux/arch/i386/kernel/entry.S new/linux/arch/i386/kernel/entry.S --- old/linux/arch/i386/kernel/entry.S Wed Jul 17 11:33:25 1996 +++ new/linux/arch/i386/kernel/entry.S Wed Nov 6 10:54:51 1996 @@ -503,6 +503,11 @@ pushl $ SYMBOL_NAME(do_page_fault) jmp error_code +ENTRY(spurious_interrupt_bug) + pushl $0 + pushl $ SYMBOL_NAME(do_spurious_interrupt_bug) + jmp error_code + .data ENTRY(sys_call_table) .long SYMBOL_NAME(sys_setup) /* 0 */ diff -ur --new-file old/linux/arch/i386/kernel/irq.c new/linux/arch/i386/kernel/irq.c --- old/linux/arch/i386/kernel/irq.c Thu Jun 6 12:44:42 1996 +++ new/linux/arch/i386/kernel/irq.c Thu Nov 7 10:25:21 1996 @@ -232,7 +232,7 @@ action = irq_action[i]; if (!action) continue; - len += sprintf(buf+len, "%2d: %8d %c %s", + len += sprintf(buf+len, "%2d: %10u %c %s", i, kstat.interrupts[i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); @@ -522,7 +522,7 @@ irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; #ifdef DEBUG - printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); + printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, irqmask); #endif irqs &= irqmask; if (!irqs) diff -ur --new-file old/linux/arch/i386/kernel/ksyms.c new/linux/arch/i386/kernel/ksyms.c --- old/linux/arch/i386/kernel/ksyms.c Sat Jun 29 11:00:45 1996 +++ new/linux/arch/i386/kernel/ksyms.c Sun Sep 15 09:34:18 1996 @@ -3,6 +3,8 @@ #include #include +#include + extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -11,6 +13,8 @@ /* platform dependent support */ X(dump_thread), X(dump_fpu), + XNOVERS(down_failed), + XNOVERS(up_wakeup), #ifdef __SMP__ X(apic_reg), /* Needed internally for the I386 inlines */ X(cpu_data), diff -ur --new-file old/linux/arch/i386/kernel/process.c new/linux/arch/i386/kernel/process.c --- old/linux/arch/i386/kernel/process.c Tue Aug 13 08:11:35 1996 +++ new/linux/arch/i386/kernel/process.c Wed Oct 30 02:42:40 1996 @@ -33,7 +33,6 @@ #include #include - asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); #ifdef CONFIG_APM @@ -183,34 +182,201 @@ * and if it doesn't work, we do some other stupid things. */ static long no_idt[2] = {0, 0}; +static int reboot_mode = 0; +static int reboot_thru_bios = 0; + +void reboot_setup(char *str, int *ints) +{ + while(1) { + switch (*str) { + case 'w': /* "warm" reboot (no memory testing etc) */ + reboot_mode = 0x1234; + break; + case 'c': /* "cold" reboot (with memory testing etc) */ + reboot_mode = 0x0; + break; + case 'b': /* "bios" reboot by jumping through the BIOS */ + reboot_thru_bios = 1; + break; + case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */ + reboot_thru_bios = 0; + break; + } + if((str = strchr(str,',')) != NULL) + str++; + else + break; + } +} + + +/* The following code and data reboots the machine by switching to real + mode and jumping to the BIOS reset entry point, as if the CPU has + really been reset. The previous version asked the keyboard + controller to pulse the CPU reset line, which is more thorough, but + doesn't work with at least one type of 486 motherboard. It is easy + to stop this code working; hence the copious comments. */ + +unsigned long long +real_mode_gdt_entries [3] = +{ + 0x0000000000000000ULL, /* Null descriptor */ + 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */ + 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ +}; + +struct +{ + unsigned short size __attribute__ ((packed)); + unsigned long long * base __attribute__ ((packed)); +} +real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries }, +real_mode_idt = { 0x3ff, 0 }; + +/* This is 16-bit protected mode code to disable paging and the cache, + switch to real mode and jump to the BIOS reset code. + + The instruction that switches to real mode by writing to CR0 must be + followed immediately by a far jump instruction, which set CS to a + valid value for real mode, and flushes the prefetch queue to avoid + running instructions that have already been decoded in protected + mode. + + Clears all the flags except ET, especially PG (paging), PE + (protected-mode enable) and TS (task switch for coprocessor state + save). Flushes the TLB after paging has been disabled. Sets CD and + NW, to disable the cache on a 486, and invalidates the cache. This + is more like the state of a 486 after reset. I don't know if + something else should be done for other chips. + + More could be done here to set up the registers as if a CPU reset had + occurred; hopefully real BIOSes don't assume much. */ + +unsigned char real_mode_switch [] = +{ + 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */ + 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */ + 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */ + 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ + 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */ + 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */ + 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */ + 0x74, 0x02, /* jz f */ + 0x0f, 0x08, /* invd */ + 0x24, 0x10, /* f: andb $0x10,al */ + 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ + 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ +}; static inline void kb_wait(void) { int i; - for (i=0; i<0x10000; i++) if ((inb_p(0x64) & 0x02) == 0) break; } -void hard_reset_now(void) +void hard_reset_now (void) { - int i, j; - sti(); -/* rebooting needs to touch the page at absolute addr 0 */ - pg0[0] = 7; - *((unsigned short *)0x472) = 0x1234; - for (;;) { - for (i=0; i<100; i++) { - kb_wait(); - for(j = 0; j < 100000 ; j++) - /* nothing */; - outb(0xfe,0x64); /* pulse reset low */ - udelay(10); + if(!reboot_thru_bios) { + sti(); + /* rebooting needs to touch the page at absolute addr 0 */ + pg0[0] = 7; + *((unsigned short *)0x472) = reboot_mode; + for (;;) { + int i; + for (i=0; i<100; i++) { + int j; + kb_wait(); + for(j = 0; j < 100000 ; j++) + /* nothing */; + outb(0xfe,0x64); /* pulse reset low */ + udelay(10); + } + __asm__ __volatile__("\tlidt %0": "=m" (no_idt)); } - __asm__ __volatile__("\tlidt %0": "=m" (no_idt)); } + + cli (); + + /* Write zero to CMOS register number 0x0f, which the BIOS POST + routine will recognize as telling it to do a proper reboot. (Well + that's what this book in front of me says -- it may only apply to + the Phoenix BIOS though, it's not clear). At the same time, + disable NMIs by setting the top bit in the CMOS address register, + as we're about to do peculiar things to the CPU. I'm not sure if + `outb_p' is needed instead of just `outb'. Use it to be on the + safe side. */ + + outb_p (0x8f, 0x70); + outb_p (0x00, 0x71); + + /* Remap the kernel at virtual address zero, as well as offset zero + from the kernel segment. This assumes the kernel segment starts at + virtual address 0xc0000000. */ + + memcpy (swapper_pg_dir, swapper_pg_dir + 768, + sizeof (swapper_pg_dir [0]) * 256); + + /* Make sure the first page is mapped to the start of physical memory. + It is normally not mapped, to trap kernel NULL pointer dereferences. */ + + pg0 [0] = 7; + + /* Use `swapper_pg_dir' as our page directory. Don't bother with + `SET_PAGE_DIR' because interrupts are disabled and we're rebooting. + This instruction flushes the TLB. */ + + __asm__ __volatile__ ("movl %0,%%cr3" : : "a" (swapper_pg_dir) : "memory"); + + /* Write 0x1234 to absolute memory location 0x472. The BIOS reads + this on booting to tell it to "Bypass memory test (also warm + boot)". This seems like a fairly standard thing that gets set by + REBOOT.COM programs, and the previous reset routine did this + too. */ + + *((unsigned short *)0x472) = reboot_mode; + + /* For the switch to real mode, copy some code to low memory. It has + to be in the first 64k because it is running in 16-bit mode, and it + has to have the same physical and virtual address, because it turns + off paging. Copy it near the end of the first page, out of the way + of BIOS variables. */ + + memcpy ((void *) (0x1000 - sizeof (real_mode_switch)), + real_mode_switch, sizeof (real_mode_switch)); + + /* Set up the IDT for real mode. */ + + __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt)); + + /* Set up a GDT from which we can load segment descriptors for real + mode. The GDT is not used in real mode; it is just needed here to + prepare the descriptors. */ + + __asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt)); + + /* Load the data segment registers, and thus the descriptors ready for + real mode. The base address of each segment is 0x100, 16 times the + selector value being loaded here. This is so that the segment + registers don't have to be reloaded after switching to real mode: + the values are consistent for real mode operation already. */ + + __asm__ __volatile__ ("movw $0x0010,%%ax\n" + "\tmovw %%ax,%%ds\n" + "\tmovw %%ax,%%es\n" + "\tmovw %%ax,%%fs\n" + "\tmovw %%ax,%%gs\n" + "\tmovw %%ax,%%ss" : : : "eax"); + + /* Jump to the 16-bit code that we copied earlier. It disables paging + and the cache, switches to real mode, and jumps to the BIOS reset + entry point. */ + + __asm__ __volatile__ ("ljmp $0x0008,%0" + : + : "i" ((void *) (0x1000 - sizeof (real_mode_switch)))); } void show_regs(struct pt_regs * regs) diff -ur --new-file old/linux/arch/i386/kernel/ptrace.c new/linux/arch/i386/kernel/ptrace.c --- old/linux/arch/i386/kernel/ptrace.c Thu May 2 15:11:56 1996 +++ new/linux/arch/i386/kernel/ptrace.c Wed Sep 11 16:57:13 1996 @@ -187,7 +187,7 @@ struct vm_area_struct * vma; addr &= PAGE_MASK; - vma = find_vma(tsk,addr); + vma = find_vma(tsk->mm,addr); if (!vma) return NULL; if (vma->vm_start <= addr) diff -ur --new-file old/linux/arch/i386/kernel/setup.c new/linux/arch/i386/kernel/setup.c --- old/linux/arch/i386/kernel/setup.c Thu Aug 15 08:33:08 1996 +++ new/linux/arch/i386/kernel/setup.c Fri Sep 20 16:00:34 1996 @@ -327,8 +327,8 @@ } len += sprintf(buffer+len, "\nbogomips\t: %lu.%02lu\n", - CD(loops_per_sec)/500000, - (CD(loops_per_sec)/5000) % 100); + CD(loops_per_sec+2500)/500000, + (CD(loops_per_sec+2500)/5000) % 100); #ifdef __SMP__ } } diff -ur --new-file old/linux/arch/i386/kernel/time.c new/linux/arch/i386/kernel/time.c --- old/linux/arch/i386/kernel/time.c Wed Aug 14 18:09:52 1996 +++ new/linux/arch/i386/kernel/time.c Thu Nov 7 18:48:18 1996 @@ -20,10 +20,13 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -462,6 +465,21 @@ needs more debugging. */ if (x86_capability & 16) { do_gettimeoffset = do_fast_gettimeoffset; + + if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) { + if( x86 == 5 ) { + if( x86_model == 0 ) { + /* turn on cycle counters during power down */ + __asm__ __volatile__ (" movl $0x83, %%ecx \n \ + rdmsr \n \ + orl $1,%%eax \n \ + wrmsr \n " + : : : "ax", "cx", "dx" ); + udelay(500); + } + } + } + /* read Pentium cycle counter */ __asm__(".byte 0x0f,0x31" :"=a" (init_timer_cc.low), diff -ur --new-file old/linux/arch/i386/kernel/traps.c new/linux/arch/i386/kernel/traps.c --- old/linux/arch/i386/kernel/traps.c Thu Aug 1 11:30:25 1996 +++ new/linux/arch/i386/kernel/traps.c Wed Nov 6 10:54:51 1996 @@ -81,6 +81,7 @@ asmlinkage void coprocessor_error(void); asmlinkage void reserved(void); asmlinkage void alignment_check(void); +asmlinkage void spurious_interrupt_bug(void); int kstack_depth_to_print = 24; @@ -174,11 +175,7 @@ DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) - -asmlinkage void do_reserved(struct pt_regs * regs, long error_code) -{ - printk("Uhhuh.. Reserved trap code, whazzup? (%ld)\n", error_code); -} +DO_ERROR(18, SIGSEGV, "reserved", reserved, current) asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { @@ -262,6 +259,12 @@ math_error(); } +asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, + long error_code) +{ + printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); +} + /* * 'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task @@ -347,7 +350,7 @@ set_trap_gate(12,&stack_segment); set_trap_gate(13,&general_protection); set_trap_gate(14,&page_fault); - set_trap_gate(15,&reserved); + set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); for (i=18;i<48;i++) diff -ur --new-file old/linux/arch/i386/lib/Makefile new/linux/arch/i386/lib/Makefile --- old/linux/arch/i386/lib/Makefile Tue Aug 15 14:07:02 1995 +++ new/linux/arch/i386/lib/Makefile Wed Sep 11 16:57:13 1996 @@ -2,7 +2,15 @@ # Makefile for i386-specific library files.. # +ifdef SMP +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o +else +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +endif + L_TARGET = lib.a -L_OBJS = checksum.o +L_OBJS = checksum.o semaphore.o include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/i386/lib/semaphore.S new/linux/arch/i386/lib/semaphore.S --- old/linux/arch/i386/lib/semaphore.S Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/lib/semaphore.S Sun Sep 15 09:34:18 1996 @@ -0,0 +1,27 @@ +/* + * linux/arch/i386/lib/semaphore.S + * + * Copyright (C) 1996 Linus Torvalds + */ + +#include + +/* + * "down_failed" is called with the eventual return address + * in %eax, and the address of the semaphore in %ecx. We need + * to increment the number of waiters on the semaphore, + * call "__down()", and then eventually return to try again. + */ +ENTRY(down_failed) + pushl %eax + pushl %ecx + call SYMBOL_NAME(__down) + popl %ecx + ret + +ENTRY(up_wakeup) + pushl %eax + pushl %ecx + call SYMBOL_NAME(__up) + popl %ecx + ret diff -ur --new-file old/linux/arch/i386/math-emu/README new/linux/arch/i386/math-emu/README --- old/linux/arch/i386/math-emu/README Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/README Tue Oct 29 14:17:25 1996 @@ -1,9 +1,9 @@ +---------------------------------------------------------------------------+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | | | - | Copyright (C) 1992,1993,1994,1995 | + | Copyright (C) 1992,1993,1994,1995,1996 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | This program is free software; you can redistribute it and/or modify | | it under the terms of the GNU General Public License version 2 as | @@ -42,11 +42,11 @@ some differences. Please report bugs, etc to me at: - billm@jacobi.maths.monash.edu.au + billm@suburbia.net --Bill Metzenthen - October 1995 + October 1996 ----------------------- Internals of wm-FPU-emu ----------------------- diff -ur --new-file old/linux/arch/i386/math-emu/fpu_emu.h new/linux/arch/i386/math-emu/fpu_emu.h --- old/linux/arch/i386/math-emu/fpu_emu.h Thu May 9 06:59:33 1996 +++ new/linux/arch/i386/math-emu/fpu_emu.h Sun Sep 22 09:13:54 1996 @@ -97,9 +97,9 @@ struct address { unsigned int offset; - unsigned int selector:16; - unsigned int opcode:11; - unsigned int empty:5; + unsigned short selector; + unsigned short opcode:11, + empty:5; }; typedef void (*FUNC)(void); typedef struct fpu_reg FPU_REG; diff -ur --new-file old/linux/arch/i386/mm/fault.c new/linux/arch/i386/mm/fault.c --- old/linux/arch/i386/mm/fault.c Thu Apr 25 07:43:05 1996 +++ new/linux/arch/i386/mm/fault.c Wed Sep 11 16:57:13 1996 @@ -33,13 +33,21 @@ */ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) { + void (*handler)(struct task_struct *, + struct vm_area_struct *, + unsigned long, + int); + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; struct vm_area_struct * vma; unsigned long address; unsigned long page; + int write; /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); - vma = find_vma(current, address); + down(&mm->mmap_sem); + vma = find_vma(mm, address); if (!vma) goto bad_area; if (vma->vm_start <= address) @@ -63,36 +71,37 @@ * we can handle it.. */ good_area: - /* - * was it a write? - */ - if (error_code & 2) { - if (!(vma->vm_flags & VM_WRITE)) - goto bad_area; - } else { - /* read with protection fault? */ - if (error_code & 1) - goto bad_area; - if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + write = 0; + handler = do_no_page; + switch (error_code & 3) { + default: /* 3: write, present */ + handler = do_wp_page; +#ifdef TEST_VERIFY_AREA + if (regs->cs == KERNEL_CS) + printk("WP fault at %08lx\n", regs->eip); +#endif + /* fall through */ + case 2: /* write, not present */ + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + case 1: /* read, present */ goto bad_area; + case 0: /* read, not present */ + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; } + handler(tsk, vma, address, write); + up(&mm->mmap_sem); /* * Did it hit the DOS screen memory VA from vm86 mode? */ if (regs->eflags & VM_MASK) { unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT; if (bit < 32) - current->tss.screen_bitmap |= 1 << bit; - } - if (error_code & 1) { -#ifdef TEST_VERIFY_AREA - if (regs->cs == KERNEL_CS) - printk("WP fault at %08x\n", regs->eip); -#endif - do_wp_page(current, vma, address, error_code & 2); - return; + tsk->tss.screen_bitmap |= 1 << bit; } - do_no_page(current, vma, address, error_code & 2); return; /* @@ -100,11 +109,12 @@ * Fix it, but check if it's kernel or user first.. */ bad_area: + up(&mm->mmap_sem); if (error_code & 4) { - current->tss.cr2 = address; - current->tss.error_code = error_code; - current->tss.trap_no = 14; - force_sig(SIGSEGV, current); + tsk->tss.cr2 = address; + tsk->tss.error_code = error_code; + tsk->tss.trap_no = 14; + force_sig(SIGSEGV, tsk); return; } /* @@ -128,7 +138,7 @@ printk(" at virtual address %08lx\n",address); __asm__("movl %%cr3,%0" : "=r" (page)); printk(KERN_ALERT "current->tss.cr3 = %08lx, %%cr3 = %08lx\n", - current->tss.cr3, page); + tsk->tss.cr3, page); page = ((unsigned long *) page)[address >> 22]; printk(KERN_ALERT "*pde = %08lx\n", page); if (page & 1) { diff -ur --new-file old/linux/arch/i386/mm/init.c new/linux/arch/i386/mm/init.c --- old/linux/arch/i386/mm/init.c Thu Apr 11 13:52:00 1996 +++ new/linux/arch/i386/mm/init.c Sun Sep 8 18:50:20 1996 @@ -198,7 +198,7 @@ address += PAGE_SIZE; } } - flush_tlb(); + local_flush_tlb(); return free_area_init(start_mem, end_mem); } @@ -272,10 +272,10 @@ /* test if the WP bit is honoured in supervisor mode */ if (wp_works_ok < 0) { pg0[0] = pte_val(mk_pte(0, PAGE_READONLY)); - flush_tlb(); + local_flush_tlb(); __asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory"); pg0[0] = 0; - flush_tlb(); + local_flush_tlb(); if (wp_works_ok < 0) wp_works_ok = 0; } diff -ur --new-file old/linux/arch/m68k/atari/stdma.c new/linux/arch/m68k/atari/stdma.c --- old/linux/arch/m68k/atari/stdma.c Mon May 20 06:54:26 1996 +++ new/linux/arch/m68k/atari/stdma.c Sun Sep 8 18:50:20 1996 @@ -132,7 +132,7 @@ int stdma_others_waiting(void) { - return stdma_wait != NULL; + return waitqueue_active(&stdma_wait); } diff -ur --new-file old/linux/drivers/atm/Config.in new/linux/drivers/atm/Config.in --- old/linux/drivers/atm/Config.in Sat Nov 16 00:48:03 1996 +++ new/linux/drivers/atm/Config.in Sat Nov 16 00:48:31 1996 @@ -19,4 +19,5 @@ if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then bool ' Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n fi + tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR y fi diff -ur --new-file old/linux/drivers/atm/Makefile new/linux/drivers/atm/Makefile --- old/linux/drivers/atm/Makefile Sat Nov 16 00:48:03 1996 +++ new/linux/drivers/atm/Makefile Sat Nov 16 00:48:31 1996 @@ -34,6 +34,14 @@ L_OBJS += fore200.o endif +ifeq ($(CONFIG_ATM_NICSTAR),y) +L_OBJS += nicstar.o +else + ifeq ($(CONFIG_ATM_NICSTAR),m) + M_OBJS += nicstar.o + endif +endif + ifeq ($(CONFIG_ATM_TCP),y) L_OBJS += atmtcp.o else diff -ur --new-file old/linux/drivers/atm/atmdev_init.c new/linux/drivers/atm/atmdev_init.c --- old/linux/drivers/atm/atmdev_init.c Sat Nov 16 00:48:03 1996 +++ new/linux/drivers/atm/atmdev_init.c Sat Nov 16 00:48:31 1996 @@ -18,6 +18,9 @@ #ifdef CONFIG_ATM_FORE200 extern int fore200_detect(void); #endif +#ifdef CONFIG_ATM_NICSTAR +extern int nicstar_detect(void); +#endif #ifdef CONFIG_ATM_TCP extern int atmtcp_init(void); #endif @@ -39,6 +42,9 @@ #endif #ifdef CONFIG_ATM_FORE200 devs += fore200_detect(); +#endif +#ifdef CONFIG_ATM_NICSTAR + devs += nicstar_detect(); #endif #ifdef CONFIG_ATM_TCP devs += atmtcp_init(); diff -ur --new-file old/linux/drivers/atm/atmtcp.c new/linux/drivers/atm/atmtcp.c --- old/linux/drivers/atm/atmtcp.c Sat Nov 16 00:48:03 1996 +++ new/linux/drivers/atm/atmtcp.c Sat Nov 16 00:48:31 1996 @@ -210,7 +210,6 @@ skb->atm.timestamp = xtime; skb->len = ntohs(hdr.length); skb->free = 1; - skb->atm.iovcnt = 0; pos = skb->data; for (left = ntohs(hdr.length); left; left -= size) { size = do_recv(PRIV(dev)->sock,pos,left,0); diff -ur --new-file old/linux/drivers/atm/eni.c new/linux/drivers/atm/eni.c --- old/linux/drivers/atm/eni.c Sat Nov 16 00:48:03 1996 +++ new/linux/drivers/atm/eni.c Sat Nov 16 00:48:31 1996 @@ -633,14 +633,14 @@ eni_dev = ENI_DEV(dev); while ((curr = eni_dev->fast)) { - EVENT("poll_tx.fast\n",0,0); + 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_tx.slow\n",0,0); + 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; @@ -921,12 +921,18 @@ static enum enq_res do_tx(struct sk_buff *skb) { + /* @@@ + * This static var may break with multiple adapters if multiple + * instances of do_tx (for different cards) are active concurrently. + * Moving it to eni_dev may cause a performance drop, so I'm avoiding + * that for now. + */ + static unsigned long dma[TX_DMA_BUF*2]; struct atm_vcc *vcc; struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; struct eni_tx *tx; unsigned long dma_rd,dma_wr; - unsigned long dma[TX_DMA_BUF*2]; unsigned long size; /* in words */ int aal5,dma_size,i,j; diff -ur --new-file old/linux/drivers/atm/nicstar.c new/linux/drivers/atm/nicstar.c --- old/linux/drivers/atm/nicstar.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/atm/nicstar.c Sat Nov 16 00:48:31 1996 @@ -0,0 +1,1923 @@ +/* nicstar.c, M. Welsh (matt.welsh@cl.cam.ac.uk) + * + * Linux driver for the IDT77201 NICStAR PCI ATM controller. + * PHY component is expected to be 155 Mbps S/UNI-Lite or IDT 77155; + * see init_nicstar() for PHY initialization to change this. This driver + * expects the Linux ATM stack to support scatter-gather lists + * (skb->atm.iovcnt != 0) for Rx skb's passed to vcc->push. + * + * Copyright (c) 1996 University of Cambridge Computer Laboratory + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * M. Welsh, 6 July 1996 + * + */ + +static char _modversion[] = "@(#) nicstar.c, $Id: nicstar.c,v 1.28 1996/11/12 23:17:53 swdaniel Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +char kernel_version[] = UTS_RELEASE; + +#include "nicstar.h" + +#undef NICSTAR_DEBUG /* Define for verbose debugging output */ + +#ifdef NICSTAR_DEBUG +#define PRINTK(args...) printk(args) +#else +#define PRINTK(args...) +#endif + +#define SLEEP_ON(x) interruptible_sleep_on(x) +#define WAKE_UP(x) wake_up_interruptible(x) + +#define TRUE 1 +#define FALSE 0 +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) + +#define CMD_BUSY(node) (readl((node)->membase + STAT) & 0x0200) + +/* For timing */ +unsigned long get_usec(void) { + struct timeval t; + do_gettimeofday(&t); + return (unsigned long)((1e6 * t.tv_sec) + t.tv_usec); +} + +/* Global definitions ******************************************************/ +static struct nicstar_dev *nicstar_devs[NICSTAR_MAX_DEVS]; +static int num_found = 0; +#if 0 +static int nicstar_rx_count = 0; +static u32 nicstar_timer = 0; +#endif + +/* Global/static function declarations *************************************/ +static int init_nicstar(struct nicstar_dev *node); +static void nicstar_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void process_tx_statq(nicstar_devp node); +static void process_rx_statq(nicstar_devp node); +#ifdef NICSTAR_RCQ +static void process_rx_rawcellq(nicstar_devp node); +static void dequeue_rawcell(u32 *cell); +#endif +static void push_rxbufs(nicstar_devp node, int lg, + u32 handle1, u32 addr1, u32 handle2, u32 addr2); +static void open_rx_connection(nicstar_devp node,int index, + u32 status); +static void close_rx_connection(nicstar_devp node, int index); +static void push_scq_entry(nicstar_devp node, struct nicstar_scq *scq, + nicstar_tbd *entry, int xmit_now); +int init_module(void); +void cleanup_module(void); +static int get_ci(nicstar_devp node, struct atm_vcc *vcc, + short *vpi, int *vci); +static void dequeue_rx(nicstar_devp node); +static void nicstar_free_rx_skb(struct atm_vcc *vcc, struct sk_buff *skb); +static void drain_scq(nicstar_devp node, struct nicstar_scq *scq, int index); +static void free_rx_buf(nicstar_devp node, int lg, u32 buf_addr); +#if NICSTAR_CBR +static void alloc_tx(nicstar_devp node, struct atm_vcc *vcc); +static void close_cbr (nicstar_devp node, struct atm_vcc *vcc); +static void close_cbr_final (nicstar_devp node, struct atm_vcc *vcc); +#endif +static int create_scq (struct nicstar_scq **scq, int size); + +/* Module entry points *****************************************************/ + +static int nicstar_open(struct atm_vcc *vcc, short vpi, int vci) { + nicstar_devp node; + int error; + + node = vcc->dev->dev_data; + + /* XXX AAL5 and AAL0 only supported */ + if ((vcc->aal != ATM_AAL5) && (vcc->aal != ATM_AAL0)) + return -EINVAL; + + error = get_ci(node, vcc, &vpi, &vci); + if (error) return error; + vcc->vpi = vpi; + vcc->vci = vci; + vcc->dev_data = &(node->vci_map[vci]); + vcc->flags |= ATM_VF_ADDR; + +#if NICSTAR_CBR +#ifndef ATM_013 + if (vcc->qos.txtp.traffic_class == ATM_CBR) { +#else + if (vcc->txtp.class == ATM_CBR) { +#endif + printk("Opening CBR connection on vpci %d.%d\n",vpi,vci); + alloc_tx(node,vcc); + } +#endif + + if (!(vcc->flags & ATM_VF_PARTIAL)) { + +#ifndef ATM_013 + if (vcc->qos.rxtp.traffic_class != ATM_NONE) { +#else + if (vcc->rxtp.class != ATM_NONE) { +#endif + u32 status = 0x8000; + switch (vcc->aal) { + case ATM_AAL0 : break; + case ATM_AAL34 : status |= 0x10000; break; + case ATM_AAL5 : status |= 0x20000; break; + default : + printk ("nicstar%d: invalid AAL %d on open",node->index,vcc->aal); + return -EINVAL; + } + open_rx_connection(node, vci, status); + } + + vcc->flags |= ATM_VF_PARTIAL; + + } + + vcc->flags |= ATM_VF_READY; + + return 0; + +} + +static void nicstar_close(struct atm_vcc *vcc) { + nicstar_devp node; + nicstar_vcimap *mapval; + + node = vcc->dev->dev_data; + mapval = vcc->dev_data; + + vcc->flags &= ~ATM_VF_READY; + +#ifndef ATM_013 + if (vcc->qos.rxtp.traffic_class != ATM_NONE) { +#else + if (vcc->rxtp.class != ATM_NONE) { +#endif + close_rx_connection(node, vcc->vci); + vcc->flags &= ~ATM_VF_PARTIAL; + mapval->rx = 0; + } + +#ifndef ATM_013 + if (vcc->qos.txtp.traffic_class != ATM_NONE) { +#else + if (vcc->txtp.class != ATM_NONE) { +#endif + mapval->tx = 0; +#ifndef ATM_013 + if (vcc->qos.txtp.traffic_class == ATM_CBR) { +#else + if (vcc->txtp.class == ATM_CBR) { +#endif +#if NICSTAR_CBR + close_cbr(node,vcc); +#endif + } + } + + vcc->flags &= ~ATM_VF_ADDR; +} + +static int nicstar_ioctl(struct atm_dev *dev, unsigned int cmd, + unsigned long arg) { + if (!dev->phy->ioctl) return -EINVAL; + return dev->phy->ioctl(dev,cmd,arg); +} + +static int nicstar_getsockopt(struct atm_vcc *vcc, + int level, + int optname, + char *optval, + int *optlen) { + return -EINVAL; +} + +static int nicstar_setsockopt(struct atm_vcc *vcc, int level, + int optname, char *optval, int optlen) { + struct atm_cirange ci; + int error; + + if (level != SOL_ATM) return -EINVAL; + switch (optname) { + case SO_CIRANGE: + if (!suser()) return -EPERM; + if (optlen != sizeof(struct atm_cirange)) + return -EINVAL; + error = verify_area(VERIFY_READ,optval,sizeof(struct atm_cirange)); + if (error) return error; + memcpy_fromfs(&ci,optval,sizeof(struct atm_cirange)); + if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) && + (ci.vci_bits == NUM_VCI_BITS || ci.vci_bits == ATM_CI_MAX)) return 0; + return -EINVAL; + break; + + default: + return -EINVAL; + } +} + +/* XXX It is possible here that when pushing multiple TBD's, the + * next TSI will be placed between them, causing an incomplete PDU xmit. + * Probably fix by not allowing a TSI to be pushed onto the SCQ when + * force_xmit == 0. + */ +static int nicstar_send(struct atm_vcc *vcc, struct sk_buff *skb) { + nicstar_devp node; + int pad, sindex, vci; + nicstar_tbd tbd1; + nicstar_scq *scq; + u32 rate; +#ifdef NICSTAR_FASTXMIT + int len, len_buf; +#else + nicstar_tbd tbd2, tbd3; +#endif + + node = vcc->dev->dev_data; + + if (!((nicstar_vcimap *)vcc->dev_data)->tx) { + printk("nicstar%d: Tx on a non-Tx VC?\n", + ((nicstar_devp)vcc->dev->dev_data)->index); + vcc->stats->tx_err++; + dev_kfree_skb(skb,FREE_WRITE); + return -EINVAL; + } +#ifndef NICSTAR_CBR +#ifndef ATM_013 + if (vcc->qos.txtp.traffic_class == ATM_CBR) { +#else + if (vcc->txtp.class == ATM_CBR) { +#endif /* ATM_013*/ + printk("nicstar: Sorry - CBR support in progress.\n"); + dev_kfree_skb(skb,FREE_WRITE); + return -EINVAL; + } +#endif /* NICSTAR_CBR */ + + /* Verify AAL is supported */ + if ((vcc->aal != ATM_AAL5) && (vcc->aal != ATM_AAL0)) { + printk("nicstar%d: Only AAL5 and AAL0 supported, sorry.\n", + ((nicstar_devp)vcc->dev->dev_data)->index); + vcc->stats->tx_err++; + dev_kfree_skb(skb,FREE_WRITE); + return -EINVAL; + } + + /* Verify that we have a linear buffer to transmit */ + if (skb->atm.iovcnt) { + printk("nicstar%d: S/G not supported, sorry.\n", + ((nicstar_devp)vcc->dev->dev_data)->index); + vcc->stats->tx_err++; + dev_kfree_skb(skb,FREE_WRITE); + return -EINVAL; + } + + skb->atm.vcc = vcc; + + vci = vcc->vci; +#ifndef ATM_013 + if (vcc->qos.txtp.traffic_class == ATM_CBR) { +#else + if (vcc->txtp.class == ATM_CBR) { +#endif +#ifdef NICSTAR_CBR + scq = (node->vci_map[vci]).scq; + rate = 0; +#endif + } + else { + scq = node->scq; + rate = TBD_UBR_M | TBD_UBR_N; + } + sindex = scq->next - scq->base; + + + /* AAL-dependent transmits */ + if (vcc->aal == ATM_AAL0) { + /* size is always 48 bytes */ + len = ATM_CELL_PAYLOAD & ~3; + /* create tbd */ + tbd1.status = TBD_AAL0 | TBD_ENDPDU | rate | len; + tbd1.buf_addr = (u32)(skb->data + 4); + tbd1.ctl_len = 0x0; + tbd1.cell_hdr = (u32)(*((u32 *)skb->data)); + push_scq_entry(node,scq,&tbd1,1); + } + else if (vcc->aal == ATM_AAL5) { /* Transmit AAL 5 */ +#ifdef NICSTAR_FASTXMIT + /* Speed up and simplify transmission by "cheating". + Basic problem is that rev C3 only supports mod 48 + buffer sizes; Matt's original uses shadow cells + to provide this. We cheat, instead, and simply + lie about the length of the buffer. This may + have security implications and problems from + going over the end of a buffer. + + For rev D or better, there is a better way; these + chips support mod 4 buffer sizes. Thus, we simply + pad the entire PDU length to mod 48 by adding a + pointer to the zero'd tmp_cell buffer. (SWD) */ + len = (((skb->len + 8 + 47) / 48) * 48); + pad = len - skb->len; + PRINTK ("new len %d skb len %ld skb end %x skb tail %x\n",len,skb->len,(u32)skb->end,(u32)skb->tail); + if (!(node->rev_C3)) { /* SAR supports mod 4 buffer sizes */ + len_buf = (((skb->len + 3) / 4) * 4); + if (pad == 0) { + tbd1.status = TBD_AAL5 | TBD_ENDPDU | rate | skb->len; + } + else { + tbd1.status = TBD_AAL5 | rate | len_buf; + } + } + else { + tbd1.status = TBD_AAL5 | TBD_ENDPDU | rate | len; + if (skb->end - pad < skb->tail) { + PRINTK ("nicstar%d: Whoops. overpadding a buffer!\n",node->index); + } + } + + tbd1.buf_addr = (u32)skb->data; + tbd1.ctl_len = skb->len; + tbd1.cell_hdr = (vcc->vci << 4); + scq->scq_shadow[sindex].skb = skb; + + if (!(node->rev_C3) && (pad != 0)) { + push_scq_entry(node, scq, &tbd1, 0); + tbd1.status = TBD_AAL5 | TBD_ENDPDU | rate | (len - len_buf); + tbd1.buf_addr = (u32)node->tmp_cell; + tbd1.ctl_len = skb->len; + tbd1.cell_hdr = (vcc->vci << 4); + push_scq_entry(node, scq, &tbd1, 1); + } + else { + push_scq_entry(node, scq, &tbd1, 1); + } + +#else + if (skb->len > 48) { + /* YYY If we transmit a buffer where pad == 48, an extra cell + is transmitted under this scheme if skb->len > 48 (SWD) */ + + pad = 48 - ((skb->len + 8) % 48); + if (pad <= 40) { + memcpy(node->scq->scq_shadow[sindex].cell, + skb->data + (skb->len) - (40 - pad), + 40 - pad); + + /* Need to do this unless we want to send garbage at + * end of last cell ... only bad for security */ + memset(node->scq->scq_shadow[sindex].cell + (40 - pad), 0, pad); + + tbd1.status = 0x08810000 | (((skb->len - (40 - pad))/sizeof(u32)) << 2); + tbd1.buf_addr = (u32)skb->data; + tbd1.ctl_len = skb->len; + tbd1.cell_hdr = (vcc->vci << 4); + + node->scq->scq_shadow[sindex].skb = skb; + push_scq_entry(node, node->scq, &tbd1, 0); + + tbd2.status = 0x48810030; + tbd2.buf_addr = (u32)(node->scq->scq_shadow[sindex].cell); + tbd2.ctl_len = skb->len; + tbd2.cell_hdr = (vcc->vci << 4); + + push_scq_entry(node, node->scq, &tbd2, 1); + + } else { + /* Pad > 40 */ + pad = pad - 40; /* Pad of second-to-last cell */ + memcpy(node->scq->scq_shadow[sindex].cell, + skb->data + (skb->len) - (48 - pad), 48 - pad); + memset(node->scq->scq_shadow[sindex].cell + (48 - pad), 0, pad); + + tbd1.status = 0x08810000 | (((skb->len - (48 - pad))/sizeof(u32)) << 2); + tbd1.buf_addr = (u32)skb->data; + tbd1.ctl_len = skb->len; + tbd1.cell_hdr = (vcc->vci << 4); + + node->scq->scq_shadow[sindex].skb = skb; + push_scq_entry(node, node->scq, &tbd1, 0); + + tbd2.status = 0x08810030; + tbd2.buf_addr = (u32)(node->scq->scq_shadow[sindex].cell); + tbd2.ctl_len = skb->len; + tbd2.cell_hdr = (vcc->vci << 4); + + push_scq_entry(node, node->scq, &tbd2, 0); + + tbd3.status = 0x48810030; + tbd3.buf_addr = (u32)(node->tmp_cell); + tbd3.ctl_len = skb->len; + tbd3.cell_hdr = (vcc->vci << 4); + + push_scq_entry(node, node->scq, &tbd3, 1); + } + + } else if (skb->len <= 40) { + + pad = 48 - (skb->len + 8); + memcpy(node->scq->scq_shadow[sindex].cell, + skb->data, skb->len); + memset(node->scq->scq_shadow[sindex].cell + skb->len, 0, pad); + tbd1.status = 0x48810030; + tbd1.buf_addr = (u32)(node->scq->scq_shadow[sindex].cell); + tbd1.ctl_len = skb->len; + tbd1.cell_hdr = (vcc->vci << 4); + + node->scq->scq_shadow[sindex].skb = skb; + push_scq_entry(node, node->scq, &tbd1, 1); + + } else { + + /* 41 .. 48 inclusive */ + pad = 48 - (skb->len); + memcpy(node->scq->scq_shadow[sindex].cell, + skb->data, skb->len); + memset(node->scq->scq_shadow[sindex].cell + skb->len, 0, pad); + + tbd1.status = 0x08810030; + tbd1.buf_addr = (u32)(node->scq->scq_shadow[sindex].cell); + tbd1.ctl_len = skb->len; + tbd1.cell_hdr = (vcc->vci << 4); + + node->scq->scq_shadow[sindex].skb = skb; + push_scq_entry(node, node->scq, &tbd1, 0); + + tbd2.status = 0x48810030; + tbd2.buf_addr = (u32)(node->tmp_cell); + tbd2.ctl_len = skb->len; + tbd2.cell_hdr = (vcc->vci << 4); + + push_scq_entry(node, node->scq, &tbd2, 1); + + } +#endif /* NICSTAR_FASTXMIT */ + } + + vcc->stats->tx++; + return 0; + +} + +static struct atmdev_ops atm_ops = { + nicstar_open, /* open */ + nicstar_close, /* close */ + nicstar_ioctl, /* ioctl */ + nicstar_getsockopt, /* getsockopt */ + nicstar_setsockopt, /* setsockopt */ + nicstar_send, /* send */ + NULL, /* sg_send */ + NULL, /* poll */ + NULL, /* send_oam */ + NULL, /* phy_put */ + NULL, /* phy_get */ + NULL, /* feedback */ +#ifndef ATM_013 + NULL, /* change_qos */ +#endif + nicstar_free_rx_skb, /* free_rx_skb */ +}; + +static int do_detect(void) { + + int error; + static unsigned char pci_bus, pci_devfn; + int pci_index; + struct nicstar_dev *node; + unsigned short pci_command; + unsigned char pci_latency; + unsigned char irq; + int i; + u32 gp; + + PRINTK("nicstar: init_module called\n"); + + if (!pcibios_present()) { + printk("nicstar: Fatal error: No PCI BIOS present\n"); + return -EIO; + } + + for (i = 0; i < NICSTAR_MAX_DEVS; i++) + nicstar_devs[i] = NULL; + + PRINTK("nicstar: Searching for IDT77201 NICStAR: vendor 0x%x, device 0x%x\n", + PCI_VENDOR_ID_IDT, + PCI_DEVICE_ID_IDT_IDT77201); + + for (pci_index = 0; pci_index < 8; pci_index++) { + + if ((error = pcibios_find_device(PCI_VENDOR_ID_IDT, + PCI_DEVICE_ID_IDT_IDT77201, + pci_index, &pci_bus, &pci_devfn)) + != PCIBIOS_SUCCESSFUL) + break; + + /* Allocate a device structure */ + node = kmalloc(sizeof(struct nicstar_dev),GFP_KERNEL); + if (node == NULL) { + printk("nicstar: Can't allocate device struct for device %d!\n",pci_index); + return -EIO; + } + nicstar_devs[num_found] = node; + node->index = num_found; + node->pci_bus = pci_bus; + node->pci_devfn = pci_devfn; + + error = pcibios_read_config_byte(pci_bus,pci_devfn,PCI_REVISION_ID, + &(node->revid)); + if (error) { + printk("nicstar: Can't read REVID from device %d\n",pci_index); + return -EIO; + } + + error = pcibios_read_config_dword(pci_bus,pci_devfn,PCI_BASE_ADDRESS_0, + (unsigned int *)&(node->iobase)); + if (error) { + printk("nicstar: Can't read iobase from device %d.\n",pci_index); + return -EIO; + } + + error = pcibios_read_config_dword(pci_bus,pci_devfn,PCI_BASE_ADDRESS_1, + (unsigned int *)&(node->membase)); + if (error) { + printk("nicstar: Can't read membase from device %d.\n",pci_index); + return -EIO; + } + + if (pcibios_read_config_byte(pci_bus,pci_devfn,PCI_INTERRUPT_LINE, + (unsigned char *)&irq)) { + printk("nicstar: Can't read INTLN from device %d.\n",pci_index); + return -EIO; + } + node->irq = irq; + + node->iobase &= PCI_BASE_ADDRESS_IO_MASK; + node->membase &= PCI_BASE_ADDRESS_MEM_MASK; + + node->membase = (unsigned long)vremap((unsigned int)node->membase,NICSTAR_IO_SIZE); + if ((unsigned long *)node->membase == NULL) { + printk("nicstar: Can't remap membase for board %d.\n",node->index); + return -EIO; + } + + printk("nicstar: Found device index %d, bus %d, function %d.\n", + pci_index,pci_bus,pci_devfn); + printk("nicstar: Revision 0x%x, using IRQ %d.\n",node->revid,node->irq); + printk("nicstar: iobase: 0x%lx, membase: 0x%lx\n",node->iobase,node->membase); + + /* Enable memory space and busmastering */ + if (pcibios_read_config_word(pci_bus,pci_devfn,PCI_COMMAND,&pci_command)) { + printk("nicstar: Can't read PCI_COMMAND from device %d.\n",pci_index); + return -EIO; + } + + pci_command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_SPECIAL | PCI_COMMAND_MASTER); + error = pcibios_write_config_word(pci_bus,pci_devfn,PCI_COMMAND, + pci_command); + + if (error) { + printk("nicstar: Can't enable board memory for device %d.\n",pci_index); + return -EIO; + } + + /* Check latency timer */ + if (pcibios_read_config_byte(pci_bus,pci_devfn,PCI_LATENCY_TIMER,&pci_latency)) { + printk("nicstar: Can't read latency timer from device %d.\n",pci_index); + return -EIO; + } + if (pci_latency < 32) { + printk("nicstar: Setting latency timer from %d to 32 clocks.\n",pci_latency); + pcibios_write_config_byte(pci_bus,pci_devfn,PCI_LATENCY_TIMER,32); + } + + num_found++; + + /* Check for SAR, revision C3; on this revision, bit 15/GP is + not writable, only readable. */ + gp = readl(node->membase + GP); + writel(gp | 0x8000,node->membase + GP); + node->rev_C3 = (readl(node->membase + GP) & 0x8000) ? 0 : 1; + if (node->rev_C3) { + printk("nicstar%d: Revision C3 77201 detected\n",node->index); + } + else { + printk("nicstar%d: Revision D or later 77201 detected\n",node->index); + } + writel(gp,node->membase + GP); /* restore original contents */ + + /* Initialize random things */ + node->inuse = 0; + + /* Get IRQ */ + if (request_irq(node->irq, &nicstar_interrupt, SA_INTERRUPT, "nicstar", node)) { + printk("nicstar: Can't allocate IRQ %d.\n",node->irq); + return -EIO; + } + + if (init_nicstar(node)) { + printk("nicstar: Error initializing device %d.\n",pci_index); + free_irq(node->irq, node); + return -EIO; + } + + /* Register device */ + node->dev = atm_dev_register("nicstar", &atm_ops, 0); + if (node->dev == NULL) { + printk("nicstar: Can't register device %d.\n",node->index); + free_irq(node->irq, node); + return -EIO; + } + node->dev->esi[0] = '\0'; + node->dev->esi[1] = '\34'; + node->dev->esi[2] = '\100'; + node->dev->esi[3] = '\0'; + node->dev->esi[4] = '\17'; + node->dev->esi[5] = '\110'; + node->dev->dev_data = node; + + node->dev->ci_range.vpi_bits = 0; + node->dev->ci_range.vci_bits = NUM_VCI_BITS; + + } + + if (num_found == 0) return -EIO; + + printk("nicstar: %d devices found.\n",num_found); + return num_found; + +} + + +#ifndef MODULE + + +int nicstar_detect(void) +{ + int devs; + + devs = do_detect(); + return devs < 0 ? 0 : devs; +} + + +#else + + +int init_module(void) { + int devs; + + printk("nicstar: Installing %s of %s %s.\n",__FILE__,__DATE__,__TIME__); + devs = do_detect(void); + if (!devs) { + printk(KERN_ERR "nicstar: no adapter found\n"); + return -ENXIO; + } + return 0; +} + + +void cleanup_module(void) { + int i, error; + nicstar_devp node; + unsigned short pci_command; + + PRINTK("nicstar: cleanup_module called\n"); + + if (MOD_IN_USE) + PRINTK("nicstar: Device busy, remove delayed.\n"); + + /* Free up the device resources */ + for (i = 0; i < NICSTAR_MAX_DEVS; i++) { + if (nicstar_devs[i] != NULL) { + node = nicstar_devs[i]; + /* Turn everything off */ + writel(0x00000000, (node->membase)+CFG); + + /* Disable PCI busmastering */ + if (pcibios_read_config_word(node->pci_bus,node->pci_devfn,PCI_COMMAND,&pci_command)) { + printk("nicstar: Can't read PCI_COMMAND from device %d.\n",node->index); + continue; + } + pci_command &= ~PCI_COMMAND_MASTER; + error = pcibios_write_config_word(node->pci_bus,node->pci_devfn, + PCI_COMMAND, pci_command); + if (error) { + printk("nicstar: Can't disable busmastering for device %d.\n",node->index); + continue; + } + + drain_scq(node,node->scq,node->scq->next - node->scq->base); + kfree(node->rx_statq_orig); + kfree(node->tx_statq_orig); + kfree(node->sm_bufs); + kfree(node->lg_bufs); + kfree(node->scq->orig); + kfree(node->scq); + free_irq(node->irq, node); + + { + struct sk_buff *skb; + while ((skb = skb_dequeue(&node->rx_skb_queue))) + dev_kfree_skb(skb, FREE_READ); + } + + atm_dev_deregister(node->dev); + + kfree(nicstar_devs[i]); + } + } + + printk("nicstar: Module cleanup succeeded.\n"); + +} + + +#endif + + +/* NICStAR functions *******************************************************/ + +static inline u32 read_sram(nicstar_devp node, u32 addr) { + u32 data; + unsigned long flags; + + save_flags(flags); cli(); + while (CMD_BUSY(node)) ; + writel(((0x50000000) | ((addr & 0x1ffff) << 2)), node->membase + CMD); + while (CMD_BUSY(node)) ; + data = readl(node->membase + DR0); + restore_flags(flags); + return data; +} + +static inline void write_sram(nicstar_devp node, int count, u32 addr, + u32 d0, u32 d1, u32 d2, u32 d3) { + unsigned long flags; + + save_flags(flags); cli(); + while (CMD_BUSY(node)); + switch (count) { + case 4: + writel(d3, node->membase + DR3); + case 3: + writel(d2, node->membase + DR2); + case 2: + writel(d1, node->membase + DR1); + case 1: + writel(d0, node->membase + DR0); + break; + default: + restore_flags(flags); + return; + } + + writel((0x40000000) | ((addr & 0x1ffff) << 2) | (count-1), node->membase+CMD); + restore_flags(flags); + return; +} + +static int init_nicstar(struct nicstar_dev *node) { + u32 i, d; + unsigned short pci_command; + int error; + u32 tmp; + + d = readl(node->membase + STAT); + /* Clear timer if overflow */ + if (d & 0x00000800) { + writel(0x00000800, node->membase+STAT); + } + + /* S/W reset */ + writel(0x80000000, node->membase+CFG); + SLOW_DOWN_IO; SLOW_DOWN_IO; SLOW_DOWN_IO; + writel(0x00000000, node->membase+CFG); + + /* Turn everything off, but use 4k recv status queue */ + writel(0x00400000, node->membase+CFG); + d = readl(node->membase + CFG); + + /* Determine PHY type/speed by reading PHY reg 0 (from IDT) */ + /* YYY This tested only for 77105 (SWD) */ + writel(0x80000200,node->membase + CMD); /* read PHY reg 0 command */ + while (CMD_BUSY(node)) ; + tmp = readl(node->membase + DR0); + if (tmp == 0x9) { + node->max_pcr = IDT_25_PCR; + printk ("nicstar%d: PHY device appears to be 25Mbps\n",node->index); + } + else if (tmp == 0x30) { + node->max_pcr = ATM_OC3_PCR; + printk ("nicstar%d: PHY device appears to be 155Mbps\n",node->index); + } + +#ifdef TEST_LOOPBACK + /* PHY reset */ + d = readl(node->membase+GP); + writel(d & 0xfffffff7, node->membase+GP); /* Out of reset */ + { int j; for (j = 0; j < 100000; j++) ; } + writel(d | 0x00000008, node->membase+GP); /* Reset */ + { int j; for (j = 0; j < 100000; j++) ; } + writel(d & 0xfffffff7, node->membase+GP); /* Out of reset */ + + /* PHY loopback */ + while(CMD_BUSY(node)) ; + printk ("nicstar%d: Setting PHY Loopback\n",node->index); + writel(0x00000022, node->membase + DR0); + if (node->max_pcr == IDT_25_PCR) { /* loopback for 77105 */ + writel(0x90000202, node->membase + CMD); + } + else { /* assume 77155 */ + writel(0x90000205, node->membase + CMD); + } + +#else + + /* PHY reset */ + d = readl(node->membase+GP); + writel(d & 0xfffffff7, node->membase+GP); /* Out of reset */ + { int j; for (j = 0; j < 100000; j++) ; } + writel(d | 0x00000008, node->membase+GP); /* Reset */ + { int j; for (j = 0; j < 100000; j++) ; } + writel(d & 0xfffffff7, node->membase+GP); /* Out of reset */ + + while (CMD_BUSY(node)) ; + writel(0x80000100, node->membase+CMD); /* Sync UTOPIA with SAR clock */ + { int j; for (j = 0; j < 100000; j++) ; } + + /* Normal mode */ + while(CMD_BUSY(node)) ; + writel(0x00000020, node->membase + DR0); + if (node->max_pcr == IDT_25_PCR) { /* 77105 */ + writel(0x90000202, node->membase + CMD); + } + else { /* assume 77155 */ + writel(0x90000205, node->membase + CMD); + } + +#endif /* PHY_LOOPBACK */ + + /* Re-enable memory space and busmastering --- in case the PCI reset + * turned us off ... + */ + if (pcibios_read_config_word(node->pci_bus,node->pci_devfn,PCI_COMMAND,&pci_command)) { + printk("nicstar: Can't read PCI_COMMAND from device %d.\n",node->index); + return -EIO; + } + pci_command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_SPECIAL | PCI_COMMAND_MASTER); + error = pcibios_write_config_word(node->pci_bus,node->pci_devfn,PCI_COMMAND, + pci_command); + if (error) { + printk("nicstar: Can't enable board memory for device %d.\n",node->index); + return -EIO; + } + + /* Set up the recv connection table */ + for (i = 0; i < RX_CTABLE_SIZE; i+=4) { +#if NICSTAR_RCQ + write_sram(node, 4, i, 0x00008000, 0x00000000, 0x00000000, 0xffffffff); +#else + write_sram(node, 4, i, 0x00000000, 0x00000000, 0x00000000, 0xffffffff); +#endif + } + + writel(0x0, node->membase + VPM); + + /* Clear VCI map and SCQ shadow */ + memset(node->vci_map, 0, sizeof(node->vci_map)); + memset(node->tmp_cell, 0, sizeof(node->tmp_cell)); + + /* Allocate Rx status queue */ + /* XXX What about 64-bit pointers? */ + node->rx_statq_orig = kmalloc(2*8192, GFP_KERNEL); + if (!node->rx_statq_orig) { + printk("nicstar%d: Can't allocate Rx status queue.\n",node->index); + return -ENOMEM; + } + node->rx_statq = (nicstar_rsqe *)(((u32)node->rx_statq_orig + (8192 - 1)) & ~(8192 - 1)); + + printk("nicstar%d: Rx status queue at 0x%x (0x%x).\n",node->index, (u32)node->rx_statq, (u32)node->rx_statq_orig); + + for (i = 0; i < RX_STATQ_ENTRIES; i++) { + node->rx_statq[i].vpi_vci = 0x0; + node->rx_statq[i].buf_handle = 0x0; + node->rx_statq[i].crc = 0x0; + node->rx_statq[i].status = 0x0; + } + + node->rx_statq_next = node->rx_statq; + node->rx_statq_last = node->rx_statq + (RX_STATQ_ENTRIES - 1); + writel((u32)node->rx_statq, node->membase + RSQB); + writel((u32)0x0, node->membase + RSQH); + + /* Allocate Tx status queue */ + /* XXX What about 64-bit pointers? */ + node->tx_statq_orig = (caddr_t)kmalloc(2*8192, GFP_KERNEL); + if (!node->tx_statq_orig) { + printk("nicstar%d: Can't allocate Tx status queue.\n",node->index); + return -ENOMEM; + } + node->tx_statq = (nicstar_tsi *)(((u32)node->tx_statq_orig + (8192 - 1)) & ~(8192 - 1)); + + printk("nicstar%d: Tx status queue at 0x%x (0x%x).\n",node->index, (u32)node->tx_statq, (u32)node->tx_statq_orig); + + for (i = 0; i < TX_STATQ_ENTRIES; i++) { + node->tx_statq[i].timestamp = 0x80000000; + } + node->tx_statq_next = node->tx_statq; + node->tx_statq_last = node->tx_statq + (TX_STATQ_ENTRIES - 1); + + writel((u32)node->tx_statq, node->membase + TSQB); + writel((u32)0x0, node->membase + TSQH); + + /* Allocate buffer queues */ + + node->sm_bufs = (caddr_t)kmalloc(SM_BUFSZ * NUM_SM_BUFS, GFP_KERNEL); + if (!node->sm_bufs) { + printk("nicstar%d: Can't allocate %d %d-byte small buffers!\n", + node->index, NUM_SM_BUFS, SM_BUFSZ); + return -ENOMEM; + } + + node->lg_bufs = (caddr_t)kmalloc(LG_BUFSZ * NUM_LG_BUFS, GFP_KERNEL); + if (!node->lg_bufs) { + printk("nicstar%d: Can't allocate %d %d-byte large buffers!\n", + node->index, NUM_LG_BUFS, LG_BUFSZ); + return -ENOMEM; + } + + printk("nicstar%d: Small buffers at 0x%x, Large buffers at 0x%x.\n", + node->index, (u32)node->sm_bufs, (u32)node->lg_bufs); + +#if NICSTAR_RCQ + node->rcq.rcq_head = (u32) node->lg_bufs; + node->rcq.rcq_base = (u32) node->lg_bufs; + /* Manual says we should check head vs tail here; but tail reg + isn't updated until after we turn on RX path. Moved check to + end of init_module... */ +#endif + + for (i = 0; i < NUM_SM_BUFS; i+=2) { + push_rxbufs(node, 0, + (u32)(node->sm_bufs + (i*SM_BUFSZ) + 4), + (u32)(node->sm_bufs + (i*SM_BUFSZ) + 4), + (u32)(node->sm_bufs + ((i+1)*SM_BUFSZ) + 4), + (u32)(node->sm_bufs + ((i+1)*SM_BUFSZ) + 4)); + } + + for (i = 0; i < NUM_LG_BUFS; i+=2) { + push_rxbufs(node, 1, + (u32)(node->lg_bufs + (i*LG_BUFSZ)), + (u32)(node->lg_bufs + (i*LG_BUFSZ)), + (u32)(node->lg_bufs + ((i+1)*LG_BUFSZ)), + (u32)(node->lg_bufs + ((i+1)*LG_BUFSZ))); + } + + skb_queue_head_init(&node->rx_skb_queue); + for (i = 0; i < NUM_RX_SKB; i++) { + struct sk_buff *skb; + skb = alloc_skb(MAX_SDU_BUFS * sizeof(struct iovec), GFP_KERNEL); + if (!skb) { + printk("nicstar%d: Can't allocate Rx skb!\n",node->index); + return -ENOMEM; + } + skb_queue_tail(&node->rx_skb_queue,skb); + } + + + /* Allocate seg chan queue */ + /* XXX What about 64-bit pointers? */ + i = create_scq(&(node->scq),SCQ_SIZE); + node->scq->id = SCQFULL_MAGIC; + if (i != 0) { return i; } + + printk("nicstar%d: SCQ at 0x%x.\n",node->index,(u32)node->scq->base); + + write_sram(node, 4, NICSTAR_VBR_SCD0, + (u32)node->scq->base, /* SCQ base */ + (u32)0x0, /* tail */ + (u32)0xffffffff, /* AAL5 CRC */ + (u32)0x0); /* rsvd */ + node->scq->scd = NICSTAR_VBR_SCD0; + + /* Install TST */ + + /* Create host transmit schedule table */ + if ((((u32) node->tx_statq - (u32) node->tx_statq_orig) - (4 * TST_SIZE)) < 1) { + printk ("Need to move host TST to later %d\n",4*TST_SIZE); + node->host_tst = (u32 *) ((u32) node->tx_statq_orig - 8 - (4 * TST_SIZE)); + printk ("New host_tst %x\n",(u32)node->host_tst); + } + else { + node->host_tst = (u32 *) node->tx_statq_orig; + } + + node->available_slots = TST_SIZE; + + memset((caddr_t) node->host_tst, 0, 4 * TST_SIZE); + +#ifdef NICSTAR_CBR + for (i = NICSTAR_TST_REGION; i < NICSTAR_TST_REGION + TST_SIZE; i += 4) { + write_sram(node, 4, i, + (u32) TSTE_VBR, /* Tx VBR SCD */ + (u32) TSTE_VBR, /* Tx VBR SCD */ + (u32) TSTE_VBR, /* Tx VBR SCD */ + (u32) TSTE_VBR ); /* Tx VBR SCD */ + } + + write_sram(node, 1, NICSTAR_TST_REGION + TST_SIZE, + (u32)0x60000000 | NICSTAR_TST_REGION, /* Jump to start */ + 0x0, 0x0, 0x0); + +#else + write_sram(node, 2, NICSTAR_TST_REGION + TST_SIZE, + (u32)0x40000000, /* Tx VBR SCD */ + /* (u32)0x20000000 | NICSTAR_SCD_REGION, *//* Tx SCD */ + (u32)0x60000000 | NICSTAR_TST_REGION, /* Jump to start */ + 0x0, 0x0); +#endif + + writel((u32)(NICSTAR_TST_REGION << 2), node->membase + TSTB); + +#if NICSTAR_RCQ + /* Turn on everything */ + writel(0x21801c38 | 0x200 | 0x800, node->membase+CFG); + /* | 0x800 turns on raw cell interrupts */ + /* | 0x200 for raw cell receive */ + /* | 0x8000 to receive cells that don't map via RCT (not there/closed) */ +#else + writel(0x21801c38, node->membase+CFG); /* Turn on everything */ +#endif + + while(CMD_BUSY(node)); +#if NICSTAR_RCQ + printk("nicstar%d: Checking raw cell queue pointer...\n",node->index); + if (node->rcq.rcq_head != readl(node->membase + RAWCT)) { + printk("nicstar%d: Raw cell head %x tail %x don't match\n", + node->index,(u32)node->rcq.rcq_head, readl(node->membase + RAWCT)); + } +#endif + return 0; +} + +static void nicstar_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + nicstar_devp node = (nicstar_devp)dev_id; + u32 stat, wr_stat; + + PRINTK("nicstar: interrupt on %d.\n",irq); + + wr_stat = 0x0; + stat = readl(node->membase + STAT); + PRINTK("nicstar%d: Interrupt: STAT 0x%x.\n",node->index, stat); + + if (stat & 0x8000) { + wr_stat |= 0x8000; + PRINTK("nicstar%d: TSI written to Tx status queue.\n",node->index); + process_tx_statq(node); + } + if (stat & 0x4000) { + wr_stat |= 0x4000; + printk("nicstar%d: Incomplete CS-PDU transmitted.\n",node->index); + } + + if (stat & 0x800) { + wr_stat |= 0x800; + PRINTK("nicstar%d: Timer overflow.\n",node->index); + } + if (stat & 0x100) { + printk("nicstar%d: Small buffer queue full (this is OK).\n",node->index); + } + if (stat & 0x80) { + printk("nicstar%d: Large buffer queue full (this is OK).\n",node->index); + } + if (stat & 0x40) { + printk("nicstar%d: Rx status queue full.\n",node->index); + wr_stat |= 0x40; + process_rx_statq(node); + } + if (stat & 0x20) { + /* wr_stat |= 0x20; */ + writel(0x20, node->membase + STAT); /* XXX mdw ack immediately */ + PRINTK("nicstar%d: End of PDU received.\n", node->index); + process_rx_statq(node); + } + if (stat & 0x10) { + wr_stat |= 0x10; + printk("nicstar%d: Raw cell received.\n",node->index); +#ifdef NICSTAR_RCQ + process_rx_rawcellq(node); +#endif + } + if (stat & 0x8) { + printk("nicstar%d: Small buffer queue empty, disabling interrupt.\n",node->index); + writel((readl(node->membase + CFG) & ~0x01000000), node->membase + CFG); + wr_stat |= 0x8; + } + if (stat & 0x4) { + printk("nicstar%d: Large buffer queue empty, disabling interrupt.\n",node->index); + writel((readl(node->membase + CFG) & ~0x01000000), node->membase + CFG); + wr_stat |= 0x4; + } + if (stat & 0x2) { + printk("nicstar%d: Rx status queue almost full.\n",node->index); + wr_stat |= 0x2; + /* XXX Turn off interrupt */ + writel((readl(node->membase + CFG) & ~0x00000400), node->membase + CFG); + process_rx_statq(node); + } + + if (wr_stat != 0x0) { + writel(wr_stat, node->membase + STAT); + } + + return; +} + + +static void process_tx_statq(nicstar_devp node) { + + volatile nicstar_tsi *next; + nicstar_scq *scq; + u32 id; + + /* Spin until something arrives. If we get a timer-rollover and a + * TSI simultaneously this could be dangerous; disabling timer + * interrupt will prevent that. + */ + next = node->tx_statq_next; + while (next->timestamp & 0x80000000) ; + + while (!(node->tx_statq_next->timestamp & 0x80000000)) { + PRINTK("nicstar%d: Tx statq entry 0x%x 0x%x\n", + node->index, + node->tx_statq_next->status, + node->tx_statq_next->timestamp); + + PRINTK ("Processing entry at %x with val %x\n",(u32) node->tx_statq_next, node->tx_statq_next->status); + +#if 0 /* original method */ + if ((node->tx_statq_next->status & 0xffff0000) == SCQFULL_MAGIC) { + drain_scq(node, node->scq, node->tx_statq_next->status & 0xffff); + /* SCQ drained, wake 'er up */ + node->scq->full = 0; + wake_up_interruptible(&node->scq->scqfull_waitq); + } +#else + id = (node->tx_statq_next->status & 0xf0000000) >> 16; + switch (id) { + case (SCQFULL_MAGIC >> 16): + scq = node->scq; + break; +#if NICSTAR_CBR + case (CBRSCQFULL_MAGIC_CLOSE >> 16): + scq = node->vci_map[(node->tx_statq_next->status & 0x0fff0000)>>16].scq; + scq->closing = 1; + break; + case (CBRSCQFULL_MAGIC >> 16): + scq = node->vci_map[(node->tx_statq_next->status & 0x0fff0000)>>16].scq; + break; +#endif + default: + scq = node->scq; + } + PRINTK ("Draining SCQ %d at %x\n",id,(u32)scq); + drain_scq(node, scq, node->tx_statq_next->status & 0xffff); + /* SCQ drained, wake 'er up */ + scq->full = 0; + wake_up_interruptible(&scq->scqfull_waitq); +#endif + + node->tx_statq_next->timestamp |= 0x80000000; + + if (node->tx_statq_next == node->tx_statq_last) + node->tx_statq_next = node->tx_statq; + else + node->tx_statq_next++; + } + + writel((u32)node->tx_statq_next - (u32)node->tx_statq, + node->membase + TSQH); + + return; +} + +#if NICSTAR_RCQ +static void process_rx_rawcellq (nicstar_devp node) { + u32 tail; + u32 timer; + u32 last; + int count; + + tail = readl(node->membase + RAWCT); + timer = readl(node->membase + TMR); + count = 0; + while (node->rcq.rcq_head != tail - RAWCELLSZ) { + if (count > 3) { + printk ("nicstar OOOPS. Count > 3\n"); + return; + } + /* service head */ + PRINTK ("nicstar: dequeueing 0x%x to 0x%x\n",(u32)node->rcq.rcq_head, + readl(node->membase + RAWCT)); + dequeue_rawcell((u32 *) node->rcq.rcq_head); + node->rcq.rcq_head += RAWCELLSZ; + last = node->rcq.rcq_base + LG_BUFSZ - RAWCELLSZ; + PRINTK ("Last %x tail %x rawct %x\n",(u32)last,(u32)tail,readl(node->membase + RAWCT)); + if (node->rcq.rcq_head == (node->rcq.rcq_base + LG_BUFSZ - RAWCELLSZ)) { + node->rcq.rcq_head = *((u32 *) node->rcq.rcq_head); + free_rx_buf(node, 1, node->rcq.rcq_base); + node->rcq.rcq_base = node->rcq.rcq_head; + if (node->rcq.rcq_base == tail) return; /* watch out for jumps */ + } + count++; + } + /* YYY Might be able to avoid this by polling the cell header; + requires initializing the header to a known value. Also + assumes that we can find such a known value */ + while (((readl(node->membase+TMR)) - timer) < 30); + /* service one cell */ + dequeue_rawcell((u32 *) node->rcq.rcq_head); + node->rcq.rcq_head += RAWCELLSZ; + if (node->rcq.rcq_head == (node->rcq.rcq_base + LG_BUFSZ - RAWCELLSZ)) { + node->rcq.rcq_head = *((u32 *) node->rcq.rcq_head); + free_rx_buf(node, 1, node->rcq.rcq_base); + node->rcq.rcq_base = node->rcq.rcq_head; + } +} + +static void dequeue_rawcell (u32 *cell) { + u32 header; + u32 *payload; + int i; + + header = *cell; + payload = cell + 4; + printk("nicstar: *Raw cell %x arrival hdr=%x pay=%x*\n", + (u32)cell,header,(u32)payload); + printk("nicstar: Header contents VCI=%d Last=%d\n", + header >> 4, (header & 0x2) >> 1); + if (header & 0x2) { + printk("nicstar: PDU length %d\n", + ((payload[10] & 0xff000000) >> 24) + + (256 * ((payload[10] & 0x00ff0000) >> 16))); + } + for (i = 0; i < 12; i++) { + printk (" %d 0x%x\n",i,(u32)payload[i]); + } +} +#endif /* NICSTAR_RCQ */ + +static void process_rx_statq(nicstar_devp node) { + u32 prev; + + /* XXX Problem here: The 77201 can interrupt us (and set EPDU + * bits in STAT) before the Rx statq entry arrives in memory. + * The interrupt could be raised for multiple incoming PDUs, + * and we could miss seeing those entries that are slow to + * arrive in memory at the end of this loop. We'll see the PDU + * on the next Rx EPDU interrupt, of course. + */ + while (node->rx_statq_next->status & 0x80000000) { + PRINTK("nicstar%d: Rx statq entry 0x%x 0x%x 0x%x 0x%x\n", + node->index, + node->rx_statq_next->vpi_vci, + node->rx_statq_next->buf_handle, + node->rx_statq_next->crc, + node->rx_statq_next->status); + + dequeue_rx(node); + node->rx_statq_next->status = 0x0; + + if (node->rx_statq_next == node->rx_statq_last) + node->rx_statq_next = node->rx_statq; + else + node->rx_statq_next++; + + } + + /* Update the RSQH to point to the last entry actually processed. */ + if (node->rx_statq_next == node->rx_statq) { + prev = (u32)node->rx_statq_last; + } else { + prev = (u32)(node->rx_statq_next - 1); + } + writel((u32)prev - (u32)node->rx_statq, + node->membase + RSQH); + + PRINTK("RSQ: 0x%x, next 0x%x, diff 0x%x, tail 0x%x\n", + (u32)node->rx_statq, + (u32)node->rx_statq_next, + (u32)node->rx_statq_next - (u32)node->rx_statq, + readl(node->membase + RSQT)); + + PRINTK("nicstar%d: Status reads 0x%x.\n", node->index, + readl(node->membase + STAT)); + + return; +} + +static void push_rxbufs(nicstar_devp node, int lg, + u32 handle1, u32 addr1, u32 handle2, u32 addr2) { + + unsigned long flags; + int bc; + + + if (lg) { + bc = (readl(node->membase + STAT) & 0x00ff0000) >> 16; + } else { + bc = (readl(node->membase + STAT) & 0xff000000) >> 24; + } + if (bc >= 254) return; + + save_flags(flags); cli(); + while (CMD_BUSY(node)) ; + + writel(handle1, node->membase + DR0); + writel(addr1, node->membase + DR1); + writel(handle2, node->membase + DR2); + writel(addr2, node->membase + DR3); + + writel(0x60000000 | (lg ? 0x01 : 0x00), node->membase + CMD); + restore_flags(flags); + return; +} + +static void open_rx_connection(nicstar_devp node, + int index, + u32 status) { +#if 0 + unsigned long flags; +#endif + + u32 sram_addr = index * (sizeof(nicstar_rcte)/sizeof(u32)); + + write_sram(node, 1, sram_addr, status | 0x80000, + 0x0, 0x0, 0x0); + +#if 0 + /* This is actually unnecessary, I believe, if we add the + open connection bit to the SRAM write. Alternately, if + we initialize the AAL during init_module, we can just + use the open connection command... (SWD) */ + save_flags(flags); cli(); + while (CMD_BUSY(node)) ; + writel(0x20080000 | (sram_addr << 2), node->membase + CMD); + restore_flags(flags); +#endif +} + +static void close_rx_connection(nicstar_devp node, + int index) { + unsigned long flags; + u32 sram_addr = index * (sizeof(nicstar_rcte)/sizeof(u32)); + + save_flags(flags); cli(); + while (CMD_BUSY(node)) ; + writel(0x20000000 | (sram_addr << 2), node->membase + CMD); + restore_flags(flags); +} + +static void push_scq_entry(nicstar_devp node, struct nicstar_scq *scq, + nicstar_tbd *entry, + int xmit_now) { + + unsigned long flags; + nicstar_tbd magic_bullet = {0xa0000000, scq->id, 0x0, 0x0}; + static int delayed_bullet = 0; + + PRINTK("tail 0x%x next 0x%x\n", + (u32) scq->tail, (u32) scq->next); + PRINTK("CFG: 0x%x\n",readl(node->membase + CFG)); + + if (scq->tail == scq->next) { + PRINTK ("Going to sleep\n"); + /* Sleep until the tail moves */ + save_flags(flags); cli(); + scq->full = 1; + + current->timeout = jiffies + SCQFULL_TIMEOUT; + interruptible_sleep_on(&scq->scqfull_waitq); + restore_flags(flags); + + if (scq->full) { + printk("nicstar%d: SCQ drain timed out.\n",node->index); + scq->full = 0; + /* XXX Just proceed here, although the SAR's probably hosed ... */ + } + } + + *scq->next = *entry; + + if (scq->next == scq->last) + scq->next = scq->base; + else + scq->next++; + + if (((scq->next - scq->base) % NUM_SCQ_TRAIL) == NUM_SCQ_TRAIL-1) { + delayed_bullet = 1; + } + + if (xmit_now) { + if (delayed_bullet) { + delayed_bullet = 0; + magic_bullet.buf_addr |= (u32)(scq->next - scq->base); + if (scq->tail == scq->next) { + PRINTK ("Going to sleep!!\n"); + /* Sleep until the tail moves */ + save_flags(flags); cli(); + scq->full = 1; + + current->timeout = jiffies + SCQFULL_TIMEOUT; + interruptible_sleep_on(&scq->scqfull_waitq); + restore_flags(flags); + + if (scq->full) { + printk("nicstar%d: SCQ drain timed out.\n",node->index); + scq->full = 0; + /* XXX Just proceed here, although the SAR's probably hosed ... */ + } + } + *scq->next = magic_bullet; + if (scq->next == scq->last) + scq->next = scq->base; + else + scq->next++; + } + write_sram(node, 1, scq->scd, + (u32)(scq->next), + 0x0, 0x0, 0x0); + } + + return; +} + +static int get_ci(nicstar_devp node, + struct atm_vcc *vcc, + short *vpi, int *vci) { + nicstar_vcimap *mapval; + + PRINTK ("Reserving vci %d\n",(u32) *vci); + /* We accept VPI 0 and VCI 0-4095. */ + if (*vpi == ATM_VPI_UNSPEC || *vci == ATM_VCI_UNSPEC) return -EINVAL; + + if (*vpi == ATM_VPI_ANY) *vpi = 0; + if (*vpi != 0) return -EINVAL; + + if (*vci == ATM_VCI_ANY) { + /* Find lowest VCI */ +#ifndef ATM_013 + for (*vci = ATM_NOT_RSV_VCI; *vci < NUM_VCI; (*vci)++) { + mapval = &(node->vci_map[*vci]); + if (vcc->qos.rxtp.traffic_class != ATM_NONE && mapval->rx) continue; + if (vcc->qos.txtp.traffic_class != ATM_NONE && mapval->tx) continue; + if (vcc->qos.rxtp.traffic_class != ATM_NONE) { + mapval->rx = 1; mapval->rx_vcc = vcc; + } + if (vcc->qos.txtp.traffic_class != ATM_NONE) { + mapval->tx = 1; mapval->tx_vcc = vcc; + } +#else + if (vcc->qos.rxtp.class != ATM_NONE && mapval->rx) continue; + if (vcc->qos.txtp.class != ATM_NONE && mapval->tx) continue; + if (vcc->qos.rxtp.class != ATM_NONE) { + mapval->rx = 1; mapval->rx_vcc = vcc; + } + if (vcc->txtp.class != ATM_NONE) { + mapval->tx = 1; mapval->tx_vcc = vcc; + } + break; +#endif /* ATM_013 */ + } + if (*vci == NUM_VCI) return -EADDRINUSE; + + } else { + /* Use this particular VCI */ + if (*vci >= NUM_VCI) return -EINVAL; + mapval = &(node->vci_map[*vci]); +#ifndef ATM_013 + if (vcc->qos.rxtp.traffic_class != ATM_NONE && mapval->rx) return -EADDRINUSE; + if (vcc->qos.txtp.traffic_class != ATM_NONE && mapval->tx) return -EADDRINUSE; + if (vcc->qos.rxtp.traffic_class != ATM_NONE) { + mapval->rx = 1; mapval->rx_vcc = vcc; + } + if (vcc->qos.txtp.traffic_class != ATM_NONE) { + mapval->tx = 1; mapval->tx_vcc = vcc; + } +#else + if (vcc->rxtp.class != ATM_NONE && mapval->rx) return -EADDRINUSE; + if (vcc->txtp.class != ATM_NONE && mapval->tx) return -EADDRINUSE; + if (vcc->rxtp.class != ATM_NONE) { + mapval->rx = 1; mapval->rx_vcc = vcc; + } + if (vcc->txtp.class != ATM_NONE) { + mapval->tx = 1; mapval->tx_vcc = vcc; + } +#endif + } + + return 0; +} + +static void free_rx_buf(nicstar_devp node, int lg, u32 buf_addr) { + static u32 sm_buf = 0x0, lg_buf = 0x0; + + if (lg) { + /* Large buffer */ + if (lg_buf == 0x0) { + lg_buf = buf_addr; + } else { + u32 buf; + buf = buf_addr; + push_rxbufs(node, 1, lg_buf, lg_buf, buf, buf); + lg_buf = 0x0; + } + } else { + /* Small buffer */ + if (sm_buf == 0x0) { + sm_buf = buf_addr; + } else { + u32 buf; + buf = buf_addr; + push_rxbufs(node, 0, sm_buf, sm_buf, buf, buf); + sm_buf = 0x0; + } + } + return; +} + +static void dequeue_rx(nicstar_devp node) { + + struct atm_vcc *vcc; + struct sk_buff *skb; + struct iovec *iov; + nicstar_vcimap *mapval; + volatile nicstar_rsqe *entry = (volatile nicstar_rsqe *)node->rx_statq_next; + unsigned short aal5_len; + unsigned short aal0_len; + u32 *aal0_ptr; + + if ((entry->vpi_vci & 0x00ff0000) || + ((entry->vpi_vci & 0xffff) > NUM_VCI)) { + printk("nicstar%d: SDU received for out of range VC %d.%d.\n", + node->index, + entry->vpi_vci >> 16, + entry->vpi_vci & 0xffff); + return; + } + + mapval = &(node->vci_map[entry->vpi_vci]); + if (!mapval->rx) { + printk("nicstar%d: SDU received on inactive VC 0.%d.\n", + node->index, entry->vpi_vci); + } + vcc = mapval->rx_vcc; + + if (!(skb = mapval->rx_skb)) { + /* Starting new SKB */ + skb = mapval->rx_skb = skb_dequeue(&node->rx_skb_queue); + if (!skb) { + printk("nicstar%d: Out of Rx skb's!\n",node->index); + goto drop_message; + } + } + + if (vcc->aal == ATM_AAL0) { + iov = &(((struct iovec *)skb->data)[skb->atm.iovcnt]); + aal0_ptr = (u32 *)entry->buf_handle; + aal0_ptr--; + *(aal0_ptr) = /* "reconstruct" cell header */ + (entry->vpi_vci << 4) | ((entry->status & 0x4000) >> 14)| + ((entry->status & 0x400) >> 10); + PRINTK("Reconstructed cell header %x at %x, bufhandle %x\n", + *aal0_ptr,(u32)aal0_ptr,entry->buf_handle); + iov->iov_base = (void *)(aal0_ptr); + aal0_len = ATM_CELL_SIZE - 1; + iov->iov_len = aal0_len; + skb->len = aal0_len; + skb->atm.iovcnt++; + vcc->stats->rx++; + skb->atm.timestamp = xtime; + vcc->push(vcc, skb); + mapval->rx_skb = NULL; + return; + } + + iov = &(((struct iovec *)skb->data)[skb->atm.iovcnt]); + iov->iov_base = (void *)entry->buf_handle; + + if (entry->status & 0x2000) { + /* Last buffer, get AAL5 len */ + aal5_len = *(unsigned short *)((unsigned char *)entry->buf_handle + ((entry->status & 0x1ff)*48) - 6); + aal5_len = ((aal5_len & 0xff) << 8) | ((aal5_len & 0xff00) >> 8); + PRINTK ("Last buffer; AAL5 len is %d\n",aal5_len); + + iov->iov_len = aal5_len - skb->len; + skb->len = aal5_len; + skb->atm.iovcnt++; + vcc->stats->rx++; + + vcc->push(vcc, skb); + mapval->rx_skb = NULL; + } else { + iov->iov_len = (entry->status & 0x1ff) * 48; + skb->len += iov->iov_len; + skb->atm.iovcnt++; + } + return; + +drop_message: + printk("nicstar%d: drop_message returned in dequeue_rx.\n",node->index); + vcc->stats->rx_drop++; + + if (entry->status & 0x1000) { + /* Large buffer */ + free_rx_buf(node, 1, entry->buf_handle); + } else { + /* Small buffer */ + free_rx_buf(node, 0, entry->buf_handle); + } + + return; +} + +static void nicstar_free_rx_skb(struct atm_vcc *vcc, struct sk_buff *skb) { + nicstar_devp node = vcc->dev->dev_data; + struct iovec *iov; + int i; + u32 ptr_chk; + + if (!skb->atm.iovcnt) { + /* XXX mdw: Eventually support this for single-cell transfers */ + printk("nicstar%d: free_rx_skb on non-iovec skb?\n", + node->index); + return; + } + + iov = (struct iovec *)skb->data; + for (i = 0; i < skb->atm.iovcnt; i++) { + if (((unsigned char *)iov[i].iov_base >= (unsigned char *)node->sm_bufs) && + ((unsigned char *)iov[i].iov_base < (unsigned char *)node->sm_bufs + SM_BUFSZ * NUM_SM_BUFS)) { + ptr_chk = (u32)iov[i].iov_base == (u32)node->sm_bufs ? 48 : + ((u32)iov[i].iov_base - ((u32)node->sm_bufs + 4)) % SM_BUFSZ; + if (ptr_chk) { + iov[0].iov_base += 4; +#ifdef NICSTAR_PARANOID + if (ptr_chk != 48) { + printk ("nicstar%d: Misaligned buffer pointer (offset %d)!\n", + node->index,ptr_chk); + } + ptr_chk = (u32)iov[i].iov_base == (u32)node->sm_bufs ? 48 : + ((u32)iov[i].iov_base - ((u32)node->sm_bufs + 4)) % SM_BUFSZ; + if (ptr_chk) { + printk("nicstar%d: Misaligned pointer STILL NOT FIXED!!! %x %d\n", + node->index,(u32)iov[i].iov_base,ptr_chk); + } +#endif + } + free_rx_buf(node, 0, (u32)(iov[i].iov_base)); + } else { + /* Large buffer */ + free_rx_buf(node, 1, (u32)(iov[i].iov_base)); + } + } + + skb->atm.iovcnt = 0; + skb->len = 0; + skb_queue_tail(&node->rx_skb_queue, skb); + + return; +} + +static void drain_scq(nicstar_devp node, struct nicstar_scq *scq, int index) { + nicstar_tbd *last = scq->base + index; + int sindex = scq->tail - scq->base; + struct sk_buff *skb; + struct atm_vcc *vcc; + + while (scq->tail != last) { + if ((skb = scq->scq_shadow[sindex].skb)) { + vcc = skb->atm.vcc; + if (skb->free == 2) printk("drain_scq: Passed skb 0x%x with free == 2.\n", (u32) skb); + if (vcc->pop) { + vcc->pop(vcc, skb); + } else { + dev_kfree_skb(skb, FREE_WRITE); + } + scq->scq_shadow[sindex].skb = 0; + } + + if (scq->tail == scq->last) { + scq->tail = scq->base; sindex = 0; + } else { + scq->tail++; sindex++; + } + } + + +#ifdef NICSTAR_CBR + if (scq->closing) { + close_cbr_final(node,node->vci_map[(scq->id >> 16) & 0xfff].tx_vcc); + } +#endif +} + +#ifdef NICSTAR_CBR +static void alloc_tx(nicstar_devp node, struct atm_vcc *vcc) { + int i, j; + int slots; + int spacing; + int vci; + + vci = vcc->vci; + create_scq(&((node->vci_map[vci]).scq),CBRSCQ_SIZE); + (node->vci_map[vci].scq)->id = CBRSCQFULL_MAGIC | (vci << 16); + PRINTK ("nicstar%d: Allocating CBR SCQ at %x\n",node->index,(u32) ((node->vci_map[vci]).scq)); + PRINTK ("nicstar%d: Allocating transmission bandwidth\n",node->index); +#ifndef ATM_013 + printk ("in pcr: %d/%d/%d\n",vcc->qos.txtp.min_pcr, + vcc->qos.txtp.max_pcr,node->max_pcr); +#else + printk ("in pcr: %d/%d/%d\n",vcc->txtp.min_pcr, + vcc->txtp.max_pcr,node->max_pcr); +#endif + (node->vci_map[vci].scq)->scd = 0; /* initialize SCD pointer */ +#ifndef ATM_013 + slots = (u32) (((float) vcc->qos.txtp.max_pcr / (float) node->max_pcr) * (float) TST_SIZE) + 1; +#else + slots = (u32) (((float) vcc->txtp.max_pcr / (float) node->max_pcr) * (float) TST_SIZE) + 1; +#endif + printk ("out: %d slots\n",slots); + if (slots > node->available_slots) { + printk("Reducing requested BW from %d to %d slots -- full up\n", + slots,node->available_slots); + slots = node->available_slots; + } + node->available_slots -= slots; + spacing = TST_SIZE / slots; + /* sort of randomly pick starting slot */ + i = readl(node->membase + TMR) % TST_SIZE; + while (slots > 0) { + j = i; + while (node->host_tst[j]) { + j++; + if (j >= TST_SIZE) { + j -= TST_SIZE; + } + } + if (!((node->vci_map[vci].scq)->scd)) { + (node->vci_map[vci].scq)->scd = NICSTAR_SCD_REGION + 12 * j; + write_sram(node, 4, NICSTAR_SCD_REGION + 12 * j, + (u32) ((nicstar_vcimap *)vcc->dev_data)->scq->base, /* base + head */ + 0x0, /* tail */ + 0xffffffff, /* aal5 crc */ + 0x0); + /* should we also init next 8 words to 0? */ + PRINTK ("CBR SCQ at j %d sram %x ptr %x\n",j, + NICSTAR_SCD_REGION + 12 * j, + read_sram(node,NICSTAR_SCD_REGION + 12 * j)); + } + node->host_tst[j] = (u32) vcc; + write_sram(node, 1, NICSTAR_TST_REGION + j, + TSTE_CBR | (node->vci_map[vci].scq)->scd, + 0x0, 0x0, 0x0); + i += spacing; + if (i >= TST_SIZE) { + i -= TST_SIZE; + } + slots--; + } +#if 0 /* dumps TST */ + for (i = 0; i < TST_SIZE; i+=4) { + printk("%d %08x %08x %08x %08x\n",i, + read_sram(node, NICSTAR_TST_REGION + i), + read_sram(node, NICSTAR_TST_REGION + i + 1), + read_sram(node, NICSTAR_TST_REGION + i + 2), + read_sram(node, NICSTAR_TST_REGION + i + 3)); + } +#endif +} + +/* Closes down a CBR connection. */ +static void close_cbr (nicstar_devp node, struct atm_vcc *vcc) { + unsigned long flags; + struct nicstar_scq *scq = ((nicstar_vcimap *)vcc->dev_data)->scq; + + PRINTK("Initial close down of CBR connection %x...",(u32) vcc); + /* Push a TSR */ + if (scq->tail == scq->next) { + /* Sleep until the tail moves */ + save_flags(flags); cli(); + scq->full = 1; + + current->timeout = jiffies + SCQFULL_TIMEOUT; + interruptible_sleep_on(&scq->scqfull_waitq); + restore_flags(flags); + + if (scq->full) { + printk("nicstar%d: SCQ drain timed out.\n",node->index); + scq->full = 0; + /* XXX Just proceed here, although the SAR's probably hosed ... */ + } + } + + PRINTK("Writing TSR\n"); + + scq->next->status = 0xa0000000; + scq->next->buf_addr = CBRSCQFULL_MAGIC_CLOSE | scq->id | (u32)(scq->next - scq->base); + scq->next->ctl_len = 0; + scq->next->cell_hdr = 0; + + if (scq->next == scq->last) + scq->next = scq->base; + else + scq->next++; + write_sram(node, 1, scq->scd, + (u32)(scq->next), + 0x0, 0x0, 0x0); +} + +static void close_cbr_final (nicstar_devp node, struct atm_vcc *vcc) { + int i, count; + + for (i = 0; i < TST_SIZE; i++) { + if (node->host_tst[i] == (u32) vcc) { + node->host_tst[i] = 0; + node->available_slots += 1; + write_sram(node,1, NICSTAR_TST_REGION + i, + TSTE_VBR, 0x0, 0x0, 0x0); + } + } + count = 0; + for (i = 0; i < TST_SIZE; i++) { + if (read_sram(node,NICSTAR_TST_REGION + i) != TSTE_VBR) { + count ++; + } + } + PRINTK ("non-VBR Count %d avail slots %d\n",count, node->available_slots); + /* free up the CBR SCQ */ + kfree(((nicstar_vcimap *)vcc->dev_data)->scq->orig); + kfree(((nicstar_vcimap *)vcc->dev_data)->scq); +} +#endif + +/* General function for creating transmission SCQs */ +static int create_scq (struct nicstar_scq **scq, int size) { + (*scq) = kmalloc(sizeof(struct nicstar_scq), GFP_KERNEL); + (*scq)->orig = kmalloc(2*size, GFP_KERNEL); + if (!(*scq)->orig) { + return -ENOMEM; + } + (*scq)->base = (nicstar_tbd *)(((u32)(*scq)->orig + (size - 1)) & ~(size - 1)); + (*scq)->next = (*scq)->base; + (*scq)->last = (*scq)->base + ((size / TBD_SIZE) - 1); + (*scq)->tail = (*scq)->last; /* XXX mdw scq_next */ + (*scq)->full = 0; + (*scq)->scqfull_waitq = NULL; + (*scq)->closing = 0; + memset (((*scq)->scq_shadow), 0, + (SCQ_ENTRIES) * (sizeof(struct nicstar_scq_shadow))); + return 0; +} diff -ur --new-file old/linux/drivers/atm/nicstar.h new/linux/drivers/atm/nicstar.h --- old/linux/drivers/atm/nicstar.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/atm/nicstar.h Sat Nov 16 00:48:31 1996 @@ -0,0 +1,265 @@ +/* nicstar.h, M. Welsh (matt.welsh@cl.cam.ac.uk) + * Definitions for IDT77201. + * + * Copyright (c) 1996 University of Cambridge Computer Laboratory + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * M. Welsh, 6 July 1996 + * + */ + +#ifndef _LINUX_NICSTAR_H +#define _LINUX_NICSTAR_H + +#include +#include + +/* User definitions *********************************************************/ + +#define NICSTAR_DEV_MAJOR 35 + +/* Kernel definitions ******************************************************/ + +#ifdef __KERNEL__ + +#define NICSTAR_MAX_DEVS 5 +#define NICSTAR_IO_SIZE (4*1024) + +#define SM_BUFSZ 52 +#define NUM_SM_BUFS 170 +#define LG_BUFSZ 2048 +#define RAWCELLSZ 64 +#define NUM_LG_BUFS 32 +#define MAX_SDU_BUFS 32 /* Max number of buffers in an AAL5 SDU */ +#define NUM_RX_SKB 32 /* Number of SKB's to allocate for receive */ +#define NUM_SCQ_TRAIL 32 /* Number of outstanding SCQ entries */ +#define TST_SIZE 200 /* Size of the Transmit Schedule Table + This determines resolution for CBR, + and thus lower bound on available BW */ +#define IDT_25_PCR (25600000/270*260/8/53) + /* Link rate: 25600000 bps + SONET overhead: /270*260 (9 section, 1 path) + bits per cell: /8/53 + max cell rate: 58141.16 cells/sec */ + +/* TST entry values */ +#define TSTE_NULL 0x0 /* force NULL cell xmit */ +#define TSTE_CBR 0x20000000 /* transmit CBR cell */ +#define TSTE_VBR 0x40000000 /* transmit VBR cell */ +#define TSTE_JUMP 0x60000000 /* Jump to SRAM addr */ + + +enum nicstar_regs { + DR0 = 0x00, + DR1 = 0x04, + DR2 = 0x08, + DR3 = 0x0c, + CMD = 0x10, + CFG = 0x14, + STAT = 0x18, + RSQB = 0x1c, + RSQT = 0x20, + RSQH = 0x24, + CDC = 0x28, + VPEC = 0x2c, + ICC = 0x30, + RAWCT = 0x34, + TMR = 0x38, + TSTB = 0x3c, + TSQB = 0x40, + TSQT = 0x44, + TSQH = 0x48, + GP = 0x4c, + VPM = 0x50 +}; + +/* misc configuration options (experimental) */ +#define NICSTAR_FASTXMIT 1 +#define NICSTAR_CBR 1 /* Constant bit rate support */ +#undef NICSTAR_RCQ /* Raw cell queue support */ +#define NICSTAR_PARANOID 1 /* adds some extra checks */ +#undef TEST_LOOPBACK /* enables PHY-level loopback */ +#undef ATM_013 + + +/* SRAM locations */ +#define NICSTAR_VBR_SCD0 (0x1e7f4) +#define NICSTAR_VBR_SCD1 (0x1e7e8) +#define NICSTAR_VBR_SCD2 (0x1e7dc) +#define NICSTAR_TST_REGION (0x1c000) /* For 32k SRAM */ +#define NICSTAR_SCD_REGION (0x1d000) /* Arbitrary value */ + +typedef struct nicstar_rcte { + u32 status; + u32 buf_handle; + u32 dma_addr; + u32 crc; +} nicstar_rcte; +#define RX_CTABLE_SIZE (16 * 1024) /* 16k words */ + +typedef struct nicstar_fbd { + u32 buf_handle; + u32 dma_addr; +} nicstar_fbd; + +#define RSQE_GFC_MASK 0x00004000 +#define RSQE_CLP_MASK 0x00000400 + +typedef struct nicstar_rsqe { + u32 vpi_vci; + u32 buf_handle; + u32 crc; + u32 status; +} nicstar_rsqe; +#define RX_STATQ_ENTRIES (512) + +typedef struct nicstar_tbd { + u32 status; + u32 buf_addr; + u32 ctl_len; + u32 cell_hdr; +} nicstar_tbd; + +#define TBD_SIZE (16) +#ifdef NICSTAR_FASTXMIT +/* following apply to NIC_TBD_DESC */ +#define TBD_ENDPDU 0x40000000 /* 1 => last buffer of + AAL5 CS_PDU */ +#define TBD_AAL0 0x00000000 /* 000 = AAL0 */ +#define TBD_AAL34 0x04000000 /* 001 = AAL3/4 */ +#define TBD_AAL5 0x08000000 /* 010 = AAL5 */ + +#endif + /* for CBR, set M and N to all 0 */ +#define TBD_UBR_M 0x00800000 /* M value timer count, VBR */ +#define TBD_UBR_N 0x00010000 /* N value timer count, VBR */ + +#define SCQ_SIZE (8192) /* Number of bytes actually allocated */ +#define SCQ_ENTRIES (SCQ_SIZE / TBD_SIZE) /* Variable-rate SCQ */ /* XXX 511 */ +#define CBRSCQ_SIZE (1024) /* space to reserve for each */ +#define CBRSCQ_ENTRIES (CBRSCQ_SIZE / TBD_SIZE) /* # entries in a CBR SCQ */ +#ifdef NICSTAR_CBR +#define SCQFULL_MAGIC (0xf0000000) +#define CBRSCQFULL_MAGIC (0xc0000000) +#define CBRSCQFULL_MAGIC_CLOSE (0xe0000000) +#else +#define SCQFULL_MAGIC (0xfeed0000) +#endif +#define SCQFULL_TIMEOUT (3 * HZ) + +typedef struct nicstar_scd { + u32 base_addr; + u32 tail_addr; + u32 crc; + u32 rsvd; + struct nicstar_tbd cache_a; + struct nicstar_tbd cache_b; +} nicstar_scd; + +typedef struct nicstar_tsi { + u32 status; + u32 timestamp; +} nicstar_tsi; +#define TX_STATQ_ENTRIES (1024) + +typedef struct nicstar_scq_shadow { +#ifndef NICSTAR_FASTXMIT + unsigned char cell[48]; +#endif + struct sk_buff *skb; +} nicstar_scq_shadow; + +typedef struct nicstar_scq { + struct nicstar_tbd *orig; + struct nicstar_tbd *base; + struct nicstar_tbd *next; + struct nicstar_tbd *last; + struct nicstar_tbd *tail; + struct wait_queue *scqfull_waitq; + u32 id; + u32 scd; /* SRAM address of paired SCD */ + int closing; + nicstar_scq_shadow scq_shadow[SCQ_ENTRIES]; + volatile int full; +} nicstar_scq; + +typedef struct nicstar_vcimap { + int tx:1; + int rx:1; + struct atm_vcc *tx_vcc; + struct atm_vcc *rx_vcc; + struct sk_buff *rx_skb; + /* Pointer to SCQ for channel if CBR */ +#ifdef NICSTAR_CBR + nicstar_scq *scq; +#endif +} nicstar_vcimap; + +#define NUM_VCI (4096) /* Size of Rx conn table, indexed by VCI only */ +#define NUM_VCI_BITS (12) /* Num sig bits */ + +typedef struct nicstar_buf_list { + caddr_t buf_addr; + struct nicstar_buf_list *next; +} nicstar_buf_list; + + +#if NICSTAR_RCQ +typedef struct nicstar_rcq { /* Raw cell queue */ + u32 rcq_head; /* pointer to current head of queue */ + u32 rcq_base; /* pointer to current buffer */ +} nicstar_rcq; +#endif + +typedef struct nicstar_dev { + + int index; /* Device index */ + unsigned long iobase, membase; /* I/O and memory addresses */ + unsigned char modid, revid; /* PCI module and revision ID */ + unsigned char pci_bus, pci_devfn; /* PCI params */ + int irq; /* IRQ line */ + int inuse; + int rev_C3; /* True if chip is rev. C3 */ + + nicstar_rsqe *rx_statq, *rx_statq_last; + volatile nicstar_rsqe *rx_statq_next; + caddr_t rx_statq_orig; + nicstar_tsi *tx_statq, *tx_statq_next, *tx_statq_last; + caddr_t tx_statq_orig; + + u32 *host_tst; + int available_slots; /* VBR slots in TST */ + int max_pcr; /* Maximum PCR for the PHY */ + +#if NICSTAR_RCQ + nicstar_rcq rcq; +#endif + + caddr_t sm_bufs, lg_bufs; + struct sk_buff_head rx_skb_queue; + + nicstar_scq *scq; + caddr_t scq_orig; + + struct atm_dev *dev; + nicstar_vcimap vci_map[NUM_VCI]; + unsigned char tmp_cell[48]; + +} nicstar_dev, *nicstar_devp; + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_NICSTAR_H_ */ diff -ur --new-file old/linux/drivers/block/cmd640.c new/linux/drivers/block/cmd640.c --- old/linux/drivers/block/cmd640.c Tue Aug 13 07:40:32 1996 +++ new/linux/drivers/block/cmd640.c Mon Sep 2 14:18:25 1996 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/cmd640.c Version 1.01 Aug 12, 1996 + * linux/drivers/block/cmd640.c Version 1.02 Sep 01, 1996 * * Copyright (C) 1995-1996 Linus Torvalds & authors (see below) */ @@ -94,9 +94,12 @@ * Version 1.00 Mmm.. cannot depend on PCMD_ENA in all systems * Version 1.01 slow/fast devsel can be selected with "hdparm -p6/-p7" * ("fast" is necessary for 32bit I/O in some systems) + * Version 1.02 fix bug that resulted in slow "setup times" + * (patch courtesy of Zoltan Hidvegi) */ #undef REALLY_SLOW_IO /* most systems can safely undef this */ +#define CMD640_PREFETCH_MASKS 1 #include #include @@ -408,9 +411,11 @@ drive->no_io_32bit = 1; drive->io_32bit = 0; } else { +#if CMD640_PREFETCH_MASKS drive->no_unmask = 1; - drive->no_io_32bit = 0; drive->unmask = 0; +#endif + drive->no_io_32bit = 0; } } @@ -454,8 +459,10 @@ cli(); b = get_cmd640_reg(reg); if (mode) { /* want prefetch on? */ +#if CMD640_PREFETCH_MASKS drive->no_unmask = 1; drive->unmask = 0; +#endif drive->no_io_32bit = 0; b &= ~prefetch_masks[index]; /* enable prefetch */ } else { @@ -556,9 +563,10 @@ * Convert setup_count to internal chipset representation */ switch (setup_count) { - case 4: setup_count = 0x00; - case 3: setup_count = 0x80; - case 2: setup_count = 0x40; + case 4: setup_count = 0x00; break; + case 3: setup_count = 0x80; break; + case 1: + case 2: setup_count = 0x40; break; default: setup_count = 0xc0; /* case 5 */ } diff -ur --new-file old/linux/drivers/block/floppy.c new/linux/drivers/block/floppy.c --- old/linux/drivers/block/floppy.c Thu Aug 8 11:30:42 1996 +++ new/linux/drivers/block/floppy.c Tue Sep 24 13:12:40 1996 @@ -662,7 +662,7 @@ { int fdc=FDC(drive); #ifdef FLOPPY_SANITY_CHECK - if (jiffies < UDP->select_delay + UDRS->select_date) + if (jiffies - UDRS->select_date < UDP->select_delay) DPRINT("WARNING disk change called early\n"); if (!(FDCS->dor & (0x10 << UNIT(drive))) || (FDCS->dor & 3) != UNIT(drive) || @@ -966,7 +966,7 @@ return 1; } - if (jiffies < delay){ + if ((signed) (jiffies - delay) < 0){ del_timer(&fd_timer); fd_timer.function = function; fd_timer.expires = delay; @@ -1415,7 +1415,7 @@ * again just before spinup completion. Beware that * after scandrives, we must again wait for selection. */ - if (ready_date > jiffies + DP->select_delay){ + if ((signed) (ready_date - jiffies) > DP->select_delay){ ready_date -= DP->select_delay; function = (timeout_fn) floppy_start; } else @@ -1685,9 +1685,13 @@ } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2); } if (handler) { - /* expected interrupt */ - floppy_tq.routine = (void *)(void *) handler; - queue_task_irq(&floppy_tq, &tq_timer); + if(intr_count >= 2) { + /* expected interrupt */ + floppy_tq.routine = (void *)(void *) handler; + queue_task_irq(&floppy_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } else + handler(); } else FDCS->reset = 1; is_alive("normal interrupt end"); @@ -1925,7 +1929,8 @@ unsigned long flags; floppy_tq.routine = (void *)(void *) handler; - queue_task(&floppy_tq, &tq_timer); + queue_task(&floppy_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); INT_OFF; while(command_status < 2 && NO_SIGNAL){ is_alive("wait_til_done"); @@ -2082,7 +2087,7 @@ /* determine interleave */ il = 1; - if (_floppy->sect > DP->interleave_sect && F_SIZECODE == 2) + if (_floppy->fmt_gap < 0x22) il++; /* initialize field */ @@ -2733,7 +2738,8 @@ if (TESTF(FD_NEED_TWADDLE)) twaddle(); floppy_tq.routine = (void *)(void *) floppy_start; - queue_task(&floppy_tq, &tq_timer); + queue_task(&floppy_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); #ifdef DEBUGT debugt("queue fd request"); #endif @@ -2754,7 +2760,8 @@ static void process_fd_request(void) { cont = &rw_cont; - queue_task(&request_tq, &tq_timer); + queue_task(&request_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); } static void do_fd_request(void) @@ -3649,7 +3656,7 @@ if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY)) return 1; - if (UDRS->last_checked + UDP->checkfreq < jiffies){ + if (UDP->checkfreq < jiffies - UDRS->last_checked){ lock_fdc(drive,0); poll_drive(0,0); process_fd_request(); @@ -4060,12 +4067,16 @@ if (fd_request_irq()) { DPRINT("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); + MOD_DEC_USE_COUNT; + usage_count--; return -1; } if (fd_request_dma()) { DPRINT("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA); fd_free_irq(); + MOD_DEC_USE_COUNT; + usage_count--; return -1; } for (fdc = 0; fdc < N_FDC; fdc++) @@ -4091,7 +4102,6 @@ return; } INT_ON; - MOD_DEC_USE_COUNT; fd_disable_dma(); fd_free_dma(); fd_disable_irq(); @@ -4124,6 +4134,7 @@ if (floppy_tq.sync) printk("task queue still active\n"); #endif + MOD_DEC_USE_COUNT; } diff -ur --new-file old/linux/drivers/block/ide-cd.c new/linux/drivers/block/ide-cd.c --- old/linux/drivers/block/ide-cd.c Thu Aug 1 14:36:31 1996 +++ new/linux/drivers/block/ide-cd.c Fri Sep 20 16:00:34 1996 @@ -106,6 +106,8 @@ * * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification * 3.16 Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl. + * 3.17 Sep 17, 1996 -- Tweak audio reads for some drives. + * Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC. * * NOTE: Direct audio reads will only work on some types of drive. * So far, i've received reports of success for Sony and Toshiba drives. @@ -132,6 +134,7 @@ #include #include #include +#include #include #include #include @@ -168,6 +171,13 @@ #endif +/* Size of buffer to allocate, in blocks, for audio reads. */ + +#ifndef CDROM_NBLOCKS_BUFFER +#define CDROM_NBLOCKS_BUFFER 8 +#endif + + /************************************************************************/ #define SECTOR_SIZE 512 @@ -1918,7 +1928,7 @@ static int -cdrom_read_block (ide_drive_t *drive, int format, int lba, +cdrom_read_block (ide_drive_t *drive, int format, int lba, int nblocks, char *buf, int buflen, struct atapi_request_sense *reqbuf) { @@ -1944,8 +1954,13 @@ pc.c[1] = (format << 2); put_unaligned(htonl(lba), (unsigned int *) &pc.c[2]); - pc.c[8] = 1; /* one block */ - pc.c[9] = 0x10; + pc.c[8] = (nblocks & 0xff); + pc.c[7] = ((nblocks>>8) & 0xff); + pc.c[6] = ((nblocks>>16) & 0xff); + if (format <= 1) + pc.c[9] = 0xf0; + else + pc.c[9] = 0x10; stat = cdrom_queue_packet_command (drive, &pc); @@ -1959,8 +1974,8 @@ "trying opcode 0xd4\n", drive->name); CDROM_CONFIG_FLAGS (drive)->old_readcd = 1; - return cdrom_read_block (drive, format, lba, buf, buflen, - reqbuf); + return cdrom_read_block (drive, format, lba, nblocks, + buf, buflen, reqbuf); } #endif /* not STANDARD_ATAPI */ @@ -2343,19 +2358,25 @@ if (lba < 0 || lba >= toc->capacity) return -EINVAL; - buf = (char *) kmalloc (CD_FRAMESIZE_RAW, GFP_KERNEL); + buf = (char *) kmalloc (CDROM_NBLOCKS_BUFFER*CD_FRAMESIZE_RAW, + GFP_KERNEL); if (buf == NULL) return -ENOMEM; while (ra.nframes > 0) { - stat = cdrom_read_block (drive, 1, lba, buf, - CD_FRAMESIZE_RAW, NULL); + int this_nblocks = ra.nframes; + if (this_nblocks > CDROM_NBLOCKS_BUFFER) + this_nblocks = CDROM_NBLOCKS_BUFFER; + stat = cdrom_read_block + (drive, 1, lba, this_nblocks, + buf, this_nblocks * CD_FRAMESIZE_RAW, NULL); if (stat) break; - memcpy_tofs (ra.buf, buf, CD_FRAMESIZE_RAW); - ra.buf += CD_FRAMESIZE_RAW; - --ra.nframes; - ++lba; + memcpy_tofs (ra.buf, buf, + this_nblocks * CD_FRAMESIZE_RAW); + ra.buf += this_nblocks * CD_FRAMESIZE_RAW; + ra.nframes -= this_nblocks; + lba += this_nblocks; } kfree (buf); @@ -2399,7 +2420,7 @@ if (buf == NULL) return -ENOMEM; - stat = cdrom_read_block (drive, format, lba, buf, blocksize, + stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize, NULL); if (stat == 0) memcpy_tofs ((char *)arg, buf, blocksize); @@ -2432,7 +2453,12 @@ return stat; } - case CDROMLOADFROMSLOT: { + case CDROMLOADFROMSLOT: + printk ("%s: Use CDROM_SELECT_DISC " + " instead of CDROMLOADFROMSLOT.\n", drive->name); + /* Fall through. */ + + case CDROM_SELECT_DISC: { struct atapi_request_sense my_reqbuf; int stat; diff -ur --new-file old/linux/drivers/block/ide-tape.c new/linux/drivers/block/ide-tape.c --- old/linux/drivers/block/ide-tape.c Sat Aug 17 19:51:18 1996 +++ new/linux/drivers/block/ide-tape.c Wed Nov 6 13:48:32 1996 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-tape.c Version 1.6 - ALPHA Aug 16, 1996 + * linux/drivers/block/ide-tape.c Version 1.9 - ALPHA Nov 5, 1996 * * Copyright (C) 1995, 1996 Gadi Oxman * @@ -32,7 +32,7 @@ * ht0 major=37,minor=0 first IDE tape, rewind on close. * nht0 major=37,minor=128 first IDE tape, no rewind on close. * - * Run /usr/src/linux/drivers/block/MAKEDEV.ide to create the above entries. + * Run /usr/src/linux/scripts/MAKEDEV.ide to create the above entries. * We currently support only one ide tape drive. * * The general magnetic tape commands compatible interface, as defined by @@ -186,6 +186,15 @@ * Fixed nasty null dereferencing bug. * Ver 1.6 Aug 16 96 Fixed FPU usage in the driver. * Fixed end of media bug. + * Ver 1.7 Sep 10 96 Minor changes for the CONNER CTT8000-A model. + * Ver 1.8 Sep 26 96 Attempt to find a better balance between good + * interactive response and high system throughput. + * Ver 1.9 Nov 5 96 Automatically cross encountered filemarks rather + * than requiring an explicit FSF command. + * Abort pending requests at end of media. + * MTTELL was sometimes returning incorrect results. + * Return the real block size in the MTIOCGET ioctl. + * Some error recovery bug fixes. * * We are currently in an *alpha* stage. The driver is not complete and not * much tested. I would strongly suggest to: @@ -415,8 +424,9 @@ #define IDETAPE_READ_REQUEST 92 #define IDETAPE_WRITE_REQUEST 93 +#define IDETAPE_ABORTED_WRITE_REQUEST 94 -#define IDETAPE_LAST_REQUEST 93 +#define IDETAPE_LAST_REQUEST 94 /* * A macro which can be used to check if a we support a given @@ -1079,6 +1089,7 @@ printk ("LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n"); printk ("IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n"); printk ("IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n"); + printk ("ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n"); printk ("PIO Cycle Timing Category: %d\n",id->tPIO); printk ("DMA Cycle Timing Category: %d\n",id->tDMA); printk ("Single Word DMA supported modes: "); @@ -1201,10 +1212,6 @@ printk ("Unable to register character device interface !\n"); /* ??? */ } - else { - printk ("ide-tape: %s <-> %s : Character device interface on major = %d\n", - drive->name,idetape_chrdev.name,major); - } } /* @@ -1226,6 +1233,10 @@ { idetape_tape_t *tape=&(drive->tape); unsigned int allocation_length; +#if IDETAPE_ANTICIPATE_READ_WRITE_DSC + ide_hwif_t *hwif = HWIF(drive); + unsigned long t1, tmid, tn; +#endif /* IDETAPE_ANTICIPATE_READ_WRITE_DSC */ #if IDETAPE_DEBUG_LOG printk ("ide-tape: Reached idetape_setup\n"); @@ -1252,10 +1263,8 @@ #if IDETAPE_PIPELINE tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES; - printk ("ide-tape: Operating in pipelined (fast and tricky) operation mode.\n"); #else tape->max_number_of_stages=0; - printk ("ide-tape: Operating in non-pipelined (slow and safe) operation mode.\n"); #endif /* IDETAPE_PIPELINE */ idetape_get_mode_sense_results (drive); @@ -1308,10 +1317,28 @@ * constantly streaming. */ - if (tape->max_number_of_stages) - tape->best_dsc_rw_frequency = (unsigned long) ((tape->capabilities.buffer_size * 32 * HZ) / (tape->capabilities.speed * 125)); - else - tape->best_dsc_rw_frequency = (unsigned long) ((tape->data_buffer_size * HZ) / (tape->capabilities.speed * 1000)); + /* + * We will ignore the above algorithm for now, as it can have + * a bad effect on interactive response under some conditions. + * The following attempts to find a balance between good latency + * and good system throughput. It will be nice to have all this + * configurable in run time at some point. + */ + t1 = (tape->data_buffer_size * HZ) / (tape->capabilities.speed * 1000); + tmid = (tape->capabilities.buffer_size * 32 * HZ) / (tape->capabilities.speed * 125); + tn = (IDETAPE_FIFO_THRESHOLD * tape->data_buffer_size * HZ) / (tape->capabilities.speed * 1000); + + if (tape->max_number_of_stages) { + if (drive->using_dma) + tape->best_dsc_rw_frequency = tmid; + else { + if (hwif->drives[drive->select.b.unit ^ 1].present || hwif->next != hwif) + tape->best_dsc_rw_frequency = IDETAPE_MIN ((tn + tmid) / 2, tmid); + else + tape->best_dsc_rw_frequency = IDETAPE_MIN (tn, tmid); + } + } else + tape->best_dsc_rw_frequency = t1; /* * Ensure that the number we got makes sense. @@ -1334,8 +1361,10 @@ tape->best_dsc_rw_frequency=IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY; #endif /* IDETAPE_ANTICIPATE_READ_WRITE_DSC */ - printk ("ide-tape: Tape speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,tape->data_buffer_size); - + printk (KERN_INFO "ide-tape: %s <-> %s, %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n", + drive->name, "ht0", tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->data_buffer_size, + tape->data_buffer_size / 1024, tape->max_number_of_stages * tape->data_buffer_size / 1024, + tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":""); return; } @@ -1487,7 +1516,9 @@ if (!pc->abort) { printk ("ide-tape: %s: I/O error, ",drive->name); printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq); +#if IDETAPE_DEBUG_LOG printk ("ide-tape: Maximum retries reached - Giving up\n"); +#endif /* IDETAPE_DEBUG_LOG */ pc->error=1; /* Giving up */ } tape->failed_pc=NULL; @@ -1678,15 +1709,15 @@ if (!pc->writing) { /* Reading - Check that we have enough space */ temp=(unsigned long) pc->actually_transferred + bcount.all; if ( temp > pc->request_transfer) { - printk ("ide-tape: The tape wants to send us more data than requested - "); if (temp > pc->buffer_size) { - printk ("Discarding data\n"); + printk ("ide-tape: The tape wants to send us more data than requested - discarding data\n"); idetape_discard_data (drive,bcount.all); ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD); return; } - else - printk ("Allowing transfer\n"); +#if IDETAPE_DEBUG_LOG + printk ("ide-tape: The tape wants to send us more data than requested - allowing transfer\n"); +#endif /* IDETAPE_DEBUG_LOG */ } } #if IDETAPE_DEBUG_BUGS @@ -2444,7 +2475,12 @@ pc->abort=1; } } - + if (pc->c[0] == IDETAPE_WRITE_CMD) { + if (result->eom || (result->sense_key == 0xd && result->asc == 0x0 && result->ascq == 0x2)) { + pc->error = IDETAPE_RQ_ERROR_EOD; + pc->abort = 1; + } + } if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) { if (result->sense_key == 8) { pc->error=IDETAPE_RQ_ERROR_EOD; @@ -2823,6 +2859,17 @@ } } +static void idetape_abort_pipeline (ide_drive_t *drive) +{ + idetape_tape_t *tape = &drive->tape; + idetape_pipeline_stage_t *stage = tape->next_stage; + + while (stage) { + stage->rq.cmd = IDETAPE_ABORTED_WRITE_REQUEST; + stage = stage->next; + } +} + /* * Functions which handle requests. */ @@ -2850,7 +2897,9 @@ if (!rq->errors) /* In case rq->errors is already set, */ rq->errors=!uptodate; /* we won't change it. */ error=rq->errors; - + if (error) + tape->failed_pc = NULL; + if (tape->active_data_request == rq) { /* The request was a pipelined data transfer request */ if (rq->cmd == IDETAPE_READ_REQUEST) { @@ -2866,8 +2915,11 @@ tape->active_data_request=NULL; if (rq->cmd == IDETAPE_WRITE_REQUEST) { - if (rq->errors) + if (rq->errors) { tape->error_in_pipeline_stage=rq->errors; + if (error == IDETAPE_RQ_ERROR_EOD) + idetape_abort_pipeline (drive); + } idetape_remove_stage_head (drive); } @@ -2925,7 +2977,7 @@ printk ("ide-tape: The block device interface should not be used for data transfers.\n"); printk ("ide-tape: Use the character device interfaces\n"); printk ("ide-tape: /dev/ht0 and /dev/nht0 instead.\n"); - printk ("ide-tape: (Run linux/drivers/block/MAKEDEV.ide to create them)\n"); + printk ("ide-tape: (Run linux/scripts/MAKEDEV.ide to create them)\n"); printk ("ide-tape: Aborting request.\n"); ide_end_request (0,HWGROUP (drive)); /* Let the common code handle it */ @@ -3048,6 +3100,12 @@ idetape_issue_packet_command (drive,pc,&idetape_pc_intr); return; + case IDETAPE_ABORTED_WRITE_REQUEST: + rq->cmd = IDETAPE_WRITE_REQUEST; + rq->errors = IDETAPE_RQ_ERROR_EOD; + idetape_end_request (1, HWGROUP(drive)); + return; + case IDETAPE_PACKET_COMMAND_REQUEST_TYPE1: case IDETAPE_PACKET_COMMAND_REQUEST_TYPE2: /* @@ -3252,8 +3310,11 @@ bytes_read=tape->tape_block_size*(rq_ptr->nr_sectors-rq_ptr->current_nr_sectors); rq_ptr->nr_sectors=rq_ptr->current_nr_sectors=0; idetape_copy_buffer_from_stage (tape->first_stage,buffer); - if (rq_ptr->errors != IDETAPE_RQ_ERROR_FILEMARK) + if (rq_ptr->errors != IDETAPE_RQ_ERROR_FILEMARK) { + tape->filemark = 0; idetape_remove_stage_head (drive); + } else + tape->filemark = 1; #if IDETAPE_DEBUG_BUGS if (bytes_read > blocks*tape->tape_block_size) { printk ("ide-tape: bug: trying to return more bytes than requested\n"); @@ -3656,7 +3717,7 @@ ide_drive_t *drive=idetape_chrdev.drive; idetape_tape_t *tape=&(drive->tape); char *buf_ptr=buf; - int bytes_read,temp,actually_read=0; + int bytes_read,temp,actually_read=0, original_count = count; #if IDETAPE_DEBUG_LOG printk ("Reached idetape_chrdev_read\n"); @@ -3700,7 +3761,7 @@ while (count >= tape->data_buffer_size) { bytes_read=idetape_add_chrdev_read_request (drive,tape->capabilities.ctl,tape->merge_buffer); if (bytes_read <= 0) - return (actually_read); + goto finish; memcpy_tofs (buf_ptr,tape->merge_buffer,bytes_read); buf_ptr+=bytes_read;count-=bytes_read;actually_read+=bytes_read; } @@ -3708,13 +3769,16 @@ if (count) { bytes_read=idetape_add_chrdev_read_request (drive,tape->capabilities.ctl,tape->merge_buffer); if (bytes_read <= 0) - return (actually_read); + goto finish; temp=IDETAPE_MIN (count,bytes_read); memcpy_tofs (buf_ptr,tape->merge_buffer,temp); actually_read+=temp; tape->merge_buffer_offset=temp; tape->merge_buffer_size=bytes_read-temp; } +finish: + if (actually_read < original_count && tape->filemark) + idetape_space_over_filemarks (drive, MTFSF, 1); return (actually_read); } @@ -3787,6 +3851,26 @@ return (actually_written); } +static int idetape_pipeline_size (ide_drive_t *drive) +{ + idetape_tape_t *tape = &drive->tape; + idetape_pipeline_stage_t *stage; + struct request *rq; + int size = 0; + + idetape_wait_for_pipeline (drive); + stage = tape->first_stage; + while (stage != NULL) { + rq = &stage->rq; + size += tape->tape_block_size * (rq->nr_sectors-rq->current_nr_sectors); + if (rq->errors == IDETAPE_RQ_ERROR_FILEMARK) + size += tape->tape_block_size; + stage = stage->next; + } + size += tape->merge_buffer_size; + return size; +} + /* * Our character device ioctls. * @@ -3797,17 +3881,7 @@ * * MTIOCTOP - Refer to idetape_mtioctop for detailed description. * - * MTIOCGET - The mt_dsreg field in the returned mtget structure - * will be set to (recommended block size << - * MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK, which - * is currently equal to the size itself. - * The other mtget fields are not supported. - * - * Note that we do not actually return the tape's - * block size. Rather, we provide the recommended - * number of bytes which should be used as a "user - * block size" with the character device read/write - * functions to maximize throughput. + * MTIOCGET - Some of the fields are not supported. * * MTIOCPOS - The current tape "position" is returned. * (A unique number which can be used with the MTSEEK @@ -3827,7 +3901,7 @@ struct mtop mtop; struct mtget mtget; struct mtpos mtpos; - int retval; + int retval, block_offset = 0; #if IDETAPE_DEBUG_LOG printk ("Reached idetape_chrdev_ioctl, cmd=%u\n",cmd); @@ -3837,14 +3911,16 @@ idetape_empty_write_pipeline (drive); idetape_flush_tape_buffers (drive); } - - if (tape->chrdev_direction == idetape_direction_read && cmd != MTIOCTOP) - idetape_discard_read_pipeline (drive); - pc.buffer=pc.temp_buffer; pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE; pc.current_position=pc.temp_buffer; + if (cmd == MTIOCGET || cmd == MTIOCPOS) { + block_offset = idetape_pipeline_size (drive) / tape->tape_block_size; + idetape_create_read_position_cmd (&pc); + retval=idetape_queue_pc_tail (drive,&pc); + if (retval) return (retval); + } switch (cmd) { case MTIOCTOP: retval=verify_area (VERIFY_READ,(char *) arg,sizeof (struct mtop)); @@ -3852,16 +3928,15 @@ memcpy_fromfs ((char *) &mtop, (char *) arg, sizeof (struct mtop)); return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count)); case MTIOCGET: + memset (&mtget, 0, sizeof (struct mtget)); + mtget.mt_blkno = tape->block_address - block_offset; mtget.mt_dsreg=(tape->data_buffer_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK; retval=verify_area (VERIFY_WRITE,(char *) arg,sizeof (struct mtget)); if (retval) return (retval); memcpy_tofs ((char *) arg,(char *) &mtget, sizeof (struct mtget)); return (0); case MTIOCPOS: - idetape_create_read_position_cmd (&pc); - retval=idetape_queue_pc_tail (drive,&pc); - if (retval) return (retval); - mtpos.mt_blkno=tape->block_address; + mtpos.mt_blkno = tape->block_address - block_offset; retval=verify_area (VERIFY_WRITE,(char *) arg,sizeof (struct mtpos)); if (retval) return (retval); memcpy_tofs ((char *) arg,(char *) &mtpos, sizeof (struct mtpos)); @@ -4022,6 +4097,7 @@ */ tape->merge_buffer_size=tape->merge_buffer_offset=0; + tape->filemark = 0; while (tape->first_stage != NULL) { /* diff -ur --new-file old/linux/drivers/block/ide-tape.h new/linux/drivers/block/ide-tape.h --- old/linux/drivers/block/ide-tape.h Sat Aug 17 19:51:18 1996 +++ new/linux/drivers/block/ide-tape.h Wed Nov 6 13:48:32 1996 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-tape.h Version 1.5 - ALPHA Apr 12, 1996 + * linux/drivers/block/ide-tape.h Version 1.9 - ALPHA Nov 5, 1996 * * Copyright (C) 1995, 1996 Gadi Oxman */ @@ -234,11 +234,20 @@ #define IDETAPE_ANTICIPATE_READ_WRITE_DSC 1 /* + * The following parameter is used to select the point in the internal + * tape fifo in which we will start to refill the buffer. Decreasing + * the following parameter will improve the system's latency and + * interactive response, while using a high value might improve sytem + * throughput. + */ +#define IDETAPE_FIFO_THRESHOLD 2 + +/* * DSC timings. */ #define IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY 5*HZ/100 /* 50 msec */ -#define IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY 30*HZ/100 /* 300 msec */ +#define IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY 40*HZ/100 /* 400 msec */ #define IDETAPE_DSC_FAST_MEDIA_ACCESS_FREQUENCY 1*HZ /* 1 second */ #define IDETAPE_FAST_SLOW_THRESHOLD 5*60*HZ /* 5 minutes */ #define IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY 60*HZ /* 1 minute */ @@ -457,6 +466,7 @@ /* Character device operation */ chrdev_direction_t chrdev_direction; /* Current character device data transfer direction */ + int filemark; /* Currently on a filemark */ byte busy; /* Device already opened */ /* Device information */ diff -ur --new-file old/linux/drivers/block/ide.c new/linux/drivers/block/ide.c --- old/linux/drivers/block/ide.c Mon Aug 12 09:24:26 1996 +++ new/linux/drivers/block/ide.c Wed Nov 6 13:48:33 1996 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide.c Version 5.51 Aug 10, 1996 + * linux/drivers/block/ide.c Version 5.52 Sep 24, 1996 * * Copyright (C) 1994-1996 Linus Torvalds & authors (see below) */ @@ -259,6 +259,8 @@ * Version 5.50 allow values as small as 20 for idebus= * Version 5.51 force non io_32bit in drive_cmd_intr() * change delay_10ms() to delay_50ms() to fix problems + * Version 5.52 fix incorrect invalidation of removable devices + * add "hdx=slow" command line option * * Some additional driver compile-time options are in ide.h * @@ -485,8 +487,18 @@ } else #endif /* SUPPORT_VLB_SYNC */ insl(data_reg, buffer, wcount); - } else - insw(data_reg, buffer, wcount<<1); + } else { +#if SUPPORT_SLOW_DATA_PORTS + if (drive->slow) { + unsigned short *ptr = (unsigned short *) buffer; + while (wcount--) { + *ptr++ = inw_p(data_reg); + *ptr++ = inw_p(data_reg); + } + } else +#endif /* SUPPORT_SLOW_DATA_PORTS */ + insw(data_reg, buffer, wcount<<1); + } } /* @@ -509,8 +521,18 @@ } else #endif /* SUPPORT_VLB_SYNC */ outsl(data_reg, buffer, wcount); - } else - outsw(data_reg, buffer, wcount<<1); + } else { +#if SUPPORT_SLOW_DATA_PORTS + if (drive->slow) { + unsigned short *ptr = (unsigned short *) buffer; + while (wcount--) { + outw_p(*ptr++, data_reg); + outw_p(*ptr++, data_reg); + } + } else +#endif /* SUPPORT_SLOW_DATA_PORTS */ + outsw(data_reg, buffer, wcount<<1); + } } /* @@ -684,6 +706,7 @@ hwgroup->poll_timeout = 0; /* end of polling */ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat); do_reset1 (drive, 1); /* do it the old fashioned way */ + return; } hwgroup->poll_timeout = 0; /* done polling */ } @@ -788,6 +811,10 @@ */ for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *rdrive = &hwif->drives[unit]; +#ifdef CONFIG_BLK_DEV_IDETAPE + if (rdrive->media == ide_tape) + rdrive->tape.reset_issued = 1; +#endif /* CONFIG_BLK_DEV_IDETAPE */ rdrive->special.all = 0; rdrive->special.b.set_geometry = 1; rdrive->special.b.recalibrate = 1; @@ -1874,7 +1901,7 @@ if (drive->media == ide_tape) return idetape_blkdev_open (inode, filp, drive); #endif /* CONFIG_BLK_DEV_IDETAPE */ - if (drive->removable) { + if (drive->removable && drive->usage == 1) { byte door_lock[] = {WIN_DOORLOCK,0,0,0}; struct request rq; check_disk_change(inode->i_rdev); @@ -1955,7 +1982,7 @@ for (p = 0; p < (1<part[p].nr_sects > 0) { kdev_t devp = MKDEV(major, minor+p); - sync_dev (devp); + fsync_dev (devp); invalidate_inodes (devp); invalidate_buffers (devp); } @@ -2249,11 +2276,13 @@ unsigned long capacity, check; id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL); - ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */ + ide_input_data(drive, id, SECTOR_WORDS);/* read 512 bytes of id info */ sti(); +#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) /* - * EATA SCSI controllers do a hardware ATA emulation: ignore them + * EATA SCSI controllers do a hardware ATA emulation: + * Ignore them if there is a driver for them available. */ if ((id->model[0] == 'P' && id->model[1] == 'M') || (id->model[0] == 'S' && id->model[1] == 'K')) { @@ -2261,6 +2290,7 @@ drive->present = 0; return; } +#endif /* * WIN_IDENTIFY returns little-endian info, @@ -2865,7 +2895,8 @@ */ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) { const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom", - "serialize", "autotune", "noautotune", NULL}; + "serialize", "autotune", "noautotune", + "slow", NULL}; unit = s[2] - 'a'; hw = unit / MAX_DRIVES; unit = unit % MAX_DRIVES; @@ -2894,6 +2925,9 @@ goto done; case -7: /* "noautotune" */ drive->autotune = 2; + goto done; + case -8: /* "slow" */ + drive->slow = 1; goto done; case 3: /* cyl,head,sect */ drive->media = ide_disk; diff -ur --new-file old/linux/drivers/block/ide.h new/linux/drivers/block/ide.h --- old/linux/drivers/block/ide.h Tue Aug 20 17:10:27 1996 +++ new/linux/drivers/block/ide.h Thu Oct 31 11:05:20 1996 @@ -25,6 +25,9 @@ #undef REALLY_FAST_IO /* define if ide ports are perfect */ #define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */ +#ifndef SUPPORT_SLOW_DATA_PORTS /* 1 to support slow data ports */ +#define SUPPORT_SLOW_DATA_PORTS 1 /* 0 to reduce kernel size */ +#endif #ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */ #define SUPPORT_VLB_SYNC 1 /* 0 to reduce kernel size */ #endif @@ -327,6 +330,7 @@ unsigned no_unmask : 1; /* disallow setting unmask bit */ unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */ unsigned nobios : 1; /* flag: do not probe bios for drive */ + unsigned slow : 1; /* flag: slow data port */ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */ #if FAKE_FDISK_FOR_EZDRIVE unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */ diff -ur --new-file old/linux/drivers/block/ide_modes.h new/linux/drivers/block/ide_modes.h --- old/linux/drivers/block/ide_modes.h Mon Aug 5 07:12:25 1996 +++ new/linux/drivers/block/ide_modes.h Thu Oct 31 11:05:20 1996 @@ -6,6 +6,8 @@ * Copyright (C) 1996 Linus Torvalds, Igor Abramov, and Mark Lord */ +#include + /* * Shared data/functions for determining best PIO mode for an IDE drive. * Most of this stuff originally lived in cmd640.c, and changes to the diff -ur --new-file old/linux/drivers/block/ll_rw_blk.c new/linux/drivers/block/ll_rw_blk.c --- old/linux/drivers/block/ll_rw_blk.c Thu Aug 15 08:55:43 1996 +++ new/linux/drivers/block/ll_rw_blk.c Fri Sep 20 16:00:34 1996 @@ -349,6 +349,10 @@ } /* look for a free request. */ + /* Loop uses two requests, 1 for loop and 1 for the real device. + * Cut max_req in half to avoid running out and deadlocking. */ + if (major == LOOP_MAJOR) + max_req >>= 1; /* * Try to coalesce the new request with old requests @@ -508,7 +512,6 @@ for (i = 0; i < nr; i++) { if (bh[i]) { set_bit(BH_Req, &bh[i]->b_state); - make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]); } } @@ -528,6 +531,7 @@ { int i, j; int buffersize; + int max_req; unsigned long rsector; kdev_t rdev; struct request * req[8]; @@ -539,10 +543,12 @@ " nonexistent block-device\n"); return; } + max_req = NR_REQUEST; switch (rw) { case READ: break; case WRITE: + max_req = (NR_REQUEST * 2) / 3; if (is_read_only(dev)) { printk(KERN_NOTICE "Can't swap to read-only device %s\n", @@ -555,6 +561,8 @@ } buffersize = PAGE_SIZE / nb; + if (major == LOOP_MAJOR) + max_req >>= 1; for (j=0, i=0; i + * For finding abug in the return of the track numbers. */ /* @@ -1911,7 +1915,9 @@ int num_tracks; - num_tracks = sony_toc.last_track_num - sony_toc.first_track_num + 1; + num_tracks = ( bcd_to_int(sony_toc.last_track_num) + - bcd_to_int(sony_toc.first_track_num) + + 1); for (i = 0; i < num_tracks; i++) { if (sony_toc.tracks[i].track == track) @@ -2563,8 +2569,8 @@ return i; memcpy_fromfs(&ti, (char *) arg, sizeof(ti)); - if ( (ti.cdti_trk0 < sony_toc.first_track_num) - || (ti.cdti_trk0 > sony_toc.last_track_num) + if ( (ti.cdti_trk0 < bcd_to_int(sony_toc.first_track_num)) + || (ti.cdti_trk0 > bcd_to_int(sony_toc.last_track_num)) || (ti.cdti_trk1 < ti.cdti_trk0)) { return -EINVAL; diff -ur --new-file old/linux/drivers/cdrom/sbpcd.c new/linux/drivers/cdrom/sbpcd.c --- old/linux/drivers/cdrom/sbpcd.c Mon Aug 12 12:44:47 1996 +++ new/linux/drivers/cdrom/sbpcd.c Mon Sep 2 14:18:26 1996 @@ -13,7 +13,7 @@ * labelled E2550UA or MK4015 or 2800F). */ -#define VERSION "v4.5 Eberhard Moenkeberg " +#define VERSION "v4.6 Eberhard Moenkeberg " /* Copyright (C) 1993, 1994, 1995 Eberhard Moenkeberg * @@ -293,11 +293,15 @@ * ll_rw_blk.c). * "Check media change" without touching any drive. * + * 4.6 Use a semaphore to synchronize multi-activity; elaborated by Rob + * Riggs . At the moment, we simply block "read" + * against "ioctl" and vice versa. This could be refined further, but + * I guess with almost no performance increase. + * Experiments to speed up the CD-55A; again with help of Rob Riggs + * (to be true, he gave both, idea & code. ;-) * - * TODO * - * synchronize multi-activity - * (data + audio + ioctl + disk change, multiple drives) + * TODO * implement "read all subchannel data" (96 bytes per frame) * * special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine @@ -571,9 +575,8 @@ static struct wait_queue *sbp_waitq = NULL; #endif FUTURE -/*==========================================================================*/ -#define SBP_BUFFER_FRAMES 8 /* driver's own read_ahead, data mode */ -/*==========================================================================*/ +static int teac=SBP_TEAC_SPEED; +static int buffers=SBP_BUFFER_FRAMES; static u_char family0[]="MATSHITA"; /* MKE CR-521, CR-522, CR-523 */ static u_char family1[]="CR-56"; /* MKE CR-562, CR-563 */ @@ -592,8 +595,11 @@ static u_char xa_head_buf[CD_XA_HEAD]; static u_char xa_tail_buf[CD_XA_TAIL]; +#if OLD_BUSY static volatile u_char busy_data=0; static volatile u_char busy_audio=0; /* true semaphores would be safer */ +#endif OLD_BUSY +static struct semaphore ioctl_read_sem = MUTEX; static u_long timeout; static volatile u_char timed_out_delay=0; static volatile u_char timed_out_data=0; @@ -828,6 +834,8 @@ sti(); } /*==========================================================================*/ +#define RETURN_UP(rc) {up(&ioctl_read_sem); return(rc);} +/*==========================================================================*/ /* * convert logical_block_address to m-s-f_number (3 bytes only) */ @@ -960,6 +968,57 @@ sbp_sleep(15*HZ/10); for (i=maxtim_data;i!=0;i--) inb(CDi_status); } +/*====================================================================*/ +/* + * CDi status loop for Teac CD-55A (Rob Riggs) + * + * This is needed because for some strange reason + * the CD-55A can take a real long time to give a + * status response. This seems to happen after we + * issue a READ command where a long seek is involved. + * + * I tried to ensure that we get max throughput with + * minimal busy waiting. We busy wait at first, then + * "switch gears" and start sleeping. We sleep for + * longer periods of time the longer we wait. + * + */ +static int CDi_stat_loop_T(void) +{ + int i, gear=1; + u_long timeout_1, timeout_2, timeout_3, timeout_4; + + timeout_1 = jiffies + HZ / 50; /* sbp_sleep(0) for a short period */ + timeout_2 = jiffies + HZ / 5; /* nap for no more than 200ms */ + timeout_3 = jiffies + 5 * HZ; /* sleep for up to 5s */ + timeout_4 = jiffies + 45 * HZ; /* long sleep for up to 45s. */ + do + { + i = inb(CDi_status); + if (!(i&s_not_data_ready)) return (i); + if (!(i&s_not_result_ready)) return (i); + switch(gear) + { + case 4: + sbp_sleep(HZ); + if (jiffies > timeout_4) gear++; + msg(DBG_TEA, "CDi_stat_loop_T: long sleep active.\n"); + break; + case 3: + sbp_sleep(HZ/10); + if (jiffies > timeout_3) gear++; + break; + case 2: + sbp_sleep(HZ/100); + if (jiffies > timeout_2) gear++; + break; + case 1: + sbp_sleep(0); + if (jiffies > timeout_1) gear++; + } + } while (gear < 5); + return -1; +} /*==========================================================================*/ static int CDi_stat_loop(void) { @@ -1290,8 +1349,20 @@ for (ntries=CMDT_TRIES;ntries>0;ntries--) { if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ); /* fixme */ +#if 01 OUT(CDo_sel_i_d,1); - i=inb(CDi_status); +#endif 01 + if (teac==2) + { + if ((i=CDi_stat_loop_T()) == -1) break; + } + else + { +#if 0 + OUT(CDo_sel_i_d,1); +#endif 0 + i=inb(CDi_status); + } if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */ { OUT(CDo_sel_i_d,1); @@ -1384,7 +1455,8 @@ return (-D_S[d].error_state-400); } if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */ - sbp_sleep(HZ/10); + if ((teac==0)||(ntries<(CMDT_TRIES-5))) sbp_sleep(HZ/10); + else sbp_sleep(HZ/100); if (ntries>(CMDT_TRIES-50)) continue; msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1); } @@ -3343,7 +3415,7 @@ } else if (famV_drive) { - if (j==100) D_S[d].drv_type=drv_at; + if ((j==100)||(j==150)) D_S[d].drv_type=drv_at; ask_mail(); /* hopefully we get some feedback by this */ } } @@ -3716,7 +3788,7 @@ return (i); } if (D_S[d].f_multisession) D_S[d].sbp_bufsiz=1; /* possibly a weird PhotoCD */ - else D_S[d].sbp_bufsiz=SBP_BUFFER_FRAMES; + else D_S[d].sbp_bufsiz=buffers; i=cc_ReadTocEntry(D_S[d].n_first_track); if (i<0) { @@ -3878,16 +3950,17 @@ msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev); return (-ENXIO); /* no such drive */ } + down(&ioctl_read_sem); if (d!=i) switch_drive(i); #if 0 st=GetStatus(); - if (st<0) return (-EIO); + if (st<0) RETURN_UP(-EIO); if (!toc_valid) { i=DiskInfo(); - if (i<0) return (-EIO); /* error reading TOC */ + if (i<0) RETURN_UP(-EIO); /* error reading TOC */ } #endif @@ -3895,9 +3968,9 @@ switch (cmd) /* Sun-compatible */ { case DDIOCSDBG: /* DDI Debug */ - if (!suser()) return (-EPERM); + if (!suser()) RETURN_UP(-EPERM); i=sbpcd_dbg_ioctl(arg,1); - return (i); + RETURN_UP(i); case CDROMPAUSE: /* Pause the drive */ msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n"); @@ -3910,19 +3983,19 @@ case audio_playing: if (famL_drive) i=cc_ReadSubQ(); else i=cc_Pause_Resume(1); - if (i<0) return (-EIO); + if (i<0) RETURN_UP(-EIO); if (famL_drive) i=cc_Pause_Resume(1); else i=cc_ReadSubQ(); - if (i<0) return (-EIO); + if (i<0) RETURN_UP(-EIO); D_S[d].pos_audio_start=D_S[d].SubQ_run_tot; D_S[d].audio_state=audio_pausing; - return (0); + RETURN_UP(0); case audio_pausing: i=cc_Seek(D_S[d].pos_audio_start,1); - if (i<0) return (-EIO); - return (0); + if (i<0) RETURN_UP(-EIO); + RETURN_UP(0); default: - return (-EINVAL); + RETURN_UP(-EINVAL); } case CDROMRESUME: /* resume paused audio play */ @@ -3935,26 +4008,26 @@ i=cc_PlayAudio(D_S[d].pos_audio_start, D_S[d].pos_audio_end); else i=cc_Pause_Resume(3); - if (i<0) return (-EIO); + if (i<0) RETURN_UP(-EIO); D_S[d].audio_state=audio_playing; - return (0); + RETURN_UP(0); case CDROMPLAYMSF: msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n"); #if SAFE_MIXED - if (D_S[d].has_data>1) return (-EBUSY); + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED if (D_S[d].audio_state==audio_playing) { i=cc_Pause_Resume(1); - if (i<0) return (-EIO); + if (i<0) RETURN_UP(-EIO); i=cc_ReadSubQ(); - if (i<0) return (-EIO); + if (i<0) RETURN_UP(-EIO); D_S[d].pos_audio_start=D_S[d].SubQ_run_tot; i=cc_Seek(D_S[d].pos_audio_start,1); } st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf)); - if (st) return (st); + if (st) RETURN_UP(st); memcpy_fromfs(&msf, (void *) arg, sizeof(struct cdrom_msf)); /* values come as msf-bin */ D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) | @@ -3971,33 +4044,36 @@ msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); DriveReset(); D_S[d].audio_state=0; - return (-EIO); + RETURN_UP(-EIO); } D_S[d].audio_state=audio_playing; - return (0); + RETURN_UP(0); case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n"); #if SAFE_MIXED - if (D_S[d].has_data>1) return (-EBUSY); + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED if (D_S[d].audio_state==audio_playing) { msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n"); - return (0); - return (-EINVAL); +#if 1 + RETURN_UP(0); /* just let us play on */ +#else + RETURN_UP(-EINVAL); /* play on, but say "error" */ +#endif } st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti)); if (st<0) { msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n"); - return (st); + RETURN_UP(st); } memcpy_fromfs(&ti,(void *) arg,sizeof(struct cdrom_ti)); msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n", ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1); - if (ti.cdti_trk0D_S[d].n_last_track) return (-EINVAL); + if (ti.cdti_trk0D_S[d].n_last_track) RETURN_UP(-EINVAL); if (ti.cdti_trk1D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track; D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address; @@ -4008,28 +4084,29 @@ msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); DriveReset(); D_S[d].audio_state=0; - return (-EIO); + RETURN_UP(-EIO); } D_S[d].audio_state=audio_playing; - return (0); + RETURN_UP(0); case CDROMREADTOCHDR: /* Read the table of contents header */ msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n"); tochdr.cdth_trk0=D_S[d].n_first_track; tochdr.cdth_trk1=D_S[d].n_last_track; st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr)); - if (st) return (st); + if (st) RETURN_UP(st); memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr)); - return (0); + RETURN_UP(0); case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n"); st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry)); - if (st) return (st); + if (st) RETURN_UP(st); memcpy_fromfs(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry)); i=tocentry.cdte_track; if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1; - else if (iD_S[d].n_last_track) return (-EINVAL); + else if (iD_S[d].n_last_track) + RETURN_UP(-EINVAL); tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F; tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F; tocentry.cdte_datamode=D_S[d].TocBuffer[i].format; @@ -4041,68 +4118,68 @@ } else if (tocentry.cdte_format==CDROM_LBA) /* blk required */ tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address); - else return (-EINVAL); + else RETURN_UP(-EINVAL); memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry)); - return (0); + RETURN_UP(0); case CDROMRESET: /* hard reset the drive */ msg(DBG_IOC,"ioctl: CDROMRESET entered.\n"); i=DriveReset(); D_S[d].audio_state=0; - return (i); + RETURN_UP(i); case CDROMSTOP: /* Spin down the drive */ msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n"); #if SAFE_MIXED - if (D_S[d].has_data>1) return (-EBUSY); + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED i=cc_Pause_Resume(1); D_S[d].audio_state=0; - return (i); + RETURN_UP(i); case CDROMSTART: /* Spin up the drive */ msg(DBG_IOC,"ioctl: CDROMSTART entered.\n"); cc_SpinUp(); D_S[d].audio_state=0; - return (0); + RETURN_UP(0); case CDROMEJECT: msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n"); if (fam0_drive) return (0); - if (D_S[d].open_count>1) return (-EBUSY); + if (D_S[d].open_count>1) RETURN_UP(-EBUSY); i=UnLockDoor(); D_S[d].open_count=-9; /* to get it locked next time again */ i=cc_SpinDown(); msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i); msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i); - if (i<0) return (-EIO); + if (i<0) RETURN_UP(-EIO); D_S[d].CD_changed=0xFF; D_S[d].diskstate_flags=0; D_S[d].audio_state=0; - return (0); + RETURN_UP(0); case CDROMEJECT_SW: msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n"); - if (fam0_drive) return (0); + if (fam0_drive) RETURN_UP(0); D_S[d].f_eject=arg; - return (0); + RETURN_UP(0); case CDROMVOLCTRL: /* Volume control */ msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n"); st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl)); - if (st) return (st); + if (st) RETURN_UP(st); memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl)); D_S[d].vol_chan0=0; D_S[d].vol_ctrl0=volctrl.channel0; D_S[d].vol_chan1=1; D_S[d].vol_ctrl1=volctrl.channel1; i=cc_SetVolume(); - return (0); + RETURN_UP(0); case CDROMVOLREAD: /* read Volume settings from drive */ msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n"); st=verify_area(VERIFY_WRITE,(void *)arg,sizeof(volctrl)); - if (st) return (st); + if (st) RETURN_UP(st); st=cc_GetVolume(); if (st<0) return (st); volctrl.channel0=D_S[d].vol_ctrl0; @@ -4110,15 +4187,16 @@ volctrl.channel2=0; volctrl.channel2=0; memcpy_tofs((void *)arg,&volctrl,sizeof(volctrl)); - return (0); + RETURN_UP(0); case CDROMSUBCHNL: /* Get subchannel info */ msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n"); - if ((st_spinning)||(!subq_valid)) { i=cc_ReadSubQ(); - if (i<0) return (-EIO); - } + if ((st_spinning)||(!subq_valid)) { + i=cc_ReadSubQ(); + if (i<0) RETURN_UP(-EIO); + } st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl)); - if (st) return (st); + if (st) RETURN_UP(st); memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl)); switch (D_S[d].audio_state) { @@ -4156,27 +4234,27 @@ SC.cdsc_adr,SC.cdsc_ctrl, SC.cdsc_trk,SC.cdsc_ind, SC.cdsc_absaddr,SC.cdsc_reladdr); - return (0); + RETURN_UP(0); case CDROMREADMODE1: msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n"); #if SAFE_MIXED - if (D_S[d].has_data>1) return (-EBUSY); + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED cc_ModeSelect(CD_FRAMESIZE); cc_ModeSense(); D_S[d].mode=READ_M1; - return (0); + RETURN_UP(0); case CDROMREADMODE2: /* not usable at the moment */ msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n"); #if SAFE_MIXED - if (D_S[d].has_data>1) return (-EBUSY); + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED cc_ModeSelect(CD_FRAMESIZE_RAW1); cc_ModeSense(); D_S[d].mode=READ_M2; - return (0); + RETURN_UP(0); case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */ msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n"); @@ -4193,11 +4271,11 @@ } else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[d].sbp_audsiz); } - return (D_S[d].sbp_audsiz); + RETURN_UP(D_S[d].sbp_audsiz); case CDROMREADAUDIO: { /* start of CDROMREADAUDIO */ - int i=0, j=0, frame, block; + int i=0, j=0, frame, block=0; u_int try=0; u_long timeout; u_char *p; @@ -4208,27 +4286,27 @@ int error_flag; msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n"); - if (fam0_drive) return (-EINVAL); - if (famL_drive) return (-EINVAL); - if (famV_drive) return (-EINVAL); - if (famT_drive) return (-EINVAL); + if (fam0_drive) RETURN_UP(-EINVAL); + if (famL_drive) RETURN_UP(-EINVAL); + if (famV_drive) RETURN_UP(-EINVAL); + if (famT_drive) RETURN_UP(-EINVAL); #if SAFE_MIXED - if (D_S[d].has_data>1) return (-EBUSY); + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED - if (D_S[d].aud_buf==NULL) return (-EINVAL); + if (D_S[d].aud_buf==NULL) RETURN_UP(-EINVAL); i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio)); - if (i) return (i); + if (i) RETURN_UP(i); memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio)); - if (read_audio.nframes>D_S[d].sbp_audsiz) return (-EINVAL); + if (read_audio.nframes>D_S[d].sbp_audsiz) RETURN_UP(-EINVAL); i=verify_area(VERIFY_WRITE, read_audio.buf, read_audio.nframes*CD_FRAMESIZE_RAW); - if (i) return (i); + if (i) RETURN_UP(i); if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */ block=msf2lba(&read_audio.addr.msf.minute); else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */ block=read_audio.addr.lba; - else return (-EINVAL); + else RETURN_UP(-EINVAL); #if 000 i=cc_SetSpeed(speed_150,0,0); if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i); @@ -4236,8 +4314,10 @@ msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n", block, blk2msf(block)); msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n"); +#if OLD_BUSY while (busy_data) sbp_sleep(HZ/10); /* wait a bit */ busy_audio=1; +#endif OLD_BUSY error_flag=0; for (data_tries=5; data_tries>0; data_tries--) { @@ -4400,43 +4480,45 @@ cc_ModeSelect(CD_FRAMESIZE); cc_ModeSense(); D_S[d].mode=READ_M1; +#if OLD_BUSY busy_audio=0; +#endif OLD_BUSY if (data_tries == 0) { msg(DBG_AUD,"read_audio: failed after 5 tries.\n"); - return (-8); + RETURN_UP(-EIO); } msg(DBG_AUD,"read_audio: successful return.\n"); - return (0); + RETURN_UP(0); } /* end of CDROMREADAUDIO */ case CDROMMULTISESSION: /* tell start-of-last-session */ msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n"); st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession)); - if (st) return (st); + if (st) RETURN_UP(st); memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession)); if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */ lba2msf(D_S[d].lba_multi,&ms_info.addr.msf.minute); else if (ms_info.addr_format==CDROM_LBA) /* lba requested */ ms_info.addr.lba=D_S[d].lba_multi; - else return (-EINVAL); + else RETURN_UP(-EINVAL); if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */ else ms_info.xa_flag=0; /* invalid redirection address */ memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession)); msg(DBG_MUL,"ioctl: CDROMMULTISESSION done (%d, %08X).\n", ms_info.xa_flag, ms_info.addr.lba); - return (0); + RETURN_UP(0); case BLKRASET: - if(!suser()) return -EACCES; - if(!(inode->i_rdev)) return -EINVAL; - if(arg > 0xff) return -EINVAL; + if(!suser()) RETURN_UP(-EACCES); + if(!(inode->i_rdev)) RETURN_UP(-EINVAL); + if(arg > 0xff) RETURN_UP(-EINVAL); read_ahead[MAJOR(inode->i_rdev)] = arg; - return (0); + RETURN_UP(0); default: msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd); - return (-EINVAL); + RETURN_UP(-EINVAL); } /* end switch(cmd) */ } /*==========================================================================*/ @@ -4489,6 +4571,7 @@ static inline void sbpcd_end_request(struct request *req, int uptodate) { req->next=CURRENT; CURRENT=req; + up(&ioctl_read_sem); end_request(uptodate); } /*==========================================================================*/ @@ -4528,6 +4611,7 @@ CURRENT=req->next; /* task can fuck it up GTL */ sti(); + down(&ioctl_read_sem); if (req->rq_status == RQ_INACTIVE) sbpcd_end_request(req, 0); if (req -> sector == -1) @@ -4545,8 +4629,10 @@ kdevname(req->rq_dev)); goto err_done; } +#if OLD_BUSY while (busy_audio) sbp_sleep(HZ); /* wait a bit */ busy_data=1; +#endif OLD_BUSY if (D_S[i].audio_state==audio_playing) goto err_done; if (d!=i) switch_drive(i); @@ -4612,7 +4698,9 @@ } err_done: +#if OLD_BUSY busy_data=0; +#endif OLD_BUSY #ifdef DEBUG_GTL printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 4 (error), Time:%li\n", xnr, req, req->sector, req->nr_sectors, jiffies); @@ -4753,8 +4841,8 @@ #if LONG_TIMING max_latency=9*HZ; #else - if (D_S[d].f_multisession) max_latency=9*HZ; - else max_latency=3*HZ; + if (D_S[d].f_multisession) max_latency=15*HZ; + else max_latency=5*HZ; #endif duration=jiffies; for (frame=0;framef_mode & 2) return -EROFS; + down(&ioctl_read_sem); switch_drive(i); i=cc_ReadError(); flags_cmd_out |= f_respo2; - cc_ReadStatus(); /* command: give 1-byte status */ + cc_ReadStatus(); /* command: give 1-byte status */ i=ResponseStatus(); if (famT_drive&&(i<0)) { cc_DriveReset(); i=ResponseStatus(); +#if 0 + sbp_sleep(HZ); +#endif 0 i=ResponseStatus(); } if (i<0) { msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i); - return (-EIO); /* drive doesn't respond */ + RETURN_UP(-EIO); /* drive doesn't respond */ } if (famT_drive) msg(DBG_TEA,"sbpcd_open: ResponseStatus=%02X\n", i); if (!st_door_closed) @@ -5065,7 +5164,7 @@ cc_SpinDown(); /* eject tray */ } #endif - return (-ENXIO); + RETURN_UP(-ENXIO); } /* * try to keep an "open" counter here and lock the door if 0->1. @@ -5092,7 +5191,7 @@ #endif SAFE_MIXED } if (!st_spinning) cc_SpinUp(); - return (0); + RETURN_UP(0); } /*==========================================================================*/ /* @@ -5108,6 +5207,7 @@ msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev); return; } + down(&ioctl_read_sem); switch_drive(i); /* * try to keep an "open" counter here and unlock the door if 1->0. @@ -5132,6 +5232,7 @@ #endif SAFE_MIXED } } + up(&ioctl_read_sem); } /*==========================================================================*/ /* @@ -5365,7 +5466,10 @@ } if (port_index>0) - msg(DBG_INF, "You should configure sbpcd.h for your hardware.\n"); + { + msg(DBG_INF, "You should read linux/Documentation/cdrom/sbpcd\n"); + msg(DBG_INF, "and then configure sbpcd.h for your hardware.\n"); + } check_datarate(); msg(DBG_INI,"check_datarate done.\n"); @@ -5450,7 +5554,7 @@ #endif MODULE } blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; - read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512); + read_ahead[MAJOR_NR] = buffers * (CD_FRAMESIZE / 512); request_region(CDo_command,4,major_name); @@ -5466,7 +5570,7 @@ */ D_S[j].aud_buf=NULL; D_S[j].sbp_audsiz=0; - D_S[j].sbp_bufsiz=SBP_BUFFER_FRAMES; + D_S[j].sbp_bufsiz=buffers; if (D_S[j].drv_type&drv_fam1) if (READ_AUDIO>0) D_S[j].sbp_audsiz=READ_AUDIO; D_S[j].sbp_buf=(u_char *) vmalloc(D_S[j].sbp_bufsiz*CD_FRAMESIZE); @@ -5476,7 +5580,7 @@ return -EIO; } #ifdef MODULE - msg(DBG_INF,"data buffer size: %d frames.\n",SBP_BUFFER_FRAMES); + msg(DBG_INF,"data buffer size: %d frames.\n",buffers); #endif MODULE if (D_S[j].sbp_audsiz>0) { diff -ur --new-file old/linux/drivers/char/Makefile new/linux/drivers/char/Makefile --- old/linux/drivers/char/Makefile Thu Aug 1 14:43:04 1996 +++ new/linux/drivers/char/Makefile Thu Oct 31 13:31:41 1996 @@ -141,6 +141,16 @@ endif endif +ifeq ($(CONFIG_PCWATCHDOG),y) +M = y +L_OBJS += pcwd.o +else + ifeq ($(CONFIG_PCWATCHDOG),m) + M_OBJS += pcwd.o + MM = m + endif +endif + ifdef CONFIG_SUN_MOUSE M = y endif @@ -193,16 +203,6 @@ M_OBJS += scc.o endif endif - -ifeq ($(CONFIG_PCWATCHDOG),y) -M = y -L_OBJS += pcwd.o -else - ifeq ($(CONFIG_PCWATCHDOG),m) - M_OBJS += pcwd.o - MM = m - endif -endif ifeq ($(CONFIG_BAYCOM),y) L_OBJS += baycom.o diff -ur --new-file old/linux/drivers/char/console.c new/linux/drivers/char/console.c --- old/linux/drivers/char/console.c Sat Nov 16 00:48:04 1996 +++ new/linux/drivers/char/console.c Sat Nov 16 00:48:32 1996 @@ -482,7 +482,7 @@ */ static void gotoxy(int currcons, int new_x, int new_y) { - int max_y; + int min_y, max_y; if (new_x < 0) x = 0; @@ -492,21 +492,28 @@ else x = new_x; if (decom) { - new_y += top; + min_y = top; max_y = bottom; - } else + } else { + min_y = 0; max_y = video_num_lines; - if (new_y < 0) - y = 0; + } + if (new_y < min_y) + y = min_y; + else if (new_y >= max_y) + y = max_y - 1; else - if (new_y >= max_y) - y = max_y - 1; - else - y = new_y; + y = new_y; pos = origin + y*video_size_row + (x<<1); need_wrap = 0; } +/* for absolute user moves, when decom is set */ +static void gotoxay(int currcons, int new_x, int new_y) +{ + gotoxy(currcons, new_x, decom ? (top+new_y) : new_y); +} + /* * Hardware scrollback support */ @@ -1075,7 +1082,7 @@ break; case 6: /* Origin relative/absolute */ decom = on_off; - gotoxy(currcons,0,0); + gotoxay(currcons,0,0); break; case 7: /* Autowrap on/off */ decawm = on_off; @@ -1426,34 +1433,35 @@ tc = translate[toggle_meta ? (c|0x80) : c]; } - /* If the original code was < 32 we only allow a - * glyph to be displayed if the code is not normally - * used (such as for cursor movement) or if the - * disp_ctrl mode has been explicitly enabled. - * Note: ESC is *never* allowed to be displayed as - * that would disable all escape sequences! - * To display font position 0x1B, go into UTF mode - * and display character U+F01B, or change the mapping. - */ - ok = (tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS - : CTRL_ACTION) >> c) & 1)))); + /* If the original code was a control character we + * only allow a glyph to be displayed if the code is + * not normally used (such as for cursor movement) or + * if the disp_ctrl mode has been explicitly enabled. + * Certain characters (as given by the CTRL_ALWAYS + * bitmap) are always displayed as control characters, + * as the console would be pretty useless without + * them; to display an arbitrary font position use the + * direct-to-font zone in UTF-8 mode. + */ + ok = tc && (c >= 32 || + (!utf && !(((disp_ctrl ? CTRL_ALWAYS + : CTRL_ACTION) >> c) & 1))) + && (c != 127 || disp_ctrl) + && (c != 128+27); if (vc_state == ESnormal && ok) { /* Now try to find out how to display it */ tc = conv_uni_to_pc(tc); - if ( tc == -4 ) - { - /* If we got -4 (not found) then see if we have - defined a replacement character (U+FFFD) */ - tc = conv_uni_to_pc(0xfffd); - } - else if ( tc == -3 ) - { - /* Bad hash table -- hope for the best */ - tc = c; - } + if ( tc == -4 ) { + /* If we got -4 (not found) then see if we have + defined a replacement character (U+FFFD) */ + tc = conv_uni_to_pc(0xfffd); + } else if ( tc == -3 ) { + /* Bad hash table -- hope for the best */ + tc = c; + } if (tc & ~console_charmask) - continue; /* Conversion failed */ + continue; /* Conversion failed */ if (need_wrap) { cr(currcons); @@ -1479,6 +1487,8 @@ * of an escape sequence. */ switch (c) { + case 0: + continue; case 7: if (bell_duration) kd_mksound(bell_pitch, bell_duration); @@ -1683,12 +1693,12 @@ continue; case 'd': if (par[0]) par[0]--; - gotoxy(currcons,x,par[0]); + gotoxay(currcons,x,par[0]); continue; case 'H': case 'f': if (par[0]) par[0]--; if (par[1]) par[1]--; - gotoxy(currcons,par[1],par[0]); + gotoxay(currcons,par[1],par[0]); continue; case 'J': csi_J(currcons,par[0]); @@ -1739,7 +1749,7 @@ par[1] <= video_num_lines) { top=par[0]-1; bottom=par[1]; - gotoxy(currcons,0,0); + gotoxay(currcons,0,0); } continue; case 's': diff -ur --new-file old/linux/drivers/char/cyclades.c new/linux/drivers/char/cyclades.c --- old/linux/drivers/char/cyclades.c Thu Aug 1 14:31:04 1996 +++ new/linux/drivers/char/cyclades.c Tue Oct 8 06:03:17 1996 @@ -1,5 +1,5 @@ static char rcsid[] = -"$Revision: 1.36.3.7A $$Date: 1996/07/27 10:25:50 $"; +"$Revision: 1.36.3.9 $$Date: 1996/10/07 19:47:13 $"; /* * linux/drivers/char/cyclades.c * @@ -24,6 +24,14 @@ * int cy_open(struct tty_struct *tty, struct file *filp); * * $Log: cyclades.c,v $ + * Revision 1.36.3.9 1996/10/07 19:47:13 bentson + * add MOD_DEC_USE_COUNT in one return from cy_close (as + * noted by Jon Lewis ) + * + * Revision 1.36.3.8 1996/06/07 16:29:00 bentson + * starting minor number at zero; added missing verify_area + * as noted by Heiko Eissfeldt + * * Revision 1.36.3.7 1996/04/19 21:06:18 bentson * remove unneeded boot message & fix CLOCAL hardware flow * control (Miquel van Smoorenburg ); @@ -2355,6 +2363,7 @@ /* If the TTY is being hung up, nothing to do */ if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } diff -ur --new-file old/linux/drivers/char/ftape/fdc-io.c new/linux/drivers/char/ftape/fdc-io.c --- old/linux/drivers/char/ftape/fdc-io.c Fri Apr 12 08:49:34 1996 +++ new/linux/drivers/char/ftape/fdc-io.c Mon Sep 2 14:18:26 1996 @@ -311,7 +311,7 @@ int current_blocked = current->blocked; static int resetting = 0; - if (wait_intr) { + if (waitqueue_active(&wait_intr)) { TRACE(1, "error: nested call"); return -EIO; /* return error... */ } diff -ur --new-file old/linux/drivers/char/ftape/fdc-isr.c new/linux/drivers/char/ftape/fdc-isr.c --- old/linux/drivers/char/ftape/fdc-isr.c Fri Apr 12 08:49:34 1996 +++ new/linux/drivers/char/ftape/fdc-isr.c Mon Sep 2 14:18:26 1996 @@ -771,7 +771,7 @@ } seek_completed = 1; fdc_mode = fdc_idle; - } else if (!wait_intr) { + } else if (!waitqueue_active(&wait_intr)) { if (expected_stray_interrupts == 0) { TRACE(2, "unexpected stray interrupt"); } else { diff -ur --new-file old/linux/drivers/char/keyb_m68k.c new/linux/drivers/char/keyb_m68k.c --- old/linux/drivers/char/keyb_m68k.c Fri Apr 5 16:16:48 1996 +++ new/linux/drivers/char/keyb_m68k.c Thu Nov 7 10:25:21 1996 @@ -508,8 +508,9 @@ #define A_CFLEX '^' #define A_TILDE '~' #define A_DIAER '"' -static unsigned char ret_diacr[] = - {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER }; +#define A_CEDIL ',' +static unsigned char ret_diacr[NR_DEAD] = + {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL }; /* If a dead key pressed twice, output a character corresponding to it, */ /* otherwise just remember the dead key. */ diff -ur --new-file old/linux/drivers/char/keyboard.c new/linux/drivers/char/keyboard.c --- old/linux/drivers/char/keyboard.c Thu Jul 25 19:24:49 1996 +++ new/linux/drivers/char/keyboard.c Thu Nov 7 10:25:21 1996 @@ -822,8 +822,9 @@ #define A_CFLEX '^' #define A_TILDE '~' #define A_DIAER '"' -static unsigned char ret_diacr[] = - {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER }; +#define A_CEDIL ',' +static unsigned char ret_diacr[NR_DEAD] = + {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL }; /* If a dead key pressed twice, output a character corresponding to it, */ /* otherwise just remember the dead key. */ diff -ur --new-file old/linux/drivers/char/mem.c new/linux/drivers/char/mem.c --- old/linux/drivers/char/mem.c Thu Aug 1 14:43:04 1996 +++ new/linux/drivers/char/mem.c Fri Sep 20 16:00:34 1996 @@ -29,6 +29,9 @@ #ifdef CONFIG_ISDN void isdn_init(void); #endif +#ifdef CONFIG_PCWATCHDOG +void pcwatchdog_init(void); +#endif static int read_ram(struct inode * inode, struct file * file, char * buf, int count) { @@ -193,6 +196,7 @@ static int read_full(struct inode * node, struct file * file, char * buf,int count) { + file->f_pos += count; return count; } @@ -392,6 +396,7 @@ #if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \ defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \ defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \ + defined (CONFIG_PCWATCHDOG) || \ defined (CONFIG_APM) || defined (CONFIG_RTC) || defined (CONFIG_SUN_MOUSE) misc_init(); #endif @@ -400,9 +405,6 @@ #endif #if CONFIG_QIC02_TAPE qic02_tape_init(); -#endif -#ifdef CONFIG_PCWATCHDOG - pcwatchdog_init(); #endif #if CONFIG_ISDN isdn_init(); diff -ur --new-file old/linux/drivers/char/misc.c new/linux/drivers/char/misc.c --- old/linux/drivers/char/misc.c Fri Apr 26 09:30:50 1996 +++ new/linux/drivers/char/misc.c Wed Nov 6 13:39:42 1996 @@ -67,6 +67,8 @@ extern int atixl_busmouse_init(void); extern int sun_mouse_init(void); extern void watchdog_init(void); +extern void wdt_init(void); +extern void pcwatchdog_init(void); extern int rtc_init(void); #ifdef CONFIG_PROC_FS @@ -217,7 +219,13 @@ #endif #ifdef CONFIG_SOFT_WATCHDOG watchdog_init(); -#endif +#endif +#ifdef CONFIG_WDT + wdt_init(); +#endif +#ifdef CONFIG_PCWATCHDOG + pcwatchdog_init(); +#endif #ifdef CONFIG_APM apm_bios_init(); #endif diff -ur --new-file old/linux/drivers/char/n_tty.c new/linux/drivers/char/n_tty.c --- old/linux/drivers/char/n_tty.c Wed Jul 24 09:49:53 1996 +++ new/linux/drivers/char/n_tty.c Mon Sep 2 14:18:26 1996 @@ -795,7 +795,7 @@ current->timeout = (unsigned long) -1; if (time) tty->minimum_to_wake = 1; - else if (!tty->read_wait || + else if (!waitqueue_active(&tty->read_wait) || (tty->minimum_to_wake > minimum)) tty->minimum_to_wake = minimum; } else { @@ -907,7 +907,7 @@ } remove_wait_queue(&tty->read_wait, &wait); - if (!tty->read_wait) + if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = minimum; current->state = TASK_RUNNING; @@ -991,7 +991,7 @@ return 1; if (tty_hung_up_p(file)) return 1; - if (!tty->read_wait) { + if (!waitqueue_active(&tty->read_wait)) { if (MIN_CHAR(tty) && !TIME_CHAR(tty)) tty->minimum_to_wake = MIN_CHAR(tty); else diff -ur --new-file old/linux/drivers/char/pcwd.c new/linux/drivers/char/pcwd.c --- old/linux/drivers/char/pcwd.c Thu Aug 1 15:12:26 1996 +++ new/linux/drivers/char/pcwd.c Thu Aug 29 18:15:14 1996 @@ -13,6 +13,8 @@ * inclusion in Linux 2.0.x kernels, thanks to Alan Cox. * 960717 Removed read/seek routines, replaced with ioctl. Also, added * check_region command due to Alan's suggestion. + * 960821 Made changes to compile in newer 2.0.x kernels. Added + * "cold reboot sense" entry. */ #include @@ -35,7 +37,7 @@ #include -#define WD_VER "0.41 (07/17/96)" +#define WD_VER "0.50 (08/21/96)" #define WD_MINOR 130 /* Minor device number */ #define WD_TIMEOUT 3 /* 1 1/2 seconds for a timeout */ @@ -160,7 +162,11 @@ printk("pcwd: Previous reboot was caused by the card.\n"); if (card_status & WD_T110) - printk("pcwd: CPU overheat sense\n"); + printk("pcwd: CPU overheat sense.\n"); + + if ((!(card_status & WD_WDRST)) && + (!(card_status & WD_T110))) + printk("pcwd: Cold boot sense.\n"); } static int pcwd_return_data(void) @@ -190,7 +196,7 @@ return(1); } -static int pcwd_ioctl(struct tty_struct *tty, struct file *file, +static int pcwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int i, cdat, rv; @@ -246,7 +252,7 @@ MOD_DEC_USE_COUNT; } -struct file_operations pcwd_fops = { +static struct file_operations pcwd_fops = { NULL, /* Seek */ NULL, /* Read */ pcwd_write, /* Write */ @@ -255,8 +261,7 @@ pcwd_ioctl, /* IOctl */ NULL, /* MMAP */ pcwd_open, /* Open */ - pcwd_close, /* Close */ - NULL + pcwd_close /* Close */ }; static struct miscdevice pcwd_miscdev = { @@ -295,12 +300,12 @@ current_readport = WD_CTLSTAT_PORT2; if (!pcwd_checkcard()) { - printk("pcwd: No card detected.\n"); + printk("pcwd: No card detected, or wrong port assigned.\n"); return(-EIO); } else - printk("pcwd: Port available at 0x370.\n"); + printk("pcwd: Watchdog Rev.A detected at port 0x370\n"); } else - printk("pcwd: Port available at 0x270.\n"); + printk("pcwd: Watchdog Rev.A detected at port 0x270\n"); pcwd_showprevstate(); @@ -308,7 +313,7 @@ printk("pcwd: Requesting region entry\n"); #endif - request_region(current_ctlport, WD_PORT_EXTENT, "PCWD (Berkshire)"); + request_region(current_ctlport, WD_PORT_EXTENT, "PCWD Rev.A (Berkshire)"); #ifdef DEBUG printk("pcwd: character device creation.\n"); @@ -329,3 +334,20 @@ #endif } #endif + +/* +** TODO: +** +** Both Revisions: +** o) Support for revision B of the Watchdog Card +** o) Implement the rest of the IOCTLs as discussed with Alan Cox +** o) Implement only card heartbeat reset via IOCTL, not via write +** o) Faster card detection routines +** o) /proc device creation +** +** Revision B functions: +** o) /dev/temp device creation for temperature device (possibly use +** the one from the WDT drivers?) +** o) Direct Motorola controller chip access via read/write routines +** o) Autoprobe IO Ports for autodetection (possibly by chip detect?) +*/ diff -ur --new-file old/linux/drivers/char/pcxx.c new/linux/drivers/char/pcxx.c --- old/linux/drivers/char/pcxx.c Thu Aug 1 14:40:32 1996 +++ new/linux/drivers/char/pcxx.c Sat Oct 5 07:46:20 1996 @@ -1120,10 +1120,12 @@ pcxe_termios = kmalloc(sizeof(struct termios *) * nbdevs, GFP_KERNEL); if (!pcxe_termios) panic("Unable to allocate pcxe_termios struct"); + memset(pcxe_termios,0,sizeof(struct termios *)*nbdevs); pcxe_termios_locked = kmalloc(sizeof(struct termios *) * nbdevs, GFP_KERNEL); if (!pcxe_termios_locked) panic("Unable to allocate pcxe_termios_locked struct"); + memset(pcxe_termios_locked,0,sizeof(struct termios *)*nbdevs); init_bh(DIGI_BH,do_pcxe_bh); enable_bh(DIGI_BH); diff -ur --new-file old/linux/drivers/char/psaux.c new/linux/drivers/char/psaux.c --- old/linux/drivers/char/psaux.c Tue Jul 23 05:48:28 1996 +++ new/linux/drivers/char/psaux.c Wed Sep 11 16:57:14 1996 @@ -25,6 +25,9 @@ * Rearranged SIGIO support to use code from tty_io. 9Sept95 ctm@ardi.com * * Modularised 8-Sep-95 Philip Blundell + * + * Fixed keyboard lockups at open time + * 3-Jul-96, 22-Aug-96 Roman Hodek */ /* Uncomment the following line if your mouse needs initialization. */ @@ -35,6 +38,7 @@ #include #include +#include #include #include #include @@ -258,10 +262,14 @@ fasync_aux(inode, file, 0); if (--aux_count) return; + /* disable kbd bh to avoid mixing of cmd bytes */ + disable_bh(KEYBOARD_BH); aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */ poll_aux_status(); outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */ poll_aux_status(); + /* reenable kbd bh */ + enable_bh(KEYBOARD_BH); free_irq(AUX_IRQ, NULL); MOD_DEC_USE_COUNT; } @@ -316,11 +324,16 @@ return -EBUSY; } MOD_INC_USE_COUNT; + /* disable kbd bh to avoid mixing of cmd bytes */ + disable_bh(KEYBOARD_BH); poll_aux_status(); outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */ aux_write_dev(AUX_ENABLE_DEV); /* enable aux device */ aux_write_cmd(AUX_INTS_ON); /* enable controller ints */ poll_aux_status(); + /* reenable kbd bh */ + enable_bh(KEYBOARD_BH); + aux_ready = 0; return 0; } @@ -377,18 +390,33 @@ static int write_aux(struct inode * inode, struct file * file, const char * buffer, int count) { - int i = count; + int retval = 0; - while (i--) { - if (!poll_aux_status()) - return -EIO; - outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); - if (!poll_aux_status()) - return -EIO; - outb_p(get_user(buffer++),AUX_OUTPUT_PORT); + if (count > 0) { + int written = 0; + + /* disable kbd bh to avoid mixing of cmd bytes */ + disable_bh(KEYBOARD_BH); + + do { + if (!poll_aux_status()) + break; + outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); + if (!poll_aux_status()) + break; + outb_p(get_user(buffer++),AUX_OUTPUT_PORT); + written++; + } while (--count); + /* reenable kbd bh */ + enable_bh(KEYBOARD_BH); + retval = -EIO; + if (written) { + retval = written; + inode->i_mtime = CURRENT_TIME; + } } - inode->i_mtime = CURRENT_TIME; - return count; + + return retval; } @@ -539,6 +567,7 @@ void cleanup_module(void) { misc_deregister(&psaux_mouse); + kfree(queue); } #endif diff -ur --new-file old/linux/drivers/char/pty.c new/linux/drivers/char/pty.c --- old/linux/drivers/char/pty.c Wed Jul 17 14:10:03 1996 +++ new/linux/drivers/char/pty.c Mon Sep 30 14:56:48 1996 @@ -181,66 +181,41 @@ int pty_open(struct tty_struct *tty, struct file * filp) { -#if PTY_SLAVE_WAITS_ON_OPEN - struct wait_queue wait = { current, NULL }; -#endif int retval; int line; struct pty_struct *pty; - + + retval = -ENODEV; if (!tty || !tty->link) - return -ENODEV; + goto out; line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= NR_PTYS)) - return -ENODEV; + goto out; pty = pty_state + line; tty->driver_data = pty; if (!tmp_buf) { - unsigned long page = get_free_page(GFP_KERNEL); + unsigned long page = __get_free_page(GFP_KERNEL); if (!tmp_buf) { + retval = -ENOMEM; if (!page) - return -ENOMEM; + goto out; tmp_buf = (unsigned char *) page; + memset((void *) page, 0, PAGE_SIZE); } else free_page(page); } + retval = -EIO; + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + goto out; + if (tty->link->count != 1) + goto out; clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); wake_up_interruptible(&pty->open_wait); set_bit(TTY_THROTTLED, &tty->flags); - if (filp->f_flags & O_NDELAY) - return 0; - /* - * If we're opening the master pty, just return. If we're - * trying to open the slave pty, then we have to wait for the - * master pty to open. - */ - if (tty->driver.subtype == PTY_TYPE_MASTER) - return 0; retval = 0; -#if PTY_SLAVE_WAITS_ON_OPEN - add_wait_queue(&pty->open_wait, &wait); - while (1) { - if (current->signal & ~current->blocked) { - retval = -ERESTARTSYS; - break; - } - /* - * Block until the master is open... - */ - current->state = TASK_INTERRUPTIBLE; - if (tty->link->count && - !test_bit(TTY_OTHER_CLOSED, &tty->flags)) - break; - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&pty->open_wait, &wait); -#else - if (!tty->link->count || test_bit(TTY_OTHER_CLOSED, &tty->flags)) - retval = -EPERM; -#endif +out: return retval; } diff -ur --new-file old/linux/drivers/char/random.c new/linux/drivers/char/random.c --- old/linux/drivers/char/random.c Tue Jul 2 18:08:42 1996 +++ new/linux/drivers/char/random.c Fri Sep 20 16:00:34 1996 @@ -1002,6 +1002,8 @@ nbytes -= i; buf += i; add_timer_randomness(r, &extract_timer_state, nbytes); + if (to_user && need_resched) + schedule(); } /* Wipe data from memory */ @@ -1229,7 +1231,7 @@ return -EINVAL; size = get_user(p++); retval = random_write(0, file, (const char *) p, size); - if (retval) + if (retval < 0) return retval; /* * Add ent_count to entropy_count, limiting the result to be diff -ur --new-file old/linux/drivers/char/selection.c new/linux/drivers/char/selection.c --- old/linux/drivers/char/selection.c Thu Jun 6 12:42:16 1996 +++ new/linux/drivers/char/selection.c Wed Oct 30 02:51:32 1996 @@ -289,9 +289,9 @@ if (!bp || !c) return 0; do_unblank_screen(); - current->state = TASK_INTERRUPTIBLE; add_wait_queue(&vt->paste_wait, &wait); - while (c) { + do { + current->state = TASK_INTERRUPTIBLE; if (test_bit(TTY_THROTTLED, &tty->flags)) { schedule(); continue; @@ -300,7 +300,8 @@ tty->ldisc.receive_buf(tty, bp, 0, l); c -= l; bp += l; - } + } while (c); + remove_wait_queue(&vt->paste_wait, &wait); current->state = TASK_RUNNING; return 0; } diff -ur --new-file old/linux/drivers/char/tty_io.c new/linux/drivers/char/tty_io.c --- old/linux/drivers/char/tty_io.c Wed Jul 24 09:53:39 1996 +++ new/linux/drivers/char/tty_io.c Thu Oct 31 13:34:58 1996 @@ -380,8 +380,9 @@ continue; if (filp->private_data != tty) continue; - if (filp->f_inode - && filp->f_inode->i_rdev == CONSOLE_DEV) + if (!filp->f_inode) + continue; + if (filp->f_inode->i_rdev == CONSOLE_DEV) continue; if (filp->f_op != &tty_fops) continue; @@ -776,8 +777,9 @@ ret = write(tty, file, buf, size); if (ret <= 0) break; - count -= ret; written += ret; + buf += ret; + count -= ret; if (!count) break; ret = -ERESTARTSYS; @@ -1349,7 +1351,7 @@ return retval; if (on) { - if (!tty->read_wait) + if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = 1; if (filp->f_owner == 0) { if (tty->pgrp) @@ -1358,7 +1360,7 @@ filp->f_owner = current->pid; } } else { - if (!tty->fasync && !tty->read_wait) + if (!tty->fasync && !waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = N_TTY_BUF_SIZE; } return 0; @@ -1694,7 +1696,7 @@ if (((*p)->tty == tty) || ((session > 0) && ((*p)->session == session))) send_sig(SIGKILL, *p, 1); - else { + else if ((*p)->files) { for (i=0; i < NR_OPEN; i++) { filp = (*p)->files->fd[i]; if (filp && (filp->f_op == &tty_fops) && diff -ur --new-file old/linux/drivers/isdn/icn/icn.c new/linux/drivers/isdn/icn/icn.c --- old/linux/drivers/isdn/icn/icn.c Sat Jun 29 19:36:22 1996 +++ new/linux/drivers/isdn/icn/icn.c Sun Sep 1 08:15:32 1996 @@ -1,4 +1,4 @@ -/* $Id: icn.c,v 1.28 1996/06/28 17:02:53 fritz Exp $ +/* $Id: icn.c,v 1.29 1996/08/29 20:34:54 fritz Exp $ * * ISDN low-level module for the ICN active ISDN-Card. * @@ -19,6 +19,11 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.c,v $ + * Revision 1.29 1996/08/29 20:34:54 fritz + * Bugfix in send queue management: + * sndcount was not updated correctly. + * Minor Bugfixes. + * * Revision 1.28 1996/06/28 17:02:53 fritz * replaced memcpy_fromfs_toio. * @@ -136,7 +141,7 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.28 $"; +*revision = "$Revision: 1.29 $"; static int icn_addcard(int, char *, char *); @@ -675,9 +680,7 @@ /* Append a packet to the transmit buffer-queue. * Parameters: * channel = Number of B-channel - * buffer = pointer to packet - * len = size of packet (max 4000) - * user = 1 = call from userproc, 0 = call from kernel + * skb = pointer to sk_buff * card = pointer to card-struct * Return: * Number of bytes transferred, -E??? on error @@ -687,6 +690,7 @@ { int len = skb->len; unsigned long flags; + struct sk_buff *nskb; if (len > 4000) { printk(KERN_WARNING @@ -700,13 +704,25 @@ return 0; save_flags(flags); cli(); + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + skb_queue_tail(&card->spqueue[channel], nskb); + dev_kfree_skb(skb, FREE_WRITE); + } else + len = 0; card->sndcount[channel] += len; - skb_queue_tail(&card->spqueue[channel], skb); restore_flags(flags); } return len; } +/* + * Check card's status after starting the bootstrap loader. + * On entry, the card's shared memory has already to be mapped. + * Return: + * 0 on success (Boot loader ready) + * -EIO on failure (timeout) + */ static int icn_check_loader(int cardnumber) { int timer = 0; @@ -1155,7 +1171,19 @@ case ICN_IOCTL_GETDOUBLE: return (int) card->doubleS0; case ICN_IOCTL_DEBUGVAR: - return (ulong) card; + if ((i = verify_area(VERIFY_WRITE, + (void *) a, + sizeof(ulong) * 2))) + return i; + memcpy_tofs((char *)a, + (char *)&card, sizeof(ulong)); + a += sizeof(ulong); + { + ulong l = (ulong)&dev; + memcpy_tofs((char *)a, + (char *)&l, sizeof(ulong)); + } + return 0; case ICN_IOCTL_LOADBOOT: icn_stopcard(card); return (icn_loadboot((u_char *) a, card)); @@ -1623,7 +1651,7 @@ } card = card->next; } - card = card; + card = cards; while (card) { last = card; card = card->next; diff -ur --new-file old/linux/drivers/isdn/icn/icn.h new/linux/drivers/isdn/icn/icn.h --- old/linux/drivers/isdn/icn/icn.h Sat Jun 29 19:36:22 1996 +++ new/linux/drivers/isdn/icn/icn.h Sun Sep 1 08:15:32 1996 @@ -1,4 +1,4 @@ -/* $Id: icn.h,v 1.20 1996/06/24 17:20:37 fritz Exp $ +/* $Id: icn.h,v 1.21 1996/08/29 20:35:57 fritz Exp $ * * ISDN lowlevel-module for the ICN active ISDN-Card. * @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.h,v $ + * Revision 1.21 1996/08/29 20:35:57 fritz + * Speed up B-Channel polling interval. + * * Revision 1.20 1996/06/24 17:20:37 fritz * Bugfixes in pollbchan_send(): * - Using lock field of skbuff breaks networking. @@ -156,7 +159,7 @@ #define ICN_BOOT_TIMEOUT1 100 /* Delay for Boot-download (jiffies) */ #define ICN_CHANLOCK_DELAY 10 /* Delay for Channel-mapping (jiffies) */ -#define ICN_TIMER_BCREAD 3 /* B-Channel poll-cycle */ +#define ICN_TIMER_BCREAD 1 /* B-Channel poll-cycle */ #define ICN_TIMER_DCREAD 50 /* D-Channel poll-cycle */ #define ICN_CODE_STAGE1 4096 /* Size of bootcode */ diff -ur --new-file old/linux/drivers/isdn/isdn_cards.c new/linux/drivers/isdn/isdn_cards.c --- old/linux/drivers/isdn/isdn_cards.c Sun Apr 21 10:56:14 1996 +++ new/linux/drivers/isdn/isdn_cards.c Wed Oct 30 02:42:40 1996 @@ -27,15 +27,15 @@ #include #ifdef CONFIG_ISDN_DRV_ICN -extern void icn_init(void); +extern int icn_init(void); #endif #ifdef CONFIG_ISDN_DRV_TELES -extern void teles_init(void); +extern int teles_init(void); #endif #ifdef CONFIG_ISDN_DRV_PCBIT -extern void pcbit_init(void); +extern int pcbit_init(void); #endif void isdn_cards_init(void) diff -ur --new-file old/linux/drivers/isdn/isdn_net.c new/linux/drivers/isdn/isdn_net.c --- old/linux/drivers/isdn/isdn_net.c Wed Jul 17 10:55:48 1996 +++ new/linux/drivers/isdn/isdn_net.c Sun Sep 1 08:15:32 1996 @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.18 1996/07/03 13:48:51 hipp Exp $ +/* $Id: isdn_net.c,v 1.20 1996/08/29 20:06:03 fritz Exp $ * * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -21,6 +21,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.20 1996/08/29 20:06:03 fritz + * Bugfix: Transmission timeout had been much to low. + * + * Revision 1.19 1996/08/12 16:24:32 hipp + * removed some (now) obsolete functions for syncPPP in rebuild_header etc. + * * Revision 1.18 1996/07/03 13:48:51 hipp * bugfix: Call dev_purge_queues() only for master device * @@ -124,7 +130,7 @@ extern void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */ -char *isdn_net_revision = "$Revision: 1.18 $"; +char *isdn_net_revision = "$Revision: 1.20 $"; /* * Code for raw-networking over ISDN @@ -369,6 +375,10 @@ */ lp->chargetime = jiffies; /* Immediately send first skb to speed up arp */ +#ifdef CONFIG_ISDN_PPP + if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) + isdn_ppp_wakeup_daemon(lp); +#endif if (lp->first_skb) { if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb))) lp->first_skb = NULL; @@ -658,7 +668,6 @@ u_char *p = buf; unsigned short proto = ETH_P_IP; int data_ofs; - int len; ip_ports *ipp; char addinfo[100]; @@ -676,22 +685,6 @@ proto = ntohs(*(unsigned short *)&buf[2]); p = &buf[4]; break; - case ISDN_NET_ENCAP_SYNCPPP: - len = 4; -#ifdef CONFIG_ISDN_MPP - if (lp->ppp_minor!=-1) { - if (ippp_table[lp->ppp_minor]->mpppcfg & - SC_MP_PROT) { - if (ippp_table[lp->ppp_minor]->mpppcfg & - SC_OUT_SHORT_SEQ) - len = 7; - else - len = 9; - } - } -#endif - p = &buf[len]; - break; } data_ofs = ((p[0] & 15) * 4); switch (proto) { @@ -851,7 +844,7 @@ isdn_net_local *lp = (isdn_net_local *) ndev->priv; if (ndev->tbusy) { - if (jiffies - ndev->trans_start < 20) + if (jiffies - ndev->trans_start < (2 * HZ)) return 1; if (!lp->dialstate) lp->stats.tx_errors++; @@ -1248,29 +1241,6 @@ *((ushort*)&skb->data[2]) = htons(type); len = 4; break; -#ifdef CONFIG_ISDN_PPP - case ISDN_NET_ENCAP_SYNCPPP: - /* reserve space to be filled in isdn_ppp_xmit */ - len = 4; -#ifdef CONFIG_ISDN_MPP - if (lp->ppp_minor!=-1) { - if (ippp_table[lp->ppp_minor]->mpppcfg & - SC_MP_PROT) { - if (ippp_table[lp->ppp_minor]->mpppcfg & - SC_OUT_SHORT_SEQ) - len = 7; - else - len = 9; - } - } -#endif - /* Initialize first 4 bytes to a value, which is - * guaranteed to be invalid. Need that to check - * for already compressed packets in isdn_ppp_xmit(). - */ - *((u32 *)skb_push(skb, len)) = 0; - break; -#endif } return len; } @@ -1756,7 +1726,7 @@ dev->st_netdev[idx] = lp->netdev; p->local.isdn_device = di; p->local.isdn_channel = ch; - p->local.ppp_minor = -1; + p->local.ppp_slot = -1; p->local.pppbind = -1; p->local.flags |= ISDN_NET_CONNECTED; p->local.dialstate = 7; @@ -1927,7 +1897,7 @@ netdev->local.pre_device = -1; netdev->local.pre_channel = -1; netdev->local.exclusive = -1; - netdev->local.ppp_minor = -1; + netdev->local.ppp_slot = -1; netdev->local.pppbind = -1; netdev->local.l2_proto = ISDN_PROTO_L2_X75I; netdev->local.l3_proto = ISDN_PROTO_L3_TRANS; @@ -2443,8 +2413,7 @@ for(i=0;ibuffs[i]))) - if(skb->free) - kfree_skb(skb,FREE_WRITE); + dev_kfree_skb(skb,FREE_WRITE); } } diff -ur --new-file old/linux/drivers/isdn/isdn_ppp.c new/linux/drivers/isdn/isdn_ppp.c --- old/linux/drivers/isdn/isdn_ppp.c Wed Jul 17 10:55:48 1996 +++ new/linux/drivers/isdn/isdn_ppp.c Sun Sep 1 08:15:32 1996 @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.13 1996/07/01 19:47:24 hipp Exp $ +/* $Id: isdn_ppp.c,v 1.14 1996/08/12 16:26:47 hipp Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.14 1996/08/12 16:26:47 hipp + * code cleanup + * changed connection management from minors to slots + * * Revision 1.13 1996/07/01 19:47:24 hipp * Fixed memory leak in VJ handling and more VJ changes * @@ -82,21 +86,21 @@ #endif /* Prototypes */ -static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int minor); -static int isdn_ppp_closewait(int); +static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot); +static int isdn_ppp_closewait(int slot); static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto); -static int isdn_ppp_if_get_unit(char **namebuf); +static int isdn_ppp_if_get_unit(char *namebuf); #ifdef CONFIG_ISDN_MPP -static int isdn_ppp_bundle(int, int); +static int isdn_ppp_bundle(struct ippp_struct *, int unit); static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask); static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min); static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb, int BEbyte, int *sqno, int min_sqno); #endif -char *isdn_ppp_revision = "$Revision: 1.13 $"; +char *isdn_ppp_revision = "$Revision: 1.14 $"; struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; extern int isdn_net_force_dial_lp(isdn_net_local *); @@ -109,10 +113,13 @@ { isdn_net_local *master_lp=lp; unsigned long flags; + struct ippp_struct *is; - if (lp->ppp_minor < 0) + if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) return 0; + is = ippp_table[lp->ppp_slot]; + save_flags(flags); cli(); #ifdef CONFIG_ISDN_MPP @@ -132,13 +139,13 @@ lp->next = lp->last = lp; /* (re)set own pointers */ #endif - isdn_ppp_closewait(lp->ppp_minor); /* force wakeup on ippp device */ + isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */ - if(ippp_table[lp->ppp_minor]->debug & 0x1) - printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_minor, (long) lp,(long) ippp_table[lp->ppp_minor]->lp); + if(is->debug & 0x1) + printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp,(long) is->lp); - ippp_table[lp->ppp_minor]->lp = NULL; /* link is down .. set lp to NULL */ - lp->ppp_minor = -1; /* is this OK ?? */ + is->lp = NULL; /* link is down .. set lp to NULL */ + lp->ppp_slot = -1; /* is this OK ?? */ restore_flags(flags); return 0; @@ -151,11 +158,11 @@ { int i; int unit = 0; - char *name; long flags; + struct ippp_struct *is; if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) - return 0; + return -1; save_flags(flags); cli(); @@ -172,19 +179,18 @@ net_dev = net_dev->next; } /* - * search a free device + * search a free device / slot */ for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - if (ippp_table[i]->state == IPPP_OPEN && !exclusive[i]) { /* OPEN, but not connected! */ + if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */ break; } } } else { - if (ippp_table[lp->pppbind]->state == IPPP_OPEN) /* OPEN, but not connected! */ - i = lp->pppbind; - else - i = ISDN_MAX_CHANNELS; /* trigger error */ + for(i=0;iminor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN) + break; } if (i >= ISDN_MAX_CHANNELS) { @@ -192,24 +198,33 @@ printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n"); return -1; } - lp->ppp_minor = i; - ippp_table[lp->ppp_minor]->lp = lp; - name = lp->name; - unit = isdn_ppp_if_get_unit(&name); /* get unit number from interface name .. ugly! */ - ippp_table[lp->ppp_minor]->unit = unit; + unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */ + if(unit < 0) { + printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",lp->name); + return -1; + } - ippp_table[lp->ppp_minor]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; + lp->ppp_slot = i; + is = ippp_table[i]; + is->lp = lp; + is->unit = unit; + is->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; restore_flags(flags); - /* - * kick the ipppd on the new device - */ - if (ippp_table[lp->ppp_minor]->wq) - wake_up_interruptible(&ippp_table[lp->ppp_minor]->wq); + return lp->ppp_slot; +} - return lp->ppp_minor; +/* + * kick the ipppd on the device + * (wakes up daemon after B-channel connect) + */ + +void isdn_ppp_wakeup_daemon(isdn_net_local *lp) +{ + if (ippp_table[lp->ppp_slot]->wq) + wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq); } /* @@ -217,51 +232,76 @@ * force wakeup of the ippp device * go into 'device waits for release' state */ -static int isdn_ppp_closewait(int minor) +static int isdn_ppp_closewait(int slot) { - if (minor < 0 || minor >= ISDN_MAX_CHANNELS) + struct ippp_struct *is; + + if (slot < 0 || slot >= ISDN_MAX_CHANNELS) return 0; + is = ippp_table[slot]; - if (ippp_table[minor]->state && ippp_table[minor]->wq) - wake_up_interruptible(&ippp_table[minor]->wq); + if (is->state && is->wq) + wake_up_interruptible(&is->wq); - ippp_table[minor]->state = IPPP_CLOSEWAIT; + is->state = IPPP_CLOSEWAIT; return 1; } /* + * isdn_ppp_find_slot / isdn_ppp_free_slot + */ + +static int isdn_ppp_get_slot(void) +{ + int i; + for(i=0;istate) + return i; + } + return -1; +} + +/* * isdn_ppp_open */ -int isdn_ppp_open(int minor, struct file *file) +int isdn_ppp_open(int min, struct file *file) { - if(ippp_table[minor]->debug & 0x1) - printk(KERN_DEBUG "ippp, open, minor: %d state: %04x\n", minor,ippp_table[minor]->state); - if (ippp_table[minor]->state) + int slot; + struct ippp_struct *is; + + slot = isdn_ppp_get_slot(); + if(slot < 0) { return -EBUSY; + } + is = file->private_data = ippp_table[slot]; - ippp_table[minor]->lp = 0; - ippp_table[minor]->mp_seqno = 0; /* MP sequence number */ - ippp_table[minor]->pppcfg = 0; /* ppp configuration */ - ippp_table[minor]->mpppcfg = 0; /* mppp configuration */ - ippp_table[minor]->range = 0x1000000; /* MP: 24 bit range */ - ippp_table[minor]->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */ - ippp_table[minor]->unit = -1; /* set, when we have our interface */ - ippp_table[minor]->mru = 1524; /* MRU, default 1524 */ - ippp_table[minor]->maxcid = 16; /* VJ: maxcid */ - ippp_table[minor]->tk = current; - ippp_table[minor]->wq = NULL; /* read() wait queue */ - ippp_table[minor]->wq1 = NULL; /* select() wait queue */ - ippp_table[minor]->first = ippp_table[minor]->rq + NUM_RCV_BUFFS - 1; /* receive queue */ - ippp_table[minor]->last = ippp_table[minor]->rq; + if(is->debug & 0x1) + printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",slot, min,is->state); + + is->lp = 0; + is->mp_seqno = 0; /* MP sequence number */ + is->pppcfg = 0; /* ppp configuration */ + is->mpppcfg = 0; /* mppp configuration */ + is->range = 0x1000000; /* MP: 24 bit range */ + is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */ + is->unit = -1; /* set, when we have our interface */ + is->mru = 1524; /* MRU, default 1524 */ + is->maxcid = 16; /* VJ: maxcid */ + is->tk = current; + is->wq = NULL; /* read() wait queue */ + is->wq1 = NULL; /* select() wait queue */ + is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ + is->last = is->rq; + is->minor = min; #ifdef CONFIG_ISDN_PPP_VJ /* * VJ header compression init */ - ippp_table[minor]->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */ + is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */ #endif - ippp_table[minor]->state = IPPP_OPEN; + is->state = IPPP_OPEN; return 0; } @@ -269,38 +309,40 @@ /* * release ippp device */ -void isdn_ppp_release(int minor, struct file *file) +void isdn_ppp_release(int min, struct file *file) { int i; + struct ippp_struct *is; - if (minor < 0 || minor >= ISDN_MAX_CHANNELS) + if (min < 0 || min >= ISDN_MAX_CHANNELS) return; + is = file->private_data; - if(ippp_table[minor]->debug & 0x1) - printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) ippp_table[minor]->lp); + if(is->debug & 0x1) + printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp); - if (ippp_table[minor]->lp) { /* a lp address says: this link is still up */ + if (is->lp) { /* a lp address says: this link is still up */ isdn_net_dev *p = dev->netdev; - p = ippp_table[minor]->lp->netdev; - ippp_table[minor]->lp->ppp_minor = -1; - isdn_net_hangup(&p->dev); /* lp->ppp_minor==-1 => no calling of isdn_ppp_closewait() */ - ippp_table[minor]->lp = NULL; + p = is->lp->netdev; + is->lp->ppp_slot = -1; + isdn_net_hangup(&p->dev); /* lp->ppp_slot==-1 => no calling of isdn_ppp_closewait() */ + is->lp = NULL; } for (i = 0; i < NUM_RCV_BUFFS; i++) { - if (ippp_table[minor]->rq[i].buf) { - kfree(ippp_table[minor]->rq[i].buf); - ippp_table[minor]->rq[i].buf = NULL; + if (is->rq[i].buf) { + kfree(is->rq[i].buf); + is->rq[i].buf = NULL; } } - ippp_table[minor]->first = ippp_table[minor]->rq + NUM_RCV_BUFFS - 1; /* receive queue */ - ippp_table[minor]->last = ippp_table[minor]->rq; + is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ + is->last = is->rq; #ifdef CONFIG_ISDN_PPP_VJ - slhc_free(ippp_table[minor]->slcomp); - ippp_table[minor]->slcomp = NULL; + slhc_free(is->slcomp); + is->slcomp = NULL; #endif - ippp_table[minor]->state = 0; + is->state = 0; } /* @@ -330,62 +372,59 @@ /* * ippp device ioctl */ -int isdn_ppp_ioctl(int minor, struct file *file, unsigned int cmd, unsigned long arg) +int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) { unsigned long val; int r; + struct ippp_struct *is; + + is = file->private_data; - if(ippp_table[minor]->debug & 0x1) - printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", - minor,cmd,ippp_table[minor]->state); + if(is->debug & 0x1) + printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",min,cmd,is->state); - if (!(ippp_table[minor]->state & IPPP_OPEN)) + if (!(is->state & IPPP_OPEN)) return -EINVAL; switch (cmd) { -#if 0 - case PPPIOCSINPSIG: /* obsolete: set input ready signal */ - /* usual: sig = SIGIO *//* we always deliver a SIGIO */ - break; -#endif case PPPIOCBUNDLE: #ifdef CONFIG_ISDN_MPP if ((r = get_arg((void *) arg, &val))) return r; printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n", - (int) minor, (int) ippp_table[minor]->unit, (int) val); - return isdn_ppp_bundle(minor, val); + (int) min, (int) is->unit, (int) val); + return isdn_ppp_bundle(is, val); #else return -1; #endif break; case PPPIOCGUNIT: /* get ppp/isdn unit number */ - if ((r = set_arg((void *) arg, ippp_table[minor]->unit))) + if ((r = set_arg((void *) arg, is->unit))) return r; break; case PPPIOCGMPFLAGS: /* get configuration flags */ - if ((r = set_arg((void *) arg, ippp_table[minor]->mpppcfg))) + if ((r = set_arg((void *) arg, is->mpppcfg))) return r; break; case PPPIOCSMPFLAGS: /* set configuration flags */ if ((r = get_arg((void *) arg, &val))) return r; - ippp_table[minor]->mpppcfg = val; + is->mpppcfg = val; break; case PPPIOCGFLAGS: /* get configuration flags */ - if ((r = set_arg((void *) arg, ippp_table[minor]->pppcfg))) + if ((r = set_arg((void *) arg, is->pppcfg))) return r; break; case PPPIOCSFLAGS: /* set configuration flags */ if ((r = get_arg((void *) arg, &val))) { return r; } - if (val & SC_ENABLE_IP && !(ippp_table[minor]->pppcfg & SC_ENABLE_IP)) { - isdn_net_local *lp = ippp_table[minor]->lp; + if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP)) { + isdn_net_local *lp = is->lp; lp->netdev->dev.tbusy = 0; mark_bh(NET_BH); /* OK .. we are ready to send buffers */ } - ippp_table[minor]->pppcfg = val; + is->pppcfg = val; break; #if 0 case PPPIOCGSTAT: /* read PPP statistic information */ @@ -396,7 +435,7 @@ case PPPIOCSMRU: /* set receive unit size for PPP */ if ((r = get_arg((void *) arg, &val))) return r; - ippp_table[minor]->mru = val; + is->mru = val; break; case PPPIOCSMPMRU: break; @@ -406,33 +445,33 @@ if ((r = get_arg((void *) arg, &val))) return r; val++; - if(ippp_table[minor]->maxcid != val) { + if(is->maxcid != val) { #ifdef CONFIG_ISDN_PPP_VJ struct slcompress *sltmp; #endif - if(ippp_table[minor]->debug & 0x1) + if(is->debug & 0x1) printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val); - ippp_table[minor]->maxcid = val; + is->maxcid = val; #ifdef CONFIG_ISDN_PPP_VJ sltmp = slhc_init(16,val); if(!sltmp) { printk(KERN_ERR "ippp, can't realloc slhc struct\n"); return -ENOMEM; } - if(ippp_table[minor]->slcomp) - slhc_free(ippp_table[minor]->slcomp); - ippp_table[minor]->slcomp = sltmp; + if(is->slcomp) + slhc_free(is->slcomp); + is->slcomp = sltmp; #endif } break; case PPPIOCGDEBUG: - if ((r = set_arg((void *) arg, ippp_table[minor]->debug))) + if ((r = set_arg((void *) arg, is->debug))) return r; break; case PPPIOCSDEBUG: if ((r = get_arg((void *) arg, &val))) return r; - ippp_table[minor]->debug = val; + is->debug = val; break; default: break; @@ -440,39 +479,42 @@ return 0; } -int isdn_ppp_select(int minor, struct file *file, int type, select_table * st) +int isdn_ppp_select(int min, struct file *file, int type, select_table * st) { struct ippp_buf_queue *bf, *bl; unsigned long flags; + struct ippp_struct *is; - if(ippp_table[minor]->debug & 0x2) - printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",minor,type); + is = file->private_data; - if (!(ippp_table[minor]->state & IPPP_OPEN)) + if(is->debug & 0x2) + printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",min,type); + + if (!(is->state & IPPP_OPEN)) return -EINVAL; switch (type) { case SEL_IN: save_flags(flags); cli(); - bl = ippp_table[minor]->last; - bf = ippp_table[minor]->first; + bl = is->last; + bf = is->first; /* * if IPPP_NOBLOCK is set we return even if we have nothing to read */ - if (bf->next == bl && !(ippp_table[minor]->state & IPPP_NOBLOCK)) { - select_wait(&ippp_table[minor]->wq, st); + if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) { + select_wait(&is->wq, st); restore_flags(flags); return 0; } - ippp_table[minor]->state &= ~IPPP_NOBLOCK; + is->state &= ~IPPP_NOBLOCK; restore_flags(flags); return 1; case SEL_OUT: /* we're always ready to send .. */ return 1; case SEL_EX: - select_wait(&ippp_table[minor]->wq1, st); + select_wait(&is->wq1, st); return 0; } return 1; @@ -482,17 +524,20 @@ * fill up isdn_ppp_read() queue .. */ -static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int minor) +static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot) { struct ippp_buf_queue *bf, *bl; unsigned long flags; unsigned char *nbuf; + struct ippp_struct *is; - if (minor < 0 || minor >= ISDN_MAX_CHANNELS) { - printk(KERN_WARNING "ippp: illegal minor.\n"); + if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { + printk(KERN_WARNING "ippp: illegal slot.\n"); return 0; } - if (!(ippp_table[minor]->state & IPPP_CONNECT)) { + is = ippp_table[slot]; + + if (!(is->state & IPPP_CONNECT)) { printk(KERN_DEBUG "ippp: device not activated.\n"); return 0; } @@ -511,23 +556,23 @@ save_flags(flags); cli(); - bf = ippp_table[minor]->first; - bl = ippp_table[minor]->last; + bf = is->first; + bl = is->last; if (bf == bl) { printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n"); bf = bf->next; kfree(bf->buf); - ippp_table[minor]->first = bf; + is->first = bf; } bl->buf = (char *) nbuf; bl->len = len+4; - ippp_table[minor]->last = bl->next; + is->last = bl->next; restore_flags(flags); - if (ippp_table[minor]->wq) - wake_up_interruptible(&ippp_table[minor]->wq); + if (is->wq) + wake_up_interruptible(&is->wq); return len; } @@ -537,14 +582,16 @@ * reports, that there is data */ -int isdn_ppp_read(int minor, struct file *file, char *buf, int count) +int isdn_ppp_read(int min, struct file *file, char *buf, int count) { - struct ippp_struct *c = ippp_table[minor]; + struct ippp_struct *is; struct ippp_buf_queue *b; int r; unsigned long flags; - if (!(ippp_table[minor]->state & IPPP_OPEN)) + is = file->private_data; + + if (!(is->state & IPPP_OPEN)) return 0; if ((r = verify_area(VERIFY_WRITE, (void *) buf, count))) @@ -553,7 +600,7 @@ save_flags(flags); cli(); - b = c->first->next; + b = is->first->next; if (!b->buf) { restore_flags(flags); return -EAGAIN; @@ -563,7 +610,7 @@ memcpy_tofs(buf, b->buf, count); kfree(b->buf); b->buf = NULL; - c->first = b; + is->first = b; restore_flags(flags); return count; @@ -573,14 +620,17 @@ * ipppd wanna write a packet to the card .. non-blocking */ -int isdn_ppp_write(int minor, struct file *file, const char *buf, int count) +int isdn_ppp_write(int min, struct file *file, const char *buf, int count) { isdn_net_local *lp; + struct ippp_struct *is; + + is = file->private_data; - if (!(ippp_table[minor]->state & IPPP_CONNECT)) + if (!(is->state & IPPP_CONNECT)) return 0; - lp = ippp_table[minor]->lp; + lp = is->lp; /* -> push it directly to the lowlevel interface */ @@ -659,7 +709,10 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb) { - if(ippp_table[lp->ppp_minor]->debug & 0x4) + struct ippp_struct *is; + is = ippp_table[lp->ppp_slot]; + + if(is->debug & 0x4) printk(KERN_DEBUG "recv skb, len: %ld\n",skb->len); if(net_dev->local.master) { @@ -669,14 +722,14 @@ if(skb->data[0] == 0xff && skb->data[1] == 0x03) skb_pull(skb,2); - else if (ippp_table[lp->ppp_minor]->pppcfg & SC_REJ_COMP_AC) { + else if (is->pppcfg & SC_REJ_COMP_AC) { skb->free = 1; dev_kfree_skb(skb,0 /* FREE_READ */ ); return; /* discard it silently */ } #ifdef CONFIG_ISDN_MPP - if (!(ippp_table[lp->ppp_minor]->mpppcfg & SC_REJ_MP_PROT)) { + if (!(is->mpppcfg & SC_REJ_MP_PROT)) { int proto; int sqno_end; if (skb->data[0] & 0x1) { @@ -690,11 +743,11 @@ isdn_net_local *lpq; int sqno, min_sqno, tseq; u_char BEbyte = skb->data[0]; - if(ippp_table[lp->ppp_minor]->debug & 0x8) - printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_minor, proto , + if(is->debug & 0x8) + printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto , (int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]); - if (!(ippp_table[lp->ppp_minor]->mpppcfg & SC_IN_SHORT_SEQ)) { + if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) { sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3]; skb_pull(skb,4); } else { @@ -702,26 +755,26 @@ skb_pull(skb,2); } - if ((tseq = ippp_table[lp->ppp_minor]->last_link_seqno) >= sqno) { - int range = ippp_table[lp->ppp_minor]->range; + if ((tseq = is->last_link_seqno) >= sqno) { + int range = is->range; if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */ printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %d, last: %d !!!\n", sqno, tseq); else { sqno += range; - ippp_table[lp->ppp_minor]->last_link_seqno = sqno; + is->last_link_seqno = sqno; } } else - ippp_table[lp->ppp_minor]->last_link_seqno = sqno; + is->last_link_seqno = sqno; for (min_sqno = 0, lpq = net_dev->queue;;) { - if (ippp_table[lpq->ppp_minor]->last_link_seqno > min_sqno) - min_sqno = ippp_table[lpq->ppp_minor]->last_link_seqno; + if (ippp_table[lpq->ppp_slot]->last_link_seqno > min_sqno) + min_sqno = ippp_table[lpq->ppp_slot]->last_link_seqno; lpq = lpq->next; if (lpq == net_dev->queue) break; } - if (min_sqno >= ippp_table[lpq->ppp_minor]->range) { /* OK, every link overflowed */ - int mask = ippp_table[lpq->ppp_minor]->range - 1; /* range is a power of 2 */ + if (min_sqno >= ippp_table[lpq->ppp_slot]->range) { /* OK, every link overflowed */ + int mask = ippp_table[lpq->ppp_slot]->range - 1; /* range is a power of 2 */ isdn_ppp_cleanup_queue(net_dev, min_sqno); isdn_ppp_mask_queue(net_dev, mask); net_dev->ib.next_num &= mask; @@ -734,14 +787,14 @@ } min_sqno &= mask; for (lpq = net_dev->queue;;) { - ippp_table[lpq->ppp_minor]->last_link_seqno &= mask; + ippp_table[lpq->ppp_slot]->last_link_seqno &= mask; lpq = lpq->next; if (lpq == net_dev->queue) break; } } if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) { - printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_minor); + printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot); if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0) return; /* no packet complete */ } else @@ -820,9 +873,14 @@ } +/* + * push frame to higher layers + * note: net_dev has to be master net_dev + */ static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto) { struct device *dev = &net_dev->dev; + struct ippp_struct *is = ippp_table[lp->ppp_slot]; if (proto < 0) { /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */ if (skb->data[0] & 0x01) { /* is it odd? */ @@ -834,12 +892,12 @@ } } - if(ippp_table[lp->ppp_minor]->debug & 0x10) + if(is->debug & 0x10) printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto); switch (proto) { case PPP_IPX: /* untested */ - if(ippp_table[lp->ppp_minor]->debug & 0x20) + if(is->debug & 0x20) printk(KERN_DEBUG "isdn_ppp: _IPX\n"); skb->dev = dev; skb->mac.raw = skb->data; @@ -847,9 +905,9 @@ break; #ifdef CONFIG_ISDN_PPP_VJ case PPP_VJC_UNCOMP: - if(ippp_table[lp->ppp_minor]->debug & 0x20) + if(is->debug & 0x20) printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n"); - if(slhc_remember(ippp_table[net_dev->local.ppp_minor]->slcomp, skb->data, skb->len) <= 0) { + if(slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) { printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n"); net_dev->local.stats.rx_dropped++; skb->free = 1; @@ -858,14 +916,14 @@ } #endif case PPP_IP: - if(ippp_table[lp->ppp_minor]->debug & 0x20) + if(is->debug & 0x20) printk(KERN_DEBUG "isdn_ppp: IP\n"); skb->dev = dev; skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IP); break; case PPP_VJC_COMP: - if(ippp_table[lp->ppp_minor]->debug & 0x20) + if(is->debug & 0x20) printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n"); #ifdef CONFIG_ISDN_PPP_VJ { @@ -885,7 +943,7 @@ skb_put(skb,skb_old->len + 40); memcpy(skb->data, skb_old->data, skb_old->len); skb->mac.raw = skb->data; - pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_minor]->slcomp, + pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb_old->len); dev_kfree_skb(skb_old,0 /* FREE_READ */ ); if(pkt_len < 0) { @@ -906,7 +964,7 @@ #endif break; default: - isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_minor); /* push data to pppd device */ + isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_slot); /* push data to pppd device */ skb->free = 1; dev_kfree_skb(skb,0 /* FREE_READ */ ); return; @@ -928,6 +986,19 @@ * skb isn't allowed!! */ +static void isdn_ppp_skb_destructor(struct sk_buff *skb) +{ + char outstr[80],*outpnt=outstr; + int i; + + *outpnt = 0; + for(i=0;i<24 && ilen;i++) { + sprintf(outpnt,"%02x ",skb->data[i]); + outpnt += 3; + } + printk(KERN_DEBUG "ippp_dstrct: %s\n",outstr); +} + int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) { struct device *mdev = ((isdn_net_local *) (dev->priv) )->master; /* get master (for redundancy) */ @@ -941,13 +1012,18 @@ else mlp = (isdn_net_local *) (dev->priv); nd = mlp->netdev; /* get master lp */ - ipts = ippp_table[mlp->ppp_minor]; + ipts = ippp_table[mlp->ppp_slot]; if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */ - printk(KERN_INFO "%s: IP frame delayed.\n",dev->name); - return 1; + if(ipts->debug & 0x1) { + printk(KERN_INFO "%s: IP frame delayed.\n",dev->name); + skb->destructor = isdn_ppp_skb_destructor; + } + return 1; } + skb->destructor = NULL; + lp = nd->queue; /* get lp on top of queue */ if(lp->sav_skb) { /* find a non-busy device */ isdn_net_local *nlp = lp->next; @@ -958,35 +1034,21 @@ } lp = nlp; } - ipt = ippp_table[lp->ppp_minor]; + ipt = ippp_table[lp->ppp_slot]; lp->huptimer = 0; - - /* If packet is to be resent, it has already been processed and - * therefore its first bytes are already initialized. In this case - * send it immediately ... - */ - if (*((u32 *)skb->data) != 0) { - printk(KERN_ERR "%s: Whoops .. packet resend should no longer happen!\n",dev->name); - return (isdn_net_send_skb(dev , lp , skb)); - } - /* ... else packet needs processing. */ + /* + * after this line .. requeueing in the device queue is no longer allowed!!! + */ - if(ippp_table[lp->ppp_minor]->debug & 0x4) + if(ipt->debug & 0x4) printk(KERN_DEBUG "xmit skb, len %ld\n",skb->len); #ifdef CONFIG_ISDN_PPP_VJ - if (ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes .. but check again */ + if (ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes .. but this check again */ struct sk_buff *new_skb; - int len = 4; -#ifdef CONFIG_ISDN_MPP - if (ipt->mpppcfg & SC_MP_PROT) /* sigh */ /* ipt or ipts ?? */ - if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) - len += 3; - else - len += 5; -#endif + new_skb = dev_alloc_skb(skb->len); if(new_skb) { u_char *buf; @@ -995,8 +1057,6 @@ new_skb->dev = skb->dev; new_skb->free = 1; skb_put(new_skb,skb->len); - skb_pull(skb,len); /* pull PPP header */ - skb_pull(new_skb,len); /* pull PPP header */ buf = skb->data; pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data, @@ -1021,12 +1081,11 @@ proto = PPP_VJC_UNCOMP; skb->data[0] = (skb->data[0] & 0x0f) | 0x40; } - skb_push(skb,len); } } #endif - if(ippp_table[lp->ppp_minor]->debug & 0x24) + if(ipt->debug & 0x24) printk(KERN_DEBUG "xmit2 skb, len %ld, proto %04x\n",skb->len,proto); #ifdef CONFIG_ISDN_MPP @@ -1036,22 +1095,23 @@ ipts->mp_seqno++; nd->queue = nd->queue->next; if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) { - /* skb_push(skb, 3); Done in isdn_net_header() */ + skb_push(skb, 3); mp_seqno &= 0xfff; - skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8); /* (B)egin & (E)ndbit .. */ - skb->data[5] = mp_seqno & 0xff; - skb->data[6] = proto; /* PID compression */ + skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8); /* (B)egin & (E)ndbit .. */ + skb->data[1] = mp_seqno & 0xff; + skb->data[2] = proto; /* PID compression */ } else { - /* skb_push(skb, 5); Done in isdn_net_header () */ - skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ - skb->data[5] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ - skb->data[6] = (mp_seqno >> 8) & 0xff; - skb->data[7] = (mp_seqno >> 0) & 0xff; - skb->data[8] = proto; /* PID compression */ + skb_push(skb, 5); + skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ + skb->data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ + skb->data[2] = (mp_seqno >> 8) & 0xff; + skb->data[3] = (mp_seqno >> 0) & 0xff; + skb->data[4] = proto; /* PID compression */ } proto = PPP_MP; /* MP Protocol, 0x003d */ } #endif + skb_push(skb,4); skb->data[0] = 0xff; /* All Stations */ skb->data[1] = 0x03; /* Unnumbered information */ skb->data[2] = proto >> 8; @@ -1100,7 +1160,7 @@ #ifdef CONFIG_ISDN_MPP -static int isdn_ppp_bundle(int minor, int unit) +static int isdn_ppp_bundle(struct ippp_struct *is, int unit) { char ifn[IFNAMSIZ + 1]; long flags; @@ -1117,7 +1177,7 @@ save_flags(flags); cli(); - nlp = ippp_table[minor]->lp; + nlp = is->lp; lp = p->queue; p->ib.bundled = 1; @@ -1127,17 +1187,17 @@ nlp->next = lp; p->queue = nlp; - ippp_table[nlp->ppp_minor]->unit = ippp_table[lp->ppp_minor]->unit; + ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit; /* maybe also SC_CCP stuff */ - ippp_table[nlp->ppp_minor]->pppcfg |= ippp_table[lp->ppp_minor]->pppcfg & + ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg & (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP); - ippp_table[nlp->ppp_minor]->mpppcfg |= ippp_table[lp->ppp_minor]->mpppcfg & + ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg & (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ); #if 0 - if (ippp_table[nlp->ppp_minor]->mpppcfg != ippp_table[lp->ppp_minor]->mpppcfg) { + if (ippp_table[nlp->ppp_slot]->mpppcfg != ippp_table[lp->ppp_slot]->mpppcfg) { printk(KERN_WARNING "isdn_ppp_bundle: different MP options %04x and %04x\n", - ippp_table[nlp->ppp_minor]->mpppcfg, ippp_table[lp->ppp_minor]->mpppcfg); + ippp_table[nlp->ppp_slot]->mpppcfg, ippp_table[lp->ppp_slot]->mpppcfg); } #endif @@ -1325,7 +1385,7 @@ #ifdef CONFIG_ISDN_PPP_VJ /* did we free a stale frame ? */ if(toss) - slhc_toss(ippp_table[dev->local.ppp_minor]->slcomp); + slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp); #endif } @@ -1355,7 +1415,7 @@ #ifdef CONFIG_ISDN_PPP_VJ /* did we step over a missing frame ? */ if(q->sqno_start != net_dev->ib.next_num) - slhc_toss(ippp_table[lp->ppp_minor]->slcomp); + slhc_toss(ippp_table[lp->ppp_slot]->slcomp); #endif ql = net_dev->ib.sq; @@ -1381,7 +1441,7 @@ * network device ioctl handlers */ -static int isdn_ppp_dev_ioctl_stats(int minor,struct ifreq *ifr,struct device *dev) +static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *dev) { struct ppp_stats *res, t; isdn_net_local *lp = (isdn_net_local *) dev->priv; @@ -1402,8 +1462,8 @@ t.p.ppp_opackets = lp->stats.tx_packets; t.p.ppp_oerrors = lp->stats.tx_errors; #ifdef CONFIG_ISDN_PPP_VJ - if(minor >= 0 && ippp_table[minor]->slcomp) { - struct slcompress *slcomp = ippp_table[minor]->slcomp; + if(slot >= 0 && ippp_table[slot]->slcomp) { + struct slcompress *slcomp = ippp_table[slot]->slcomp; t.vj.vjs_packets = slcomp->sls_o_compressed+slcomp->sls_o_uncompressed; t.vj.vjs_compressed = slcomp->sls_o_compressed; t.vj.vjs_searches = slcomp->sls_o_searches; @@ -1428,7 +1488,7 @@ isdn_net_local *lp = (isdn_net_local *) dev->priv; #if 0 - printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_minor); + printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_slot); #endif if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) @@ -1443,7 +1503,7 @@ memcpy_tofs(r, PPP_VERSION, len); break; case SIOCGPPPSTATS: - error = isdn_ppp_dev_ioctl_stats (lp->ppp_minor, ifr, dev); + error = isdn_ppp_dev_ioctl_stats (lp->ppp_slot, ifr, dev); break; default: error = -EINVAL; @@ -1452,24 +1512,26 @@ return error; } -static int isdn_ppp_if_get_unit(char **namebuf) +static int isdn_ppp_if_get_unit(char *name) { - char *name = *namebuf; int len, i, unit = 0, deci; len = strlen(name); + + if(strncmp("ippp",name,4) || len > 8) + return -1; + for (i = 0, deci = 1; i < len; i++, deci *= 10) { - if (name[len - 1 - i] >= '0' && name[len - 1 - i] <= '9') - unit += (name[len - 1 - i] - '0') * deci; + char a = name[len-i-1]; + if (a >= '0' && a <= '9') + unit += (a - '0') * deci; else break; } - if (!i) + if (!i || len-i != 4) unit = -1; - *namebuf = name + len - 1 - i; return unit; - } diff -ur --new-file old/linux/drivers/isdn/isdn_ppp.h new/linux/drivers/isdn/isdn_ppp.h --- old/linux/drivers/isdn/isdn_ppp.h Sun May 19 14:29:29 1996 +++ new/linux/drivers/isdn/isdn_ppp.h Sun Sep 1 08:15:32 1996 @@ -51,5 +51,6 @@ extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long); extern void isdn_ppp_release(int, struct file *); extern int isdn_ppp_dial_slave(char *); +extern void isdn_ppp_wakeup_daemon(isdn_net_local *); extern struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; diff -ur --new-file old/linux/drivers/isdn/pcbit/layer2.h new/linux/drivers/isdn/pcbit/layer2.h --- old/linux/drivers/isdn/pcbit/layer2.h Tue Apr 23 11:31:35 1996 +++ new/linux/drivers/isdn/pcbit/layer2.h Sun Sep 8 18:50:20 1996 @@ -14,7 +14,7 @@ #ifndef LAYER2_H #define LAYER2_H -#include +#include #define BANK1 0x0000U /* PC -> Board */ #define BANK2 0x01ffU /* Board -> PC */ @@ -85,7 +85,7 @@ */ struct msg_fmt { -#if __BYTE_ORDER == 1234 /* Little Endian */ +#ifdef __LITTLE_ENDIAN /* Little Endian */ u_char scmd; u_char cmd; u_char proc; diff -ur --new-file old/linux/drivers/isdn/teles/card.c new/linux/drivers/isdn/teles/card.c --- old/linux/drivers/isdn/teles/card.c Sat Jun 29 19:36:23 1996 +++ new/linux/drivers/isdn/teles/card.c Sun Sep 1 08:15:32 1996 @@ -1,4 +1,4 @@ -/* $Id: card.c,v 1.12 1996/06/24 17:16:52 fritz Exp $ +/* $Id: card.c,v 1.13 1996/07/18 11:21:24 jdenoud Exp $ * * card.c low level stuff for the Teles S0 isdn card * @@ -7,6 +7,9 @@ * Beat Doebeli log all D channel traffic * * $Log: card.c,v $ + * Revision 1.13 1996/07/18 11:21:24 jdenoud + * Use small buffers for incoming audio data + * * Revision 1.12 1996/06/24 17:16:52 fritz * Added check for misconfigured membase. * @@ -424,7 +427,7 @@ { byte r; struct HscxState *hsp = sp->hs + hscx; - int count; + int count, err; if (!hsp->init) return; @@ -471,9 +474,15 @@ } afterRME: if (val & 0x40) { /* RPF */ - if (!hsp->rcvibh) - if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool, - GFP_ATOMIC, (void *) 1, 2)) { + if (!hsp->rcvibh) { + if (hsp->mode == 1) + err=BufPoolGet(&hsp->rcvibh, &hsp->smallpool, + GFP_ATOMIC, (void *)1, 2); + else + err=BufPoolGet(&hsp->rcvibh, &hsp->rbufpool, + GFP_ATOMIC, (void *)1, 2); + + if (err) { printk(KERN_WARNING "HSCX RPF out of buffers at %ld\n", jiffies); @@ -482,7 +491,8 @@ goto afterRPF; } else hsp->rcvptr = 0; - + } + hscx_empty_fifo(hsp, 32); if (hsp->mode == 1) { /* receive audio data */ diff -ur --new-file old/linux/drivers/net/3c501.c new/linux/drivers/net/3c501.c --- old/linux/drivers/net/3c501.c Mon May 6 11:26:07 1996 +++ new/linux/drivers/net/3c501.c Wed Nov 6 13:39:42 1996 @@ -237,8 +237,6 @@ static int el1_probe1(struct device *dev, int ioaddr) { -#ifndef MODULE - const char *mname; /* Vendor name */ unsigned char station_addr[6]; int autoirq = 0; @@ -343,8 +341,6 @@ */ ether_setup(dev); - -#endif /* !MODULE */ return 0; } diff -ur --new-file old/linux/drivers/net/3c509.c new/linux/drivers/net/3c509.c --- old/linux/drivers/net/3c509.c Sun Aug 18 10:22:50 1996 +++ new/linux/drivers/net/3c509.c Wed Oct 30 02:42:40 1996 @@ -27,6 +27,10 @@ FIXES: Alan Cox: Removed the 'Unexpected interrupt' bug. Michael Meskes: Upgraded to Donald Becker's version 1.07. + Alan Cox: Increased the eeprom delay. Regardless of + what the docs say some people definitely + get problems with lower (but in card spec) + delays */ static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n"; @@ -312,7 +316,7 @@ { outw(EEPROM_READ + index, ioaddr + 10); /* Pause for at least 162 us. for the read to take place. */ - udelay (200); + udelay (500); return inw(ioaddr + 12); } @@ -326,7 +330,7 @@ outb(EEPROM_READ + index, id_port); /* Pause for at least 162 us. for the read to take place. */ - udelay (200); + udelay (500); for (bit = 15; bit >= 0; bit--) word = (word << 1) + (inb(id_port) & 0x01); diff -ur --new-file old/linux/drivers/net/3c59x.c new/linux/drivers/net/3c59x.c --- old/linux/drivers/net/3c59x.c Thu Jul 18 09:08:45 1996 +++ new/linux/drivers/net/3c59x.c Thu Nov 7 10:25:21 1996 @@ -315,7 +315,7 @@ root_vortex_dev = NULL; cards_found = vortex_scan(0); - return cards_found < 0 ? cards_found : 0; + return cards_found ? 0 : -ENODEV; } #else @@ -634,7 +634,7 @@ /* Switch to register set 7 for normal use. */ EL3WINDOW(7); - /* Set reciever mode: presumably accept b-case and phys addr only. */ + /* Set receiver mode: presumably accept b-case and phys addr only. */ set_rx_mode(dev); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ diff -ur --new-file old/linux/drivers/net/CONFIG new/linux/drivers/net/CONFIG --- old/linux/drivers/net/CONFIG Thu May 16 19:12:56 1996 +++ new/linux/drivers/net/CONFIG Wed Oct 30 02:42:40 1996 @@ -62,6 +62,10 @@ # DE4X5_DO_MEMCPY Forces the Intels to use memory copies into sk_buffs # rather than straight DMA. # +# DEFXX The DIGITAL series of FDDI EISA (DEFEA) and PCI (DEFPA) +# controllers +# DEFXX_DEBUG Set the desired debug level +# # TULIP Tulip (dc21040/dc21041/ds21140) driver # TULIP_PORT specify default if_port # 0: 10TP @@ -92,5 +96,6 @@ DEPCA_OPTS = EWRK3_OPTS = DE4X5_OPTS = -DDE4X5_AUTOSENSE=AUTO +DEFXX_OPTS = ELP_OPTS = TULIP_OPTS = diff -ur --new-file old/linux/drivers/net/Config.in new/linux/drivers/net/Config.in --- old/linux/drivers/net/Config.in Mon May 20 07:08:36 1996 +++ new/linux/drivers/net/Config.in Wed Oct 30 02:42:40 1996 @@ -84,7 +84,7 @@ tristate 'NE2000/NE1000 support' CONFIG_NE2000 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'NI5210 support' CONFIG_NI52 - bool 'NI6510 support' CONFIG_NI65 + tristate 'NI6510 support' CONFIG_NI65 fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'SEEQ8005 support (EXPERIMENTAL)' CONFIG_SEEQ8005 @@ -115,6 +115,10 @@ bool 'Token Ring driver support' CONFIG_TR if [ "$CONFIG_TR" = "y" ]; then tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR +fi +bool 'FDDI driver support' CONFIG_FDDI +if [ "$CONFIG_FDDI" = "y" ]; then + bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX fi tristate 'ARCnet support' CONFIG_ARCNET if [ "$CONFIG_ARCNET" != "n" ]; then diff -ur --new-file old/linux/drivers/net/Makefile new/linux/drivers/net/Makefile --- old/linux/drivers/net/Makefile Mon May 6 11:44:31 1996 +++ new/linux/drivers/net/Makefile Wed Oct 30 02:42:40 1996 @@ -350,6 +350,10 @@ endif endif +ifeq ($(CONFIG_DEFXX),y) +L_OBJS += defxx.o +endif + ifeq ($(CONFIG_NI52),y) L_OBJS += ni52.o else @@ -360,6 +364,10 @@ ifeq ($(CONFIG_NI65),y) L_OBJS += ni65.o +else + ifeq ($(CONFIG_NI65),m) + M_OBJS += ni65.o + endif endif ifeq ($(CONFIG_ELPLUS),y) @@ -525,6 +533,9 @@ de4x5.o: de4x5.c CONFIG $(CC) $(CPPFLAGS) $(CFLAGS) $(DE4X5_OPTS) -c $< + +defxx.o: defxx.c CONFIG + $(CC) $(CPPFLAGS) $(CFLAGS) $(DEFXX_OPTS) -c $< ewrk3.o: ewrk3.c CONFIG $(CC) $(CPPFLAGS) $(CFLAGS) $(EWRK3_OPTS) -c $< diff -ur --new-file old/linux/drivers/net/Space.c new/linux/drivers/net/Space.c --- old/linux/drivers/net/Space.c Mon Jun 3 11:42:41 1996 +++ new/linux/drivers/net/Space.c Fri Nov 1 22:07:23 1996 @@ -13,7 +13,7 @@ * field of the 'device' structure to store the unit number... * -FvK * - * Version: @(#)Space.c 1.0.7 08/12/93 + * Version: @(#)Space.c 1.0.8 07/31/96 * * Authors: Ross Biro, * Fred N. van Kempen, @@ -195,6 +195,9 @@ #ifdef CONFIG_NI52 && ni52_probe(dev) #endif +#ifdef CONFIG_NI65 + && ni65_probe(dev) +#endif #ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */ && atarilance_probe(dev) #endif @@ -381,6 +384,30 @@ # define NEXT_DEV (&ibmtr_dev0) #endif + +#ifdef CONFIG_DEFXX + extern int dfx_probe(struct device *dev); + static struct device fddi7_dev = + {"fddi7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, dfx_probe}; + static struct device fddi6_dev = + {"fddi6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi7_dev, dfx_probe}; + static struct device fddi5_dev = + {"fddi5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi6_dev, dfx_probe}; + static struct device fddi4_dev = + {"fddi4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi5_dev, dfx_probe}; + static struct device fddi3_dev = + {"fddi3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi4_dev, dfx_probe}; + static struct device fddi2_dev = + {"fddi2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi3_dev, dfx_probe}; + static struct device fddi1_dev = + {"fddi1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi2_dev, dfx_probe}; + static struct device fddi0_dev = + {"fddi0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi1_dev, dfx_probe}; + +#undef NEXT_DEV +#define NEXT_DEV (&fddi0_dev) +#endif + #ifdef CONFIG_NET_IPIP #ifdef CONFIG_IP_FORWARD extern int tunnel_init(struct device *); diff -ur --new-file old/linux/drivers/net/ac3200.c new/linux/drivers/net/ac3200.c --- old/linux/drivers/net/ac3200.c Fri Mar 1 06:50:42 1996 +++ new/linux/drivers/net/ac3200.c Sun Sep 8 18:50:21 1996 @@ -137,7 +137,7 @@ for (i = 0; i < 4; i++) if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) { printk("EISA ID mismatch, %8x vs %8x.\n", - inl(ioaddr + AC_EISA_ID), AC_EISA_ID); + inl(ioaddr + AC_ID_PORT), AC_EISA_ID); return ENODEV; } diff -ur --new-file old/linux/drivers/net/arcnet.c new/linux/drivers/net/arcnet.c --- old/linux/drivers/net/arcnet.c Tue Aug 6 11:58:35 1996 +++ new/linux/drivers/net/arcnet.c Sat Oct 19 08:36:20 1996 @@ -17,6 +17,14 @@ ********************** + v2.56 (96/10/18) + - Turned arc0e/arc0s startup messages back on by default, as most + people will probably not notice the additional devices + otherwise, and experience more protocol confusion than + necessary. + - Fixed a tiny but noticeable bug in the packet debugging routines + (thanks Tomasz) + v2.55 (96/08/05) - A couple more messages moved to D_EXTRA. - SLOW_XMIT_COPY off by default. @@ -202,7 +210,7 @@ */ static const char *version = - "arcnet.c: v2.55 96/08/05 Avery Pennarun \n"; + "arcnet.c: v2.56 96/10/18 Avery Pennarun \n"; @@ -708,8 +716,7 @@ { if (i%16==0) printk("\n" KERN_DEBUG "[%04X] ",i); - else - printk("%02X ",((u_char *)skb->data)[i]); + printk("%02X ",((u_char *)skb->data)[i]); } printk("\n"); restore_flags(flags); @@ -731,8 +738,7 @@ { if (i%16==0) printk("\n" KERN_DEBUG "[%04X] ",i); - else - printk("%02X ",buffer[i]); + printk("%02X ",buffer[i]); } printk("\n"); restore_flags(flags); @@ -754,6 +760,8 @@ * NOTE: the list of possible ports/shmems is static, so it is retained * across calls to arcnet_probe. So, if more than one ARCnet probe is made, * values that were discarded once will not even be tried again. + * + * FIXME: grab all devices in one shot and eliminate the big static array. */ int arcnet_probe(struct device *dev) { @@ -775,9 +783,8 @@ ports[(count-0x200)/16] = count; for (count=0xA0000; count<=0xFF800; count+=2048) shmems[(count-0xA0000)/2048] = count; - } - else init_once=1; + } BUGLVL(D_NORMAL) printk(version); @@ -1370,7 +1377,7 @@ /* The RFC1201 driver is the default - just store */ lp->adev=dev; - BUGMSG(D_EXTRA,"ARCnet RFC1201 protocol initialized.\n"); + BUGMSG(D_NORMAL,"ARCnet RFC1201 protocol initialized.\n"); #ifdef CONFIG_ARCNET_ETH /* Initialize the ethernet-encap protocol driver */ @@ -1388,7 +1395,7 @@ lp->edev->init=arcnetE_init; register_netdev(lp->edev); #else - BUGMSG(D_EXTRA,"Ethernet-Encap protocol not available (disabled).\n"); + BUGMSG(D_NORMAL,"Ethernet-Encap protocol not available (disabled).\n"); #endif #ifdef CONFIG_ARCNET_1051 @@ -1400,7 +1407,7 @@ lp->sdev->init=arcnetS_init; register_netdev(lp->sdev); #else - BUGMSG(D_EXTRA,"RFC1051 protocol not available (disabled).\n"); + BUGMSG(D_NORMAL,"RFC1051 protocol not available (disabled).\n"); #endif /* we're started */ @@ -2781,7 +2788,7 @@ dev->stop=arcnetE_open_close; dev->hard_start_xmit=arcnetE_send_packet; - BUGMSG(D_EXTRA,"ARCnet Ethernet-Encap protocol initialized.\n"); + BUGMSG(D_NORMAL,"ARCnet Ethernet-Encap protocol initialized.\n"); return 0; } @@ -2961,7 +2968,7 @@ dev->hard_start_xmit=arcnetS_send_packet; dev->hard_header=arcnetS_header; dev->rebuild_header=arcnetS_rebuild_header; - BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n"); + BUGMSG(D_NORMAL,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n"); return 0; } @@ -3241,10 +3248,10 @@ static int io=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ -static int irqnum=0; /* or use the insmod io= irqnum= shmem= options */ +static int irqnum=0; /* or use the insmod io= irq= shmem= options */ static int irq=0; static int shmem=0; -static char *device = NULL; +static char *device = NULL; /* use eg. device="arc1" to change name */ int init_module(void) @@ -3304,15 +3311,3 @@ } #endif /* MODULE */ - - - -/* - * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c arcnet.c" - * version-control: t - * kept-new-versions: 5 - * tab-width: 8 - * End: - */ - diff -ur --new-file old/linux/drivers/net/de4x5.c new/linux/drivers/net/de4x5.c --- old/linux/drivers/net/de4x5.c Tue Aug 20 07:45:26 1996 +++ new/linux/drivers/net/de4x5.c Wed Sep 11 16:57:14 1996 @@ -210,11 +210,13 @@ 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. Fix EISA probe bugs reported by and + 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media + with a loopback packet. ========================================================================= */ -static const char *version = "de4x5.c:v0.44 96/8/13 davies@wanton.lkg.dec.com\n"; +static const char *version = "de4x5.c:v0.441 96/9/9 davies@wanton.lkg.dec.com\n"; #include @@ -2227,12 +2229,8 @@ if (sts < 0) { next_tick = sts & ~TIMER_CB; } else { - if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { - lp->media = NC; - } else { - lp->local_state++; /* Ensure media connected */ - next_tick = dc21041_autoconf(dev); - } + lp->local_state++; /* Ensure media connected */ + next_tick = dc21041_autoconf(dev); } break; diff -ur --new-file old/linux/drivers/net/defxx.c new/linux/drivers/net/defxx.c --- old/linux/drivers/net/defxx.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/defxx.c Wed Oct 30 02:42:40 1996 @@ -0,0 +1,3421 @@ +/* + * File Name: + * defxx.c + * + * Copyright Information: + * Copyright Digital Equipment Corporation 1996. + * + * This software may be used and distributed according to the terms of + * the GNU Public License, incorporated herein by reference. + * + * Abstract: + * A Linux device driver supporting the Digital Equipment Corporation + * FDDI EISA and PCI controller families. Supported adapters include: + * + * DEC FDDIcontroller/EISA (DEFEA) + * DEC FDDIcontroller/PCI (DEFPA) + * + * Maintainers: + * LVS Lawrence V. Stefani + * + * Contact: + * The author may be reached at: + * + * Inet: stefani@lkg.dec.com + * Mail: Digital Equipment Corporation + * 550 King Street + * M/S: LKG1-3/M07 + * Littleton, MA 01460 + * + * Credits: + * I'd like to thank Patricia Cross for helping me get started with + * Linux, David Davies for a lot of help upgrading and configuring + * my development system and for answering many OS and driver + * development questions, and Alan Cox for recommendations and + * integration help on getting FDDI support into Linux. LVS + * + * Driver Architecture: + * The driver architecture is largely based on previous driver work + * for other operating systems. The upper edge interface and + * functions were largely taken from existing Linux device drivers + * such as David Davies' DE4X5.C driver and Donald Becker's TULIP.C + * driver. + * + * Adapter Probe - + * The driver scans for supported EISA adapters by reading the + * SLOT ID register for each EISA slot and making a match + * against the expected value. The supported PCI adapters are + * discovered using successive calls to pcibios_find_device. + * The first time the probe routine is called, all supported + * devices are discovered and initialized. The adapters aren't + * brought up to an operational state until the open routine is + * called. + * + * Bus-Specific Initialization - + * This driver currently supports both EISA and PCI controller + * families. While the custom DMA chip and FDDI logic is similar + * or identical, the bus logic is very different. After + * initialization, the only bus-specific differences is in how the + * driver enables and disables interrupts. Other than that, the + * run-time critical code behaves the same on both families. + * It's important to note that both adapter families are configured + * to I/O map, rather than memory map, the adapter registers. + * + * Driver Open/Close - + * In the driver open routine, the driver ISR (interrupt service + * routine) is registered and the adapter is brought to an + * operational state. In the driver close routine, the opposite + * occurs; the driver ISR is deregistered and the adapter is + * brought to a safe, but closed state. Users may use consecutive + * commands to bring the adapter up and down as in the following + * example: + * ifconfig fddi0 up + * ifconfig fddi0 down + * ifconfig fddi0 up + * + * Driver Shutdown - + * Apparently, there is no shutdown or halt routine support under + * Linux. This routine would be called during "reboot" or + * "shutdown" to allow the driver to place the adapter in a safe + * state before a warm reboot occurs. To be really safe, the user + * should close the adapter before shutdown (eg. ifconfig fddi0 down) + * to ensure that the adapter DMA engine is taken off-line. However, + * the current driver code anticipates this problem and always issues + * a soft reset of the adapter at the beginning of driver initialization. + * A future driver enhancement in this area may occur in 2.1.X where + * Alan indicated that a shutdown handler may be implemented. + * + * Interrupt Service Routine - + * The driver supports shared interrupts, so the ISR is registered for + * each board with the appropriate flag and the pointer to that board's + * device structure. This provides the context during interrupt + * processing to support shared interrupts and multiple boards. + * + * Interrupt enabling/disabling can occur at many levels. At the host + * end, you can disable system interrupts, or disable interrupts at the + * PIC (on Intel systems). Across the bus, both EISA and PCI adapters + * have a bus-logic chip interrupt enable/disable as well as a DMA + * controller interrupt enable/disable. + * + * The driver currently enables and disables adapter interrupts at the + * bus-logic chip and assumes that Linux will take care of clearing or + * acknowledging any host-based interrupt chips. + * + * Control Functions - + * Control functions are those used to support functions such as adding + * or deleting multicast addresses, enabling or disabling packet + * reception filters, or other custom/proprietary commands. Presently, + * the driver supports the "get statistics", "set multicast list", and + * "set mac address" functions defined by Linux. A list of possible + * enhancements include: + * + * - Custom ioctl interface for executing port interface commands + * - Custom ioctl interface for adding unicast addresses to + * adapter CAM (to support bridge functions). + * - Custom ioctl interface for supporting firmware upgrades. + * + * Hardware (port interface) Support Routines - + * The driver function names that start with "dfx_hw_" represent + * low-level port interface routines that are called frequently. They + * include issuing a DMA or port control command to the adapter, + * resetting the adapter, or reading the adapter state. Since the + * driver initialization and run-time code must make calls into the + * port interface, these routines were written to be as generic and + * usable as possible. + * + * Receive Path - + * The adapter DMA engine supports a 256 entry receive descriptor block + * of which up to 255 entries can be used at any given time. The + * architecture is a standard producer, consumer, completion model in + * which the driver "produces" receive buffers to the adapter, the + * adapter "consumes" the receive buffers by DMAing incoming packet data, + * and the driver "completes" the receive buffers by servicing the + * incoming packet, then "produces" a new buffer and starts the cycle + * again. Receive buffers can be fragmented in up to 16 fragments + * (descriptor entries). For simplicity, this driver posts + * single-fragment receive buffers of 4608 bytes, then allocates a + * sk_buff, copies the data, then reposts the buffer. To reduce CPU + * utilization, a better approach would be to pass up the receive + * buffer (no extra copy) then allocate and post a replacement buffer. + * This is a performance enhancement that should be looked into at + * some point. + * + * Transmit Path - + * Like the receive path, the adapter DMA engine supports a 256 entry + * transmit descriptor block of which up to 255 entries can be used at + * any given time. Transmit buffers can be fragmented in up to 255 + * fragments (descriptor entries). This driver always posts one + * fragment per transmit packet request. + * + * The fragment contains the entire packet from FC to end of data. + * Before posting the buffer to the adapter, the driver sets a three-byte + * packet request header (PRH) which is required by the Motorola MAC chip + * used on the adapters. The PRH tells the MAC the type of token to + * receive/send, whether or not to generate and append the CRC, whether + * synchronous or asynchronous framing is used, etc. Since the PRH + * definition is not necessarily consistent across all FDDI chipsets, + * the driver, rather than the common FDDI packet handler routines, + * sets these bytes. + * + * To reduce the amount of descriptor fetches needed per transmit request, + * the driver takes advantage of the fact that there are at least three + * bytes available before the skb->data field on the outgoing transmit + * request. This is guaranteed by having fddi_setup() in net_init.c set + * dev->hard_header_len to 24 bytes. 21 bytes accounts for the largest + * header in an 802.2 SNAP frame. The other 3 bytes are the extra "pad" + * bytes which we'll use to store the PRH. + * + * There's a subtle advantage to adding these pad bytes to the + * hard_header_len, it ensures that the data portion of the packet for + * an 802.2 SNAP frame is longword aligned. Other FDDI driver + * implementations may not need the extra padding and can start copying + * or DMAing directly from the FC byte which starts at skb->data. Should + * another driver implementation need ADDITIONAL padding, the net_init.c + * module should be updated and dev->hard_header_len should be increased. + * NOTE: To maintain the alignment on the data portion of the packet, + * dev->hard_header_len should always be evenly divisible by 4 and at + * least 24 bytes in size. + * + * Modification History: + * Date Name Description + * 16-Aug-96 LVS Created. + * 20-Aug-96 LVS Updated dfx_probe so that version information + * string is only displayed if 1 or more cards are + * found. Changed dfx_rcv_queue_process to copy + * 3 NULL bytes before FC to ensure that data is + * longword aligned in receive buffer. + * 09-Sep-96 LVS Updated dfx_ctl_set_multicast_list to enable + * LLC group promiscuous mode if multicast list + * is too large. LLC individual/group promiscuous + * mode is now disabled if IFF_PROMISC flag not set. + * dfx_xmt_queue_pkt no longer checks for NULL skb + * on Alan Cox recommendation. Added node address + * override support. + * 12-Sep-96 LVS Reset current address to factory address during + * device open. Updated transmit path to post a + * single fragment which includes PRH->end of data. + */ + +/* Version information string - should be updated prior to each new release!!! */ + +static const char *version = "defxx.c:v1.04 09/16/96 Lawrence V. Stefani (stefani@lkg.dec.com)\n"; + +/* Include files */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "defxx.h" + +/* Define global routines */ + +int dfx_probe(struct device *dev); + +/* Define module-wide (static) routines */ + +static struct device *dfx_alloc_device(struct device *dev, u16 iobase); + +static void dfx_bus_init(struct device *dev); +static void dfx_bus_config_check(DFX_board_t *bp); + +static int dfx_driver_init(struct device *dev); +static int dfx_adap_init(DFX_board_t *bp); + +static int dfx_open(struct device *dev); +static int dfx_close(struct device *dev); + +static void dfx_int_pr_halt_id(DFX_board_t *bp); +static void dfx_int_type_0_process(DFX_board_t *bp); +static void dfx_int_common(DFX_board_t *bp); +static void dfx_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct enet_statistics *dfx_ctl_get_stats(struct device *dev); +static void dfx_ctl_set_multicast_list(struct device *dev); +static int dfx_ctl_set_mac_address(struct device *dev, void *addr); +static int dfx_ctl_update_cam(DFX_board_t *bp); +static int dfx_ctl_update_filters(DFX_board_t *bp); + +static int dfx_hw_dma_cmd_req(DFX_board_t *bp); +static int dfx_hw_port_ctrl_req(DFX_board_t *bp, PI_UINT32 command, PI_UINT32 data_a, PI_UINT32 data_b, PI_UINT32 *host_data); +static void dfx_hw_adap_reset(DFX_board_t *bp, PI_UINT32 type); +static int dfx_hw_adap_state_rd(DFX_board_t *bp); +static int dfx_hw_dma_uninit(DFX_board_t *bp, PI_UINT32 type); + +static void dfx_rcv_init(DFX_board_t *bp); +static void dfx_rcv_queue_process(DFX_board_t *bp); + +static int dfx_xmt_queue_pkt(struct sk_buff *skb, struct device *dev); +static void dfx_xmt_done(DFX_board_t *bp); +static void dfx_xmt_flush(DFX_board_t *bp); + +/* Define module-wide (static) variables */ + +static int num_boards = 0; /* total number of adapters configured */ +static int already_probed = 0; /* have we already entered dfx_probe? */ + + +/* + * ======================= + * = dfx_port_write_byte = + * = dfx_port_read_byte = + * = dfx_port_write_long = + * = dfx_port_read_long = + * ======================= + * + * Overview: + * Routines for reading and writing values from/to adapter + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * offset - register offset from base I/O address + * data - for dfx_port_write_byte and dfx_port_write_long, this + * is a value to write. + * for dfx_port_read_byte and dfx_port_read_byte, this + * is a pointer to store the read value. + * + * Functional Description: + * These routines perform the correct operation to read or write + * the adapter register. + * + * EISA port block base addresses are based on the slot number in which the + * controller is installed. For example, if the EISA controller is installed + * in slot 4, the port block base address is 0x4000. If the controller is + * installed in slot 2, the port block base address is 0x2000, and so on. + * This port block can be used to access PDQ, ESIC, and DEFEA on-board + * registers using the register offsets defined in DEFXX.H. + * + * PCI port block base addresses are assigned by the PCI BIOS or system + * firmware. There is one 128 byte port block which can be accessed. It + * allows for I/O mapping of both PDQ and PFI registers using the register + * offsets defined in DEFXX.H. + * + * Return Codes: + * None + * + * Assumptions: + * bp->base_addr is a valid base I/O address for this adapter. + * offset is a valid register offset for this adapter. + * + * Side Effects: + * Rather than produce macros for these functions, these routines + * are defined using "inline" to ensure that the compiler will + * generate inline code and not waste a procedure call and return. + * This provides all the benefits of macros, but with the + * advantage of strict data type checking. + */ + +static inline void dfx_port_write_byte( + DFX_board_t *bp, + int offset, + u8 data + ) + + { + u16 port = bp->base_addr + offset; + + outb(data, port); + return; + } + +static inline void dfx_port_read_byte( + DFX_board_t *bp, + int offset, + u8 *data + ) + + { + u16 port = bp->base_addr + offset; + + *data = inb(port); + return; + } + +static inline void dfx_port_write_long( + DFX_board_t *bp, + int offset, + u32 data + ) + + { + u16 port = bp->base_addr + offset; + + outl(data, port); + return; + } + +static inline void dfx_port_read_long( + DFX_board_t *bp, + int offset, + u32 *data + ) + + { + u16 port = bp->base_addr + offset; + + *data = inl(port); + return; + } + + +/* + * ============= + * = dfx_probe = + * ============= + * + * Overview: + * Probes for supported FDDI EISA and PCI controllers + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This routine is called by the OS for each FDDI device name (fddi0, + * fddi1,...,fddi6, fddi7) specified in drivers/net/Space.c. Since + * the DEFXX.C driver currently does not support being loaded as a + * module, dfx_probe() will initialize all devices the first time + * it is called. + * + * Let's say that dfx_probe() is getting called to initialize fddi0. + * Furthermore, let's say there are three supported controllers in the + * system. Before dfx_probe() leaves, devices fddi0, fddi1, and fddi2 + * will be initialized and a global flag will be set to indicate that + * dfx_probe() has already been called. + * + * However...the OS doesn't know that we've already initialized + * devices fddi1 and fddi2 so dfx_probe() gets called again and again + * until it reaches the end of the device list for FDDI (presently, + * fddi7). It's important that the driver "pretend" to probe for + * devices fddi1 and fddi2 and return success. Devices fddi3 + * through fddi7 will return failure since they weren't initialized. + * + * This algorithm seems to work for the time being. As other FDDI + * drivers are written for Linux, a more generic approach (perhaps + * similar to the Ethernet card approach) may need to be implemented. + * + * Return Codes: + * 0 - This device (fddi0, fddi1, etc) configured successfully + * -ENODEV - No devices present, or no Digital FDDI EISA or PCI device + * present for this device name + * + * Assumptions: + * For the time being, DEFXX.C is the only FDDI driver under Linux. + * As this assumption changes, this routine will likely be impacted. + * Also, it is assumed that no more than eight (8) FDDI controllers + * will be configured in the system (fddi0 through fddi7). This + * routine will not allocate new device structures. If more than + * eight FDDI controllers need to be configured, drivers/net/Space.c + * should be updated as well as the DFX_MAX_NUM_BOARDS constant in + * DEFXX.H. + * + * Side Effects: + * Device structures for FDDI adapters (fddi0, fddi1, etc) are + * initialized and the board resources are read and stored in + * the device structure. + */ + +int dfx_probe( + struct device *dev + ) + + { + int i; /* used in for loops */ + int version_disp; /* was version info string already displayed? */ + int port_len; /* length of port address range (in bytes) */ + u8 pci_bus; /* PCI bus number (0-255) */ + u8 pci_dev_fun; /* PCI device and function numbers (0-255) */ + u16 port; /* temporary I/O (port) address */ + u16 command; /* PCI Configuration space Command register val */ + u32 slot_id; /* EISA hardware (slot) ID read from adapter */ + DFX_board_t *bp; /* board pointer */ + + DBG_printk("In dfx_probe...\n"); + + /* + * Verify whether we're going through dfx_probe() again + * + * If so, see if we're going through for a subsequent fddi device that + * we've already initialized. If we are, return success (0). If not, + * return failure (-ENODEV). + */ + + version_disp = 0; /* default to version string not displayed */ + if (already_probed) + { + DBG_printk("Already entered dfx_probe\n"); + if (dev != NULL) + if ((strncmp(dev->name, "fddi", 4) == 0) && (dev->base_addr != 0)) + { + DBG_printk("In dfx_probe for fddi adapter (%s) we've already initialized it, so return success\n", dev->name); + return(0); + } + return(-ENODEV); + } + already_probed = 1; /* set global flag */ + + /* Scan for FDDI EISA controllers */ + + for (i=0; i < DFX_MAX_EISA_SLOTS; i++) /* only scan for up to 16 EISA slots */ + { + port = (i << 12) + PI_ESIC_K_SLOT_ID; /* port = I/O address for reading slot ID */ + slot_id = inl(port); /* read EISA HW (slot) ID */ + if ((slot_id & 0xF0FFFFFF) == DEFEA_PRODUCT_ID) + { + if (!version_disp) /* display version info if adapter is found */ + { + version_disp = 1; /* set display flag to TRUE so that */ + printk(version); /* we only display this string ONCE */ + } + + port = (i << 12); /* recalc base addr */ + + /* Verify port address range is not already being used */ + + port_len = PI_ESIC_K_CSR_IO_LEN; + if (check_region(port, port_len) == 0) + { + /* Allocate a new device structure for this adapter */ + + dev = dfx_alloc_device(dev, port); + if (dev != NULL) + { + /* Initialize board structure with bus-specific info */ + + bp = (DFX_board_t *) dev->priv; + bp->dev = dev; + bp->bus_type = DFX_BUS_TYPE_EISA; + if (dfx_driver_init(dev) == DFX_K_SUCCESS) + num_boards++; /* only increment global board count on success */ + else + dev->base_addr = 0; /* clear port address field in device structure on failure */ + } + } + else + printk("I/O range allocated to adapter (0x%X-0x%X) is already being used!\n", port, (port + port_len-1)); + } + } + + /* Scan for FDDI PCI controllers */ + + if (pcibios_present()) /* is PCI BIOS even present? */ + for (i=0; i < DFX_MAX_NUM_BOARDS; i++) /* scan for up to 8 PCI cards */ + if (pcibios_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_FDDI, i, &pci_bus, &pci_dev_fun) == 0) + { + if (!version_disp) /* display version info if adapter is found */ + { + version_disp = 1; /* set display flag to TRUE so that */ + printk(version); /* we only display this string ONCE */ + } + + /* Verify that I/O enable bit is set (PCI slot is enabled) */ + + pcibios_read_config_word(pci_bus, pci_dev_fun, PCI_COMMAND, &command); + if ((command & PCI_COMMAND_IO) == 0) + printk("I/O enable bit not set! Verify that slot is enabled\n"); + else + { + /* Turn off memory mapped space and enable mastering */ + + command |= PCI_COMMAND_MASTER; + command &= ~PCI_COMMAND_MEMORY; + pcibios_write_config_word(pci_bus, pci_dev_fun, PCI_COMMAND, command); + + /* Read I/O base address from PCI Configuration Space */ + + pcibios_read_config_word(pci_bus, pci_dev_fun, PCI_BASE_ADDRESS_1, &port); + port &= PCI_BASE_ADDRESS_IO_MASK; /* clear I/O bit (bit 0) */ + + /* Verify port address range is not already being used */ + + port_len = PFI_K_CSR_IO_LEN; + if (check_region(port, port_len) == 0) + { + /* Allocate a new device structure for this adapter */ + + dev = dfx_alloc_device(dev, port); + if (dev != NULL) + { + /* Initialize board structure with bus-specific info */ + + bp = (DFX_board_t *) dev->priv; + bp->dev = dev; + bp->bus_type = DFX_BUS_TYPE_PCI; + bp->pci_bus = pci_bus; + bp->pci_dev_fun = pci_dev_fun; + if (dfx_driver_init(dev) == DFX_K_SUCCESS) + num_boards++; /* only increment global board count on success */ + else + dev->base_addr = 0; /* clear port address field in device structure on failure */ + } + } + else + printk("I/O range allocated to adapter (0x%X-0x%X) is already being used!\n", port, (port + port_len-1)); + } + } + + /* + * If we're at this point we're going through dfx_probe() for the first + * time. Return success (0) if we've initialized 1 or more boards. + * Otherwise, return failure (-ENODEV). + */ + + if (num_boards > 0) + return(0); + else + return(-ENODEV); + } + + +/* + * ==================== + * = dfx_alloc_device = + * ==================== + * + * Overview: + * Allocate new device structure for adapter + * + * Returns: + * Pointer to device structure for this adapter or NULL if + * none are available or could not allocate memory for + * private board structure. + * + * Arguments: + * dev - pointer to device information for last device + * iobase - base I/O address of new adapter + * + * Functional Description: + * The algorithm for allocating a new device structure is + * fairly simple. Since we're presently the only FDDI driver + * under Linux, we'll find the first device structure with an + * "fddi*" device name that's free. If we run out of devices, + * we'll fail on error. This is simpler than trying to + * allocate the memory for a new device structure, determine + * the next free number (beyond 7) and link it into the chain + * of devices. A user can always modify drivers/net/Space.c + * to add new FDDI device structures if necessary. + * + * Beyond finding a free FDDI device structure, this routine + * initializes most of the fields, resource tags, and dispatch + * pointers in the device structure and calls the common + * fddi_setup() routine to perform the rest of the device + * structure initialization. + * + * Return Codes: + * None + * + * Assumptions: + * If additional FDDI drivers are integrated into Linux, + * we'll likely need to use a different approach to + * allocate a device structure. Perhaps one that is + * similar to what the Ethernet drivers use. + * + * Side Effects: + * None + */ + +struct device *dfx_alloc_device( + struct device *dev, + u16 iobase + ) + + { + struct device *tmp_dev; /* pointer to a device structure */ + + DBG_printk("In dfx_alloc_device...\n"); + + /* Find next free fddi entry */ + + for (tmp_dev = dev; tmp_dev != NULL; tmp_dev = tmp_dev->next) + if ((strncmp(tmp_dev->name, "fddi", 4) == 0) && (tmp_dev->base_addr == 0)) + break; + if (tmp_dev == NULL) + { + printk("Could not find free FDDI device structure for this adapter!\n"); + return(NULL); + } + DBG_printk("Device entry free, device name = %s\n", tmp_dev->name); + + /* Allocate space for private board structure */ + + tmp_dev->priv = (void *) kmalloc(sizeof(DFX_board_t), GFP_KERNEL); + if (tmp_dev->priv == NULL) + { + printk("Could not allocate memory for private board structure!\n"); + return(NULL); + } + memset(tmp_dev->priv, 0, sizeof(DFX_board_t)); /* clear structure */ + + /* Initialize new device structure */ + + tmp_dev->rmem_end = 0; /* shared memory isn't used */ + tmp_dev->rmem_start = 0; /* shared memory isn't used */ + tmp_dev->mem_end = 0; /* shared memory isn't used */ + tmp_dev->mem_start = 0; /* shared memory isn't used */ + tmp_dev->base_addr = iobase; /* save port (I/O) base address */ + tmp_dev->irq = 0; /* set in dfx_bus_init() */ + tmp_dev->if_port = 0; /* not applicable to FDDI adapters */ + tmp_dev->dma = 0; /* Bus Master DMA doesn't require channel */ + + tmp_dev->get_stats = &dfx_ctl_get_stats; + tmp_dev->open = &dfx_open; + tmp_dev->stop = &dfx_close; + tmp_dev->hard_start_xmit = &dfx_xmt_queue_pkt; + tmp_dev->hard_header = NULL; /* set in fddi_setup() */ + tmp_dev->rebuild_header = NULL; /* set in fddi_setup() */ + tmp_dev->set_multicast_list = &dfx_ctl_set_multicast_list; + tmp_dev->set_mac_address = &dfx_ctl_set_mac_address; + tmp_dev->do_ioctl = NULL; /* not supported for now &&& */ + tmp_dev->set_config = NULL; /* not supported for now &&& */ + tmp_dev->header_cache_bind = NULL; /* not supported */ + tmp_dev->header_cache_update = NULL; /* not supported */ + tmp_dev->change_mtu = NULL; /* set in fddi_setup() */ + + /* Initialize remaining device structure information */ + + fddi_setup(tmp_dev); + return(tmp_dev); + } + + +/* + * ================ + * = dfx_bus_init = + * ================ + * + * Overview: + * Initializes EISA and PCI controller bus-specific logic. + * + * Returns: + * None + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * Determine and save adapter IRQ in device table, + * then perform bus-specific logic initialization. + * + * Return Codes: + * None + * + * Assumptions: + * dev->base_addr has already been set with the proper + * base I/O address for this device. + * + * Side Effects: + * Interrupts are enabled at the adapter bus-specific logic. + * Note: Interrupts at the DMA engine (PDQ chip) are not + * enabled yet. + */ + +void dfx_bus_init( + struct device *dev + ) + + { + DFX_board_t *bp = (DFX_board_t *)dev->priv; + u8 val; /* used for I/O read/writes */ + + DBG_printk("In dfx_bus_init...\n"); + + /* + * Initialize base I/O address field in bp structure + * + * Note: bp->base_addr is the same as dev->base_addr. + * It's useful because often we'll need to read + * or write registers where we already have the + * bp pointer instead of the dev pointer. Having + * the base address in the bp structure will + * save a pointer dereference. + * + * IMPORTANT!! This field must be defined before + * any of the dfx_port_* inline functions are + * called. + */ + + bp->base_addr = dev->base_addr; + + /* Initialize adapter based on bus type */ + + if (bp->bus_type == DFX_BUS_TYPE_EISA) + { + /* Get the interrupt level from the ESIC chip */ + + dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &val); + switch ((val & PI_CONFIG_STAT_0_M_IRQ) >> PI_CONFIG_STAT_0_V_IRQ) + { + case PI_CONFIG_STAT_0_IRQ_K_9: + dev->irq = 9; + break; + + case PI_CONFIG_STAT_0_IRQ_K_10: + dev->irq = 10; + break; + + case PI_CONFIG_STAT_0_IRQ_K_11: + dev->irq = 11; + break; + + case PI_CONFIG_STAT_0_IRQ_K_15: + dev->irq = 15; + break; + } + + /* Enable access to I/O on the board by writing 0x03 to Function Control Register */ + + dfx_port_write_byte(bp, PI_ESIC_K_FUNCTION_CNTRL, PI_ESIC_K_FUNCTION_CNTRL_IO_ENB); + + /* Set the I/O decode range of the board */ + + val = ((dev->base_addr >> 12) << PI_IO_CMP_V_SLOT); + dfx_port_write_byte(bp, PI_ESIC_K_IO_CMP_0_1, val); + dfx_port_write_byte(bp, PI_ESIC_K_IO_CMP_1_1, val); + + /* Enable access to rest of module (including PDQ and packet memory) */ + + dfx_port_write_byte(bp, PI_ESIC_K_SLOT_CNTRL, PI_SLOT_CNTRL_M_ENB); + + /* + * Map PDQ registers into I/O space. This is done by clearing a bit + * in Burst Holdoff register. + */ + + dfx_port_read_byte(bp, PI_ESIC_K_BURST_HOLDOFF, &val); + dfx_port_write_byte(bp, PI_ESIC_K_BURST_HOLDOFF, (val & ~PI_BURST_HOLDOFF_M_MEM_MAP)); + + /* Enable interrupts at EISA bus interface chip (ESIC) */ + + dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &val); + dfx_port_write_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, (val | PI_CONFIG_STAT_0_M_INT_ENB)); + } + else + { + /* Get the interrupt level from the PCI Configuration Table */ + + pcibios_read_config_byte(bp->pci_bus, bp->pci_dev_fun, PCI_INTERRUPT_LINE, &val); + dev->irq = val; /* save IRQ value in device table */ + + /* Check Latency Timer and set if less than minimal */ + + pcibios_read_config_byte(bp->pci_bus, bp->pci_dev_fun, PCI_LATENCY_TIMER, &val); + if (val < PFI_K_LAT_TIMER_MIN) /* if less than min, override with default */ + { + val = PFI_K_LAT_TIMER_DEF; + pcibios_write_config_byte(bp->pci_bus, bp->pci_dev_fun, PCI_LATENCY_TIMER, val); + } + + /* Enable interrupts at PCI bus interface chip (PFI) */ + + dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, (PFI_MODE_M_PDQ_INT_ENB | PFI_MODE_M_DMA_ENB)); + } + return; + } + + +/* + * ======================== + * = dfx_bus_config_check = + * ======================== + * + * Overview: + * Checks the configuration (burst size, full-duplex, etc.) If any parameters + * are illegal, then this routine will set new defaults. + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * For Revision 1 FDDI EISA, Revision 2 or later FDDI EISA with rev E or later + * PDQ, and all FDDI PCI controllers, all values are legal. + * + * Return Codes: + * None + * + * Assumptions: + * dfx_adap_init has NOT been called yet so burst size and other items have + * not been set. + * + * Side Effects: + * None + */ + +void dfx_bus_config_check( + DFX_board_t *bp + ) + + { + int status; /* return code from adapter port control call */ + u32 slot_id; /* EISA-bus hardware id (DEC3001, DEC3002,...) */ + u32 host_data; /* LW data returned from port control call */ + + DBG_printk("In dfx_bus_config_check...\n"); + + /* Configuration check only valid for EISA adapter */ + + if (bp->bus_type == DFX_BUS_TYPE_EISA) + { + dfx_port_read_long(bp, PI_ESIC_K_SLOT_ID, &slot_id); + + /* + * First check if revision 2 EISA controller. Rev. 1 cards used + * PDQ revision B, so no workaround needed in this case. Rev. 3 + * cards used PDQ revision E, so no workaround needed in this + * case, either. Only Rev. 2 cards used either Rev. D or E + * chips, so we must verify the chip revision on Rev. 2 cards. + */ + + if (slot_id == DEFEA_PROD_ID_2) + { + /* + * Revision 2 FDDI EISA controller found, so let's check PDQ + * revision of adapter. + */ + + status = dfx_hw_port_ctrl_req(bp, + PI_PCTRL_M_SUB_CMD, + PI_SUB_CMD_K_PDQ_REV_GET, + 0, + &host_data); + if ((status != DFX_K_SUCCESS) || (host_data == 2)) + { + /* + * Either we couldn't determine the PDQ revision, or + * we determined that it is at revision D. In either case, + * we need to implement the workaround. + */ + + /* Ensure that the burst size is set to 8 longwords or less */ + + switch (bp->burst_size) + { + case PI_PDATA_B_DMA_BURST_SIZE_32: + case PI_PDATA_B_DMA_BURST_SIZE_16: + bp->burst_size = PI_PDATA_B_DMA_BURST_SIZE_8; + break; + + default: + break; + } + + /* Ensure that full-duplex mode is not enabled */ + + bp->full_duplex_enb = PI_SNMP_K_FALSE; + } + } + } + return; + } + + +/* + * =================== + * = dfx_driver_init = + * =================== + * + * Overview: + * Initializes remaining adapter board structure information + * and makes sure adapter is in a safe state prior to dfx_open(). + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This function allocates additional resources such as the host memory + * blocks needed by the adapter (eg. descriptor and consumer blocks). + * Remaining bus initialization steps are also completed. The adapter + * is also reset so that it is in the DMA_UNAVAILABLE state. The OS + * must call dfx_open() to open the adapter and bring it on-line. + * + * Return Codes: + * DFX_K_SUCCESS - initialization succeeded + * DFX_K_FAILURE - initialization failed - could not allocate memory + * or read adapter MAC address + * + * Assumptions: + * Memory allocated from kmalloc() call is physically contiguous, locked + * memory whose physical address equals its virtual address. + * + * Side Effects: + * Adapter is reset and should be in DMA_UNAVAILABLE state before + * returning from this routine. + */ + +int dfx_driver_init( + struct device *dev + ) + + { + DFX_board_t *bp = (DFX_board_t *)dev->priv; + int alloc_size; /* total buffer size needed */ + char *top_v, *curr_v; /* virtual addrs into memory block */ + u32 top_p, curr_p; /* physical addrs into memory block */ + u32 data; /* host data register value */ + + DBG_printk("In dfx_driver_init...\n"); + + /* Initialize bus-specific hardware registers */ + + dfx_bus_init(dev); + + /* + * Initialize default values for configurable parameters + * + * Note: All of these parameters are ones that a user may + * want to customize. It'd be nice to break these + * out into Space.c or someplace else that's more + * accessible/understandable than this file. + */ + + bp->full_duplex_enb = PI_SNMP_K_FALSE; + bp->req_ttrt = 8 * 12500; /* 8ms in 80 nanosec units */ + bp->burst_size = PI_PDATA_B_DMA_BURST_SIZE_DEF; + bp->rcv_bufs_to_post = RCV_BUFS_DEF; + + /* + * Ensure that HW configuration is OK + * + * Note: Depending on the hardware revision, we may need to modify + * some of the configurable parameters to workaround hardware + * limitations. We'll perform this configuration check AFTER + * setting the parameters to their default values. + */ + + dfx_bus_config_check(bp); + + /* Disable PDQ interrupts first */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); + + /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */ + + (void) dfx_hw_dma_uninit(bp, PI_PDATA_A_RESET_M_SKIP_ST); + + /* Read the factory MAC address from the adapter then save it */ + + if (dfx_hw_port_ctrl_req(bp, + PI_PCTRL_M_MLA, + PI_PDATA_A_MLA_K_LO, + 0, + &data) != DFX_K_SUCCESS) + { + printk("%s: Could not read adapter factory MAC address!\n", dev->name); + return(DFX_K_FAILURE); + } + memcpy(&bp->factory_mac_addr[0], &data, sizeof(u32)); + + if (dfx_hw_port_ctrl_req(bp, + PI_PCTRL_M_MLA, + PI_PDATA_A_MLA_K_HI, + 0, + &data) != DFX_K_SUCCESS) + { + printk("%s: Could not read adapter factory MAC address!\n", dev->name); + return(DFX_K_FAILURE); + } + memcpy(&bp->factory_mac_addr[4], &data, sizeof(u16)); + + /* + * Set current address to factory address + * + * Note: Node address override support is handled through + * dfx_ctl_set_mac_address. + */ + + memcpy(dev->dev_addr, bp->factory_mac_addr, FDDI_K_ALEN); + if (bp->bus_type == DFX_BUS_TYPE_EISA) + printk("%s: DEFEA at I/O addr = 0x%lX, IRQ = %d, Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", + dev->name, + dev->base_addr, + dev->irq, + dev->dev_addr[0], + dev->dev_addr[1], + dev->dev_addr[2], + dev->dev_addr[3], + dev->dev_addr[4], + dev->dev_addr[5]); + else + printk("%s: DEFPA at I/O addr = 0x%lX, IRQ = %d, Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", + dev->name, + dev->base_addr, + dev->irq, + dev->dev_addr[0], + dev->dev_addr[1], + dev->dev_addr[2], + dev->dev_addr[3], + dev->dev_addr[4], + dev->dev_addr[5]); + + /* + * Get memory for descriptor block, consumer block, and other buffers + * that need to be DMA read or written to by the adapter. + */ + + alloc_size = sizeof(PI_DESCR_BLOCK) + + PI_CMD_REQ_K_SIZE_MAX + + PI_CMD_RSP_K_SIZE_MAX + + (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) + + sizeof(PI_CONSUMER_BLOCK) + + (PI_ALIGN_K_DESC_BLK - 1); + top_v = (char *) kmalloc(alloc_size, GFP_KERNEL); + if (top_v == NULL) + { + printk("%s: Could not allocate memory for host buffers and structures!\n", dev->name); + return(DFX_K_FAILURE); + } + memset(top_v, 0, alloc_size); /* zero out memory before continuing */ + top_p = virt_to_bus(top_v); /* get physical address of buffer */ + + /* + * To guarantee the 8K alignment required for the descriptor block, 8K - 1 + * plus the amount of memory needed was allocated. The physical address + * is now 8K aligned. By carving up the memory in a specific order, + * we'll guarantee the alignment requirements for all other structures. + * + * Note: If the assumptions change regarding the non-paged, non-cached, + * physically contiguous nature of the memory block or the address + * alignments, then we'll need to implement a different algorithm + * for allocating the needed memory. + */ + + curr_p = (u32) (ALIGN(top_p, PI_ALIGN_K_DESC_BLK)); + curr_v = top_v + (curr_p - top_p); + + /* Reserve space for descriptor block */ + + bp->descr_block_virt = (PI_DESCR_BLOCK *) curr_v; + bp->descr_block_phys = curr_p; + curr_v += sizeof(PI_DESCR_BLOCK); + curr_p += sizeof(PI_DESCR_BLOCK); + + /* Reserve space for command request buffer */ + + bp->cmd_req_virt = (PI_DMA_CMD_REQ *) curr_v; + bp->cmd_req_phys = curr_p; + curr_v += PI_CMD_REQ_K_SIZE_MAX; + curr_p += PI_CMD_REQ_K_SIZE_MAX; + + /* Reserve space for command response buffer */ + + bp->cmd_rsp_virt = (PI_DMA_CMD_RSP *) curr_v; + bp->cmd_rsp_phys = curr_p; + curr_v += PI_CMD_RSP_K_SIZE_MAX; + curr_p += PI_CMD_RSP_K_SIZE_MAX; + + /* Reserve space for the LLC host receive queue buffers */ + + bp->rcv_block_virt = curr_v; + bp->rcv_block_phys = curr_p; + curr_v += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX); + curr_p += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX); + + /* Reserve space for the consumer block */ + + bp->cons_block_virt = (PI_CONSUMER_BLOCK *) curr_v; + bp->cons_block_phys = curr_p; + + /* Display virtual and physical addresses if debug driver */ + + DBG_printk("%s: Descriptor block virt = %0lX, phys = %0X\n", dev->name, (long)bp->descr_block_virt, bp->descr_block_phys); + DBG_printk("%s: Command Request buffer virt = %0lX, phys = %0X\n", dev->name, (long)bp->cmd_req_virt, bp->cmd_req_phys); + DBG_printk("%s: Command Response buffer virt = %0lX, phys = %0X\n", dev->name, (long)bp->cmd_rsp_virt, bp->cmd_rsp_phys); + DBG_printk("%s: Receive buffer block virt = %0lX, phys = %0X\n", dev->name, (long)bp->rcv_block_virt, bp->rcv_block_phys); + DBG_printk("%s: Consumer block virt = %0lX, phys = %0X\n", dev->name, (long)bp->cons_block_virt, bp->cons_block_phys); + + return(DFX_K_SUCCESS); + } + + +/* + * ================= + * = dfx_adap_init = + * ================= + * + * Overview: + * Brings the adapter to the link avail/link unavailable state. + * + * Returns: + * Condition code + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * Issues the low-level firmware/hardware calls necessary to bring + * the adapter up, or to properly reset and restore adapter during + * run-time. + * + * Return Codes: + * DFX_K_SUCCESS - Adapter brought up successfully + * DFX_K_FAILURE - Adapter initialization failed + * + * Assumptions: + * bp->reset_type should be set to a valid reset type value before + * calling this routine. + * + * Side Effects: + * Adapter should be in LINK_AVAILABLE or LINK_UNAVAILABLE state + * upon a successful return of this routine. + */ + +int dfx_adap_init( + DFX_board_t *bp + ) + + { + DBG_printk("In dfx_adap_init...\n"); + + /* Disable PDQ interrupts first */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); + + /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */ + + if (dfx_hw_dma_uninit(bp, bp->reset_type) != DFX_K_SUCCESS) + { + printk("%s: Could not uninitialize/reset adapter!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* + * When the PDQ is reset, some false Type 0 interrupts may be pending, + * so we'll acknowledge all Type 0 interrupts now before continuing. + */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, PI_HOST_INT_K_ACK_ALL_TYPE_0); + + /* + * Clear Type 1 and Type 2 registers before going to DMA_AVAILABLE state + * + * Note: We only need to clear host copies of these registers. The PDQ reset + * takes care of the on-board register values. + */ + + bp->cmd_req_reg.lword = 0; + bp->cmd_rsp_reg.lword = 0; + bp->rcv_xmt_reg.lword = 0; + + /* Clear consumer block before going to DMA_AVAILABLE state */ + + memset(bp->cons_block_virt, 0, sizeof(PI_CONSUMER_BLOCK)); + + /* Initialize the DMA Burst Size */ + + if (dfx_hw_port_ctrl_req(bp, + PI_PCTRL_M_SUB_CMD, + PI_SUB_CMD_K_BURST_SIZE_SET, + bp->burst_size, + NULL) != DFX_K_SUCCESS) + { + printk("%s: Could not set adapter burst size!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* + * Set base address of Consumer Block + * + * Assumption: 32-bit physical address of consumer block is 64 byte + * aligned. That is, bits 0-5 of the address must be zero. + */ + + if (dfx_hw_port_ctrl_req(bp, + PI_PCTRL_M_CONS_BLOCK, + bp->cons_block_phys, + 0, + NULL) != DFX_K_SUCCESS) + { + printk("%s: Could not set consumer block address!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* + * Set base address of Descriptor Block and bring adapter to DMA_AVAILABLE state + * + * Note: We also set the literal and data swapping requirements in this + * command. Since this driver presently runs on Intel platforms + * which are Little Endian, we'll tell the adapter to byte swap + * data only. This code will need to change when we support + * Big Endian systems (eg. PowerPC). + * + * Assumption: 32-bit physical address of descriptor block is 8Kbyte + * aligned. That is, bits 0-12 of the address must be zero. + */ + + if (dfx_hw_port_ctrl_req(bp, + PI_PCTRL_M_INIT, + (u32) (bp->descr_block_phys | PI_PDATA_A_INIT_M_BSWAP_DATA), + 0, + NULL) != DFX_K_SUCCESS) + { + printk("%s: Could not set descriptor block address!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* Set transmit flush timeout value */ + + bp->cmd_req_virt->cmd_type = PI_CMD_K_CHARS_SET; + bp->cmd_req_virt->char_set.item[0].item_code = PI_ITEM_K_FLUSH_TIME; + bp->cmd_req_virt->char_set.item[0].value = 3; /* 3 seconds */ + bp->cmd_req_virt->char_set.item[0].item_index = 0; + bp->cmd_req_virt->char_set.item[1].item_code = PI_ITEM_K_EOL; + if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) + { + printk("%s: DMA command request failed!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* Set the initial values for eFDXEnable and MACTReq MIB objects */ + + bp->cmd_req_virt->cmd_type = PI_CMD_K_SNMP_SET; + bp->cmd_req_virt->snmp_set.item[0].item_code = PI_ITEM_K_FDX_ENB_DIS; + bp->cmd_req_virt->snmp_set.item[0].value = bp->full_duplex_enb; + bp->cmd_req_virt->snmp_set.item[0].item_index = 0; + bp->cmd_req_virt->snmp_set.item[1].item_code = PI_ITEM_K_MAC_T_REQ; + bp->cmd_req_virt->snmp_set.item[1].value = bp->req_ttrt; + bp->cmd_req_virt->snmp_set.item[1].item_index = 0; + bp->cmd_req_virt->snmp_set.item[2].item_code = PI_ITEM_K_EOL; + if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) + { + printk("%s: DMA command request failed!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* Initialize adapter CAM */ + + if (dfx_ctl_update_cam(bp) != DFX_K_SUCCESS) + { + printk("%s: Adapter CAM update failed!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* Initialize adapter filters */ + + if (dfx_ctl_update_filters(bp) != DFX_K_SUCCESS) + { + printk("%s: Adapter filters update failed!\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* Initialize receive descriptor block and produce buffers */ + + dfx_rcv_init(bp); + + /* Issue START command and bring adapter to LINK_(UN)AVAILABLE state */ + + bp->cmd_req_virt->cmd_type = PI_CMD_K_START; + if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) + { + printk("%s: Start command failed\n", bp->dev->name); + return(DFX_K_FAILURE); + } + + /* Initialization succeeded, reenable PDQ interrupts */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_ENABLE_DEF_INTS); + return(DFX_K_SUCCESS); + } + + +/* + * ============ + * = dfx_open = + * ============ + * + * Overview: + * Opens the adapter + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This function brings the adapter to an operational state. + * + * Return Codes: + * 0 - Adapter was successfully opened + * -EAGAIN - Could not register IRQ or adapter initialization failed + * + * Assumptions: + * This routine should only be called for a device that was + * initialized successfully during the dfx_probe process. + * + * Side Effects: + * Adapter should be in LINK_AVAILABLE or LINK_UNAVAILABLE state + * if the open is successful. + */ + +int dfx_open( + struct device *dev + ) + + { + DFX_board_t *bp = (DFX_board_t *)dev->priv; + + DBG_printk("In dfx_open...\n"); + + /* Register IRQ - support shared interrupts by passing device ptr */ + + if (request_irq(dev->irq, (void *)dfx_interrupt, SA_SHIRQ, dev->name, dev)) + { + printk("%s: Requested IRQ %d is busy\n", dev->name, dev->irq); + return(-EAGAIN); + } + + /* + * Set current address to factory MAC address + * + * Note: We've already done this step in dfx_driver_init. + * However, it's possible that a user has set a node + * address override, then closed and reopened the + * adapter. Unless we reset the device address field + * now, we'll continue to use the existing modified + * address. + */ + + memcpy(dev->dev_addr, bp->factory_mac_addr, FDDI_K_ALEN); + + /* Clear local unicast/multicast address tables and counts */ + + memset(bp->uc_table, 0, sizeof(bp->uc_table)); + memset(bp->mc_table, 0, sizeof(bp->mc_table)); + bp->uc_count = 0; + bp->mc_count = 0; + + /* Disable promiscuous filter settings */ + + bp->ind_group_prom = PI_FSTATE_K_BLOCK; + bp->group_prom = PI_FSTATE_K_BLOCK; + + /* Reset and initialize adapter */ + + bp->reset_type = PI_PDATA_A_RESET_M_SKIP_ST; /* skip self-test */ + if (dfx_adap_init(bp) != DFX_K_SUCCESS) + { + printk("%s: Adapter open failed!\n", dev->name); + return(-EAGAIN); + } + + /* Set device structure info */ + + dev->tbusy = 0; + dev->interrupt = DFX_UNMASK_INTERRUPTS; + dev->start = 1; + return(0); + } + + +/* + * ============= + * = dfx_close = + * ============= + * + * Overview: + * Closes the device/module. + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This routine closes the adapter and brings it to a safe state. + * The interrupt service routine is deregistered with the OS. + * The adapter can be opened again with another call to dfx_open(). + * + * Return Codes: + * Always return 0. + * + * Assumptions: + * No further requests for this adapter are made after this routine is + * called. dfx_open() can be called to reset and reinitialize the + * adapter. + * + * Side Effects: + * Adapter should be in DMA_UNAVAILABLE state upon completion of this + * routine. + */ + +int dfx_close( + struct device *dev + ) + + { + DFX_board_t *bp = (DFX_board_t *)dev->priv; + + DBG_printk("In dfx_close...\n"); + + /* Disable PDQ interrupts first */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); + + /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */ + + (void) dfx_hw_dma_uninit(bp, PI_PDATA_A_RESET_M_SKIP_ST); + + /* + * Flush any pending transmit buffers + * + * Note: It's important that we flush the transmit buffers + * BEFORE we clear our copy of the Type 2 register. + * Otherwise, we'll have no idea how many buffers + * we need to free. + */ + + dfx_xmt_flush(bp); + + /* + * Clear Type 1 and Type 2 registers after adapter reset + * + * Note: Even though we're closing the adapter, it's + * possible that an interrupt will occur after + * dfx_close is called. Without some assurance to + * the contrary we want to make sure that we don't + * process receive and transmit LLC frames and update + * the Type 2 register with bad information. + */ + + bp->cmd_req_reg.lword = 0; + bp->cmd_rsp_reg.lword = 0; + bp->rcv_xmt_reg.lword = 0; + + /* Clear consumer block for the same reason given above */ + + memset(bp->cons_block_virt, 0, sizeof(PI_CONSUMER_BLOCK)); + + /* Clear device structure flags */ + + dev->start = 0; + dev->tbusy = 1; + + /* Deregister (free) IRQ */ + + free_irq(dev->irq, dev); + return(0); + } + + +/* + * ====================== + * = dfx_int_pr_halt_id = + * ====================== + * + * Overview: + * Displays halt id's in string form. + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * Determine current halt id and display appropriate string. + * + * Return Codes: + * None + * + * Assumptions: + * None + * + * Side Effects: + * None + */ + +void dfx_int_pr_halt_id( + DFX_board_t *bp + ) + + { + PI_UINT32 port_status; /* PDQ port status register value */ + PI_UINT32 halt_id; /* PDQ port status halt ID */ + + /* Read the latest port status */ + + dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &port_status); + + /* Display halt state transition information */ + + halt_id = (port_status & PI_PSTATUS_M_HALT_ID) >> PI_PSTATUS_V_HALT_ID; + switch (halt_id) + { + case PI_HALT_ID_K_SELFTEST_TIMEOUT: + printk("%s: Halt ID: Selftest Timeout\n", bp->dev->name); + break; + + case PI_HALT_ID_K_PARITY_ERROR: + printk("%s: Halt ID: Host Bus Parity Error\n", bp->dev->name); + break; + + case PI_HALT_ID_K_HOST_DIR_HALT: + printk("%s: Halt ID: Host-Directed Halt\n", bp->dev->name); + break; + + case PI_HALT_ID_K_SW_FAULT: + printk("%s: Halt ID: Adapter Software Fault\n", bp->dev->name); + break; + + case PI_HALT_ID_K_HW_FAULT: + printk("%s: Halt ID: Adapter Hardware Fault\n", bp->dev->name); + break; + + case PI_HALT_ID_K_PC_TRACE: + printk("%s: Halt ID: FDDI Network PC Trace Path Test\n", bp->dev->name); + break; + + case PI_HALT_ID_K_DMA_ERROR: + printk("%s: Halt ID: Adapter DMA Error\n", bp->dev->name); + break; + + case PI_HALT_ID_K_IMAGE_CRC_ERROR: + printk("%s: Halt ID: Firmware Image CRC Error\n", bp->dev->name); + break; + + case PI_HALT_ID_K_BUS_EXCEPTION: + printk("%s: Halt ID: 68000 Bus Exception\n", bp->dev->name); + break; + + default: + printk("%s: Halt ID: Unknown (code = %X)\n", bp->dev->name, halt_id); + break; + } + return; + } + + +/* + * ========================== + * = dfx_int_type_0_process = + * ========================== + * + * Overview: + * Processes Type 0 interrupts. + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * Processes all enabled Type 0 interrupts. If the reason for the interrupt + * is a serious fault on the adapter, then an error message is displayed + * and the adapter is reset. + * + * One tricky potential timing window is the rapid succession of "link avail" + * "link unavail" state change interrupts. The acknowledgement of the Type 0 + * interrupt must be done before reading the state from the Port Status + * register. This is true because a state change could occur after reading + * the data, but before acknowledging the interrupt. If this state change + * does happen, it would be lost because the driver is using the old state, + * and it will never know about the new state because it subsequently + * acknowledges the state change interrupt. + * + * INCORRECT CORRECT + * read type 0 int reasons read type 0 int reasons + * read adapter state ack type 0 interrupts + * ack type 0 interrupts read adapter state + * ... process interrupt ... ... process interrupt ... + * + * Return Codes: + * None + * + * Assumptions: + * None + * + * Side Effects: + * An adapter reset may occur if the adapter has any Type 0 error interrupts + * or if the port status indicates that the adapter is halted. The driver + * is responsible for reinitializing the adapter with the current CAM + * contents and adapter filter settings. + */ + +void dfx_int_type_0_process( + DFX_board_t *bp + ) + + { + PI_UINT32 type_0_status; /* Host Interrupt Type 0 register */ + PI_UINT32 state; /* current adap state (from port status) */ + + /* + * Read host interrupt Type 0 register to determine which Type 0 + * interrupts are pending. Immediately write it back out to clear + * those interrupts. + */ + + dfx_port_read_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, &type_0_status); + dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, type_0_status); + + /* Check for Type 0 error interrupts */ + + if (type_0_status & (PI_TYPE_0_STAT_M_NXM | + PI_TYPE_0_STAT_M_PM_PAR_ERR | + PI_TYPE_0_STAT_M_BUS_PAR_ERR)) + { + /* Check for Non-Existent Memory error */ + + if (type_0_status & PI_TYPE_0_STAT_M_NXM) + printk("%s: Non-Existent Memory Access Error\n", bp->dev->name); + + /* Check for Packet Memory Parity error */ + + if (type_0_status & PI_TYPE_0_STAT_M_PM_PAR_ERR) + printk("%s: Packet Memory Parity Error\n", bp->dev->name); + + /* Check for Host Bus Parity error */ + + if (type_0_status & PI_TYPE_0_STAT_M_BUS_PAR_ERR) + printk("%s: Host Bus Parity Error\n", bp->dev->name); + + /* Reset adapter and bring it back on-line */ + + bp->link_available = PI_K_FALSE; /* link is no longer available */ + bp->reset_type = 0; /* rerun on-board diagnostics */ + printk("%s: Resetting adapter...\n", bp->dev->name); + if (dfx_adap_init(bp) != DFX_K_SUCCESS) + { + printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name); + dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); + return; + } + printk("%s: Adapter reset successful!\n", bp->dev->name); + return; + } + + /* Check for transmit flush interrupt */ + + if (type_0_status & PI_TYPE_0_STAT_M_XMT_FLUSH) + { + /* Flush any pending xmt's and acknowledge the flush interrupt */ + + bp->link_available = PI_K_FALSE; /* link is no longer available */ + dfx_xmt_flush(bp); /* flush any outstanding packets */ + (void) dfx_hw_port_ctrl_req(bp, + PI_PCTRL_M_XMT_DATA_FLUSH_DONE, + 0, + 0, + NULL); + } + + /* Check for adapter state change */ + + if (type_0_status & PI_TYPE_0_STAT_M_STATE_CHANGE) + { + /* Get latest adapter state */ + + state = dfx_hw_adap_state_rd(bp); /* get adapter state */ + if (state == PI_STATE_K_HALTED) + { + /* + * Adapter has transitioned to HALTED state, try to reset + * adapter to bring it back on-line. If reset fails, + * leave the adapter in the broken state. + */ + + printk("%s: Controller has transitioned to HALTED state!\n", bp->dev->name); + dfx_int_pr_halt_id(bp); /* display halt id as string */ + + /* Reset adapter and bring it back on-line */ + + bp->link_available = PI_K_FALSE; /* link is no longer available */ + bp->reset_type = 0; /* rerun on-board diagnostics */ + printk("%s: Resetting adapter...\n", bp->dev->name); + if (dfx_adap_init(bp) != DFX_K_SUCCESS) + { + printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name); + dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS); + return; + } + printk("%s: Adapter reset successful!\n", bp->dev->name); + } + else if (state == PI_STATE_K_LINK_AVAIL) + { + bp->link_available = PI_K_TRUE; /* set link available flag */ + } + } + return; + } + + +/* + * ================== + * = dfx_int_common = + * ================== + * + * Overview: + * Interrupt service routine (ISR) + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * This is the ISR which processes incoming adapter interrupts. + * + * Return Codes: + * None + * + * Assumptions: + * This routine assumes PDQ interrupts have not been disabled. + * When interrupts are disabled at the PDQ, the Port Status register + * is automatically cleared. This routine uses the Port Status + * register value to determine whether a Type 0 interrupt occurred, + * so it's important that adapter interrupts are not normally + * enabled/disabled at the PDQ. + * + * It's vital that this routine is NOT reentered for the + * same board and that the OS is not in another section of + * code (eg. dfx_xmt_queue_pkt) for the same board on a + * different thread. + * + * Side Effects: + * Pending interrupts are serviced. Depending on the type of + * interrupt, acknowledging and clearing the interrupt at the + * PDQ involves writing a register to clear the interrupt bit + * or updating completion indices. + */ + +void dfx_int_common( + DFX_board_t *bp + ) + + { + PI_UINT32 port_status; /* Port Status register */ + + /* Process xmt interrupts - frequent case, so always call this routine */ + + dfx_xmt_done(bp); /* free consumed xmt packets */ + + /* Process rcv interrupts - frequent case, so always call this routine */ + + dfx_rcv_queue_process(bp); /* service received LLC frames */ + + /* + * Transmit and receive producer and completion indices are updated on the + * adapter by writing to the Type 2 Producer register. Since the frequent + * case is that we'll be processing either LLC transmit or receive buffers, + * we'll optimize I/O writes by doing a single register write here. + */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_2_PROD, bp->rcv_xmt_reg.lword); + + /* Read PDQ Port Status register to find out which interrupts need processing */ + + dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &port_status); + + /* Process Type 0 interrupts (if any) - infrequent, so only call when needed */ + + if (port_status & PI_PSTATUS_M_TYPE_0_PENDING) + dfx_int_type_0_process(bp); /* process Type 0 interrupts */ + return; + } + + +/* + * ================= + * = dfx_interrupt = + * ================= + * + * Overview: + * Interrupt processing routine + * + * Returns: + * None + * + * Arguments: + * irq - interrupt vector + * dev_id - pointer to device information + * regs - pointer to registers structure + * + * Functional Description: + * This routine calls the interrupt processing routine for this adapter. It + * disables and reenables adapter interrupts, as appropriate. We can support + * shared interrupts since the incoming dev_id pointer provides our device + * structure context. + * + * Return Codes: + * None + * + * Assumptions: + * The interrupt acknowledgement at the hardware level (eg. ACKing the PIC + * on Intel-based systems) is done by the operating system outside this + * routine. + * + * System interrupts are enabled through this call. + * + * Side Effects: + * Interrupts are disabled, then reenabled at the adapter. + */ + +void dfx_interrupt( + int irq, + void *dev_id, + struct pt_regs *regs + ) + + { + struct device *dev = (struct device *) dev_id; + DFX_board_t *bp; /* private board structure pointer */ + u8 tmp; /* used for disabling/enabling ints */ + + /* Get board pointer only if device structure is valid */ + + if (dev == NULL) + { + printk("dfx_interrupt(): irq %d for unknown device!\n", irq); + return; + } + bp = (DFX_board_t *) dev->priv; + + /* See if we're already servicing an interrupt */ + + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler!\n", dev->name); + dev->interrupt = DFX_MASK_INTERRUPTS; /* ensure non reentrancy */ + + /* Service adapter interrupts */ + + if (bp->bus_type == DFX_BUS_TYPE_PCI) + { + /* Disable PDQ-PFI interrupts at PFI */ + + dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, PFI_MODE_M_DMA_ENB); + + /* Call interrupt service routine for this adapter */ + + dfx_int_common(bp); + + /* Clear PDQ interrupt status bit and reenable interrupts */ + + dfx_port_write_long(bp, PFI_K_REG_STATUS, PFI_STATUS_M_PDQ_INT); + dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, + (PFI_MODE_M_PDQ_INT_ENB + PFI_MODE_M_DMA_ENB)); + } + else + { + /* Disable interrupts at the ESIC */ + + dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &tmp); + tmp &= ~PI_CONFIG_STAT_0_M_INT_ENB; + dfx_port_write_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, tmp); + + /* Call interrupt service routine for this adapter */ + + dfx_int_common(bp); + + /* Reenable interrupts at the ESIC */ + + dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &tmp); + tmp |= PI_CONFIG_STAT_0_M_INT_ENB; + dfx_port_write_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, tmp); + } + + dev->interrupt = DFX_UNMASK_INTERRUPTS; + return; + } + + +/* + * ===================== + * = dfx_ctl_get_stats = + * ===================== + * + * Overview: + * Get statistics for FDDI adapter + * + * Returns: + * Pointer to FDDI statistics structure + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * Gets current MIB objects from adapter, then + * returns FDDI statistics structure as defined + * in if_fddi.h. + * + * Note: Since the FDDI statistics structure is + * still new and the device structure doesn't + * have an FDDI-specific get statistics handler, + * we'll return the FDDI statistics structure as + * a pointer to an Ethernet statistics structure. + * That way, at least the first part of the statistics + * structure can be decoded properly, and it allows + * "smart" applications to perform a second cast to + * decode the FDDI-specific statistics. + * + * We'll have to pay attention to this routine as the + * device structure becomes more mature and LAN media + * independent. + * + * Return Codes: + * None + * + * Assumptions: + * None + * + * Side Effects: + * None + */ + +struct enet_statistics *dfx_ctl_get_stats( + struct device *dev + ) + + { + DFX_board_t *bp = (DFX_board_t *)dev->priv; + + /* Fill the bp->stats structure with driver-maintained counters */ + + bp->stats.rx_packets = bp->rcv_total_frames; + bp->stats.tx_packets = bp->xmt_total_frames; + bp->stats.rx_errors = (u32)(bp->rcv_crc_errors + bp->rcv_frame_status_errors + bp->rcv_length_errors); + bp->stats.tx_errors = bp->xmt_length_errors; + bp->stats.rx_dropped = bp->rcv_discards; + bp->stats.tx_dropped = bp->xmt_discards; + bp->stats.multicast = bp->rcv_multicast_frames; + bp->stats.transmit_collision = 0; /* always zero (0) for FDDI */ + + /* Get FDDI SMT MIB objects */ + + bp->cmd_req_virt->cmd_type = PI_CMD_K_SMT_MIB_GET; + if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) + return((struct enet_statistics *) &bp->stats); + + /* Fill the bp->stats structure with the SMT MIB object values */ + + memcpy(bp->stats.smt_station_id, &bp->cmd_rsp_virt->smt_mib_get.smt_station_id, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_station_id)); + bp->stats.smt_op_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_op_version_id; + bp->stats.smt_hi_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_hi_version_id; + bp->stats.smt_lo_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_lo_version_id; + memcpy(bp->stats.smt_user_data, &bp->cmd_rsp_virt->smt_mib_get.smt_user_data, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_user_data)); + bp->stats.smt_mib_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_mib_version_id; + bp->stats.smt_mac_cts = bp->cmd_rsp_virt->smt_mib_get.smt_mac_ct; + bp->stats.smt_non_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_non_master_ct; + bp->stats.smt_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_master_ct; + bp->stats.smt_available_paths = bp->cmd_rsp_virt->smt_mib_get.smt_available_paths; + bp->stats.smt_config_capabilities = bp->cmd_rsp_virt->smt_mib_get.smt_config_capabilities; + bp->stats.smt_config_policy = bp->cmd_rsp_virt->smt_mib_get.smt_config_policy; + bp->stats.smt_connection_policy = bp->cmd_rsp_virt->smt_mib_get.smt_connection_policy; + bp->stats.smt_t_notify = bp->cmd_rsp_virt->smt_mib_get.smt_t_notify; + bp->stats.smt_stat_rpt_policy = bp->cmd_rsp_virt->smt_mib_get.smt_stat_rpt_policy; + bp->stats.smt_trace_max_expiration = bp->cmd_rsp_virt->smt_mib_get.smt_trace_max_expiration; + bp->stats.smt_bypass_present = bp->cmd_rsp_virt->smt_mib_get.smt_bypass_present; + bp->stats.smt_ecm_state = bp->cmd_rsp_virt->smt_mib_get.smt_ecm_state; + bp->stats.smt_cf_state = bp->cmd_rsp_virt->smt_mib_get.smt_cf_state; + bp->stats.smt_remote_disconnect_flag = bp->cmd_rsp_virt->smt_mib_get.smt_remote_disconnect_flag; + bp->stats.smt_station_status = bp->cmd_rsp_virt->smt_mib_get.smt_station_status; + bp->stats.smt_peer_wrap_flag = bp->cmd_rsp_virt->smt_mib_get.smt_peer_wrap_flag; + bp->stats.smt_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_msg_time_stamp.ls; + bp->stats.smt_transition_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_transition_time_stamp.ls; + bp->stats.mac_frame_status_functions = bp->cmd_rsp_virt->smt_mib_get.mac_frame_status_functions; + bp->stats.mac_t_max_capability = bp->cmd_rsp_virt->smt_mib_get.mac_t_max_capability; + bp->stats.mac_tvx_capability = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_capability; + bp->stats.mac_available_paths = bp->cmd_rsp_virt->smt_mib_get.mac_available_paths; + bp->stats.mac_current_path = bp->cmd_rsp_virt->smt_mib_get.mac_current_path; + memcpy(bp->stats.mac_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_upstream_nbr, FDDI_K_ALEN); + memcpy(bp->stats.mac_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_downstream_nbr, FDDI_K_ALEN); + memcpy(bp->stats.mac_old_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_upstream_nbr, FDDI_K_ALEN); + memcpy(bp->stats.mac_old_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_downstream_nbr, FDDI_K_ALEN); + bp->stats.mac_dup_address_test = bp->cmd_rsp_virt->smt_mib_get.mac_dup_address_test; + bp->stats.mac_requested_paths = bp->cmd_rsp_virt->smt_mib_get.mac_requested_paths; + bp->stats.mac_downstream_port_type = bp->cmd_rsp_virt->smt_mib_get.mac_downstream_port_type; + memcpy(bp->stats.mac_smt_address, &bp->cmd_rsp_virt->smt_mib_get.mac_smt_address, FDDI_K_ALEN); + bp->stats.mac_t_req = bp->cmd_rsp_virt->smt_mib_get.mac_t_req; + bp->stats.mac_t_neg = bp->cmd_rsp_virt->smt_mib_get.mac_t_neg; + bp->stats.mac_t_max = bp->cmd_rsp_virt->smt_mib_get.mac_t_max; + bp->stats.mac_tvx_value = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_value; + bp->stats.mac_frame_error_threshold = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_threshold; + bp->stats.mac_frame_error_ratio = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_ratio; + bp->stats.mac_rmt_state = bp->cmd_rsp_virt->smt_mib_get.mac_rmt_state; + bp->stats.mac_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_da_flag; + bp->stats.mac_una_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_unda_flag; + bp->stats.mac_frame_error_flag = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_flag; + bp->stats.mac_ma_unitdata_available = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_available; + bp->stats.mac_hardware_present = bp->cmd_rsp_virt->smt_mib_get.mac_hardware_present; + bp->stats.mac_ma_unitdata_enable = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_enable; + bp->stats.path_tvx_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_tvx_lower_bound; + bp->stats.path_t_max_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_t_max_lower_bound; + bp->stats.path_max_t_req = bp->cmd_rsp_virt->smt_mib_get.path_max_t_req; + memcpy(bp->stats.path_configuration, &bp->cmd_rsp_virt->smt_mib_get.path_configuration, sizeof(bp->cmd_rsp_virt->smt_mib_get.path_configuration)); + bp->stats.port_my_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[0]; + bp->stats.port_my_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[1]; + bp->stats.port_neighbor_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[0]; + bp->stats.port_neighbor_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[1]; + bp->stats.port_connection_policies[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[0]; + bp->stats.port_connection_policies[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[1]; + bp->stats.port_mac_indicated[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[0]; + bp->stats.port_mac_indicated[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[1]; + bp->stats.port_current_path[0] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[0]; + bp->stats.port_current_path[1] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[1]; + memcpy(&bp->stats.port_requested_paths[0*3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[0], 3); + memcpy(&bp->stats.port_requested_paths[1*3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[1], 3); + bp->stats.port_mac_placement[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[0]; + bp->stats.port_mac_placement[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[1]; + bp->stats.port_available_paths[0] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[0]; + bp->stats.port_available_paths[1] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[1]; + bp->stats.port_pmd_class[0] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[0]; + bp->stats.port_pmd_class[1] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[1]; + bp->stats.port_connection_capabilities[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[0]; + bp->stats.port_connection_capabilities[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[1]; + bp->stats.port_bs_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[0]; + bp->stats.port_bs_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[1]; + bp->stats.port_ler_estimate[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[0]; + bp->stats.port_ler_estimate[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[1]; + bp->stats.port_ler_cutoff[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[0]; + bp->stats.port_ler_cutoff[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[1]; + bp->stats.port_ler_alarm[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[0]; + bp->stats.port_ler_alarm[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[1]; + bp->stats.port_connect_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[0]; + bp->stats.port_connect_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[1]; + bp->stats.port_pcm_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[0]; + bp->stats.port_pcm_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[1]; + bp->stats.port_pc_withhold[0] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[0]; + bp->stats.port_pc_withhold[1] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[1]; + bp->stats.port_ler_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[0]; + bp->stats.port_ler_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[1]; + bp->stats.port_hardware_present[0] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[0]; + bp->stats.port_hardware_present[1] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[1]; + + /* Get FDDI counters */ + + bp->cmd_req_virt->cmd_type = PI_CMD_K_CNTRS_GET; + if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) + return((struct enet_statistics *) &bp->stats); + + /* Fill the bp->stats structure with the FDDI counter values */ + + bp->stats.mac_frame_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.frame_cnt.ls; + bp->stats.mac_copied_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.copied_cnt.ls; + bp->stats.mac_transmit_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.transmit_cnt.ls; + bp->stats.mac_error_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.error_cnt.ls; + bp->stats.mac_lost_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.lost_cnt.ls; + bp->stats.port_lct_fail_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[0].ls; + bp->stats.port_lct_fail_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[1].ls; + bp->stats.port_lem_reject_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[0].ls; + bp->stats.port_lem_reject_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[1].ls; + bp->stats.port_lem_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[0].ls; + bp->stats.port_lem_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[1].ls; + + return((struct enet_statistics *) &bp->stats); + } + + +/* + * ============================== + * = dfx_ctl_set_multicast_list = + * ============================== + * + * Overview: + * Enable/Disable LLC frame promiscuous mode reception + * on the adapter and/or update multicast address table. + * + * Returns: + * None + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This routine follows a fairly simple algorithm for setting the + * adapter filters and CAM: + * + * if IFF_PROMISC flag is set + * enable LLC individual/group promiscuous mode + * else + * disable LLC individual/group promiscuous mode + * if number of incoming multicast addresses > + * (CAM max size - number of unicast addresses in CAM) + * enable LLC group promiscuous mode + * set driver-maintained multicast address count to zero + * else + * disable LLC group promiscuous mode + * set driver-maintained multicast address count to incoming count + * update adapter CAM + * update adapter filters + * + * Return Codes: + * None + * + * Assumptions: + * Multicast addresses are presented in canonical (LSB) format. + * + * Side Effects: + * On-board adapter CAM and filters are updated. + */ + +void dfx_ctl_set_multicast_list( + struct device *dev + ) + + { + DFX_board_t *bp = (DFX_board_t *)dev->priv; + int i; /* used as index in for loop */ + struct dev_mc_list *dmi; /* ptr to multicast addr entry */ + + /* Enable LLC frame promiscuous mode, if necessary */ + + if (dev->flags & IFF_PROMISC) + bp->ind_group_prom = PI_FSTATE_K_PASS; /* Enable LLC ind/group prom mode */ + + /* Else, update multicast address table */ + + else + { + bp->ind_group_prom = PI_FSTATE_K_BLOCK; /* Disable LLC ind/group prom mode */ + /* + * Check whether incoming multicast address count exceeds table size + * + * Note: The adapters utilize an on-board 64 entry CAM for + * supporting perfect filtering of multicast packets + * and bridge functions when adding unicast addresses. + * There is no hash function available. To support + * additional multicast addresses, the all multicast + * filter (LLC group promiscuous mode) must be enabled. + * + * The firmware reserves two CAM entries for SMT-related + * multicast addresses, which leaves 62 entries available. + * The following code ensures that we're not being asked + * to add more than 62 addresses to the CAM. If we are, + * the driver will enable the all multicast filter. + * Should the number of multicast addresses drop below + * the high water mark, the filter will be disabled and + * perfect filtering will be used. + */ + + if (dev->mc_count > (PI_CMD_ADDR_FILTER_K_SIZE - bp->uc_count)) + { + bp->group_prom = PI_FSTATE_K_PASS; /* Enable LLC group prom mode */ + bp->mc_count = 0; /* Don't add mc addrs to CAM */ + } + else + { + bp->group_prom = PI_FSTATE_K_BLOCK; /* Disable LLC group prom mode */ + bp->mc_count = dev->mc_count; /* Add mc addrs to CAM */ + } + + /* Copy addresses to multicast address table, then update adapter CAM */ + + dmi = dev->mc_list; /* point to first multicast addr */ + for (i=0; i < bp->mc_count; i++) + { + memcpy(&bp->mc_table[i*FDDI_K_ALEN], dmi->dmi_addr, FDDI_K_ALEN); + dmi = dmi->next; /* point to next multicast addr */ + } + if (dfx_ctl_update_cam(bp) != DFX_K_SUCCESS) + { + DBG_printk("%s: Could not update multicast address table!\n", dev->name); + } + else + { + DBG_printk("%s: Multicast address table updated! Added %d addresses.\n", dev->name, bp->mc_count); + } + } + + /* Update adapter filters */ + + if (dfx_ctl_update_filters(bp) != DFX_K_SUCCESS) + { + DBG_printk("%s: Could not update adapter filters!\n", dev->name); + } + else + { + DBG_printk("%s: Adapter filters updated!\n", dev->name); + } + return; + } + + +/* + * =========================== + * = dfx_ctl_set_mac_address = + * =========================== + * + * Overview: + * Add node address override (unicast address) to adapter + * CAM and update dev_addr field in device table. + * + * Returns: + * None + * + * Arguments: + * dev - pointer to device information + * addr - pointer to sockaddr structure containing unicast address to add + * + * Functional Description: + * The adapter supports node address overrides by adding one or more + * unicast addresses to the adapter CAM. This is similar to adding + * multicast addresses. In this routine we'll update the driver and + * device structures with the new address, then update the adapter CAM + * to ensure that the adapter will copy and strip frames destined and + * sourced by that address. + * + * Return Codes: + * Always returns zero. + * + * Assumptions: + * The address pointed to by addr->sa_data is a valid unicast + * address and is presented in canonical (LSB) format. + * + * Side Effects: + * On-board adapter CAM is updated. On-board adapter filters + * may be updated. + */ + +int dfx_ctl_set_mac_address( + struct device *dev, + void *addr + ) + + { + DFX_board_t *bp = (DFX_board_t *)dev->priv; + struct sockaddr *p_sockaddr = (struct sockaddr *)addr; + + /* Copy unicast address to driver-maintained structs and update count */ + + memcpy(dev->dev_addr, p_sockaddr->sa_data, FDDI_K_ALEN); /* update device struct */ + memcpy(&bp->uc_table[0], p_sockaddr->sa_data, FDDI_K_ALEN); /* update driver struct */ + bp->uc_count = 1; + + /* + * Verify we're not exceeding the CAM size by adding unicast address + * + * Note: It's possible that before entering this routine we've + * already filled the CAM with 62 multicast addresses. + * Since we need to place the node address override into + * the CAM, we have to check to see that we're not + * exceeding the CAM size. If we are, we have to enable + * the LLC group (multicast) promiscuous mode filter as + * in dfx_ctl_set_multicast_list. + */ + + if ((bp->uc_count + bp->mc_count) > PI_CMD_ADDR_FILTER_K_SIZE) + { + bp->group_prom = PI_FSTATE_K_PASS; /* Enable LLC group prom mode */ + bp->mc_count = 0; /* Don't add mc addrs to CAM */ + + /* Update adapter filters */ + + if (dfx_ctl_update_filters(bp) != DFX_K_SUCCESS) + { + DBG_printk("%s: Could not update adapter filters!\n", dev->name); + } + else + { + DBG_printk("%s: Adapter filters updated!\n", dev->name); + } + } + + /* Update adapter CAM with new unicast address */ + + if (dfx_ctl_update_cam(bp) != DFX_K_SUCCESS) + { + DBG_printk("%s: Could not set new MAC address!\n", dev->name); + } + else + { + DBG_printk("%s: Adapter CAM updated with new MAC address\n", dev->name); + } + return(0); /* always return zero */ + } + + +/* + * ====================== + * = dfx_ctl_update_cam = + * ====================== + * + * Overview: + * Procedure to update adapter CAM (Content Addressable Memory) + * with desired unicast and multicast address entries. + * + * Returns: + * Condition code + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * Updates adapter CAM with current contents of board structure + * unicast and multicast address tables. Since there are only 62 + * free entries in CAM, this routine ensures that the command + * request buffer is not overrun. + * + * Return Codes: + * DFX_K_SUCCESS - Request succeeded + * DFX_K_FAILURE - Request failed + * + * Assumptions: + * All addresses being added (unicast and multicast) are in canonical + * order. + * + * Side Effects: + * On-board adapter CAM is updated. + */ + +int dfx_ctl_update_cam( + DFX_board_t *bp + ) + + { + int i; /* used as index */ + PI_LAN_ADDR *p_addr; /* pointer to CAM entry */ + + /* + * Fill in command request information + * + * Note: Even though both the unicast and multicast address + * table entries are stored as contiguous 6 byte entries, + * the firmware address filter set command expects each + * entry to be two longwords (8 bytes total). We must be + * careful to only copy the six bytes of each unicast and + * multicast table entry into each command entry. This + * is also why we must first clear the entire command + * request buffer. + */ + + memset(bp->cmd_req_virt, 0, PI_CMD_REQ_K_SIZE_MAX); /* first clear buffer */ + bp->cmd_req_virt->cmd_type = PI_CMD_K_ADDR_FILTER_SET; + p_addr = &bp->cmd_req_virt->addr_filter_set.entry[0]; + + /* Now add unicast addresses to command request buffer, if any */ + + for (i=0; i < (int)bp->uc_count; i++) + { + if (i < PI_CMD_ADDR_FILTER_K_SIZE) + { + memcpy(p_addr, &bp->uc_table[i*FDDI_K_ALEN], FDDI_K_ALEN); + p_addr++; /* point to next command entry */ + } + } + + /* Now add multicast addresses to command request buffer, if any */ + + for (i=0; i < (int)bp->mc_count; i++) + { + if ((i + bp->uc_count) < PI_CMD_ADDR_FILTER_K_SIZE) + { + memcpy(p_addr, &bp->mc_table[i*FDDI_K_ALEN], FDDI_K_ALEN); + p_addr++; /* point to next command entry */ + } + } + + /* Issue command to update adapter CAM, then return */ + + if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) + return(DFX_K_FAILURE); + return(DFX_K_SUCCESS); + } + + +/* + * ========================== + * = dfx_ctl_update_filters = + * ========================== + * + * Overview: + * Procedure to update adapter filters with desired + * filter settings. + * + * Returns: + * Condition code + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * Enables or disables filter using current filter settings. + * + * Return Codes: + * DFX_K_SUCCESS - Request succeeded. + * DFX_K_FAILURE - Request failed. + * + * Assumptions: + * We must always pass up packets destined to the broadcast + * address (FF-FF-FF-FF-FF-FF), so we'll always keep the + * broadcast filter enabled. + * + * Side Effects: + * On-board adapter filters are updated. + */ + +int dfx_ctl_update_filters( + DFX_board_t *bp + ) + + { + int i = 0; /* used as index */ + + /* Fill in command request information */ + + bp->cmd_req_virt->cmd_type = PI_CMD_K_FILTERS_SET; + + /* Initialize Broadcast filter - * ALWAYS ENABLED * */ + + bp->cmd_req_virt->filter_set.item[i].item_code = PI_ITEM_K_BROADCAST; + bp->cmd_req_virt->filter_set.item[i++].value = PI_FSTATE_K_PASS; + + /* Initialize LLC Individual/Group Promiscuous filter */ + + bp->cmd_req_virt->filter_set.item[i].item_code = PI_ITEM_K_IND_GROUP_PROM; + bp->cmd_req_virt->filter_set.item[i++].value = bp->ind_group_prom; + + /* Initialize LLC Group Promiscuous filter */ + + bp->cmd_req_virt->filter_set.item[i].item_code = PI_ITEM_K_GROUP_PROM; + bp->cmd_req_virt->filter_set.item[i++].value = bp->group_prom; + + /* Terminate the item code list */ + + bp->cmd_req_virt->filter_set.item[i].item_code = PI_ITEM_K_EOL; + + /* Issue command to update adapter filters, then return */ + + if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS) + return(DFX_K_FAILURE); + return(DFX_K_SUCCESS); + } + + +/* + * ====================== + * = dfx_hw_dma_cmd_req = + * ====================== + * + * Overview: + * Sends PDQ DMA command to adapter firmware + * + * Returns: + * Condition code + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * The command request and response buffers are posted to the adapter in the manner + * described in the PDQ Port Specification: + * + * 1. Command Response Buffer is posted to adapter. + * 2. Command Request Buffer is posted to adapter. + * 3. Command Request consumer index is polled until it indicates that request + * buffer has been DMA'd to adapter. + * 4. Command Response consumer index is polled until it indicates that response + * buffer has been DMA'd from adapter. + * + * This ordering ensures that a response buffer is already available for the firmware + * to use once it's done processing the request buffer. + * + * Return Codes: + * DFX_K_SUCCESS - DMA command succeeded + * DFX_K_OUTSTATE - Adapter is NOT in proper state + * DFX_K_HW_TIMEOUT - DMA command timed out + * + * Assumptions: + * Command request buffer has already been filled with desired DMA command. + * + * Side Effects: + * None + */ + +int dfx_hw_dma_cmd_req( + DFX_board_t *bp + ) + + { + int status; /* adapter status */ + int timeout_cnt; /* used in for loops */ + + /* Make sure the adapter is in a state that we can issue the DMA command in */ + + status = dfx_hw_adap_state_rd(bp); + if ((status == PI_STATE_K_RESET) || + (status == PI_STATE_K_HALTED) || + (status == PI_STATE_K_DMA_UNAVAIL) || + (status == PI_STATE_K_UPGRADE)) + return(DFX_K_OUTSTATE); + + /* Put response buffer on the command response queue */ + + bp->descr_block_virt->cmd_rsp[bp->cmd_rsp_reg.index.prod].long_0 = (u32) (PI_RCV_DESCR_M_SOP | + ((PI_CMD_RSP_K_SIZE_MAX / PI_ALIGN_K_CMD_RSP_BUFF) << PI_RCV_DESCR_V_SEG_LEN)); + bp->descr_block_virt->cmd_rsp[bp->cmd_rsp_reg.index.prod].long_1 = bp->cmd_rsp_phys; + + /* Bump (and wrap) the producer index and write out to register */ + + bp->cmd_rsp_reg.index.prod += 1; + bp->cmd_rsp_reg.index.prod &= PI_CMD_RSP_K_NUM_ENTRIES-1; + dfx_port_write_long(bp, PI_PDQ_K_REG_CMD_RSP_PROD, bp->cmd_rsp_reg.lword); + + /* Put request buffer on the command request queue */ + + bp->descr_block_virt->cmd_req[bp->cmd_req_reg.index.prod].long_0 = (u32) (PI_XMT_DESCR_M_SOP | + PI_XMT_DESCR_M_EOP | (PI_CMD_REQ_K_SIZE_MAX << PI_XMT_DESCR_V_SEG_LEN)); + bp->descr_block_virt->cmd_req[bp->cmd_req_reg.index.prod].long_1 = bp->cmd_req_phys; + + /* Bump (and wrap) the producer index and write out to register */ + + bp->cmd_req_reg.index.prod += 1; + bp->cmd_req_reg.index.prod &= PI_CMD_REQ_K_NUM_ENTRIES-1; + dfx_port_write_long(bp, PI_PDQ_K_REG_CMD_REQ_PROD, bp->cmd_req_reg.lword); + + /* + * Here we wait for the command request consumer index to be equal + * to the producer, indicating that the adapter has DMAed the request. + */ + + for (timeout_cnt = 20000; timeout_cnt > 0; timeout_cnt--) + { + if (bp->cmd_req_reg.index.prod == (u8)(bp->cons_block_virt->cmd_req)) + break; + udelay(100); /* wait for 100 microseconds */ + } + if (timeout_cnt == 0) + return(DFX_K_HW_TIMEOUT); + + /* Bump (and wrap) the completion index and write out to register */ + + bp->cmd_req_reg.index.comp += 1; + bp->cmd_req_reg.index.comp &= PI_CMD_REQ_K_NUM_ENTRIES-1; + dfx_port_write_long(bp, PI_PDQ_K_REG_CMD_REQ_PROD, bp->cmd_req_reg.lword); + + /* + * Here we wait for the command response consumer index to be equal + * to the producer, indicating that the adapter has DMAed the response. + */ + + for (timeout_cnt = 20000; timeout_cnt > 0; timeout_cnt--) + { + if (bp->cmd_rsp_reg.index.prod == (u8)(bp->cons_block_virt->cmd_rsp)) + break; + udelay(100); /* wait for 100 microseconds */ + } + if (timeout_cnt == 0) + return(DFX_K_HW_TIMEOUT); + + /* Bump (and wrap) the completion index and write out to register */ + + bp->cmd_rsp_reg.index.comp += 1; + bp->cmd_rsp_reg.index.comp &= PI_CMD_RSP_K_NUM_ENTRIES-1; + dfx_port_write_long(bp, PI_PDQ_K_REG_CMD_RSP_PROD, bp->cmd_rsp_reg.lword); + return(DFX_K_SUCCESS); + } + + +/* + * ======================== + * = dfx_hw_port_ctrl_req = + * ======================== + * + * Overview: + * Sends PDQ port control command to adapter firmware + * + * Returns: + * Host data register value in host_data if ptr is not NULL + * + * Arguments: + * bp - pointer to board information + * command - port control command + * data_a - port data A register value + * data_b - port data B register value + * host_data - ptr to host data register value + * + * Functional Description: + * Send generic port control command to adapter by writing + * to various PDQ port registers, then polling for completion. + * + * Return Codes: + * DFX_K_SUCCESS - port control command succeeded + * DFX_K_HW_TIMEOUT - port control command timed out + * + * Assumptions: + * None + * + * Side Effects: + * None + */ + +int dfx_hw_port_ctrl_req( + DFX_board_t *bp, + PI_UINT32 command, + PI_UINT32 data_a, + PI_UINT32 data_b, + PI_UINT32 *host_data + ) + + { + PI_UINT32 port_cmd; /* Port Control command register value */ + int timeout_cnt; /* used in for loops */ + + /* Set Command Error bit in command longword */ + + port_cmd = (PI_UINT32) (command | PI_PCTRL_M_CMD_ERROR); + + /* Issue port command to the adapter */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_PORT_DATA_A, data_a); + dfx_port_write_long(bp, PI_PDQ_K_REG_PORT_DATA_B, data_b); + dfx_port_write_long(bp, PI_PDQ_K_REG_PORT_CTRL, port_cmd); + + /* Now wait for command to complete */ + + if (command == PI_PCTRL_M_BLAST_FLASH) + timeout_cnt = 600000; /* set command timeout count to 60 seconds */ + else + timeout_cnt = 20000; /* set command timeout count to 2 seconds */ + + for (; timeout_cnt > 0; timeout_cnt--) + { + dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_CTRL, &port_cmd); + if (!(port_cmd & PI_PCTRL_M_CMD_ERROR)) + break; + udelay(100); /* wait for 100 microseconds */ + } + if (timeout_cnt == 0) + return(DFX_K_HW_TIMEOUT); + + /* + * If the address of host_data is non-zero, assume caller has supplied a + * non NULL pointer, and return the contents of the HOST_DATA register in + * it. + */ + + if (host_data != NULL) + dfx_port_read_long(bp, PI_PDQ_K_REG_HOST_DATA, host_data); + return(DFX_K_SUCCESS); + } + + +/* + * ===================== + * = dfx_hw_adap_reset = + * ===================== + * + * Overview: + * Resets adapter + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * type - type of reset to perform + * + * Functional Description: + * Issue soft reset to adapter by writing to PDQ Port Reset + * register. Use incoming reset type to tell adapter what + * kind of reset operation to perform. + * + * Return Codes: + * None + * + * Assumptions: + * This routine merely issues a soft reset to the adapter. + * It is expected that after this routine returns, the caller + * will appropriately poll the Port Status register for the + * adapter to enter the proper state. + * + * Side Effects: + * Internal adapter registers are cleared. + */ + +void dfx_hw_adap_reset( + DFX_board_t *bp, + PI_UINT32 type + ) + + { + /* Set Reset type and assert reset */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_PORT_DATA_A, type); /* tell adapter type of reset */ + dfx_port_write_long(bp, PI_PDQ_K_REG_PORT_RESET, PI_RESET_M_ASSERT_RESET); + + /* Wait for at least 1 Microsecond according to the spec. We wait 20 just to be safe */ + + udelay(20); + + /* Deassert reset */ + + dfx_port_write_long(bp, PI_PDQ_K_REG_PORT_RESET, 0); + return; + } + + +/* + * ======================== + * = dfx_hw_adap_state_rd = + * ======================== + * + * Overview: + * Returns current adapter state + * + * Returns: + * Adapter state per PDQ Port Specification + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * Reads PDQ Port Status register and returns adapter state. + * + * Return Codes: + * None + * + * Assumptions: + * None + * + * Side Effects: + * None + */ + +int dfx_hw_adap_state_rd( + DFX_board_t *bp + ) + + { + PI_UINT32 port_status; /* Port Status register value */ + + dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &port_status); + return((port_status & PI_PSTATUS_M_STATE) >> PI_PSTATUS_V_STATE); + } + + +/* + * ===================== + * = dfx_hw_dma_uninit = + * ===================== + * + * Overview: + * Brings adapter to DMA_UNAVAILABLE state + * + * Returns: + * Condition code + * + * Arguments: + * bp - pointer to board information + * type - type of reset to perform + * + * Functional Description: + * Bring adapter to DMA_UNAVAILABLE state by performing the following: + * 1. Set reset type bit in Port Data A Register then reset adapter. + * 2. Check that adapter is in DMA_UNAVAILABLE state. + * + * Return Codes: + * DFX_K_SUCCESS - adapter is in DMA_UNAVAILABLE state + * DFX_K_HW_TIMEOUT - adapter did not reset properly + * + * Assumptions: + * None + * + * Side Effects: + * Internal adapter registers are cleared. + */ + +int dfx_hw_dma_uninit( + DFX_board_t *bp, + PI_UINT32 type + ) + + { + int timeout_cnt; /* used in for loops */ + + /* Set reset type bit and reset adapter */ + + dfx_hw_adap_reset(bp, type); + + /* Now wait for adapter to enter DMA_UNAVAILABLE state */ + + for (timeout_cnt = 100000; timeout_cnt > 0; timeout_cnt--) + { + if (dfx_hw_adap_state_rd(bp) == PI_STATE_K_DMA_UNAVAIL) + break; + udelay(100); /* wait for 100 microseconds */ + } + if (timeout_cnt == 0) + return(DFX_K_HW_TIMEOUT); + return(DFX_K_SUCCESS); + } + + +/* + * ================ + * = dfx_rcv_init = + * ================ + * + * Overview: + * Produces buffers to adapter LLC Host receive descriptor block + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * This routine can be called during dfx_adap_init() or during an adapter + * reset. It initializes the descriptor block and produces all allocated + * LLC Host queue receive buffers. + * + * Return Codes: + * None + * + * Assumptions: + * The PDQ has been reset and the adapter and driver maintained Type 2 + * register indices are cleared. + * + * Side Effects: + * Receive buffers are posted to the adapter LLC queue and the adapter + * is notified. + */ + +void dfx_rcv_init( + DFX_board_t *bp + ) + + { + int i, j; /* used in for loop */ + + /* + * Since each receive buffer is a single fragment of same length, initialize + * first longword in each receive descriptor for entire LLC Host descriptor + * block. Also initialize second longword in each receive descriptor with + * physical address of receive buffer. We'll always allocate receive + * buffers in powers of 2 so that we can easily fill the 256 entry descriptor + * block and produce new receive buffers by simply updating the receive + * producer index. + * + * Assumptions: + * To support all shipping versions of PDQ, the receive buffer size + * must be mod 128 in length and the physical address must be 128 byte + * aligned. In other words, bits 0-6 of the length and address must + * be zero for the following descriptor field entries to be correct on + * all PDQ-based boards. We guaranteed both requirements during + * driver initialization when we allocated memory for the receive buffers. + */ + + for (i=0; i < (int)(bp->rcv_bufs_to_post); i++) + for (j=0; (i + j) < (int)PI_RCV_DATA_K_NUM_ENTRIES; j += bp->rcv_bufs_to_post) + { + bp->descr_block_virt->rcv_data[i+j].long_0 = (u32) (PI_RCV_DESCR_M_SOP | + ((PI_RCV_DATA_K_SIZE_MAX / PI_ALIGN_K_RCV_DATA_BUFF) << PI_RCV_DESCR_V_SEG_LEN)); + bp->descr_block_virt->rcv_data[i+j].long_1 = (u32) (bp->rcv_block_phys + (i * PI_RCV_DATA_K_SIZE_MAX)); + bp->p_rcv_buff_va[i+j] = (char *) (bp->rcv_block_virt + (i * PI_RCV_DATA_K_SIZE_MAX)); + } + + /* Update receive producer and Type 2 register */ + + bp->rcv_xmt_reg.index.rcv_prod = bp->rcv_bufs_to_post; + dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_2_PROD, bp->rcv_xmt_reg.lword); + return; + } + + +/* + * ========================= + * = dfx_rcv_queue_process = + * ========================= + * + * Overview: + * Process received LLC frames. + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * Received LLC frames are processed until there are no more consumed frames. + * Once all frames are processed, the receive buffers are returned to the + * adapter. Note that this algorithm fixes the length of time that can be spent + * in this routine, because there are a fixed number of receive buffers to + * process and buffers are not produced until this routine exits and returns + * to the ISR. + * + * Return Codes: + * None + * + * Assumptions: + * None + * + * Side Effects: + * None + */ + +void dfx_rcv_queue_process( + DFX_board_t *bp + ) + + { + PI_TYPE_2_CONSUMER *p_type_2_cons; /* ptr to rcv/xmt consumer block register */ + char *p_buff; /* ptr to start of packet receive buffer (FMC descriptor) */ + u32 descr, pkt_len; /* FMC descriptor field and packet length */ + struct sk_buff *skb; /* pointer to a sk_buff to hold incoming packet data */ + + /* Service all consumed LLC receive frames */ + + p_type_2_cons = (PI_TYPE_2_CONSUMER *)(&bp->cons_block_virt->xmt_rcv_data); + while (bp->rcv_xmt_reg.index.rcv_comp != p_type_2_cons->index.rcv_cons) + { + /* Process any errors */ + + p_buff = (char *) bp->p_rcv_buff_va[bp->rcv_xmt_reg.index.rcv_comp]; + memcpy(&descr, p_buff + RCV_BUFF_K_DESCR, sizeof(u32)); + + if (descr & PI_FMC_DESCR_M_RCC_FLUSH) + { + if (descr & PI_FMC_DESCR_M_RCC_CRC) + bp->rcv_crc_errors++; + else + bp->rcv_frame_status_errors++; + } + else + { + /* The frame was received without errors - verify packet length */ + + pkt_len = (u32)((descr & PI_FMC_DESCR_M_LEN) >> PI_FMC_DESCR_V_LEN); + pkt_len -= 4; /* subtract 4 byte CRC */ + if (!IN_RANGE(pkt_len, FDDI_K_LLC_ZLEN, FDDI_K_LLC_LEN)) + bp->rcv_length_errors++; + else + { + skb = dev_alloc_skb(pkt_len+3); /* alloc new buffer to pass up, add room for PRH */ + if (skb == NULL) + { + printk("%s: Could not allocate receive buffer. Dropping packet.\n", bp->dev->name); + bp->rcv_discards++; + } + else + { + /* Receive buffer allocated, pass receive packet up */ + + memcpy(skb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3); + skb->data += 3; /* adjust data field so that it points to FC byte */ + skb->len = pkt_len; /* pass up packet length, NOT including CRC */ + skb->dev = bp->dev; /* pass up device pointer */ + skb->protocol = fddi_type_trans(skb, bp->dev); + netif_rx(skb); + + /* Update the rcv counters */ + + bp->rcv_total_frames++; + if (*(p_buff + RCV_BUFF_K_DA) & 0x01) + bp->rcv_multicast_frames++; + } + } + } + + /* + * Advance the producer (for recycling) and advance the completion + * (for servicing received frames). Note that it is okay to + * advance the producer without checking that it passes the + * completion index because they are both advanced at the same + * rate. + */ + + bp->rcv_xmt_reg.index.rcv_prod += 1; + bp->rcv_xmt_reg.index.rcv_comp += 1; + } + return; + } + + +/* + * ===================== + * = dfx_xmt_queue_pkt = + * ===================== + * + * Overview: + * Queues packets for transmission + * + * Returns: + * Condition code + * + * Arguments: + * skb - pointer to sk_buff to queue for transmission + * dev - pointer to device information + * + * Functional Description: + * Here we assume that an incoming skb transmit request + * is contained in a single physically contiguous buffer + * in which the virtual address of the start of packet + * (skb->data) can be converted to a physical address + * by using virt_to_bus(). + * + * Since the adapter architecture requires a three byte + * packet request header to prepend the start of packet, + * we'll write the three byte field immediately prior to + * the FC byte. This assumption is valid because we've + * ensured that dev->hard_header_len includes three pad + * bytes. By posting a single fragment to the adapter, + * we'll reduce the number of descriptor fetches and + * bus traffic needed to send the request. + * + * Also, we can't free the skb until after it's been DMA'd + * out by the adapter, so we'll queue it in the driver and + * return it in dfx_xmt_done. + * + * Return Codes: + * 0 - driver queued packet, link is unavailable, or skbuff was bad + * 1 - caller should requeue the sk_buff for later transmission + * + * Assumptions: + * First and foremost, we assume the incoming skb pointer + * is NOT NULL and is pointing to a valid sk_buff structure. + * + * The outgoing packet is complete, starting with the + * frame control byte including the last byte of data, + * but NOT including the 4 byte CRC. We'll let the + * adapter hardware generate and append the CRC. + * + * The entire packet is stored in one physically + * contiguous buffer which is not cached and whose + * 32-bit physical address can be determined. + * + * It's vital that this routine is NOT reentered for the + * same board and that the OS is not in another section of + * code (eg. dfx_int_common) for the same board on a + * different thread. + * + * Side Effects: + * None + */ + +int dfx_xmt_queue_pkt( + struct sk_buff *skb, + struct device *dev + ) + + { + DFX_board_t *bp = (DFX_board_t *) dev->priv; + u8 prod; /* local transmit producer index */ + PI_XMT_DESCR *p_xmt_descr; /* ptr to transmit descriptor block entry */ + XMT_DRIVER_DESCR *p_xmt_drv_descr; /* ptr to transmit driver descriptor */ + + /* + * Verify that incoming transmit request is OK + * + * Note: The packet size check is consistent with other + * Linux device drivers, although the correct packet + * size should be verified before calling the + * transmit routine. + */ + + if (!IN_RANGE(skb->len, FDDI_K_LLC_ZLEN, FDDI_K_LLC_LEN)) + { + printk("%s: Invalid packet length - %lu bytes\n", dev->name, skb->len); + bp->xmt_length_errors++; /* bump error counter */ + dev_tint(dev); /* dequeue packets from xmt queue and send them */ + return(0); /* return "success" */ + } + + /* + * See if adapter link is available, if not, free buffer + * + * Note: If the link isn't available, free buffer and return 0 + * rather than tell the upper layer to requeue the packet. + * The methodology here is that by the time the link + * becomes available, the packet to be sent will be + * fairly stale. By simply dropping the packet, the + * higher layer protocols will eventually time out + * waiting for response packets which it won't receive. + */ + + if (bp->link_available == PI_K_FALSE) + { + if (dfx_hw_adap_state_rd(bp) == PI_STATE_K_LINK_AVAIL) /* is link really available? */ + bp->link_available = PI_K_TRUE; /* if so, set flag and continue */ + else + { + bp->xmt_discards++; /* bump error counter */ + dev_kfree_skb(skb, FREE_WRITE); /* free sk_buff now */ + return(0); /* return "success" */ + } + } + + /* Get the current producer and the next free xmt data descriptor */ + + prod = bp->rcv_xmt_reg.index.xmt_prod; + p_xmt_descr = &(bp->descr_block_virt->xmt_data[prod]); + + /* + * Get pointer to auxiliary queue entry to contain information for this packet. + * + * Note: The current xmt producer index will become the current xmt completion + * index when we complete this packet later on. So, we'll get the + * pointer to the next auxiliary queue entry now before we bump the + * producer index. + */ + + p_xmt_drv_descr = &(bp->xmt_drv_descr_blk[prod++]); /* also bump producer index */ + + /* Write the three PRH bytes immediately before the FC byte */ + + *((char *)skb->data - 3) = DFX_PRH0_BYTE; /* these byte values are defined */ + *((char *)skb->data - 2) = DFX_PRH1_BYTE; /* in the Motorola FDDI MAC chip */ + *((char *)skb->data - 1) = DFX_PRH2_BYTE; /* specification */ + + /* + * Write the descriptor with buffer info and bump producer + * + * Note: Since we need to start DMA from the packet request + * header, we'll add 3 bytes to the DMA buffer length, + * and we'll determine the physical address of the + * buffer from the PRH, not skb->data. + * + * Assumptions: + * 1. Packet starts with the frame control (FC) byte + * at skb->data. + * 2. The 4-byte CRC is not appended to the buffer or + * included in the length. + * 3. Packet length (skb->len) is from FC to end of + * data, inclusive. + * 4. The packet length does not exceed the maximum + * FDDI LLC frame length of 4491 bytes. + * 5. The entire packet is contained in a physically + * contiguous, non-cached, locked memory space + * comprised of a single buffer pointed to by + * skb->data. + * 6. The physical address of the start of packet + * can be determined from the virtual address + * by using virt_to_bus() and is only 32-bits + * wide. + */ + + p_xmt_descr->long_0 = (u32) (PI_XMT_DESCR_M_SOP | PI_XMT_DESCR_M_EOP | ((skb->len + 3) << PI_XMT_DESCR_V_SEG_LEN)); + p_xmt_descr->long_1 = (u32) virt_to_bus(skb->data - 3); + + /* + * Verify that descriptor is actually available + * + * Note: If descriptor isn't available, return 1 which tells + * the upper layer to requeue the packet for later + * transmission. + * + * We need to ensure that the producer never reaches the + * completion, except to indicate that the queue is empty. + */ + + if (prod == bp->rcv_xmt_reg.index.xmt_comp) + return(1); /* requeue packet for later */ + + /* + * Save info for this packet for xmt done indication routine + * + * Normally, we'd save the producer index in the p_xmt_drv_descr + * structure so that we'd have it handy when we complete this + * packet later (in dfx_xmt_done). However, since the current + * transmit architecture guarantees a single fragment for the + * entire packet, we can simply bump the completion index by + * one (1) for each completed packet. + * + * Note: If this assumption changes and we're presented with + * an inconsistent number of transmit fragments for packet + * data, we'll need to modify this code to save the current + * transmit producer index. + */ + + p_xmt_drv_descr->p_skb = skb; + + /* Update Type 2 register */ + + bp->rcv_xmt_reg.index.xmt_prod = prod; + dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_2_PROD, bp->rcv_xmt_reg.lword); + return(0); /* packet queued to adapter */ + } + + +/* + * ================ + * = dfx_xmt_done = + * ================ + * + * Overview: + * Processes all frames that have been transmitted. + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * For all consumed transmit descriptors that have not + * yet been completed, we'll free the skb we were holding + * onto using dev_kfree_skb and bump the appropriate + * counters. + * + * Return Codes: + * None + * + * Assumptions: + * The Type 2 register is not updated in this routine. It is + * assumed that it will be updated in the ISR when dfx_xmt_done + * returns. + * + * Side Effects: + * None + */ + +void dfx_xmt_done( + DFX_board_t *bp + ) + + { + XMT_DRIVER_DESCR *p_xmt_drv_descr; /* ptr to transmit driver descriptor */ + PI_TYPE_2_CONSUMER *p_type_2_cons; /* ptr to rcv/xmt consumer block register */ + + /* Service all consumed transmit frames */ + + p_type_2_cons = (PI_TYPE_2_CONSUMER *)(&bp->cons_block_virt->xmt_rcv_data); + while (bp->rcv_xmt_reg.index.xmt_comp != p_type_2_cons->index.xmt_cons) + { + /* Get pointer to the transmit driver descriptor block information */ + + p_xmt_drv_descr = &(bp->xmt_drv_descr_blk[bp->rcv_xmt_reg.index.xmt_comp]); + + /* Return skb to operating system */ + + dev_kfree_skb(p_xmt_drv_descr->p_skb, FREE_WRITE); + + /* Increment transmit counters */ + + bp->xmt_total_frames++; + + /* + * Move to start of next packet by updating completion index + * + * Here we assume that a transmit packet request is always + * serviced by posting one fragment. We can therefore + * simplify the completion code by incrementing the + * completion index by one. This code will need to be + * modified if this assumption changes. See comments + * in dfx_xmt_queue_pkt for more details. + */ + + bp->rcv_xmt_reg.index.xmt_comp += 1; + } + return; + } + + +/* + * ================= + * = dfx_xmt_flush = + * ================= + * + * Overview: + * Processes all frames whether they've been transmitted + * or not. + * + * Returns: + * None + * + * Arguments: + * bp - pointer to board information + * + * Functional Description: + * For all produced transmit descriptors that have not + * yet been completed, we'll free the skb we were holding + * onto using dev_kfree_skb and bump the appropriate + * counters. Of course, it's possible that some of + * these transmit requests actually did go out, but we + * won't make that distinction here. Finally, we'll + * update the consumer index to match the producer. + * + * Return Codes: + * None + * + * Assumptions: + * This routine does NOT update the Type 2 register. It + * is assumed that this routine is being called during a + * transmit flush interrupt, or a shutdown or close routine. + * + * Side Effects: + * None + */ + +void dfx_xmt_flush( + DFX_board_t *bp + ) + + { + u32 prod_cons; /* rcv/xmt consumer block longword */ + XMT_DRIVER_DESCR *p_xmt_drv_descr; /* ptr to transmit driver descriptor */ + + /* Flush all outstanding transmit frames */ + + while (bp->rcv_xmt_reg.index.xmt_comp != bp->rcv_xmt_reg.index.xmt_prod) + { + /* Get pointer to the transmit driver descriptor block information */ + + p_xmt_drv_descr = &(bp->xmt_drv_descr_blk[bp->rcv_xmt_reg.index.xmt_comp]); + + /* Return skb to operating system */ + + dev_kfree_skb(p_xmt_drv_descr->p_skb, FREE_WRITE); + + /* Increment transmit error counter */ + + bp->xmt_discards++; + + /* + * Move to start of next packet by updating completion index + * + * Here we assume that a transmit packet request is always + * serviced by posting one fragment. We can therefore + * simplify the completion code by incrementing the + * completion index by one. This code will need to be + * modified if this assumption changes. See comments + * in dfx_xmt_queue_pkt for more details. + */ + + bp->rcv_xmt_reg.index.xmt_comp += 1; + } + + /* Update the transmit consumer index in the consumer block */ + + prod_cons = (u32)(bp->cons_block_virt->xmt_rcv_data & ~PI_CONS_M_XMT_INDEX); + prod_cons |= (u32)(bp->rcv_xmt_reg.index.xmt_prod << PI_CONS_V_XMT_INDEX); + bp->cons_block_virt->xmt_rcv_data = prod_cons; + return; + } + + +/* + * Local variables: + * kernel-compile-command: "gcc -D__KERNEL__ -I/root/linux/include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586 -c defxx.c" + * End: + */ diff -ur --new-file old/linux/drivers/net/defxx.h new/linux/drivers/net/defxx.h --- old/linux/drivers/net/defxx.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/defxx.h Wed Oct 30 02:42:40 1996 @@ -0,0 +1,1780 @@ +/* + * File Name: + * defxx.h + * + * Copyright Information: + * Copyright Digital Equipment Corporation 1996. + * + * This software may be used and distributed according to the terms of + * the GNU Public License, incorporated herein by reference. + * + * Abstract: + * Contains all definitions specified by port specification and required + * by the defxx.c driver. + * + * Maintainers: + * LVS Lawrence V. Stefani + * + * Contact: + * The author may be reached at: + * + * Inet: stefani@lkg.dec.com + * Mail: Digital Equipment Corporation + * 550 King Street + * M/S: LKG1-3/M07 + * Littleton, MA 01460 + * + * Modification History: + * Date Name Description + * 16-Aug-96 LVS Created. + * 09-Sep-96 LVS Added group_prom field. Moved read/write I/O + * macros to DEFXX.C. + * 12-Sep-96 LVS Removed packet request header pointers. + */ + +#ifndef _DEFXX_H_ +#define _DEFXX_H_ + +/* Define basic types for unsigned chars, shorts, longs */ + +typedef u8 PI_UINT8; +typedef u16 PI_UINT16; +typedef u32 PI_UINT32; + +/* Define general structures */ + +typedef struct /* 64-bit counter */ + { + PI_UINT32 ms; + PI_UINT32 ls; + } PI_CNTR; + +typedef struct /* LAN address */ + { + PI_UINT32 lwrd_0; + PI_UINT32 lwrd_1; + } PI_LAN_ADDR; + +typedef struct /* Station ID address */ + { + PI_UINT32 octet_7_4; + PI_UINT32 octet_3_0; + } PI_STATION_ID; + + +/* Define general constants */ + +#define PI_ALIGN_K_DESC_BLK 8192 /* Descriptor block boundary */ +#define PI_ALIGN_K_CONS_BLK 64 /* Consumer block boundary */ +#define PI_ALIGN_K_CMD_REQ_BUFF 128 /* Xmt Command que buffer alignment */ +#define PI_ALIGN_K_CMD_RSP_BUFF 128 /* Rcv Command que buffer alignment */ +#define PI_ALIGN_K_UNSOL_BUFF 128 /* Unsol que buffer alignment */ +#define PI_ALIGN_K_XMT_DATA_BUFF 0 /* Xmt data que buffer alignment */ +#define PI_ALIGN_K_RCV_DATA_BUFF 128 /* Rcv que buffer alignment */ + +/* Define PHY index values */ + +#define PI_PHY_K_S 0 /* Index to S phy */ +#define PI_PHY_K_A 0 /* Index to A phy */ +#define PI_PHY_K_B 1 /* Index to B phy */ +#define PI_PHY_K_MAX 2 /* Max number of phys */ + +/* Define FMC descriptor fields */ + +#define PI_FMC_DESCR_V_SOP 31 +#define PI_FMC_DESCR_V_EOP 30 +#define PI_FMC_DESCR_V_FSC 27 +#define PI_FMC_DESCR_V_FSB_ERROR 26 +#define PI_FMC_DESCR_V_FSB_ADDR_RECOG 25 +#define PI_FMC_DESCR_V_FSB_ADDR_COPIED 24 +#define PI_FMC_DESCR_V_FSB 22 +#define PI_FMC_DESCR_V_RCC_FLUSH 21 +#define PI_FMC_DESCR_V_RCC_CRC 20 +#define PI_FMC_DESCR_V_RCC_RRR 17 +#define PI_FMC_DESCR_V_RCC_DD 15 +#define PI_FMC_DESCR_V_RCC_SS 13 +#define PI_FMC_DESCR_V_RCC 13 +#define PI_FMC_DESCR_V_LEN 0 + +#define PI_FMC_DESCR_M_SOP 0x80000000 +#define PI_FMC_DESCR_M_EOP 0x40000000 +#define PI_FMC_DESCR_M_FSC 0x38000000 +#define PI_FMC_DESCR_M_FSB_ERROR 0x04000000 +#define PI_FMC_DESCR_M_FSB_ADDR_RECOG 0x02000000 +#define PI_FMC_DESCR_M_FSB_ADDR_COPIED 0x01000000 +#define PI_FMC_DESCR_M_FSB 0x07C00000 +#define PI_FMC_DESCR_M_RCC_FLUSH 0x00200000 +#define PI_FMC_DESCR_M_RCC_CRC 0x00100000 +#define PI_FMC_DESCR_M_RCC_RRR 0x000E0000 +#define PI_FMC_DESCR_M_RCC_DD 0x00018000 +#define PI_FMC_DESCR_M_RCC_SS 0x00006000 +#define PI_FMC_DESCR_M_RCC 0x003FE000 +#define PI_FMC_DESCR_M_LEN 0x00001FFF + +#define PI_FMC_DESCR_K_RCC_FMC_INT_ERR 0x01AA + +#define PI_FMC_DESCR_K_RRR_SUCCESS 0x00 +#define PI_FMC_DESCR_K_RRR_SA_MATCH 0x01 +#define PI_FMC_DESCR_K_RRR_DA_MATCH 0x02 +#define PI_FMC_DESCR_K_RRR_FMC_ABORT 0x03 +#define PI_FMC_DESCR_K_RRR_LENGTH_BAD 0x04 +#define PI_FMC_DESCR_K_RRR_FRAGMENT 0x05 +#define PI_FMC_DESCR_K_RRR_FORMAT_ERR 0x06 +#define PI_FMC_DESCR_K_RRR_MAC_RESET 0x07 + +#define PI_FMC_DESCR_K_DD_NO_MATCH 0x0 +#define PI_FMC_DESCR_K_DD_PROMISCUOUS 0x1 +#define PI_FMC_DESCR_K_DD_CAM_MATCH 0x2 +#define PI_FMC_DESCR_K_DD_LOCAL_MATCH 0x3 + +#define PI_FMC_DESCR_K_SS_NO_MATCH 0x0 +#define PI_FMC_DESCR_K_SS_BRIDGE_MATCH 0x1 +#define PI_FMC_DESCR_K_SS_NOT_POSSIBLE 0x2 +#define PI_FMC_DESCR_K_SS_LOCAL_MATCH 0x3 + +/* Define some max buffer sizes */ + +#define PI_CMD_REQ_K_SIZE_MAX 512 +#define PI_CMD_RSP_K_SIZE_MAX 512 +#define PI_UNSOL_K_SIZE_MAX 512 +#define PI_SMT_HOST_K_SIZE_MAX 4608 /* 4 1/2 K */ +#define PI_RCV_DATA_K_SIZE_MAX 4608 /* 4 1/2 K */ +#define PI_XMT_DATA_K_SIZE_MAX 4608 /* 4 1/2 K */ + +/* Define adapter states */ + +#define PI_STATE_K_RESET 0 +#define PI_STATE_K_UPGRADE 1 +#define PI_STATE_K_DMA_UNAVAIL 2 +#define PI_STATE_K_DMA_AVAIL 3 +#define PI_STATE_K_LINK_AVAIL 4 +#define PI_STATE_K_LINK_UNAVAIL 5 +#define PI_STATE_K_HALTED 6 +#define PI_STATE_K_RING_MEMBER 7 +#define PI_STATE_K_NUMBER 8 + +/* Define codes for command type */ + +#define PI_CMD_K_START 0x00 +#define PI_CMD_K_FILTERS_SET 0x01 +#define PI_CMD_K_FILTERS_GET 0x02 +#define PI_CMD_K_CHARS_SET 0x03 +#define PI_CMD_K_STATUS_CHARS_GET 0x04 +#define PI_CMD_K_CNTRS_GET 0x05 +#define PI_CMD_K_CNTRS_SET 0x06 +#define PI_CMD_K_ADDR_FILTER_SET 0x07 +#define PI_CMD_K_ADDR_FILTER_GET 0x08 +#define PI_CMD_K_ERROR_LOG_CLEAR 0x09 +#define PI_CMD_K_ERROR_LOG_GET 0x0A +#define PI_CMD_K_FDDI_MIB_GET 0x0B +#define PI_CMD_K_DEC_EXT_MIB_GET 0x0C +#define PI_CMD_K_DEVICE_SPECIFIC_GET 0x0D +#define PI_CMD_K_SNMP_SET 0x0E +#define PI_CMD_K_UNSOL_TEST 0x0F +#define PI_CMD_K_SMT_MIB_GET 0x10 +#define PI_CMD_K_SMT_MIB_SET 0x11 +#define PI_CMD_K_MAX 0x11 /* Must match last */ + +/* Define item codes for Chars_Set and Filters_Set commands */ + +#define PI_ITEM_K_EOL 0x00 /* End-of-Item list */ +#define PI_ITEM_K_T_REQ 0x01 /* DECnet T_REQ */ +#define PI_ITEM_K_TVX 0x02 /* DECnet TVX */ +#define PI_ITEM_K_RESTRICTED_TOKEN 0x03 /* DECnet Restricted Token */ +#define PI_ITEM_K_LEM_THRESHOLD 0x04 /* DECnet LEM Threshold */ +#define PI_ITEM_K_RING_PURGER 0x05 /* DECnet Ring Purger Enable */ +#define PI_ITEM_K_CNTR_INTERVAL 0x06 /* Chars_Set */ +#define PI_ITEM_K_IND_GROUP_PROM 0x07 /* Filters_Set */ +#define PI_ITEM_K_GROUP_PROM 0x08 /* Filters_Set */ +#define PI_ITEM_K_BROADCAST 0x09 /* Filters_Set */ +#define PI_ITEM_K_SMT_PROM 0x0A /* Filters_Set */ +#define PI_ITEM_K_SMT_USER 0x0B /* Filters_Set */ +#define PI_ITEM_K_RESERVED 0x0C /* Filters_Set */ +#define PI_ITEM_K_IMPLEMENTOR 0x0D /* Filters_Set */ +#define PI_ITEM_K_LOOPBACK_MODE 0x0E /* Chars_Set */ +#define PI_ITEM_K_CONFIG_POLICY 0x10 /* SMTConfigPolicy */ +#define PI_ITEM_K_CON_POLICY 0x11 /* SMTConnectionPolicy */ +#define PI_ITEM_K_T_NOTIFY 0x12 /* SMTTNotify */ +#define PI_ITEM_K_STATION_ACTION 0x13 /* SMTStationAction */ +#define PI_ITEM_K_MAC_PATHS_REQ 0x15 /* MACPathsRequested */ +#define PI_ITEM_K_MAC_ACTION 0x17 /* MACAction */ +#define PI_ITEM_K_CON_POLICIES 0x18 /* PORTConnectionPolicies */ +#define PI_ITEM_K_PORT_PATHS_REQ 0x19 /* PORTPathsRequested */ +#define PI_ITEM_K_MAC_LOOP_TIME 0x1A /* PORTMACLoopTime */ +#define PI_ITEM_K_TB_MAX 0x1B /* PORTTBMax */ +#define PI_ITEM_K_LER_CUTOFF 0x1C /* PORTLerCutoff */ +#define PI_ITEM_K_LER_ALARM 0x1D /* PORTLerAlarm */ +#define PI_ITEM_K_PORT_ACTION 0x1E /* PORTAction */ +#define PI_ITEM_K_FLUSH_TIME 0x20 /* Chars_Set */ +#define PI_ITEM_K_MAC_T_REQ 0x29 /* MACTReq */ +#define PI_ITEM_K_EMAC_RING_PURGER 0x2A /* eMACRingPurgerEnable */ +#define PI_ITEM_K_EMAC_RTOKEN_TIMEOUT 0x2B /* eMACRestrictedTokenTimeout */ +#define PI_ITEM_K_FDX_ENB_DIS 0x2C /* eFDXEnable */ +#define PI_ITEM_K_MAX 0x2C /* Must equal high item */ + +/* Values for some of the items */ + +#define PI_K_FALSE 0 /* Generic false */ +#define PI_K_TRUE 1 /* Generic true */ + +#define PI_SNMP_K_TRUE 1 /* SNMP true/false values */ +#define PI_SNMP_K_FALSE 2 + +#define PI_FSTATE_K_BLOCK 0 /* Filter State */ +#define PI_FSTATE_K_PASS 1 + +/* Define command return codes */ + +#define PI_RSP_K_SUCCESS 0x00 +#define PI_RSP_K_FAILURE 0x01 +#define PI_RSP_K_WARNING 0x02 +#define PI_RSP_K_LOOP_MODE_BAD 0x03 +#define PI_RSP_K_ITEM_CODE_BAD 0x04 +#define PI_RSP_K_TVX_BAD 0x05 +#define PI_RSP_K_TREQ_BAD 0x06 +#define PI_RSP_K_TOKEN_BAD 0x07 +#define PI_RSP_K_NO_EOL 0x0C +#define PI_RSP_K_FILTER_STATE_BAD 0x0D +#define PI_RSP_K_CMD_TYPE_BAD 0x0E +#define PI_RSP_K_ADAPTER_STATE_BAD 0x0F +#define PI_RSP_K_RING_PURGER_BAD 0x10 +#define PI_RSP_K_LEM_THRESHOLD_BAD 0x11 +#define PI_RSP_K_LOOP_NOT_SUPPORTED 0x12 +#define PI_RSP_K_FLUSH_TIME_BAD 0x13 +#define PI_RSP_K_NOT_IMPLEMENTED 0x14 +#define PI_RSP_K_CONFIG_POLICY_BAD 0x15 +#define PI_RSP_K_STATION_ACTION_BAD 0x16 +#define PI_RSP_K_MAC_ACTION_BAD 0x17 +#define PI_RSP_K_CON_POLICIES_BAD 0x18 +#define PI_RSP_K_MAC_LOOP_TIME_BAD 0x19 +#define PI_RSP_K_TB_MAX_BAD 0x1A +#define PI_RSP_K_LER_CUTOFF_BAD 0x1B +#define PI_RSP_K_LER_ALARM_BAD 0x1C +#define PI_RSP_K_MAC_PATHS_REQ_BAD 0x1D +#define PI_RSP_K_MAC_T_REQ_BAD 0x1E +#define PI_RSP_K_EMAC_RING_PURGER_BAD 0x1F +#define PI_RSP_K_EMAC_RTOKEN_TIME_BAD 0x20 +#define PI_RSP_K_NO_SUCH_ENTRY 0x21 +#define PI_RSP_K_T_NOTIFY_BAD 0x22 +#define PI_RSP_K_TR_MAX_EXP_BAD 0x23 +#define PI_RSP_K_MAC_FRM_ERR_THR_BAD 0x24 +#define PI_RSP_K_MAX_T_REQ_BAD 0x25 +#define PI_RSP_K_FDX_ENB_DIS_BAD 0x26 +#define PI_RSP_K_ITEM_INDEX_BAD 0x27 +#define PI_RSP_K_PORT_ACTION_BAD 0x28 + +/* Commonly used structures */ + +typedef struct /* Item list */ + { + PI_UINT32 item_code; + PI_UINT32 value; + } PI_ITEM_LIST; + +typedef struct /* Response header */ + { + PI_UINT32 reserved; + PI_UINT32 cmd_type; + PI_UINT32 status; + } PI_RSP_HEADER; + + +/* Start Command */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_START_REQ; + +/* Start Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_START_RSP; + +/* Filters_Set Request */ + +#define PI_CMD_FILTERS_SET_K_ITEMS_MAX 63 /* Fits in a 512 byte buffer */ + +typedef struct + { + PI_UINT32 cmd_type; + PI_ITEM_LIST item[PI_CMD_FILTERS_SET_K_ITEMS_MAX]; + } PI_CMD_FILTERS_SET_REQ; + +/* Filters_Set Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_FILTERS_SET_RSP; + +/* Filters_Get Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_FILTERS_GET_REQ; + +/* Filters_Get Response */ + +typedef struct + { + PI_RSP_HEADER header; + PI_UINT32 ind_group_prom; + PI_UINT32 group_prom; + PI_UINT32 broadcast_all; + PI_UINT32 smt_all; + PI_UINT32 smt_user; + PI_UINT32 reserved_all; + PI_UINT32 implementor_all; + } PI_CMD_FILTERS_GET_RSP; + + +/* Chars_Set Request */ + +#define PI_CMD_CHARS_SET_K_ITEMS_MAX 42 /* Fits in a 512 byte buffer */ + +typedef struct + { + PI_UINT32 cmd_type; + struct /* Item list */ + { + PI_UINT32 item_code; + PI_UINT32 value; + PI_UINT32 item_index; + } item[PI_CMD_CHARS_SET_K_ITEMS_MAX]; + } PI_CMD_CHARS_SET_REQ; + +/* Chars_Set Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_CHARS_SET_RSP; + + +/* SNMP_Set Request */ + +#define PI_CMD_SNMP_SET_K_ITEMS_MAX 42 /* Fits in a 512 byte buffer */ + +typedef struct + { + PI_UINT32 cmd_type; + struct /* Item list */ + { + PI_UINT32 item_code; + PI_UINT32 value; + PI_UINT32 item_index; + } item[PI_CMD_SNMP_SET_K_ITEMS_MAX]; + } PI_CMD_SNMP_SET_REQ; + +/* SNMP_Set Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_SNMP_SET_RSP; + + +/* SMT_MIB_Set Request */ + +#define PI_CMD_SMT_MIB_SET_K_ITEMS_MAX 42 /* Max number of items */ + +typedef struct + { + PI_UINT32 cmd_type; + struct + { + PI_UINT32 item_code; + PI_UINT32 value; + PI_UINT32 item_index; + } item[PI_CMD_SMT_MIB_SET_K_ITEMS_MAX]; + } PI_CMD_SMT_MIB_SET_REQ; + +/* SMT_MIB_Set Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_SMT_MIB_SET_RSP; + +/* SMT_MIB_Get Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_SMT_MIB_GET_REQ; + +/* SMT_MIB_Get Response */ + +typedef struct /* Refer to ANSI FDDI SMT Rev. 7.3 */ + { + PI_RSP_HEADER header; + + /* SMT GROUP */ + + PI_STATION_ID smt_station_id; + PI_UINT32 smt_op_version_id; + PI_UINT32 smt_hi_version_id; + PI_UINT32 smt_lo_version_id; + PI_UINT32 smt_user_data[8]; + PI_UINT32 smt_mib_version_id; + PI_UINT32 smt_mac_ct; + PI_UINT32 smt_non_master_ct; + PI_UINT32 smt_master_ct; + PI_UINT32 smt_available_paths; + PI_UINT32 smt_config_capabilities; + PI_UINT32 smt_config_policy; + PI_UINT32 smt_connection_policy; + PI_UINT32 smt_t_notify; + PI_UINT32 smt_stat_rpt_policy; + PI_UINT32 smt_trace_max_expiration; + PI_UINT32 smt_bypass_present; + PI_UINT32 smt_ecm_state; + PI_UINT32 smt_cf_state; + PI_UINT32 smt_remote_disconnect_flag; + PI_UINT32 smt_station_status; + PI_UINT32 smt_peer_wrap_flag; + PI_CNTR smt_msg_time_stamp; + PI_CNTR smt_transition_time_stamp; + + /* MAC GROUP */ + + PI_UINT32 mac_frame_status_functions; + PI_UINT32 mac_t_max_capability; + PI_UINT32 mac_tvx_capability; + PI_UINT32 mac_available_paths; + PI_UINT32 mac_current_path; + PI_LAN_ADDR mac_upstream_nbr; + PI_LAN_ADDR mac_downstream_nbr; + PI_LAN_ADDR mac_old_upstream_nbr; + PI_LAN_ADDR mac_old_downstream_nbr; + PI_UINT32 mac_dup_address_test; + PI_UINT32 mac_requested_paths; + PI_UINT32 mac_downstream_port_type; + PI_LAN_ADDR mac_smt_address; + PI_UINT32 mac_t_req; + PI_UINT32 mac_t_neg; + PI_UINT32 mac_t_max; + PI_UINT32 mac_tvx_value; + PI_UINT32 mac_frame_error_threshold; + PI_UINT32 mac_frame_error_ratio; + PI_UINT32 mac_rmt_state; + PI_UINT32 mac_da_flag; + PI_UINT32 mac_unda_flag; + PI_UINT32 mac_frame_error_flag; + PI_UINT32 mac_ma_unitdata_available; + PI_UINT32 mac_hardware_present; + PI_UINT32 mac_ma_unitdata_enable; + + /* PATH GROUP */ + + PI_UINT32 path_configuration[8]; + PI_UINT32 path_tvx_lower_bound; + PI_UINT32 path_t_max_lower_bound; + PI_UINT32 path_max_t_req; + + /* PORT GROUP */ + + PI_UINT32 port_my_type[PI_PHY_K_MAX]; + PI_UINT32 port_neighbor_type[PI_PHY_K_MAX]; + PI_UINT32 port_connection_policies[PI_PHY_K_MAX]; + PI_UINT32 port_mac_indicated[PI_PHY_K_MAX]; + PI_UINT32 port_current_path[PI_PHY_K_MAX]; + PI_UINT32 port_requested_paths[PI_PHY_K_MAX]; + PI_UINT32 port_mac_placement[PI_PHY_K_MAX]; + PI_UINT32 port_available_paths[PI_PHY_K_MAX]; + PI_UINT32 port_pmd_class[PI_PHY_K_MAX]; + PI_UINT32 port_connection_capabilities[PI_PHY_K_MAX]; + PI_UINT32 port_bs_flag[PI_PHY_K_MAX]; + PI_UINT32 port_ler_estimate[PI_PHY_K_MAX]; + PI_UINT32 port_ler_cutoff[PI_PHY_K_MAX]; + PI_UINT32 port_ler_alarm[PI_PHY_K_MAX]; + PI_UINT32 port_connect_state[PI_PHY_K_MAX]; + PI_UINT32 port_pcm_state[PI_PHY_K_MAX]; + PI_UINT32 port_pc_withhold[PI_PHY_K_MAX]; + PI_UINT32 port_ler_flag[PI_PHY_K_MAX]; + PI_UINT32 port_hardware_present[PI_PHY_K_MAX]; + + /* GROUP for things that were added later, so must be at the end. */ + + PI_CNTR path_ring_latency; + + } PI_CMD_SMT_MIB_GET_RSP; + + +/* + * Item and group code definitions for SMT 7.3 mandatory objects. These + * definitions are to be used as appropriate in SMT_MIB_SET commands and + * certain host-sent SMT frames such as PMF Get and Set requests. The + * codes have been taken from the MIB summary section of ANSI SMT 7.3. + */ + +#define PI_GRP_K_SMT_STATION_ID 0x100A +#define PI_ITEM_K_SMT_STATION_ID 0x100B +#define PI_ITEM_K_SMT_OP_VERS_ID 0x100D +#define PI_ITEM_K_SMT_HI_VERS_ID 0x100E +#define PI_ITEM_K_SMT_LO_VERS_ID 0x100F +#define PI_ITEM_K_SMT_USER_DATA 0x1011 +#define PI_ITEM_K_SMT_MIB_VERS_ID 0x1012 + +#define PI_GRP_K_SMT_STATION_CONFIG 0x1014 +#define PI_ITEM_K_SMT_MAC_CT 0x1015 +#define PI_ITEM_K_SMT_NON_MASTER_CT 0x1016 +#define PI_ITEM_K_SMT_MASTER_CT 0x1017 +#define PI_ITEM_K_SMT_AVAIL_PATHS 0x1018 +#define PI_ITEM_K_SMT_CONFIG_CAPS 0x1019 +#define PI_ITEM_K_SMT_CONFIG_POL 0x101A +#define PI_ITEM_K_SMT_CONN_POL 0x101B +#define PI_ITEM_K_SMT_T_NOTIFY 0x101D +#define PI_ITEM_K_SMT_STAT_POL 0x101E +#define PI_ITEM_K_SMT_TR_MAX_EXP 0x101F +#define PI_ITEM_K_SMT_PORT_INDEXES 0x1020 +#define PI_ITEM_K_SMT_MAC_INDEXES 0x1021 +#define PI_ITEM_K_SMT_BYPASS_PRESENT 0x1022 + +#define PI_GRP_K_SMT_STATUS 0x1028 +#define PI_ITEM_K_SMT_ECM_STATE 0x1029 +#define PI_ITEM_K_SMT_CF_STATE 0x102A +#define PI_ITEM_K_SMT_REM_DISC_FLAG 0x102C +#define PI_ITEM_K_SMT_STATION_STATUS 0x102D +#define PI_ITEM_K_SMT_PEER_WRAP_FLAG 0x102E + +#define PI_GRP_K_SMT_MIB_OPERATION 0x1032 +#define PI_ITEM_K_SMT_MSG_TIME_STAMP 0x1033 +#define PI_ITEM_K_SMT_TRN_TIME_STAMP 0x1034 + +#define PI_ITEM_K_SMT_STATION_ACT 0x103C + +#define PI_GRP_K_MAC_CAPABILITIES 0x200A +#define PI_ITEM_K_MAC_FRM_STAT_FUNC 0x200B +#define PI_ITEM_K_MAC_T_MAX_CAP 0x200D +#define PI_ITEM_K_MAC_TVX_CAP 0x200E + +#define PI_GRP_K_MAC_CONFIG 0x2014 +#define PI_ITEM_K_MAC_AVAIL_PATHS 0x2016 +#define PI_ITEM_K_MAC_CURRENT_PATH 0x2017 +#define PI_ITEM_K_MAC_UP_NBR 0x2018 +#define PI_ITEM_K_MAC_DOWN_NBR 0x2019 +#define PI_ITEM_K_MAC_OLD_UP_NBR 0x201A +#define PI_ITEM_K_MAC_OLD_DOWN_NBR 0x201B +#define PI_ITEM_K_MAC_DUP_ADDR_TEST 0x201D +#define PI_ITEM_K_MAC_REQ_PATHS 0x2020 +#define PI_ITEM_K_MAC_DOWN_PORT_TYPE 0x2021 +#define PI_ITEM_K_MAC_INDEX 0x2022 + +#define PI_GRP_K_MAC_ADDRESS 0x2028 +#define PI_ITEM_K_MAC_SMT_ADDRESS 0x2029 + +#define PI_GRP_K_MAC_OPERATION 0x2032 +#define PI_ITEM_K_MAC_TREQ 0x2033 +#define PI_ITEM_K_MAC_TNEG 0x2034 +#define PI_ITEM_K_MAC_TMAX 0x2035 +#define PI_ITEM_K_MAC_TVX_VALUE 0x2036 + +#define PI_GRP_K_MAC_COUNTERS 0x2046 +#define PI_ITEM_K_MAC_FRAME_CT 0x2047 +#define PI_ITEM_K_MAC_COPIED_CT 0x2048 +#define PI_ITEM_K_MAC_TRANSMIT_CT 0x2049 +#define PI_ITEM_K_MAC_ERROR_CT 0x2051 +#define PI_ITEM_K_MAC_LOST_CT 0x2052 + +#define PI_GRP_K_MAC_FRM_ERR_COND 0x205A +#define PI_ITEM_K_MAC_FRM_ERR_THR 0x205F +#define PI_ITEM_K_MAC_FRM_ERR_RAT 0x2060 + +#define PI_GRP_K_MAC_STATUS 0x206E +#define PI_ITEM_K_MAC_RMT_STATE 0x206F +#define PI_ITEM_K_MAC_DA_FLAG 0x2070 +#define PI_ITEM_K_MAC_UNDA_FLAG 0x2071 +#define PI_ITEM_K_MAC_FRM_ERR_FLAG 0x2072 +#define PI_ITEM_K_MAC_MA_UNIT_AVAIL 0x2074 +#define PI_ITEM_K_MAC_HW_PRESENT 0x2075 +#define PI_ITEM_K_MAC_MA_UNIT_ENAB 0x2076 + +#define PI_GRP_K_PATH_CONFIG 0x320A +#define PI_ITEM_K_PATH_INDEX 0x320B +#define PI_ITEM_K_PATH_CONFIGURATION 0x3212 +#define PI_ITEM_K_PATH_TVX_LB 0x3215 +#define PI_ITEM_K_PATH_T_MAX_LB 0x3216 +#define PI_ITEM_K_PATH_MAX_T_REQ 0x3217 + +#define PI_GRP_K_PORT_CONFIG 0x400A +#define PI_ITEM_K_PORT_MY_TYPE 0x400C +#define PI_ITEM_K_PORT_NBR_TYPE 0x400D +#define PI_ITEM_K_PORT_CONN_POLS 0x400E +#define PI_ITEM_K_PORT_MAC_INDICATED 0x400F +#define PI_ITEM_K_PORT_CURRENT_PATH 0x4010 +#define PI_ITEM_K_PORT_REQ_PATHS 0x4011 +#define PI_ITEM_K_PORT_MAC_PLACEMENT 0x4012 +#define PI_ITEM_K_PORT_AVAIL_PATHS 0x4013 +#define PI_ITEM_K_PORT_PMD_CLASS 0x4016 +#define PI_ITEM_K_PORT_CONN_CAPS 0x4017 +#define PI_ITEM_K_PORT_INDEX 0x401D + +#define PI_GRP_K_PORT_OPERATION 0x401E +#define PI_ITEM_K_PORT_BS_FLAG 0x4021 + +#define PI_GRP_K_PORT_ERR_CNTRS 0x4028 +#define PI_ITEM_K_PORT_LCT_FAIL_CT 0x402A + +#define PI_GRP_K_PORT_LER 0x4032 +#define PI_ITEM_K_PORT_LER_ESTIMATE 0x4033 +#define PI_ITEM_K_PORT_LEM_REJ_CT 0x4034 +#define PI_ITEM_K_PORT_LEM_CT 0x4035 +#define PI_ITEM_K_PORT_LER_CUTOFF 0x403A +#define PI_ITEM_K_PORT_LER_ALARM 0x403B + +#define PI_GRP_K_PORT_STATUS 0x403C +#define PI_ITEM_K_PORT_CONNECT_STATE 0x403D +#define PI_ITEM_K_PORT_PCM_STATE 0x403E +#define PI_ITEM_K_PORT_PC_WITHHOLD 0x403F +#define PI_ITEM_K_PORT_LER_FLAG 0x4040 +#define PI_ITEM_K_PORT_HW_PRESENT 0x4041 + +#define PI_ITEM_K_PORT_ACT 0x4046 + +/* Addr_Filter_Set Request */ + +#define PI_CMD_ADDR_FILTER_K_SIZE 62 + +typedef struct + { + PI_UINT32 cmd_type; + PI_LAN_ADDR entry[PI_CMD_ADDR_FILTER_K_SIZE]; + } PI_CMD_ADDR_FILTER_SET_REQ; + +/* Addr_Filter_Set Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_ADDR_FILTER_SET_RSP; + +/* Addr_Filter_Get Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_ADDR_FILTER_GET_REQ; + +/* Addr_Filter_Get Response */ + +typedef struct + { + PI_RSP_HEADER header; + PI_LAN_ADDR entry[PI_CMD_ADDR_FILTER_K_SIZE]; + } PI_CMD_ADDR_FILTER_GET_RSP; + +/* Status_Chars_Get Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_STATUS_CHARS_GET_REQ; + +/* Status_Chars_Get Response */ + +typedef struct + { + PI_RSP_HEADER header; + PI_STATION_ID station_id; /* Station */ + PI_UINT32 station_type; + PI_UINT32 smt_ver_id; + PI_UINT32 smt_ver_id_max; + PI_UINT32 smt_ver_id_min; + PI_UINT32 station_state; + PI_LAN_ADDR link_addr; /* Link */ + PI_UINT32 t_req; + PI_UINT32 tvx; + PI_UINT32 token_timeout; + PI_UINT32 purger_enb; + PI_UINT32 link_state; + PI_UINT32 tneg; + PI_UINT32 dup_addr_flag; + PI_LAN_ADDR una; + PI_LAN_ADDR una_old; + PI_UINT32 un_dup_addr_flag; + PI_LAN_ADDR dna; + PI_LAN_ADDR dna_old; + PI_UINT32 purger_state; + PI_UINT32 fci_mode; + PI_UINT32 error_reason; + PI_UINT32 loopback; + PI_UINT32 ring_latency; + PI_LAN_ADDR last_dir_beacon_sa; + PI_LAN_ADDR last_dir_beacon_una; + PI_UINT32 phy_type[PI_PHY_K_MAX]; /* Phy */ + PI_UINT32 pmd_type[PI_PHY_K_MAX]; + PI_UINT32 lem_threshold[PI_PHY_K_MAX]; + PI_UINT32 phy_state[PI_PHY_K_MAX]; + PI_UINT32 nbor_phy_type[PI_PHY_K_MAX]; + PI_UINT32 link_error_est[PI_PHY_K_MAX]; + PI_UINT32 broken_reason[PI_PHY_K_MAX]; + PI_UINT32 reject_reason[PI_PHY_K_MAX]; + PI_UINT32 cntr_interval; /* Miscellaneous */ + PI_UINT32 module_rev; + PI_UINT32 firmware_rev; + PI_UINT32 mop_device_type; + PI_UINT32 phy_led[PI_PHY_K_MAX]; + PI_UINT32 flush_time; + } PI_CMD_STATUS_CHARS_GET_RSP; + +/* FDDI_MIB_Get Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_FDDI_MIB_GET_REQ; + +/* FDDI_MIB_Get Response */ + +typedef struct + { + PI_RSP_HEADER header; + + /* SMT GROUP */ + + PI_STATION_ID smt_station_id; + PI_UINT32 smt_op_version_id; + PI_UINT32 smt_hi_version_id; + PI_UINT32 smt_lo_version_id; + PI_UINT32 smt_mac_ct; + PI_UINT32 smt_non_master_ct; + PI_UINT32 smt_master_ct; + PI_UINT32 smt_paths_available; + PI_UINT32 smt_config_capabilities; + PI_UINT32 smt_config_policy; + PI_UINT32 smt_connection_policy; + PI_UINT32 smt_t_notify; + PI_UINT32 smt_status_reporting; + PI_UINT32 smt_ecm_state; + PI_UINT32 smt_cf_state; + PI_UINT32 smt_hold_state; + PI_UINT32 smt_remote_disconnect_flag; + PI_UINT32 smt_station_action; + + /* MAC GROUP */ + + PI_UINT32 mac_frame_status_capabilities; + PI_UINT32 mac_t_max_greatest_lower_bound; + PI_UINT32 mac_tvx_greatest_lower_bound; + PI_UINT32 mac_paths_available; + PI_UINT32 mac_current_path; + PI_LAN_ADDR mac_upstream_nbr; + PI_LAN_ADDR mac_old_upstream_nbr; + PI_UINT32 mac_dup_addr_test; + PI_UINT32 mac_paths_requested; + PI_UINT32 mac_downstream_port_type; + PI_LAN_ADDR mac_smt_address; + PI_UINT32 mac_t_req; + PI_UINT32 mac_t_neg; + PI_UINT32 mac_t_max; + PI_UINT32 mac_tvx_value; + PI_UINT32 mac_t_min; + PI_UINT32 mac_current_frame_status; + /* mac_frame_cts */ + /* mac_error_cts */ + /* mac_lost_cts */ + PI_UINT32 mac_frame_error_threshold; + PI_UINT32 mac_frame_error_ratio; + PI_UINT32 mac_rmt_state; + PI_UINT32 mac_da_flag; + PI_UINT32 mac_una_da_flag; + PI_UINT32 mac_frame_condition; + PI_UINT32 mac_chip_set; + PI_UINT32 mac_action; + + /* PATH GROUP => Does not need to be implemented */ + + /* PORT GROUP */ + + PI_UINT32 port_pc_type[PI_PHY_K_MAX]; + PI_UINT32 port_pc_neighbor[PI_PHY_K_MAX]; + PI_UINT32 port_connection_policies[PI_PHY_K_MAX]; + PI_UINT32 port_remote_mac_indicated[PI_PHY_K_MAX]; + PI_UINT32 port_ce_state[PI_PHY_K_MAX]; + PI_UINT32 port_paths_requested[PI_PHY_K_MAX]; + PI_UINT32 port_mac_placement[PI_PHY_K_MAX]; + PI_UINT32 port_available_paths[PI_PHY_K_MAX]; + PI_UINT32 port_mac_loop_time[PI_PHY_K_MAX]; + PI_UINT32 port_tb_max[PI_PHY_K_MAX]; + PI_UINT32 port_bs_flag[PI_PHY_K_MAX]; + /* port_lct_fail_cts[PI_PHY_K_MAX]; */ + PI_UINT32 port_ler_estimate[PI_PHY_K_MAX]; + /* port_lem_reject_cts[PI_PHY_K_MAX]; */ + /* port_lem_cts[PI_PHY_K_MAX]; */ + PI_UINT32 port_ler_cutoff[PI_PHY_K_MAX]; + PI_UINT32 port_ler_alarm[PI_PHY_K_MAX]; + PI_UINT32 port_connect_state[PI_PHY_K_MAX]; + PI_UINT32 port_pcm_state[PI_PHY_K_MAX]; + PI_UINT32 port_pc_withhold[PI_PHY_K_MAX]; + PI_UINT32 port_ler_condition[PI_PHY_K_MAX]; + PI_UINT32 port_chip_set[PI_PHY_K_MAX]; + PI_UINT32 port_action[PI_PHY_K_MAX]; + + /* ATTACHMENT GROUP */ + + PI_UINT32 attachment_class; + PI_UINT32 attachment_ob_present; + PI_UINT32 attachment_imax_expiration; + PI_UINT32 attachment_inserted_status; + PI_UINT32 attachment_insert_policy; + + /* CHIP SET GROUP => Does not need to be implemented */ + + } PI_CMD_FDDI_MIB_GET_RSP; + +/* DEC_Ext_MIB_Get Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_DEC_EXT_MIB_GET_REQ; + +/* DEC_Ext_MIB_Get (efddi and efdx groups only) Response */ + +typedef struct + { + PI_RSP_HEADER header; + + /* SMT GROUP */ + + PI_UINT32 esmt_station_type; + + /* MAC GROUP */ + + PI_UINT32 emac_link_state; + PI_UINT32 emac_ring_purger_state; + PI_UINT32 emac_ring_purger_enable; + PI_UINT32 emac_frame_strip_mode; + PI_UINT32 emac_ring_error_reason; + PI_UINT32 emac_up_nbr_dup_addr_flag; + PI_UINT32 emac_restricted_token_timeout; + + /* PORT GROUP */ + + PI_UINT32 eport_pmd_type[PI_PHY_K_MAX]; + PI_UINT32 eport_phy_state[PI_PHY_K_MAX]; + PI_UINT32 eport_reject_reason[PI_PHY_K_MAX]; + + /* FDX (Full-Duplex) GROUP */ + + PI_UINT32 efdx_enable; /* Valid only in SMT 7.3 */ + PI_UINT32 efdx_op; /* Valid only in SMT 7.3 */ + PI_UINT32 efdx_state; /* Valid only in SMT 7.3 */ + + } PI_CMD_DEC_EXT_MIB_GET_RSP; + +typedef struct + { + PI_CNTR traces_rcvd; /* Station */ + PI_CNTR frame_cnt; /* Link */ + PI_CNTR error_cnt; + PI_CNTR lost_cnt; + PI_CNTR octets_rcvd; + PI_CNTR octets_sent; + PI_CNTR pdus_rcvd; + PI_CNTR pdus_sent; + PI_CNTR mcast_octets_rcvd; + PI_CNTR mcast_octets_sent; + PI_CNTR mcast_pdus_rcvd; + PI_CNTR mcast_pdus_sent; + PI_CNTR xmt_underruns; + PI_CNTR xmt_failures; + PI_CNTR block_check_errors; + PI_CNTR frame_status_errors; + PI_CNTR pdu_length_errors; + PI_CNTR rcv_overruns; + PI_CNTR user_buff_unavailable; + PI_CNTR inits_initiated; + PI_CNTR inits_rcvd; + PI_CNTR beacons_initiated; + PI_CNTR dup_addrs; + PI_CNTR dup_tokens; + PI_CNTR purge_errors; + PI_CNTR fci_strip_errors; + PI_CNTR traces_initiated; + PI_CNTR directed_beacons_rcvd; + PI_CNTR emac_frame_alignment_errors; + PI_CNTR ebuff_errors[PI_PHY_K_MAX]; /* Phy */ + PI_CNTR lct_rejects[PI_PHY_K_MAX]; + PI_CNTR lem_rejects[PI_PHY_K_MAX]; + PI_CNTR link_errors[PI_PHY_K_MAX]; + PI_CNTR connections[PI_PHY_K_MAX]; + PI_CNTR copied_cnt; /* Valid only if using SMT 7.3 */ + PI_CNTR transmit_cnt; /* Valid only if using SMT 7.3 */ + PI_CNTR tokens; + } PI_CNTR_BLK; + +/* Counters_Get Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_CNTRS_GET_REQ; + +/* Counters_Get Response */ + +typedef struct + { + PI_RSP_HEADER header; + PI_CNTR time_since_reset; + PI_CNTR_BLK cntrs; + } PI_CMD_CNTRS_GET_RSP; + +/* Counters_Set Request */ + +typedef struct + { + PI_UINT32 cmd_type; + PI_CNTR_BLK cntrs; + } PI_CMD_CNTRS_SET_REQ; + +/* Counters_Set Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_CNTRS_SET_RSP; + +/* Error_Log_Clear Request */ + +typedef struct + { + PI_UINT32 cmd_type; + } PI_CMD_ERROR_LOG_CLEAR_REQ; + +/* Error_Log_Clear Response */ + +typedef struct + { + PI_RSP_HEADER header; + } PI_CMD_ERROR_LOG_CLEAR_RSP; + +/* Error_Log_Get Request */ + +#define PI_LOG_ENTRY_K_INDEX_MIN 0 /* Minimum index for entry */ + +typedef struct + { + PI_UINT32 cmd_type; + PI_UINT32 entry_index; + } PI_CMD_ERROR_LOG_GET_REQ; + +/* Error_Log_Get Response */ + +#define PI_K_LOG_FW_SIZE 111 /* Max number of fw longwords */ +#define PI_K_LOG_DIAG_SIZE 6 /* Max number of diag longwords */ + +typedef struct + { + struct + { + PI_UINT32 fru_imp_mask; + PI_UINT32 test_id; + PI_UINT32 reserved[PI_K_LOG_DIAG_SIZE]; + } diag; + PI_UINT32 fw[PI_K_LOG_FW_SIZE]; + } PI_LOG_ENTRY; + +typedef struct + { + PI_RSP_HEADER header; + PI_UINT32 event_status; + PI_UINT32 caller_id; + PI_UINT32 timestamp_l; + PI_UINT32 timestamp_h; + PI_UINT32 write_count; + PI_LOG_ENTRY entry_info; + } PI_CMD_ERROR_LOG_GET_RSP; + +/* Define error log related constants and types. */ +/* Not all of the caller id's can occur. The only ones currently */ +/* implemented are: none, selftest, mfg, fw, console */ + +#define PI_LOG_EVENT_STATUS_K_VALID 0 /* Valid Event Status */ +#define PI_LOG_EVENT_STATUS_K_INVALID 1 /* Invalid Event Status */ +#define PI_LOG_CALLER_ID_K_NONE 0 /* No caller */ +#define PI_LOG_CALLER_ID_K_SELFTEST 1 /* Normal power-up selftest */ +#define PI_LOG_CALLER_ID_K_MFG 2 /* Mfg power-up selftest */ +#define PI_LOG_CALLER_ID_K_ONLINE 3 /* On-line diagnostics */ +#define PI_LOG_CALLER_ID_K_HW 4 /* Hardware */ +#define PI_LOG_CALLER_ID_K_FW 5 /* Firmware */ +#define PI_LOG_CALLER_ID_K_CNS_HW 6 /* CNS firmware */ +#define PI_LOG_CALLER_ID_K_CNS_FW 7 /* CNS hardware */ +#define PI_LOG_CALLER_ID_K_CONSOLE 8 /* Console Caller Id */ + +/* + * Place all DMA commands in the following request and response structures + * to simplify code. + */ + +typedef union + { + PI_UINT32 cmd_type; + PI_CMD_START_REQ start; + PI_CMD_FILTERS_SET_REQ filter_set; + PI_CMD_FILTERS_GET_REQ filter_get; + PI_CMD_CHARS_SET_REQ char_set; + PI_CMD_ADDR_FILTER_SET_REQ addr_filter_set; + PI_CMD_ADDR_FILTER_GET_REQ addr_filter_get; + PI_CMD_STATUS_CHARS_GET_REQ stat_char_get; + PI_CMD_CNTRS_GET_REQ cntrs_get; + PI_CMD_CNTRS_SET_REQ cntrs_set; + PI_CMD_ERROR_LOG_CLEAR_REQ error_log_clear; + PI_CMD_ERROR_LOG_GET_REQ error_log_read; + PI_CMD_SNMP_SET_REQ snmp_set; + PI_CMD_FDDI_MIB_GET_REQ fddi_mib_get; + PI_CMD_DEC_EXT_MIB_GET_REQ dec_mib_get; + PI_CMD_SMT_MIB_SET_REQ smt_mib_set; + PI_CMD_SMT_MIB_GET_REQ smt_mib_get; + char pad[PI_CMD_REQ_K_SIZE_MAX]; + } PI_DMA_CMD_REQ; + +typedef union + { + PI_RSP_HEADER header; + PI_CMD_START_RSP start; + PI_CMD_FILTERS_SET_RSP filter_set; + PI_CMD_FILTERS_GET_RSP filter_get; + PI_CMD_CHARS_SET_RSP char_set; + PI_CMD_ADDR_FILTER_SET_RSP addr_filter_set; + PI_CMD_ADDR_FILTER_GET_RSP addr_filter_get; + PI_CMD_STATUS_CHARS_GET_RSP stat_char_get; + PI_CMD_CNTRS_GET_RSP cntrs_get; + PI_CMD_CNTRS_SET_RSP cntrs_set; + PI_CMD_ERROR_LOG_CLEAR_RSP error_log_clear; + PI_CMD_ERROR_LOG_GET_RSP error_log_get; + PI_CMD_SNMP_SET_RSP snmp_set; + PI_CMD_FDDI_MIB_GET_RSP fddi_mib_get; + PI_CMD_DEC_EXT_MIB_GET_RSP dec_mib_get; + PI_CMD_SMT_MIB_SET_RSP smt_mib_set; + PI_CMD_SMT_MIB_GET_RSP smt_mib_get; + char pad[PI_CMD_RSP_K_SIZE_MAX]; + } PI_DMA_CMD_RSP; + +typedef union + { + PI_DMA_CMD_REQ request; + PI_DMA_CMD_RSP response; + } PI_DMA_CMD_BUFFER; + + +/* Define format of Consumer Block (resident in host memory) */ + +typedef struct + { + volatile PI_UINT32 xmt_rcv_data; + volatile PI_UINT32 reserved_1; + volatile PI_UINT32 smt_host; + volatile PI_UINT32 reserved_2; + volatile PI_UINT32 unsol; + volatile PI_UINT32 reserved_3; + volatile PI_UINT32 cmd_rsp; + volatile PI_UINT32 reserved_4; + volatile PI_UINT32 cmd_req; + volatile PI_UINT32 reserved_5; + } PI_CONSUMER_BLOCK; + +#define PI_CONS_M_RCV_INDEX 0x000000FF +#define PI_CONS_M_XMT_INDEX 0x00FF0000 +#define PI_CONS_V_RCV_INDEX 0 +#define PI_CONS_V_XMT_INDEX 16 + +/* Offsets into consumer block */ + +#define PI_CONS_BLK_K_XMT_RCV 0x00 +#define PI_CONS_BLK_K_SMT_HOST 0x08 +#define PI_CONS_BLK_K_UNSOL 0x10 +#define PI_CONS_BLK_K_CMD_RSP 0x18 +#define PI_CONS_BLK_K_CMD_REQ 0x20 + +/* Offsets into descriptor block */ + +#define PI_DESCR_BLK_K_RCV_DATA 0x0000 +#define PI_DESCR_BLK_K_XMT_DATA 0x0800 +#define PI_DESCR_BLK_K_SMT_HOST 0x1000 +#define PI_DESCR_BLK_K_UNSOL 0x1200 +#define PI_DESCR_BLK_K_CMD_RSP 0x1280 +#define PI_DESCR_BLK_K_CMD_REQ 0x1300 + +/* Define format of a rcv descr (Rcv Data, Cmd Rsp, Unsolicited, SMT Host) */ +/* Note a field has been added for later versions of the PDQ to allow for */ +/* finer granularity of the rcv buffer alignment. For backwards */ +/* compatibility, the two bits (which allow the rcv buffer to be longword */ +/* aligned) have been added at the MBZ bits. To support previous drivers, */ +/* the MBZ definition is left intact. */ + +typedef struct + { + PI_UINT32 long_0; + PI_UINT32 long_1; + } PI_RCV_DESCR; + +#define PI_RCV_DESCR_M_SOP 0x80000000 +#define PI_RCV_DESCR_M_SEG_LEN_LO 0x60000000 +#define PI_RCV_DESCR_M_MBZ 0x60000000 +#define PI_RCV_DESCR_M_SEG_LEN 0x1F800000 +#define PI_RCV_DESCR_M_SEG_LEN_HI 0x1FF00000 +#define PI_RCV_DESCR_M_SEG_CNT 0x000F0000 +#define PI_RCV_DESCR_M_BUFF_HI 0x0000FFFF + +#define PI_RCV_DESCR_V_SOP 31 +#define PI_RCV_DESCR_V_SEG_LEN_LO 29 +#define PI_RCV_DESCR_V_MBZ 29 +#define PI_RCV_DESCR_V_SEG_LEN 23 +#define PI_RCV_DESCR_V_SEG_LEN_HI 20 +#define PI_RCV_DESCR_V_SEG_CNT 16 +#define PI_RCV_DESCR_V_BUFF_HI 0 + +/* Define the format of a transmit descriptor (Xmt Data, Cmd Req) */ + +typedef struct + { + PI_UINT32 long_0; + PI_UINT32 long_1; + } PI_XMT_DESCR; + +#define PI_XMT_DESCR_M_SOP 0x80000000 +#define PI_XMT_DESCR_M_EOP 0x40000000 +#define PI_XMT_DESCR_M_MBZ 0x20000000 +#define PI_XMT_DESCR_M_SEG_LEN 0x1FFF0000 +#define PI_XMT_DESCR_M_BUFF_HI 0x0000FFFF + +#define PI_XMT_DESCR_V_SOP 31 +#define PI_XMT_DESCR_V_EOP 30 +#define PI_XMT_DESCR_V_MBZ 29 +#define PI_XMT_DESCR_V_SEG_LEN 16 +#define PI_XMT_DESCR_V_BUFF_HI 0 + +/* Define format of the Descriptor Block (resident in host memory) */ + +#define PI_RCV_DATA_K_NUM_ENTRIES 256 +#define PI_XMT_DATA_K_NUM_ENTRIES 256 +#define PI_SMT_HOST_K_NUM_ENTRIES 64 +#define PI_UNSOL_K_NUM_ENTRIES 16 +#define PI_CMD_RSP_K_NUM_ENTRIES 16 +#define PI_CMD_REQ_K_NUM_ENTRIES 16 + +typedef struct + { + PI_RCV_DESCR rcv_data[PI_RCV_DATA_K_NUM_ENTRIES]; + PI_XMT_DESCR xmt_data[PI_XMT_DATA_K_NUM_ENTRIES]; + PI_RCV_DESCR smt_host[PI_SMT_HOST_K_NUM_ENTRIES]; + PI_RCV_DESCR unsol[PI_UNSOL_K_NUM_ENTRIES]; + PI_RCV_DESCR cmd_rsp[PI_CMD_RSP_K_NUM_ENTRIES]; + PI_XMT_DESCR cmd_req[PI_CMD_REQ_K_NUM_ENTRIES]; + } PI_DESCR_BLOCK; + +/* Define Port Registers - offsets from PDQ Base address */ + +#define PI_PDQ_K_REG_PORT_RESET 0x00000000 +#define PI_PDQ_K_REG_HOST_DATA 0x00000004 +#define PI_PDQ_K_REG_PORT_CTRL 0x00000008 +#define PI_PDQ_K_REG_PORT_DATA_A 0x0000000C +#define PI_PDQ_K_REG_PORT_DATA_B 0x00000010 +#define PI_PDQ_K_REG_PORT_STATUS 0x00000014 +#define PI_PDQ_K_REG_TYPE_0_STATUS 0x00000018 +#define PI_PDQ_K_REG_HOST_INT_ENB 0x0000001C +#define PI_PDQ_K_REG_TYPE_2_PROD_NOINT 0x00000020 +#define PI_PDQ_K_REG_TYPE_2_PROD 0x00000024 +#define PI_PDQ_K_REG_CMD_RSP_PROD 0x00000028 +#define PI_PDQ_K_REG_CMD_REQ_PROD 0x0000002C +#define PI_PDQ_K_REG_SMT_HOST_PROD 0x00000030 +#define PI_PDQ_K_REG_UNSOL_PROD 0x00000034 + +/* Port Control Register - Command codes for primary commands */ + +#define PI_PCTRL_M_CMD_ERROR 0x8000 +#define PI_PCTRL_M_BLAST_FLASH 0x4000 +#define PI_PCTRL_M_HALT 0x2000 +#define PI_PCTRL_M_COPY_DATA 0x1000 +#define PI_PCTRL_M_ERROR_LOG_START 0x0800 +#define PI_PCTRL_M_ERROR_LOG_READ 0x0400 +#define PI_PCTRL_M_XMT_DATA_FLUSH_DONE 0x0200 +#define PI_PCTRL_M_INIT 0x0100 +#define PI_PCTRL_M_INIT_START 0x0080 +#define PI_PCTRL_M_CONS_BLOCK 0x0040 +#define PI_PCTRL_M_UNINIT 0x0020 +#define PI_PCTRL_M_RING_MEMBER 0x0010 +#define PI_PCTRL_M_MLA 0x0008 +#define PI_PCTRL_M_FW_REV_READ 0x0004 +#define PI_PCTRL_M_DEV_SPECIFIC 0x0002 +#define PI_PCTRL_M_SUB_CMD 0x0001 + +/* Define sub-commands accessed via the PI_PCTRL_M_SUB_CMD command */ + +#define PI_SUB_CMD_K_LINK_UNINIT 0x0001 +#define PI_SUB_CMD_K_BURST_SIZE_SET 0x0002 +#define PI_SUB_CMD_K_PDQ_REV_GET 0x0004 +#define PI_SUB_CMD_K_HW_REV_GET 0x0008 + +/* Define some Port Data B values */ + +#define PI_PDATA_B_DMA_BURST_SIZE_4 0 /* valid values for command */ +#define PI_PDATA_B_DMA_BURST_SIZE_8 1 +#define PI_PDATA_B_DMA_BURST_SIZE_16 2 +#define PI_PDATA_B_DMA_BURST_SIZE_32 3 /* not supported on PCI */ +#define PI_PDATA_B_DMA_BURST_SIZE_DEF PI_PDATA_B_DMA_BURST_SIZE_16 + +/* Port Data A Reset state */ + +#define PI_PDATA_A_RESET_M_UPGRADE 0x00000001 +#define PI_PDATA_A_RESET_M_SOFT_RESET 0x00000002 +#define PI_PDATA_A_RESET_M_SKIP_ST 0x00000004 + +/* Read adapter MLA address port control command constants */ + +#define PI_PDATA_A_MLA_K_LO 0 +#define PI_PDATA_A_MLA_K_HI 1 + +/* Byte Swap values for init command */ + +#define PI_PDATA_A_INIT_M_DESC_BLK_ADDR 0x0FFFFE000 +#define PI_PDATA_A_INIT_M_RESERVED 0x000001FFC +#define PI_PDATA_A_INIT_M_BSWAP_DATA 0x000000002 +#define PI_PDATA_A_INIT_M_BSWAP_LITERAL 0x000000001 + +#define PI_PDATA_A_INIT_V_DESC_BLK_ADDR 13 +#define PI_PDATA_A_INIT_V_RESERVED 3 +#define PI_PDATA_A_INIT_V_BSWAP_DATA 1 +#define PI_PDATA_A_INIT_V_BSWAP_LITERAL 0 + +/* Port Reset Register */ + +#define PI_RESET_M_ASSERT_RESET 1 + +/* Port Status register */ + +#define PI_PSTATUS_V_RCV_DATA_PENDING 31 +#define PI_PSTATUS_V_XMT_DATA_PENDING 30 +#define PI_PSTATUS_V_SMT_HOST_PENDING 29 +#define PI_PSTATUS_V_UNSOL_PENDING 28 +#define PI_PSTATUS_V_CMD_RSP_PENDING 27 +#define PI_PSTATUS_V_CMD_REQ_PENDING 26 +#define PI_PSTATUS_V_TYPE_0_PENDING 25 +#define PI_PSTATUS_V_RESERVED_1 16 +#define PI_PSTATUS_V_RESERVED_2 11 +#define PI_PSTATUS_V_STATE 8 +#define PI_PSTATUS_V_HALT_ID 0 + +#define PI_PSTATUS_M_RCV_DATA_PENDING 0x80000000 +#define PI_PSTATUS_M_XMT_DATA_PENDING 0x40000000 +#define PI_PSTATUS_M_SMT_HOST_PENDING 0x20000000 +#define PI_PSTATUS_M_UNSOL_PENDING 0x10000000 +#define PI_PSTATUS_M_CMD_RSP_PENDING 0x08000000 +#define PI_PSTATUS_M_CMD_REQ_PENDING 0x04000000 +#define PI_PSTATUS_M_TYPE_0_PENDING 0x02000000 +#define PI_PSTATUS_M_RESERVED_1 0x01FF0000 +#define PI_PSTATUS_M_RESERVED_2 0x0000F800 +#define PI_PSTATUS_M_STATE 0x00000700 +#define PI_PSTATUS_M_HALT_ID 0x000000FF + +/* Define Halt Id's */ +/* Do not insert into this list, only append. */ + +#define PI_HALT_ID_K_SELFTEST_TIMEOUT 0 +#define PI_HALT_ID_K_PARITY_ERROR 1 +#define PI_HALT_ID_K_HOST_DIR_HALT 2 +#define PI_HALT_ID_K_SW_FAULT 3 +#define PI_HALT_ID_K_HW_FAULT 4 +#define PI_HALT_ID_K_PC_TRACE 5 +#define PI_HALT_ID_K_DMA_ERROR 6 /* Host Data has error reg */ +#define PI_HALT_ID_K_IMAGE_CRC_ERROR 7 /* Image is bad, update it */ +#define PI_HALT_ID_K_BUS_EXCEPTION 8 /* 68K bus exception */ + +/* Host Interrupt Enable Register as seen by host */ + +#define PI_HOST_INT_M_XMT_DATA_ENB 0x80000000 /* Type 2 Enables */ +#define PI_HOST_INT_M_RCV_DATA_ENB 0x40000000 +#define PI_HOST_INT_M_SMT_HOST_ENB 0x10000000 /* Type 1 Enables */ +#define PI_HOST_INT_M_UNSOL_ENB 0x20000000 +#define PI_HOST_INT_M_CMD_RSP_ENB 0x08000000 +#define PI_HOST_INT_M_CMD_REQ_ENB 0x04000000 +#define PI_HOST_INT_M_TYPE_1_RESERVED 0x00FF0000 +#define PI_HOST_INT_M_TYPE_0_RESERVED 0x0000FF00 /* Type 0 Enables */ +#define PI_HOST_INT_M_1MS 0x00000080 +#define PI_HOST_INT_M_20MS 0x00000040 +#define PI_HOST_INT_M_CSR_CMD_DONE 0x00000020 +#define PI_HOST_INT_M_STATE_CHANGE 0x00000010 +#define PI_HOST_INT_M_XMT_FLUSH 0x00000008 +#define PI_HOST_INT_M_NXM 0x00000004 +#define PI_HOST_INT_M_PM_PAR_ERR 0x00000002 +#define PI_HOST_INT_M_BUS_PAR_ERR 0x00000001 + +#define PI_HOST_INT_V_XMT_DATA_ENB 31 /* Type 2 Enables */ +#define PI_HOST_INT_V_RCV_DATA_ENB 30 +#define PI_HOST_INT_V_SMT_HOST_ENB 29 /* Type 1 Enables */ +#define PI_HOST_INT_V_UNSOL_ENB 28 +#define PI_HOST_INT_V_CMD_RSP_ENB 27 +#define PI_HOST_INT_V_CMD_REQ_ENB 26 +#define PI_HOST_INT_V_TYPE_1_RESERVED 16 +#define PI_HOST_INT_V_TYPE_0_RESERVED 8 /* Type 0 Enables */ +#define PI_HOST_INT_V_1MS_ENB 7 +#define PI_HOST_INT_V_20MS_ENB 6 +#define PI_HOST_INT_V_CSR_CMD_DONE_ENB 5 +#define PI_HOST_INT_V_STATE_CHANGE_ENB 4 +#define PI_HOST_INT_V_XMT_FLUSH_ENB 3 +#define PI_HOST_INT_V_NXM_ENB 2 +#define PI_HOST_INT_V_PM_PAR_ERR_ENB 1 +#define PI_HOST_INT_V_BUS_PAR_ERR_ENB 0 + +#define PI_HOST_INT_K_ACK_ALL_TYPE_0 0x000000FF +#define PI_HOST_INT_K_DISABLE_ALL_INTS 0x00000000 +#define PI_HOST_INT_K_ENABLE_ALL_INTS 0xFFFFFFFF +#define PI_HOST_INT_K_ENABLE_DEF_INTS 0xC000001F + +/* Type 0 Interrupt Status Register */ + +#define PI_TYPE_0_STAT_M_1MS 0x00000080 +#define PI_TYPE_0_STAT_M_20MS 0x00000040 +#define PI_TYPE_0_STAT_M_CSR_CMD_DONE 0x00000020 +#define PI_TYPE_0_STAT_M_STATE_CHANGE 0x00000010 +#define PI_TYPE_0_STAT_M_XMT_FLUSH 0x00000008 +#define PI_TYPE_0_STAT_M_NXM 0x00000004 +#define PI_TYPE_0_STAT_M_PM_PAR_ERR 0x00000002 +#define PI_TYPE_0_STAT_M_BUS_PAR_ERR 0x00000001 + +#define PI_TYPE_0_STAT_V_1MS 7 +#define PI_TYPE_0_STAT_V_20MS 6 +#define PI_TYPE_0_STAT_V_CSR_CMD_DONE 5 +#define PI_TYPE_0_STAT_V_STATE_CHANGE 4 +#define PI_TYPE_0_STAT_V_XMT_FLUSH 3 +#define PI_TYPE_0_STAT_V_NXM 2 +#define PI_TYPE_0_STAT_V_PM_PAR_ERR 1 +#define PI_TYPE_0_STAT_V_BUS_PAR_ERR 0 + +/* Register definition structures are defined for both big and little endian systems */ + +#ifndef BIG_ENDIAN + +/* Little endian format of Type 1 Producer register */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 prod; + PI_UINT8 comp; + PI_UINT8 mbz_1; + PI_UINT8 mbz_2; + } index; + } PI_TYPE_1_PROD_REG; + +/* Little endian format of Type 2 Producer register */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 rcv_prod; + PI_UINT8 xmt_prod; + PI_UINT8 rcv_comp; + PI_UINT8 xmt_comp; + } index; + } PI_TYPE_2_PROD_REG; + +/* Little endian format of Type 1 Consumer Block longword */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 cons; + PI_UINT8 res0; + PI_UINT8 res1; + PI_UINT8 res2; + } index; + } PI_TYPE_1_CONSUMER; + +/* Little endian format of Type 2 Consumer Block longword */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 rcv_cons; + PI_UINT8 res0; + PI_UINT8 xmt_cons; + PI_UINT8 res1; + } index; + } PI_TYPE_2_CONSUMER; + +#else + +/* Big endian format of Type 1 Producer register */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 mbz_2; + PI_UINT8 mbz_1; + PI_UINT8 comp; + PI_UINT8 prod; + } index; + } PI_TYPE_1_PROD_REG; + +/* Big endian format of Type 2 Producer register */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 xmt_comp; + PI_UINT8 rcv_comp; + PI_UINT8 xmt_prod; + PI_UINT8 rcv_prod; + } index; + } PI_TYPE_2_PROD_REG; + +/* Big endian format of Type 1 Consumer Block longword */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 res2; + PI_UINT8 res1; + PI_UINT8 res0; + PI_UINT8 cons; + } index; + } PI_TYPE_1_CONSUMER; + +/* Big endian format of Type 2 Consumer Block longword */ + +typedef union + { + PI_UINT32 lword; + struct + { + PI_UINT8 res1; + PI_UINT8 xmt_cons; + PI_UINT8 res0; + PI_UINT8 rcv_cons; + } index; + } PI_TYPE_2_CONSUMER; + +#endif /* #ifndef BIG_ENDIAN */ + +/* Define EISA controller register offsets */ + +#define PI_ESIC_K_BURST_HOLDOFF 0x040 +#define PI_ESIC_K_SLOT_ID 0xC80 +#define PI_ESIC_K_SLOT_CNTRL 0xC84 +#define PI_ESIC_K_MEM_ADD_CMP_0 0xC85 +#define PI_ESIC_K_MEM_ADD_CMP_1 0xC86 +#define PI_ESIC_K_MEM_ADD_CMP_2 0xC87 +#define PI_ESIC_K_MEM_ADD_HI_CMP_0 0xC88 +#define PI_ESIC_K_MEM_ADD_HI_CMP_1 0xC89 +#define PI_ESIC_K_MEM_ADD_HI_CMP_2 0xC8A +#define PI_ESIC_K_MEM_ADD_MASK_0 0xC8B +#define PI_ESIC_K_MEM_ADD_MASK_1 0xC8C +#define PI_ESIC_K_MEM_ADD_MASK_2 0xC8D +#define PI_ESIC_K_MEM_ADD_LO_CMP_0 0xC8E +#define PI_ESIC_K_MEM_ADD_LO_CMP_1 0xC8F +#define PI_ESIC_K_MEM_ADD_LO_CMP_2 0xC90 +#define PI_ESIC_K_IO_CMP_0_0 0xC91 +#define PI_ESIC_K_IO_CMP_0_1 0xC92 +#define PI_ESIC_K_IO_CMP_1_0 0xC93 +#define PI_ESIC_K_IO_CMP_1_1 0xC94 +#define PI_ESIC_K_IO_CMP_2_0 0xC95 +#define PI_ESIC_K_IO_CMP_2_1 0xC96 +#define PI_ESIC_K_IO_CMP_3_0 0xC97 +#define PI_ESIC_K_IO_CMP_3_1 0xC98 +#define PI_ESIC_K_IO_ADD_MASK_0_0 0xC99 +#define PI_ESIC_K_IO_ADD_MASK_0_1 0xC9A +#define PI_ESIC_K_IO_ADD_MASK_1_0 0xC9B +#define PI_ESIC_K_IO_ADD_MASK_1_1 0xC9C +#define PI_ESIC_K_IO_ADD_MASK_2_0 0xC9D +#define PI_ESIC_K_IO_ADD_MASK_2_1 0xC9E +#define PI_ESIC_K_IO_ADD_MASK_3_0 0xC9F +#define PI_ESIC_K_IO_ADD_MASK_3_1 0xCA0 +#define PI_ESIC_K_MOD_CONFIG_1 0xCA1 +#define PI_ESIC_K_MOD_CONFIG_2 0xCA2 +#define PI_ESIC_K_MOD_CONFIG_3 0xCA3 +#define PI_ESIC_K_MOD_CONFIG_4 0xCA4 +#define PI_ESIC_K_MOD_CONFIG_5 0xCA5 +#define PI_ESIC_K_MOD_CONFIG_6 0xCA6 +#define PI_ESIC_K_MOD_CONFIG_7 0xCA7 +#define PI_ESIC_K_DIP_SWITCH 0xCA8 +#define PI_ESIC_K_IO_CONFIG_STAT_0 0xCA9 +#define PI_ESIC_K_IO_CONFIG_STAT_1 0xCAA +#define PI_ESIC_K_DMA_CONFIG 0xCAB +#define PI_ESIC_K_INPUT_PORT 0xCAC +#define PI_ESIC_K_OUTPUT_PORT 0xCAD +#define PI_ESIC_K_FUNCTION_CNTRL 0xCAE +#define PI_ESIC_K_CSR_IO_LEN PI_ESIC_K_FUNCTION_CNTRL+1 /* always last reg + 1 */ + +/* Define the value all drivers must write to the function control register. */ + +#define PI_ESIC_K_FUNCTION_CNTRL_IO_ENB 0x03 + +/* Define the bits in the slot control register. */ + +#define PI_SLOT_CNTRL_M_RESET 0x04 /* Don't use. */ +#define PI_SLOT_CNTRL_M_ERROR 0x02 /* Not implemented. */ +#define PI_SLOT_CNTRL_M_ENB 0x01 /* Must be set. */ + +/* Define the bits in the burst holdoff register. */ + +#define PI_BURST_HOLDOFF_M_HOLDOFF 0xFC +#define PI_BURST_HOLDOFF_M_RESERVED 0x02 +#define PI_BURST_HOLDOFF_M_MEM_MAP 0x01 + +#define PI_BURST_HOLDOFF_V_HOLDOFF 2 +#define PI_BURST_HOLDOFF_V_RESERVED 1 +#define PI_BURST_HOLDOFF_V_MEM_MAP 0 + +/* + * Define the fields in the IO Compare registers. + * The driver must initialize the slot field with the slot ID shifted by the + * amount shown below. + */ + +#define PI_IO_CMP_V_SLOT 4 + +/* Define the fields in the Interrupt Channel Configuration and Status reg */ + +#define PI_CONFIG_STAT_0_M_PEND 0x80 +#define PI_CONFIG_STAT_0_M_RES_1 0x40 +#define PI_CONFIG_STAT_0_M_IREQ_OUT 0x20 +#define PI_CONFIG_STAT_0_M_IREQ_IN 0x10 +#define PI_CONFIG_STAT_0_M_INT_ENB 0x08 +#define PI_CONFIG_STAT_0_M_RES_0 0x04 +#define PI_CONFIG_STAT_0_M_IRQ 0x03 + +#define PI_CONFIG_STAT_0_V_PEND 7 +#define PI_CONFIG_STAT_0_V_RES_1 6 +#define PI_CONFIG_STAT_0_V_IREQ_OUT 5 +#define PI_CONFIG_STAT_0_V_IREQ_IN 4 +#define PI_CONFIG_STAT_0_V_INT_ENB 3 +#define PI_CONFIG_STAT_0_V_RES_0 2 +#define PI_CONFIG_STAT_0_V_IRQ 0 + +#define PI_CONFIG_STAT_0_IRQ_K_9 0 +#define PI_CONFIG_STAT_0_IRQ_K_10 1 +#define PI_CONFIG_STAT_0_IRQ_K_11 2 +#define PI_CONFIG_STAT_0_IRQ_K_15 3 + +/* Define DEC FDDIcontroller/EISA (DEFEA) EISA hardware ID's */ + +#define DEFEA_PRODUCT_ID 0x0030A310 /* DEC product 300 (no rev) */ +#define DEFEA_PROD_ID_1 0x0130A310 /* DEC product 300, rev 1 */ +#define DEFEA_PROD_ID_2 0x0230A310 /* DEC product 300, rev 2 */ +#define DEFEA_PROD_ID_3 0x0330A310 /* DEC product 300, rev 3 */ + +/**********************************************/ +/* Digital PFI Specification v1.0 Definitions */ +/**********************************************/ + +/* PCI Configuration Space Constants */ + +#define PFI_K_LAT_TIMER_DEF 0x88 /* def max master latency timer */ +#define PFI_K_LAT_TIMER_MIN 0x20 /* min max master latency timer */ +#define PFI_K_CSR_MEM_LEN 0x80 /* 128 bytes */ +#define PFI_K_CSR_IO_LEN 0x80 /* 128 bytes */ +#define PFI_K_PKT_MEM_LEN 0x10000 /* 64K bytes */ + +/* PFI Register Offsets (starting at PDQ Register Base Address) */ + +#define PFI_K_REG_RESERVED_0 0X00000038 +#define PFI_K_REG_RESERVED_1 0X0000003C +#define PFI_K_REG_MODE_CTRL 0X00000040 +#define PFI_K_REG_STATUS 0X00000044 +#define PFI_K_REG_FIFO_WRITE 0X00000048 +#define PFI_K_REG_FIFO_READ 0X0000004C + +/* PFI Mode Control Register Constants */ + +#define PFI_MODE_M_RESERVED 0XFFFFFFF0 +#define PFI_MODE_M_TGT_ABORT_ENB 0X00000008 +#define PFI_MODE_M_PDQ_INT_ENB 0X00000004 +#define PFI_MODE_M_PFI_INT_ENB 0X00000002 +#define PFI_MODE_M_DMA_ENB 0X00000001 + +#define PFI_MODE_V_RESERVED 4 +#define PFI_MODE_V_TGT_ABORT_ENB 3 +#define PFI_MODE_V_PDQ_INT_ENB 2 +#define PFI_MODE_V_PFI_INT_ENB 1 +#define PFI_MODE_V_DMA_ENB 0 + +#define PFI_MODE_K_ALL_DISABLE 0X00000000 + +/* PFI Status Register Constants */ + +#define PFI_STATUS_M_RESERVED 0XFFFFFFC0 +#define PFI_STATUS_M_PFI_ERROR 0X00000020 /* only valid in rev 1 or later PFI */ +#define PFI_STATUS_M_PDQ_INT 0X00000010 +#define PFI_STATUS_M_PDQ_DMA_ABORT 0X00000008 +#define PFI_STATUS_M_FIFO_FULL 0X00000004 +#define PFI_STATUS_M_FIFO_EMPTY 0X00000002 +#define PFI_STATUS_M_DMA_IN_PROGRESS 0X00000001 + +#define PFI_STATUS_V_RESERVED 6 +#define PFI_STATUS_V_PFI_ERROR 5 /* only valid in rev 1 or later PFI */ +#define PFI_STATUS_V_PDQ_INT 4 +#define PFI_STATUS_V_PDQ_DMA_ABORT 3 +#define PFI_STATUS_V_FIFO_FULL 2 +#define PFI_STATUS_V_FIFO_EMPTY 1 +#define PFI_STATUS_V_DMA_IN_PROGRESS 0 + +#define DFX_MAX_EISA_SLOTS 16 /* maximum number of EISA slots to scan */ +#define DFX_MAX_NUM_BOARDS 8 /* maximum number of adapters supported */ + +#define DFX_BUS_TYPE_PCI 0 /* type code for DEC FDDIcontroller/PCI */ +#define DFX_BUS_TYPE_EISA 1 /* type code for DEC FDDIcontroller/EISA */ + +#define DFX_FC_PRH2_PRH1_PRH0 0x54003820 /* Packet Request Header bytes + FC */ +#define DFX_PRH0_BYTE 0x20 /* Packet Request Header byte 0 */ +#define DFX_PRH1_BYTE 0x38 /* Packet Request Header byte 1 */ +#define DFX_PRH2_BYTE 0x00 /* Packet Request Header byte 2 */ + +/* Driver routine status (return) codes */ + +#define DFX_K_SUCCESS 0 /* routine succeeded */ +#define DFX_K_FAILURE 1 /* routine failed */ +#define DFX_K_OUTSTATE 2 /* bad state for command */ +#define DFX_K_HW_TIMEOUT 3 /* command timed out */ + +/* Define LLC host receive buffer min/max/default values */ + +#define RCV_BUFS_MIN 2 /* minimum pre-allocated receive buffers */ +#define RCV_BUFS_MAX 32 /* maximum pre-allocated receive buffers */ +#define RCV_BUFS_DEF 8 /* default pre-allocated receive buffers */ + +/* Define offsets into FDDI LLC or SMT receive frame buffers - used when indicating frames */ + +#define RCV_BUFF_K_DESCR 0 /* four byte FMC descriptor */ +#define RCV_BUFF_K_PADDING 4 /* three null bytes */ +#define RCV_BUFF_K_FC 7 /* one byte frame control */ +#define RCV_BUFF_K_DA 8 /* six byte destination address */ +#define RCV_BUFF_K_SA 14 /* six byte source address */ +#define RCV_BUFF_K_DATA 20 /* offset to start of packet data */ + +/* Define offsets into FDDI LLC transmit frame buffers - used when sending frames */ + +#define XMT_BUFF_K_FC 0 /* one byte frame control */ +#define XMT_BUFF_K_DA 1 /* six byte destination address */ +#define XMT_BUFF_K_SA 7 /* six byte source address */ +#define XMT_BUFF_K_DATA 13 /* offset to start of packet data */ + +/* + * Macro evaluates to "value" aligned to "size" bytes. Make sure that + * "size" is greater than 0 bytes. + */ + +#define ALIGN(value,size) ((value + (size - 1)) & ~(size - 1)) + +/* Macro for checking a "value" is within a specific range */ + +#define IN_RANGE(value,low,high) ((value >= low) && (value <= high)) + +/* Only execute special print call when debug driver was built */ + +#ifdef DEFXX_DEBUG +#define DBG_printk(args...) printk(## args) +#else +#define DBG_printk(args...) +#endif + +/* Define constants for masking/unmasking interrupts */ + +#define DFX_MASK_INTERRUPTS 1 +#define DFX_UNMASK_INTERRUPTS 0 + +/* Define structure for driver transmit descriptor block */ + +typedef struct + { + struct sk_buff *p_skb; /* ptr to skb */ + } XMT_DRIVER_DESCR; + +typedef struct DFX_board_tag + { + /* Keep virtual and physical pointers to locked, physically contiguous memory */ + + PI_DESCR_BLOCK *descr_block_virt; /* PDQ descriptor block virt address */ + u32 descr_block_phys; /* PDQ descriptor block phys address */ + PI_DMA_CMD_REQ *cmd_req_virt; /* Command request buffer virt address */ + u32 cmd_req_phys; /* Command request buffer phys address */ + PI_DMA_CMD_RSP *cmd_rsp_virt; /* Command response buffer virt address */ + u32 cmd_rsp_phys; /* Command response buffer phys address */ + char *rcv_block_virt; /* LLC host receive queue buf blk virt */ + u32 rcv_block_phys; /* LLC host receive queue buf blk phys */ + PI_CONSUMER_BLOCK *cons_block_virt; /* PDQ consumer block virt address */ + u32 cons_block_phys; /* PDQ consumer block phys address */ + + /* Keep local copies of Type 1 and Type 2 register data */ + + PI_TYPE_1_PROD_REG cmd_req_reg; /* Command Request register */ + PI_TYPE_1_PROD_REG cmd_rsp_reg; /* Command Response register */ + PI_TYPE_2_PROD_REG rcv_xmt_reg; /* Type 2 (RCV/XMT) register */ + + /* Storage for unicast and multicast address entries in adapter CAM */ + + u8 uc_table[1*FDDI_K_ALEN]; + u32 uc_count; /* number of unicast addresses */ + u8 mc_table[PI_CMD_ADDR_FILTER_K_SIZE*FDDI_K_ALEN]; + u32 mc_count; /* number of multicast addresses */ + + /* Current packet filter settings */ + + u32 ind_group_prom; /* LLC individual & group frame prom mode */ + u32 group_prom; /* LLC group (multicast) frame prom mode */ + + /* Link available flag needed to determine whether to drop outgoing packet requests */ + + u32 link_available; /* is link available? */ + + /* Resources to indicate reset type when resetting adapter */ + + u32 reset_type; /* skip or rerun diagnostics */ + + /* Store pointers to receive buffers for queue processing code */ + + char *p_rcv_buff_va[PI_RCV_DATA_K_NUM_ENTRIES]; + + /* Store pointers to transmit buffers for transmit completion code */ + + XMT_DRIVER_DESCR xmt_drv_descr_blk[PI_XMT_DATA_K_NUM_ENTRIES]; + + /* Store device, bus-specific, and parameter information for this adapter */ + + struct device *dev; /* pointer to device structure */ + u32 bus_type; /* bus type (0 == PCI, 1 == EISA) */ + u16 base_addr; /* base I/O address (same as dev->base_addr) */ + u8 pci_bus; /* PCI bus number */ + u8 pci_dev_fun; /* PCI device and function numbers */ + u32 full_duplex_enb; /* FDDI Full Duplex enable (1 == on, 2 == off) */ + u32 req_ttrt; /* requested TTRT value (in 80ns units) */ + u32 burst_size; /* adapter burst size (enumerated) */ + u32 rcv_bufs_to_post; /* receive buffers to post for LLC host queue */ + u8 factory_mac_addr[FDDI_K_ALEN]; /* factory (on-board) MAC address */ + + /* Common FDDI statistics structure and private counters */ + + struct fddi_statistics stats; + + u32 rcv_discards; + u32 rcv_crc_errors; + u32 rcv_frame_status_errors; + u32 rcv_length_errors; + u32 rcv_total_frames; + u32 rcv_multicast_frames; + u32 xmt_discards; + u32 xmt_length_errors; + u32 xmt_total_frames; + } DFX_board_t; + +#endif /* #ifndef _DEFXX_H_ */ diff -ur --new-file old/linux/drivers/net/dlci.c new/linux/drivers/net/dlci.c --- old/linux/drivers/net/dlci.c Mon May 13 22:23:07 1996 +++ new/linux/drivers/net/dlci.c Sun Sep 15 09:34:18 1996 @@ -5,7 +5,7 @@ * interfaces. Requires 'dlcicfg' program to create usable * interfaces, the initial one, 'dlci' is for IOCTL use only. * - * Version: @(#)dlci.c 0.25 13 May 1996 + * Version: @(#)dlci.c 0.30 12 Sep 1996 * * Author: Mike McLagan * @@ -13,14 +13,13 @@ * * 0.15 Mike Mclagan Packet freeing, bug in kmalloc call * DLCI_RET handling - * * 0.20 Mike McLagan More conservative on which packets * are returned for retry and whic are * are dropped. If DLCI_RET_DROP is * returned from the FRAD, the packet is * sent back to Linux for re-transmission - * * 0.25 Mike McLagan Converted to use SIOC IOCTL calls + * 0.30 Jim Freeman Fixed to allow IPX traffic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,7 +53,7 @@ #include static const char *devname = "dlci"; -static const char *version = "DLCI driver v0.25, 13 May 1996, mike.mclagan@linux.org"; +static const char *version = "DLCI driver v0.30, 12 Sep 1996, mike.mclagan@linux.org"; static struct device *open_dev[CONFIG_DLCI_COUNT]; @@ -143,7 +142,7 @@ hdr.pad = FRAD_P_PADDING; hdr.NLPID = FRAD_P_SNAP; memset(hdr.OUI, 0, sizeof(hdr.OUI)); - hdr.PID = type; + hdr.PID = htons(type); hlen = sizeof(hdr); break; } @@ -194,7 +193,8 @@ /* at this point, it's an EtherType frame */ header = sizeof(struct frhdr); - skb->protocol = htons(hdr->PID); + /* Already in network order ! */ + skb->protocol = hdr->PID; process = 1; break; @@ -619,12 +619,6 @@ for (i = 0; i < DEV_NUMBUFFS; i++) skb_queue_head_init(&dev->buffs[i]); - - if (strcmp(dev->name, devname) == 0) - { - dev->type = 0xFFFF; - dev->family = AF_UNSPEC; - } return(0); } diff -ur --new-file old/linux/drivers/net/eql.c new/linux/drivers/net/eql.c --- old/linux/drivers/net/eql.c Sat Nov 16 00:48:04 1996 +++ new/linux/drivers/net/eql.c Sat Nov 16 00:48:32 1996 @@ -17,7 +17,7 @@ */ static const char *version = - "Equalizer1996: $Revision: 1.2 $ $Date: 1996/04/11 17:51:52 $ Simon Janes (simon@ncm.com)\n"; + "Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n"; /* * Sources: @@ -460,28 +460,35 @@ if (master_dev != 0 && slave_dev != 0) { - if (! eql_is_master (slave_dev) && /* slave is not a master */ - ! eql_is_slave (slave_dev) ) /* slave is not already a slave */ - { - slave_t *s = eql_new_slave (); - equalizer_t *eql = (equalizer_t *) master_dev->priv; - s->dev = slave_dev; - s->priority = srq.priority; - s->priority_bps = srq.priority; - s->priority_Bps = srq.priority / 8; - slave_dev->flags |= IFF_SLAVE; - eql_insert_slave (eql->queue, s); - return 0; + if ((master_dev->flags & IFF_UP) == IFF_UP) + { + /*slave is not a master & not already a slave:*/ + if (! eql_is_master (slave_dev) && + ! eql_is_slave (slave_dev) ) + { + slave_t *s = eql_new_slave (); + equalizer_t *eql = + (equalizer_t *) master_dev->priv; + s->dev = slave_dev; + s->priority = srq.priority; + s->priority_bps = srq.priority; + s->priority_Bps = srq.priority / 8; + slave_dev->flags |= IFF_SLAVE; + eql_insert_slave (eql->queue, s); + return 0; + } +#ifdef EQL_DEBUG + else if (eql_debug >= 20) + printk ("EQL enslave: slave is master or slave is already slave\n"); +#endif } #ifdef EQL_DEBUG - if (eql_debug >= 20) - printk ("EQL enslave: slave is master or slave is already slave\n"); + else if (eql_debug >= 20) + printk ("EQL enslave: master device not up!\n"); #endif - - return -EINVAL; } #ifdef EQL_DEBUG - if (eql_debug >= 20) + else if (eql_debug >= 20) printk ("EQL enslave: master or slave are NULL"); #endif return -EINVAL; diff -ur --new-file old/linux/drivers/net/ibmtr.c new/linux/drivers/net/ibmtr.c --- old/linux/drivers/net/ibmtr.c Mon May 6 11:26:08 1996 +++ new/linux/drivers/net/ibmtr.c Thu Aug 29 18:15:14 1996 @@ -50,6 +50,11 @@ - detecting PCMCIA Card Removal in interrupt handler. if ISRP is FF, then a PCMCIA card has been removed + Changes by Paul Norton (pnorton@cts.com) : + - restructured the READ.LOG logic to prevent the transmit SRB + from being rudely overwritten before the transmit cycle is + complete. (August 15 1996) + Warnings !!!!!!!!!!!!!! This driver is only partially sanitized for support of multiple adapters. It will almost definitely fail if more than one @@ -168,6 +173,7 @@ static int tok_close(struct device *dev); static int tok_send_packet(struct sk_buff *skb, struct device *dev); static struct enet_statistics * tok_get_stats(struct device *dev); +void tr_readlog(struct device *dev); static struct timer_list tr_timer={NULL,NULL,0,0L,tok_open_adapter}; @@ -331,6 +337,7 @@ memset(ti, 0, sizeof(struct tok_info)); ti->mmio= t_mmio; + ti->readlog_pending = 0; dev->priv = ti; /* this seems like the logical use of the field ... let's try some empirical tests @@ -730,6 +737,7 @@ ti->current_skb=NULL; } dev->tbusy=0; + if (ti->readlog_pending) tr_readlog(dev); } } break; @@ -746,6 +754,7 @@ ti->current_skb=NULL; } dev->tbusy=0; + if (ti->readlog_pending) tr_readlog(dev); } } break; @@ -761,7 +770,7 @@ ti->current_skb=NULL; open_ret_code = readb(ti->init_srb +offsetof(struct srb_open_response, ret_code)); - open_error_code = readw(ti->init_srb +offsetof(struct srb_open_response, error_code)); + open_error_code = ntohs(readw(ti->init_srb +offsetof(struct srb_open_response, error_code))); if (open_ret_code==7) { @@ -774,7 +783,7 @@ DPRINTK("retrying open to adjust to ring speed\n"); else if ((open_error_code==0x2d) && ti->auto_ringspeedsave) DPRINTK("No signal detected for Auto Speed Detection\n"); - else DPRINTK("Unrecoverable error: error code = %02X\n", + else DPRINTK("Unrecoverable error: error code = %04x\n", open_error_code); } else if (!open_ret_code) { @@ -887,10 +896,6 @@ case REC_DATA: case XMIT_UI_FRAME: case XMIT_DIR_FRAME: - if (readb(ti->asb+2)!=0xff) /* checks ret_code */ - DPRINTK("ASB error %02X in cmd %02X\n", - (int)readb(ti->asb+2), - (int)readb(ti->asb)); break; default: @@ -899,6 +904,9 @@ } /* ASB command check */ + if (readb(ti->asb+2)!=0xff) /* checks ret_code */ + DPRINTK("ASB error %02X in cmd %02X\n", + (int)readb(ti->asb+2),(int)readb(ti->asb)); writeb(~ASB_FREE_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); } /* ASB response */ @@ -938,14 +946,10 @@ DPRINTK("New ring status: %02X\n", ring_status); if (ring_status & LOG_OVERFLOW) { - - writeb(DIR_READ_LOG, ti->srb); - writeb(INT_ENABLE, - ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); - writeb(CMD_IN_SRB, - ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - dev->tbusy=1; /* really srb busy... */ - + if (dev->tbusy) + ti->readlog_pending = 1; + else + tr_readlog(dev); } } break; @@ -1269,6 +1273,7 @@ dev_kfree_skb(ti->current_skb,FREE_WRITE); ti->current_skb=NULL; mark_bh(NET_BH); + if (ti->readlog_pending) tr_readlog(dev); } static void tr_rx(struct device *dev) @@ -1468,7 +1473,18 @@ } return 0; -} +} + +void tr_readlog(struct device *dev) { + struct tok_info *ti; + ti=(struct tok_info *) dev->priv; + + ti->readlog_pending = 0; + writeb(DIR_READ_LOG, ti->srb); + writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); + writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + dev->tbusy=1; /* really srb busy... */ +} /* tok_get_stats(): Basically a scaffold routine which will return the address of the tr_statistics structure associated with @@ -1493,16 +1509,21 @@ 0, 0, 0, NULL, tok_probe }; static int io = 0xa20; +static char *device = NULL; int init_module(void) { + if (device) + strcpy(dev_ibmtr.name,device); + else if (!dev_ibmtr.name[0]) strcpy(dev_ibmtr.name,"tr0"); + if (io == 0) printk("ibmtr: You should not use auto-probing with insmod!\n"); dev_ibmtr.base_addr = io; dev_ibmtr.irq = 0; if (register_netdev(&dev_ibmtr) != 0) { - printk("ibmtr: register_netdev() returned non-zero.\n"); + printk("ibmtr: No adapters were found.\n"); return -EIO; } return 0; diff -ur --new-file old/linux/drivers/net/ibmtr.h new/linux/drivers/net/ibmtr.h --- old/linux/drivers/net/ibmtr.h Fri Apr 12 08:49:38 1996 +++ new/linux/drivers/net/ibmtr.h Thu Aug 29 18:15:14 1996 @@ -206,6 +206,7 @@ struct tr_statistics tr_stats; unsigned char auto_ringspeedsave; open_state open_status; + unsigned char readlog_pending; }; /* token ring adapter commands */ diff -ur --new-file old/linux/drivers/net/lance.c new/linux/drivers/net/lance.c --- old/linux/drivers/net/lance.c Thu Jun 6 11:59:42 1996 +++ new/linux/drivers/net/lance.c Thu Aug 29 18:15:14 1996 @@ -30,9 +30,10 @@ to Donald Becker. I didn't receive any answer on all my letters to him. Who knows why... But may be you are more lucky? ;-) SAW + Fixed 7990 autoIRQ failure and reversed unneeded alignment. 8/20/96 djb */ -static const char *version = "lance.c:v1.08.02 Mar 17 1996 tsbogend@bigbug.franken.de\n"; +static const char *version = "lance.c:v1.09 Aug 20 1996 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; #include #include @@ -314,7 +315,8 @@ #if defined(CONFIG_PCI) if (pcibios_present()) { int pci_index; - printk("lance.c: PCI bios is present, checking for devices...\n"); + if (lance_debug > 1) + printk("lance.c: PCI bios is present, checking for devices...\n"); for (pci_index = 0; pci_index < 8; pci_index++) { unsigned char pci_bus, pci_device_fn; unsigned int pci_ioaddr; @@ -449,16 +451,19 @@ } #endif /* Make certain the data structures used by the LANCE are aligned and DMAble. */ - lp = (struct lance_private *) LANCE_KMALLOC(sizeof(*lp)); + lp = (struct lance_private *)(((unsigned long)kmalloc(sizeof(*lp)+7, + GFP_DMA | GFP_KERNEL)+7) & ~7); if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp); memset(lp, 0, sizeof(*lp)); dev->priv = lp; lp->name = chipname; - /* I'm not sure that buffs also must be aligned but it's safer to do it -- SAW */ - lp->rx_buffs = (unsigned long) LANCE_KMALLOC(PKT_BUF_SZ*RX_RING_SIZE); - lp->tx_bounce_buffs = NULL; + lp->rx_buffs = (unsigned long)kmalloc(PKT_BUF_SZ*RX_RING_SIZE, + GFP_DMA | GFP_KERNEL); if (lance_need_isa_bounce_buffers) - lp->tx_bounce_buffs = LANCE_KMALLOC(PKT_BUF_SZ*TX_RING_SIZE); + lp->tx_bounce_buffs = kmalloc(PKT_BUF_SZ*TX_RING_SIZE, + GFP_DMA | GFP_KERNEL); + else + lp->tx_bounce_buffs = NULL; lp->chip_version = lance_version; @@ -516,7 +521,7 @@ } if (dev->irq >= 2) printk(" assigned IRQ %d", dev->irq); - else { + else if (lance_version != 0) { /* 7990 boards need DMA detection first. */ /* To auto-IRQ we enable the initialization-done and DMA error interrupts. For ISA boards we get a DMA error, but VLB and PCI boards will work. */ @@ -525,7 +530,7 @@ /* Trigger an initialization just for the interrupt. */ outw(0x0041, ioaddr+LANCE_DATA); - dev->irq = autoirq_report(1); + dev->irq = autoirq_report(2); if (dev->irq) printk(", probed IRQ %d", dev->irq); else { @@ -581,6 +586,20 @@ printk("DMA detection failed.\n"); return; } + } + + if (lance_version == 0 && dev->irq == 0) { + /* We may auto-IRQ now that we have a DMA channel. */ + /* Trigger an initialization just for the interrupt. */ + autoirq_setup(0); + outw(0x0041, ioaddr+LANCE_DATA); + + dev->irq = autoirq_report(4); + if (dev->irq == 0) { + printk(" Failed to detect the 7990 IRQ line.\n"); + return; + } + printk(" Auto-IRQ detected IRQ%d.\n", dev->irq); } if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { diff -ur --new-file old/linux/drivers/net/ne.c new/linux/drivers/net/ne.c --- old/linux/drivers/net/ne.c Tue May 28 06:39:18 1996 +++ new/linux/drivers/net/ne.c Mon Oct 7 18:27:46 1996 @@ -299,14 +299,21 @@ wordlength = 1; } + /* At this point, wordlength *only* tells us if the SA_prom is doubled + up or not because some broken PCI cards don't respect the byte-wide + request in program_seq above, and hence don't have doubled up values. + These broken cards would otherwise be detected as an ne1000. */ + + if (wordlength == 2) + for (i = 0; i < 16; i++) + SA_prom[i] = SA_prom[i+i]; + + if (pci_irq_line) + wordlength = 2; /* Catch broken cards mentioned above. */ + if (wordlength == 2) { /* We must set the 8390 for word mode. */ outb_p(0x49, ioaddr + EN0_DCFG); - /* We used to reset the ethercard here, but it doesn't seem - to be necessary. */ - /* Un-double the SA_prom values. */ - for (i = 0; i < 16; i++) - SA_prom[i] = SA_prom[i+i]; start_page = NESM_START_PG; stop_page = NESM_STOP_PG; } else { diff -ur --new-file old/linux/drivers/net/net_init.c new/linux/drivers/net/net_init.c --- old/linux/drivers/net/net_init.c Sat Nov 16 00:48:04 1996 +++ new/linux/drivers/net/net_init.c Sat Nov 16 00:48:32 1996 @@ -21,7 +21,12 @@ Changed 29/10/95, Alan Cox to pass sockaddr's around for mac addresses. - 14/06/96 - Paul Gortmaker: Add generic eth_change_mtu() function. + 14/06/96 - Paul Gortmaker: Add generic eth_change_mtu() function. + + August 12, 1996 - Lawrence V. Stefani: Added fddi_change_mtu() and + fddi_setup() functions. + Sept. 10, 1996 - Lawrence V. Stefani: Increased hard_header_len to + include 3 pad bytes. */ #include @@ -34,6 +39,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_NET_ALIAS @@ -154,6 +160,18 @@ return 0; } +#ifdef CONFIG_FDDI + +static int fddi_change_mtu(struct device *dev, int new_mtu) +{ + if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN)) + return(-EINVAL); + dev->mtu = new_mtu; + return(0); +} + +#endif + void ether_setup(struct device *dev) { int i; @@ -229,6 +247,43 @@ dev->pa_mask = 0; dev->pa_alen = 4; } + +#endif + +#ifdef CONFIG_FDDI + +void fddi_setup(struct device *dev) + { + int i; + + /* + * Fill in the fields of the device structure with FDDI-generic values. + * This should be in a common file instead of per-driver. + */ + for (i=0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); + + dev->change_mtu = fddi_change_mtu; + dev->hard_header = fddi_header; + dev->rebuild_header = fddi_rebuild_header; + + dev->type = ARPHRD_FDDI; + dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */ + dev->mtu = FDDI_K_SNAP_DLEN; /* Assume max payload of 802.2 SNAP frame */ + dev->addr_len = FDDI_K_ALEN; + dev->tx_queue_len = 100; /* Long queues on FDDI */ + + memset(dev->broadcast, 0xFF, FDDI_K_ALEN); + + /* New-style flags */ + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = 4; + return; + } #endif diff -ur --new-file old/linux/drivers/net/ni65.c new/linux/drivers/net/ni65.c --- old/linux/drivers/net/ni65.c Fri Apr 12 08:49:38 1996 +++ new/linux/drivers/net/ni65.c Fri Nov 1 22:07:23 1996 @@ -1,8 +1,15 @@ /* - * ni6510 (am7990 'lance' chip) driver for Linux-net-3 by MH - * Alphacode v0.51 (96/02/20) for 1.3.66 (or later) + * ni6510 (am7990 'lance' chip) driver for Linux-net-3 + * BETAcode v0.71 (96/09/29) for 2.0.0 (or later) + * copyrights (c) 1994,1995,1996 by M.Hipp + * + * This driver can handle the old ni6510 board and the newer ni6510 + * EtherBlaster. (probably it also works with every full NE2100 + * compatible card) * - * copyright (c) 1994,1995,1996 by M.Hipp + * To compile as module, type: + * gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ -DMODULE -c ni65.c + * driver probes: io: 0x360,0x300,0x320,0x340 / dma: 3,5,6,7 * * This is an extension to the Linux operating system, and is covered by the * same Gnu Public License that covers the Linux-kernel. @@ -16,27 +23,41 @@ * and from the original drivers by D.Becker * * known problems: - * on some PCI boards (including my own) the card/board/ISA-bridge has - * problems with bus master DMA. This results in lotsa overruns. - * It may help to '#define RCV_PARANOIA_CHECK' - * or just play with your BIOS options to optimize ISA-DMA access. + * - on some PCI boards (including my own) the card/board/ISA-bridge has + * problems with bus master DMA. This results in lotsa overruns. + * It may help to '#define RCV_PARANOIA_CHECK' or try to #undef + * the XMT and RCV_VIA_SKB option .. this reduces driver performance. + * Or just play with your BIOS options to optimize ISA-DMA access. + * Maybe you also wanna play with the LOW_PERFORAMCE and MID_PERFORMANCE + * defines -> please report me your experience then + * - Harald reported for ASUS SP3G mainboards, that you should use + * the 'optimal settings' from the user's manual on page 3-12! * * credits: * thanx to Jason Sullivan for sending me a ni6510 card! + * lot of debug runs with ASUS SP3G Boards (Intel Saturn) by Harald Koenig * - * simple performance test: - * 8.1 seconds for getting a 8MB file via FTP -> near 1MB/s + * simple performance test: (486DX-33/Ni6510-EB receives from 486DX4-100/Ni6510-EB) + * average: FTP -> 8384421 bytes received in 8.5 seconds + * (no RCV_VIA_SKB,no XMT_VIA_SKB,PARANOIA_CHECK,4 XMIT BUFS, 8 RCV_BUFFS) + * peak: FTP -> 8384421 bytes received in 7.5 seconds + * (RCV_VIA_SKB,XMT_VIA_SKB,no PARANOIA_CHECK,1(!) XMIT BUF, 16 RCV BUFFS) */ /* - * 96.Feb.19: fixed a few bugs .. cleanups .. tested for 1.3.66 + * 96.Sept.29: virt_to_bus stuff added for new memory modell + * 96.April.29: Added Harald Koenig's Patches (MH) + * 96.April.13: enhanced error handling .. more tests (MH) + * 96.April.5/6: a lot of performance tests. Got it stable now (hopefully) (MH) + * 96.April.1: (no joke ;) .. added EtherBlaster and Module support (MH) + * 96.Feb.19: fixed a few bugs .. cleanups .. tested for 1.3.66 (MH) * hopefully no more 16MB limit * * 95.Nov.18: multicast tweaked (AC). * * 94.Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH) * - * 94,July.16: fixed bugs in recv_skb and skb-alloc stuff (MH) + * 94.July.16: fixed bugs in recv_skb and skb-alloc stuff (MH) */ #include @@ -56,75 +77,121 @@ #include #include +#include +#include + #include "ni65.h" /* - * the current setting allows max. performance + * the current setting allows an acceptable performance * for 'RCV_PARANOIA_CHECK' read the 'known problems' part in * the header of this file + * 'invert' the defines for max. performance. This may cause DMA problems + * on some boards (e.g on my ASUS SP3G) */ -#define RCV_VIA_SKB -#undef RCV_PARANOIA_CHECK -#define XMT_VIA_SKB +#undef XMT_VIA_SKB +#undef RCV_VIA_SKB +#define RCV_PARANOIA_CHECK + +#define MID_PERFORMANCE + +#if defined( LOW_PERFORMANCE ) + static int isa0=7,isa1=7,csr80=0x0c10; +#elif defined( MID_PERFORMANCE ) + static int isa0=5,isa1=5,csr80=0x2810; +#else /* high performance */ + static int isa0=4,isa1=4,csr80=0x0017; +#endif /* - * a few card specific defines + * a few card/vendor specific defines */ -#define NI65_TOTAL_SIZE 16 -#define NI65_ADDR0 0x02 -#define NI65_ADDR1 0x07 -#define NI65_ADDR2 0x01 -#define NI65_ID0 0x00 -#define NI65_ID1 0x55 +#define NI65_ID0 0x00 +#define NI65_ID1 0x55 +#define NI65_EB_ID0 0x52 +#define NI65_EB_ID1 0x44 +#define NE2100_ID0 0x57 +#define NE2100_ID1 0x57 -#define PORT dev->base_addr +#define PORT p->cmdr_addr /* * buffer configuration */ +#if 1 +#define RMDNUM 16 +#define RMDNUMMASK 0x80000000 +#else #define RMDNUM 8 #define RMDNUMMASK 0x60000000 /* log2(RMDNUM)<<29 */ +#endif + +#if 0 +#define TMDNUM 1 +#define TMDNUMMASK 0x00000000 +#else #define TMDNUM 4 #define TMDNUMMASK 0x40000000 /* log2(TMDNUM)<<29 */ +#endif -#define R_BUF_SIZE 1536 -#define T_BUF_SIZE 1536 +/* slightly oversized */ +#define R_BUF_SIZE 1544 +#define T_BUF_SIZE 1544 /* * lance register defines */ #define L_DATAREG 0x00 #define L_ADDRREG 0x02 - #define L_RESET 0x04 #define L_CONFIG 0x05 -#define L_EBASE 0x08 +#define L_BUSIF 0x06 /* - * to access the am7990-regs, you have to write + * to access the lance/am7990-regs, you have to write * reg-number into L_ADDRREG, then you can access it using L_DATAREG */ -#define CSR0 0x00 -#define CSR1 0x01 -#define CSR2 0x02 -#define CSR3 0x03 +#define CSR0 0x00 +#define CSR1 0x01 +#define CSR2 0x02 +#define CSR3 0x03 + +#define INIT_RING_BEFORE_START 0x1 +#define FULL_RESET_ON_ERROR 0x2 +#if 0 #define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \ outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);} #define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\ inw(PORT+L_DATAREG)) +#if 0 #define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);} +#else +#define writedatareg(val) { writereg(val,CSR0); } +#endif +#else +#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);outw(val,PORT+L_DATAREG);} +#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_DATAREG)) +#define writedatareg(val) { writereg(val,CSR0); } +#endif -static int ni65_probe1(struct device **dev,int); -static void ni65_interrupt(int irq, void * dev_id, struct pt_regs *regs); -static void ni65_recv_intr(struct device *dev,int); -static void ni65_xmit_intr(struct device *dev,int); -static int ni65_open(struct device *dev); -static int ni65_am7990_reinit(struct device *dev); -static int ni65_send_packet(struct sk_buff *skb, struct device *dev); -static int ni65_close(struct device *dev); -static struct enet_statistics *ni65_get_stats(struct device *); -static void set_multicast_list(struct device *dev); +static unsigned char ni_vendor[] = { 0x02,0x07,0x01 }; + +static struct card { + unsigned char id0,id1; + short id_offset; + short total_size; + short cmd_offset; + short addr_offset; + unsigned char *vendor_id; + char *cardname; + unsigned char config; +} cards[] = { + { NI65_ID0,NI65_ID1,0x0e,0x10,0x0,0x8,ni_vendor,"ni6510", 0x1 } , + { NI65_EB_ID0,NI65_EB_ID1,0x0e,0x18,0x10,0x0,ni_vendor,"ni6510 EtherBlaster", 0x2 } , + { NE2100_ID0,NE2100_ID1,0x0e,0x18,0x10,0x0,NULL,"generic NE2100", 0x0 } +}; +#define NUM_CARDS 3 struct priv { @@ -142,132 +209,267 @@ struct sk_buff *tmd_skb[TMDNUM]; #endif void *tmdbounce[TMDNUM]; + int tmdbouncenum; int lock,xmit_queued; struct enet_statistics stats; + void *self; + int cmdr_addr; + int cardno; + int features; }; +static int ni65_probe1(struct device *dev,int); +static void ni65_interrupt(int irq, void * dev_id, struct pt_regs *regs); +static void ni65_recv_intr(struct device *dev,int); +static void ni65_xmit_intr(struct device *dev,int); +static int ni65_open(struct device *dev); +static int ni65_lance_reinit(struct device *dev); +static void ni65_init_lance(struct priv *p,unsigned char*,int,int); +static int ni65_send_packet(struct sk_buff *skb, struct device *dev); +static int ni65_close(struct device *dev); +static int ni65_alloc_buffer(struct device *dev); +static void ni65_free_buffer(struct priv *p); +static struct enet_statistics *ni65_get_stats(struct device *); +static void set_multicast_list(struct device *dev); + static int irqtab[] = { 9,12,15,5 }; /* irq config-translate */ -static int dmatab[] = { 0,3,5,6 }; /* dma config-translate */ -static int debuglevel = 0; +static int dmatab[] = { 0,3,5,6,7 }; /* dma config-translate and autodetect */ + +static int debuglevel = 1; /* - * open (most done by init) + * set 'performance' registers .. we must STOP lance for that + */ +static void ni65_set_performance(struct priv *p) +{ + writereg(CSR0_STOP | CSR0_CLRALL,CSR0); /* STOP */ + + if( !(cards[p->cardno].config & 0x02) ) + return; + + outw(80,PORT+L_ADDRREG); + if(inw(PORT+L_ADDRREG) != 80) + return; + + writereg( (csr80 & 0x3fff) ,80); /* FIFO watermarks */ + outw(0,PORT+L_ADDRREG); + outw((short)isa0,PORT+L_BUSIF); /* write ISA 0: DMA_R : isa0 * 50ns */ + outw(1,PORT+L_ADDRREG); + outw((short)isa1,PORT+L_BUSIF); /* write ISA 1: DMA_W : isa1 * 50ns */ + + outw(CSR0,PORT+L_ADDRREG); /* switch back to CSR0 */ +} + +/* + * open interface (up) */ static int ni65_open(struct device *dev) { - if(ni65_am7990_reinit(dev)) + struct priv *p = (struct priv *) dev->priv; + int irqval = request_irq(dev->irq, &ni65_interrupt,0, + cards[p->cardno].cardname,NULL); + if (irqval) { + printk ("%s: unable to get IRQ %d (irqval=%d).\n", + dev->name,dev->irq, irqval); + return -EAGAIN; + } + irq2dev_map[dev->irq] = dev; + + if(ni65_lance_reinit(dev)) { dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; + MOD_INC_USE_COUNT; return 0; } else { + irq2dev_map[dev->irq] = NULL; + free_irq(dev->irq,NULL); dev->start = 0; return -EAGAIN; } } +/* + * close interface (down) + */ static int ni65_close(struct device *dev) { - outw(0,PORT+L_RESET); /* that's the hard way */ + struct priv *p = (struct priv *) dev->priv; + + outw(inw(PORT+L_RESET),PORT+L_RESET); /* that's the hard way */ + +#ifdef XMT_VIA_SKB + { + int i; + for(i=0;itmd_skb[i]) { + dev_kfree_skb(p->tmd_skb[i],FREE_WRITE); + p->tmd_skb[i] = NULL; + } + } + } +#endif + irq2dev_map[dev->irq] = NULL; + free_irq(dev->irq,NULL); dev->tbusy = 1; dev->start = 0; + MOD_DEC_USE_COUNT; return 0; } /* * Probe The Card (not the lance-chip) - * and set hardaddress */ +#ifdef MODULE +static +#endif int ni65_probe(struct device *dev) { int *port; - static int ports[] = {0x300,0x320,0x340,0x360, 0}; + static int ports[] = {0x360,0x300,0x320,0x340, 0}; - if(dev) { - int base_addr = dev->base_addr; - if (base_addr > 0x1ff) /* Check a single specified location. */ - return ni65_probe1(&dev, base_addr); - else if (base_addr > 0) /* Don't probe at all. */ - return -ENXIO; - dev->base_addr = base_addr; - } + if (dev->base_addr > 0x1ff) /* Check a single specified location. */ + return ni65_probe1(dev, dev->base_addr); + else if (dev->base_addr > 0) /* Don't probe at all. */ + return -ENXIO; for (port = ports; *port; port++) { - int ioaddr = *port; - - if (check_region(ioaddr, NI65_TOTAL_SIZE)) - continue; - if( !(inb(ioaddr+L_EBASE+6) == NI65_ID0) || - !(inb(ioaddr+L_EBASE+7) == NI65_ID1) ) - continue; - if (ni65_probe1(&dev, ioaddr) == 0) + if (ni65_probe1(dev, *port) == 0) return 0; } return -ENODEV; } -int ni65_init(void) -{ - ni65_probe(NULL); - return 0; -} - -static int ni65_probe1(struct device **dev1,int ioaddr) +/* + * this is the real card probe .. + */ +static int ni65_probe1(struct device *dev,int ioaddr) { - int i; - unsigned char *ptr; + int i,j; struct priv *p; - struct device *dev = *dev1; - if(inb(ioaddr+L_EBASE+0) != NI65_ADDR0 || inb(ioaddr+L_EBASE+1) != NI65_ADDR1 - || inb(ioaddr+L_EBASE+2) != NI65_ADDR2) - { - printk("%s: wrong Hardaddress \n",dev ? dev->name : "ni6510" ); - return -ENODEV; - } - - if(!dev) { - dev = init_etherdev(0,0); - *dev1 = dev; + for(i=0;i= 0) { + if(inb(ioaddr+cards[i].id_offset+0) != cards[i].id0 || + inb(ioaddr+cards[i].id_offset+1) != cards[i].id1) { + continue; + } + } + if(cards[i].vendor_id) { + for(j=0;j<3;j++) + if(inb(ioaddr+cards[i].addr_offset+j) != cards[i].vendor_id[j]) + continue; + } + break; } - dev->base_addr = ioaddr; + if(i == NUM_CARDS) + return -ENODEV; - for(i=0;i<6;i++) - dev->dev_addr[i] = inb(PORT+L_EBASE+i); + for(j=0;j<6;j++) + dev->dev_addr[j] = inb(ioaddr+cards[i].addr_offset+j); - if(dev->irq == 0) - dev->irq = irqtab[(inw(PORT+L_CONFIG)>>2)&3]; - if(dev->dma == 0) - dev->dma = dmatab[inw(PORT+L_CONFIG)&3]; - - printk("%s: %s found at %#3lx, IRQ %d DMA %d.\n", dev->name, - "ni6510", dev->base_addr, dev->irq,dev->dma); - - { - int irqval = request_irq(dev->irq, &ni65_interrupt,0,"ni6510",NULL); - if (irqval) { - printk ("%s: unable to get IRQ %d (irqval=%d).\n", - dev->name,dev->irq, irqval); - return -EAGAIN; + if( (j=ni65_alloc_buffer(dev)) < 0) + return j; + p = (struct priv *) dev->priv; + p->cmdr_addr = ioaddr + cards[i].cmd_offset; + p->cardno = i; + + printk("%s: %s found at %#3x, ", dev->name, cards[p->cardno].cardname , ioaddr); + + outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */ + if( (j=readreg(CSR0)) != 0x4) { + printk(KERN_ERR "can't RESET card: %04x\n",j); + ni65_free_buffer(p); + return -EAGAIN; + } + + outw(88,PORT+L_ADDRREG); + if(inw(PORT+L_ADDRREG) == 88) { + unsigned long v; + v = inw(PORT+L_DATAREG); + v <<= 16; + outw(89,PORT+L_ADDRREG); + v |= inw(PORT+L_DATAREG); + printk("Version %#08lx, ",v); + p->features = INIT_RING_BEFORE_START; + } + else { + printk("ancient LANCE, "); + p->features = 0x0; + } + + if(test_bit(0,&cards[i].config)) { + dev->irq = irqtab[(inw(ioaddr+L_CONFIG)>>2)&3]; + dev->dma = dmatab[inw(ioaddr+L_CONFIG)&3]; + printk("IRQ %d (from card), DMA %d (from card).\n",dev->irq,dev->dma); + } + else { + if(dev->dma == 0) { + /* 'stuck test' from lance.c */ + int dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) | (inb(DMA2_STAT_REG) & 0xf0); + for(i=1;i<5;i++) { + int dma = dmatab[i]; + if(test_bit(dma,&dma_channels) || request_dma(dma,"ni6510")) + continue; + disable_dma(dma); + set_dma_mode(dma,DMA_MODE_CASCADE); + enable_dma(dma); + ni65_init_lance(p,dev->dev_addr,0,0); /* trigger memory access */ + disable_dma(dma); + free_dma(dma); + if(readreg(CSR0) & CSR0_IDON) + break; + } + if(i == 5) { + printk("Can't detect DMA channel!\n"); + ni65_free_buffer(p); + return -EAGAIN; + } + dev->dma = dmatab[i]; + printk("DMA %d (autodetected), ",dev->dma); } - if(request_dma(dev->dma, "ni6510") != 0) + else + printk("DMA %d (assigned), ",dev->dma); + + if(dev->irq < 2) { - printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma); - free_irq(dev->irq,NULL); - return -EAGAIN; + ni65_init_lance(p,dev->dev_addr,0,0); + autoirq_setup(0); + writereg(CSR0_INIT|CSR0_INEA,CSR0); /* trigger interrupt */ + + if(!(dev->irq = autoirq_report(2))) + { + printk("Failed to detect IRQ line!\n"); + ni65_free_buffer(p); + return -EAGAIN; + } + printk("IRQ %d (autodetected).\n",dev->irq); } + else + printk("IRQ %d (assigned).\n",dev->irq); + } + + if(request_dma(dev->dma, cards[p->cardno].cardname ) != 0) + { + printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma); + ni65_free_buffer(p); + return -EAGAIN; } - irq2dev_map[dev->irq] = dev; /* - * Grab the region so we can find another board if autoIRQ fails. + * Grab the region so we can find another board. */ - request_region(ioaddr,NI65_TOTAL_SIZE,"ni6510"); + request_region(ioaddr,cards[p->cardno].total_size,cards[p->cardno].cardname); + + dev->base_addr = ioaddr; dev->open = ni65_open; dev->stop = ni65_close; @@ -281,75 +483,240 @@ dev->tbusy = 0; dev->start = 0; + return 0; /* everything is OK */ +} + +/* + * set lance register and trigger init + */ +static void ni65_init_lance(struct priv *p,unsigned char *daddr,int filter,int mode) +{ + int i; + u32 pib; + + writereg(CSR0_CLRALL|CSR0_STOP,CSR0); + + for(i=0;i<6;i++) + p->ib.eaddr[i] = daddr[i]; + + for(i=0;i<8;i++) + p->ib.filter[i] = filter; + p->ib.mode = mode; + + p->ib.trp = (u32) virt_to_bus(p->tmdhead) | TMDNUMMASK; + p->ib.rrp = (u32) virt_to_bus(p->rmdhead) | RMDNUMMASK; + writereg(0,CSR3); /* busmaster/no word-swap */ + pib = (u32) virt_to_bus(&p->ib); + writereg(pib & 0xffff,CSR1); + writereg(pib >> 16,CSR2); + + writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */ + + for(i=0;i<32;i++) + { + udelay(4000); + if(inw(PORT+L_DATAREG) & (CSR0_IDON | CSR0_MERR) ) + break; /* init ok ? */ + } +} + +/* + * allocate memory area and check the 16MB border + */ +static void *ni65_alloc_mem(struct device *dev,char *what,int size,int type) +{ + struct sk_buff *skb=NULL; + unsigned char *ptr; + void *ret; + + if(type) { + ret = skb = alloc_skb(2+16+size,GFP_KERNEL|GFP_DMA); + if(!skb) { + printk("%s: unable to allocate %s memory.\n",dev->name,what); + return NULL; + } + skb->dev = dev; + skb_reserve(skb,2+16); + skb_put(skb,R_BUF_SIZE); /* grab the whole space .. (not necessary) */ + ptr = skb->data; + } + else { + ret = ptr = kmalloc(T_BUF_SIZE,GFP_KERNEL | GFP_DMA); + if(!ret) { + printk("%s: unable to allocate %s memory.\n",dev->name,what); + return NULL; + } + } + if( (u32) virt_to_bus(ptr+size) > 0x1000000) { + printk("%s: unable to allocate %s memory in lower 16MB!\n",dev->name,what); + if(type) + kfree_skb(skb,FREE_WRITE); + else + kfree(ptr); + return NULL; + } + return ret; +} + +/* + * allocate all memory structures .. send/recv buffers etc ... + */ +static int ni65_alloc_buffer(struct device *dev) +{ + unsigned char *ptr; + struct priv *p; + int i; + /* * we need 8-aligned memory .. */ - ptr = kmalloc(sizeof(struct priv)+8,GFP_KERNEL|GFP_DMA); + ptr = ni65_alloc_mem(dev,"BUFFER",sizeof(struct priv)+8,0); if(!ptr) return -ENOMEM; - ptr = (unsigned char *) (((unsigned long) ptr + 7) & ~0x7); - if( (unsigned long) ptr + sizeof(struct priv) > 0x1000000) { - printk("%s: Can't alloc buffer in lower 16MB!\n",dev->name); - return -EAGAIN; - } - p = dev->priv = (struct priv *) ptr; + + p = dev->priv = (struct priv *) (((unsigned long) ptr + 7) & ~0x7); memset((char *) dev->priv,0,sizeof(struct priv)); + p->self = ptr; for(i=0;iname); +#ifdef XMT_VIA_SKB + p->tmd_skb[i] = NULL; +#endif + p->tmdbounce[i] = ni65_alloc_mem(dev,"XMIT",T_BUF_SIZE,0); + if(!p->tmdbounce[i]) { + ni65_free_buffer(p); + return -ENOMEM; + } + } + + for(i=0;irecv_skb[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,1); + if(!p->recv_skb[i]) { + ni65_free_buffer(p); return -ENOMEM; } - if( (unsigned long) (ptr+T_BUF_SIZE) > 0x1000000) { - printk("%s: Can't alloc Xmit-Mem in lower 16MB!\n",dev->name); - return -EAGAIN; +#else + p->recvbounce[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,0); + if(!p->recvbounce[i]) { + ni65_free_buffer(p); + return -ENOMEM; } - p->tmdbounce[i] = ptr; +#endif + } + + return 0; /* everything is OK */ +} + +/* + * free buffers and private struct + */ +static void ni65_free_buffer(struct priv *p) +{ + int i; + + if(!p) + return; + + for(i=0;itmdbounce[i]) + kfree(p->tmdbounce[i]); #ifdef XMT_VIA_SKB - p->tmd_skb[i] = NULL; + if(p->tmd_skb[i]) + dev_kfree_skb(p->tmd_skb[i],FREE_WRITE); #endif } + for(i=0;iname); - return -ENOMEM; - } - skb->dev = dev; - skb_reserve(skb,2); - skb_put(skb,R_BUF_SIZE); /* grab the whole space .. (not necessary) */ - if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000 ) { - printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name); - return -EAGAIN; - } - p->recv_skb[i] = skb; - } + if(p->recv_skb[i]) + dev_kfree_skb(p->recv_skb[i],FREE_WRITE); #else - for(i=0;irecvbounce[i] = kmalloc(R_BUF_SIZE,GFP_KERNEL | GFP_DMA )) ) { - printk("%s: unable to alloc recv-mem\n",dev->name); - return -ENOMEM; - } - if( (unsigned long) p->recvbounce[i] + R_BUF_SIZE > 0x1000000 ) { - printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name); - return -EAGAIN; - } - } + if(p->recvbounce[i]) + kfree(p->recvbounce[i]); +#endif + } + if(p->self) + kfree(p->self); +} + + +/* + * stop and (re)start lance .. e.g after an error + */ +static void ni65_stop_start(struct device *dev,struct priv *p) +{ + int csr0 = CSR0_INEA; + + writedatareg(CSR0_STOP); + + if(debuglevel > 1) + printk("ni65_stop_start\n"); + + if(p->features & INIT_RING_BEFORE_START) { + int i; +#ifdef XMT_VIA_SKB + struct sk_buff *skb_save[TMDNUM]; #endif + unsigned long buffer[TMDNUM]; + short blen[TMDNUM]; - return 0; /* we've found everything */ + if(p->xmit_queued) { + while(1) { + if((p->tmdhead[p->tmdlast].u.s.status & XMIT_OWN)) + break; + p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1); + if(p->tmdlast == p->tmdnum) + break; + } + } + + for(i=0;itmdhead + i; +#ifdef XMT_VIA_SKB + skb_save[i] = p->tmd_skb[i]; +#endif + buffer[i] = (u32) bus_to_virt(tmdp->u.buffer); + blen[i] = tmdp->blen; + tmdp->u.s.status = 0x0; + } + + for(i=0;irmdhead + i; + rmdp->u.s.status = RCV_OWN; + } + p->tmdnum = p->xmit_queued = 0; + writedatareg(CSR0_STRT | csr0); + + for(i=0;itmdlast) & (TMDNUM-1); + p->tmdhead[i].u.buffer = (u32) virt_to_bus((char *)buffer[num]); /* status is part of buffer field */ + p->tmdhead[i].blen = blen[num]; + if(p->tmdhead[i].u.s.status & XMIT_OWN) { + p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1); + p->xmit_queued = 1; + writedatareg(CSR0_TDMD | CSR0_INEA | csr0); + } +#ifdef XMT_VIA_SKB + p->tmd_skb[i] = skb_save[num]; +#endif + } + p->rmdnum = p->tmdlast = 0; + if(!p->lock) + dev->tbusy = (p->tmdnum || !p->xmit_queued) ? 0 : 1; + dev->trans_start = jiffies; + } + else + writedatareg(CSR0_STRT | csr0); } /* * init lance (write init-values .. init-buffers) (open-helper) */ - -static int ni65_am7990_reinit(struct device *dev) +static int ni65_lance_reinit(struct device *dev) { int i; struct priv *p = (struct priv *) dev->priv; @@ -361,17 +728,16 @@ set_dma_mode(dev->dma,DMA_MODE_CASCADE); enable_dma(dev->dma); - outw(0,PORT+L_RESET); /* first: reset the card */ - if(inw(PORT+L_DATAREG) != 0x4) + outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */ + if( (i=readreg(CSR0) ) != 0x4) { - printk(KERN_ERR "%s: can't RESET ni6510 card: %04x\n",dev->name,(int) inw(PORT+L_DATAREG)); + printk(KERN_ERR "%s: can't RESET %s card: %04x\n",dev->name, + cards[p->cardno].cardname,(int) i); disable_dma(dev->dma); - free_dma(dev->dma); - free_irq(dev->irq, NULL); return 0; } - p->tmdnum = 0; p->tmdlast = 0; + p->rmdnum = p->tmdnum = p->tmdlast = p->tmdbouncenum = 0; for(i=0;itmdhead + i; @@ -386,66 +752,40 @@ tmdp->blen = tmdp->status2 = 0; } - p->rmdnum = 0; for(i=0;irmdhead + i; #ifdef RCV_VIA_SKB - rmdp->u.buffer = (unsigned long) p->recv_skb[i]->data; + rmdp->u.buffer = (u32) virt_to_bus(p->recv_skb[i]->data); #else - rmdp->u.buffer = (unsigned long) p->recvbounce[i]; + rmdp->u.buffer = (u32) virt_to_bus(p->recvbounce[i]); #endif rmdp->blen = -(R_BUF_SIZE-8); rmdp->mlen = 0; rmdp->u.s.status = RCV_OWN; } - for(i=0;i<6;i++) - p->ib.eaddr[i] = dev->dev_addr[i]; - - for(i=0;i<8;i++) - p->ib.filter[i] = 0x0; - p->ib.mode = 0x0; - - if(dev->flags & IFF_PROMISC) { - p->ib.mode = M_PROM; - } - else if(dev->mc_count || dev->flags & IFF_ALLMULTI) { - for(i=0;i<8;i++) - p->ib.filter[i] = 0xff; - } - - p->ib.trp = (unsigned long) p->tmdhead | TMDNUMMASK; - p->ib.rrp = (unsigned long) p->rmdhead | RMDNUMMASK; - - writereg(0,CSR3); /* busmaster/no word-swap */ - writereg((unsigned short) (((unsigned long) &(p->ib)) & 0xffff),CSR1); - writereg((unsigned short) (((unsigned long) &(p->ib))>>16),CSR2); - - writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */ + if(dev->flags & IFF_PROMISC) + ni65_init_lance(p,dev->dev_addr,0x00,M_PROM); + else if(dev->mc_count || dev->flags & IFF_ALLMULTI) + ni65_init_lance(p,dev->dev_addr,0xff,0x0); + else + ni65_init_lance(p,dev->dev_addr,0x00,0x00); /* + * ni65_set_lance_mem() sets L_ADDRREG to CSR0 * NOW, WE WILL NEVER CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED */ - for(i=0;i<32;i++) - { - __delay((loops_per_sec>>8)); /* wait a while */ - if(inw(PORT+L_DATAREG) & CSR0_IDON) - break; /* init ok ? */ - } - if(i == 32) - { - printk(KERN_ERR "%s: can't init am7990/lance, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG)); - disable_dma(dev->dma); - free_dma(dev->dma); - free_irq(dev->irq, NULL); - return 0; /* false */ - } - - writedatareg(CSR0_CLRALL | CSR0_INEA | CSR0_STRT); /* start lance , enable interrupts */ - - return 1; /* OK */ + if(inw(PORT+L_DATAREG) & CSR0_IDON) { + ni65_set_performance(p); + /* init OK: start lance , enable interrupts */ + writedatareg(CSR0_CLRALL | CSR0_INEA | CSR0_STRT); + return 1; /* ->OK */ + } + printk(KERN_ERR "%s: can't init lance, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG)); + disable_dma(dev->dma); + return 0; /* ->Error */ } /* @@ -455,6 +795,7 @@ { int csr0; struct device *dev = (struct device *) irq2dev_map[irq]; + struct priv *p; int bcnt = 32; if (dev == NULL) { @@ -462,62 +803,104 @@ return; } - dev->interrupt = 1; + if(set_bit(0,(int *) &dev->interrupt)) { + printk("ni65: oops .. interrupt while proceeding interrupt\n"); + return; + } + p = (struct priv *) dev->priv; while(--bcnt) { - csr0 = inw(PORT+L_DATAREG); - writedatareg(csr0 & CSR0_CLRALL); /* ack interrupts, disable int. */ + +#if 0 + writedatareg( (csr0 & CSR0_CLRALL) ); /* ack interrupts, disable int. */ +#else + writedatareg( (csr0 & CSR0_CLRALL) | CSR0_INEA ); /* ack interrupts, interrupts enabled */ +#endif if(!(csr0 & (CSR0_ERR | CSR0_RINT | CSR0_TINT))) break; + if(csr0 & CSR0_RINT) /* RECV-int? */ + ni65_recv_intr(dev,csr0); + if(csr0 & CSR0_TINT) /* XMIT-int? */ + ni65_xmit_intr(dev,csr0); + if(csr0 & CSR0_ERR) { struct priv *p = (struct priv *) dev->priv; - + if(debuglevel > 1) + printk("%s: general error: %04x.\n",dev->name,csr0); if(csr0 & CSR0_BABL) p->stats.tx_errors++; - if(csr0 & CSR0_MISS) + if(csr0 & CSR0_MISS) { + int i; + for(i=0;irmdhead[i].u.s.status); + printk("\n"); p->stats.rx_errors++; + } if(csr0 & CSR0_MERR) { - writedatareg(CSR0_STOP); - writedatareg(CSR0_STRT); + if(debuglevel > 1) + printk("%s: Ooops .. memory error: %04x.\n",dev->name,csr0); + ni65_stop_start(dev,p); } } - if(csr0 & CSR0_RINT) /* RECV-int? */ - ni65_recv_intr(dev,csr0); - if(csr0 & CSR0_TINT) /* XMIT-int? */ - ni65_xmit_intr(dev,csr0); } #ifdef RCV_PARANOIA_CHECK { + int j; + for(j=0;jpriv; - int i,f=0; - for(i=0;irmdhead + ((p->rmdnum - i - 1) & (RMDNUM-1)); - if(! (rmdp->u.s.status & RCV_OWN) ) - f = 1; - else if(f) + int i,k,num1,num2; + for(i=RMDNUM-1;i>0;i--) { + num2 = (p->rmdnum + i) & (RMDNUM-1); + if(!(p->rmdhead[num2].u.s.status & RCV_OWN)) break; } - if(i < RMDNUM) { - p->rmdnum = (p->rmdnum + 8 - i) & (RMDNUM - 1); - printk(KERN_ERR "%s: Ooops, receive ring corrupted\n",dev->name); + if(i) { + for(k=0;krmdnum + k) & (RMDNUM-1); + if(!(p->rmdhead[num1].u.s.status & RCV_OWN)) + break; + } + if(!k) + break; + + if(debuglevel > 0) + { + char buf[256],*buf1; + int k; + buf1 = buf; + for(k=0;krmdhead[k].u.s.status)); /* & RCV_OWN) ); */ + buf1 += 3; + } + *buf1 = 0; + printk(KERN_ERR "%s: Ooops, receive ring corrupted %2d %2d | %s\n",dev->name,p->rmdnum,i,buf); + } + p->rmdnum = num1; ni65_recv_intr(dev,csr0); + if((p->rmdhead[num2].u.s.status & RCV_OWN)) + break; /* ok, we are 'in sync' again */ } + else + break; + } } #endif - if(csr0 & (CSR0_RXON | CSR0_TXON) != (CSR0_RXON | CSR0_TXON) ) { - writedatareg(CSR0_STOP); - writedatareg(CSR0_STRT | CSR0_INEA); + if( (csr0 & (CSR0_RXON | CSR0_TXON)) != (CSR0_RXON | CSR0_TXON) ) { + printk("%s: RX or TX was offline -> restart\n",dev->name); + ni65_stop_start(dev,p); } else writedatareg(CSR0_INEA); + dev->interrupt = 0; return; @@ -539,13 +922,6 @@ if(tmdstat & XMIT_OWN) break; -#ifdef XMT_VIA_SKB - if(p->tmd_skb[p->tmdlast]) { - dev_kfree_skb(p->tmd_skb[p->tmdlast],FREE_WRITE); - p->tmd_skb[p->tmdlast] = NULL; - } -#endif - if(tmdstat & XMIT_ERR) { #if 0 @@ -558,20 +934,34 @@ if(tmdp->status2 & XMIT_LCAR) p->stats.tx_carrier_errors++; if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) { + /* this stops the xmitter */ p->stats.tx_fifo_errors++; - writedatareg(CSR0_STOP); - writedatareg(CSR0_STRT); - if(debuglevel > 1) + if(debuglevel > 0) printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name); + if(p->features & INIT_RING_BEFORE_START) { + tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END; /* test: resend this frame */ + ni65_stop_start(dev,p); + break; /* no more Xmit processing .. */ + } + else + ni65_stop_start(dev,p); } if(debuglevel > 2) printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2); - p->stats.tx_errors++; + if(!(csr0 & CSR0_BABL)) /* don't count errors twice */ + p->stats.tx_errors++; tmdp->status2 = 0; } else p->stats.tx_packets++; +#ifdef XMT_VIA_SKB + if(p->tmd_skb[p->tmdlast]) { + dev_kfree_skb(p->tmd_skb[p->tmdlast],FREE_WRITE); + p->tmd_skb[p->tmdlast] = NULL; + } +#endif + p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1); if(p->tmdlast == p->tmdnum) p->xmit_queued = 0; @@ -583,16 +973,17 @@ /* * We have received a packet */ - static void ni65_recv_intr(struct device *dev,int csr0) { struct rmd *rmdp; int rmdstat,len; + int cnt=0; struct priv *p = (struct priv *) dev->priv; rmdp = p->rmdhead + p->rmdnum; while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN)) { + cnt++; if( (rmdstat & (RCV_START | RCV_END | RCV_ERR)) != (RCV_START | RCV_END) ) /* error or oversized? */ { if(!(rmdstat & RCV_ERR)) { @@ -603,27 +994,27 @@ } } else { - printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n", - dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) ); + if(debuglevel > 2) + printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n", + dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) ); if(rmdstat & RCV_FRAM) p->stats.rx_frame_errors++; if(rmdstat & RCV_OFLO) p->stats.rx_over_errors++; - if(rmdstat & (RCV_OFLO | RCV_BUF_ERR) ) { - writedatareg(CSR0_STOP); - writedatareg(CSR0_STRT); - if(debuglevel > 1) - printk(KERN_ERR "%s: Rcv FIFO/BUFF error.\n",dev->name); - } - if(rmdstat & RCV_CRC) p->stats.rx_crc_errors++; + if(rmdstat & RCV_CRC) + p->stats.rx_crc_errors++; + if(rmdstat & RCV_BUF_ERR) + p->stats.rx_fifo_errors++; } - rmdp->u.s.status = RCV_OWN; /* change owner */ - p->stats.rx_errors++; + if(!(csr0 & CSR0_MISS)) /* don't count errors twice */ + p->stats.rx_errors++; } else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60) { #ifdef RCV_VIA_SKB - struct sk_buff *skb = dev_alloc_skb(R_BUF_SIZE+2); + struct sk_buff *skb = alloc_skb(R_BUF_SIZE+2+16,GFP_ATOMIC); + if (skb) + skb_reserve(skb,16); #else struct sk_buff *skb = dev_alloc_skb(len+2); #endif @@ -640,7 +1031,7 @@ struct sk_buff *skb1 = p->recv_skb[p->rmdnum]; skb_put(skb,R_BUF_SIZE); p->recv_skb[p->rmdnum] = skb; - rmdp->u.buffer = (unsigned long) skb->data; + rmdp->u.buffer = (u32) virt_to_bus(skb->data); skb = skb1; skb_trim(skb,len); } @@ -648,23 +1039,23 @@ skb_put(skb,len); eth_copy_and_sum(skb, (unsigned char *) p->recvbounce[p->rmdnum],len,0); #endif - rmdp->u.s.status = RCV_OWN; p->stats.rx_packets++; skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); } else { - rmdp->u.s.status = RCV_OWN; printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name); p->stats.rx_dropped++; } } else { - rmdp->u.s.status = RCV_OWN; printk(KERN_INFO "%s: received runt packet\n",dev->name); p->stats.rx_errors++; } + rmdp->blen = -(R_BUF_SIZE-8); + rmdp->mlen = 0; + rmdp->u.s.status = RCV_OWN; /* change owner */ p->rmdnum = (p->rmdnum + 1) & (RMDNUM-1); rmdp = p->rmdhead + p->rmdnum; } @@ -680,11 +1071,17 @@ if(dev->tbusy) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 5) + if (tickssofar < 50) return 1; printk(KERN_ERR "%s: xmitter timed out, try to restart!\n",dev->name); - ni65_am7990_reinit(dev); +{ + int i; + for(i=0;itmdhead[i].u.s.status); + printk("\n"); +} + ni65_lance_reinit(dev); dev->tbusy=0; dev->trans_start = jiffies; } @@ -708,28 +1105,37 @@ { short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - struct tmd *tmdp = p->tmdhead + p->tmdnum; + struct tmd *tmdp; long flags; #ifdef XMT_VIA_SKB if( (unsigned long) (skb->data + skb->len) > 0x1000000) { #endif - tmdp->u.buffer = (unsigned long ) p->tmdbounce[p->tmdnum]; - memcpy((char *) tmdp->u.buffer,(char *)skb->data, + + memcpy((char *) p->tmdbounce[p->tmdbouncenum] ,(char *)skb->data, (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len); dev_kfree_skb (skb, FREE_WRITE); + + save_flags(flags); + cli(); + + tmdp = p->tmdhead + p->tmdnum; + tmdp->u.buffer = (u32) virt_to_bus(p->tmdbounce[p->tmdbouncenum]); + p->tmdbouncenum = (p->tmdbouncenum + 1) & (TMDNUM - 1); + #ifdef XMT_VIA_SKB } else { - tmdp->u.buffer = (unsigned long) skb->data; + save_flags(flags); + cli(); + + tmdp = p->tmdhead + p->tmdnum; + tmdp->u.buffer = (u32) virt_to_bus(skb->data); p->tmd_skb[p->tmdnum] = skb; } #endif tmdp->blen = -len; - save_flags(flags); - cli(); - tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END; writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */ @@ -748,17 +1154,75 @@ static struct enet_statistics *ni65_get_stats(struct device *dev) { + +#if 0 + int i; + struct priv *p = (struct priv *) dev->priv; + for(i=0;irmdhead + ((p->rmdnum + i) & (RMDNUM-1)); + printk("%02x ",rmdp->u.s.status); + } + printk("\n"); +#endif + return &((struct priv *) dev->priv)->stats; } static void set_multicast_list(struct device *dev) { - if(!ni65_am7990_reinit(dev)) + if(!ni65_lance_reinit(dev)) printk(KERN_ERR "%s: Can't switch card into MC mode!\n",dev->name); dev->tbusy = 0; } +#ifdef MODULE +static struct device dev_ni65 = { + " ", /* "ni6510": device name inserted by net_init.c */ + 0, 0, 0, 0, + 0x360, 9, /* I/O address, IRQ */ + 0, 0, 0, NULL, ni65_probe }; + +/* set: io,irq,dma or set it when calling insmod */ +static int irq=0; +static int io=0; +static int dma=0; + +int init_module(void) +{ +#if 0 + if(io <= 0x0 || irq < 2) { + printk("ni65: Autoprobing not allowed for modules.\n"); + printk("ni65: Set symbols 'io' 'irq' and 'dma'\n"); + return -ENODEV; + } +#endif + dev_ni65.irq = irq; + dev_ni65.dma = dma; + dev_ni65.base_addr = io; + if (register_netdev(&dev_ni65) != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + struct priv *p; + p = (struct priv *) dev_ni65.priv; + if(!p) { + printk("Ooops .. no privat struct\n"); + return; + } + disable_dma(dev_ni65.dma); + free_dma(dev_ni65.dma); + release_region(dev_ni65.base_addr,cards[p->cardno].total_size); + ni65_free_buffer(p); + dev_ni65.priv = NULL; + unregister_netdev(&dev_ni65); +} +#endif /* MODULE */ + /* * END of ni65.c */ + diff -ur --new-file old/linux/drivers/net/ni65.h new/linux/drivers/net/ni65.h --- old/linux/drivers/net/ni65.h Mon Feb 26 10:58:15 1996 +++ new/linux/drivers/net/ni65.h Fri Nov 1 22:07:23 1996 @@ -1,6 +1,6 @@ /* am7990 (lance) definitions * - * This is a extension to the Linux operating system, and is covered by + * This is an extension to the Linux operating system, and is covered by * same Gnu Public License that covers that work. * * Michael Hipp @@ -92,23 +92,23 @@ unsigned char eaddr[6]; unsigned char filter[8]; /* bit 29-31: number of rmd's (power of 2) */ - unsigned long rrp; /* receive ring pointer (align 8) */ + u32 rrp; /* receive ring pointer (align 8) */ /* bit 29-31: number of tmd's (power of 2) */ - unsigned long trp; /* transmit ring pointer (align 8) */ + u32 trp; /* transmit ring pointer (align 8) */ }; struct rmd /* Receive Message Descriptor */ { union { - volatile unsigned long buffer; + volatile u32 buffer; struct { volatile unsigned char dummy[3]; volatile unsigned char status; } s; } u; - short blen; + volatile short blen; volatile unsigned short mlen; }; @@ -116,14 +116,14 @@ { union { - volatile unsigned long buffer; + volatile u32 buffer; struct { volatile unsigned char dummy[3]; volatile unsigned char status; } s; } u; - unsigned short blen; + volatile unsigned short blen; volatile unsigned short status2; }; diff -ur --new-file old/linux/drivers/net/ppp.c new/linux/drivers/net/ppp.c --- old/linux/drivers/net/ppp.c Sat Nov 16 00:48:04 1996 +++ new/linux/drivers/net/ppp.c Sat Nov 16 00:48:32 1996 @@ -3082,6 +3082,14 @@ len = skb->len; data = skb_data(skb); /* + * Bug trap for null data. Release the skb and bail out. + */ + if(data == NULL) { + printk("ppp_dev_xmit: data=NULL before ppp_dev_xmit_ip.\n"); + dev_kfree_skb (skb, FREE_WRITE); + return 0; + } +/* * Look at the protocol in the skb to determine the difference between * an IP frame and an IPX frame. */ diff -ur --new-file old/linux/drivers/net/sdla.c new/linux/drivers/net/sdla.c --- old/linux/drivers/net/sdla.c Sat May 18 10:15:09 1996 +++ new/linux/drivers/net/sdla.c Sun Sep 15 09:34:18 1996 @@ -5,7 +5,7 @@ * * Global definitions for the Frame relay interface. * - * Version: @(#)sdla.c 0.25 14 May 1996 + * Version: @(#)sdla.c 0.30 12 Sep 1996 * * Credits: Sangoma Technologies, for the use of 2 cards for an extended * period of time. @@ -23,7 +23,8 @@ * non DLCI devices. * 0.25 Mike McLagan Fixed problem with rejecting packets * from non DLCI devices. - * + * 0.30 Mike McLagan Fixed kernel panic when used with modified + * ifconfig * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +59,7 @@ #include -static const char* version = "SDLA driver v0.25, 14 May 1996, mike.mclagan@linux.org"; +static const char* version = "SDLA driver v0.30, 12 Sep 1996, mike.mclagan@linux.org"; static const char* devname = "sdla"; @@ -424,6 +425,7 @@ window = flp->type == SDLA_S508 ? SDLA_508_CMD_BUF : SDLA_502_CMD_BUF; cmd_buf = (struct sdla_cmd *)(dev->mem_start + (window & SDLA_ADDR_MASK)); ret = 0; + len = 0; jiffs = jiffies + HZ; /* 1 second is plenty */ save_flags(pflags); cli(); @@ -603,7 +605,7 @@ int sdla_dlci_conf(struct device *slave, struct device *master, int get) { struct frad_local *flp; - struct frad_local *dlp; + struct dlci_local *dlp; int i; short len, ret; diff -ur --new-file old/linux/drivers/net/slhc.c new/linux/drivers/net/slhc.c --- old/linux/drivers/net/slhc.c Tue Jun 25 11:03:29 1996 +++ new/linux/drivers/net/slhc.c Wed Oct 30 02:42:41 1996 @@ -246,6 +246,14 @@ struct iphdr *ip; struct tcphdr *th, *oth; + + /* + * Don't play with runt packets. + */ + + if(isizeihl*4 + th->doff*4; /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or - * some other control bit is set). + * some other control bit is set). Also uncompressible if + * its a runt. */ - if(th->syn || th->fin || th->rst || + if(hlen > isize || th->syn || th->fin || th->rst || ! (th->ack)){ /* TCP connection stuff; send as regular IP */ comp->sls_o_tcp++; diff -ur --new-file old/linux/drivers/net/tulip.c new/linux/drivers/net/tulip.c --- old/linux/drivers/net/tulip.c Wed Apr 17 08:31:21 1996 +++ new/linux/drivers/net/tulip.c Fri Nov 1 12:59:05 1996 @@ -1096,7 +1096,7 @@ /* Log any net taps. */ printk("%s: Promiscuous mode enabled.\n", dev->name); } - else if (dev->mc_count > 15 || (dev->flags&IFF_ALLMULTI)) + else if (dev->mc_count > 14 || (dev->flags&IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ tio_write(csr6 | TCMOD_ALLMCAST, CSR6); @@ -1109,7 +1109,7 @@ unsigned short *eaddrs; int i; - /* We have <= 15 addresses that we can use the wonderful + /* We have < 15 addresses that we can use the wonderful 16 address perfect filtering of the Tulip. Note that only the low shortword of setup_frame[] is valid. */ tio_write(csr6 | 0x0000, CSR6); @@ -1122,13 +1122,36 @@ } /* Fill the rest of the table with our physical address. */ eaddrs = (unsigned short *)dev->dev_addr; + /* Always accept broadcast packets */ + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; do { *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[2]; - } while (++i < 16); + } while (++i < 15); /* Now add this frame to the Tx list. */ + { + unsigned long flags; + unsigned int entry; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + tp->dirty_tx++; + restore_flags(flags); + + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + tp->tx_ring[entry].length = 192 | + (entry == TX_RING_SIZE-1 ? 0x0a000000 : 0x08000000); + tp->tx_ring[entry].buffer1 = virt_to_bus((char *)tp->setup_frame); + tp->tx_ring[entry].buffer2 = 0; + tp->tx_ring[entry].status = TRING_OWN; + /* Trigger an immediate transmit demand. */ + tio_write(TPOLL_TRIGGER, CSR1); + } } } diff -ur --new-file old/linux/drivers/pci/pci.c new/linux/drivers/pci/pci.c --- old/linux/drivers/pci/pci.c Sat Aug 17 20:03:34 1996 +++ new/linux/drivers/pci/pci.c Wed Nov 6 11:12:35 1996 @@ -69,6 +69,8 @@ DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), DEVICE( DEC, DEC_FDDI, "DEFPA"), DEVICE( DEC, DEC_TULIP_PLUS, "DC21041"), + DEVICE( DEC, DEC_21052_AB, "DC21052-AB"), + DEVICE( DEC, DEC_21152_AA, "DC21152-AA"), DEVICE( CIRRUS, CIRRUS_5430, "GD 5430"), DEVICE( CIRRUS, CIRRUS_5434_4, "GD 5434"), DEVICE( CIRRUS, CIRRUS_5434_8, "GD 5434"), @@ -230,12 +232,15 @@ DEVICE( INTEL, INTEL_82371_1, "82371 Triton PIIX"), DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), - DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX"), - DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX"), + DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"), + DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"), + DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"), + DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"), DEVICE( INTEL, INTEL_P6, "Orion P6"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), DEVICE( ADAPTEC, ADAPTEC_7860, "AIC-7860"), + DEVICE( ADAPTEC, ADAPTEC_7861, "AIC-7861"), DEVICE( ADAPTEC, ADAPTEC_7870, "AIC-7870"), DEVICE( ADAPTEC, ADAPTEC_7871, "AIC-7871"), DEVICE( ADAPTEC, ADAPTEC_7872, "AIC-7872"), @@ -433,6 +438,7 @@ case PCI_CLASS_SERIAL_FIREWIRE: return "FireWire (IEEE 1394)"; case PCI_CLASS_SERIAL_ACCESS: return "ACCESS Bus"; case PCI_CLASS_SERIAL_SSA: return "SSA"; + case PCI_CLASS_SERIAL_USB: return "USB Controller"; case PCI_CLASS_SERIAL_FIBER: return "Fiber Channel"; default: return "Unknown class"; diff -ur --new-file old/linux/drivers/sbus/char/sunkbd.c new/linux/drivers/sbus/char/sunkbd.c --- old/linux/drivers/sbus/char/sunkbd.c Sat May 4 18:39:23 1996 +++ new/linux/drivers/sbus/char/sunkbd.c Thu Nov 7 10:25:21 1996 @@ -735,8 +735,9 @@ #define A_CFLEX '^' #define A_TILDE '~' #define A_DIAER '"' -static unsigned char ret_diacr[] = - {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER }; +#define A_CEDIL ',' +static unsigned char ret_diacr[NR_DEAD] = + {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL }; /* If a dead key pressed twice, output a character corresponding to it, */ /* otherwise just remember the dead key. */ diff -ur --new-file old/linux/drivers/scsi/AM53C974.c new/linux/drivers/scsi/AM53C974.c --- old/linux/drivers/scsi/AM53C974.c Fri Apr 12 08:49:40 1996 +++ new/linux/drivers/scsi/AM53C974.c Wed Oct 30 03:03:42 1996 @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -2241,3 +2243,10 @@ cmd->scsi_done(cmd); return SCSI_ABORT_SUCCESS; } + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = AM53C974; + +#include "scsi_module.c" +#endif diff -ur --new-file old/linux/drivers/scsi/BusLogic.c new/linux/drivers/scsi/BusLogic.c --- old/linux/drivers/scsi/BusLogic.c Wed Jul 17 20:22:13 1996 +++ new/linux/drivers/scsi/BusLogic.c Mon Oct 28 12:39:30 1996 @@ -25,7 +25,7 @@ #define BusLogic_DriverVersion "2.0.6" -#define BusLogic_DriverDate "17 July 1996" +#define BusLogic_DriverDate "19 October 1996" #include diff -ur --new-file old/linux/drivers/scsi/BusLogic.h new/linux/drivers/scsi/BusLogic.h --- old/linux/drivers/scsi/BusLogic.h Wed Jul 17 20:22:13 1996 +++ new/linux/drivers/scsi/BusLogic.h Mon Oct 28 12:39:30 1996 @@ -1066,11 +1066,10 @@ static inline void BusLogic_Delay(int Seconds) { - unsigned long TimeoutJiffies = jiffies + Seconds * HZ; unsigned long ProcessorFlags; save_flags(ProcessorFlags); sti(); - while (jiffies < TimeoutJiffies) barrier(); + while (--Seconds >= 0) udelay(1000000); restore_flags(ProcessorFlags); } diff -ur --new-file old/linux/drivers/scsi/ChangeLog.ncr53c8xx new/linux/drivers/scsi/ChangeLog.ncr53c8xx --- old/linux/drivers/scsi/ChangeLog.ncr53c8xx Tue Jul 23 09:26:41 1996 +++ new/linux/drivers/scsi/ChangeLog.ncr53c8xx Sun Sep 1 08:15:32 1996 @@ -1,3 +1,10 @@ +Fri Aug 30 10:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - Version 1.12c + Incorporate the changes of FreeBSD/ncr.c revision 1.76. + The changes add support for the 53c860 and 53c875, + but without taking advantage of the new features. + Those chips are used exactly as the old 53c810. + Sun Jul 21 00:00 1996 Gerard Roudier (groudier@club-internet.fr) * ncr53c8xx.c, README.ncr53c8xx Add the ncr53c8xx_select_queue_depths() function. diff -ur --new-file old/linux/drivers/scsi/Config.in new/linux/drivers/scsi/Config.in --- old/linux/drivers/scsi/Config.in Sun Jul 7 10:18:51 1996 +++ new/linux/drivers/scsi/Config.in Wed Oct 30 02:44:13 1996 @@ -21,12 +21,12 @@ dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI -bool 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 +dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI -dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI dep_tristate 'EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI +dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then diff -ur --new-file old/linux/drivers/scsi/Makefile new/linux/drivers/scsi/Makefile --- old/linux/drivers/scsi/Makefile Sat Jul 6 11:06:37 1996 +++ new/linux/drivers/scsi/Makefile Wed Oct 30 02:42:41 1996 @@ -179,6 +179,10 @@ ifeq ($(CONFIG_SCSI_AM53C974),y) L_OBJS += AM53C974.o +else + ifeq ($(CONFIG_SCSI_AM53C974),m) + M_OBJS += AM53C974.o + endif endif ifeq ($(CONFIG_SCSI_BUSLOGIC),y) diff -ur --new-file old/linux/drivers/scsi/NCR5380.c new/linux/drivers/scsi/NCR5380.c --- old/linux/drivers/scsi/NCR5380.c Mon Apr 29 16:14:19 1996 +++ new/linux/drivers/scsi/NCR5380.c Tue Oct 29 15:21:48 1996 @@ -734,7 +734,7 @@ */ static void NCR5380_print_status (struct Scsi_Host *instance) { - char pr_bfr[256]; + static char pr_bfr[512]; char *start; int len; @@ -767,7 +767,7 @@ */ #undef SPRINTF -#define SPRINTF(args...) do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0) +#define SPRINTF(args...) do { if(pos < buffer + length-80) pos += sprintf(pos, ## args); } while(0) static char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length); static diff -ur --new-file old/linux/drivers/scsi/README.ncr53c8xx new/linux/drivers/scsi/README.ncr53c8xx --- old/linux/drivers/scsi/README.ncr53c8xx Thu Jul 11 07:19:59 1996 +++ new/linux/drivers/scsi/README.ncr53c8xx Fri Sep 20 16:00:34 1996 @@ -10,7 +10,7 @@ 1. Introduction 2. Supported chips and SCSI features 3. Summary of other supported features -4. Memory mapped IO versus normal IO +4. Memory mapped I/O versus normal I/O 5. Tagged command queueing 6. Parity checking 7. Profiling information @@ -39,7 +39,7 @@ maintained by: Gerard Roudier -The original driver has been written for 386bsd and FreeBSD by +The original driver has been written for 386bsd and FreeBSD by: Wolfgang Stanglmeier Stefan Esser @@ -50,14 +50,14 @@ http://www.symbios.com This short documentation only describes the features of the NCR53C8XX driver, -configuration parameters and control commands available through the proc scsi +configuration parameters and control commands available through the proc SCSI file system read / write operations. -This driver has been tested ok with linux/i386 and is currently untested +This driver has been tested OK with linux/i386 and is currently untested under linux/Alpha. If you intend to use this driver under linux/Alpha, just try it first with read-only or mounted read-only devices. -I am not a native speaker of English and there is probably lots of +I am not a native speaker of English and there are probably lots of mistakes in this README file. Any help will be welcome. @@ -68,14 +68,14 @@ Synchronous negotiation Disconnection Tagged command queuing - Scsi parity checking + SCSI parity checking Master parity checking "Wide negotiation" is supported for chips that allow it. The following table shows some characteristics of NCR 8xx family chips: On board Supported by Tested with -Chip SDMS BIOS Wide Ultra Scsi the driver the driver +Chip SDMS BIOS Wide Ultra SCSI the driver the driver ---- --------- ---- ---------- ------------ ----------- 810 N N N Y Y 810A N N N Y Y @@ -84,73 +84,73 @@ 825A Y Y N Y Not yet 875 Y Y Y(1) Y Not yet -(1) Ultra scsi extensions will be supported in a future release of the +(1) Ultra SCSI extensions will be supported in a future release of the driver. 3. Summary of other supported features. Module: allow to load the driver - Memory mapped IO: increase performances - Profiling information: read operations from the proc scsi file system - Control commands: write operations to the proc scsi file system + Memory mapped I/O: increases performance + Profiling information: read operations from the proc SCSI file system + Control commands: write operations to the proc SCSI file system Debugging information: written to syslog (expert only) Scatter / gather Shared interrupt -4. Memory mapped IO versus normal IO +4. Memory mapped I/O versus normal I/O -Memory mapped IO have less latency than normal IO. -Since linux-1.3.x, memory mapped IO is used rather than normal IO. -Memory mapped IO seems to works fine on most hardware configuration, but some -bad designed motherboards may break this feature. - -During initialisation phase, the driver first tries to use memory mapped io. -If nothing seems wrong, it will use memory mapped io. -If a flaw is detected, it will use normal io. +Memory mapped I/O has less latency than normal I/O. +Since linux-1.3.x, memory mapped I/O is used rather than normal I/O. +Memory mapped I/O seems to work fine on most hardware configurations, but some +poorly designed motherboards may break this feature. + +During the initialization phase, the driver first tries to use memory mapped +I/O. If nothing seems wrong, it will use memory mapped I/O. +If a flaw is detected, it will use normal I/O. -However, it's possible that memory mapped io does not work properly and the +However, it's possible that memory mapped I/O does not work properly and the driver has not detected the problem. -The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED allow to force the -driver to use normal io in all cases. +The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the +driver to use normal I/O in all cases. 5. Tagged command queueing -Some scsi devices donnot support properly tagged command queuing. -A safe configuration can be to not enable tagged command queuing support at +Some SCSI devices do not properly support tagged command queuing. +A safe configuration is to not enable tagged command queuing support at boot-up, and to enable support of it with the control command "settags" described further in this text. -Once you are sure that all your devices support properly tagged command queuing, +Once you are sure that all your devices properly support tagged command queuing, you can enable it by default with the CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE configuration option. 6. Parity checking -The driver supports scsi parity checking and PCI bus master parity checking. +The driver supports SCSI parity checking and PCI bus master parity checking. These features must be enabled in order to ensure safe data transfers. However, some flawed devices or mother boards will have problems with parity. You can disable parity by choosing first "CONFIG_EXPERIMENTAL". Then, "make config" will allow to set the following configuration options: - CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK (disable scsi parity checking) + CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK (disable SCSI parity checking) CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK (disable master parity checking) 7. Profiling information -Profiling information are available through the proc scsi file system. +Profiling information is available through the proc SCSI file system. The device associated with a host has the following pathname: /proc/scsi/ncr53c8xx/N (N=0,1,2 ....) -Generally, only 1 board is used on hardware configuration, and the device is: +Generally, only 1 board is used on hardware configuration, and that device is: /proc/scsi/ncr53c8xx/0 -However, if the driver has been made as module, the number of the host is +However, if the driver has been made as module, the number of the hosts is incremented each time the driver is loaded. In order to display profiling information, just enter: @@ -175,8 +175,8 @@ ms_post = 1320 ------------------------------------------------------- -General information is easy to understand. The device id and the -revision id identify the scsi chip as follows: +General information is easy to understand. The device ID and the +revision ID identify the SCSI chip as follows: Chip Device id Revision Id ---- --------- ----------- @@ -188,11 +188,11 @@ 825A 0x3 >= 0x10 875 0xf -The profiling information is updated upon completion of scsi commands. -The data structure is allocated and zeroed when the host adapter is +The profiling information is updated upon completion of SCSI commands. +A data structure is allocated and zeroed when the host adapter is attached. So, if the driver is a module, the profile counters are cleared each time the driver is loaded. -The "clearprof" command allow to clear these counters at any time. +The "clearprof" command allows you to clear these counters at any time. The following counters are available: ("num" prefix means "number of", "ms" means milli-seconds) @@ -206,8 +206,8 @@ Example above: 671 MB transferred num_disc - Number of scsi disconnections - Example above: 25763 scsi disconnections + Number of SCSI disconnections + Example above: 25763 SCSI disconnections num_break number of script interruptions (phase mismatch) @@ -222,7 +222,7 @@ Example above: 18038 interruptions "on the fly" ms_setup - Elapsed time for scsi commands setups + Elapsed time for SCSI commands setups Example above: 4.94 seconds ms_data @@ -230,12 +230,12 @@ Example above: 369.94 seconds spent for data transfer ms_disc - Elapsed time for scsi disconnections + Elapsed time for SCSI disconnections Example above: 183.09 seconds spent disconnected ms_post Elapsed time for command post processing - (time from scsi status get to command completion call) + (time from SCSI status get to command completion call) Example above: 1.32 seconds spent for post processing Due to the 1/100 second tick of the system clock, "ms_post" time may be @@ -243,13 +243,13 @@ In the example above, we got 18038 interrupts "on the fly" and only 1673 script breaks probably due to disconnections inside a segment of the scatter list. -It is an excellent result due to the fact that the driver tries to use small +This is an excellent result due to the fact that the driver tries to use small data segments (512) for the scatter list. The CPU load of this rescatter process -is acceptable. Unlike other scsi processors, NCR53C8XX controllers do not need -large data chunks in order to get better performances, and it seems that it -is the opposite. -The scatter/gather algorithm of the middle scsi driver is not optimal for -NCR scsi processors and should be tunable according to host type. +is acceptable. Unlike other SCSI processors, NCR53C8XX controllers do not need +large data chunks in order to get better performance, and it seems that it +is just the opposite. +The scatter/gather algorithm of the middle SCSI driver is not optimal for +NCR SCSI processors and should be tunable according to host type. You can tune the "wished" segment size for the scatterlist by changing the following "define" in the file ncr53c8xx.h. @@ -261,7 +261,7 @@ 8. Control commands Control commands can be sent to the driver with write operations to the -proc scsi file system. The generic command syntax is the following: +proc SCSI file system. The generic command syntax is the following: echo " " >/proc/scsi/ncr53c8xx/0 (assumes controller number is 0) @@ -311,14 +311,14 @@ setdebug Available debug flags: - alloc: print infos about memory allocations (ccb, lcb) - queue: print infos about insertions into the command start queue + alloc: print info about memory allocations (ccb, lcb) + queue: print info about insertions into the command start queue result: print sense data on CHECK CONDITION status - scatter: print infos about the scatter process - scripts: print infos about the script binding process + scatter: print info about the scatter process + scripts: print info about the script binding process tiny: print minimal debugging information - timing: print timing information of the ncr chip. - nego: print information about scsi negotiations + timing: print timing information of the NCR chip + nego: print information about SCSI negotiations phase: print information on script interruptions @@ -327,38 +327,38 @@ clearprof The profile counters are automatically cleared when the amount of data - transfered reach 1000 GB in order to avoid overflow. - The "clearprof" command allow to clear these counters at any time. + transfered reaches 1000 GB in order to avoid overflow. + The "clearprof" command allows you to clear these counters at any time. 9. Configuration parameters If the firmware of all your devices is perfect enough, all the features supported by the driver can be enabled at start-up. -However, if only one has a flaw for some scsi feature, you can disable the +However, if only one has a flaw for some SCSI feature, you can disable the support by the driver of this feature at linux start-up and enable this feature after boot-up only for devices that support it safely. CONFIG_SCSI_NCR53C8XX_IOMAPPED (default answer: n) - Answer "y" if you suspect your mother board to not allow memory mapped IO. - May slow down a little performances. + Answer "y" if you suspect your mother board to not allow memory mapped I/O. + May slow down performance a little. CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE (default answer: n) - Answer "y" if you are sure that all your scsi devices that are able to + Answer "y" if you are sure that all your SCSI devices that are able to accept tagged commands will proceed safely. CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS (default answer: n) - This option allow to force asynchronous transfer mode for all scsi devices. + This option forces asynchronous transfer mode for all SCSI devices. CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO (default answer: n) - Force synchronous negotiation for all scsi-2 devices. - Some scsi-2 devices do not report this feature in byte 7 of inquiry - response and however support it properly (TAMARACK scanners for example). + Force synchronous negotiation for all SCSI-2 devices. + Some SCSI-2 devices do not report this feature in byte 7 of inquiry + response but do support it properly (TAMARACK scanners for example). CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT (default and only reasonnable answer: n) - If you suspect a device of yours to not support properly disconnections, - you can answer "y". Then, all scsi devices will never disconnect the bus - even while long scsi operations. + If you suspect a device of yours does not properly support disconnections, + you can answer "y". Then, all SCSI devices will never disconnect the bus + even while performing long SCSI operations. 10. Some constants and flags of the ncr53c8xx.h header files @@ -368,17 +368,17 @@ Do that only if you know what you are doing. SCSI_NCR_IOMAPPED (default: not defined) - If defined, normal IO is forced. + If defined, normal I/O is forced. SCSI_NCR_SHARE_IRQ (default: defined) - If defined, request shared irq. + If defined, request shared IRQ. SCSI_NCR_MAX_TAGS (default: 4) Maximum number of simultaneous tagged commands to a device. Can be changed by "settags " SCSI_NCR_ALWAYS_SIMPLE_TAG (default: defined) - Use SIMPLE TAG for read and write commands + Use SIMPLE TAG for read and write commands. Can be changed by "setorder " SCSI_NCR_TAGGED_QUEUE_DISABLED (default: defined) @@ -389,38 +389,38 @@ If defined, targets are not allowed to disconnect. SCSI_NCR_FORCE_SYNC_NEGO (default: not defined) - If defined, synchronous negotiation is tried for all scsi-2 devices. + If defined, synchronous negotiation is tried for all SCSI-2 devices. Can be changed by "setsync " SCSI_NCR_DISABLE_MPARITY_CHECK (default: not defined) If defined, master parity checking is disabled. SCSI_NCR_DISABLE_PARITY_CHECK (default: not defined) - If defined, scsi parity checking is disabled. + If defined, SCSI parity checking is disabled. SCSI_NCR_PROFILE (default: defined) - If defined, profile information are gathered + If defined, profiling information is gathered. SCSI_NCR_MAX_SCATTER (default: 128) Scatter list size of the driver ccb. SCSI_NCR_SEGMENT_SIZE (default: 512) - If defined, the driver try to use scatter segments of this size. + If defined, the driver will try to use scatter segments of this size. If not defined, the Linux scatter list is used as is. SCSI_NCR_MAX_TARGET (default: 16) - Max number of target per host. + Max number of targets per host. SCSI_NCR_MAX_HOST (default: 2) Max number of host controllers. SCSI_NCR_SETTLE_TIME (default: 2) - Number of seconds the driver wait after reset. + Number of seconds the driver will wait after reset. SCSI_NCR_TIMEOUT_ALERT (default: 3) If a pending command will time out after this amount of seconds, an ordered tag is used for the next command. - Avoid timeouts for unordered tagged commands. + Avoids timeouts for unordered tagged commands. SCSI_NCR_CAN_QUEUE (default: 7*SCSI_NCR_MAX_TAGS) Max number of commands that can be queued to a host. @@ -432,7 +432,7 @@ Max size of the Linux scatter/gather list. SCSI_NCR_MAX_LUN (default: 8) - Max number of luns per target + Max number of LUNs per target. 11. Provided files @@ -474,9 +474,9 @@ This install script only supports linux-1.2.13 and linux-1.3.45 to 1.3.100. -This procedure just move the standard driver files to SAVE_53 sub-directory +This procedure just moves the standard driver files to SAVE_53 sub-directory of linux/drivers/scsi, copies the drivers file to that directory and patches -the scsi Makefile. +the SCSI Makefile. The standard driver can be restored with Uninstall.ncr53c8xx If your linux directory is at the standard location @@ -497,7 +497,7 @@ Since release 1.3.90, additionnal configuation parameters have been added for the standard NCR driver. Just reply Y or N as you want to these questions; - The NCR53C8XX driver ignore those parameters. + The NCR53C8XX driver ignores those parameters. If you prefer the standard NCR driver of Linux: just enter: @@ -509,9 +509,9 @@ 13. Installation procedure for Linux version 2 -This procedure add the driver to the kernel tree. +This procedure adds the driver to the kernel tree. Using "make config" you can choose between the standard driver and the BSD one. -It is possible to configure the both drivers as module and to switch from one +It is possible to configure both drivers as modules and to switch from one to the other at run time. Take care to unload the current driver module before loading the other one. @@ -532,9 +532,9 @@ 14. Control commands under linux-1.2.13 -Profile data and control commands using the proc scsi file system are not +Profiling data and control commands using the proc SCSI file system are not available for linux-1.2.13. -The only control command available is "scsitag" which allows to enable +The only control command available is "scsitag" which allows you to enable tagged command queuing support after linux boot-up. Tagged command queueing is disabled by default at system startup. @@ -549,19 +549,19 @@ 15.1 Tagged commands with Iomega Jaz device -I never tried such devices, however it has been reported to me the following: +I have not tried this device, however it has been reported to me the following: This device is capable of Tagged command queuing. However while spinning up, -it rejects Tagged commands. This behaviour is conforms to 6.8.2 of scsi-2 +it rejects Tagged commands. This behaviour is conforms to 6.8.2 of SCSI-2 specifications. The current behaviour of the driver in that situation is not satisfying. So do not enable Tagged command queuing for devices that are able to spin down. -The other problems that may appear are timeouts. The only way to avoid timeouts +The other problem that may appear is timeouts. The only way to avoid timeouts seems to edit linux/drivers/scsi/sd.c and to increase the current timeout values. 15.2 Tagged command queuing cannot be disabled at run time -Once Tagged command queuing has been enabled, the driver will not allow to +Once Tagged command queuing has been enabled, the driver will not allow you to disable this feature ("settags 0" is not supported). This problem is due to some limitations of the code added to the Linux version of the driver. diff -ur --new-file old/linux/drivers/scsi/advansys.c new/linux/drivers/scsi/advansys.c --- old/linux/drivers/scsi/advansys.c Wed Aug 14 08:59:04 1996 +++ new/linux/drivers/scsi/advansys.c Fri Sep 27 06:52:29 1996 @@ -1,4 +1,4 @@ -/* $Id: advansys.c,v 1.15 1996/08/12 17:20:23 bobf Exp bobf $ */ +/* $Id: advansys.c,v 1.20 1996/09/26 00:47:54 bobf Exp bobf $ */ /* * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters * @@ -18,9 +18,9 @@ */ /* - * The driver has been run with the v1.2.13, v1.3.57, and v2.0.11 kernels. + * The driver has been run with the v1.2.13, v1.3.57, and v2.0.21 kernels. */ -#define ASC_VERSION "1.5" /* AdvanSys Driver Version */ +#define ASC_VERSION "1.7" /* AdvanSys Driver Version */ /* @@ -28,14 +28,15 @@ A. Adapters Supported by this Driver B. Linux v1.2.X - Directions for Adding the AdvanSys Driver - C. Linux v1.3.X, v2.X.X - Directions for Adding the AdvanSys Driver - D. Source Comments - E. Driver Compile Time Options and Debugging - F. Driver LILO Option - G. Release History - H. Known Problems or Issues - I. Credits - J. AdvanSys Contact Information + C. Linux v1.3.1 - v1.3.57 - Directions for Adding the AdvanSys Driver + D. Linux v1.3.58 and Newer - Upgrading the AdvanSys Driver + E. Source Comments + F. Driver Compile Time Options and Debugging + G. Driver LILO Option + H. Release History + I. Known Problems or Issues + J. Credits + K. AdvanSys Contact Information A. Adapters Supported by this Driver @@ -47,8 +48,8 @@ Descriptor Block) requests that can be stored in the RISC chip cache and board LRAM. A CDB is a single SCSI command. The driver detect routine will display the number of CDBs available for each - adapter detected. This value can be lowered in the BIOS by changing - the 'Host Queue Size' adapter setting. + adapter detected. The number of CDBs used by the driver can be + lowered in the BIOS by changing the 'Host Queue Size' adapter setting. Connectivity Products: ABP510/5150 - Bus-Master ISA (240 CDB) (Footnote 1) @@ -56,8 +57,10 @@ ABP5142 - Bus-Master ISA PnP with floppy (16 CDB) ABP920 - Bus-Master PCI (16 CDB) ABP930 - Bus-Master PCI (16 CDB) + ABP930U - Bus-Master PCI Ultra (16 CDB) ABP960 - Bus-Master PCI MAC/PC (16 CDB) (Footnote 2) - + ABP960U - Bus-Master PCI MAC/PC Ultra (16 CDB) + Single Channel Products: ABP542 - Bus-Master ISA with floppy (240 CDB) ABP742 - Bus-Master EISA (240 CDB) @@ -65,6 +68,7 @@ ABP940 - Bus-Master PCI (240 CDB) ABP940U - Bus-Master PCI Ultra (240 CDB) ABP970 - Bus-Master PCI MAC/PC (240 CDB) + ABP970U - Bus-Master PCI MAC/PC Ultra (240 CDB) Dual Channel Products: ABP752 - Dual Channel Bus-Master EISA (240 CDB Per Channel) @@ -72,17 +76,18 @@ ABP950 - Dual Channel Bus-Master PCI (240 CDB Per Channel) Footnotes: - 1. These boards have been shipped by HP with the 4020i CD-R drive. - They have no BIOS so they cannot control a boot device, but they - can control secondary devices. + 1. This board has been shipped by HP with the 4020i CD-R drive. + The board has no BIOS so it cannot control a boot device, but + it can control any secondary SCSI device. - 2. This board has been shipped by Iomega with the Jaz Jet drive. + 2. This board has been sold by Iomega as a Jaz Jet PCI adapter. B. Linux v1.2.X - Directions for Adding the AdvanSys Driver These directions apply to v1.2.13. For versions that follow v1.2.13. but precede v1.3.57 some of the changes for Linux v1.3.X listed - below may need to be modified or included. + below may need to be modified or included. A patch is available + for v1.2.13 from the AdvanSys WWW and FTP sites. There are two source files: advansys.h and advansys.c. Copy both of these files to the directory /usr/src/linux/drivers/scsi. @@ -148,11 +153,13 @@ 'make modules_install'. Use 'insmod' and 'rmmod' to install and remove advansys.o. - C. Linux v1.3.X, v2.X.X - Directions for Adding the AdvanSys Driver + C. Linux v1.3.1 - v1.3.57 - Directions for Adding the AdvanSys Driver These directions apply to v1.3.57. For versions that precede v1.3.57 - some of these changes may need to be modified or eliminated. Beginning - with v1.3.58 this driver is included with the Linux distribution. + some of these changes may need to be modified or eliminated. A patch + is available for v1.3.57 from the AdvanSys WWW and FTP sites. + Beginning with v1.3.58 this driver is included with the Linux + distribution eliminating the need for making any changes. There are two source files: advansys.h and advansys.c. Copy both of these files to the directory /usr/src/linux/drivers/scsi. @@ -212,7 +219,21 @@ 'make modules_install'. Use 'insmod' and 'rmmod' to install and remove advansys.o. - D. Source Comments + D. Linux v1.3.58 and Newer - Upgrading the AdvanSys Driver + + To upgrade the AdvanSys driver in a Linux v1.3.58 and newer + kernel, first check the version of the current driver. The + version is defined by the manifest constant ASC_VERSION at + the beginning of advansys.c. The new driver should have a + ASC_VERSION value greater than the current version. To install + the new driver rename advansys.c and advansys.h in the Linux + kernel source tree drivers/scsi directory to different names + or save them to a different directory in case you want to revert + to the old version of the driver. After the old driver is saved + copy the new advansys.c and advansys.h to drivers/scsi, rebuild + the kernel, and install the new kernel. No other changes are needed. + + E. Source Comments 1. Use tab stops set to 4 for the source files. For vi use 'se tabstops=4'. @@ -251,7 +272,7 @@ --- Asc Library Constants and Macros --- Asc Library Functions - E. Driver Compile Time Options and Debugging + F. Driver Compile Time Options and Debugging In this source file the following constants can be defined. They are defined in the source below. Both of these options are enabled by @@ -299,7 +320,7 @@ 2. ADVANSYS_STATS - enable statistics Statistics are maintained on a per adapter basis. Driver entry - point call counts and tranfer size counts are maintained. + point call counts and transfer size counts are maintained. Statistics are only available for kernels greater than or equal to v1.3.0 with the CONFIG_PROC_FS (/proc) file system configured. @@ -315,7 +336,7 @@ contain adapter and device configuration information. - F. Driver LILO Option + G. Driver LILO Option If init/main.c is modified as described in the 'Directions for Adding the AdvanSys Driver to Linux' section (B.4.) above, the driver will @@ -346,7 +367,7 @@ the 'Driver Compile Time Options and Debugging' section above for more information. - G. Release History + H. Release History BETA-1.0 (12/23/95): First Release @@ -389,7 +410,7 @@ request_irq and supplying a dev_id pointer to both request_irq() and free_irq(). 3. In AscSearchIOPortAddr11() restore a call to check_region() which - should be used before any I/O port probing. + should be used before I/O port probing. 4. Fix bug in asc_prt_hex() which resulted in the displaying the wrong data. 5. Incorporate miscellaneous Asc Library bug fixes and new microcode. @@ -403,15 +424,28 @@ made in v1.3.89. The advansys_select_queue_depths() function was added for the v1.3.89 changes. - H. Known Problems or Issues + 1.6 (9/10/96): + 1. Incorporate miscellaneous Asc Library bug fixes and new microcode. + + 1.7 (9/25/96): + 1. Enable clustering and optimize the setting of the maximum number + of scatter gather elements for any particular board. Clustering + increases CPU utilization, but results in a relatively larger + increase in I/O throughput. + 2. Improve the performance of the request queuing functions by + adding a last pointer to the queue structure. + 3. Correct problems with reset and abort request handling that + could have hung or crashed Linux. + 4. Add more information to the adapter /proc file: + /proc/scsi/advansys[0...]. + 5. Remove the request timeout issue form the driver issues list. + 6. Miscellaneous documentation additions and changes. + + I. Known Problems or Issues - 1. For the first scsi command sent to a device the driver increases - the timeout value. This gives the driver more time to perform - its own initialization for the board and each device. The timeout - value is only changed on the first scsi command for each device - and never thereafter. The same change is made for reset commands. + None - I. Credits + J. Credits Nathan Hartwell provided the directions and basis for the Linux v1.3.X changes which were included in the @@ -420,7 +454,7 @@ Thomas E Zerucha pointed out a bug in advansys_biosparam() which was fixed in the 1.3 release. - J. AdvanSys Contact Information + K. AdvanSys Contact Information Mail: Advanced System Products, Inc. 1150 Ringwood Court @@ -505,8 +539,8 @@ */ #define ASC_LIB_VERSION_MAJOR 1 -#define ASC_LIB_VERSION_MINOR 21 -#define ASC_LIB_SERIAL_NUMBER 88 +#define ASC_LIB_VERSION_MINOR 22 +#define ASC_LIB_SERIAL_NUMBER 89 typedef unsigned char uchar; @@ -581,6 +615,11 @@ #define ASC_PCI_ID2FUNC( id ) (((id) >> 8) & 0x7) #define ASC_PCI_MKID( bus, dev, func ) ((((dev) & 0x1F) << 11) | (((func) & 0x7) << 8) | ((bus) & 0xFF)) +#define Asc_DvcLib_Status int +#define ASC_DVCLIB_CALL_DONE (1) +#define ASC_DVCLIB_CALL_FAILED (0) +#define ASC_DVCLIB_CALL_ERROR (-1) + #define Lptr #define dosfar #define far @@ -592,8 +631,8 @@ #define outp(port, byte) outb((byte), (port)) #define outpw(port, word) outw((word), (port)) #define outpl(port, long) outl((long), (port)) -#define ASC_MAX_SG_QUEUE 5 -#define ASC_MAX_SG_LIST (1 + ((ASC_SG_LIST_PER_Q) * (ASC_MAX_SG_QUEUE))) +#define ASC_MAX_SG_QUEUE 7 +#define ASC_MAX_SG_LIST SG_ALL #define CC_INIT_INQ_DISPLAY FALSE #define CC_CLEAR_LRAM_SRB_PTR FALSE @@ -609,8 +648,6 @@ #define CC_MEMORY_MAPPED_IO FALSE #define CC_INCLUDE_EEP_CONFIG TRUE #define CC_PCI_ULTRA TRUE -#define CC_INIT_TARGET_READ_CAPACITY TRUE -#define CC_INIT_TARGET_TEST_UNIT_READY TRUE #define CC_ASC_SCSI_Q_USRDEF FALSE #define CC_ASC_SCSI_REQ_Q_USRDEF FALSE #define CC_ASCISR_CHECK_INT_PENDING TRUE @@ -619,13 +656,10 @@ #define CC_DISABLE_PCI_PARITY_INT TRUE #define CC_INCLUDE_EEP_CONFIG TRUE #define CC_INIT_INQ_DISPLAY FALSE -#define CC_INIT_TARGET_TEST_UNIT_READY TRUE -#define CC_INIT_TARGET_START_UNIT TRUE #define CC_PLEXTOR_VL FALSE #define CC_TMP_USE_EEP_SDTR FALSE #define CC_CHK_COND_REDO_SDTR TRUE #define CC_SET_PCI_LATENCY_TIMER_ZERO TRUE -#define CC_FIX_QUANTUM_XP34301_1071 FALSE #define CC_DISABLE_ASYN_FIX_WANGTEK_TAPE TRUE #define ASC_CS_TYPE unsigned short @@ -666,6 +700,8 @@ #define ASC_CHIP_MAX_VER_EISA (0x47) #define ASC_CHIP_VER_EISA_BIT (0x40) #define ASC_CHIP_LATEST_VER_EISA ( ( ASC_CHIP_MIN_VER_EISA - 1 ) + 3 ) +#define ASC_MAX_LIB_SUPPORTED_ISA_CHIP_VER 0x21 +#define ASC_MAX_LIB_SUPPORTED_PCI_CHIP_VER 0x0A #define ASC_MAX_VL_DMA_ADDR (0x07FFFFFFL) #define ASC_MAX_VL_DMA_COUNT (0x07FFFFFFL) #define ASC_MAX_PCI_DMA_ADDR (0xFFFFFFFFL) @@ -1313,6 +1349,7 @@ #define ASC_IERR_SCAM 0x0800 #define ASC_IERR_SET_SDTR 0x1000 #define ASC_IERR_RW_LRAM 0x8000 +#define ASC_DVCLIB_STATUS 0x00 #define ASC_DEF_IRQ_NO 10 #define ASC_MAX_IRQ_NO 15 #define ASC_MIN_IRQ_NO 10 @@ -1343,10 +1380,10 @@ #define ASC_IOADR_DEF ASC_IOADR_8 #define ASC_LIB_SCSIQ_WK_SP 256 #define ASC_MAX_SYN_XFER_NO 16 -#define ASC_SYN_XFER_NO 8 #define ASC_SYN_MAX_OFFSET 0x0F #define ASC_DEF_SDTR_OFFSET 0x0F #define ASC_DEF_SDTR_INDEX 0x00 +#define ASC_SDTR_ULTRA_PCI_10MB_INDEX 0x02 #define SYN_XFER_NS_0 25 #define SYN_XFER_NS_1 30 #define SYN_XFER_NS_2 35 @@ -1462,9 +1499,9 @@ ASC_SCSI_BIT_ID_TYPE no_scam; ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer; uchar max_sdtr_index; - uchar res4; + uchar host_init_sdtr_index; ulong drv_ptr; - ulong res6; + ulong uc_break; ulong res7; ulong res8; } ASC_DVC_VAR; @@ -1500,7 +1537,7 @@ #define ASC_CNTL_INIT_VERBOSE ( ushort )0x0800 #define ASC_CNTL_SCSI_PARITY ( ushort )0x1000 #define ASC_CNTL_BURST_MODE ( ushort )0x2000 -#define ASC_CNTL_USE_8_IOP_BASE ( ushort )0x4000 +#define ASC_CNTL_SDTR_ENABLE_ULTRA ( ushort )0x4000 #define ASC_EEP_DVC_CFG_BEG_VL 2 #define ASC_EEP_MAX_DVC_ADDR_VL 15 #define ASC_EEP_DVC_CFG_BEG 32 @@ -1537,15 +1574,23 @@ #define ASC_EEP_CMD_WRITE_ABLE 0x30 #define ASC_EEP_CMD_WRITE_DISABLE 0x00 #define ASC_OVERRUN_BSIZE 0x00000048UL +#define ASC_CTRL_BREAK_ONCE 0x0001 +#define ASC_CTRL_BREAK_STAY_IDLE 0x0002 #define ASCV_MSGOUT_BEG 0x0000 #define ASCV_MSGOUT_SDTR_PERIOD (ASCV_MSGOUT_BEG+3) #define ASCV_MSGOUT_SDTR_OFFSET (ASCV_MSGOUT_BEG+4) +#define ASCV_BREAK_SAVED_CODE ( ushort )0x0006 #define ASCV_MSGIN_BEG (ASCV_MSGOUT_BEG+8) #define ASCV_MSGIN_SDTR_PERIOD (ASCV_MSGIN_BEG+3) #define ASCV_MSGIN_SDTR_OFFSET (ASCV_MSGIN_BEG+4) #define ASCV_SDTR_DATA_BEG (ASCV_MSGIN_BEG+8) #define ASCV_SDTR_DONE_BEG (ASCV_SDTR_DATA_BEG+8) #define ASCV_MAX_DVC_QNG_BEG ( ushort )0x0020 +#define ASCV_BREAK_ADDR ( ushort )0x0028 +#define ASCV_BREAK_NOTIFY_COUNT ( ushort )0x002A +#define ASCV_BREAK_CONTROL ( ushort )0x002C +#define ASCV_BREAK_HIT_COUNT ( ushort )0x002E + #define ASCV_ASCDVC_ERR_CODE_W ( ushort )0x0030 #define ASCV_MCODE_CHKSUM_W ( ushort )0x0032 #define ASCV_MCODE_SIZE_W ( ushort )0x0034 @@ -2052,7 +2097,11 @@ #define ASC_NUM_BUS 4 /* Reference Scsi_Host hostdata */ -#define ASC_BOARDP(host) ((struct asc_board *) &((host)->hostdata)) +#define ASC_BOARDP(host) ((asc_board_t *) &((host)->hostdata)) + +/* asc_board_t flags */ +#define ASC_HOST_IN_RESET 0x01 +#define ASC_HOST_IN_ABORT 0x02 #define NO_ISA_DMA 0xff /* No ISA DMA Channel Used */ @@ -2102,6 +2151,12 @@ #define ASC_FRONT 1 #define ASC_BACK 2 +/* asc_dequeue_list() argument */ +#define ASC_TID_ALL (-1) + +/* Return non-zero, if the queue is empty. */ +#define ASC_QUEUE_EMPTY(ascq) ((ascq)->q_tidmask == 0) + /* PCI configuration declarations */ #define PCI_BASE_CLASS_PREDEFINED 0x00 @@ -2364,6 +2419,7 @@ ulong check_interrupt;/* # advansys_interrupt() check pending calls */ ulong interrupt; /* # advansys_interrupt() interrupts */ ulong callback; /* # calls asc_isr_callback() */ + ulong done; /* # calls request scsi_done */ /* AscExeScsiQueue() Statistics */ ulong asc_noerror; /* # AscExeScsiQueue() ASC_NOERROR returns. */ ulong asc_busy; /* # AscExeScsiQueue() ASC_BUSY returns. */ @@ -2374,7 +2430,7 @@ ulong cont_xfer; /* # contiguous transfer 512-bytes */ ulong sg_cnt; /* # scatter-gather I/O requests received */ ulong sg_elem; /* # scatter-gather elements */ - ulong sg_xfer; /* # scatter-gather tranfer 512-bytes */ + ulong sg_xfer; /* # scatter-gather transfer 512-bytes */ /* Device SCSI Command Queuing Statistics */ ASC_SCSI_BIT_ID_TYPE queue_full; ushort queue_full_cnt[ASC_MAX_TID+1]; @@ -2385,11 +2441,12 @@ * Request queuing structure */ typedef struct asc_queue { - ASC_SCSI_BIT_ID_TYPE tidmask; /* queue mask */ - REQP queue[ASC_MAX_TID+1]; /* queue linked list */ + ASC_SCSI_BIT_ID_TYPE q_tidmask; /* queue mask */ + REQP q_first[ASC_MAX_TID+1]; /* first queued request */ + REQP q_last[ASC_MAX_TID+1]; /* last queued request */ #ifdef ADVANSYS_STATS - short cur_count[ASC_MAX_TID+1]; /* current queue count */ - short max_count[ASC_MAX_TID+1]; /* maximum queue count */ + short q_cur_cnt[ASC_MAX_TID+1]; /* current queue count */ + short q_max_cnt[ASC_MAX_TID+1]; /* maximum queue count */ #endif /* ADVANSYS_STATS */ } asc_queue_t; @@ -2400,28 +2457,25 @@ * of the 'Scsi_Host' structure starting at the 'hostdata' * field. It is guaranteed to be allocated from DMA-able memory. */ -struct asc_board { - int id; /* Board Id */ - /* Asc Library */ - ASC_DVC_VAR asc_dvc_var; /* Board configuration */ - ASC_DVC_CFG asc_dvc_cfg; /* Device configuration */ - /* Queued Commands */ - asc_queue_t active; /* Active command queue */ - asc_queue_t pending; /* Pending command queue */ - /* Target Initialization */ - ASC_SCSI_BIT_ID_TYPE init_tidmask; /* Target initialized mask */ - ASC_SCSI_REQ_Q scsireqq; - ASC_CAP_INFO cap_info; - ASC_SCSI_INQUIRY inquiry; - ASCEEP_CONFIG eep_config; /* EEPROM configuration */ +typedef struct asc_board { + int id; /* Board Id */ + uint flags; /* Board flags */ + ASC_DVC_VAR asc_dvc_var; /* Board configuration */ + ASC_DVC_CFG asc_dvc_cfg; /* Device configuration */ + asc_queue_t active; /* Active command queue */ + asc_queue_t waiting; /* Waiting command queue */ + ASC_SCSI_BIT_ID_TYPE init_tidmask; /* Target initialized mask */ + ASCEEP_CONFIG eep_config; /* EEPROM configuration */ + asc_queue_t scsi_done_q; /* Completion command queue */ + ulong reset_jiffies; /* Saved time of last reset */ #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) /* /proc/scsi/advansys/[0...] */ - char *prtbuf; /* Statistics Print Buffer */ + char *prtbuf; /* Statistics Print Buffer */ #endif /* version >= v1.3.0 */ #ifdef ADVANSYS_STATS - struct asc_stats asc_stats; /* Board statistics */ + struct asc_stats asc_stats; /* Board statistics */ #endif /* ADVANSYS_STATS */ -}; +} asc_board_t; /* * PCI configuration structures @@ -2494,12 +2548,22 @@ STATIC int asc_board_count = 0; STATIC struct Scsi_Host *asc_host[ASC_NUM_BOARD_SUPPORTED] = { 0 }; -/* Global list of commands needing done function. */ -STATIC Scsi_Cmnd *asc_scsi_done = NULL; - /* Overrun buffer shared between all boards. */ STATIC uchar overrun_buf[ASC_OVERRUN_BSIZE] = { 0 }; +/* + * Global structures used for device initialization. + */ +STATIC ASC_SCSI_REQ_Q asc_scsireqq = { { 0 } }; +STATIC ASC_CAP_INFO asc_cap_info = { 0 }; +STATIC ASC_SCSI_INQUIRY asc_inquiry = { { 0 } }; + +/* + * Global structures required to issue a command. + */ +STATIC ASC_SCSI_Q asc_scsi_q = { { 0 } }; +STATIC ASC_SG_HEAD asc_sg_head = { 0 }; + /* List of supported bus types. */ STATIC ushort asc_bus[ASC_NUM_BUS] = { ASC_IS_ISA, @@ -2549,6 +2613,7 @@ Scsi_Device *); #endif /* version >= v1.3.89 */ STATIC void advansys_command_done(Scsi_Cmnd *); +STATIC void asc_scsi_done_list(Scsi_Cmnd *); STATIC int asc_execute_scsi_cmnd(Scsi_Cmnd *); STATIC void asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *); STATIC int asc_init_dev(ASC_DVC_VAR *, Scsi_Cmnd *); @@ -2561,11 +2626,13 @@ STATIC void asc_put_cfg_byte(PCI_DATA *, uchar); void asc_enqueue(asc_queue_t *, REQP, int); REQP asc_dequeue(asc_queue_t *, int); +REQP asc_dequeue_list(asc_queue_t *, REQP *, int); int asc_rmqueue(asc_queue_t *, REQP); int asc_isqueued(asc_queue_t *, REQP); void asc_execute_queue(asc_queue_t *); STATIC int asc_prt_board_devices(struct Scsi_Host *, char *, int); STATIC int asc_prt_board_eeprom(struct Scsi_Host *, char *, int); +STATIC int asc_prt_driver_conf(struct Scsi_Host *, char *, int); STATIC int asc_prt_board_info(struct Scsi_Host *, char *, int); STATIC int asc_proc_copy(off_t, off_t, char *, int , char *, int); STATIC int asc_prt_line(char *, int, char *fmt, ...); @@ -2618,7 +2685,7 @@ int hostno, int inout) { struct Scsi_Host *shp; - struct asc_board *boardp; + asc_board_t *boardp; int i; char *cp; int cplen; @@ -2738,6 +2805,22 @@ advoffset += cplen; curbuf += cnt; + /* + * Display driver configuration and information for the board. + */ + cp = boardp->prtbuf; + cplen = asc_prt_driver_conf(shp, cp, ASC_PRTBUF_SIZE); + ASC_ASSERT(cplen < ASC_PRTBUF_SIZE); + cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen); + totcnt += cnt; + leftlen -= cnt; + if (leftlen == 0) { + ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt); + return totcnt; + } + advoffset += cplen; + curbuf += cnt; + #ifdef ADVANSYS_STATS /* * Display driver statistics for the board. @@ -2800,7 +2883,7 @@ int iop; int bus; struct Scsi_Host *shp; - struct asc_board *boardp; + asc_board_t *boardp; ASC_DVC_VAR *asc_dvc_varp; int ioport = 0; int share_irq = FALSE; @@ -2966,14 +3049,14 @@ * initialize it. */ ASC_DBG(2, "advansys_detect: scsi_register()\n"); - shp = scsi_register(tpnt, sizeof(struct asc_board)); + shp = scsi_register(tpnt, sizeof(asc_board_t)); /* Save a pointer to the Scsi_host of each board found. */ asc_host[asc_board_count++] = shp; /* Initialize private per board data */ boardp = ASC_BOARDP(shp); - memset(boardp, 0, sizeof(struct asc_board)); + memset(boardp, 0, sizeof(asc_board_t)); boardp->id = asc_board_count - 1; asc_dvc_varp = &boardp->asc_dvc_var; asc_dvc_varp->cfg = &boardp->asc_dvc_cfg; @@ -2984,7 +3067,7 @@ if ((boardp->prtbuf = kmalloc(ASC_PRTBUF_SIZE, GFP_ATOMIC)) == NULL) { ASC_PRINT3( -"advansys_detect: Board %d: kmalloc(%d, %d) returned NULL\n", +"advansys_detect: board %d: kmalloc(%d, %d) returned NULL\n", boardp->id, ASC_PRTBUF_SIZE, GFP_ATOMIC); scsi_unregister(shp); asc_board_count--; @@ -3021,7 +3104,7 @@ break; default: ASC_PRINT2( -"advansys_detect: Board %d: unknown adapter type: %d", +"advansys_detect: board %d: unknown adapter type: %d", boardp->id, asc_dvc_varp->bus_type); shp->unchecked_isa_dma = TRUE; share_irq = FALSE; @@ -3042,38 +3125,38 @@ break; case ASC_WARN_IO_PORT_ROTATE: ASC_PRINT1( -"AscInitGetConfig: Board: %d: I/O port address modified\n", +"AscInitGetConfig: board %d: I/O port address modified\n", boardp->id); break; case ASC_WARN_AUTO_CONFIG: ASC_PRINT1( -"AscInitGetConfig: Board %d: I/O port increment switch enabled\n", +"AscInitGetConfig: board %d: I/O port increment switch enabled\n", boardp->id); break; case ASC_WARN_EEPROM_CHKSUM: ASC_PRINT1( -"AscInitGetConfig: Board %d: EEPROM checksum error\n", +"AscInitGetConfig: board %d: EEPROM checksum error\n", boardp->id); break; case ASC_WARN_IRQ_MODIFIED: ASC_PRINT1( -"AscInitGetConfig: Board %d: IRQ modified\n", +"AscInitGetConfig: board %d: IRQ modified\n", boardp->id); break; case ASC_WARN_CMD_QNG_CONFLICT: ASC_PRINT1( -"AscInitGetConfig: Board %d: tag queuing enabled w/o disconnects\n", +"AscInitGetConfig: board %d: tag queuing enabled w/o disconnects\n", boardp->id); break; default: ASC_PRINT2( -"AscInitGetConfig: Board %d: unknown warning: %x\n", +"AscInitGetConfig: board %d: unknown warning: %x\n", boardp->id, ret); break; } if (asc_dvc_varp->err_code != 0) { ASC_PRINT3( -"AscInitGetConfig: Board %d error: init_state %x, err_code %x\n", +"AscInitGetConfig: board %d error: init_state %x, err_code %x\n", boardp->id, asc_dvc_varp->init_state, asc_dvc_varp->err_code); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) @@ -3117,38 +3200,38 @@ break; case ASC_WARN_IO_PORT_ROTATE: ASC_PRINT1( -"AscInitSetConfig: Board %d: I/O port address modified\n", +"AscInitSetConfig: board %d: I/O port address modified\n", boardp->id); break; case ASC_WARN_AUTO_CONFIG: ASC_PRINT1( -"AscInitSetConfig: Board %d: I/O port increment switch enabled\n", +"AscInitSetConfig: board %d: I/O port increment switch enabled\n", boardp->id); break; case ASC_WARN_EEPROM_CHKSUM: ASC_PRINT1( -"AscInitSetConfig: Board %d: EEPROM checksum error\n", +"AscInitSetConfig: board %d: EEPROM checksum error\n", boardp->id); break; case ASC_WARN_IRQ_MODIFIED: ASC_PRINT1( -"AscInitSetConfig: Board %d: IRQ modified\n", +"AscInitSetConfig: board %d: IRQ modified\n", boardp->id); break; case ASC_WARN_CMD_QNG_CONFLICT: ASC_PRINT1( -"AscInitSetConfig: Board %d: tag queuing w/o disconnects\n", +"AscInitSetConfig: board %d: tag queuing w/o disconnects\n", boardp->id); break; default: ASC_PRINT2( -"AscInitSetConfig: Board %d: unknown warning: %x\n", +"AscInitSetConfig: board %d: unknown warning: %x\n", boardp->id, ret); break; } if (asc_dvc_varp->err_code != 0) { ASC_PRINT3( -"AscInitSetConfig: Board %d error: init_state %x, err_code %x\n", +"AscInitSetConfig: board %d error: init_state %x, err_code %x\n", boardp->id, asc_dvc_varp->init_state, asc_dvc_varp->err_code); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) @@ -3168,6 +3251,16 @@ shp->irq = asc_dvc_varp->irq_no; } + /* + * One host supports one channel. There are two different + * hosts for each channel of a dual channel board. + */ +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) + shp->max_channel = 0; +#endif /* version >= v1.3.89 */ + shp->max_id = ASC_MAX_TID + 1; + shp->max_lun = ASC_MAX_LUN + 1; + shp->io_port = asc_dvc_varp->iop_base; shp->n_io_port = ASC_IOADR_GAP; shp->this_id = asc_dvc_varp->cfg->chip_scsi_id; @@ -3196,7 +3289,6 @@ shp->cmd_per_lun = 0; /* 'cmd_per_lun' is no longer used. */ #endif /* version >= v1.3.89 */ - /* * Maximum number of scatter-gather elements adapter can handle. * @@ -3206,8 +3298,27 @@ #ifdef MODULE shp->sg_tablesize = 8; #else /* MODULE */ - shp->sg_tablesize = ASC_MAX_SG_LIST; + /* + * Allow two commands with 'sg_tablesize' scatter-gather + * elements to be executed simultaneously. This value is + * the theoretical hardware limit. It may be decreased + * below. + */ + shp->sg_tablesize = + (((asc_dvc_varp->max_total_qng - 2) / 2) * + ASC_SG_LIST_PER_Q) + 1; #endif /* MODULE */ + + /* + * The value of 'sg_tablesize' can not exceed the SCSI + * mid-level driver definition of SG_ALL. SG_ALL also + * must not be exceeded, because it is used to define the + * size of the scatter-gather table in 'struct asc_sg_head'. + */ + if (shp->sg_tablesize > SG_ALL) { + shp->sg_tablesize = SG_ALL; + } + ASC_DBG1(1, "advansys_detect: sg_tablesize: %d\n", shp->sg_tablesize); @@ -3231,7 +3342,7 @@ shp->dma_channel = asc_dvc_varp->cfg->isa_dma_channel; if ((ret = request_dma(shp->dma_channel, "advansys")) != 0) { ASC_PRINT3( -"advansys_detect: Board %d: request_dma() %d failed %d\n", +"advansys_detect: board %d: request_dma() %d failed %d\n", boardp->id, shp->dma_channel, ret); release_region(shp->io_port, shp->n_io_port); #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0) @@ -3255,7 +3366,7 @@ "advansys", boardp)) != 0) { #endif /* version >= v1.3.70 */ ASC_PRINT2( -"advansys_detect: Board %d: request_irq() failed %d\n", +"advansys_detect: board %d: request_irq() failed %d\n", boardp->id, ret); release_region(shp->io_port, shp->n_io_port); if (shp->dma_channel != NO_ISA_DMA) { @@ -3275,7 +3386,7 @@ ASC_DBG(2, "advansys_detect: AscInitAsc1000Driver()\n"); if (AscInitAsc1000Driver(asc_dvc_varp)) { ASC_PRINT3( -"AscInitAsc1000Driver: Board %d error: init_state %x, err_code %x\n", +"AscInitAsc1000Driver: board %d: error: init_state %x, err_code %x\n", boardp->id, asc_dvc_varp->init_state, asc_dvc_varp->err_code); release_region(shp->io_port, shp->n_io_port); @@ -3309,7 +3420,7 @@ int advansys_release(struct Scsi_Host *shp) { - struct asc_board *boardp; + asc_board_t *boardp; ASC_DBG(1, "advansys_release: begin\n"); boardp = ASC_BOARDP(shp); @@ -3344,18 +3455,23 @@ const char * advansys_info(struct Scsi_Host *shp) { - static char info[ASC_INFO_SIZE]; - struct asc_board *boardp; - ASC_DVC_VAR *asc_dvc_varp; - char *busname; + static char info[ASC_INFO_SIZE]; + asc_board_t *boardp; + ASC_DVC_VAR *asc_dvc_varp; + char *busname; boardp = ASC_BOARDP(shp); asc_dvc_varp = &boardp->asc_dvc_var; ASC_DBG(1, "advansys_info: begin\n"); if (asc_dvc_varp->bus_type & ASC_IS_ISA) { + if ((asc_dvc_varp->bus_type & ASC_IS_ISAPNP) == ASC_IS_ISAPNP) { + busname = "ISA PnP"; + } else { + busname = "ISA"; + } sprintf(info, - "AdvanSys SCSI %s: ISA (%u CDB): BIOS %X, IO %X-%X, IRQ %u, DMA %u", - ASC_VERSION, boardp->asc_dvc_var.max_total_qng, + "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X-%X, IRQ %u, DMA %u", + ASC_VERSION, busname, boardp->asc_dvc_var.max_total_qng, (unsigned) shp->base, shp->io_port, shp->io_port + (shp->n_io_port - 1), shp->irq, shp->dma_channel); } else { @@ -3364,16 +3480,21 @@ } else if (asc_dvc_varp->bus_type & ASC_IS_EISA) { busname = "EISA"; } else if (asc_dvc_varp->bus_type & ASC_IS_PCI) { - busname = "PCI"; + if ((asc_dvc_varp->bus_type & ASC_IS_PCI_ULTRA) + == ASC_IS_PCI_ULTRA) { + busname = "PCI Ultra"; + } else { + busname = "PCI"; + } } else { busname = "?"; ASC_PRINT2( -"advansys_info: Board %d: unknown bus type %d\n", +"advansys_info: board %d: unknown bus type %d\n", boardp->id, asc_dvc_varp->bus_type); } /* No DMA channel for non-ISA busses. */ sprintf(info, - "AdvanSys SCSI %s: %s (%u CDB): BIOS %X, IO %X-%X, IRQ %u", + "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X-%X, IRQ %u", ASC_VERSION, busname, boardp->asc_dvc_var.max_total_qng, (unsigned) shp->base, shp->io_port, shp->io_port + (shp->n_io_port - 1), shp->irq); @@ -3416,52 +3537,88 @@ int advansys_queuecommand(Scsi_Cmnd *scp, void (*done)(Scsi_Cmnd *)) { - struct Scsi_Host *shp; - struct asc_board *boardp; - int flags = 0; - int interrupts_disabled; + struct Scsi_Host *shp; + asc_board_t *boardp; + int flags; + Scsi_Cmnd *done_scp; shp = scp->host; boardp = ASC_BOARDP(shp); ASC_STATS(shp, queuecommand); /* - * If there are any pending commands for this board before trying - * to execute them, disable interrupts to preserve request ordering. - * - * The typical case will be no pending commands and interrupts - * not disabled. + * Disable interrupts to preserve request ordering and provide + * mutually exclusive access to global structures used to initiate + * a request. */ - if (boardp->pending.tidmask == 0) { - interrupts_disabled = ASC_FALSE; - } else { - /* Disable interrupts */ - interrupts_disabled = ASC_TRUE; - save_flags(flags); - cli(); - ASC_DBG1(1, "advansys_queuecommand: asc_execute_queue() %x\n", - boardp->pending.tidmask); - asc_execute_queue(&boardp->pending); - } + save_flags(flags); + cli(); /* - * Save the function pointer to Linux mid-level 'done' function and - * execute the command. + * Block new commands while handling a reset or abort request. */ - scp->scsi_done = done; - if (asc_execute_scsi_cmnd(scp) == ASC_BUSY) { - if (interrupts_disabled == ASC_FALSE) { - save_flags(flags); - cli(); - interrupts_disabled = ASC_TRUE; + if (boardp->flags & (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) { + if (boardp->flags & ASC_HOST_IN_RESET) { + ASC_DBG1(1, + "advansys_queuecommand: scp %x blocked for reset request\n", + (unsigned) scp); + scp->result = HOST_BYTE(DID_RESET); + } else { + ASC_DBG1(1, + "advansys_queuecommand: scp %x blocked for abort request\n", + (unsigned) scp); + scp->result = HOST_BYTE(DID_ABORT); } - asc_enqueue(&boardp->pending, scp, ASC_BACK); - } - if (interrupts_disabled == ASC_TRUE) { + /* + * Add blocked requests to the board's 'scsi_done_q'. The queued + * requests will be completed at the end of the abort or reset + * handling. + */ + asc_enqueue(&boardp->scsi_done_q, scp, ASC_BACK); restore_flags(flags); + return 0; + } + + /* + * Attempt to execute any waiting commands for the board. + */ + if (!ASC_QUEUE_EMPTY(&boardp->waiting)) { + ASC_DBG(1, + "advansys_queuecommand: before asc_execute_queue() waiting\n"); + asc_execute_queue(&boardp->waiting); + } + + /* + * Save the function pointer to Linux mid-level 'done' function + * and attempt to execute the command. + * + * If ASC_ERROR is returned the request has been added to the + * board's 'active' queue and will be completed by the interrupt + * handler. + * + * If ASC_BUSY is returned add the request to the board's per + * target waiting list. + * + * If an error occurred, the request will have been placed on the + * board's 'scsi_done_q' and must be completed before returning. + */ + scp->scsi_done = done; + switch (asc_execute_scsi_cmnd(scp)) { + case ASC_NOERROR: + break; + case ASC_BUSY: + asc_enqueue(&boardp->waiting, scp, ASC_BACK); + break; + case ASC_ERROR: + default: + done_scp = asc_dequeue_list(&boardp->scsi_done_q, NULL, ASC_TID_ALL); + /* Interrupts could be enabled here. */ + asc_scsi_done_list(done_scp); + break; } + restore_flags(flags); return 0; } @@ -3473,49 +3630,70 @@ int advansys_abort(Scsi_Cmnd *scp) { - struct asc_board *boardp; + struct Scsi_Host *shp; + asc_board_t *boardp; ASC_DVC_VAR *asc_dvc_varp; int flags; - int abort; - int ret; + int status = ASC_FALSE; + int abort_do_done = ASC_FALSE; + Scsi_Cmnd *done_scp; + int ret = ASC_ERROR; ASC_DBG1(1, "advansys_abort: scp %x\n", (unsigned) scp); - ASC_STATS(scp->host, abort); /* Save current flags and disable interrupts. */ save_flags(flags); cli(); +#ifdef ADVANSYS_STATS + if (scp->host != NULL) { + ASC_STATS(scp->host, abort); + } +#endif /* ADVANSYS_STATS */ + #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) if (scp->serial_number != scp->serial_number_at_timeout) { ret = SCSI_ABORT_NOT_RUNNING; } else #endif /* version >= v1.3.89 */ - if (scp->host == NULL) { + if ((shp = scp->host) == NULL) { + scp->result = HOST_BYTE(DID_ERROR); + ret = SCSI_ABORT_ERROR; + } else if ((boardp = ASC_BOARDP(shp))->flags & + (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) { + ASC_PRINT2( +"advansys_abort: board %d: Nested host reset or abort, flags 0x%x\n", + boardp->id, boardp->flags); scp->result = HOST_BYTE(DID_ERROR); ret = SCSI_ABORT_ERROR; } else { - boardp = ASC_BOARDP(scp->host); - if (asc_rmqueue(&boardp->pending, scp) == ASC_TRUE) { + /* Set abort flag to avoid nested reset or abort requests. */ + boardp->flags |= ASC_HOST_IN_ABORT; + + if (asc_rmqueue(&boardp->waiting, scp) == ASC_TRUE) { /* - * If asc_rmqueue() found the command on the pending - * queue, it had not been sent to the Asc Library. - * After the queue is removed, no other handling is required. + * If asc_rmqueue() found the command on the waiting + * queue, it had not been sent to the device. After + * the queue is removed, no other handling is required. */ + ASC_DBG1(1, "advansys_abort: scp %x found on waiting queue\n", + (unsigned) scp); scp->result = HOST_BYTE(DID_ABORT); ret = SCSI_ABORT_SUCCESS; } else if (asc_isqueued(&boardp->active, scp) == ASC_TRUE) { /* * If asc_isqueued() found the command on the active - * queue, it has been sent to the Asc Library. The - * command should be returned through the interrupt - * handler after calling AscAbortSRB(). + * queue, it has been sent to the device. The command + * should be returned through the interrupt handler after + * calling AscAbortSRB(). */ asc_dvc_varp = &boardp->asc_dvc_var; scp->result = HOST_BYTE(DID_ABORT); - /* Must enable interrupts for AscAbortSRB() */ - sti(); - switch (abort = AscAbortSRB(asc_dvc_varp, (ulong) scp)) { + + sti(); /* Enable interrupts for AscAbortSRB(). */ + ASC_DBG1(1, "advansys_abort: before AscAbortSRB(), scp %x\n", + (unsigned) scp); + switch (status = AscAbortSRB(asc_dvc_varp, (ulong) scp)) { case ASC_TRUE: /* asc_isr_callback() will be called */ ASC_DBG(1, "advansys_abort: AscAbortSRB() TRUE\n"); @@ -3533,25 +3711,69 @@ break; } cli(); + /* * If the abort failed, remove the request from the * active list and complete it. */ - if (abort != ASC_TRUE) { + if (status != ASC_TRUE) { if (asc_rmqueue(&boardp->active, scp) == ASC_TRUE) { scp->result = HOST_BYTE(DID_ABORT); - scp->scsi_done(scp); + abort_do_done = ASC_TRUE; } } + } else { /* - * The command was not found on the active or pending queues. + * The command was not found on the active or waiting queues. */ ret = SCSI_ABORT_NOT_RUNNING; } + + /* Clear abort flag. */ + boardp->flags &= ~ASC_HOST_IN_ABORT; + + /* + * Because the ASC_HOST_IN_ABORT flag causes both + * 'advansys_interrupt' and 'asc_isr_callback' to + * queue requests to the board's 'scsi_done_q' and + * prevents waiting commands from being executed, + * these queued requests must be handled here. + */ + done_scp = asc_dequeue_list(&boardp->scsi_done_q, NULL, ASC_TID_ALL); + + /* + * Start any waiting commands for the board. + */ + if (!ASC_QUEUE_EMPTY(&boardp->waiting)) { + ASC_DBG(1, "advansys_interrupt: before asc_execute_queue()\n"); + asc_execute_queue(&boardp->waiting); + } + + /* Interrupts could be enabled here. */ + + /* + * If needed, complete the aborted request. + */ + if (abort_do_done == ASC_TRUE) { + ASC_STATS(scp->host, done); + scp->scsi_done(scp); + } + + /* + * It is possible for the request done function to re-enable + * interrupts without confusing the driver. But here interrupts + * aren't enabled until all requests have been completed. + */ + asc_scsi_done_list(done_scp); } - restore_flags(flags); + ASC_DBG1(1, "advansys_abort: ret %d\n", ret); + + /* Re-enable interrupts, if they were enabled on entry. */ + restore_flags(flags); + + ASC_ASSERT(ret != ASC_ERROR); return ret; } @@ -3567,44 +3789,74 @@ advansys_reset(Scsi_Cmnd *scp, unsigned int reset_flags) #endif /* version >= v1.3.89 */ { - struct asc_board *boardp; + struct Scsi_Host *shp; + asc_board_t *boardp; ASC_DVC_VAR *asc_dvc_varp; int flags; - Scsi_Cmnd *tscp; -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) + Scsi_Cmnd *done_scp = NULL, *last_scp = NULL; + Scsi_Cmnd *tscp, *new_last_scp; int scp_found = ASC_FALSE; -#endif /* version >= v1.3.89 */ - int i; - int ret; + int device_reset = ASC_FALSE; + int status; + int target; + int ret = ASC_ERROR; ASC_DBG1(1, "advansys_reset: %x\n", (unsigned) scp); - ASC_STATS(scp->host, reset); /* Save current flags and disable interrupts. */ save_flags(flags); cli(); +#ifdef ADVANSYS_STATS + if (scp->host != NULL) { + ASC_STATS(scp->host, reset); + } +#endif /* ADVANSYS_STATS */ + #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) if (scp->serial_number != scp->serial_number_at_timeout) { ret = SCSI_RESET_NOT_RUNNING; } else #endif /* version >= v1.3.89 */ - if (scp->host == NULL) { + if ((shp = scp->host) == NULL) { + scp->result = HOST_BYTE(DID_ERROR); + ret = SCSI_RESET_ERROR; + } else if ((boardp = ASC_BOARDP(shp))->flags & + (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) { + ASC_PRINT2( +"advansys_reset: board %d: Nested host reset or abort, flags 0x%x\n", + boardp->id, boardp->flags); + scp->result = HOST_BYTE(DID_ERROR); + ret = SCSI_RESET_ERROR; + } else if (jiffies >= boardp->reset_jiffies && + jiffies < (boardp->reset_jiffies + (10 * HZ))) { + /* + * Don't allow a reset to be attempted within 10 seconds + * of the last reset. + * + * If 'jiffies' wrapping occurs, the reset request will go + * through, because a wrapped 'jiffies' would not pass the + * test above. + */ + ASC_DBG(1, + "advansys_reset: reset within 10 sec of last reset ignored\n"); scp->result = HOST_BYTE(DID_ERROR); ret = SCSI_RESET_ERROR; } else { - boardp = ASC_BOARDP(scp->host); + /* Set reset flag to avoid nested reset or abort requests. */ + boardp->flags |= ASC_HOST_IN_RESET; -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) /* - * If the request is on the target pending or active queue, - * note that it was found. + * If the request is on the target waiting or active queue, + * note that it was found and remove it from its queue. */ - if ((asc_isqueued(&boardp->pending, scp) == ASC_TRUE) || - (asc_isqueued(&boardp->active, scp) == ASC_TRUE)) { + if (asc_rmqueue(&boardp->active, scp) == ASC_TRUE) { + ASC_DBG(1, "advansys_reset: active scp_found = TRUE\n"); + scp_found = ASC_TRUE; + } else if (asc_rmqueue(&boardp->waiting, scp) == ASC_TRUE) { + ASC_DBG(1, "advansys_reset: waiting scp_found = TRUE\n"); scp_found = ASC_TRUE; } -#endif /* version >= v1.3.89 */ /* * If the suggest reset bus flags are set, reset the bus. @@ -3617,85 +3869,192 @@ #endif /* version >= v1.3.89 */ /* - * Done all pending requests for all targets with DID_RESET. - */ - for (i = 0; i <= ASC_MAX_TID; i++) { - while ((tscp = asc_dequeue(&boardp->pending, i)) != NULL) { - tscp->result = HOST_BYTE(DID_RESET); - tscp->scsi_done(tscp); - } - } - - /* * Reset the target's SCSI bus. */ + ASC_DBG(1, "advansys_reset: before AscResetSB()\n"); sti(); /* Enable interrupts for AscResetSB(). */ - switch (AscResetSB(asc_dvc_varp)) { + status = AscResetSB(asc_dvc_varp); + cli(); + switch (status) { case ASC_TRUE: - ASC_DBG(1, "advansys_reset: AscResetSB() TRUE\n"); + ASC_DBG(1, "advansys_reset: AscResetSB() success\n"); ret = SCSI_RESET_SUCCESS; break; case ASC_ERROR: default: - ASC_DBG(1, "advansys_reset: AscResetSB() ERROR\n"); + ASC_DBG(1, "advansys_reset: AscResetSB() failed\n"); ret = SCSI_RESET_ERROR; break; } - cli(); - /* - * Done all active requests for all targets with DID_RESET. - */ - for (i = 0; i <= ASC_MAX_TID; i++) { - while ((tscp = asc_dequeue(&boardp->active, i)) != NULL) { - tscp->result = HOST_BYTE(DID_RESET); - tscp->scsi_done(tscp); - } - } #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) } else { /* - * Done all pending requests for the target with DID_RESET. + * Reset the specified device. If the device reset fails, + * then reset the SCSI bus. */ - while ((tscp = asc_dequeue(&boardp->pending, scp->target)) - != NULL) { - tscp->result = HOST_BYTE(DID_RESET); - tscp->scsi_done(tscp); - } - sti(); /* Enabled interrupts for AscResetDevice(). */ - ASC_DBG(1, "advansys_reset: AscResetDevice()\n"); - (void) AscResetDevice(asc_dvc_varp, scp->target); + ASC_DBG1(1, "advansys_reset: before AscResetDevice(), target %d\n", + scp->target); + sti(); /* Enable interrupts for AscResetDevice(). */ + status = AscResetDevice(asc_dvc_varp, scp->target); cli(); /* - * Done all active requests for the target with DID_RESET. + * If the device has been reset, try to initialize it. */ - while ((tscp = asc_dequeue(&boardp->active, scp->target)) - != NULL) { - tscp->result = HOST_BYTE(DID_RESET); - tscp->scsi_done(tscp); + if (status == ASC_TRUE) { + status = asc_init_dev(asc_dvc_varp, scp); + } + + switch (status) { + case ASC_TRUE: + ASC_DBG(1, "advansys_reset: AscResetDevice() success\n"); + device_reset = ASC_TRUE; + ret = SCSI_RESET_SUCCESS; + break; + case ASC_ERROR: + default: + ASC_DBG(1, +"advansys_reset: AscResetDevice() failed; Calling AscResetSB()\n"); + sti(); /* Enable interrupts for AscResetSB(). */ + status = AscResetSB(asc_dvc_varp); + cli(); + switch (status) { + case ASC_TRUE: + ASC_DBG(1, "advansys_reset: AscResetSB() TRUE\n"); + ret = SCSI_RESET_SUCCESS; + break; + case ASC_ERROR: + default: + ASC_DBG(1, "advansys_reset: AscResetSB() ERROR\n"); + ret = SCSI_RESET_ERROR; + break; + } + break; } } #endif /* version >= v1.3.89 */ + /* + * Because the ASC_HOST_IN_RESET flag causes both + * 'advansys_interrupt' and 'asc_isr_callback' to + * queue requests to the board's 'scsi_done_q' and + * prevents waiting commands from being executed, + * these queued requests must be handled here. + */ + done_scp = asc_dequeue_list(&boardp->scsi_done_q, &last_scp, + ASC_TID_ALL); + + /* + * If a device reset was performed dequeue all waiting + * and active requests for the device and set the request + * status to DID_RESET. + * + * If a SCSI bus reset was performed dequeue all waiting + * and active requests for all devices and set the request + * status to DID_RESET. + */ + if (device_reset == ASC_TRUE) { + target = scp->target; + } else { + target = ASC_TID_ALL; + } + + /* + * Add active requests to 'done_scp' and set the request status + * to DID_RESET. + */ + if (done_scp == NULL) { + done_scp = asc_dequeue_list(&boardp->active, &last_scp, target); + for (tscp = done_scp; tscp; tscp = REQPNEXT(tscp)) { + tscp->result = HOST_BYTE(DID_RESET); + } + } else { + ASC_ASSERT(last_scp != NULL); + REQPNEXT(last_scp) = asc_dequeue_list(&boardp->active, + &new_last_scp, target); + if (new_last_scp != NULL) { + ASC_ASSERT(REQPNEXT(last_scp) != NULL); + for (tscp = REQPNEXT(last_scp); tscp; tscp = REQPNEXT(tscp)) { + tscp->result = HOST_BYTE(DID_RESET); + } + last_scp = new_last_scp; + } + } + + /* + * Add waiting requests to 'done_scp' and set the request status + * to DID_RESET. + */ + if (done_scp == NULL) { + done_scp = asc_dequeue_list(&boardp->waiting, &last_scp, target); + for (tscp = done_scp; tscp; tscp = REQPNEXT(tscp)) { + tscp->result = HOST_BYTE(DID_RESET); + } + } else { + ASC_ASSERT(last_scp != NULL); + REQPNEXT(last_scp) = asc_dequeue_list(&boardp->waiting, + &new_last_scp, target); + if (new_last_scp != NULL) { + ASC_ASSERT(REQPNEXT(last_scp) != NULL); + for (tscp = REQPNEXT(last_scp); tscp; tscp = REQPNEXT(tscp)) { + tscp->result = HOST_BYTE(DID_RESET); + } + last_scp = new_last_scp; + } + } + + /* Save the time of the most recently completed reset. */ + boardp->reset_jiffies = jiffies; + + /* Clear reset flag. */ + boardp->flags &= ~ASC_HOST_IN_RESET; + + /* + * Start any waiting commands for the board. + */ + if (!ASC_QUEUE_EMPTY(&boardp->waiting)) { + ASC_DBG(1, "advansys_interrupt: before asc_execute_queue()\n"); + asc_execute_queue(&boardp->waiting); + } + + /* Interrupts could be enabled here. */ + #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) /* - * If the command was not on the active or pending request - * queues and the SCSI_RESET_SYNCHRONOUS flag is set, then - * done the command now. If the command had been on the - * active or pending request queues it would have already - * been completed. + * If the command was found on the active or waiting request + * queues or if the the SCSI_RESET_SYNCHRONOUS flag is set, + * then done the command now. */ - if (scp_found == ASC_FALSE && (reset_flags & SCSI_RESET_SYNCHRONOUS)) { + if (scp_found == ASC_TRUE || (reset_flags & SCSI_RESET_SYNCHRONOUS)) { scp->result = HOST_BYTE(DID_RESET); - scp->scsi_done(tscp); + ASC_STATS(scp->host, done); + scp->scsi_done(scp); + } +#else /* version >= v1.3.89 */ + if (scp_found == ASC_TRUE) { + scp->result = HOST_BYTE(DID_RESET); + ASC_STATS(scp->host, done); + scp->scsi_done(scp); } #endif /* version >= v1.3.89 */ ret = SCSI_RESET_SUCCESS; + + /* + * It is possible for the request done function to re-enable + * interrupts without confusing the driver. But here interrupts + * aren't enabled until requests have been completed. + */ + asc_scsi_done_list(done_scp); } - restore_flags(flags); + ASC_DBG1(1, "advansys_reset: ret %d", ret); + + /* Re-enable interrupts, if they were enabled on entry. */ + restore_flags(flags); + + ASC_ASSERT(ret != ASC_ERROR); return ret; } @@ -3826,10 +4185,10 @@ * First-level interrupt handler. * * For versions > v1.3.70, 'dev_id' is a pointer to the interrupting - * adapter's struct asc_board. Because all boards are currently checked + * adapter's asc_board_t. Because all boards are currently checked * for interrupts on each interrupt, 'dev_id' is not referenced. 'dev_id' - * could be used to identify an interrupt passed to the AdvanSys driver - * but actually for a device sharing an interrupt with an AdvanSys adapter. + * could be used to identify an interrupt passed to the AdvanSys driver, + * which is for a device sharing an interrupt with an AdvanSys adapter. */ STATIC void #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,70) @@ -3838,12 +4197,13 @@ advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs) #endif /* version >= v1.3.70 */ { - int i; - int flags; - Scsi_Cmnd *scp; - Scsi_Cmnd *tscp; + int flags; + int i; + asc_board_t *boardp; + Scsi_Cmnd *done_scp = NULL, *last_scp = NULL; + Scsi_Cmnd *new_last_scp; - /* Disable interrupts, if the aren't already disabled. */ + /* Disable interrupts, if they aren't already disabled. */ save_flags(flags); cli(); @@ -3854,31 +4214,57 @@ */ for (i = 0; i < asc_board_count; i++) { ASC_STATS(asc_host[i], check_interrupt); + boardp = ASC_BOARDP(asc_host[i]); while (AscIsIntPending(asc_host[i]->io_port)) { ASC_STATS(asc_host[i], interrupt); ASC_DBG(1, "advansys_interrupt: before AscISR()\n"); - AscISR(&ASC_BOARDP(asc_host[i])->asc_dvc_var); + AscISR(&boardp->asc_dvc_var); + } + + /* + * Start waiting requests and create a list of completed requests. + * + * If a reset or abort request is being performed for the board, + * the reset or abort handler will complete pending requests after + * it has completed. + */ + if ((boardp->flags & (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) == 0) { + /* Start any waiting commands for the board. */ + if (!ASC_QUEUE_EMPTY(&boardp->waiting)) { + ASC_DBG(1, "advansys_interrupt: before asc_execute_queue()\n"); + asc_execute_queue(&boardp->waiting); + } + + /* + * Add to the list of requests that must be completed. + */ + if (done_scp == NULL) { + done_scp = asc_dequeue_list(&boardp->scsi_done_q, &last_scp, + ASC_TID_ALL); + } else { + ASC_ASSERT(last_scp != NULL); + REQPNEXT(last_scp) = asc_dequeue_list(&boardp->scsi_done_q, + &new_last_scp, ASC_TID_ALL); + if (new_last_scp != NULL) { + ASC_ASSERT(REQPNEXT(last_scp) != NULL); + last_scp = new_last_scp; + } + } } } + /* Interrupts could be enabled here. */ + /* - * While interrupts are still disabled save the list of requests that - * need their done function called. After re-enabling interrupts call - * the done function which may re-enable interrupts anyway. + * It is possible for the request done function to re-enable + * interrupts without confusing the driver. But here interrupts + * aren't enabled until all requests have been completed. */ - if ((scp = asc_scsi_done) != NULL) { - asc_scsi_done = NULL; - } + asc_scsi_done_list(done_scp); /* Re-enable interrupts, if they were enabled on entry. */ restore_flags(flags); - while (scp) { - tscp = (Scsi_Cmnd *) scp->host_scribble; - scp->scsi_done(scp); - scp = tscp; - } - ASC_DBG(1, "advansys_interrupt: end\n"); return; } @@ -3891,8 +4277,8 @@ STATIC void advansys_select_queue_depths(struct Scsi_Host *shp, Scsi_Device *devicelist) { - Scsi_Device *device; - struct asc_board *boardp; + Scsi_Device *device; + asc_board_t *boardp; boardp = ASC_BOARDP(shp); for (device = devicelist; device != NULL; device = device->next) { @@ -3918,6 +4304,27 @@ } /* + * Complete all requests on the singly linked list pointed + * to by 'scp'. + * + * Interrupts can be enabled on entry. + */ +STATIC void +asc_scsi_done_list(Scsi_Cmnd *scp) +{ + Scsi_Cmnd *tscp; + + while (scp != NULL) { + tscp = REQPNEXT(scp); + REQPNEXT(scp) = NULL; + ASC_STATS(scp->host, done); + scp->scsi_done(scp); + scp = tscp; + } + return; +} + +/* * Execute a single 'Scsi_Cmnd'. * * The function 'done' is called when the request has been completed. @@ -3952,20 +4359,21 @@ * scsi_done - used to save caller's done function * host_scribble - used for pointer to another Scsi_Cmnd * - * If this function returns ASC_NOERROR or ASC_ERROR the done - * function has been called. If ASC_BUSY is returned the request - * must be enqueued by the caller and re-tried later. + * If this function returns ASC_NOERROR or ASC_ERROR the request + * has been enqueued on the board's 'scsi_done_q' and must be + * completed by the caller. + * + * If ASC_BUSY is returned the request must be enqueued by the + * caller and re-tried later. */ STATIC int asc_execute_scsi_cmnd(Scsi_Cmnd *scp) { - struct asc_board *boardp; - ASC_DVC_VAR *asc_dvc_varp; - ASC_SCSI_Q scsiq; - ASC_SG_HEAD sghead; - int flags; - int ret; + asc_board_t *boardp; + ASC_DVC_VAR *asc_dvc_varp; + int ret; + ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_DBG2(1, "asc_execute_scsi_cmnd: scp %x, done %x\n", (unsigned) scp, (unsigned) scp->scsi_done); @@ -3979,30 +4387,34 @@ if ((boardp->init_tidmask & ASC_TIX_TO_TARGET_ID(scp->target)) == 0) { if (asc_init_dev(asc_dvc_varp, scp) == ASC_FALSE) { scp->result = HOST_BYTE(DID_BAD_TARGET); - scp->scsi_done(scp); + asc_enqueue(&boardp->scsi_done_q, scp, ASC_BACK); return ASC_ERROR; } boardp->init_tidmask |= ASC_TIX_TO_TARGET_ID(scp->target); } - memset(&scsiq, 0, sizeof(ASC_SCSI_Q)); + /* + * Mutually exclusive access is required to 'asc_scsi_q' and + * 'asc_sg_head' until after the request is started. + */ + memset(&asc_scsi_q, 0, sizeof(ASC_SCSI_Q)); /* * Point the ASC_SCSI_Q to the 'Scsi_Cmnd'. */ - scsiq.q2.srb_ptr = (ulong) scp; + asc_scsi_q.q2.srb_ptr = (ulong) scp; /* * Build the ASC_SCSI_Q request. */ - scsiq.cdbptr = &scp->cmnd[0]; - scsiq.q2.cdb_len = scp->cmd_len; - scsiq.q1.target_id = ASC_TID_TO_TARGET_ID(scp->target); - scsiq.q1.target_lun = scp->lun; - scsiq.q2.target_ix = ASC_TIDLUN_TO_IX(scp->target, scp->lun); - scsiq.q1.sense_addr = (ulong) &scp->sense_buffer[0]; - scsiq.q1.sense_len = sizeof(scp->sense_buffer); - scsiq.q2.tag_code = M2_QTAG_MSG_SIMPLE; + asc_scsi_q.cdbptr = &scp->cmnd[0]; + asc_scsi_q.q2.cdb_len = scp->cmd_len; + asc_scsi_q.q1.target_id = ASC_TID_TO_TARGET_ID(scp->target); + asc_scsi_q.q1.target_lun = scp->lun; + asc_scsi_q.q2.target_ix = ASC_TIDLUN_TO_IX(scp->target, scp->lun); + asc_scsi_q.q1.sense_addr = (ulong) &scp->sense_buffer[0]; + asc_scsi_q.q1.sense_len = sizeof(scp->sense_buffer); + asc_scsi_q.q2.tag_code = M2_QTAG_MSG_SIMPLE; /* * Build ASC_SCSI_Q for a contiguous buffer or a scatter-gather @@ -4014,12 +4426,12 @@ */ ASC_STATS(scp->host, cont_cnt); /* request_buffer is already a real address. */ - scsiq.q1.data_addr = (ulong) scp->request_buffer; - scsiq.q1.data_cnt = scp->request_bufflen; + asc_scsi_q.q1.data_addr = (ulong) scp->request_buffer; + asc_scsi_q.q1.data_cnt = scp->request_bufflen; ASC_STATS_ADD(scp->host, cont_xfer, ASC_CEILING(scp->request_bufflen, 512)); - scsiq.q1.sg_queue_cnt = 0; - scsiq.sg_head = NULL; + asc_scsi_q.q1.sg_queue_cnt = 0; + asc_scsi_q.sg_head = NULL; } else { /* * CDB scatter-gather request list. @@ -4027,11 +4439,12 @@ int sgcnt; struct scatterlist *slp; - if (scp->use_sg > ASC_MAX_SG_LIST) { - ASC_PRINT3("asc_execute_scsi_cmnd: Board %d: use_sg %d > %d\n", - boardp->id, scp->use_sg, ASC_MAX_SG_LIST); + if (scp->use_sg > scp->host->sg_tablesize) { + ASC_PRINT3( +"asc_execute_scsi_cmnd: board %d: use_sg %d > sg_tablesize %d\n", + boardp->id, scp->use_sg, scp->host->sg_tablesize); scp->result = HOST_BYTE(DID_ERROR); - scp->scsi_done(scp); + asc_enqueue(&boardp->scsi_done_q, scp, ASC_BACK); return ASC_ERROR; } @@ -4041,40 +4454,37 @@ * Allocate a ASC_SG_HEAD structure and set the ASC_SCSI_Q * to point to it. */ - memset(&sghead, 0, sizeof(ASC_SG_HEAD)); + memset(&asc_sg_head, 0, sizeof(ASC_SG_HEAD)); - scsiq.q1.cntl |= QC_SG_HEAD; - scsiq.sg_head = &sghead; - scsiq.q1.data_cnt = 0; - scsiq.q1.data_addr = 0; - sghead.entry_cnt = scsiq.q1.sg_queue_cnt = scp->use_sg; - ASC_STATS_ADD(scp->host, sg_elem, sghead.entry_cnt); + asc_scsi_q.q1.cntl |= QC_SG_HEAD; + asc_scsi_q.sg_head = &asc_sg_head; + asc_scsi_q.q1.data_cnt = 0; + asc_scsi_q.q1.data_addr = 0; + asc_sg_head.entry_cnt = asc_scsi_q.q1.sg_queue_cnt = scp->use_sg; + ASC_STATS_ADD(scp->host, sg_elem, asc_sg_head.entry_cnt); /* * Convert scatter-gather list into ASC_SG_HEAD list. */ slp = (struct scatterlist *) scp->request_buffer; for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) { - sghead.sg_list[sgcnt].addr = (ulong) slp->address; - sghead.sg_list[sgcnt].bytes = slp->length; + asc_sg_head.sg_list[sgcnt].addr = (ulong) slp->address; + asc_sg_head.sg_list[sgcnt].bytes = slp->length; ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512)); } } - ASC_DBG_PRT_SCSI_Q(2, &scsiq); + ASC_DBG_PRT_SCSI_Q(2, &asc_scsi_q); ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len); /* - * Disable interrupts to issue the command and add the - * command to the active queue if it is started. + * Execute the command. If there is no error, add the command + * to the active queue. */ - save_flags(flags); - cli(); - - switch (ret = AscExeScsiQueue(asc_dvc_varp, &scsiq)) { + switch (ret = AscExeScsiQueue(asc_dvc_varp, &asc_scsi_q)) { case ASC_NOERROR: - asc_enqueue(&boardp->active, scp, ASC_BACK); ASC_STATS(scp->host, asc_noerror); + asc_enqueue(&boardp->active, scp, ASC_BACK); ASC_DBG(1, "asc_execute_scsi_cmnd: AscExeScsiQueue(), ASC_NOERROR\n"); break; case ASC_BUSY: @@ -4083,24 +4493,24 @@ break; case ASC_ERROR: ASC_PRINT2( -"asc_execute_scsi_cmnd: Board %d: AscExeScsiQueue() ASC_ERROR, err_code %x\n", +"asc_execute_scsi_cmnd: board %d: AscExeScsiQueue() ASC_ERROR, err_code %x\n", boardp->id, asc_dvc_varp->err_code); ASC_STATS(scp->host, asc_error); scp->result = HOST_BYTE(DID_ERROR); - scp->scsi_done(scp); + asc_enqueue(&boardp->scsi_done_q, scp, ASC_BACK); break; default: ASC_PRINT2( -"asc_execute_scsi_cmnd: Board %d: AscExeScsiQueue() unknown, err_code %x\n", +"asc_execute_scsi_cmnd: board %d: AscExeScsiQueue() unknown, err_code %x\n", boardp->id, asc_dvc_varp->err_code); ASC_STATS(scp->host, asc_unknown); scp->result = HOST_BYTE(DID_ERROR); - scp->scsi_done(scp); + asc_enqueue(&boardp->scsi_done_q, scp, ASC_BACK); break; } - restore_flags(flags); ASC_DBG(1, "asc_execute_scsi_cmnd: end\n"); + ASC_ASSERT(interrupts_enabled() == ASC_FALSE); return ret; } @@ -4110,10 +4520,10 @@ void asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) { - struct asc_board *boardp; + asc_board_t *boardp; Scsi_Cmnd *scp; struct Scsi_Host *shp; - Scsi_Cmnd **scpp; + int i; ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_DBG2(1, "asc_isr_callback: asc_dvc_varp %x, qdonep %x\n", @@ -4128,16 +4538,40 @@ ASC_DBG1(1, "asc_isr_callback: scp %x\n", (unsigned) scp); ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len); + if (scp == NULL) { + ASC_PRINT("asc_isr_callback: scp is NULL\n"); + return; + } + + /* + * If the request's host pointer is not valid, display a + * message and return. + */ shp = scp->host; - ASC_ASSERT(shp); + for (i = 0; i < asc_board_count; i++) { + if (asc_host[i] == shp) { + break; + } + } + if (i == asc_board_count) { + ASC_PRINT2("asc_isr_callback: scp %x has bad host pointer, host %x\n", + (unsigned) scp, (unsigned) shp); + return; + } + ASC_STATS(shp, callback); ASC_DBG1(1, "asc_isr_callback: shp %x\n", (unsigned) shp); + /* + * If the request isn't found on the active queue, it may + * have been removed to handle a reset or abort request. + * Display a message and return. + */ boardp = ASC_BOARDP(shp); if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) { - ASC_PRINT2( -"asc_isr_callback: Board %d: scp %x not on active queue\n", + ASC_PRINT2("asc_isr_callback: board %d: scp %x not on active queue\n", boardp->id, (unsigned) scp); + return; } /* @@ -4205,33 +4639,14 @@ break; } - /* - * Before calling 'scsi_done' for the current 'Scsi_Cmnd' and possibly - * triggering more commands to be issued, try to start any pending - * commands. - */ - if (boardp->pending.tidmask != 0) { - /* - * If there are any pending commands for this board before trying - * to execute them, disable interrupts to preserve request ordering. - */ - ASC_ASSERT(interrupts_enabled() == ASC_FALSE); - ASC_DBG1(1, "asc_isr_callback: asc_execute_queue() %x\n", - boardp->pending.tidmask); - asc_execute_queue(&boardp->pending); - } - /* - * Because interrupts may be enabled by the 'Scsi_Cmnd' done function, - * add the command to the end of the global done list. The done function - * for the command will be called in advansys_interrupt(). + * Because interrupts may be enabled by the 'Scsi_Cmnd' done + * function, add the command to the end of the board's done queue. + * The done function for the command will be called from + * advansys_interrupt(). */ - for (scpp = &asc_scsi_done; *scpp; - scpp = (Scsi_Cmnd **) &(*scpp)->host_scribble) { - ; - } - *scpp = scp; - scp->host_scribble = NULL; + asc_enqueue(&boardp->scsi_done_q, scp, ASC_BACK); + return; } @@ -4243,10 +4658,7 @@ STATIC int asc_init_dev(ASC_DVC_VAR *asc_dvc_varp, Scsi_Cmnd *scp) { - struct asc_board *boardp; - ASC_SCSI_REQ_Q *scsireqq; - ASC_CAP_INFO *cap_info; - ASC_SCSI_INQUIRY *inquiry; + asc_board_t *boardp; int found; ASC_SCSI_BIT_ID_TYPE save_use_tagged_qng; ASC_SCSI_BIT_ID_TYPE save_can_tagged_qng; @@ -4257,26 +4669,15 @@ ASC_DBG1(1, "asc_init_dev: target %d\n", (unsigned) scp->target); - /* The hosts's target id is set in init_tidmask during initialization. */ + /* The host's target id is set in init_tidmask during initialization. */ ASC_ASSERT(asc_dvc_varp->cfg->chip_scsi_id != scp->target); boardp = ASC_BOARDP(scp->host); - /* - * XXX - Host drivers should not modify the timeout field. - * But on the first command only add some extra time to - * allow the driver to complete its initialization for the - * device. - */ - scp->timeout += 2000; /* Add 5 seconds to the request timeout. */ - /* Set-up AscInitPollTarget() arguments. */ - scsireqq = &boardp->scsireqq; - memset(scsireqq, 0, sizeof(ASC_SCSI_REQ_Q)); - cap_info = &boardp->cap_info; - memset(cap_info, 0, sizeof(ASC_CAP_INFO)); - inquiry = &boardp->inquiry; - memset(inquiry, 0, sizeof(ASC_SCSI_INQUIRY)); + memset(&asc_scsireqq, 0, sizeof(ASC_SCSI_REQ_Q)); + memset(&asc_cap_info, 0, sizeof(ASC_CAP_INFO)); + memset(&asc_inquiry, 0, sizeof(ASC_SCSI_INQUIRY)); /* * XXX - AscInitPollBegin() re-initializes these fields to @@ -4289,28 +4690,29 @@ ASC_DBG(2, "asc_init_dev: AscInitPollBegin()\n"); if (AscInitPollBegin(asc_dvc_varp)) { - ASC_PRINT1("asc_init_dev: Board %d: AscInitPollBegin() failed\n", + ASC_PRINT1("asc_init_dev: board %d: AscInitPollBegin() failed\n", boardp->id); return ASC_FALSE; } - scsireqq->sense_ptr = &scsireqq->sense[0]; - scsireqq->r1.sense_len = ASC_MIN_SENSE_LEN; - scsireqq->r1.target_id = ASC_TID_TO_TARGET_ID(scp->target); - scsireqq->r1.target_lun = 0; - scsireqq->r2.target_ix = ASC_TIDLUN_TO_IX(scp->target, 0); + asc_scsireqq.sense_ptr = &asc_scsireqq.sense[0]; + asc_scsireqq.r1.sense_len = ASC_MIN_SENSE_LEN; + asc_scsireqq.r1.target_id = ASC_TID_TO_TARGET_ID(scp->target); + asc_scsireqq.r1.target_lun = 0; + asc_scsireqq.r2.target_ix = ASC_TIDLUN_TO_IX(scp->target, 0); found = ASC_FALSE; ASC_DBG(2, "asc_init_dev: AscInitPollTarget()\n"); - switch (ret = AscInitPollTarget(asc_dvc_varp, scsireqq, inquiry, cap_info)) { + switch (ret = AscInitPollTarget(asc_dvc_varp, &asc_scsireqq, + &asc_inquiry, &asc_cap_info)) { case ASC_TRUE: found = ASC_TRUE; #ifdef ADVANSYS_DEBUG tidmask = ASC_TIX_TO_TARGET_ID(scp->target); ASC_DBG2(1, "asc_init_dev: lba %lu, blk_size %lu\n", - cap_info->lba, cap_info->blk_size); + asc_cap_info.lba, asc_cap_info.blk_size); ASC_DBG1(1, "asc_init_dev: peri_dvc_type %x\n", - inquiry->byte0.peri_dvc_type); + asc_inquiry.byte0.peri_dvc_type); if (asc_dvc_varp->use_tagged_qng & tidmask) { ASC_DBG1(1, "asc_init_dev: command queuing enabled: %d\n", asc_dvc_varp->max_dvc_qng[scp->target]); @@ -4334,12 +4736,12 @@ ASC_DBG(1, "asc_init_dev: no device found\n"); break; case ASC_ERROR: - ASC_PRINT1("asc_init_dev: Board %d: AscInitPollTarget() ASC_ERROR\n", + ASC_PRINT1("asc_init_dev: board %d: AscInitPollTarget() ASC_ERROR\n", boardp->id); break; default: ASC_PRINT2( -"asc_init_dev: Board %d: AscInitPollTarget() unknown ret %d\n", +"asc_init_dev: board %d: AscInitPollTarget() unknown ret %d\n", boardp->id, ret); break; } @@ -4351,6 +4753,8 @@ ASC_DBG(2, "asc_init_dev: AscInitPollEnd()\n"); AscInitPollEnd(asc_dvc_varp); + ASC_DBG1(1, "asc_init_dev: found %d\n", found); + return found; } @@ -4753,36 +5157,42 @@ void asc_enqueue(asc_queue_t *ascq, REQP reqp, int flag) { - REQP *reqpp; int tid; - ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_DBG3(2, "asc_enqueue: ascq %x, reqp %x, flag %d\n", (unsigned) ascq, (unsigned) reqp, flag); - tid = REQPTID(reqp); + ASC_ASSERT(interrupts_enabled() == ASC_FALSE); + ASC_ASSERT(reqp != NULL); ASC_ASSERT(flag == ASC_FRONT || flag == ASC_BACK); + tid = REQPTID(reqp); + ASC_ASSERT(tid >= 0 && tid <= ASC_MAX_TID); if (flag == ASC_FRONT) { - REQPNEXT(reqp) = ascq->queue[tid]; - ascq->queue[tid] = reqp; + REQPNEXT(reqp) = ascq->q_first[tid]; + ascq->q_first[tid] = reqp; + /* If the queue was empty, set the last pointer. */ + if (ascq->q_last[tid] == NULL) { + ascq->q_last[tid] = reqp; + } } else { /* ASC_BACK */ - for (reqpp = &ascq->queue[tid]; *reqpp; reqpp = REQPNEXTP(*reqpp)) { - ASC_ASSERT(ascq->tidmask & ASC_TIX_TO_TARGET_ID(tid)); - ; + if (ascq->q_last[tid] != NULL) { + REQPNEXT(ascq->q_last[tid]) = reqp; } - *reqpp = reqp; + ascq->q_last[tid] = reqp; REQPNEXT(reqp) = NULL; + /* If the queue was empty, set the first pointer. */ + if (ascq->q_first[tid] == NULL) { + ascq->q_first[tid] = reqp; + } } /* The queue has at least one entry, set its bit. */ - ascq->tidmask |= ASC_TIX_TO_TARGET_ID(tid); + ascq->q_tidmask |= ASC_TIX_TO_TARGET_ID(tid); #ifdef ADVANSYS_STATS - /* - * Maintain request queue statistics. - */ - ascq->cur_count[tid]++; - if (ascq->cur_count[tid] > ascq->max_count[tid]) { - ascq->max_count[tid] = ascq->cur_count[tid]; - ASC_DBG2(1, "asc_enqueue: new max_count[%d] %d\n", - tid, ascq->max_count[tid]); + /* Maintain request queue statistics. */ + ascq->q_cur_cnt[tid]++; + if (ascq->q_cur_cnt[tid] > ascq->q_max_cnt[tid]) { + ascq->q_max_cnt[tid] = ascq->q_cur_cnt[tid]; + ASC_DBG2(1, "asc_enqueue: new q_max_cnt[%d] %d\n", + tid, ascq->q_max_cnt[tid]); } #endif /* ADVANSYS_STATS */ ASC_DBG1(1, "asc_enqueue: reqp %x\n", (unsigned) reqp); @@ -4801,30 +5211,103 @@ { REQP reqp; - ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_DBG2(1, "asc_dequeue: ascq %x, tid %d\n", (unsigned) ascq, tid); - if ((reqp = ascq->queue[tid]) != NULL) { - ASC_ASSERT(ascq->tidmask & ASC_TIX_TO_TARGET_ID(tid)); - ascq->queue[tid] = REQPNEXT(reqp); - /* If the queue is empty, clear its bit. */ - if (ascq->queue[tid] == NULL) { - ascq->tidmask &= ~ASC_TIX_TO_TARGET_ID(tid); + ASC_ASSERT(interrupts_enabled() == ASC_FALSE); + ASC_ASSERT(tid >= 0 && tid <= ASC_MAX_TID); + if ((reqp = ascq->q_first[tid]) != NULL) { + ASC_ASSERT(ascq->q_tidmask & ASC_TIX_TO_TARGET_ID(tid)); + ascq->q_first[tid] = REQPNEXT(reqp); + /* If the queue is empty, clear its bit and the last pointer. */ + if (ascq->q_first[tid] == NULL) { + ascq->q_tidmask &= ~ASC_TIX_TO_TARGET_ID(tid); + ASC_ASSERT(ascq->q_last[tid] == reqp); + ascq->q_last[tid] = NULL; } - } #ifdef ADVANSYS_STATS - /* - * Maintain request queue statistics. - */ - if (reqp != NULL) { - ascq->cur_count[tid]--; - } - ASC_ASSERT(ascq->cur_count[tid] >= 0); + /* Maintain request queue statistics. */ + ascq->q_cur_cnt[tid]--; + ASC_ASSERT(ascq->q_cur_cnt[tid] >= 0); #endif /* ADVANSYS_STATS */ + } ASC_DBG1(1, "asc_dequeue: reqp %x\n", (unsigned) reqp); return reqp; } /* + * Return a pointer to a singly linked list of all the requests queued + * for 'tid' on the 'asc_queue_t' pointed to by 'ascq'. + * + * If 'lastpp' is not NULL, '*lastpp' will be set to point to the + * the last request returned in the singly linked list. + * + * 'tid' should either be a valid target id or if it is ASC_TID_ALL, + * then all queued requests are concatenated into one list and + * returned. + * + * Note: If 'lastpp' is used to append a new list to the end of + * an old list, only change the old list last pointer if '*lastpp' + * (or the function return value) is not NULL, i.e. use a temporary + * variable for 'lastpp' and check its value after the function return + * before assigning it to the list last pointer. + */ +REQP +asc_dequeue_list(asc_queue_t *ascq, REQP *lastpp, int tid) +{ + REQP firstp, lastp; + int i; + + ASC_DBG2(1, "asc_dequeue_list: ascq %x, tid %d\n", (unsigned) ascq, tid); + ASC_ASSERT(interrupts_enabled() == ASC_FALSE); + ASC_ASSERT((tid == ASC_TID_ALL) || (tid >= 0 && tid <= ASC_MAX_TID)); + + /* + * If 'tid' is not ASC_TID_ALL, return requests only for + * the specified 'tid'. If 'tid' is ASC_TID_ALL, return all + * requests for all tids. + */ + if (tid != ASC_TID_ALL) { + /* Return all requests for the specified 'tid'. */ + if ((ascq->q_tidmask & ASC_TIX_TO_TARGET_ID(tid)) == 0) { + /* List is empty set first and last return pointers to NULL. */ + firstp = lastp = NULL; + } else { + firstp = ascq->q_first[tid]; + lastp = ascq->q_last[tid]; + ascq->q_first[tid] = ascq->q_last[tid] = NULL; + ascq->q_tidmask &= ~ASC_TIX_TO_TARGET_ID(tid); +#ifdef ADVANSYS_STATS + ascq->q_cur_cnt[tid] = 0; +#endif /* ADVANSYS_STATS */ + } + } else { + /* Return all requests for all tids. */ + firstp = lastp = NULL; + for (i = 0; i <= ASC_MAX_TID; i++) { + if (ascq->q_tidmask & ASC_TIX_TO_TARGET_ID(i)) { + if (firstp == NULL) { + firstp = ascq->q_first[i]; + lastp = ascq->q_last[i]; + } else { + ASC_ASSERT(lastp != NULL); + REQPNEXT(lastp) = ascq->q_first[i]; + lastp = ascq->q_last[i]; + } + ascq->q_first[i] = ascq->q_last[i] = NULL; + ascq->q_tidmask &= ~ASC_TIX_TO_TARGET_ID(i); +#ifdef ADVANSYS_STATS + ascq->q_cur_cnt[i] = 0; +#endif /* ADVANSYS_STATS */ + } + } + } + if (lastpp) { + *lastpp = lastp; + } + ASC_DBG1(1, "asc_dequeue_list: firstp %x\n", (unsigned) firstp); + return firstp; +} + +/* * Remove the specified 'REQP' from the specified queue for * the specified target device. Clear the 'tidmask' bit for the * device if no more commands are left queued for it. @@ -4837,34 +5320,61 @@ int asc_rmqueue(asc_queue_t *ascq, REQP reqp) { - REQP *reqpp; + REQP currp, prevp; int tid; - int ret; + int ret = ASC_FALSE; + ASC_DBG2(1, "asc_rmqueue: ascq %x, reqp %d\n", + (unsigned) ascq, (unsigned) reqp); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); - ret = ASC_FALSE; + ASC_ASSERT(reqp != NULL); + tid = REQPTID(reqp); - for (reqpp = &ascq->queue[tid]; *reqpp; reqpp = REQPNEXTP(*reqpp)) { - ASC_ASSERT(ascq->tidmask & ASC_TIX_TO_TARGET_ID(tid)); - if (*reqpp == reqp) { - ret = ASC_TRUE; - *reqpp = REQPNEXT(reqp); - REQPNEXT(reqp) = NULL; - /* If the queue is now empty, clear its bit. */ - if (ascq->queue[tid] == NULL) { - ascq->tidmask &= ~ASC_TIX_TO_TARGET_ID(tid); + ASC_ASSERT(tid >= 0 && tid <= ASC_MAX_TID); + + /* + * Handle the common case of 'reqp' being the first + * entry on the queue. + */ + if (reqp == ascq->q_first[tid]) { + ret = ASC_TRUE; + ascq->q_first[tid] = REQPNEXT(reqp); + /* If the queue is now empty, clear its bit and the last pointer. */ + if (ascq->q_first[tid] == NULL) { + ascq->q_tidmask &= ~ASC_TIX_TO_TARGET_ID(tid); + ASC_ASSERT(ascq->q_last[tid] == reqp); + ascq->q_last[tid] = NULL; + } + } else if (ascq->q_first[tid] != NULL) { + ASC_ASSERT(ascq->q_last[tid] != NULL); + /* + * Because the case of 'reqp' being the first entry has been + * handled above and it is known the queue is not empty, if + * 'reqp' is found on the queue it is guaranteed the queue will + * not become empty and that 'q_first[tid]' will not be changed. + * + * Set 'prevp' to the first entry, 'currp' to the second entry, + * and search for 'reqp'. + */ + for (prevp = ascq->q_first[tid], currp = REQPNEXT(prevp); + currp; prevp = currp, currp = REQPNEXT(currp)) { + if (currp == reqp) { + ret = ASC_TRUE; + REQPNEXT(prevp) = REQPNEXT(currp); + REQPNEXT(reqp) = NULL; + if (ascq->q_last[tid] == reqp) { + ascq->q_last[tid] = prevp; + } + break; } - break; /* Note: *reqpp may now be NULL, don't iterate. */ } } #ifdef ADVANSYS_STATS - /* - * Maintain request queue statistics. - */ + /* Maintain request queue statistics. */ if (ret == ASC_TRUE) { - ascq->cur_count[tid]--; + ascq->q_cur_cnt[tid]--; } - ASC_ASSERT(ascq->cur_count[tid] >= 0); + ASC_ASSERT(ascq->q_cur_cnt[tid] >= 0); #endif /* ADVANSYS_STATS */ ASC_DBG2(1, "asc_rmqueue: reqp %x, ret %d\n", (unsigned) reqp, ret); return ret; @@ -4877,16 +5387,21 @@ int asc_isqueued(asc_queue_t *ascq, REQP reqp) { - REQP *reqpp; + REQP treqp; int tid; - int ret; + int ret = ASC_FALSE; + ASC_DBG2(1, "asc_isqueued: ascq %x, reqp %x\n", + (unsigned) ascq, (unsigned) reqp); ASC_ASSERT(interrupts_enabled() == ASC_FALSE); - ret = ASC_FALSE; + ASC_ASSERT(reqp != NULL); + tid = REQPTID(reqp); - for (reqpp = &ascq->queue[tid]; *reqpp; reqpp = REQPNEXTP(*reqpp)) { - ASC_ASSERT(ascq->tidmask & ASC_TIX_TO_TARGET_ID(tid)); - if (*reqpp == reqp) { + ASC_ASSERT(tid >= 0 && tid <= ASC_MAX_TID); + + for (treqp = ascq->q_first[tid]; treqp; treqp = REQPNEXT(treqp)) { + ASC_ASSERT(ascq->q_tidmask & ASC_TIX_TO_TARGET_ID(tid)); + if (treqp == reqp) { ret = ASC_TRUE; break; } @@ -4906,13 +5421,13 @@ REQP reqp; int i; - ASC_ASSERT(interrupts_enabled() == ASC_FALSE); ASC_DBG1(1, "asc_execute_queue: ascq %x\n", (unsigned) ascq); + ASC_ASSERT(interrupts_enabled() == ASC_FALSE); /* * Execute queued commands for devices attached to * the current board in round-robin fashion. */ - scan_tidmask = ascq->tidmask; + scan_tidmask = ascq->q_tidmask; do { for (i = 0; i <= ASC_MAX_TID; i++) { if (scan_tidmask & ASC_TIX_TO_TARGET_ID(i)) { @@ -4944,11 +5459,11 @@ STATIC int asc_prt_board_devices(struct Scsi_Host *shp, char *cp, int cplen) { - struct asc_board *boardp; - int leftlen; - int totlen; - int len; - int i; + asc_board_t *boardp; + int leftlen; + int totlen; + int len; + int i; boardp = ASC_BOARDP(shp); leftlen = cplen; @@ -4989,14 +5504,14 @@ STATIC int asc_prt_board_eeprom(struct Scsi_Host *shp, char *cp, int cplen) { - struct asc_board *boardp; - ASC_DVC_VAR *asc_dvc_varp; - int leftlen; - int totlen; - int len; - ASCEEP_CONFIG *ep; - int i; - int isa_dma_speed[] = { 10, 8, 7, 6, 5, 4, 3, 2 }; + asc_board_t *boardp; + ASC_DVC_VAR *asc_dvc_varp; + int leftlen; + int totlen; + int len; + ASCEEP_CONFIG *ep; + int i; + int isa_dma_speed[] = { 10, 8, 7, 6, 5, 4, 3, 2 }; boardp = ASC_BOARDP(shp); asc_dvc_varp = &boardp->asc_dvc_var; @@ -5069,6 +5584,70 @@ } /* + * asc_prt_driver_conf() + * + * Note: no single line should be greater than ASC_PRTLINE_SIZE, + * cf. asc_prt_line(). + * + * Return the number of characters copied into 'cp'. No more than + * 'cplen' characters will be copied to 'cp'. + */ +STATIC int +asc_prt_driver_conf(struct Scsi_Host *shp, char *cp, int cplen) +{ + int leftlen; + int totlen; + int len; + + leftlen = cplen; + totlen = len = 0; + + len = asc_prt_line(cp, leftlen, +"\nLinux Driver Configuration and Information for AdvanSys SCSI Host %d:\n", + shp->host_no); + ASC_PRT_NEXT(); + + len = asc_prt_line(cp, leftlen, +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89) +" host_busy %u, last_reset %u, max_id %u, max_lun %u, max_channel %u\n", + shp->host_busy, shp->last_reset, shp->max_id, shp->max_lun, + shp->max_channel); +#else /* version >= v1.3.89 */ +" host_busy %u, last_reset %u, max_id %u, max_lun %u\n", + shp->host_busy, shp->last_reset, shp->max_id, shp->max_lun); +#endif /* version >= v1.3.89 */ + ASC_PRT_NEXT(); + + len = asc_prt_line(cp, leftlen, +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,57) +" unique_id %d, can_queue %d, this_id %d, sg_tablesize %u, cmd_per_lun %u\n", + shp->unique_id, shp->can_queue, shp->this_id, shp->sg_tablesize, + shp->cmd_per_lun); +#else /* version >= v1.3.57 */ +" can_queue %d, this_id %d, sg_tablesize %u, cmd_per_lun %u\n", + shp->can_queue, shp->this_id, shp->sg_tablesize, shp->cmd_per_lun); +#endif /* version >= v1.3.57 */ + ASC_PRT_NEXT(); + + len = asc_prt_line(cp, leftlen, +#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,57) +" unchecked_isa_dma %d, use_clustering %d, loaded_as_module %d\n", + shp->unchecked_isa_dma, shp->use_clustering, shp->loaded_as_module); +#else /* version >= v1.3.57 */ +" unchecked_isa_dma %d, loaded_as_module %d\n", + shp->unchecked_isa_dma, shp->loaded_as_module); +#endif /* version >= v1.3.57 */ + ASC_PRT_NEXT(); + + len = asc_prt_line(cp, leftlen, +" flags %x, reset_jiffies %x, jiffies %x\n", + ASC_BOARDP(shp)->flags, ASC_BOARDP(shp)->reset_jiffies, jiffies); + ASC_PRT_NEXT(); + + return totlen; +} + +/* * asc_prt_board_info() * * Print dynamic board configuration information. @@ -5082,7 +5661,7 @@ STATIC int asc_prt_board_info(struct Scsi_Host *shp, char *cp, int cplen) { - struct asc_board *boardp; + asc_board_t *boardp; int leftlen; int totlen; int len; @@ -5106,7 +5685,7 @@ ASC_PRT_NEXT(); len = asc_prt_line(cp, leftlen, -" chip_version %u, lib_version %u, lib_serial_no %u mcode_date %u\n", +" chip_version %u, lib_version %u, lib_serial_no %u, mcode_date %u\n", c->chip_version, c->lib_version, c->lib_serial_no, c->mcode_date); ASC_PRT_NEXT(); @@ -5115,7 +5694,7 @@ c->mcode_version, v->err_code); ASC_PRT_NEXT(); - /* Current number of commands pending for the host. */ + /* Current number of commands waiting for the host. */ len = asc_prt_line(cp, leftlen, " Total Command Pending: %d\n", v->cur_total_qng); ASC_PRT_NEXT(); @@ -5150,7 +5729,7 @@ len = asc_prt_line(cp, leftlen, "\n"); ASC_PRT_NEXT(); - /* Current number of commands pending for a device. */ + /* Current number of commands waiting for a device. */ len = asc_prt_line(cp, leftlen, " Command Queue Pending: "); ASC_PRT_NEXT(); @@ -5495,7 +6074,7 @@ } /* - * Return the BIOS address of the adatper at the specified + * Return the BIOS address of the adapter at the specified * I/O port and with the specified bus type. * * This function was formerly supplied by the library. @@ -5565,7 +6144,7 @@ struct asc_stats *s; int i; asc_queue_t *active; - asc_queue_t *pending; + asc_queue_t *waiting; leftlen = cplen; totlen = len = 0; @@ -5581,8 +6160,8 @@ ASC_PRT_NEXT(); len = asc_prt_line(cp, leftlen, -" check_interrupt %lu, interrupt %lu, callback %lu\n", - s->check_interrupt, s->interrupt, s->callback); +" check_interrupt %lu, interrupt %lu, callback %lu, done %lu\n", + s->check_interrupt, s->interrupt, s->callback, s->done); ASC_PRT_NEXT(); len = asc_prt_line(cp, leftlen, @@ -5598,13 +6177,13 @@ ASC_PRT_NEXT(); active = &ASC_BOARDP(shp)->active; - pending = &ASC_BOARDP(shp)->pending; + waiting = &ASC_BOARDP(shp)->waiting; for (i = 0; i < ASC_MAX_TID + 1; i++) { - if (active->max_count[i] > 0 || pending->max_count[i] > 0) { + if (active->q_max_cnt[i] > 0 || waiting->q_max_cnt[i] > 0) { len = asc_prt_line(cp, leftlen, -" target %d: active [cur %d, max %d], pending [cur %d, max %d]\n", - i, active->cur_count[i], active->max_count[i], - pending->cur_count[i], pending->max_count[i]); +" target %d: active [cur %d, max %d], waiting [cur %d, max %d]\n", + i, active->q_cur_cnt[i], active->q_max_cnt[i], + waiting->q_cur_cnt[i], waiting->q_max_cnt[i]); ASC_PRT_NEXT(); } } @@ -6287,10 +6866,11 @@ sdtr_xmsg.req_ack_offset = ASC_SYN_MAX_OFFSET; } if ( - (sdtr_xmsg.xfer_period < asc_dvc->sdtr_period_tbl[0]) + (sdtr_xmsg.xfer_period < asc_dvc->sdtr_period_tbl[asc_dvc->host_init_sdtr_index]) || (sdtr_xmsg.xfer_period > asc_dvc->sdtr_period_tbl[asc_dvc->max_sdtr_index]) ) { sdtr_accept = FALSE; + sdtr_xmsg.xfer_period = asc_dvc->sdtr_period_tbl[ asc_dvc->host_init_sdtr_index ] ; } if (sdtr_accept) { sdtr_data = AscCalSDTRData(asc_dvc, sdtr_xmsg.xfer_period, @@ -6344,7 +6924,7 @@ sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no); q_cntl |= QC_MSG_OUT; AscMsgOutSDTR(asc_dvc, - asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) & (uchar) (ASC_SYN_XFER_NO - 1)], + asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) & (uchar) (asc_dvc->max_sdtr_index - 1)], (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET)); } #endif @@ -6418,8 +6998,8 @@ } #ifdef ADVANSYS_STATS { - struct asc_board *boardp; - int i; + asc_board_t *boardp; + int i; for (i = 0; i < ASC_NUM_BOARD_SUPPORTED; i++) { if (asc_host[i] == NULL) { continue; @@ -6768,156 +7348,156 @@ return (0); } -uchar _mcode_buf[] = -{ - 0x01, 0x03, 0x01, 0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xC4, 0x0C, 0x08, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x23, 0x00, 0x20, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x73, 0x48, 0x04, 0x36, 0x00, 0x00, 0xA2, 0xC6, 0x00, 0x80, 0x73, 0x03, 0x23, 0x36, 0x40, - 0xB6, 0x00, 0x36, 0x00, 0x05, 0xD6, 0x0C, 0xD2, 0x12, 0xDA, 0x00, 0xA2, 0xC6, 0x00, 0x92, 0x80, - 0x20, 0x98, 0x50, 0x00, 0xF5, 0x00, 0x4A, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, - 0x4F, 0x00, 0xF5, 0x00, 0x4A, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, 0x80, 0x62, - 0x92, 0x80, 0x00, 0x62, 0x92, 0x80, 0x00, 0x46, 0x17, 0xEE, 0x13, 0xEA, 0x02, 0x01, 0x09, 0xD8, - 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xDA, 0x00, 0xA8, 0x97, 0x7F, 0x23, 0x04, 0x61, 0x84, 0x01, - 0xD2, 0x84, 0xD0, 0xC1, 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xE6, 0x01, 0xA8, 0x97, - 0xD2, 0x81, 0x00, 0x33, 0x02, 0x00, 0xC2, 0x88, 0x80, 0x73, 0x80, 0x77, 0x00, 0x01, 0x01, 0xA1, - 0x06, 0x01, 0x4F, 0x00, 0x86, 0x97, 0x07, 0xA6, 0x10, 0x01, 0x00, 0x33, 0x03, 0x00, 0xC2, 0x88, - 0x03, 0x03, 0x03, 0xDE, 0x00, 0x33, 0x05, 0x00, 0xC2, 0x88, 0xCE, 0x00, 0x69, 0x60, 0xCE, 0x00, - 0x02, 0x03, 0x4A, 0x60, 0x00, 0xA2, 0x84, 0x01, 0x80, 0x63, 0x07, 0xA6, 0x30, 0x01, 0x84, 0x81, - 0x03, 0x03, 0x80, 0x63, 0xE2, 0x00, 0x07, 0xA6, 0x40, 0x01, 0x00, 0x33, 0x04, 0x00, 0xC2, 0x88, - 0x03, 0x07, 0x02, 0x01, 0x04, 0xCA, 0x0D, 0x23, 0x6A, 0x98, 0x4D, 0x04, 0xF0, 0x84, 0x05, 0xD8, - 0x0D, 0x23, 0x6A, 0x98, 0xCD, 0x04, 0x15, 0x23, 0xF8, 0x88, 0xFB, 0x23, 0x02, 0x61, 0x82, 0x01, - 0x80, 0x63, 0x02, 0x03, 0x06, 0xA3, 0x6E, 0x01, 0x00, 0x33, 0x0A, 0x00, 0xC2, 0x88, 0x4E, 0x00, - 0x07, 0xA3, 0x7A, 0x01, 0x00, 0x33, 0x0B, 0x00, 0xC2, 0x88, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, - 0x1A, 0x00, 0xC2, 0x88, 0x50, 0x04, 0x94, 0x81, 0x06, 0xAB, 0x8E, 0x01, 0x94, 0x81, 0x4E, 0x00, - 0x07, 0xA3, 0x9E, 0x01, 0x50, 0x00, 0x00, 0xA3, 0x48, 0x01, 0x00, 0x05, 0x88, 0x81, 0x48, 0x97, - 0x02, 0x01, 0x05, 0xC6, 0x04, 0x23, 0xA0, 0x01, 0x15, 0x23, 0xA1, 0x01, 0xCA, 0x81, 0xFD, 0x23, - 0x02, 0x61, 0x82, 0x01, 0x0A, 0xDA, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xC0, 0x01, 0x80, 0x63, - 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1B, 0x00, 0xC2, 0x88, 0x06, 0x23, 0x6A, 0x98, 0xCD, 0x04, - 0xD2, 0x84, 0x06, 0x01, 0x00, 0xA2, 0xE0, 0x01, 0x57, 0x60, 0x00, 0xA0, 0xE6, 0x01, 0xD2, 0x84, - 0x80, 0x23, 0xA0, 0x01, 0xD2, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x0E, 0x02, - 0x04, 0x01, 0x0D, 0xDE, 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x86, 0x97, 0x08, 0x82, 0x08, 0x23, - 0x02, 0x41, 0x82, 0x01, 0x4F, 0x00, 0x64, 0x97, 0x48, 0x04, 0xFF, 0x23, 0x84, 0x80, 0xF2, 0x97, - 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0, 0x01, 0x23, 0xE8, 0x00, 0x81, 0x73, 0x06, 0x29, 0x03, 0x42, - 0x06, 0xE2, 0x03, 0xEE, 0x66, 0xEB, 0x11, 0x23, 0xF8, 0x88, 0x06, 0x98, 0xF8, 0x80, 0x80, 0x73, - 0x80, 0x77, 0x06, 0xA6, 0x3C, 0x02, 0x00, 0x33, 0x31, 0x00, 0xC2, 0x88, 0x04, 0x01, 0x03, 0xD8, - 0xB4, 0x98, 0x3E, 0x96, 0x4E, 0x82, 0xCE, 0x95, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0xB6, 0x2D, - 0x02, 0xA6, 0x78, 0x02, 0x07, 0xA6, 0x66, 0x02, 0x06, 0xA6, 0x6A, 0x02, 0x03, 0xA6, 0x6E, 0x02, - 0x00, 0x33, 0x10, 0x00, 0xC2, 0x88, 0x6A, 0x95, 0x50, 0x82, 0x34, 0x96, 0x50, 0x82, 0x04, 0x23, - 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, 0x28, 0x84, 0x04, 0x01, 0x0C, 0xDC, 0xE0, 0x23, 0x25, 0x61, - 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01, 0x03, 0x23, 0xA4, 0x01, - 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, 0x1C, 0x01, 0x02, 0xA6, 0xAA, 0x02, 0x07, 0xA6, 0x66, 0x02, - 0x06, 0xA6, 0x6A, 0x02, 0x00, 0x33, 0x12, 0x00, 0xC2, 0x88, 0x00, 0x0E, 0x80, 0x63, 0x00, 0x43, - 0x00, 0xA0, 0x98, 0x02, 0x4D, 0x04, 0x04, 0x01, 0x0B, 0xDC, 0xE7, 0x23, 0x04, 0x61, 0x84, 0x01, - 0x10, 0x31, 0x12, 0x35, 0x14, 0x01, 0xEC, 0x00, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0xEA, 0x82, - 0x18, 0x23, 0x04, 0x61, 0x18, 0xA0, 0xE2, 0x02, 0x04, 0x01, 0x98, 0xC8, 0x00, 0x33, 0x1F, 0x00, - 0xC2, 0x88, 0x08, 0x31, 0x0A, 0x35, 0x0C, 0x39, 0x0E, 0x3D, 0x80, 0x98, 0xB6, 0x2D, 0x01, 0xA6, - 0x14, 0x03, 0x00, 0xA6, 0x14, 0x03, 0x07, 0xA6, 0x0C, 0x03, 0x06, 0xA6, 0x10, 0x03, 0x03, 0xA6, - 0x0C, 0x04, 0x02, 0xA6, 0x78, 0x02, 0x00, 0x33, 0x33, 0x00, 0xC2, 0x88, 0x6A, 0x95, 0xEE, 0x82, - 0x34, 0x96, 0xEE, 0x82, 0x84, 0x98, 0x80, 0x42, 0x80, 0x98, 0x60, 0xE4, 0x04, 0x01, 0x29, 0xC8, - 0x31, 0x05, 0x07, 0x01, 0x00, 0xA2, 0x54, 0x03, 0x00, 0x43, 0x87, 0x01, 0x05, 0x05, 0x88, 0x98, - 0x80, 0x98, 0x00, 0xA6, 0x16, 0x03, 0x07, 0xA6, 0x4C, 0x03, 0x03, 0xA6, 0x28, 0x04, 0x06, 0xA6, - 0x50, 0x03, 0x01, 0xA6, 0x16, 0x03, 0x00, 0x33, 0x25, 0x00, 0xC2, 0x88, 0x6A, 0x95, 0x32, 0x83, - 0x34, 0x96, 0x32, 0x83, 0x04, 0x01, 0x0C, 0xCE, 0x03, 0xC8, 0x00, 0x33, 0x42, 0x00, 0xC2, 0x88, - 0x00, 0x01, 0x05, 0x05, 0xFF, 0xA2, 0x72, 0x03, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x2E, 0x83, - 0x05, 0x05, 0x15, 0x01, 0x00, 0xA2, 0x92, 0x03, 0xEC, 0x00, 0x6E, 0x00, 0x95, 0x01, 0x6C, 0x38, - 0x00, 0x3F, 0x00, 0x00, 0x01, 0xA6, 0x8E, 0x03, 0x00, 0xA6, 0x8E, 0x03, 0x02, 0x84, 0x80, 0x42, - 0x80, 0x98, 0x01, 0xA6, 0x9C, 0x03, 0x00, 0xA6, 0xB4, 0x03, 0x02, 0x84, 0xA8, 0x98, 0x80, 0x42, - 0x01, 0xA6, 0x9C, 0x03, 0x07, 0xA6, 0xAA, 0x03, 0xCC, 0x83, 0x6A, 0x95, 0xA0, 0x83, 0x00, 0x33, - 0x2F, 0x00, 0xC2, 0x88, 0xA8, 0x98, 0x80, 0x42, 0x00, 0xA6, 0xB4, 0x03, 0x07, 0xA6, 0xC2, 0x03, - 0xCC, 0x83, 0x6A, 0x95, 0xB8, 0x83, 0x00, 0x33, 0x26, 0x00, 0xC2, 0x88, 0x38, 0x2B, 0x80, 0x32, - 0x80, 0x36, 0x04, 0x23, 0xA0, 0x01, 0x12, 0x23, 0xA1, 0x01, 0x02, 0x84, 0x04, 0xF0, 0x80, 0x6B, - 0x00, 0x33, 0x20, 0x00, 0xC2, 0x88, 0x03, 0xA6, 0x00, 0x04, 0x07, 0xA6, 0xF8, 0x03, 0x06, 0xA6, - 0xFC, 0x03, 0x00, 0x33, 0x17, 0x00, 0xC2, 0x88, 0x6A, 0x95, 0xE6, 0x83, 0x34, 0x96, 0xE6, 0x83, - 0x0C, 0x84, 0x04, 0xF0, 0x80, 0x6B, 0x00, 0x33, 0x20, 0x00, 0xC2, 0x88, 0xB6, 0x2D, 0x03, 0xA6, - 0x28, 0x04, 0x07, 0xA6, 0x20, 0x04, 0x06, 0xA6, 0x24, 0x04, 0x00, 0x33, 0x30, 0x00, 0xC2, 0x88, - 0x6A, 0x95, 0x0C, 0x84, 0x34, 0x96, 0x0C, 0x84, 0x1D, 0x01, 0x06, 0xCC, 0x00, 0x33, 0x00, 0x84, - 0xC0, 0x20, 0x00, 0x23, 0xEA, 0x00, 0x81, 0x62, 0xA2, 0x0D, 0x80, 0x63, 0x07, 0xA6, 0x46, 0x04, - 0x00, 0x33, 0x18, 0x00, 0xC2, 0x88, 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01, 0x07, 0xA4, 0x50, 0x04, - 0x23, 0x01, 0x00, 0xA2, 0x72, 0x04, 0x0A, 0xA0, 0x62, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1D, 0x00, - 0xC2, 0x88, 0x0B, 0xA0, 0x6E, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00, 0xC2, 0x88, 0x42, 0x23, - 0xF8, 0x88, 0x00, 0x23, 0x22, 0xA3, 0xD2, 0x04, 0x08, 0x23, 0x22, 0xA3, 0x8E, 0x04, 0x28, 0x23, - 0x22, 0xA3, 0x9A, 0x04, 0x02, 0x23, 0x22, 0xA3, 0xB0, 0x04, 0x42, 0x23, 0xF8, 0x88, 0x4A, 0x00, - 0x06, 0x61, 0x00, 0xA0, 0x9A, 0x04, 0x45, 0x23, 0xF8, 0x88, 0x06, 0x98, 0x00, 0xA2, 0xAC, 0x04, - 0xB4, 0x98, 0x00, 0x33, 0x00, 0x82, 0xC0, 0x20, 0x81, 0x62, 0xF4, 0x81, 0x47, 0x23, 0xF8, 0x88, - 0x04, 0x01, 0x0B, 0xDE, 0x06, 0x98, 0xB4, 0x98, 0x00, 0x33, 0x00, 0x81, 0xC0, 0x20, 0x81, 0x62, - 0x14, 0x01, 0x00, 0xA0, 0x0E, 0x02, 0x43, 0x23, 0xF8, 0x88, 0x04, 0x23, 0xA0, 0x01, 0x44, 0x23, - 0xA1, 0x01, 0x80, 0x73, 0x4D, 0x00, 0x03, 0xA3, 0xE0, 0x04, 0x00, 0x33, 0x27, 0x00, 0xC2, 0x88, - 0x04, 0x01, 0x04, 0xDC, 0x02, 0x23, 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01, 0x06, 0x98, 0x14, 0x95, - 0x4B, 0x00, 0xF6, 0x00, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0xA3, 0x0E, 0x05, 0x00, 0x05, 0x76, 0x00, - 0x06, 0x61, 0x00, 0xA2, 0x08, 0x05, 0xF6, 0x84, 0x48, 0x97, 0xCD, 0x04, 0x12, 0x85, 0x48, 0x04, - 0xFF, 0x23, 0x84, 0x80, 0x02, 0x01, 0x03, 0xDA, 0x80, 0x23, 0x82, 0x01, 0x22, 0x85, 0x02, 0x23, - 0xA0, 0x01, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x2E, 0x05, 0x1D, 0x01, 0x04, 0xD6, 0xFF, 0x23, - 0x86, 0x41, 0x4B, 0x60, 0xCB, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x49, 0x00, 0x81, 0x01, 0x04, 0x01, - 0x02, 0xC8, 0x30, 0x01, 0x80, 0x01, 0xF7, 0x04, 0x03, 0x01, 0x49, 0x04, 0x80, 0x01, 0xC9, 0x00, - 0x00, 0x05, 0x00, 0x01, 0xFF, 0xA0, 0x4E, 0x05, 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00, 0x5D, 0x00, - 0xFE, 0xC7, 0x00, 0x62, 0x00, 0x23, 0xEA, 0x00, 0x00, 0x63, 0x07, 0xA4, 0xCC, 0x05, 0x03, 0x03, - 0x02, 0xA0, 0x7C, 0x05, 0xC8, 0x85, 0x00, 0x33, 0x2D, 0x00, 0xC2, 0x88, 0x04, 0xA0, 0xA2, 0x05, - 0x80, 0x63, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x8E, 0x05, 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23, - 0x02, 0x41, 0x82, 0x01, 0x50, 0x00, 0x64, 0x97, 0xF0, 0x84, 0x04, 0x23, 0x02, 0x41, 0x82, 0x01, - 0xF0, 0x84, 0x08, 0xA0, 0xA8, 0x05, 0xC8, 0x85, 0x03, 0xA0, 0xAE, 0x05, 0xC8, 0x85, 0x01, 0xA0, - 0xBA, 0x05, 0x88, 0x00, 0x80, 0x63, 0xB8, 0x96, 0x6A, 0x85, 0x07, 0xA0, 0xC6, 0x05, 0x06, 0x23, - 0x6A, 0x98, 0x48, 0x23, 0xF8, 0x88, 0xC8, 0x86, 0x80, 0x63, 0x6A, 0x85, 0x00, 0x63, 0x4A, 0x00, - 0x06, 0x61, 0x00, 0xA2, 0x0A, 0x06, 0x1D, 0x01, 0x18, 0xD4, 0xC0, 0x23, 0x07, 0x41, 0x83, 0x03, - 0x80, 0x63, 0x06, 0xA6, 0xEC, 0x05, 0x00, 0x33, 0x37, 0x00, 0xC2, 0x88, 0x1D, 0x01, 0x02, 0xD6, - 0x46, 0x23, 0xF8, 0x88, 0x63, 0x60, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x04, 0x06, 0x00, 0x33, - 0x38, 0x00, 0xC2, 0x88, 0xEF, 0x04, 0x6F, 0x00, 0x00, 0x63, 0x4B, 0x00, 0x06, 0x41, 0xCB, 0x00, - 0x52, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x22, 0x06, 0x1D, 0x01, 0x03, 0xCA, 0xC0, 0x23, 0x07, 0x41, - 0x00, 0x63, 0x1D, 0x01, 0x04, 0xCC, 0x00, 0x33, 0x00, 0x83, 0xC0, 0x20, 0x81, 0x62, 0x80, 0x23, - 0x07, 0x41, 0x00, 0x63, 0x80, 0x67, 0x08, 0x23, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x06, 0xA6, - 0x50, 0x06, 0x07, 0xA6, 0xA4, 0x06, 0x02, 0xA6, 0xFC, 0x06, 0x00, 0x33, 0x39, 0x00, 0xC2, 0x88, - 0x00, 0x00, 0x01, 0xA0, 0x16, 0x07, 0xCE, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x64, 0x06, - 0x07, 0xA6, 0xA4, 0x06, 0x00, 0x00, 0x01, 0xA0, 0x16, 0x07, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63, - 0x01, 0x00, 0x06, 0xA6, 0x80, 0x06, 0x07, 0xA6, 0xA4, 0x06, 0x00, 0x33, 0x3A, 0x00, 0xC2, 0x88, - 0x40, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x72, 0x06, 0x06, 0xA6, 0x98, 0x06, 0x07, 0xA6, - 0xA4, 0x06, 0x00, 0x33, 0x3B, 0x00, 0xC2, 0x88, 0x80, 0x67, 0x40, 0x0E, 0x80, 0x63, 0x07, 0xA6, - 0xA4, 0x06, 0x00, 0x63, 0x03, 0x03, 0x80, 0x63, 0x88, 0x00, 0x01, 0xA2, 0xB8, 0x06, 0x07, 0xA2, - 0xFC, 0x06, 0x00, 0x33, 0x35, 0x00, 0xC2, 0x88, 0x07, 0xA6, 0xC2, 0x06, 0x00, 0x33, 0x2A, 0x00, - 0xC2, 0x88, 0x03, 0x03, 0x03, 0xA2, 0xCE, 0x06, 0x07, 0x23, 0x80, 0x00, 0x08, 0x87, 0x80, 0x63, - 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6, 0xDE, 0x06, 0x00, 0x33, 0x29, 0x00, 0xC2, 0x88, 0x00, 0x43, - 0x00, 0xA2, 0xEA, 0x06, 0xC0, 0x0E, 0x80, 0x63, 0xD4, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80, - 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01, 0x08, 0xDA, 0x80, 0x63, 0x00, 0x63, 0x80, 0x67, 0x00, 0x33, - 0x00, 0x40, 0xC0, 0x20, 0x81, 0x62, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x5C, 0x06, - 0x00, 0x33, 0x2C, 0x00, 0xC2, 0x88, 0x0C, 0xA2, 0x30, 0x07, 0xCE, 0x95, 0x83, 0x03, 0x80, 0x63, - 0x06, 0xA6, 0x2E, 0x07, 0x07, 0xA6, 0xA4, 0x06, 0x00, 0x33, 0x3D, 0x00, 0xC2, 0x88, 0x00, 0x00, - 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, 0x46, 0x07, 0x07, 0xA6, 0xA4, 0x06, 0xBF, 0x23, - 0x04, 0x61, 0x84, 0x01, 0xD2, 0x84, 0x00, 0x63, 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01, - 0xF2, 0x00, 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05, - 0x81, 0x05, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00, - 0x81, 0x01, 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00, - 0x80, 0x01, 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00, - 0x70, 0x00, 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04, - 0x70, 0x00, 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01, - 0xA2, 0x01, 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0xC6, 0x07, - 0x00, 0x33, 0x07, 0x00, 0xC2, 0x88, 0x80, 0x05, 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00, - 0xB0, 0x01, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2, - 0xE6, 0x07, 0x00, 0x05, 0xDC, 0x87, 0x00, 0x01, 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05, - 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08, - 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, - 0x00, 0xA0, 0x16, 0x08, 0x18, 0x88, 0x00, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, - 0xF3, 0x04, 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2, - 0x46, 0x08, 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0x26, 0x08, - 0x06, 0x98, 0x14, 0x95, 0x26, 0x88, 0x73, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x5C, 0x88, - 0x02, 0x01, 0x04, 0xD8, 0x48, 0x97, 0x06, 0x98, 0x14, 0x95, 0x4C, 0x88, 0x75, 0x00, 0x00, 0xA3, - 0x66, 0x08, 0x00, 0x05, 0x50, 0x88, 0x73, 0x04, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, - 0x78, 0x08, 0x00, 0x33, 0x3E, 0x00, 0xC2, 0x88, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, - 0x38, 0x2B, 0x9E, 0x88, 0x38, 0x2B, 0x94, 0x88, 0x32, 0x09, 0x31, 0x05, 0x94, 0x98, 0x05, 0x05, - 0xB2, 0x09, 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32, - 0x80, 0x36, 0x80, 0x3A, 0x80, 0x3E, 0x00, 0x63, 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A, - 0x40, 0x3E, 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, 0x00, 0xA0, 0xB4, 0x08, 0x5D, 0x00, 0xFE, 0xC3, - 0x00, 0x63, 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73, - 0x13, 0x23, 0xF8, 0x88, 0x66, 0x20, 0xC0, 0x20, 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01, - 0x81, 0x62, 0xE2, 0x88, 0x80, 0x73, 0x80, 0x77, 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2, - 0xF1, 0xC7, 0x41, 0x23, 0xF8, 0x88, 0x11, 0x23, 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xD2, 0x84, -}; +uchar _mcode_buf[ ] = { + 0x01, 0x03, 0x01, 0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x0D, 0x09, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x23, 0x00, 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x73, 0x48, 0x04, 0x36, 0x00, 0x00, 0xA2, 0xC2, 0x00, 0x80, 0x73, 0x03, 0x23, 0x36, 0x40, + 0xB6, 0x00, 0x36, 0x00, 0x05, 0xD6, 0x0C, 0xD2, 0x12, 0xDA, 0x00, 0xA2, 0xC2, 0x00, 0x92, 0x80, + 0x18, 0x98, 0x50, 0x00, 0xF5, 0x00, 0x42, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, + 0x4F, 0x00, 0xF5, 0x00, 0x42, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, 0x80, 0x62, + 0x92, 0x80, 0x00, 0x46, 0x17, 0xEE, 0x13, 0xEA, 0x02, 0x01, 0x09, 0xD8, 0xCD, 0x04, 0x4D, 0x00, + 0x00, 0xA3, 0xD6, 0x00, 0xA0, 0x97, 0x7F, 0x23, 0x04, 0x61, 0x84, 0x01, 0xCC, 0x84, 0xD2, 0xC1, + 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xE2, 0x01, 0xA0, 0x97, 0xCE, 0x81, 0x00, 0x33, + 0x02, 0x00, 0xBA, 0x88, 0x80, 0x73, 0x80, 0x77, 0x00, 0x01, 0x01, 0xA1, 0x02, 0x01, 0x4F, 0x00, + 0x7E, 0x97, 0x07, 0xA6, 0x0C, 0x01, 0x00, 0x33, 0x03, 0x00, 0xBA, 0x88, 0x03, 0x03, 0x03, 0xDE, + 0x00, 0x33, 0x05, 0x00, 0xBA, 0x88, 0xCE, 0x00, 0x69, 0x60, 0xCE, 0x00, 0x02, 0x03, 0x4A, 0x60, + 0x00, 0xA2, 0x80, 0x01, 0x80, 0x63, 0x07, 0xA6, 0x2C, 0x01, 0x80, 0x81, 0x03, 0x03, 0x80, 0x63, + 0xE2, 0x00, 0x07, 0xA6, 0x3C, 0x01, 0x00, 0x33, 0x04, 0x00, 0xBA, 0x88, 0x03, 0x07, 0x02, 0x01, + 0x04, 0xCA, 0x0D, 0x23, 0x62, 0x98, 0x4D, 0x04, 0xEA, 0x84, 0x05, 0xD8, 0x0D, 0x23, 0x62, 0x98, + 0xCD, 0x04, 0x15, 0x23, 0xF0, 0x88, 0xFB, 0x23, 0x02, 0x61, 0x82, 0x01, 0x80, 0x63, 0x02, 0x03, + 0x06, 0xA3, 0x6A, 0x01, 0x00, 0x33, 0x0A, 0x00, 0xBA, 0x88, 0x4E, 0x00, 0x07, 0xA3, 0x76, 0x01, + 0x00, 0x33, 0x0B, 0x00, 0xBA, 0x88, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1A, 0x00, 0xBA, 0x88, + 0x50, 0x04, 0x90, 0x81, 0x06, 0xAB, 0x8A, 0x01, 0x90, 0x81, 0x4E, 0x00, 0x07, 0xA3, 0x9A, 0x01, + 0x50, 0x00, 0x00, 0xA3, 0x44, 0x01, 0x00, 0x05, 0x84, 0x81, 0x40, 0x97, 0x02, 0x01, 0x05, 0xC6, + 0x04, 0x23, 0xA0, 0x01, 0x15, 0x23, 0xA1, 0x01, 0xC6, 0x81, 0xFD, 0x23, 0x02, 0x61, 0x82, 0x01, + 0x0A, 0xDA, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xBC, 0x01, 0x80, 0x63, 0xCD, 0x04, 0x36, 0x2D, + 0x00, 0x33, 0x1B, 0x00, 0xBA, 0x88, 0x06, 0x23, 0x62, 0x98, 0xCD, 0x04, 0xCC, 0x84, 0x06, 0x01, + 0x00, 0xA2, 0xDC, 0x01, 0x57, 0x60, 0x00, 0xA0, 0xE2, 0x01, 0xCC, 0x84, 0x80, 0x23, 0xA0, 0x01, + 0xCC, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x08, 0x02, 0x04, 0x01, 0x0C, 0xDE, + 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x7E, 0x97, 0x04, 0x82, 0x08, 0x23, 0x02, 0x41, 0x82, 0x01, + 0x4F, 0x00, 0x5C, 0x97, 0x48, 0x04, 0x84, 0x80, 0xEA, 0x97, 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0, + 0x01, 0x23, 0xE8, 0x00, 0x81, 0x73, 0x06, 0x29, 0x03, 0x42, 0x06, 0xE2, 0x03, 0xEE, 0x67, 0xEB, + 0x11, 0x23, 0xF0, 0x88, 0xFE, 0x97, 0xF4, 0x80, 0x80, 0x73, 0x80, 0x77, 0x06, 0xA6, 0x36, 0x02, + 0x00, 0x33, 0x31, 0x00, 0xBA, 0x88, 0x04, 0x01, 0x03, 0xD8, 0xAC, 0x98, 0x36, 0x96, 0x48, 0x82, + 0xC6, 0x95, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0xB6, 0x2D, 0x02, 0xA6, 0x72, 0x02, 0x07, 0xA6, + 0x60, 0x02, 0x06, 0xA6, 0x64, 0x02, 0x03, 0xA6, 0x68, 0x02, 0x00, 0x33, 0x10, 0x00, 0xBA, 0x88, + 0x62, 0x95, 0x4A, 0x82, 0x2C, 0x96, 0x4A, 0x82, 0x04, 0x23, 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, + 0x22, 0x84, 0x04, 0x01, 0x0C, 0xDC, 0xE0, 0x23, 0x25, 0x61, 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, + 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01, 0x03, 0x23, 0xA4, 0x01, 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, + 0x1C, 0x01, 0x02, 0xA6, 0xA4, 0x02, 0x07, 0xA6, 0x60, 0x02, 0x06, 0xA6, 0x64, 0x02, 0x00, 0x33, + 0x12, 0x00, 0xBA, 0x88, 0x00, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x92, 0x02, 0x4D, 0x04, + 0x04, 0x01, 0x0B, 0xDC, 0xE7, 0x23, 0x04, 0x61, 0x84, 0x01, 0x10, 0x31, 0x12, 0x35, 0x14, 0x01, + 0xEC, 0x00, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0xE4, 0x82, 0x18, 0x23, 0x04, 0x61, 0x18, 0xA0, + 0xDC, 0x02, 0x04, 0x01, 0x98, 0xC8, 0x00, 0x33, 0x1F, 0x00, 0xBA, 0x88, 0x08, 0x31, 0x0A, 0x35, + 0x0C, 0x39, 0x0E, 0x3D, 0x78, 0x98, 0xB6, 0x2D, 0x01, 0xA6, 0x0E, 0x03, 0x00, 0xA6, 0x0E, 0x03, + 0x07, 0xA6, 0x06, 0x03, 0x06, 0xA6, 0x0A, 0x03, 0x03, 0xA6, 0x06, 0x04, 0x02, 0xA6, 0x72, 0x02, + 0x00, 0x33, 0x33, 0x00, 0xBA, 0x88, 0x62, 0x95, 0xE8, 0x82, 0x2C, 0x96, 0xE8, 0x82, 0x7C, 0x98, + 0x80, 0x42, 0x78, 0x98, 0x60, 0xE4, 0x04, 0x01, 0x29, 0xC8, 0x31, 0x05, 0x07, 0x01, 0x00, 0xA2, + 0x4E, 0x03, 0x00, 0x43, 0x87, 0x01, 0x05, 0x05, 0x80, 0x98, 0x78, 0x98, 0x00, 0xA6, 0x10, 0x03, + 0x07, 0xA6, 0x46, 0x03, 0x03, 0xA6, 0x22, 0x04, 0x06, 0xA6, 0x4A, 0x03, 0x01, 0xA6, 0x10, 0x03, + 0x00, 0x33, 0x25, 0x00, 0xBA, 0x88, 0x62, 0x95, 0x2C, 0x83, 0x2C, 0x96, 0x2C, 0x83, 0x04, 0x01, + 0x0C, 0xCE, 0x03, 0xC8, 0x00, 0x33, 0x42, 0x00, 0xBA, 0x88, 0x00, 0x01, 0x05, 0x05, 0xFF, 0xA2, + 0x6C, 0x03, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x28, 0x83, 0x05, 0x05, 0x15, 0x01, 0x00, 0xA2, + 0x8C, 0x03, 0xEC, 0x00, 0x6E, 0x00, 0x95, 0x01, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0x01, 0xA6, + 0x88, 0x03, 0x00, 0xA6, 0x88, 0x03, 0xFC, 0x83, 0x80, 0x42, 0x78, 0x98, 0x01, 0xA6, 0x96, 0x03, + 0x00, 0xA6, 0xAE, 0x03, 0xFC, 0x83, 0xA0, 0x98, 0x80, 0x42, 0x01, 0xA6, 0x96, 0x03, 0x07, 0xA6, + 0xA4, 0x03, 0xC6, 0x83, 0x62, 0x95, 0x9A, 0x83, 0x00, 0x33, 0x2F, 0x00, 0xBA, 0x88, 0xA0, 0x98, + 0x80, 0x42, 0x00, 0xA6, 0xAE, 0x03, 0x07, 0xA6, 0xBC, 0x03, 0xC6, 0x83, 0x62, 0x95, 0xB2, 0x83, + 0x00, 0x33, 0x26, 0x00, 0xBA, 0x88, 0x38, 0x2B, 0x80, 0x32, 0x80, 0x36, 0x04, 0x23, 0xA0, 0x01, + 0x12, 0x23, 0xA1, 0x01, 0xFC, 0x83, 0x04, 0xF0, 0x80, 0x6B, 0x00, 0x33, 0x20, 0x00, 0xBA, 0x88, + 0x03, 0xA6, 0xFA, 0x03, 0x07, 0xA6, 0xF2, 0x03, 0x06, 0xA6, 0xF6, 0x03, 0x00, 0x33, 0x17, 0x00, + 0xBA, 0x88, 0x62, 0x95, 0xE0, 0x83, 0x2C, 0x96, 0xE0, 0x83, 0x06, 0x84, 0x04, 0xF0, 0x80, 0x6B, + 0x00, 0x33, 0x20, 0x00, 0xBA, 0x88, 0xB6, 0x2D, 0x03, 0xA6, 0x22, 0x04, 0x07, 0xA6, 0x1A, 0x04, + 0x06, 0xA6, 0x1E, 0x04, 0x00, 0x33, 0x30, 0x00, 0xBA, 0x88, 0x62, 0x95, 0x06, 0x84, 0x2C, 0x96, + 0x06, 0x84, 0x1D, 0x01, 0x06, 0xCC, 0x00, 0x33, 0x00, 0x84, 0xC0, 0x20, 0x00, 0x23, 0xEA, 0x00, + 0x81, 0x62, 0xA2, 0x0D, 0x80, 0x63, 0x07, 0xA6, 0x40, 0x04, 0x00, 0x33, 0x18, 0x00, 0xBA, 0x88, + 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01, 0x07, 0xA4, 0x4A, 0x04, 0x23, 0x01, 0x00, 0xA2, 0x6C, 0x04, + 0x0A, 0xA0, 0x5C, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1D, 0x00, 0xBA, 0x88, 0x0B, 0xA0, 0x68, 0x04, + 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00, 0xBA, 0x88, 0x42, 0x23, 0xF0, 0x88, 0x00, 0x23, 0x22, 0xA3, + 0xCC, 0x04, 0x08, 0x23, 0x22, 0xA3, 0x88, 0x04, 0x28, 0x23, 0x22, 0xA3, 0x94, 0x04, 0x02, 0x23, + 0x22, 0xA3, 0xAA, 0x04, 0x42, 0x23, 0xF0, 0x88, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0x94, 0x04, + 0x45, 0x23, 0xF0, 0x88, 0xFE, 0x97, 0x00, 0xA2, 0xA6, 0x04, 0xAC, 0x98, 0x00, 0x33, 0x00, 0x82, + 0xC0, 0x20, 0x81, 0x62, 0xF0, 0x81, 0x47, 0x23, 0xF0, 0x88, 0x04, 0x01, 0x0B, 0xDE, 0xFE, 0x97, + 0xAC, 0x98, 0x00, 0x33, 0x00, 0x81, 0xC0, 0x20, 0x81, 0x62, 0x14, 0x01, 0x00, 0xA0, 0x08, 0x02, + 0x43, 0x23, 0xF0, 0x88, 0x04, 0x23, 0xA0, 0x01, 0x44, 0x23, 0xA1, 0x01, 0x80, 0x73, 0x4D, 0x00, + 0x03, 0xA3, 0xDA, 0x04, 0x00, 0x33, 0x27, 0x00, 0xBA, 0x88, 0x04, 0x01, 0x04, 0xDC, 0x02, 0x23, + 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xFE, 0x97, 0x0C, 0x95, 0x4B, 0x00, 0xF6, 0x00, 0x4F, 0x04, + 0x4F, 0x00, 0x00, 0xA3, 0x08, 0x05, 0x00, 0x05, 0x76, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x02, 0x05, + 0xF0, 0x84, 0x40, 0x97, 0xCD, 0x04, 0x0A, 0x85, 0x48, 0x04, 0x84, 0x80, 0x02, 0x01, 0x03, 0xDA, + 0x80, 0x23, 0x82, 0x01, 0x1A, 0x85, 0x02, 0x23, 0xA0, 0x01, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, + 0x26, 0x05, 0x1D, 0x01, 0x04, 0xD6, 0xFF, 0x23, 0x86, 0x41, 0x4B, 0x60, 0xCB, 0x00, 0xFF, 0x23, + 0x80, 0x01, 0x49, 0x00, 0x81, 0x01, 0x04, 0x01, 0x02, 0xC8, 0x30, 0x01, 0x80, 0x01, 0xF7, 0x04, + 0x03, 0x01, 0x49, 0x04, 0x80, 0x01, 0xC9, 0x00, 0x00, 0x05, 0x00, 0x01, 0xFF, 0xA0, 0x46, 0x05, + 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00, 0x5D, 0x00, 0xFE, 0xC7, 0x00, 0x62, 0x00, 0x23, 0xEA, 0x00, + 0x00, 0x63, 0x07, 0xA4, 0xC4, 0x05, 0x03, 0x03, 0x02, 0xA0, 0x74, 0x05, 0xC0, 0x85, 0x00, 0x33, + 0x2D, 0x00, 0xBA, 0x88, 0x04, 0xA0, 0x9A, 0x05, 0x80, 0x63, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, + 0x86, 0x05, 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23, 0x02, 0x41, 0x82, 0x01, 0x50, 0x00, 0x5C, 0x97, + 0xEA, 0x84, 0x04, 0x23, 0x02, 0x41, 0x82, 0x01, 0xEA, 0x84, 0x08, 0xA0, 0xA0, 0x05, 0xC0, 0x85, + 0x03, 0xA0, 0xA6, 0x05, 0xC0, 0x85, 0x01, 0xA0, 0xB2, 0x05, 0x88, 0x00, 0x80, 0x63, 0xB0, 0x96, + 0x62, 0x85, 0x07, 0xA0, 0xBE, 0x05, 0x06, 0x23, 0x62, 0x98, 0x48, 0x23, 0xF0, 0x88, 0xC0, 0x86, + 0x80, 0x63, 0x62, 0x85, 0x00, 0x63, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x02, 0x06, 0x1D, 0x01, + 0x18, 0xD4, 0xC0, 0x23, 0x07, 0x41, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0xE4, 0x05, 0x00, 0x33, + 0x37, 0x00, 0xBA, 0x88, 0x1D, 0x01, 0x02, 0xD6, 0x46, 0x23, 0xF0, 0x88, 0x63, 0x60, 0x83, 0x03, + 0x80, 0x63, 0x06, 0xA6, 0xFC, 0x05, 0x00, 0x33, 0x38, 0x00, 0xBA, 0x88, 0xEF, 0x04, 0x6F, 0x00, + 0x00, 0x63, 0x4B, 0x00, 0x06, 0x41, 0xCB, 0x00, 0x52, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x1A, 0x06, + 0x1D, 0x01, 0x03, 0xCA, 0xC0, 0x23, 0x07, 0x41, 0x00, 0x63, 0x1D, 0x01, 0x04, 0xCC, 0x00, 0x33, + 0x00, 0x83, 0xC0, 0x20, 0x81, 0x62, 0x80, 0x23, 0x07, 0x41, 0x00, 0x63, 0x80, 0x67, 0x08, 0x23, + 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x06, 0xA6, 0x48, 0x06, 0x07, 0xA6, 0x9C, 0x06, 0x02, 0xA6, + 0xF4, 0x06, 0x00, 0x33, 0x39, 0x00, 0xBA, 0x88, 0x00, 0x00, 0x01, 0xA0, 0x0E, 0x07, 0xC6, 0x95, + 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x5C, 0x06, 0x07, 0xA6, 0x9C, 0x06, 0x00, 0x00, 0x01, 0xA0, + 0x0E, 0x07, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63, 0x01, 0x00, 0x06, 0xA6, 0x78, 0x06, 0x07, 0xA6, + 0x9C, 0x06, 0x00, 0x33, 0x3A, 0x00, 0xBA, 0x88, 0x40, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, + 0x6A, 0x06, 0x06, 0xA6, 0x90, 0x06, 0x07, 0xA6, 0x9C, 0x06, 0x00, 0x33, 0x3B, 0x00, 0xBA, 0x88, + 0x80, 0x67, 0x40, 0x0E, 0x80, 0x63, 0x07, 0xA6, 0x9C, 0x06, 0x00, 0x63, 0x03, 0x03, 0x80, 0x63, + 0x88, 0x00, 0x01, 0xA2, 0xB0, 0x06, 0x07, 0xA2, 0xF4, 0x06, 0x00, 0x33, 0x35, 0x00, 0xBA, 0x88, + 0x07, 0xA6, 0xBA, 0x06, 0x00, 0x33, 0x2A, 0x00, 0xBA, 0x88, 0x03, 0x03, 0x03, 0xA2, 0xC6, 0x06, + 0x07, 0x23, 0x80, 0x00, 0x00, 0x87, 0x80, 0x63, 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6, 0xD6, 0x06, + 0x00, 0x33, 0x29, 0x00, 0xBA, 0x88, 0x00, 0x43, 0x00, 0xA2, 0xE2, 0x06, 0xC0, 0x0E, 0x80, 0x63, + 0xCC, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80, 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01, 0x08, 0xDA, + 0x80, 0x63, 0x00, 0x63, 0x80, 0x67, 0x00, 0x33, 0x00, 0x40, 0xC0, 0x20, 0x81, 0x62, 0x00, 0x63, + 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x54, 0x06, 0x00, 0x33, 0x2C, 0x00, 0xBA, 0x88, 0x0C, 0xA2, + 0x28, 0x07, 0xC6, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x26, 0x07, 0x07, 0xA6, 0x9C, 0x06, + 0x00, 0x33, 0x3D, 0x00, 0xBA, 0x88, 0x00, 0x00, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, + 0x3E, 0x07, 0x07, 0xA6, 0x9C, 0x06, 0xBF, 0x23, 0x04, 0x61, 0x84, 0x01, 0xCC, 0x84, 0x00, 0x63, + 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01, 0xF2, 0x00, 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, + 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05, 0x81, 0x05, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, + 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00, 0x81, 0x01, 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, + 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00, 0x80, 0x01, 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, + 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00, 0x70, 0x00, 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, + 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04, 0x70, 0x00, 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, + 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01, 0xA2, 0x01, 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, + 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0xBE, 0x07, 0x00, 0x33, 0x07, 0x00, 0xBA, 0x88, 0x80, 0x05, + 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00, 0xB0, 0x01, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, + 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2, 0xDE, 0x07, 0x00, 0x05, 0xD4, 0x87, 0x00, 0x01, + 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, + 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, + 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x00, 0xA0, 0x0E, 0x08, 0x10, 0x88, 0x00, 0x43, + 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, + 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2, 0x3E, 0x08, 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, + 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0x1E, 0x08, 0xFE, 0x97, 0x0C, 0x95, 0x1E, 0x88, 0x73, 0x04, + 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x54, 0x88, 0x02, 0x01, 0x04, 0xD8, 0x40, 0x97, 0xFE, 0x97, + 0x0C, 0x95, 0x44, 0x88, 0x75, 0x00, 0x00, 0xA3, 0x5E, 0x08, 0x00, 0x05, 0x48, 0x88, 0x73, 0x04, + 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x70, 0x08, 0x00, 0x33, 0x3E, 0x00, 0xBA, 0x88, + 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x38, 0x2B, 0x96, 0x88, 0x38, 0x2B, 0x8C, 0x88, + 0x32, 0x09, 0x31, 0x05, 0x8C, 0x98, 0x05, 0x05, 0xB2, 0x09, 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, + 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32, 0x80, 0x36, 0x80, 0x3A, 0x80, 0x3E, 0x00, 0x63, + 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A, 0x40, 0x3E, 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, + 0x00, 0xA0, 0xAC, 0x08, 0x5D, 0x00, 0xFE, 0xC3, 0x00, 0x63, 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, + 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73, 0x13, 0x23, 0xF0, 0x88, 0x66, 0x20, 0xC0, 0x20, + 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01, 0x81, 0x62, 0xDA, 0x88, 0x80, 0x73, 0x80, 0x77, + 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2, 0xF1, 0xC7, 0x41, 0x23, 0xF0, 0x88, 0x11, 0x23, + 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xCC, 0x84, +} ; + +ushort _mcode_size = sizeof(_mcode_buf); +ulong _mcode_chksum = 0x012BA2FAUL ; -ushort _mcode_size = sizeof (_mcode_buf); -ulong _mcode_chksum = 0x012CD3FFUL; #define ASC_SYN_OFFSET_ONE_DISABLE_LIST 16 uchar _syn_offset_one_disable_cmd[ASC_SYN_OFFSET_ONE_DISABLE_LIST] = { @@ -6978,7 +7558,9 @@ return (ERR); } scsiq->q1.q_no = 0; - scsiq->q1.extra_bytes = 0; + if ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0) { + scsiq->q1.extra_bytes = 0; + } sta = 0; target_ix = scsiq->q2.target_ix; tid_no = ASC_TIX_TO_TID(target_ix); @@ -6988,7 +7570,7 @@ ((asc_dvc->sdtr_done & scsiq->q1.target_id) != 0)) { sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no); AscMsgOutSDTR(asc_dvc, - asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) & (uchar) (ASC_SYN_XFER_NO - 1)], + asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) & (uchar) (asc_dvc->max_sdtr_index - 1)], (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET)); scsiq->q1.cntl |= (QC_MSG_OUT | QC_URGENT); } @@ -7082,7 +7664,7 @@ addr = sg_head->sg_list[sg_entry_cnt_minus_one].addr + sg_head->sg_list[sg_entry_cnt_minus_one].bytes; extra_bytes = (uchar) ((ushort) addr & 0x0003); - if (extra_bytes != 0) { + if ((extra_bytes != 0) && ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0)) { scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES; scsiq->q1.extra_bytes = extra_bytes; sg_head->sg_list[sg_entry_cnt_minus_one].bytes -= (ulong) extra_bytes; @@ -7122,7 +7704,7 @@ ) { addr = scsiq->q1.data_addr + scsiq->q1.data_cnt; extra_bytes = (uchar) ((ushort) addr & 0x0003); - if (extra_bytes != 0) { + if ((extra_bytes != 0) && ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0)) { if (((ushort) scsiq->q1.data_cnt & 0x01FF) == 0) { scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES; scsiq->q1.data_cnt -= (ulong) extra_bytes; @@ -7307,7 +7889,7 @@ return (cur_free_qs); } if (n_qs > 1) { - if (n_qs > asc_dvc->last_q_shortage) { + if ((n_qs > asc_dvc->last_q_shortage) && ( n_qs <= ( asc_dvc->max_total_qng - ASC_MIN_FREE_Q ))) { asc_dvc->last_q_shortage = n_qs; } } @@ -7332,7 +7914,7 @@ ((asc_dvc->sdtr_done & scsiq->q1.target_id) == 0)) { tid_no = ASC_TIX_TO_TID(scsiq->q2.target_ix); sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no); - syn_period_ix = (sdtr_data >> 4) & (ASC_SYN_XFER_NO - 1); + syn_period_ix = (sdtr_data >> 4) & (asc_dvc->max_sdtr_index - 1); syn_offset = sdtr_data & ASC_SYN_MAX_OFFSET; AscMsgOutSDTR(asc_dvc, asc_dvc->sdtr_period_tbl[syn_period_ix], @@ -8686,8 +9268,7 @@ asc_dvc->max_dma_count = AscGetMaxDmaCount(asc_dvc->bus_type); asc_dvc->redo_scam = 0; asc_dvc->res2 = 0; - asc_dvc->res4 = 0; - asc_dvc->res6 = 0; + asc_dvc->host_init_sdtr_index = 0; asc_dvc->res7 = 0; asc_dvc->res8 = 0; asc_dvc->cfg->disc_enable = ASC_SCSI_WIDTH_BIT_SET; @@ -8746,7 +9327,6 @@ asc_dvc->scsiq_busy_head[i] = (ASC_SCSI_Q dosfar *) 0L; asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q dosfar *) 0L; asc_dvc->cfg->max_tag_qng[i] = ASC_MAX_INRAM_TAG_QNG; - asc_dvc->cfg->sdtr_period_offset[i] = (uchar) (ASC_DEF_SDTR_OFFSET | (ASC_DEF_SDTR_INDEX << 4)); } return (warn_code); } @@ -8855,12 +9435,20 @@ } eep_config->chip_scsi_id &= ASC_MAX_TID; asc_dvc->cfg->chip_scsi_id = eep_config->chip_scsi_id; + if (((asc_dvc->bus_type & ASC_IS_PCI_ULTRA ) == ASC_IS_PCI_ULTRA ) && + !(asc_dvc->dvc_cntl & ASC_CNTL_SDTR_ENABLE_ULTRA)) { + asc_dvc->host_init_sdtr_index = ASC_SDTR_ULTRA_PCI_10MB_INDEX; + } + for (i = 0; i <= ASC_MAX_TID; i++) { #if CC_TMP_USE_EEP_SDTR asc_dvc->cfg->sdtr_period_offset[i] = eep_config->dos_int13_table[i]; #endif asc_dvc->dos_int13_table[i] = eep_config->dos_int13_table[i]; asc_dvc->cfg->max_tag_qng[i] = eep_config->max_tag_qng; + asc_dvc->cfg->sdtr_period_offset[i] = + (uchar) (ASC_DEF_SDTR_OFFSET | + (asc_dvc->host_init_sdtr_index << 4)); } eep_config->cfg_msw = AscGetChipCfgMsw(iop_base); #if CC_CHK_FIX_EEP_CONTENT @@ -8880,6 +9468,7 @@ PortAddr iop_base; ushort warn_code; ushort cfg_msw; + int i; iop_base = asc_dvc->iop_base; warn_code = 0; cfg_msw = AscGetChipCfgMsw(iop_base); @@ -8889,12 +9478,19 @@ AscSetChipCfgMsw(iop_base, cfg_msw); } if (!AscTestExternalLram(asc_dvc)) { - if (asc_dvc->bus_type & ASC_IS_PCI) { + if (((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA)) { + asc_dvc->max_total_qng = ASC_MAX_PCI_ULTRA_INRAM_TOTAL_QNG ; + for( i = 0 ; i <= ASC_MAX_TID ; i++ ) { + asc_dvc->cfg->max_tag_qng[ i ] = ASC_MAX_PCI_ULTRA_INRAM_TAG_QNG ; + } + } else { cfg_msw |= 0x0800; AscSetChipCfgMsw(iop_base, cfg_msw); asc_dvc->max_total_qng = ASC_MAX_PCI_INRAM_TOTAL_QNG; + for (i = 0 ; i <= ASC_MAX_TID ; i++) { + asc_dvc->cfg->max_tag_qng[ i ] = ASC_MAX_INRAM_TAG_QNG; + } } - } else { } if (asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_VL | ASC_IS_EISA)) { asc_dvc->irq_no = AscGetChipIRQ(iop_base, asc_dvc->bus_type); @@ -8959,6 +9555,7 @@ ASC_ISR_CALLBACK asc_isr_callback; uchar cp_sen_len; uchar i; + ASC_DBG(1, "AscInitPollIsrCallBack: begin\n"); if ((scsi_done_q->d2.flag & ASC_FLAG_SCSIQ_REQ) != 0) { scsiq_req = (ASC_SCSI_REQ_Q dosfar *) scsi_done_q->d2.srb_ptr; scsiq_req->r3.done_stat = scsi_done_q->d3.done_stat; @@ -8981,6 +9578,7 @@ (*asc_isr_callback) (asc_dvc, scsi_done_q); } } + ASC_DBG(1, "AscInitPollIsrCallBack: end\n"); return; } @@ -9316,6 +9914,7 @@ asc_dvc->init_sdtr &= ~tid_bits; tmp_disable_init_sdtr = TRUE; } + ASC_DBG(1, "AscInitPollTarget: before PollScsiInquiry\n"); if ( PollScsiInquiry(asc_dvc, scsiq, (uchar dosfar *) inq, sizeof (ASC_SCSI_INQUIRY)) == 1 @@ -9353,19 +9952,8 @@ if (inq->byte7.CmdQue) { asc_dvc->cfg->can_tagged_qng |= tid_bits; if (asc_dvc->cfg->cmd_qng_enabled & tid_bits) { -#if CC_FIX_QUANTUM_XP34301_1071 - if ( - (inq->add_len >= 32) - && (AscCompareString(inq->vendor_id, (uchar *) "QUANTUM XP34301", 15) == 0) - && (AscCompareString(inq->product_rev_level, (uchar *) "1071", 4) == 0) - ) { - } else { -#endif asc_dvc->use_tagged_qng |= tid_bits; asc_dvc->max_dvc_qng[tid_no] = asc_dvc->cfg->max_tag_qng[tid_no]; -#if CC_FIX_QUANTUM_XP34301_1071 - } -#endif } } if (!inq->byte7.Sync) { @@ -9404,12 +9992,12 @@ } } sta = 1; -#if CC_INIT_TARGET_TEST_UNIT_READY + ASC_DBG(1, "AscInitPollTarget: before InitTestUnitReady\n"); sta = InitTestUnitReady(asc_dvc, scsiq); -#endif -#if CC_INIT_TARGET_READ_CAPACITY if (sta == 1) { if ((cap_info != 0L) && support_read_cap) { + ASC_DBG(1, + "AscInitPollTarget: before PollScsiReadCapacity\n"); if (PollScsiReadCapacity(asc_dvc, scsiq, cap_info) != 1) { cap_info->lba = 0L; @@ -9418,12 +10006,11 @@ } } } -#endif } else { asc_dvc->start_motor &= ~tid_bits; } - } else { } + ASC_DBG1(1, "AscInitPollTarget: dvc_found %d\n", dvc_found); return (dvc_found); } @@ -9435,13 +10022,14 @@ ) { int status; - int retry; - retry = 0; + int retry = 0; + + ASC_DBG1(1, "PollQueueDone: timeout_sec %d", timeout_sec); do { - if ( - (status = AscExeScsiQueue(asc_dvc, - (ASC_SCSI_Q dosfar *) scsiq)) == 1 - ) { + ASC_DBG(1, "PollQueueDone: before AscExeScsiQueue\n"); + if ((status = AscExeScsiQueue(asc_dvc, + (ASC_SCSI_Q dosfar *) scsiq)) == 1) { + ASC_DBG(1, "PollQueueDone: before AscPollQDone\n"); if ((status = AscPollQDone(asc_dvc, scsiq, timeout_sec)) != 1) { if (status == 0x80) { @@ -9461,9 +10049,13 @@ scsiq->r3.scsi_msg = 0; AscAbortSRB(asc_dvc, (ulong) scsiq); } + ASC_DBG1(1, "PollQueueDone: done_stat %x\n", scsiq->r3.done_stat); return (scsiq->r3.done_stat); } - } while ((status == 0) || (status == 0x80)); + DvcSleepMilliSecond(5); + } while (((status == 0) || (status == 0x80)) && + retry++ < ASC_MAX_INIT_BUSY_RETRY); + ASC_DBG(1, "PollQueueDone: done_stat QD_WITH_ERROR\n"); return (scsiq->r3.done_stat = QD_WITH_ERROR); } @@ -9481,7 +10073,6 @@ return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q dosfar *) scsiq, 4)); } -#if CC_INIT_TARGET_START_UNIT int PollScsiStartUnit( REG ASC_DVC_VAR asc_ptr_type * asc_dvc, @@ -9493,9 +10084,7 @@ } return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q dosfar *) scsiq, 40)); } -#endif -#if CC_INIT_TARGET_READ_CAPACITY int PollScsiReadCapacity( REG ASC_DVC_VAR asc_ptr_type * asc_dvc, @@ -9522,7 +10111,6 @@ } return (scsiq->r3.done_stat = QD_WITH_ERROR); } -#endif ulong dosfar * swapfarbuf4( @@ -9542,7 +10130,6 @@ return ((ulong dosfar *) buf); } -#if CC_INIT_TARGET_TEST_UNIT_READY int PollScsiTestUnitReady( REG ASC_DVC_VAR asc_ptr_type * asc_dvc, @@ -9603,7 +10190,6 @@ } return (0); } -#endif int AscPollQDone( @@ -9866,7 +10452,6 @@ return (0); } -#if CC_INIT_TARGET_READ_CAPACITY int AscScsiReadCapacity( REG ASC_DVC_VAR asc_ptr_type * asc_dvc, @@ -9890,9 +10475,7 @@ scsiq->r2.cdb_len = 10; return (0); } -#endif -#if CC_INIT_TARGET_TEST_UNIT_READY int AscScsiTestUnitReady( REG ASC_DVC_VAR asc_ptr_type * asc_dvc, @@ -9913,9 +10496,7 @@ scsiq->r2.cdb_len = 6; return (0); } -#endif -#if CC_INIT_TARGET_START_UNIT int AscScsiStartStopUnit( REG ASC_DVC_VAR asc_ptr_type * asc_dvc, @@ -9936,4 +10517,3 @@ scsiq->r2.cdb_len = 6; return (0); } -#endif diff -ur --new-file old/linux/drivers/scsi/advansys.h new/linux/drivers/scsi/advansys.h --- old/linux/drivers/scsi/advansys.h Wed Aug 14 08:59:04 1996 +++ new/linux/drivers/scsi/advansys.h Fri Sep 27 06:52:29 1996 @@ -1,4 +1,4 @@ -/* $Id: advansys.h,v 1.11 1996/08/12 17:20:44 bobf Exp bobf $ */ +/* $Id: advansys.h,v 1.12 1996/09/23 18:12:02 bobf Exp bobf $ */ /* * advansys.h - Linux Host Driver for AdvanSys SCSI Adapters * @@ -85,10 +85,12 @@ 1, /* unsigned unchecked_isa_dma:1 */ \ /* \ * All adapters controlled by this driver are capable of large \ - * scatter-gather lists. This apparently obviates any performance - * gain provided by setting 'use_clustering'. \ + * scatter-gather lists. According to the mid-level SCSI documentation \ + * this obviates any performance gain provided by setting \ + * 'use_clustering'. But empirically while CPU utilization is increased \ + * by enabling clustering, I/O throughput increases as well. \ */ \ - DISABLE_CLUSTERING, /* unsigned use_clustering:1 */ \ + ENABLE_CLUSTERING, /* unsigned use_clustering:1 */ \ } #else /* version >= v1.3.0 */ #define ADVANSYS { \ @@ -126,10 +128,12 @@ 1, /* unsigned unchecked_isa_dma:1 */ \ /* \ * All adapters controlled by this driver are capable of large \ - * scatter-gather lists. This apparently obviates any performance - * gain provided by setting 'use_clustering'. \ + * scatter-gather lists. According to the mid-level SCSI documentation \ + * this obviates any performance gain provided by setting \ + * 'use_clustering'. But empirically while CPU utilization is increased \ + * by enabling clustering, I/O throughput increases as well. \ */ \ - DISABLE_CLUSTERING, /* unsigned use_clustering:1 */ \ + ENABLE_CLUSTERING, /* unsigned use_clustering:1 */ \ } #endif /* version >= v1.3.0 */ #endif /* _ADVANSYS_H */ diff -ur --new-file old/linux/drivers/scsi/aha152x.c new/linux/drivers/scsi/aha152x.c --- old/linux/drivers/scsi/aha152x.c Sat Aug 17 20:02:50 1996 +++ new/linux/drivers/scsi/aha152x.c Wed Oct 30 02:42:41 1996 @@ -1,6 +1,6 @@ /* aha152x.c -- Adaptec AHA-152x driver - * Author: Juergen E. Fischer, fischer@et-inf.fho-emden.de - * Copyright 1993, 1994, 1995, 1996 Juergen E. Fischer + * Author: Jürgen E. Fischer, fischer@et-inf.fho-emden.de + * Copyright 1993, 1994, 1995, 1996 Jürgen E. Fischer * * * This driver is based on @@ -20,9 +20,12 @@ * General Public License for more details. * * - * $Id: aha152x.c,v 1.17 1996/08/17 16:05:14 fischer Exp fischer $ + * $Id: aha152x.c,v 1.18 1996/09/07 20:10:40 fischer Exp $ * * $Log: aha152x.c,v $ + * Revision 1.18 1996/09/07 20:10:40 fischer + * - fixed can_queue handling (multiple outstanding commands working again) + * * Revision 1.17 1996/08/17 16:05:14 fischer * - biosparam improved * - interrupt verification @@ -401,7 +404,7 @@ /* END OF DEFINES */ -extern long loops_per_sec; +extern unsigned long loops_per_sec; #define DELAY_DEFAULT 100 @@ -975,7 +978,7 @@ shpnt->this_id=setup[i].scsiid; if(setup[i].reconnect) - shpnt->hostt->can_queue=AHA152X_MAXQUEUE; + shpnt->can_queue=AHA152X_MAXQUEUE; /* RESET OUT */ SETBITS(SCSISEQ, SCSIRSTO); diff -ur --new-file old/linux/drivers/scsi/aha152x.h new/linux/drivers/scsi/aha152x.h --- old/linux/drivers/scsi/aha152x.h Sat Aug 17 20:02:50 1996 +++ new/linux/drivers/scsi/aha152x.h Wed Sep 11 16:57:14 1996 @@ -2,7 +2,7 @@ #define _AHA152X_H /* - * $Id: aha152x.h,v 1.17 1996/08/17 16:07:38 fischer Exp fischer $ + * $Id: aha152x.h,v 1.18 1996/09/07 20:10:26 fischer Exp $ */ #if defined(__KERNEL__) @@ -23,7 +23,7 @@ (unless we support more than 1 cmd_per_lun this should do) */ #define AHA152X_MAXQUEUE 7 -#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.17 $" +#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.18 $" extern struct proc_dir_entry proc_scsi_aha152x; diff -ur --new-file old/linux/drivers/scsi/aha1542.c new/linux/drivers/scsi/aha1542.c --- old/linux/drivers/scsi/aha1542.c Thu Aug 1 14:43:04 1996 +++ new/linux/drivers/scsi/aha1542.c Thu Nov 7 10:25:21 1996 @@ -1027,7 +1027,7 @@ shpnt->dma_channel = dma_chan; shpnt->irq = irq_level; HOSTDATA(shpnt)->bios_translation = trans; - if(trans == 2) + if(trans == BIOS_TRANSLATION_25563) printk("aha1542.c: Using extended bios translation\n"); HOSTDATA(shpnt)->aha1542_last_mbi_used = (2*AHA1542_MAILBOXES - 1); HOSTDATA(shpnt)->aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1); @@ -1300,8 +1300,8 @@ int size = disk->capacity; translation_algorithm = HOSTDATA(disk->device->host)->bios_translation; - /* Should this be > 1024, or >= 1024? Enquiring minds want to know. */ - if((size>>11) > 1024 && translation_algorithm == 2) { + + if((size>>11) > 1024 && translation_algorithm == BIOS_TRANSLATION_25563) { /* Please verify that this is the same as what DOS returns */ ip[0] = 255; ip[1] = 63; @@ -1310,8 +1310,8 @@ ip[0] = 64; ip[1] = 32; ip[2] = size >> 11; - }; -/* if (ip[2] >= 1024) ip[2] = 1024; */ + } + return 0; } diff -ur --new-file old/linux/drivers/scsi/aic7xxx.c new/linux/drivers/scsi/aic7xxx.c --- old/linux/drivers/scsi/aic7xxx.c Sat Aug 10 09:44:18 1996 +++ new/linux/drivers/scsi/aic7xxx.c Mon Oct 28 23:21:33 1996 @@ -1,3 +1,5 @@ +#define EXPERIMENTAL_FLAGS 0 + /*+M************************************************************************* * Adaptec AIC7xxx device driver for Linux. * @@ -41,7 +43,7 @@ * * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96 * - * $Id: aic7xxx.c,v 3.4 1996/08/09 15:56:31 deang Exp $ + * $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $ *-M*************************************************************************/ #ifdef MODULE @@ -50,6 +52,7 @@ #include #include +#include #include #include #include @@ -66,15 +69,22 @@ #include "aic7xxx.h" #include "aic7xxx_reg.h" #include +#include /* for kmalloc() */ #include /* for CONFIG_PCI */ +/* + * To generate the correct addresses for the controller to issue + * on the bus. Originally added for DEC Alpha support. + */ +#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a)) + struct proc_dir_entry proc_scsi_aic7xxx = { PROC_SCSI_AIC7XXX, 7, "aic7xxx", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -#define AIC7XXX_C_VERSION "$Revision: 3.4 $" +#define AIC7XXX_C_VERSION "$Revision: 4.0 $" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) ((a < b) ? a : b) @@ -133,12 +143,13 @@ * o 3985 support - The 3985 adapter is much like the 3940, but * has three 7870 controllers as opposed to two for the 3940. * It will get probed and recognized as three different adapters, - * but all three controllers share the same bank of 255 SCBs - * instead of each controller having their own bank (like the - * controllers on the 3940). For this reason, it is important - * that all devices be resident on just one channel of the 3985. - * In the near future, we'll modify the driver to reserve 1/3 - * of the SCBs for each controller. + * but all three controllers can share the same external bank of + * 255 SCBs. If you enable AIC7XXX_SHARE_SCBS, then the driver + * will attempt to share the common bank of SCBs between the three + * controllers of the 3985. This is experimental and hasn't + * been tested. By default, we do not share the bank of SCBs, + * and force the controllers to use their own internal bank of + * 16 SCBs. Please let us know if sharing the SCB array works. * * o SCB paging support - SCB paging is enabled by defining * AIC7XXX_PAGE_ENABLE. Support for this was taken from the @@ -173,6 +184,7 @@ * Uncomment the following define for collection of SCSI transfer statistics * for the /proc filesystem. * + * NOTE: Do NOT enable this when running on kernels version 1.2.x and below. * NOTE: This does affect performance since it has to maintain statistics. */ /* #define AIC7XXX_PROC_STATS */ @@ -183,6 +195,12 @@ /* #define AIC7XXX_PAGE_ENABLE */ /* + * Uncomment the following to enable sharing of the external bank + * of 255 SCBs for the 3985. + */ +#define AIC7XXX_SHARE_SCBS + +/* * For debugging the abort/reset code. */ #define AIC7XXX_DEBUG_ABORT @@ -203,6 +221,7 @@ AIC_7850, /* PCI aic7850 */ AIC_7855, /* PCI aic7855 */ AIC_7860, /* PCI aic7860 (7850 Ultra) */ + AIC_7861, /* PCI aic7861 on 2940AU */ AIC_7870, /* PCI aic7870 on motherboard */ AIC_7871, /* PCI aic7871 on 294x */ AIC_7872, /* PCI aic7872 on 3940 */ @@ -257,6 +276,7 @@ "AIC-7850", /* AIC_7850 */ "AIC-7855", /* AIC_7855 */ "AIC-7850 Ultra", /* AIC_7860 */ + "AHA-2940A Ultra", /* AIC_7861 */ "AIC-7870", /* AIC_7870 */ "AHA-2940", /* AIC_7871 */ "AHA-3940", /* AIC_7872 */ @@ -296,7 +316,6 @@ #define MINSLOT 1 #define MAXSLOT 15 #define SLOTBASE(x) ((x) << 12) -#define MAXIRQ 15 /* * Standard EISA Host ID regs (Offset from slot base) @@ -348,6 +367,18 @@ #define STPWLEVEL 0x00000002ul #define DIFACTNEGEN 0x00000001ul /* aic7870 only */ + +/* + * Define the different types of SEEPROMs on aic7xxx adapters + * and make it also represent the address size used in accessing + * its registers. The 93C46 chips have 1024 bits organized into + * 64 16-bit words, while the 93C56 chips have 2048 bits organized + * into 128 16-bit words. The C46 chips use 6 bits to address + * each word, while the C56 and C66 (4096 bits) use 8 bits to + * address each word. + */ +typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type; + /* * * Define the format of the SEEPROM registers (16 bits). @@ -466,18 +497,6 @@ #define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in) /* - * Since the sequencer code DMAs the scatter-gather structures - * directly from memory, we use this macro to assert that the - * kernel structure hasn't changed. - */ -#define SG_STRUCT_CHECK(sg) \ - ((char *) &(sg).address - (char *) &(sg) != 0 || \ - (char *) &(sg).length - (char *) &(sg) != 8 || \ - sizeof((sg).address) != 4 || \ - sizeof((sg).length) != 4 || \ - sizeof(sg) != 12) - -/* * "Static" structures. Note that these are NOT initialized * to zero inside the kernel - we have to initialize them all * explicitly. @@ -487,7 +506,7 @@ * use the IRQ as an index into aic7xxx_boards[] to locate the card * information. */ -static struct Scsi_Host *aic7xxx_boards[MAXIRQ + 1]; +static struct Scsi_Host *aic7xxx_boards[NR_IRQS + 1]; /* * When we detect and register the card, it is possible to @@ -526,6 +545,21 @@ * for driver level bookkeeping. */ +/* + * As of Linux 2.1, the mid-level SCSI code uses virtual addresses + * in the scatter-gather lists. We need to convert the virtual + * addresses to physical addresses. + */ +struct hw_scatterlist { + unsigned int address; + unsigned int length; +}; + +/* + * Maximum number of SG segments these cards can support. + */ +#define MAX_SG 256 + struct aic7xxx_scb { /* ------------ Begin hardware supported fields ---------------- */ /* 0*/ unsigned char control; @@ -534,9 +568,9 @@ /* 3*/ unsigned char SG_segment_count; /* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed)); /* 8*/ unsigned char residual_SG_segment_count; -/* 9*/ unsigned char residual_data_count[3]; +/* 9*/ unsigned char residual_data_count[3] __attribute__ ((packed)); /*12*/ unsigned char data_pointer[4] __attribute__ ((packed)); -/*16*/ unsigned long data_count; +/*16*/ unsigned int data_count __attribute__ ((packed)); /* must be 32 bits */ /*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed)); /*24*/ unsigned char SCSI_cmd_length; /*25*/ u_char tag; /* Index into our kernel SCB array. @@ -566,8 +600,7 @@ SCB_WAITINGQ | SCB_ASSIGNEDQ) int state; /* current state of scb */ unsigned int position; /* Position in scb array */ - struct scatterlist sg; - struct scatterlist sense_sg; + struct hw_scatterlist sg_list[MAX_SG]; /* SG list in adapter format */ unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */ }; @@ -592,6 +625,15 @@ static unsigned char generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 }; +typedef struct { + scb_queue_type free_scbs; /* + * SCBs assigned to free slot on + * card (no paging required) + */ + int numscbs; /* current number of scbs */ + int activescbs; /* active scbs */ +} scb_usage_type; + /* * The maximum number of SCBs we could have for ANY type * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE @@ -608,8 +650,6 @@ int base; /* card base address */ int maxhscbs; /* hardware SCBs */ int maxscbs; /* max SCBs (including pageable) */ - int numscbs; /* current number of scbs */ - int activescbs; /* active scbs */ #define A_SCANNED 0x0001 #define B_SCANNED 0x0002 #define EXTENDED_TRANSLATION 0x0004 @@ -638,15 +678,11 @@ unsigned char qcntmask; struct seeprom_config seeprom; struct Scsi_Host *next; /* allow for multiple IRQs */ - struct aic7xxx_scb scb_array[AIC7XXX_MAXSCB]; /* active commands */ + struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; /* active commands */ struct aic7xxx_scb *pagedout_ntscbs[16]; /* * paged-out, non-tagged scbs * indexed by target. */ - scb_queue_type free_scbs; /* - * SCBs assigned to free slot on - * card (no paging required) - */ scb_queue_type page_scbs; /* * SCBs that will require paging * before use (no assigned slot) @@ -660,6 +696,9 @@ * have now been assigned a slot * by aic7xxx_free_scb */ + scb_usage_type scb_usage; + scb_usage_type *scb_link; + struct aic7xxx_cmd_queue { Scsi_Cmnd *head; Scsi_Cmnd *tail; @@ -750,6 +789,9 @@ #ifdef CONFIG_PCI static int number_of_3940s = 0; static int number_of_3985s = 0; +#ifdef AIC7XXX_SHARE_SCBS +static scb_usage_type *shared_3985_scbs = NULL; +#endif #endif CONFIG_PCI #ifdef AIC7XXX_DEBUG @@ -802,6 +844,7 @@ case AIC_7850: case AIC_7855: case AIC_7860: + case AIC_7861: case AIC_7870: case AIC_7871: case AIC_7872: @@ -877,7 +920,10 @@ scb->SCSI_cmd_length); printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n", (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status, - scb->residual_SG_segment_count, scb->residual_data_count); + scb->residual_SG_segment_count, + ((scb->residual_data_count[2] << 16) | + (scb->residual_data_count[1] << 8) | + (scb->residual_data_count[0])); printk("data ptr 0x%x, data count %d, next waiting %d\n", (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) | (scb->data_pointer[1] << 8) | scb->data_pointer[0], @@ -911,6 +957,8 @@ * 0 use edge triggered * 1 use level triggered */ +static int aic7xxx_enable_ultra = 0; /* enable ultra SCSI speeds */ + /*+F************************************************************************* * Function: * aic7xxx_setup @@ -933,6 +981,7 @@ { "extended", &aic7xxx_extended }, { "no_reset", &aic7xxx_no_reset }, { "irq_trigger", &aic7xxx_irq_trigger }, + { "ultra", &aic7xxx_enable_ultra }, { NULL, NULL } }; @@ -1123,7 +1172,7 @@ struct scatterlist *sg; segments = cmd->use_sg - sg_last; - sg = (struct scatterlist *) cmd->buffer; + sg = (struct scatterlist *) cmd->request_buffer; if (cmd->use_sg) { @@ -1155,53 +1204,60 @@ unsigned long ultra_enb_addr; unsigned char ultra_enb, sxfrctl0; - for (i = 0; i < num_aic7xxx_syncrates; i++) + /* + * If the offset is 0, then the device is requesting asynchronous + * transfers. + */ + if (offset != 0) { - if ((aic7xxx_syncrates[i].period - period) >= 0) + for (i = 0; i < num_aic7xxx_syncrates; i++) { - /* - * Watch out for Ultra speeds when ultra is not enabled and - * vice-versa. - */ - if (!(p->flags & ULTRA_ENABLED) && - (aic7xxx_syncrates[i].rate & ULTRA_SXFR)) + if ((aic7xxx_syncrates[i].period - period) >= 0) { /* - * This should only happen if the drive is the first to negotiate - * and chooses a high rate. We'll just move down the table until - * we hit a non ultra speed. + * Watch out for Ultra speeds when ultra is not enabled and + * vice-versa. */ - continue; - } - *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F); + if (!(p->flags & ULTRA_ENABLED) && + (aic7xxx_syncrates[i].rate & ULTRA_SXFR)) + { + /* + * This should only happen if the drive is the first to negotiate + * and chooses a high rate. We'll just move down the table until + * we hit a non ultra speed. + */ + continue; + } + *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F); - /* - * Ensure Ultra mode is set properly for this target. - */ - ultra_enb_addr = ULTRA_ENB; - if ((channel == 'B') || (target > 7)) - { - ultra_enb_addr++; - } - ultra_enb = inb(p->base + ultra_enb_addr); - sxfrctl0 = inb(p->base + SXFRCTL0); - if (aic7xxx_syncrates[i].rate & ULTRA_SXFR) - { - ultra_enb |= 0x01 << (target & 0x07); - sxfrctl0 |= ULTRAEN; - } - else - { - ultra_enb &= ~(0x01 << (target & 0x07)); - sxfrctl0 &= ~ULTRAEN; - } - outb(ultra_enb, p->base + ultra_enb_addr); - outb(sxfrctl0, p->base + SXFRCTL0); + /* + * Ensure Ultra mode is set properly for this target. + */ + ultra_enb_addr = ULTRA_ENB; + if ((channel == 'B') || (target > 7)) + { + ultra_enb_addr++; + } + ultra_enb = inb(p->base + ultra_enb_addr); + sxfrctl0 = inb(p->base + SXFRCTL0); + if (aic7xxx_syncrates[i].rate & ULTRA_SXFR) + { + ultra_enb |= 0x01 << (target & 0x07); + sxfrctl0 |= ULTRAEN; + } + else + { + ultra_enb &= ~(0x01 << (target & 0x07)); + sxfrctl0 &= ~ULTRAEN; + } + outb(ultra_enb, p->base + ultra_enb_addr); + outb(sxfrctl0, p->base + SXFRCTL0); - printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " - "offset %d.\n", p->host_no, target, channel, - aic7xxx_syncrates[i].english, offset); - return; + printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " + "offset %d.\n", p->host_no, target, channel, + aic7xxx_syncrates[i].english, offset); + return; + } } } @@ -1433,10 +1489,10 @@ struct aic7xxx_scb *scbp = NULL; int maxscbs; - scbp = p->free_scbs.head; + scbp = p->scb_link->free_scbs.head; if (scbp != NULL) { - scbq_remove_head(&p->free_scbs); + scbq_remove_head(&p->scb_link->free_scbs); } else { @@ -1459,23 +1515,30 @@ maxscbs = p->maxscbs; else maxscbs = p->maxhscbs; - if (p->numscbs < maxscbs) + if (p->scb_link->numscbs < maxscbs) { - scbp = &(p->scb_array[p->numscbs]); - memset(scbp, 0, sizeof(*scbp)); - scbp->tag = p->numscbs; - if (p->numscbs < p->maxhscbs) - scbp->position = p->numscbs; - else - scbp->position = SCB_LIST_NULL; - p->numscbs++; + int scb_index = p->scb_link->numscbs; + int scb_size = sizeof(struct aic7xxx_scb); + + p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA); + scbp = (p->scb_array[scb_index]); + if (scbp != NULL) + { + memset(scbp, 0, sizeof(*scbp)); + scbp->tag = scb_index; + if (scb_index < p->maxhscbs) + scbp->position = scb_index; + else + scbp->position = SCB_LIST_NULL; + p->scb_link->numscbs++; + } } } } if (scbp != NULL) { #ifdef AIC7XXX_DEBUG - p->activescbs++; + p->scb_link->activescbs++; #endif } return (scbp); @@ -1568,10 +1631,10 @@ } else { - scbq_insert_head(&p->free_scbs, scb); + scbq_insert_head(&p->scb_link->free_scbs, scb); } #ifdef AIC7XXX_DEBUG - p->activescbs--; /* For debugging purposes. */ + p->scb_link->activescbs--; /* For debugging purposes. */ #endif } } @@ -1608,9 +1671,9 @@ struct aic7xxx_scb *scb; int i; - for (i = 0; i < p->numscbs; i++) + for (i = 0; i < p->scb_link->numscbs; i++) { - scb = &(p->scb_array[i]); + scb = (p->scb_array[i]); if (scb->state & SCB_QUEUED_FOR_DONE) { #ifdef AIC7XXX_DEBUG_ABORT @@ -1749,7 +1812,7 @@ { saved_queue[i] = inb(QINFIFO + base); outb(saved_queue[i], SCBPTR + base); - scb = &(p->scb_array[inb(SCB_TAG + base)]); + scb = (p->scb_array[inb(SCB_TAG + base)]); if (aic7xxx_match_scb(scb, target, channel)) { /* @@ -1787,7 +1850,7 @@ while (next != SCB_LIST_NULL) { outb(next, SCBPTR + base); - scb = &(p->scb_array[inb(SCB_TAG + base)]); + scb = (p->scb_array[inb(SCB_TAG + base)]); /* * Select the SCB. */ @@ -1809,9 +1872,9 @@ * for this target that are active. These are other (most likely * tagged) commands that were disconnected when the reset occurred. */ - for (i = 0; i < p->numscbs; i++) + for (i = 0; i < p->scb_link->numscbs; i++) { - scb = &(p->scb_array[i]); + scb = (p->scb_array[i]); if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel)) { /* @@ -1964,9 +2027,10 @@ } /* - * Delay by the bus settle time. + * Cause the mid-level SCSI code to delay any further + * queueing by the bus settle time for us. */ - aic7xxx_delay(AIC7XXX_RESET_DELAY); + p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); /* * Now loop through all the SCBs that have been marked for abortion, @@ -2028,7 +2092,7 @@ aic7xxx_run_waiting_queues(struct aic7xxx_host *p) { struct aic7xxx_scb *scb; - u_char cur_scb; + u_char cur_scb, intstat; u_long base = p->base; long flags; @@ -2040,6 +2104,7 @@ PAUSE_SEQUENCER(p); cur_scb = inb(SCBPTR + base); + intstat = inb(INTSTAT + base); /* * First handle SCBs that are waiting but have been assigned a slot. @@ -2097,7 +2162,7 @@ * Find the in-core SCB for the one we're paging out. */ out_scbi = inb(SCB_TAG + base); - out_scbp = &(p->scb_array[out_scbi]); + out_scbp = (p->scb_array[out_scbi]); /* Do the page out and mark the paged in SCB as active. */ aic7xxx_page_scb(p, out_scbp, scb); @@ -2132,7 +2197,15 @@ } /* Restore old position */ outb(cur_scb, SCBPTR + base); - UNPAUSE_SEQUENCER(p); + + /* + * Guard against unpausing the sequencer if there is an interrupt + * waiting to happen. + */ + if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) + { + UNPAUSE_SEQUENCER(p); + } restore_flags(flags); } @@ -2159,7 +2232,7 @@ unsigned char max_offset, rej_byte; unsigned short target_mask; char channel; - void *addr; + unsigned int addr; /* must be 32 bits */ Scsi_Cmnd *cmd; p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; @@ -2278,7 +2351,7 @@ scb = p->pagedout_ntscbs[index]; } else - scb = &(p->scb_array[arg_1]); + scb = (p->scb_array[arg_1]); if (!(scb->state & SCB_PAGED_OUT)) { @@ -2296,10 +2369,10 @@ * assigned SCB, an SCB that just completed, or the first one * on the disconnected SCB list. */ - if (p->free_scbs.head != NULL) + if (p->scb_link->free_scbs.head != NULL) { - outscb = p->free_scbs.head; - scbq_remove_head(&p->free_scbs); + outscb = p->scb_link->free_scbs.head; + scbq_remove_head(&p->scb_link->free_scbs); scb->position = outscb->position; outscb->position = SCB_LIST_NULL; scbq_insert_head(&p->page_scbs, outscb); @@ -2329,7 +2402,7 @@ { intstat &= ~CMDCMPLT; } - outscb = &(p->scb_array[scb_index]); + outscb = (p->scb_array[scb_index]); if (!(outscb->state & SCB_ACTIVE)) { printk(KERN_WARNING "scsi%d: No command for completed SCB %d " @@ -2370,7 +2443,7 @@ { outb(disc_scb, SCBPTR + base); tag = inb(SCB_TAG + base); - outscb = &(p->scb_array[tag]); + outscb = (p->scb_array[tag]); next = inb(SCB_NEXT + base); if (next != SCB_LIST_NULL) { @@ -2387,7 +2460,7 @@ disc_scb = inb(QINFIFO + base); outb(disc_scb, SCBPTR + base); tag = inb(SCB_TAG + base); - outscb = &p->scb_array[tag]; + outscb = (p->scb_array[tag]); if ((outscb->control & 0x23) != TAG_ENB) { /* @@ -2418,7 +2491,7 @@ outb(saved_queue[queued], SCBPTR + base); tag = inb(SCB_TAG + base); - outscb = &p->scb_array[tag]; + outscb = (p->scb_array[tag]); } scb->position = outscb->position; outscb->position = SCB_LIST_NULL; @@ -2459,7 +2532,7 @@ if ((rej_byte & 0xF0) == 0x20) { scb_index = inb(SCB_TAG + base); - scb = &(p->scb_array[scb_index]); + scb = (p->scb_array[scb_index]); printk(KERN_WARNING "scsi%d: Tagged message received without identify." "Disabling tagged commands for target %d channel %c.\n", p->host_no, scsi_id, channel); @@ -2511,13 +2584,39 @@ outb(scratch, TARG_SCRATCH + base + scratch_offset); outb(scratch, SCSIRATE + base); if ((scratch & 0x0F) == 0) - { /* - * The requested rate was so low that asynchronous transfers - * are faster (not to mention the controller won't support - * them), so we issue a reject to ensure we go to asynchronous - * transfers. - */ - outb(SEND_REJ, RETURN_1 + base); + { + /* + * One of two things happened. Either the device requested + * asynchronous data transfers, or it requested a synchronous + * data transfer rate that was so low that asynchronous + * transfers are faster (not to mention the controller won't + * support them). In both cases the synchronous data transfer + * rate and the offset are set to 0 indicating asynchronous + * transfers. + * + * If the device requested an asynchronous transfer, then + * accept the request. If the device is being forced to + * asynchronous data transfers and this is the first time + * we've seen the request, accept the request. If we've + * already seen the request, then attempt to force + * asynchronous data transfers by rejecting the message. + */ + if ((offset == 0) || (p->sdtr_pending & target_mask)) + { + /* + * Device requested asynchronous transfers or we're + * forcing asynchronous transfers for the first time. + */ + outb(0, RETURN_1 + base); + } + else + { + /* + * The first time in forcing asynchronous transfers + * failed, so we try sending a reject message. + */ + outb(SEND_REJ, RETURN_1 + base); + } } else { @@ -2667,13 +2766,13 @@ */ scb_index = inb(SCB_TAG + base); - scb = &(p->scb_array[scb_index]); + scb = (p->scb_array[scb_index]); outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%x.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned int) scb->cmd); + "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, + intstat, scb_index, scb->state, (unsigned long) scb->cmd); } else { @@ -2693,7 +2792,7 @@ if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE)) { unsigned char tcl; - void *req_buf; + unsigned int req_buf; /* must be 32 bits */ tcl = scb->target_channel_lun; @@ -2707,22 +2806,23 @@ scb->sense_cmd[1] = (cmd->lun << 5); scb->sense_cmd[4] = sizeof(cmd->sense_buffer); - scb->sense_sg.address = (char *) &cmd->sense_buffer; - scb->sense_sg.length = sizeof(cmd->sense_buffer); - req_buf = &scb->sense_sg; + scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer); + scb->sg_list[0].length = sizeof(cmd->sense_buffer); + req_buf = VIRT_TO_BUS(&scb->sg_list[0]); cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); scb->control = scb->control & DISCENB; scb->target_channel_lun = tcl; - addr = scb->sense_cmd; + addr = VIRT_TO_BUS(scb->sense_cmd); scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); scb->SG_segment_count = 1; memcpy(scb->SG_list_pointer, &req_buf, - sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sense_sg.length; - memcpy(scb->data_pointer, &(scb->sense_sg.address), 4); + sizeof(scb->SG_list_pointer)); + scb->data_count = scb->sg_list[0].length; + memcpy(scb->data_pointer, &(scb->sg_list[0].address), + sizeof(scb->data_pointer)); aic7xxx_putscb(p, scb); /* @@ -2749,7 +2849,17 @@ p->host_no, scb->target_channel_lun); if (!aic7xxx_error(cmd)) { - aic7xxx_error(cmd) = DID_BUS_BUSY; + /* The error code here used to be DID_BUS_BUSY, + * but after extensive testing, it has been determined + * that a DID_BUS_BUSY return is a waste of time. If + * the problem is something that will go away, then it + * will, if it isn't, then you don't want the endless + * looping that you get with a DID_BUS_BUSY. Better + * to be on the safe side and specify an error condition + * that will eventually lead to a reset or abort of some + * sort instead of an endless loop. + */ + aic7xxx_error(cmd) = DID_RETRY_COMMAND; } break; @@ -2773,12 +2883,12 @@ case RESIDUAL: scb_index = inb(SCB_TAG + base); - scb = &(p->scb_array[scb_index]); + scb = (p->scb_array[scb_index]); if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%x.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned int) scb->cmd); + "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, + intstat, scb_index, scb->state, (unsigned long) scb->cmd); } else { @@ -2816,12 +2926,12 @@ case ABORT_TAG: scb_index = inb(SCB_TAG + base); - scb = &(p->scb_array[scb_index]); + scb = (p->scb_array[scb_index]); if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%x\n", p->host_no, - intstat, scb_index, scb->state, (unsigned int) scb->cmd); + "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no, + intstat, scb_index, scb->state, (unsigned long) scb->cmd); } else { @@ -2841,12 +2951,12 @@ case AWAITING_MSG: scb_index = inb(SCB_TAG + base); - scb = &(p->scb_array[scb_index]); + scb = (p->scb_array[scb_index]); if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%x.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned int) scb->cmd); + "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, + intstat, scb_index, scb->state, (unsigned long) scb->cmd); } else { @@ -2874,7 +2984,7 @@ case IMMEDDONE: scb_index = inb(SCB_TAG + base); - scb = &(p->scb_array[scb_index]); + scb = (p->scb_array[scb_index]); #ifdef AIC7XXX_DEBUG_ABORT printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n", scsi_id, scb_index, scb->state); @@ -2911,7 +3021,7 @@ { unsigned int overrun; - scb = &p->scb_array[inb(base + SCB_TAG)]; + scb = (p->scb_array[inb(base + SCB_TAG)]); overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) | (inb(base + STCNT2) << 16); overrun =0x00FFFFFF - overrun; @@ -2953,7 +3063,7 @@ } scb_index = inb(SCB_TAG + base); - scb = &(p->scb_array[scb_index]); + scb = (p->scb_array[scb_index]); if (status & SCSIRSTI) { PAUSE_SEQUENCER(p); @@ -3103,13 +3213,13 @@ do { complete = inb(QOUTFIFO + base); - scb = &(p->scb_array[complete]); + scb = (p->scb_array[complete]); if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n" - " QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%x, " + " QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, " "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base), - inb(QINCNT + base), scb->state, (unsigned int) scb->cmd, + inb(QINCNT + base), scb->state, (unsigned long) scb->cmd, scb->position); outb(CLRCMDINT, CLRINT + base); continue; @@ -3201,13 +3311,12 @@ { Scsi_Device *device = scsi_devs; int tq_depth = 2; + struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata; #ifdef AIC7XXX_CMDS_PER_LUN tq_depth = AIC7XXX_CMDS_PER_LUN; #else { - struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata; - if (p->maxhscbs <= 4) { tq_depth = 4; /* Not many SCBs to work with. */ @@ -3227,14 +3336,26 @@ #ifdef AIC7XXX_TAGGED_QUEUEING if (device->tagged_supported) { - device->queue_depth = tq_depth; - if (device->tagged_queue == 0) + unsigned short target_mask = (1 << device->id) | device->channel; + + if (!(p->discenable & target_mask)) { - printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, " - "channel %d, LUN %d, queue depth %d.\n", host->host_no, - device->id, device->channel, device->lun, device->queue_depth); - device->tagged_queue = 1; - device->current_tag = SCB_LIST_NULL; + printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable " + "tagged queueing for target %d, channel %d, LUN %d.\n", + host->host_no, device->id, device->channel, device->lun); + } + else + { + device->queue_depth = tq_depth; + if (device->tagged_queue == 0) + { + printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, " + "channel %d, LUN %d, queue depth %d.\n", host->host_no, + device->id, device->channel, device->lun, + device->queue_depth); + device->tagged_queue = 1; + device->current_tag = SCB_LIST_NULL; + } } } #endif @@ -3473,7 +3594,7 @@ * Reads the serial EEPROM and returns 1 if successful and 0 if * not successful. * - * The instruction set of the 93C46 chip is as follows: + * The instruction set of the 93C46/56/66 chips is as follows: * * Start OP * Function Bit Code Address Data Description @@ -3489,6 +3610,8 @@ * EWDS 1 00 00XXXX Disables all programming * instructions * *Note: A value of X for address is a don't care condition. + * *Note: The 93C56 and 93C66 have 8 address bits. + * * * The 93C46 has a four wire interface: clock, chip select, data in, and * data out. In order to perform one of the above functions, you need @@ -3515,7 +3638,8 @@ * *-F*************************************************************************/ static int -read_seeprom(int base, int offset, struct seeprom_config *sc) +read_seeprom(int base, int offset, struct seeprom_config *sc, + seeprom_chip_type chip) { int i = 0, k; unsigned long timeout; @@ -3583,7 +3707,7 @@ /* * Send the 6 bit address (MSB first, LSB last). */ - for (i = 5; i >= 0; i--) + for (i = ((int) chip - 1); i >= 0; i--) { temp = k + offset; temp = (temp >> i) & 1; /* Mask out all but lower bit. */ @@ -3925,6 +4049,7 @@ break; case AIC_7860: + case AIC_7861: case AIC_7880: case AIC_7881: case AIC_7882: @@ -3934,7 +4059,7 @@ * Remember if Ultra was enabled in case there is no SEEPROM. * Fall through to the rest of the AIC_78xx code. */ - if (inb(SXFRCTL0 + base) & ULTRAEN) + if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra) config->flags |= ULTRA_ENABLED; case AIC_7850: @@ -3957,7 +4082,16 @@ config->parity = AIC_ENABLED; printk(KERN_INFO "aic7xxx: Reading SEEPROM..."); - have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), &sc); + if ((config->type == AIC_7873) || (config->type == AIC_7883)) + { + have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), + &sc, c56_66); + } + else + { + have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), + &sc, c46); + } if (!have_seeprom) { for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++) @@ -4142,25 +4276,6 @@ debug_config(config); /* - * Before registry, make sure that the offsets of the - * struct scatterlist are what the sequencer will expect, - * otherwise disable scatter-gather altogether until someone - * can fix it. This is important since the sequencer will - * DMA elements of the SG array in while executing commands. - */ - if (template->sg_tablesize != SG_NONE) - { - struct scatterlist sg; - - if (SG_STRUCT_CHECK(sg)) - { - printk(KERN_WARNING "aic7xxx: Warning - Kernel scatter-gather structures " - "changed, disabling it.\n"); - template->sg_tablesize = SG_NONE; - } - } - - /* * Register each "host" and fill in the returned Scsi_Host * structure as best we can. Some of the parameters aren't * really relevant for bus types beyond ISA, and none of the @@ -4175,7 +4290,7 @@ host->this_id = config->scsi_id; host->io_port = config->base; host->n_io_port = 0xFF; - host->base = (char *)config->mbase; + host->base = (unsigned char *)config->mbase; host->irq = config->irq; if (config->bus_type == AIC_WIDE) { @@ -4195,18 +4310,26 @@ p->maxscbs = config->maxscbs; p->maxhscbs = config->maxhscbs; p->qcntmask = config->qcntmask; - p->numscbs = 0; p->mbase = (char *)config->mbase; p->type = config->type; p->chip_type = config->chip_type; p->flags = config->flags; p->chan_num = config->chan_num; + p->scb_link = &(p->scb_usage); +#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS) + if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883))) + { + shared_3985_scbs = &(p->scb_usage); + p->scb_link = &(p->scb_usage); + } +#endif + p->scb_link->numscbs = 0; p->bus_type = config->bus_type; p->seeprom = sc; p->next = NULL; p->completeq.head = NULL; p->completeq.tail = NULL; - scbq_init(&p->free_scbs); + scbq_init(&p->scb_link->free_scbs); scbq_init(&p->page_scbs); scbq_init(&p->waiting_scbs); scbq_init(&p->assigned_scbs); @@ -4289,7 +4412,7 @@ outb(config->scsi_id_b, SCSIID + base); scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL); outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if 1 +#if EXPERIMENTAL_FLAGS outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); #else outb(ENSELTIMO, SIMODE1 + base); @@ -4311,7 +4434,7 @@ outb(config->scsi_id, SCSIID + base); scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL); outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if 1 +#if EXPERIMENTAL_FLAGS outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); #else outb(ENSELTIMO, SIMODE1 + base); @@ -4567,7 +4690,7 @@ * a NULL entry to indicate that no prior hosts have * been found/registered for that IRQ. */ - for (i = 0; i <= MAXIRQ; i++) + for (i = 0; i <= NUMBER(aic7xxx_boards); i++) { aic7xxx_boards[i] = NULL; } @@ -4643,6 +4766,7 @@ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x}, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x}, @@ -4690,6 +4814,7 @@ case AIC_7850: case AIC_7855: case AIC_7860: + case AIC_7861: config.bios = AIC_DISABLED; config.flags |= USE_DEFAULTS; config.bus_speed = DFTHRSH_100; @@ -4703,7 +4828,7 @@ case AIC_7873: /* 3985 */ case AIC_7883: /* 3985-Ultra */ - config.chan_num = number_of_3985s & 0x3; /* Has 3 controllers */ + config.chan_num = number_of_3985s; /* Has 3 controllers */ number_of_3985s++; if (number_of_3985s == 3) { @@ -4773,7 +4898,12 @@ config.high_term = AIC_UNKNOWN; if (aic7xxx_extended) config.flags |= EXTENDED_TRANSLATION; +#ifdef AIC7XXX_SHARE_SCBs if (devconfig & RAMPSM) +#else + if ((devconfig & RAMPSM) && (config.type != AIC_7873) && + (config.type != AIC_7883)) +#endif { /* * External SRAM present. The probe will walk the SCBs to see @@ -4827,9 +4957,8 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, struct aic7xxx_scb *scb) { - void *addr; + unsigned int addr; /* must be 32 bits */ unsigned short mask; - struct scatterlist *sg; mask = (0x01 << TARGET_INDEX(cmd)); /* @@ -4900,27 +5029,43 @@ * XXX - this relies on the host data being stored in a * little-endian format. */ - addr = cmd->cmnd; + addr = VIRT_TO_BUS(cmd->cmnd); scb->SCSI_cmd_length = cmd->cmd_len; memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); if (cmd->use_sg) { + struct scatterlist *sg; /* Must be mid-level SCSI code scatterlist */ + + /* + * We must build an SG list in adapter format, as the kernel's SG list + * cannot be used directly because of data field size (__alpha__) + * differences and the kernel SG list uses virtual addresses where + * we need physical addresses. + */ + int i; + + sg = (struct scatterlist *)cmd->request_buffer; + for (i = 0; i < cmd->use_sg; i++) + { + scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address); + scb->sg_list[i].length = (unsigned int) sg[i].length; + } scb->SG_segment_count = cmd->use_sg; - memcpy(scb->SG_list_pointer, &cmd->request_buffer, - sizeof(scb->SG_list_pointer)); - memcpy(&sg, &cmd->request_buffer, sizeof(sg)); - memcpy(scb->data_pointer, &(sg[0].address), sizeof(scb->data_pointer)); - scb->data_count = sg[0].length; + addr = VIRT_TO_BUS(scb->sg_list); + memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); + memcpy(scb->data_pointer, &(scb->sg_list[0].address), + sizeof(scb->data_pointer)); + scb->data_count = scb->sg_list[0].length; #if 0 - debug("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n", + printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n", cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count); #endif } else { #if 0 - debug("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n", + printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n", (unsigned long) cmd->request_buffer, cmd->request_bufflen); #endif if (cmd->request_bufflen == 0) @@ -4938,12 +5083,13 @@ else { scb->SG_segment_count = 1; - scb->sg.address = (char *) cmd->request_buffer; - scb->sg.length = cmd->request_bufflen; - addr = &scb->sg; + scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer); + scb->sg_list[0].length = cmd->request_bufflen; + addr = VIRT_TO_BUS(&scb->sg_list[0]); memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sg.length; - memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer)); + scb->data_count = scb->sg_list[0].length; + addr = VIRT_TO_BUS(cmd->request_buffer); + memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer)); } } } @@ -4958,12 +5104,17 @@ int aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) { - long flags; + long processor_flags; struct aic7xxx_host *p; struct aic7xxx_scb *scb; - u_char curscb; + u_char curscb, intstat; p = (struct aic7xxx_host *) cmd->host->hostdata; + if (p->host != cmd->host) + { + printk(KERN_INFO "scsi%d: Internal host structure != scsi.c host " + "structure.\n", p->host_no); + } /* * Check to see if channel was scanned. @@ -4983,19 +5134,28 @@ } #if 0 - debug("aic7xxx: (queue) cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n", + printk("aic7xxx: (queue) cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n", cmd->cmnd[0], cmd->cmd_len, cmd->target, cmd->channel, cmd->lun & 0x07); #endif /* - * This is a critical section, since we don't want the - * interrupt routine mucking with the host data or the - * card. Since the kernel documentation is vague on - * whether or not we are in a cli/sti pair already, save - * the flags to be on the safe side. + * This is a critical section, since we don't want the interrupt + * routine mucking with the host data or the card. For this reason + * it is nice to know that this function can only be called in one + * of two ways from scsi.c First, as part of a routine queue command, + * in which case, the irq for our card is disabled before this + * function is called. This doesn't help us if there is more than + * one card using more than one IRQ in our system, therefore, we + * should disable all interrupts on these grounds alone. Second, + * this can be called as part of the scsi_done routine, in which case + * we are in the aic7xxx_isr routine already and interrupts are + * disabled, therefore we should saveflags first, then disable the + * interrupts, do our work, then restore the CPU flags. If it weren't + * for the possibility of more than one card using more than one IRQ + * in our system, we wouldn't have to touch the interrupt flags at all. */ - save_flags(flags); + save_flags(processor_flags); cli(); scb = aic7xxx_allocate_scb(p); @@ -5018,7 +5178,7 @@ aic7xxx_buildscb(p, cmd, scb); #if 0 - if (scb != &p->scb_array[scb->position]) + if (scb != (p->scb_array[scb->position])) { printk("aic7xxx: (queue) Address of SCB by position does not match SCB " "address.\n"); @@ -5057,6 +5217,7 @@ * XXX - should the interrupts be left on while doing this? */ PAUSE_SEQUENCER(p); + intstat = inb(INTSTAT + p->base); /* * Save the SCB pointer and put our own pointer in - this @@ -5071,7 +5232,14 @@ outb(scb->position, QINFIFO + p->base); scb->state |= SCB_ACTIVE; - UNPAUSE_SEQUENCER(p); + /* + * Guard against unpausing the sequencer if there is an interrupt + * waiting to happen. + */ + if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) + { + UNPAUSE_SEQUENCER(p); + } } } else @@ -5088,7 +5256,7 @@ printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n", (long) cmd, (long) scb->cmd, scb->position); #endif; - restore_flags(flags); + restore_flags(processor_flags); } return (0); } @@ -5108,21 +5276,17 @@ aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { struct aic7xxx_scb *scb; - long flags; unsigned char bus_state; int base, result = -1; char channel; - scb = &(p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_array[aic7xxx_position(cmd)]); base = p->base; channel = scb->target_channel_lun & SELBUSB ? 'B': 'A'; if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS)) { - save_flags(flags); - cli(); - if (scb->state & SCB_IN_PROGRESS) { /* @@ -5203,7 +5367,7 @@ * too much time, so we try the bus device reset there first. */ active_scb = inb(SCBPTR + base); - active_scbp = &(p->scb_array[inb(SCB_TAG + base)]); + active_scbp = (p->scb_array[inb(SCB_TAG + base)]); control = inb(SCB_CONTROL + base); /* @@ -5303,7 +5467,11 @@ } } } - restore_flags(flags); + } + /* Make sure the sequencer is unpaused upon return. */ + if (result == -1) + { + UNPAUSE_SEQUENCER(p); } return (result); } @@ -5324,7 +5492,7 @@ int base, result; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = &(p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_array[aic7xxx_position(cmd)]); base = p->base; #ifdef AIC7XXX_DEBUG_ABORT @@ -5369,9 +5537,10 @@ struct aic7xxx_host *p; int base, found, tindex, min_target, max_target, result = -1; char channel = 'A'; + unsigned long processor_flags; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = &(p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_array[aic7xxx_position(cmd)]); base = p->base; channel = cmd->channel ? 'B': 'A'; tindex = (cmd->channel << 4) | cmd->target; @@ -5380,10 +5549,20 @@ printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel); #endif + /* + * This routine is called by scsi.c, in which case the interrupts + * very well may be on when we are called. As such, we need to save + * the flags to be sure, then turn interrupts off, and then call our + * various method funtions which all assume interrupts are off. + */ + save_flags(processor_flags); + cli(); + if (scb->cmd != cmd) scb = NULL; - if (!(flags & SCSI_RESET_SUGGEST_HOST_RESET) && (scb != NULL)) + if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) + && (scb != NULL)) { /* * Attempt a bus device reset if commands have completed successfully @@ -5439,7 +5618,8 @@ /* * The bus device reset failed; try resetting the channel. */ - if (flags & SCSI_RESET_ASYNCHRONOUS) + if (!(flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET)) + && (flags & SCSI_RESET_ASYNCHRONOUS)) { if (scb == NULL) { @@ -5458,6 +5638,10 @@ if (result == -1) { + /* + * The reset channel function assumes that the sequencer is paused. + */ + PAUSE_SEQUENCER(p); found = aic7xxx_reset_channel(p, channel, TRUE); /* @@ -5506,6 +5690,7 @@ result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET; } } + restore_flags(processor_flags); return (result); } diff -ur --new-file old/linux/drivers/scsi/aic7xxx.seq new/linux/drivers/scsi/aic7xxx.seq --- old/linux/drivers/scsi/aic7xxx.seq Sat Aug 10 09:44:18 1996 +++ new/linux/drivers/scsi/aic7xxx.seq Sun Oct 13 11:42:28 1996 @@ -28,7 +28,7 @@ * *-M*************************************************************************/ -VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 3.1 1996/07/23 03:37:26 deang Exp $" +VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $" #ifdef linux #include "aic7xxx_reg.h" @@ -402,18 +402,8 @@ test DFCNTRL,HDMAENACK jnz dma_finish2 /* - * Copy data from FIFO into SCB data pointer and data count. This assumes - * that the struct scatterlist has this structure (this and sizeof(struct - * scatterlist) == 12 are asserted in aic7xxx.c for the Linux driver): - * - * struct scatterlist { - * char *address; four bytes, little-endian order - * ... four bytes, ignored - * unsigned short length; two bytes, little-endian order - * } - * - * - * In FreeBSD, the scatter list entry is only 8 bytes. + * Copy data from FIFO into SCB data pointer and data count. In + * both FreeBSD and Linux, the scatter list entry is 8 bytes. * * struct ahc_dma_seg { * physaddr addr; four bytes, little-endian order @@ -425,16 +415,6 @@ mov HADDR1,DFDAT mov HADDR2,DFDAT mov HADDR3,DFDAT -/* - * For Linux, we must throw away four bytes since there is a 32bit gap - * in the middle of a struct scatterlist. - */ -#ifdef linux - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT -#endif mov HCNT0,DFDAT mov HCNT1,DFDAT mov HCNT2,DFDAT diff -ur --new-file old/linux/drivers/scsi/aic7xxx_proc.c new/linux/drivers/scsi/aic7xxx_proc.c --- old/linux/drivers/scsi/aic7xxx_proc.c Sat Aug 10 09:44:18 1996 +++ new/linux/drivers/scsi/aic7xxx_proc.c Sun Oct 13 11:42:28 1996 @@ -24,7 +24,7 @@ * * Dean W. Gehnert, deang@teleport.com, 05/01/96 * - * $Id: aic7xxx_proc.c,v 3.2 1996/07/23 03:37:26 deang Exp $ + * $Id: aic7xxx_proc.c,v 4.0 1996/10/13 08:23:42 deang Exp $ *-M*************************************************************************/ #define BLS buffer + len + size @@ -172,7 +172,7 @@ size += sprintf(BLS, " Base IO: %#.4x\n", p->base); size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); size += sprintf(BLS, " SCBs: Used %d, HW %d, Page %d\n", - p->numscbs, p->maxhscbs, p->maxscbs); + p->scb_link->numscbs, p->maxhscbs, p->maxscbs); size += sprintf(BLS, " Interrupts: %d", p->isr_count); if (p->chip_type == AIC_777x) { diff -ur --new-file old/linux/drivers/scsi/aic7xxx_reg.h new/linux/drivers/scsi/aic7xxx_reg.h --- old/linux/drivers/scsi/aic7xxx_reg.h Sat Aug 10 09:44:18 1996 +++ new/linux/drivers/scsi/aic7xxx_reg.h Sun Oct 13 11:42:28 1996 @@ -20,7 +20,7 @@ * * This version corresponds to version 1.12 of FreeBSDs aic7xxx_reg.h * - * $Id: aic7xxx_reg.h,v 3.1 1996/07/23 03:37:26 deang Exp $ + * $Id: aic7xxx_reg.h,v 4.0 1996/10/13 08:23:42 deang Exp $ *-M*************************************************************************/ /* @@ -558,11 +558,7 @@ #define SCB_NEXT 0x0ba #define SCB_PREV 0x0bb -#ifdef linux -#define SG_SIZEOF 0x0c /* sizeof(struct scatterlist) */ -#else #define SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */ -#endif /* --------------------- AHA-2840-only definitions -------------------- */ diff -ur --new-file old/linux/drivers/scsi/eata_dma.c new/linux/drivers/scsi/eata_dma.c --- old/linux/drivers/scsi/eata_dma.c Wed Aug 14 09:21:03 1996 +++ new/linux/drivers/scsi/eata_dma.c Mon Oct 28 23:30:43 1996 @@ -58,7 +58,7 @@ * Jagdis who did a lot of testing and found quite a number * * of bugs during the development. * ************************************************************ - * last change: 96/08/13 OS: Linux 2.0.12 * + * last change: 96/10/21 OS: Linux 2.0.23 * ************************************************************/ /* Look in eata_dma.h for configuration and revision information */ @@ -295,12 +295,9 @@ sp->EOC = FALSE; /* Clean out this flag */ if (ccb->status == LOCKED || ccb->status == RESET) { - ccb->status = FREE; - eata_stat = inb(base + HA_RSTATUS); - printk("eata_dma: int_handler, reseted command returned," - " freeing reseted queueslot\n"); + printk("eata_dma: int_handler, reseted command pid %ld returned" + "\n", cmd->pid); DBG(DBG_INTR && DBG_DELAY, DELAY(1)); - break; } eata_stat = inb(base + HA_RSTATUS); @@ -467,23 +464,33 @@ struct Scsi_Host *sh; struct eata_ccb *ccb; struct scatterlist *sl; + save_flags(flags); cli(); + +#if 0 + for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) { + if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) { + printk("eata_dma: scsi%d interrupt pending in eata_queue.\n" + " Calling interrupt handler.\n", sh->host_no); + eata_int_handler(sh->irq, 0, 0); + } + } +#endif queue_counter++; hd = HD(cmd); sh = cmd->host; -#if 1 if (cmd->cmnd[0] == REQUEST_SENSE && cmd->sense_buffer[0] != 0) { DBG(DBG_REQSENSE, printk(KERN_DEBUG "Tried to REQUEST SENSE\n")); cmd->result = DID_OK << 16; done(cmd); + return(0); } -#endif /* check for free slot */ for (y = hd->last_ccb + 1, x = 0; x < sh->can_queue; x++, y++) { @@ -678,7 +685,6 @@ " reason %x\n", cmd->pid, cmd->target, cmd->lun, cmd->abort_reason)); - /* Some interrupt controllers seem to loose interrupts */ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) { if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) { printk("eata_dma: scsi%d interrupt pending in eata_reset.\n" @@ -686,6 +692,7 @@ eata_int_handler(sh->irq, 0, 0); } } + if (HD(cmd)->state == RESET) { printk("eata_reset: exit, already in reset.\n"); restore_flags(flags); @@ -766,9 +773,9 @@ restore_flags(flags); if (success) { - DBG(DBG_ABNORM, printk("eata_reset: exit, success.\n")); + DBG(DBG_ABNORM, printk("eata_reset: exit, pending.\n")); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); - return (SCSI_RESET_SUCCESS); + return (SCSI_RESET_PENDING); } else { DBG(DBG_ABNORM, printk("eata_reset: exit, wakeup.\n")); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); @@ -852,8 +859,9 @@ device->queue_depth = (TYPE_OTHER_QUEUE * factor) / 10; break; } - } else /* ISA forces us to limit the QS because of bounce buffers*/ - device->queue_depth = 2; /* I know this is cruel */ + } else /* ISA forces us to limit the queue depth because of the + * bounce buffer memory overhead. I know this is cruel */ + device->queue_depth = 2; /* * It showed that we need to set an upper limit of commands @@ -863,6 +871,8 @@ */ if(device->queue_depth > UPPER_DEVICE_QUEUE_LIMIT) device->queue_depth = UPPER_DEVICE_QUEUE_LIMIT; + if(device->queue_depth == 0) + device->queue_depth = 1; printk(KERN_INFO "scsi%d: queue depth for target %d on channel %d " "set to %d\n", host->host_no, device->id, device->channel, diff -ur --new-file old/linux/drivers/scsi/eata_dma.h new/linux/drivers/scsi/eata_dma.h --- old/linux/drivers/scsi/eata_dma.h Wed Aug 14 09:21:03 1996 +++ new/linux/drivers/scsi/eata_dma.h Mon Oct 28 23:30:43 1996 @@ -4,7 +4,7 @@ * mike@i-Connect.Net * * neuffer@mail.uni-mainz.de * ********************************************************* -* last change: 96/08/14 * +* last change: 96/10/14 * ********************************************************/ #ifndef _EATA_DMA_H @@ -17,7 +17,7 @@ #define VER_MAJOR 2 #define VER_MINOR 5 -#define VER_SUB "9a" +#define VER_SUB "9b" /************************************************************************ diff -ur --new-file old/linux/drivers/scsi/eata_generic.h new/linux/drivers/scsi/eata_generic.h --- old/linux/drivers/scsi/eata_generic.h Wed Aug 14 09:21:03 1996 +++ new/linux/drivers/scsi/eata_generic.h Mon Oct 28 23:30:43 1996 @@ -68,7 +68,7 @@ #define SG_SIZE 64 #define SG_SIZE_BIG 252 /* max. 8096 elements, 64k */ -#define UPPER_DEVICE_QUEUE_LIMIT 24 /* The limit we have to set for the +#define UPPER_DEVICE_QUEUE_LIMIT 64 /* The limit we have to set for the * device queue to keep the broken * midlevel SCSI code from producing * bogus timeouts diff -ur --new-file old/linux/drivers/scsi/fdomain.c new/linux/drivers/scsi/fdomain.c --- old/linux/drivers/scsi/fdomain.c Fri Aug 9 09:43:24 1996 +++ new/linux/drivers/scsi/fdomain.c Sat Nov 2 15:41:06 1996 @@ -1,6 +1,6 @@ /* fdomain.c -- Future Domain TMC-16x0 SCSI driver * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu - * Revised: Thu Aug 8 14:58:51 1996 by r.faith@ieee.org + * Revised: Sat Nov 2 09:27:47 1996 by root@cs.unc.edu * Author: Rickard E. Faith, faith@cs.unc.edu * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith * @@ -461,7 +461,7 @@ { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 }, { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 }, { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 }, - { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, 0, 1 }, + { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 }, /* This next signature may not be a 3.5 bios */ { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 }, { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 }, diff -ur --new-file old/linux/drivers/scsi/g_NCR5380.h new/linux/drivers/scsi/g_NCR5380.h --- old/linux/drivers/scsi/g_NCR5380.h Mon Apr 29 16:14:19 1996 +++ new/linux/drivers/scsi/g_NCR5380.h Fri Sep 20 16:00:34 1996 @@ -32,6 +32,8 @@ #ifndef GENERIC_NCR5380_H #define GENERIC_NCR5380_H +#include + #define GENERIC_NCR5380_PUBLIC_RELEASE 1 #ifdef NCR53C400 diff -ur --new-file old/linux/drivers/scsi/ncr53c8xx.c new/linux/drivers/scsi/ncr53c8xx.c --- old/linux/drivers/scsi/ncr53c8xx.c Mon Aug 12 10:45:46 1996 +++ new/linux/drivers/scsi/ncr53c8xx.c Mon Sep 30 06:53:59 1996 @@ -40,7 +40,7 @@ */ /* -** 21 July 1996, version 1.12b +** 30 August 1996, version 1.12c ** ** Supported SCSI-II features: ** Synchronous negotiation @@ -55,6 +55,8 @@ ** 53C815 (~53C810 with on board rom BIOS) ** 53C820 (Wide, NCR BIOS in flash bios required) ** 53C825 (Wide, ~53C820 with on board rom BIOS) +** 53C860 (not yet tested) +** 53C875 (not yet tested) ** ** Other features: ** Memory mapped IO (linux-1.3.X only) @@ -545,7 +547,7 @@ #define INB_OFF(o) IOM_INB_OFF(o) #define INW(r) IOM_INW(r) #define INL(r) IOM_INL(r) -#define INL_OFF(r) IOM_INL_OFF(o) +#define INL_OFF(o) IOM_INL_OFF(o) #define OUTB(r, val) IOM_OUTB(r, val) #define OUTW(r, val) IOM_OUTW(r, val) @@ -1492,7 +1494,7 @@ static void ncr_complete (ncb_p np, ccb_p cp); static void ncr_exception (ncb_p np); static void ncr_free_ccb (ncb_p np, ccb_p cp); -static void ncr_getclock (ncb_p np); +static void ncr_getclock (ncb_p np, u_char scntl3); static ccb_p ncr_get_ccb (ncb_p np, u_long t,u_long l); static void ncr_init (ncb_p np, char * msg, u_long code); static int ncr_intr (ncb_p np); @@ -3540,7 +3542,7 @@ ** Find the right value for scntl3. */ - ncr_getclock (np); + ncr_getclock (np, INB(nc_scntl3)); /* ** Reset chip. @@ -3585,7 +3587,7 @@ # ifdef SCSI_NCR_SHARE_IRQ printf("%s: requesting shared irq %d (dev_id=0x%lx)\n", ncr_name(np), irq, (u_long) np); - if (request_irq(irq, ncr53c8xx_intr, SA_SHIRQ, "53c8xx", np)) { + if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) { # else if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) { # endif @@ -4323,6 +4325,7 @@ lcb_p lp; int target, lun; int i; + u_char scntl3; printf("%s: releasing host resources\n", ncr_name(np)); @@ -4369,12 +4372,15 @@ /* ** Reset NCR chip + ** Preserve scntl3 for automatic clock detection. */ printf("%s: resetting chip\n", ncr_name(np)); + scntl3 = INB (nc_scntl3); OUTB (nc_istat, SRST); DELAY (1000); OUTB (nc_istat, 0 ); + OUTB (nc_scntl3, scntl3); /* ** Release Memory mapped IO region and IO mapped region @@ -4784,6 +4790,11 @@ ncr_wakeup (np, code); /* + ** Remove Reset, abort ... + */ + OUTB (nc_istat, 0 ); + + /* ** Init chip. */ /** NCR53C810 **/ @@ -4816,8 +4827,8 @@ if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) || ChipDevice == PCI_DEVICE_ID_NCR_53C875) { OUTB(nc_dmode, 0xc0); /* Set 16-transfer burst */ - OUTB(nc_ctest5, 0x04); /* Set DMA FIFO to 88 */ #if 0 + OUTB(nc_ctest5, 0x04); /* Set DMA FIFO to 88 */ OUTB(nc_ctest5, 0x24); /* Set DMA FIFO to 536 */ OUTB(nc_dmode, 0x40); /* Set 64-transfer burst */ OUTB(nc_ctest3, 0x01); /* Set write and invalidate */ @@ -4832,8 +4843,6 @@ burstlen = 0xc0; #endif - OUTB (nc_istat, 0 ); /* Remove Reset, abort ... */ - #ifdef SCSI_NCR_DISABLE_PARITY_CHECK OUTB (nc_scntl0, 0xc0 ); /* full arb., (no parity) */ #else @@ -6278,9 +6287,18 @@ /* ** Check against controller limits. */ - fak = (4ul * per - 1) / np->ns_sync - 3; - if (ofs && (fak>7)) {chg = 1; ofs = 0;} - if (!ofs) fak=7; + if (ofs != 0) { + fak = (4ul * per - 1) / np->ns_sync - 3; + if (fak>7) { + chg = 1; + ofs = 0; + } + } + if (ofs == 0) { + fak = 7; + per = 0; + tp->minsync = 0; + } if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->cmd); @@ -7311,8 +7329,9 @@ #endif /* NCR_CLOCK */ -static void ncr_getclock (ncb_p np) +static void ncr_getclock (ncb_p np, u_char scntl3) { +#if 0 u_char tbl[5] = {6,2,3,4,6}; u_char f; u_char ns_clock = (1000/NCR_CLOCK); @@ -7335,6 +7354,25 @@ if (DEBUG_FLAGS & DEBUG_TIMING) printf ("%s: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n", ncr_name (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3); +#else + /* + * For now just preserve the BIOS setting ... + */ + + if ((scntl3 & 7) < 3) { + printf ("%s: assuming 40MHz clock\n", ncr_name(np)); + scntl3 = 3; /* assume 40MHz if no value supplied by BIOS */ + } + + np->ns_sync = 25; + np->ns_async = 50; + np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7); + + if (bootverbose) { + printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n", + ncr_name(np), scntl3, np->rv_scntl3); + } +#endif } /*===================== LINUX ENTRY POINTS SECTION ==========================*/ @@ -7601,7 +7639,6 @@ #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70) # ifdef SCSI_NCR_SHARE_IRQ if (dev_id == &host_data->ncb_data) - ncr_intr(&host_data->ncb_data); # endif #endif ncr_intr(&host_data->ncb_data); diff -ur --new-file old/linux/drivers/scsi/ncr53c8xx.h new/linux/drivers/scsi/ncr53c8xx.h --- old/linux/drivers/scsi/ncr53c8xx.h Tue Jul 23 09:26:41 1996 +++ new/linux/drivers/scsi/ncr53c8xx.h Sun Sep 1 08:15:33 1996 @@ -196,7 +196,7 @@ #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) -#define NCR53C8XX {NULL,NULL,NULL,NULL,"ncr53c8xx (rel 1.12b)", ncr53c8xx_detect,\ +#define NCR53C8XX {NULL,NULL,NULL,NULL,"ncr53c8xx (rel 1.12c)", ncr53c8xx_detect,\ ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \ ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \ NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\ @@ -207,7 +207,7 @@ #else -#define NCR53C8XX {NULL, NULL, "ncr53c8xx (rel 1.12b)", ncr53c8xx_detect,\ +#define NCR53C8XX {NULL, NULL, "ncr53c8xx (rel 1.12c)", ncr53c8xx_detect,\ ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \ ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \ NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\ diff -ur --new-file old/linux/drivers/scsi/scsi.c new/linux/drivers/scsi/scsi.c --- old/linux/drivers/scsi/scsi.c Thu Aug 1 14:43:04 1996 +++ new/linux/drivers/scsi/scsi.c Wed Oct 30 02:12:04 1996 @@ -17,8 +17,8 @@ * add scatter-gather, multiple outstanding request, and other * enhancements. * - * Native multichannel and wide scsi support added - * by Michael Neuffer neuffer@goofy.zdv.uni-mainz.de + * Native multichannel, wide scsi, /proc/scsi and hot plugging + * support added by Michael Neuffer * * Added request_module("scsi_hostadapter") for kerneld: * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules) @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -275,6 +276,7 @@ */ {"SONY","CD-ROM CDU-8001","*", BLIST_BORKEN}, {"TEXEL","CD-ROM","1.06", BLIST_BORKEN}, +{"IOMEGA","Io20S *F","*", BLIST_KEY}, {"INSITE","Floptical F*8I","*", BLIST_KEY}, {"INSITE","I325VM","*", BLIST_KEY}, {"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN}, @@ -282,6 +284,7 @@ {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN}, {"CANON","IPUBJD","*", BLIST_SPARSELUN}, +{"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN}, /* * Must be at end of list... */ @@ -1214,17 +1217,19 @@ */ timeout = host->last_reset + MIN_RESET_DELAY; if (jiffies < timeout) { - /* - * NOTE: This may be executed from within an interrupt - * handler! This is bad, but for now, it'll do. The irq - * level of the interrupt handler has been masked out by the - * platform dependent interrupt handling code already, so the - * sti() here will not cause another call to the SCSI host's - * interrupt handler (assuming there is one irq-level per - * host). - */ - sti(); - while (jiffies < timeout) barrier(); + int ticks_remaining = timeout - jiffies; + /* + * NOTE: This may be executed from within an interrupt + * handler! This is bad, but for now, it'll do. The irq + * level of the interrupt handler has been masked out by the + * platform dependent interrupt handling code already, so the + * sti() here will not cause another call to the SCSI host's + * interrupt handler (assuming there is one irq-level per + * host). + */ + sti(); + while (--ticks_remaining >= 0) udelay(1000000/HZ); + host->last_reset = jiffies - MIN_RESET_DELAY; } restore_flags(flags); @@ -1790,7 +1795,8 @@ if ((++SCpnt->retries) < SCpnt->allowed) { if ((SCpnt->retries >= (SCpnt->allowed >> 1)) - && !(jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD) + && !(SCpnt->host->last_reset > 0 && + jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD) && !(SCpnt->flags & WAS_RESET)) { printk("scsi%d channel %d : resetting for second half of retries.\n", @@ -2116,7 +2122,19 @@ host->last_reset = jiffies; temp = host->hostt->reset(SCpnt, reset_flags); - host->last_reset = jiffies; + /* + This test allows the driver to introduce an additional bus + settle time delay by setting last_reset up to 20 seconds in + the future. In the normal case where the driver does not + modify last_reset, it must be assumed that the actual bus + reset occurred immediately prior to the return to this code, + and so last_reset must be updated to the current time, so + that the delay in internal_cmnd will guarantee at least a + MIN_RESET_DELAY bus settle time. + */ + if ((host->last_reset < jiffies) || + (host->last_reset > (jiffies + 20 * HZ))) + host->last_reset = jiffies; } else { @@ -2125,7 +2143,9 @@ host->last_reset = jiffies; SCpnt->flags |= (WAS_RESET | IS_RESETTING); temp = host->hostt->reset(SCpnt, reset_flags); - host->last_reset = jiffies; + if ((host->last_reset < jiffies) || + (host->last_reset > (jiffies + 20 * HZ))) + host->last_reset = jiffies; if (!host->block) host->host_busy--; } @@ -3047,7 +3067,11 @@ */ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) - if(shpnt->hostt == tpnt) scan_scsis(shpnt,0,0,0,0); + if(shpnt->hostt == tpnt) { + scan_scsis(shpnt,0,0,0,0); + if (shpnt->select_queue_depths != NULL) + (shpnt->select_queue_depths)(shpnt, scsi_devices); + } for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)(); diff -ur --new-file old/linux/drivers/scsi/scsicam.c new/linux/drivers/scsi/scsicam.c --- old/linux/drivers/scsi/scsicam.c Sun Aug 4 12:39:07 1996 +++ new/linux/drivers/scsi/scsicam.c Thu Nov 7 10:25:21 1996 @@ -54,22 +54,28 @@ if (!(bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024))) return -1; -#ifdef DEBUG - printk ("scsicam_bios_param : trying existing mapping\n"); -#endif + /* try to infer mapping from partition table */ ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, (unsigned int *) ip + 0, (unsigned int *) ip + 1); brelse (bh); if (ret_code == -1) { -#ifdef DEBUG - printk ("scsicam_bios_param : trying optimal mapping\n"); -#endif + /* pick some standard mapping with at most 1024 cylinders, + and at most 62 sectors per track - this works up to + 7905 MB */ ret_code = setsize ((unsigned long) size, (unsigned int *) ip + 2, (unsigned int *) ip + 0, (unsigned int *) ip + 1); } - return ret_code; + /* if something went wrong, then apparently we have to return + a geometry with more than 1024 cylinders */ + if (ret_code || ip[0] > 255 || ip[1] > 63) { + ip[0] = 64; + ip[1] = 32; + ip[2] = size / (ip[0] * ip[1]); + } + + return 0; } /* diff -ur --new-file old/linux/drivers/scsi/sd.c new/linux/drivers/scsi/sd.c --- old/linux/drivers/scsi/sd.c Fri Aug 9 10:20:56 1996 +++ new/linux/drivers/scsi/sd.c Mon Oct 28 23:30:44 1996 @@ -428,7 +428,7 @@ SCpnt->host->host_no, (int) SCpnt->channel, (int) SCpnt->target, (int) SCpnt->lun); print_command(SCpnt->cmnd); - print_sense("sr", SCpnt); + print_sense("sd", SCpnt); SCpnt = end_scsi_request(SCpnt, 0, block_sectors); requeue_sd_request(SCpnt); return; @@ -512,7 +512,7 @@ if (flag++ == 0) SCpnt = allocate_device(&CURRENT, - rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device, 0); + rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device, 0); else SCpnt = NULL; /* @@ -537,7 +537,8 @@ cli(); req = CURRENT; while(req){ - SCpnt = request_queueable(req, rscsi_disks[DEVICE_NR(req->rq_dev)].device); + SCpnt = request_queueable(req, + rscsi_disks[DEVICE_NR(req->rq_dev)].device); if(SCpnt) break; req1 = req; req = req->next; diff -ur --new-file old/linux/drivers/scsi/sg.c new/linux/drivers/scsi/sg.c --- old/linux/drivers/scsi/sg.c Thu Aug 1 14:43:05 1996 +++ new/linux/drivers/scsi/sg.c Sun Sep 1 08:15:33 1996 @@ -275,8 +275,39 @@ * See if the command completed normally, or whether something went * wrong. */ - memcpy(device->header.sense_buffer, SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer)); - device->header.result = (SCpnt->sense_buffer[0] == 0 ? 0 : EIO); + memcpy(device->header.sense_buffer, SCpnt->sense_buffer, + sizeof(SCpnt->sense_buffer)); + switch (host_byte(SCpnt->result)) { + case DID_OK: + device->header.result = 0; + break; + case DID_NO_CONNECT: + case DID_BUS_BUSY: + case DID_TIME_OUT: + device->header.result = EBUSY; + break; + case DID_BAD_TARGET: + case DID_ABORT: + case DID_PARITY: + case DID_RESET: + case DID_BAD_INTR: + device->header.result = EIO; + break; + case DID_ERROR: + /* + * There really should be DID_UNDERRUN and DID_OVERRUN error values, + * and a means for callers of scsi_do_cmd to indicate whether an + * underrun or overrun should signal an error. Until that can be + * implemented, this kludge allows for returning useful error values + * except in cases that return DID_ERROR that might be due to an + * underrun. + */ + if (SCpnt->sense_buffer[0] == 0 && + status_byte(SCpnt->result) == GOOD) + device->header.result = 0; + else device->header.result = EIO; + break; + } /* * Now wake up the process that is waiting for the diff -ur --new-file old/linux/drivers/scsi/sr.c new/linux/drivers/scsi/sr.c --- old/linux/drivers/scsi/sr.c Tue Aug 13 08:27:48 1996 +++ new/linux/drivers/scsi/sr.c Fri Sep 20 16:00:34 1996 @@ -167,7 +167,8 @@ if (driver_byte(result) != 0 && /* An error occurred */ SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */ (SCpnt->sense_buffer[2] == MEDIUM_ERROR || - SCpnt->sense_buffer[2] == VOLUME_OVERFLOW)) + SCpnt->sense_buffer[2] == VOLUME_OVERFLOW || + SCpnt->sense_buffer[2] == ILLEGAL_REQUEST)) { long error_sector = (SCpnt->sense_buffer[3] << 24) | (SCpnt->sense_buffer[4] << 16) | @@ -858,8 +859,8 @@ if (count+1 != SCpnt->use_sg) panic("Bad sr request list"); break; }; - if (((long) sgpnt[count].address) + sgpnt[count].length > ISA_DMA_THRESHOLD && - SCpnt->host->unchecked_isa_dma) { + if (((long) sgpnt[count].address) + sgpnt[count].length - 1 > + ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) { sgpnt[count].alt_address = sgpnt[count].address; /* We try to avoid exhausting the DMA pool, since it is easier * to control usage here. In other places we might have a more diff -ur --new-file old/linux/drivers/scsi/st.c new/linux/drivers/scsi/st.c --- old/linux/drivers/scsi/st.c Fri Aug 16 07:02:48 1996 +++ new/linux/drivers/scsi/st.c Tue Oct 29 16:40:50 1996 @@ -11,7 +11,7 @@ Copyright 1992 - 1996 Kai Makisara email Kai.Makisara@metla.fi - Last modified: Sun Jul 7 10:08:46 1996 by root@kai.makisara.fi + Last modified: Tue Oct 1 22:53:51 1996 by makisara@kai.makisara.fi Some small formal changes - aeb, 950809 */ @@ -2192,7 +2192,8 @@ return (-EBUSY); if ((STp->buffer)->last_result_fatal != 0 || - ((STp->buffer)->b_data[0] & 4)) { + (STp->device->scsi_level >= SCSI_2 && + ((STp->buffer)->b_data[0] & 4) != 0)) { *block = *partition = 0; #if DEBUG if (debugging) @@ -2313,6 +2314,7 @@ return (-EBUSY); STp->drv_block = (STp->mt_status)->mt_fileno = (-1); + STp->eof = ST_NOEOF; if ((STp->buffer)->last_result_fatal != 0) { result = (-EIO); if (STp->can_partitions && diff -ur --new-file old/linux/drivers/scsi/wd7000.c new/linux/drivers/scsi/wd7000.c --- old/linux/drivers/scsi/wd7000.c Fri Mar 1 06:50:54 1996 +++ new/linux/drivers/scsi/wd7000.c Fri Sep 20 16:00:34 1996 @@ -98,6 +98,35 @@ * one of the FD boards, it would be nice to come up with a signature * for it. * J.B. Jan 1994. + * + * + * Revisions by Miroslav Zagorac + * + * 08/24/1996. + * + * Enhancement for wd7000_detect function has been made, so you don't have + * to enter BIOS ROM adress in initialisation data (see struct Config). + * We cannot detect IRQ, DMA and I/O base address for now, so we have to + * enter them as arguments while wd_7000 is detected. If someone has IRQ, + * DMA or I/O base address set to some other value, he can enter them in + * configuration without any problem. Also I wrote a function wd7000_setup, + * so now you can enter WD-7000 definition as kernel arguments, + * as in lilo.conf: + * + * append="wd7000=IRQ,DMA,IO" + * + * PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize + * adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is + * useless for Linux. B^) + * + * + * 09/06/1996. + * + * Autodetecting of I/O base address from wd7000_detect function is removed, + * some little bugs removed, etc... + * + * Thanks to Roger Scott for driver debugging. + * */ #ifdef MODULE @@ -121,7 +150,7 @@ #include "hosts.h" #include "sd.h" -#define ANY2SCSI_INLINE /* undef this to use old macros */ +#define ANY2SCSI_INLINE /* undef this to use old macros */ #undef DEBUG #include "wd7000.h" @@ -158,7 +187,7 @@ */ typedef volatile struct mailbox{ unchar status; - unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ + unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ } Mailbox; /* @@ -167,17 +196,17 @@ * */ typedef struct adapter { - struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ - int iobase; /* This adapter's I/O base address */ - int irq; /* This adapter's IRQ level */ - int dma; /* This adapter's DMA channel */ - struct { /* This adapter's mailboxes */ - Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ - Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ + struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ + int iobase; /* This adapter's I/O base address */ + int irq; /* This adapter's IRQ level */ + int dma; /* This adapter's DMA channel */ + struct { /* This adapter's mailboxes */ + Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ + Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ } mb; - int next_ogmb; /* to reduce contention at mailboxes */ - unchar control; /* shadows CONTROL port value */ - unchar rev1, rev2; /* filled in by wd7000_revision */ + int next_ogmb; /* to reduce contention at mailboxes */ + unchar control; /* shadows CONTROL port value */ + unchar rev1, rev2; /* filled in by wd7000_revision */ } Adapter; /* @@ -186,22 +215,48 @@ * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be * changed to pick up the IRQ level correctly. */ -Adapter *irq2host[16] = {NULL}; /* Possible IRQs are 0-15 */ +Adapter *irq2host[16] = {NULL}; /* Possible IRQs are 0-15 */ + +/* + * (linear) base address for ROM BIOS + */ +static const long wd7000_biosaddr[] = { + 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000, + 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000 +}; +#define NUM_ADDRS (sizeof(wd7000_biosaddr)/sizeof(long)) + +static const unsigned short wd7000_iobase[] = { + 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, + 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, + 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, + 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8 +}; +#define NUM_IOPORTS (sizeof(wd7000_iobase)/sizeof(unsigned short)) + +static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 }; +#define NUM_IRQS (sizeof(wd7000_irq)/sizeof(short)) +static const short wd7000_dma[] = { 5, 6, 7 }; +#define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short)) + /* * Standard Adapter Configurations - used by wd7000_detect */ typedef struct { - const void *bios; /* (linear) base address for ROM BIOS */ - int iobase; /* I/O ports base address */ - int irq; /* IRQ level */ - int dma; /* DMA channel */ + int irq; /* IRQ level */ + int dma; /* DMA channel */ + unsigned iobase; /* I/O base address */ } Config; +/* + * Add here your configuration... + */ static const Config configs[] = { - {(void *) 0xce000, 0x350, 15, 6}, /* defaults for single adapter */ - {(void *) 0xc8000, 0x330, 11, 5}, /* defaults for second adapter */ - {(void *) 0xd8000, 0x350, 15, 6}, /* Arghhh.... who added this ? */ + { 15, 6, 0x350 }, /* defaults for single adapter */ + { 11, 5, 0x320 }, /* defaults for second adapter */ + { 7, 6, 0x350 }, /* My configuration (Zaga) */ + { -1, -1, 0x0 } /* Empty slot */ }; #define NUM_CONFIGS (sizeof(configs)/sizeof(Config)) @@ -211,13 +266,13 @@ * added for the Future Domain version. */ typedef struct signature { - const void *sig; /* String to look for */ - unsigned ofs; /* offset from BIOS base address */ - unsigned len; /* length of string */ + const void *sig; /* String to look for */ + unsigned ofs; /* offset from BIOS base address */ + unsigned len; /* length of string */ } Signature; static const Signature signatures[] = { - {"SSTBIOS",0x0000d,7} /* "SSTBIOS" @ offset 0x0000d */ + { "SSTBIOS", 0x0000d, 7 } /* "SSTBIOS" @ offset 0x0000d */ }; #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature)) @@ -226,19 +281,20 @@ * I/O Port Offsets and Bit Definitions * 4 addresses are used. Those not defined here are reserved. */ -#define ASC_STAT 0 /* Status, Read */ -#define ASC_COMMAND 0 /* Command, Write */ -#define ASC_INTR_STAT 1 /* Interrupt Status, Read */ -#define ASC_INTR_ACK 1 /* Acknowledge, Write */ -#define ASC_CONTROL 2 /* Control, Write */ - -/* ASC Status Port - */ -#define INT_IM 0x80 /* Interrupt Image Flag */ -#define CMD_RDY 0x40 /* Command Port Ready */ -#define CMD_REJ 0x20 /* Command Port Byte Rejected */ -#define ASC_INIT 0x10 /* ASC Initialized Flag */ -#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ +#define ASC_STAT 0 /* Status, Read */ +#define ASC_COMMAND 0 /* Command, Write */ +#define ASC_INTR_STAT 1 /* Interrupt Status, Read */ +#define ASC_INTR_ACK 1 /* Acknowledge, Write */ +#define ASC_CONTROL 2 /* Control, Write */ + +/* + * ASC Status Port + */ +#define INT_IM 0x80 /* Interrupt Image Flag */ +#define CMD_RDY 0x40 /* Command Port Ready */ +#define CMD_REJ 0x20 /* Command Port Byte Rejected */ +#define ASC_INIT 0x10 /* ASC Initialized Flag */ +#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ /* COMMAND opcodes * @@ -248,31 +304,31 @@ * discernible effect whatsoever. I think they may be related to certain * ICB commands, but again, the OEM manual doesn't make that clear. */ -#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ -#define INITIALIZATION 1 /* initialization (10 bytes) */ -#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ -#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ -#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ -#define SOFT_RESET 5 /* SCSI bus soft reset */ -#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ -#define START_OGMB 0x80 /* start command in OGMB (n) */ -#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ +#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ +#define INITIALIZATION 1 /* initialization (10 bytes) */ +#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ +#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ +#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ +#define SOFT_RESET 5 /* SCSI bus soft reset */ +#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ +#define START_OGMB 0x80 /* start command in OGMB (n) */ +#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ /* where (n) = lower 6 bits */ /* For INITIALIZATION: */ typedef struct initCmd { - unchar op; /* command opcode (= 1) */ - unchar ID; /* Adapter's SCSI ID */ - unchar bus_on; /* Bus on time, x 125ns (see below) */ - unchar bus_off; /* Bus off time, "" "" */ - unchar rsvd; /* Reserved */ - unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ - unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ - unchar icmbs; /* Number of incoming MBs, "" "" */ + unchar op; /* command opcode (= 1) */ + unchar ID; /* Adapter's SCSI ID */ + unchar bus_on; /* Bus on time, x 125ns (see below) */ + unchar bus_off; /* Bus off time, "" "" */ + unchar rsvd; /* Reserved */ + unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ + unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ + unchar icmbs; /* Number of incoming MBs, "" "" */ } InitCmd; -#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ -#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ +#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ +#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ /* Interrupt Status Port - also returns diagnostic codes at ASC reset * @@ -301,29 +357,29 @@ #define ASC_RES 0x01 /* ASC Reset */ /* - Driver data structures: - - mb and scbs are required for interfacing with the host adapter. - An SCB has extra fields not visible to the adapter; mb's - _cannot_ do this, since the adapter assumes they are contiguous in - memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact - to access them. - - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each; - the additional bytes are used only by the driver. - - For now, a pool of SCBs are kept in global storage by this driver, - and are allocated and freed as needed. - - The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command, - not when it has finished. Since the SCB must be around for completion, - problems arise when SCBs correspond to OGMBs, which may be reallocated - earlier (or delayed unnecessarily until a command completes). - Mailboxes are used as transient data structures, simply for - carrying SCB addresses to/from the 7000-FASST2. - - Note also since SCBs are not "permanently" associated with mailboxes, - there is no need to keep a global list of Scsi_Cmnd pointers indexed - by OGMB. Again, SCBs reference their Scsi_Cmnds directly, so mailbox - indices need not be involved. -*/ + * Driver data structures: + * - mb and scbs are required for interfacing with the host adapter. + * An SCB has extra fields not visible to the adapter; mb's + * _cannot_ do this, since the adapter assumes they are contiguous in + * memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact + * to access them. + * - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each; + * the additional bytes are used only by the driver. + * - For now, a pool of SCBs are kept in global storage by this driver, + * and are allocated and freed as needed. + * + * The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command, + * not when it has finished. Since the SCB must be around for completion, + * problems arise when SCBs correspond to OGMBs, which may be reallocated + * earlier (or delayed unnecessarily until a command completes). + * Mailboxes are used as transient data structures, simply for + * carrying SCB addresses to/from the 7000-FASST2. + * + * Note also since SCBs are not "permanently" associated with mailboxes, + * there is no need to keep a global list of Scsi_Cmnd pointers indexed + * by OGMB. Again, SCBs reference their Scsi_Cmnds directly, so mailbox + * indices need not be involved. + */ /* * WD7000-specific scatter/gather element structure @@ -483,10 +539,114 @@ static int freescbs = MAX_SCBS; /* free list counter */ /* + * + */ +static short wd7000_setupIRQ[NUM_CONFIGS]; +static short wd7000_setupDMA[NUM_CONFIGS]; +static short wd7000_setupIO[NUM_CONFIGS]; +static short wd7000_card_num = 0; + +/* * END of data/declarations - code follows. */ +/* + * Note: You can now set these options from the kernel's "command line". + * The syntax is: + * + * wd7000=IRQ,DMA,IO + * eg: + * wd7000=7,6,0x350 + * + * will configure the driver for a WD-7000 controller + * using IRQ 15 with a DMA channel 6, at IO base address 0x350. + */ +void wd7000_setup (char *str, int *ints) +{ + short i, j; + + if (wd7000_card_num >= NUM_CONFIGS) { + printk ("wd7000_setup: Too many \"wd7000=\" configurations in " + "command line!\n"); + + return; + } + + if (ints[0] != 3) + printk ("wd7000_setup: Error in command line! " + "Usage: wd7000=IRQ,DMA,IO\n"); + else { + for (i = 0; i < NUM_IRQS; i++) + if (ints[1] == wd7000_irq[i]) + break; + + if (i == NUM_IRQS) { + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " + "invalid IRQ.\n", ints[1], ints[2], ints[3]); + return; + } + else + wd7000_setupIRQ[wd7000_card_num] = ints[1]; + + for (i = 0; i < NUM_DMAS; i++) + if (ints[2] == wd7000_dma[i]) + break; + + if (i == NUM_DMAS) { + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " + "invalid DMA channel.\n", ints[1], ints[2], ints[3]); + return; + } + else + wd7000_setupDMA[wd7000_card_num] = ints[2]; + + for (i = 0; i < NUM_IOPORTS; i++) + if (ints[3] == wd7000_iobase[i]) + break; + + if (i == NUM_IOPORTS) { + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " + "invalid I/O base address.\n", ints[1], ints[2], ints[3]); + return; + } + else + wd7000_setupIO[wd7000_card_num] = ints[3]; + + if (wd7000_card_num) + for (i = 0; i < (wd7000_card_num - 1); i++) + for (j = i + 1; j < wd7000_card_num; j++) + if (wd7000_setupIRQ[i] == wd7000_setupIRQ[j]) { + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " + "duplicated IRQ!\n", + ints[1], ints[2], ints[3]); + return; + } + else if (wd7000_setupDMA[i] == wd7000_setupDMA[j]) { + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " + "duplicated DMA channel!\n", + ints[1], ints[2], ints[3]); + return; + } + else if (wd7000_setupIO[i] == wd7000_setupIO[j]) { + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " + "duplicated I/O base address!\n", + ints[1], ints[2], ints[3]); + return; + } + +#ifdef DEBUG + printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x\n", + wd7000_setupIRQ[wd7000_card_num], + wd7000_setupDMA[wd7000_card_num], + wd7000_setupIO[wd7000_card_num]); +#endif + + wd7000_card_num++; + } +} + + #ifdef ANY2SCSI_INLINE /* Since they're used a lot, I've redone the following from the macros @@ -555,16 +715,20 @@ #define WAITnexttimeout 200 /* 2 seconds */ -#define WAIT(port, mask, allof, noneof) \ - { register volatile unsigned WAITbits; \ - register unsigned long WAITtimeout = jiffies + WAITnexttimeout; \ - while (1) { \ - WAITbits = inb(port) & (mask); \ - if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \ - break; \ - if (jiffies > WAITtimeout) goto fail; \ - } \ - } +static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned noneof) +{ + register unsigned WAITbits; + register unsigned long WAITtimeout = jiffies + WAITnexttimeout; + + while (jiffies <= WAITtimeout) { + WAITbits = inb (port) & mask; + + if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0)) + return (0); + } + + return (1); +} static inline void delay( unsigned how_long ) @@ -577,17 +741,19 @@ static inline int command_out(Adapter *host, unchar *cmd, int len) { - WAIT(host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0); - while (len--) { - do { - outb(*cmd, host->iobase+ASC_COMMAND); - WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0); - } while (inb(host->iobase+ASC_STAT) & CMD_REJ); - cmd++; + if (! WAIT (host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0)) { + while (len--) { + do { + outb(*cmd, host->iobase+ASC_COMMAND); + WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0); + } while (inb(host->iobase+ASC_STAT) & CMD_REJ); + + cmd++; + } + + return 1; } - return 1; -fail: printk("wd7000 command_out: WAIT failed(%d)\n", len+1); return 0; } @@ -627,7 +793,8 @@ timeout = jiffies + WAITnexttimeout; do { sti(); /* Yes this is really needed here */ - now = jiffies; while (jiffies == now) /* wait a jiffy */; + now = jiffies; + while (jiffies == now); /* wait a jiffy */ cli(); } while (freescbs < needed && jiffies <= timeout); /* @@ -699,7 +866,7 @@ Mailbox *ogmbs = host->mb.ogmb; int *next_ogmb = &(host->next_ogmb); #ifdef DEBUG - printk("wd7000 mail_out: %06x",(unsigned int) scbptr); + printk("wd7000 mail_out: 0x%06lx",(long) scbptr); #endif /* We first look for a free outgoing mailbox */ save_flags(flags); @@ -708,7 +875,7 @@ for (i = 0; i < OGMB_CNT; i++) { if (ogmbs[ogmb].status == 0) { #ifdef DEBUG - printk(" using OGMB %x",ogmb); + printk(" using OGMB 0x%x",ogmb); #endif ogmbs[ogmb].status = 1; any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); @@ -720,7 +887,7 @@ } restore_flags(flags); #ifdef DEBUG - printk(", scb is %x",(unsigned int) scbptr); + printk(", scb is 0x%06lx",(long) scbptr); #endif if (i >= OGMB_CNT) { /* @@ -790,7 +957,7 @@ } #ifdef DEBUG if (scsierr||hosterr) - printk("\nSCSI command error: SCSI %02x host %04x return %d", + printk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr,in_error,hosterr); #endif return scsierr | (hosterr << 16); @@ -800,7 +967,7 @@ static void wd7000_scsi_done(Scsi_Cmnd * SCpnt) { #ifdef DEBUG - printk("wd7000_scsi_done: %06x\n",(unsigned int) SCpnt); + printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt); #endif SCpnt->SCp.phase = 0; } @@ -819,12 +986,12 @@ Mailbox *icmbs = host->mb.icmb; #ifdef DEBUG - printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host); + printk("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host); #endif flag = inb(host->iobase+ASC_INTR_STAT); #ifdef DEBUG - printk("wd7000_intr_handle: intr stat = %02x\n",flag); + printk("wd7000_intr_handle: intr stat = 0x%02x\n",flag); #endif if (!(inb(host->iobase+ASC_STAT) & INT_IM)) { @@ -847,7 +1014,7 @@ /* The interrupt is for a mailbox */ if (!(flag & IMB_INTR)) { #ifdef DEBUG - printk("wd7000_intr_handle: free outgoing mailbox"); + printk("wd7000_intr_handle: free outgoing mailbox\n"); #endif /* * If sleep_on() and the "interrupt on free OGMB" command are @@ -862,7 +1029,7 @@ icmb_status = icmbs[icmb].status; if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ #ifdef DEBUG - printk("wd7000_intr_handle: unsolicited interrupt %02xh\n", + printk("wd7000_intr_handle: unsolicited interrupt 0x%02xh\n", icmb_status); #endif wd7000_intr_ack(host); @@ -983,7 +1150,7 @@ return 0; } if (make_code(icb.vue|(icb.status << 8),0)) { - printk("wd7000_diagnostics: failed (%02x,%02x)\n", + printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", icb.vue, icb.status); return 0; } @@ -1000,17 +1167,22 @@ int diag; /* - Reset the adapter - only. The SCSI bus was initialized at power-up, - and we need to do this just so we control the mailboxes, etc. - */ + * Reset the adapter - only. The SCSI bus was initialized at power-up, + * and we need to do this just so we control the mailboxes, etc. + */ outb(ASC_RES, host->iobase+ASC_CONTROL); delay(1); /* reset pulse: this is 10ms, only need 25us */ outb(0,host->iobase+ASC_CONTROL); host->control = 0; /* this must always shadow ASC_CONTROL */ - WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0); - if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1) { + if (WAIT (host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { + printk ("wd7000_init: WAIT timed out.\n"); + return 0; /* 0 = not ok */ + } + + if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1) { printk("wd7000_init: "); + switch (diag) { case 2: printk("RAM failure.\n"); @@ -1031,8 +1203,7 @@ printk("ROM checksum error.\n"); break; default: - printk("diagnostic code %02Xh received.\n", diag); - break; + printk("diagnostic code 0x%02Xh received.\n", diag); } return 0; } @@ -1046,7 +1217,11 @@ printk("wd7000_init: adapter initialization failed.\n"); return 0; } - WAIT(host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0); + + if (WAIT (host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { + printk ("wd7000_init: WAIT timed out.\n"); + return 0; + } if (request_irq(host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) { printk("wd7000_init: can't get IRQ %d.\n", host->irq); @@ -1067,10 +1242,6 @@ } return 1; - - fail: - printk("wd7000_init: WAIT timed out.\n"); - return 0; /* 0 = not ok */ } @@ -1093,8 +1264,7 @@ } -int wd7000_detect(Scsi_Host_Template * tpnt) -/* +/* * Returns the number of adapters this driver is supporting. * * The source for hosts.c says to wait to call scsi_register until 100% @@ -1104,37 +1274,88 @@ * calling scsi_unregister. * */ +int wd7000_detect (Scsi_Host_Template *tpnt) { - int i,j, present = 0; - const Config *cfg; - const Signature *sig; + short present = 0, biosaddr_ptr, cfg_ptr, sig_ptr, i, pass; + short biosptr[NUM_CONFIGS]; + unsigned iobase; Adapter *host = NULL; struct Scsi_Host *sh; + for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1); + tpnt->proc_dir = &proc_scsi_wd7000; - /* Set up SCB free list, which is shared by all adapters */ - init_scbs(); + /* + * Set up SCB free list, which is shared by all adapters + */ + init_scbs (); + + for (pass = 0, cfg_ptr = 0; pass < NUM_CONFIGS; pass++) { + for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++) + for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) { + for (i = 0; i < pass; i++) + if (biosptr[i] == biosaddr_ptr) + break; + + if ((i == pass) && + !memcmp ((void *) (wd7000_biosaddr[biosaddr_ptr] + + signatures[sig_ptr].ofs), signatures[sig_ptr].sig, + signatures[sig_ptr].len)) + goto bios_matched; + } + +bios_matched: - cfg = configs; - for (i = 0; i < NUM_CONFIGS; i++) { - sig = signatures; - for (j = 0; j < NUM_SIGNATURES; j++) { - if (!memcmp(cfg->bios+sig->ofs, sig->sig, sig->len)) { - /* matched this one */ #ifdef DEBUG - printk("WD-7000 SST BIOS detected at %04X: checking...\n", - (int) cfg->bios); + printk ("wd7000_detect: pass %d\n", pass + 1); + + if (biosaddr_ptr == NUM_ADDRS) + printk ("WD-7000 SST BIOS not detected...\n"); + else + printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n", + wd7000_biosaddr[biosaddr_ptr]); #endif - /* - * We won't explicitly test the configuration (in this - * version); instead, we'll just see if it works to - * setup the adapter; if it does, we'll use it. - */ - if (check_region(cfg->iobase, 4)) { /* ports in use */ - printk("IO %xh already in use.\n", host->iobase); - continue; - } + + if (wd7000_card_num) + iobase = wd7000_setupIO[wd7000_card_num - 1]; + else { + if (configs[cfg_ptr++].irq < 0) + continue; + + iobase = configs[cfg_ptr - 1].iobase; + } + +#ifdef DEBUG + printk ("wd7000_detect: check IO 0x%x region...\n", iobase); +#endif + + if (! check_region (iobase, 4)) { + +#ifdef DEBUG + printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase); +#endif + + /* + * ASC reset... + */ + outb (ASC_RES, iobase + ASC_CONTROL); + delay (1); + outb (0, iobase + ASC_CONTROL); + + if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) +#ifdef DEBUG + { + printk ("failed!\n"); + continue; + } + else + printk ("ok!\n"); +#else + continue; +#endif + + if (inb (iobase + ASC_INTR_STAT) == 1) { /* * We register here, to get a pointer to the extra space, * which we'll use as the Adapter structure (host) for @@ -1142,49 +1363,82 @@ * Scsi_Host structure (sh), and is located by the empty * array hostdata. */ - sh = scsi_register(tpnt, sizeof(Adapter) ); + sh = scsi_register (tpnt, sizeof (Adapter)); host = (Adapter *) sh->hostdata; + #ifdef DEBUG - printk("wd7000_detect: adapter allocated at %06x\n", - (int)host); + printk ("wd7000_detect: adapter allocated at 0x%x\n", + (int) host); #endif - memset( host, 0, sizeof(Adapter) ); - host->sh = sh; - host->irq = cfg->irq; - host->iobase = cfg->iobase; - host->dma = cfg->dma; + + memset (host, 0, sizeof (Adapter)); + + if (wd7000_card_num) { + host->irq = wd7000_setupIRQ[--wd7000_card_num]; + host->dma = wd7000_setupDMA[wd7000_card_num]; + } + else { + host->irq = configs[cfg_ptr - 1].irq; + host->dma = configs[cfg_ptr - 1].dma; + } + + host->sh = sh; + host->iobase = iobase; irq2host[host->irq] = host; - if (!wd7000_init(host)) { /* Initialization failed */ +#ifdef DEBUG + printk ("wd7000_detect: Trying init WD-7000 card at IO " + "0x%x, IRQ %d, DMA %d...\n", + host->iobase, host->irq, host->dma); +#endif + + if (! wd7000_init (host)) { /* Initialization failed */ scsi_unregister (sh); + continue; } /* * OK from here - we'll use this adapter/configuration. */ - wd7000_revision(host); /* important for scatter/gather */ + wd7000_revision (host); /* important for scatter/gather */ - printk("Western Digital WD-7000 (%d.%d) ", - host->rev1, host->rev2); - printk("using IO %xh IRQ %d DMA %d.\n", - host->iobase, host->irq, host->dma); + /* + * Register our ports. + */ + request_region (host->iobase, 4, "wd7000"); - request_region(host->iobase, 4,"wd7000"); /* Register our ports */ /* - * For boards before rev 6.0, scatter/gather isn't supported. + * For boards before rev 6.0, scatter/gather + * isn't supported. */ - if (host->rev1 < 6) sh->sg_tablesize = SG_NONE; + if (host->rev1 < 6) + sh->sg_tablesize = SG_NONE; + + present++; /* count it */ - present++; /* count it */ - break; /* don't try any more sigs */ + if (biosaddr_ptr != NUM_ADDRS) + biosptr[pass] = biosaddr_ptr; + + printk ("Western Digital WD-7000 (rev %d.%d) ", + host->rev1, host->rev2); + printk ("using IO 0x%x, IRQ %d, DMA %d.\n", + host->iobase, host->irq, host->dma); } - sig++; /* try next signature with this configuration */ } - cfg++; /* try next configuration */ + +#ifdef DEBUG + else + printk ("wd7000_detect: IO 0x%x region already allocated!\n", + iobase); +#endif + } - return present; + if (! present) + printk ("Failed initialization of WD-7000 SCSI card!\n"); + + return (present); } diff -ur --new-file old/linux/drivers/sound/Readme.cards new/linux/drivers/sound/Readme.cards --- old/linux/drivers/sound/Readme.cards Sat Jul 6 10:31:43 1996 +++ new/linux/drivers/sound/Readme.cards Fri Sep 20 16:00:35 1996 @@ -1036,5 +1036,5 @@ European/Finnish mirror: http://personal.eunet.fi/pp/voxware www home page of commercial -UNIX Sound System drivers: http://www.4front-tech.com/uss.html +Open Sound System drivers: http://www.4front-tech.com/uss.html diff -ur --new-file old/linux/drivers/sound/audio.c new/linux/drivers/sound/audio.c --- old/linux/drivers/sound/audio.c Sun Aug 18 09:46:48 1996 +++ new/linux/drivers/sound/audio.c Tue Oct 8 17:12:33 1996 @@ -403,7 +403,7 @@ case SNDCTL_DSP_GETOSPACE: if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; + return -EPERM; if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -(EBUSY); @@ -418,7 +418,7 @@ return err; if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0) - info.bytes += buf_size - buf_ptr; + info.bytes -= buf_ptr; memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); return 0; diff -ur --new-file old/linux/drivers/sound/configure.c new/linux/drivers/sound/configure.c --- old/linux/drivers/sound/configure.c Sat Jul 20 07:56:20 1996 +++ new/linux/drivers/sound/configure.c Wed Sep 25 20:35:30 1996 @@ -735,11 +735,13 @@ * IRQ and DMA settings */ +#if 0 /* Disable this broken question. */ ask_int_choice (B (OPT_AEDSP16), "AEDSP16_BASE", "I/O base for Audio Excel DSP 16", FMT_HEX, 0x220, "220 or 240"); +#endif ask_int_choice (B (OPT_SB), "SBC_BASE", "I/O base for SB", diff -ur --new-file old/linux/drivers/sound/dev_table.h new/linux/drivers/sound/dev_table.h --- old/linux/drivers/sound/dev_table.h Sun Jul 7 10:18:51 1996 +++ new/linux/drivers/sound/dev_table.h Fri Sep 20 16:00:35 1996 @@ -15,6 +15,7 @@ #ifndef _DEV_TABLE_H_ #define _DEV_TABLE_H_ +#include /* * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) diff -ur --new-file old/linux/drivers/sound/dmabuf.c new/linux/drivers/sound/dmabuf.c --- old/linux/drivers/sound/dmabuf.c Sun Aug 18 09:46:48 1996 +++ new/linux/drivers/sound/dmabuf.c Sun Sep 1 08:15:33 1996 @@ -1182,7 +1182,7 @@ if (!audio_devs[dev]->go) tmout = 0; else - tmout = 2 * HZ; + tmout = 10 * HZ; { diff -ur --new-file old/linux/drivers/sound/lowlevel/Makefile new/linux/drivers/sound/lowlevel/Makefile --- old/linux/drivers/sound/lowlevel/Makefile Thu May 16 10:05:57 1996 +++ new/linux/drivers/sound/lowlevel/Makefile Sun Sep 8 18:50:21 1996 @@ -9,7 +9,7 @@ endif lowlevel.o: $(OBJS) - ld -r -o lowlevel.o $(OBJS) + $(LD) -r -o lowlevel.o $(OBJS) clean: rm -f core x y z *~ *.o diff -ur --new-file old/linux/drivers/sound/sb_common.c new/linux/drivers/sound/sb_common.c --- old/linux/drivers/sound/sb_common.c Sun Aug 4 11:42:54 1996 +++ new/linux/drivers/sound/sb_common.c Wed Sep 11 16:57:14 1996 @@ -674,9 +674,6 @@ devc->dev = num_audiodevs; devc->caps = hw_config->driver_use_1; - irq2devc[hw_config->irq] = devc; - devc->irq_ok = 0; - if (snd_set_irq_handler (hw_config->irq, sbintr, "sound blaster", devc->osp) < 0) { @@ -685,6 +682,9 @@ return; } + irq2devc[hw_config->irq] = devc; + devc->irq_ok = 0; + if (devc->major == 4) if (!sb16_set_irq_hw (devc, devc->irq)) /* Unsupported IRQ */ { @@ -705,7 +705,9 @@ } #ifndef NO_SB_IRQ_TEST - for (n = 0; n < 3 && devc->irq_ok == 0; n++) + if (devc->major != 4 || devc->minor > 11) /* Not Sb16 v4.5 or v4.11 */ + { + for (n = 0; n < 3 && devc->irq_ok == 0; n++) if (sb_dsp_command (devc, 0xf2)) /* Cause interrupt immediately */ { int i; @@ -723,6 +725,7 @@ { DDB (printk ("IRQ test OK (IRQ%d)\n", devc->irq)); } + } #endif request_region (hw_config->io_base, 16, "sound blaster"); diff -ur --new-file old/linux/fs/binfmt_elf.c new/linux/fs/binfmt_elf.c --- old/linux/fs/binfmt_elf.c Mon Aug 12 10:09:46 1996 +++ new/linux/fs/binfmt_elf.c Sun Sep 8 18:50:21 1996 @@ -179,6 +179,7 @@ struct elf_phdr *elf_phdata = NULL; struct elf_phdr *eppnt; unsigned long load_addr; + int load_addr_set = 0; int elf_exec_fileno; int retval; unsigned long last_bss, elf_bss; @@ -247,7 +248,7 @@ if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; - if (interp_elf_ex->e_type == ET_EXEC || load_addr != 0) { + if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) { elf_type |= MAP_FIXED; vaddr = eppnt->p_vaddr; } @@ -257,7 +258,7 @@ eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), elf_prot, elf_type, - ELF_PAGESTART(eppnt->p_offset)); + eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); if (error > -1024UL) { /* Real error */ @@ -266,8 +267,10 @@ return ~0UL; } - if (!load_addr && interp_elf_ex->e_type == ET_DYN) + if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { load_addr = error; + load_addr_set = 1; + } /* * Find the end of the file mapping for this phdr, and keep @@ -365,6 +368,7 @@ struct exec interp_ex; struct inode *interpreter_inode; unsigned long load_addr; + int load_addr_set = 0; unsigned int interpreter_type = INTERPRETER_NONE; unsigned char ibcs2_interpreter; int i; @@ -581,15 +585,18 @@ elf_prot, (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE), - ELF_PAGESTART(elf_ppnt->p_offset)); + (elf_ppnt->p_offset - + ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); #ifdef LOW_ELF_STACK if (ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack) elf_stack = ELF_PAGESTART(elf_ppnt->p_vaddr); #endif - if (!load_addr) + if (!load_addr_set) { load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; + load_addr_set = 1; + } k = elf_ppnt->p_vaddr; if (k < start_code) start_code = k; k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; @@ -796,10 +803,12 @@ /* Now use mmap to map the library into memory. */ error = do_mmap(file, ELF_PAGESTART(elf_phdata->p_vaddr), - elf_phdata->p_filesz + ELF_PAGEOFFSET(elf_phdata->p_vaddr), + (elf_phdata->p_filesz + + ELF_PAGEOFFSET(elf_phdata->p_vaddr)), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, - ELF_PAGESTART(elf_phdata->p_offset)); + (elf_phdata->p_offset - + ELF_PAGEOFFSET(elf_phdata->p_vaddr))); k = elf_phdata->p_vaddr + elf_phdata->p_filesz; if (k > elf_bss) elf_bss = k; diff -ur --new-file old/linux/fs/binfmt_java.c new/linux/fs/binfmt_java.c --- old/linux/fs/binfmt_java.c Mon May 13 06:17:23 1996 +++ new/linux/fs/binfmt_java.c Fri Sep 20 16:00:35 1996 @@ -10,7 +10,6 @@ #include #include #include -#include #define _PATH_JAVA "/usr/bin/java" #define _PATH_APPLET "/usr/bin/appletviewer" @@ -93,7 +92,7 @@ /* * OK, we've set the interpreter name - * Splice in (1) the interpreter's name for argv[0] (_PATH_BSHELL) + * Splice in (1) the interpreter's name for argv[0] (_PATH_SH) * (2) the name of the appletviewer wrapper for argv[1] (_PATH_APPLET) * (3) filename of html file (replace argv[0]) * diff -ur --new-file old/linux/fs/buffer.c new/linux/fs/buffer.c --- old/linux/fs/buffer.c Fri Aug 9 15:15:58 1996 +++ new/linux/fs/buffer.c Fri Sep 20 16:00:35 1996 @@ -69,6 +69,7 @@ int nr_free[NR_SIZES] = {0,}; int buffermem = 0; int nr_buffer_heads = 0; +int refilled = 0; /* Set NZ when a buffer freelist is refilled */ extern int *blksize_size[]; /* Here is the parameter block for the bdflush process. If you add or @@ -493,7 +494,7 @@ bh->b_count++; wait_on_buffer(bh); if (bh->b_dev == dev && bh->b_blocknr == block - && bh->b_size == size) + && bh->b_size == size) return bh; bh->b_count--; } @@ -512,7 +513,7 @@ switch (size) { default: panic("Invalid blocksize passed to set_blocksize"); - case 512: case 1024: case 2048: case 4096: case 8192: ; + case 512: case 1024: case 2048: case 4096: case 8192: ; } if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) { @@ -568,6 +569,7 @@ if (nr_free[isize] > 100) return; + ++refilled; /* If there are too many dirty buffers, we wake up the update process now so as to ensure that there are still clean buffers available for user processes to use (and dirty) */ @@ -723,7 +725,7 @@ if (nr_free_pages > min_free_pages + 5) { if (grow_buffers(GFP_BUFFER, size)) { - needed -= PAGE_SIZE; + needed -= PAGE_SIZE; goto repeat0; }; } @@ -810,6 +812,7 @@ void refile_buffer(struct buffer_head * buf) { int dispose; + int isize; if(buf->b_dev == B_FREE) { printk("Attempt to refile free buffer\n"); @@ -835,10 +838,21 @@ remove_from_queues(buf); buf->b_list = dispose; insert_into_queues(buf); - if(dispose == BUF_DIRTY && nr_buffers_type[BUF_DIRTY] > + if (dispose == BUF_DIRTY) { + /* This buffer is dirty, maybe we need to start flushing. */ + /* If too high a percentage of the buffers are dirty... */ + if (nr_buffers_type[BUF_DIRTY] > (nr_buffers - nr_buffers_type[BUF_SHARED]) * bdf_prm.b_un.nfract/100) wakeup_bdflush(0); + /* If this is a loop device, and + * more than half of the buffers of this size are dirty... */ + /* (Prevents no-free-buffers deadlock with loop device.) */ + isize = BUFSIZE_INDEX(buf->b_size); + if (MAJOR(buf->b_dev) == LOOP_MAJOR && + nr_buffers_st[isize][BUF_DIRTY]*2>nr_buffers_size[isize]) + wakeup_bdflush(1); + } } } @@ -924,7 +938,7 @@ index = BUFSIZE_INDEX(bh->b_size); if (buffer_uptodate(bh)) - return(bh); + return(bh); else ll_rw_block(READ, 1, &bh); blocks = (filesize - pos) >> (9+index); @@ -952,7 +966,7 @@ if (j>1) ll_rw_block(READA, (j-1), bhlist+1); for(i=1; ib_wait; - memset(bh,0,sizeof(*bh)); - ((volatile struct buffer_head *) bh)->b_wait = wait; bh->b_next_free = unused_list; unused_list = bh; wake_up(&buffer_wait); @@ -1051,10 +1060,6 @@ return NULL; bh = unused_list; unused_list = bh->b_next_free; - bh->b_next_free = NULL; - bh->b_data = NULL; - bh->b_size = 0; - bh->b_state = 0; return bh; } @@ -1075,11 +1080,18 @@ bh = get_unused_buffer_head(); if (!bh) goto no_grow; + + bh->b_dev = B_FREE; /* Flag as unused */ bh->b_this_page = head; head = bh; - bh->b_data = (char *) (page+offset); + + bh->b_state = 0; + bh->b_next_free = NULL; + bh->b_count = 0; bh->b_size = size; - bh->b_dev = B_FREE; /* Flag as unused */ + + bh->b_data = (char *) (page+offset); + bh->b_list = 0; } return head; /* @@ -1133,13 +1145,11 @@ * This function expects the page to be locked and may return before I/O is complete. * You then have to check page->locked, page->uptodate, and maybe wait on page->wait. */ -int brw_page(int rw, unsigned long address, kdev_t dev, int b[], int size, int bmap) +int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) { struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE]; int block, nr; - struct page *page; - page = mem_map + MAP_NR(address); if (!PageLocked(page)) panic("brw_page: page not locked for I/O"); clear_bit(PG_uptodate, &page->flags); @@ -1149,7 +1159,7 @@ * They do _not_ show up in the buffer hash table! * They are _not_ registered in page->buffers either! */ - bh = create_buffers(address, size); + bh = create_buffers(page_address(page), size); if (!bh) { clear_bit(PG_locked, &page->flags); wake_up(&page->wait); @@ -1341,7 +1351,7 @@ } while (i > 0); /* IO start */ - brw_page(READ, page_address(page), inode->i_dev, nr, inode->i_sb->s_blocksize, 1); + brw_page(READ, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1); return 0; } @@ -1375,7 +1385,7 @@ tmp = bh; while (1) { - nr_free[isize]++; + nr_free[isize]++; if (insert_point) { tmp->b_next_free = insert_point->b_next_free; tmp->b_prev_free = insert_point; @@ -1387,6 +1397,7 @@ } insert_point = tmp; ++nr_buffers; + ++nr_buffers_size[isize]; if (tmp->b_this_page) tmp = tmp->b_this_page; else @@ -1402,6 +1413,11 @@ /* =========== Reduce the buffer memory ============= */ +static inline int buffer_waiting(struct buffer_head * bh) +{ + return waitqueue_active(&bh->b_wait); +} + /* * try_to_free_buffer() checks if all the buffers on this particular page * are unused, and free's the page if so. @@ -1411,7 +1427,7 @@ { unsigned long page; struct buffer_head * tmp, * p; - int isize = BUFSIZE_INDEX(bh->b_size); + int isize = BUFSIZE_INDEX(bh->b_size); *bhp = bh; page = (unsigned long) bh->b_data; @@ -1421,7 +1437,8 @@ if (!tmp) return 0; if (tmp->b_count || buffer_protected(tmp) || - buffer_dirty(tmp) || buffer_locked(tmp) || tmp->b_wait) + buffer_dirty(tmp) || buffer_locked(tmp) || + buffer_waiting(tmp)) return 0; if (priority && buffer_touched(tmp)) return 0; @@ -1605,8 +1622,8 @@ continue; } /* At priority 6, only consider really old - (age==0) buffers for reclaiming. At - priority 0, consider any buffers. */ + (age==0) buffers for reclaiming. At + priority 0, consider any buffers. */ if ((age_of((unsigned long) bh->b_data) >> (6-priority)) > 0) continue; @@ -1731,7 +1748,7 @@ unsigned int starting_block, int size) { struct buffer_head *bh; - int isize = BUFSIZE_INDEX(size); + int isize = BUFSIZE_INDEX(size); int i; /* We want to give ourselves a really good shot at generating @@ -1757,7 +1774,7 @@ static unsigned long try_to_generate_cluster(kdev_t dev, int block, int size) { struct buffer_head * bh, * tmp, * arr[MAX_BUF_PER_PAGE]; - int isize = BUFSIZE_INDEX(size); + int isize = BUFSIZE_INDEX(size); unsigned long offset; unsigned long page; int nblock; @@ -1884,9 +1901,12 @@ */ struct wait_queue * bdflush_wait = NULL; struct wait_queue * bdflush_done = NULL; +struct task_struct *bdflush_tsk = 0; static void wakeup_bdflush(int wait) { + if (current == bdflush_tsk) + return; wake_up(&bdflush_wait); if (wait) { run_task_queue(&tq_disk); @@ -2016,6 +2036,14 @@ * the syscall above, but now we launch it ourselves internally with * kernel_thread(...) directly after the first thread in init/main.c */ +/* To prevent deadlocks for a loop device: + * 1) Do non-blocking writes to loop (avoids deadlock with running + * out of request blocks). + * 2) But do a blocking write if the only dirty buffers are loop buffers + * (otherwise we go into an infinite busy-loop). + * 3) Quit writing loop blocks if a freelist went low (avoids deadlock + * with running out of free buffers for loop's "real" device). +*/ int bdflush(void * unused) { int i; @@ -2023,6 +2051,8 @@ int nlist; int ncount; struct buffer_head * bh, *next; + int major; + int wrta_cmd = WRITEA; /* non-blocking write for LOOP */ /* * We have a bare-bones task_struct, and really should fill @@ -2033,6 +2063,7 @@ current->session = 1; current->pgrp = 1; sprintf(current->comm, "kflushd"); + bdflush_tsk = current; /* * As a kernel thread we want to tamper with system buffers @@ -2058,6 +2089,7 @@ #endif { ndirty = 0; + refilled = 0; repeat: bh = lru_list[nlist]; if(bh) @@ -2080,11 +2112,21 @@ if (buffer_locked(bh) || !buffer_dirty(bh)) continue; + major = MAJOR(bh->b_dev); /* Should we write back buffers that are shared or not?? currently dirty buffers are not shared, so it does not matter */ + if (refilled && major == LOOP_MAJOR) + continue; bh->b_count++; ndirty++; bh->b_flushtime = 0; + if (major == LOOP_MAJOR) { + ll_rw_block(wrta_cmd,1, &bh); + wrta_cmd = WRITEA; + if (buffer_dirty(bh)) + --ndirty; + } + else ll_rw_block(WRITE, 1, &bh); #ifdef DEBUG if(nlist != BUF_DIRTY) ncount++; @@ -2096,6 +2138,14 @@ if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount); printk("sleeping again.\n"); #endif + /* If we didn't write anything, but there are still + * dirty buffers, then make the next write to a + * loop device to be a blocking write. + * This lets us block--which we _must_ do! */ + if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0) { + wrta_cmd = WRITE; + continue; + } run_task_queue(&tq_disk); wake_up(&bdflush_done); diff -ur --new-file old/linux/fs/exec.c new/linux/fs/exec.c --- old/linux/fs/exec.c Fri Jul 5 14:06:38 1996 +++ new/linux/fs/exec.c Wed Sep 11 16:57:15 1996 @@ -299,7 +299,7 @@ mpnt->vm_offset = 0; mpnt->vm_inode = NULL; mpnt->vm_pte = 0; - insert_vm_struct(current, mpnt); + insert_vm_struct(current->mm, mpnt); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; } diff -ur --new-file old/linux/fs/ext2/namei.c new/linux/fs/ext2/namei.c --- old/linux/fs/ext2/namei.c Wed Jul 10 12:11:15 1996 +++ new/linux/fs/ext2/namei.c Fri Sep 20 16:00:35 1996 @@ -519,7 +519,7 @@ inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse (dir_block); - inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->fs->umask); + inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; diff -ur --new-file old/linux/fs/locks.c new/linux/fs/locks.c --- old/linux/fs/locks.c Tue Jul 2 18:08:42 1996 +++ new/linux/fs/locks.c Fri Sep 20 16:00:35 1996 @@ -128,12 +128,20 @@ static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx); static struct file_lock *file_lock_table = NULL; +static struct file_lock *unused_file_locks = NULL; -/* Free lock not inserted in any queue */ +/* + * Free lock not inserted in any queue + * + * Careful! We can't just "kfree()" it: there may be other processes + * that have yet to remove themselves from the wait queues. Thus the + * internal memory management. + */ static inline void locks_free_lock(struct file_lock *fl) { - kfree(fl); - return; + struct file_lock *next = unused_file_locks; + unused_file_locks = fl; + fl->fl_next = next; } /* Add lock fl to the blocked list pointed to by block. @@ -636,10 +644,12 @@ if (my_task == blocked_task) return (1); for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) { + struct wait_queue *head; if (fl->fl_owner == NULL || fl->fl_wait == NULL) continue; + head = WAIT_QUEUE_HEAD(&fl->fl_wait); dlock_wait = fl->fl_wait; - do { + while (dlock_wait != head) { if (dlock_wait->task == blocked_task) { if (fl->fl_owner == my_task) { return (1); @@ -648,7 +658,7 @@ goto next_task; } dlock_wait = dlock_wait->next; - } while (dlock_wait != fl->fl_wait); + } } return (0); } @@ -903,26 +913,30 @@ static struct file_lock *locks_alloc_lock(struct file_lock *fl) { - struct file_lock *tmp; + struct file_lock *retval; - /* Okay, let's make a new file_lock structure... */ - if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock), - GFP_ATOMIC)) == NULL) - return (tmp); - - tmp->fl_nextlink = NULL; - tmp->fl_prevlink = NULL; - tmp->fl_next = NULL; - tmp->fl_block = NULL; - tmp->fl_flags = fl->fl_flags; - tmp->fl_owner = fl->fl_owner; - tmp->fl_file = fl->fl_file; - tmp->fl_wait = NULL; - tmp->fl_type = fl->fl_type; - tmp->fl_start = fl->fl_start; - tmp->fl_end = fl->fl_end; - - return (tmp); + retval = unused_file_locks; + if (retval) { + unused_file_locks = retval->fl_next; + goto init_file_lock; + } + retval = (struct file_lock *) + kmalloc(sizeof(struct file_lock), GFP_ATOMIC); + if (retval) { + retval->fl_wait = NULL; +init_file_lock: + retval->fl_next = NULL; + retval->fl_nextlink = NULL; + retval->fl_prevlink = NULL; + retval->fl_block = NULL; + retval->fl_owner = fl->fl_owner; + retval->fl_file = fl->fl_file; + retval->fl_flags = fl->fl_flags; + retval->fl_type = fl->fl_type; + retval->fl_start = fl->fl_start; + retval->fl_end = fl->fl_end; + } + return retval; } /* Insert file lock fl into an inode's lock list at the position indicated @@ -979,7 +993,7 @@ } wake_up(&fl->fl_wait); - kfree(fl); + locks_free_lock(fl); return; } @@ -1014,10 +1028,11 @@ (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink, (long)fl->fl_next, (long)fl->fl_block, id, pfx); if ((wt = fl->fl_wait) != NULL) { - do { + struct wait_queue *head = WAIT_QUEUE_HEAD(&fl->fl_wait); + while (wt != head) { p += sprintf(p, " %d", wt->task->pid); wt = wt->next; - } while (wt != fl->fl_wait); + } } p += sprintf(p, "\n"); return (p); diff -ur --new-file old/linux/fs/namei.c new/linux/fs/namei.c --- old/linux/fs/namei.c Sun Jul 7 19:27:04 1996 +++ new/linux/fs/namei.c Fri Sep 20 16:00:35 1996 @@ -32,7 +32,7 @@ if (get_fs() == KERNEL_DS) return 0; - vma = find_vma(current, address); + vma = find_vma(current->mm, address); if (!vma || vma->vm_start > address || !(vma->vm_flags & VM_READ)) return -EFAULT; address = vma->vm_end - address; @@ -585,7 +585,7 @@ if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); down(&dir->i_sem); - error = dir->i_op->mkdir(dir, basename, namelen, mode & 0777 & ~current->fs->umask); + error = dir->i_op->mkdir(dir, basename, namelen, mode & 01777 & ~current->fs->umask); up(&dir->i_sem); iput(dir); return error; diff -ur --new-file old/linux/fs/ncpfs/dir.c new/linux/fs/ncpfs/dir.c --- old/linux/fs/ncpfs/dir.c Thu Jul 18 14:52:00 1996 +++ new/linux/fs/ncpfs/dir.c Wed Sep 11 16:57:15 1996 @@ -252,7 +252,7 @@ if (c_entry == NULL) { i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE; - c_entry = (struct ncp_dirent *) ncp_kmalloc(i, GFP_KERNEL); + c_entry = (struct ncp_dirent *) vmalloc(i); if (c_entry == NULL) { printk("ncp_readdir: no MEMORY for cache\n"); @@ -560,9 +560,8 @@ return; } - ncp_kfree_s(c_entry, - sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE); - c_entry = NULL; + vfree(c_entry); + c_entry = NULL; DPRINTK("ncp_free_dir_cache: exit\n"); } diff -ur --new-file old/linux/fs/nfs/rpcsock.c new/linux/fs/nfs/rpcsock.c --- old/linux/fs/nfs/rpcsock.c Thu Jun 6 20:22:24 1996 +++ new/linux/fs/nfs/rpcsock.c Sun Sep 8 18:50:21 1996 @@ -564,7 +564,7 @@ unsigned long t0 = jiffies; rsock->shutdown = 1; - while (rsock->pending || rsock->backlog) { + while (rsock->pending || waitqueue_active(&rsock->backlog)) { interruptible_sleep_on(&rsock->shutwait); if (current->signal & ~current->blocked) return -EINTR; diff -ur --new-file old/linux/fs/proc/array.c new/linux/fs/proc/array.c --- old/linux/fs/proc/array.c Mon Aug 12 08:14:46 1996 +++ new/linux/fs/proc/array.c Wed Oct 30 02:42:41 1996 @@ -307,7 +307,7 @@ static int get_version(char * buffer) { - extern char *linux_banner; + extern const char *linux_banner; strcpy(buffer, linux_banner); return strlen(buffer); @@ -860,13 +860,16 @@ * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH * + (index into the line) */ -#ifdef __alpha__ -#define MAPS_LINE_FORMAT "%016lx-%016lx %s %016lx %s %lu\n" -#define MAPS_LINE_MAX 73 /* sum of 16 1 16 1 4 1 16 1 5 1 10 1 */ -#else -#define MAPS_LINE_FORMAT "%08lx-%08lx %s %08lx %s %lu\n" -#define MAPS_LINE_MAX 49 /* sum of 8 1 8 1 4 1 8 1 5 1 10 1 */ -#endif +/* for systems with sizeof(void*) == 4: */ +#define MAPS_LINE_FORMAT4 "%08lx-%08lx %s %08lx %s %lu\n" +#define MAPS_LINE_MAX4 49 /* sum of 8 1 8 1 4 1 8 1 5 1 10 1 */ + +/* for systems with sizeof(void*) == 8: */ +#define MAPS_LINE_FORMAT8 "%016lx-%016lx %s %016lx %s %lu\n" +#define MAPS_LINE_MAX8 73 /* sum of 16 1 16 1 4 1 16 1 5 1 10 1 */ + +#define MAPS_LINE_MAX MAPS_LINE_MAX8 + static int read_maps (int pid, struct file * file, char * buf, int count) { @@ -918,7 +921,8 @@ ino = 0; } - len = sprintf(line, MAPS_LINE_FORMAT, + len = sprintf(line, + sizeof(void*) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8, map->vm_start, map->vm_end, str, map->vm_offset, kdevname(dev), ino); diff -ur --new-file old/linux/fs/proc/mem.c new/linux/fs/proc/mem.c --- old/linux/fs/proc/mem.c Thu May 2 15:00:28 1996 +++ new/linux/fs/proc/mem.c Wed Sep 11 16:57:16 1996 @@ -23,12 +23,12 @@ */ #define mem_write NULL -static int check_range(struct task_struct * tsk, unsigned long addr, int count) +static int check_range(struct mm_struct * mm, unsigned long addr, int count) { struct vm_area_struct *vma; int retval; - vma = find_vma(tsk, addr); + vma = find_vma(mm, addr); if (!vma) return -EACCES; if (vma->vm_start > addr) @@ -93,7 +93,7 @@ if (!tsk) return -ESRCH; addr = file->f_pos; - count = check_range(tsk, addr, count); + count = check_range(tsk->mm, addr, count); if (count < 0) return count; tmp = buf; diff -ur --new-file old/linux/fs/smbfs/Makefile new/linux/fs/smbfs/Makefile --- old/linux/fs/smbfs/Makefile Tue Nov 7 18:54:28 1995 +++ new/linux/fs/smbfs/Makefile Wed Sep 11 16:57:16 1996 @@ -11,4 +11,8 @@ O_OBJS := proc.o sock.o inode.o file.o dir.o ioctl.o mmap.o M_OBJS := $(O_TARGET) +# If you want debugging output, please uncomment the following line + +# EXTRA_CFLAGS += -DDEBUG_SMB=1 -DDEBUG_SMB_MALLOC=1 + include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/fs/smbfs/dir.c new/linux/fs/smbfs/dir.c --- old/linux/fs/smbfs/dir.c Wed Jul 3 11:09:32 1996 +++ new/linux/fs/smbfs/dir.c Wed Sep 11 16:57:16 1996 @@ -33,9 +33,6 @@ get_pname_static(struct inode *dir, const char *name, int len, char *path, int *res_len); -static struct inode * -smb_iget(struct inode *dir, char *path, struct smb_dirent *finfo); - static void put_pname(char *path); @@ -129,17 +126,6 @@ * smb_readdir provides a listing in the form of filling the dirent structure. * Note that dirent resides in the user space. This is to support reading of a * directory "stream". - * Arguments: - * inode --- Pointer to to the directory. - * filp --- The directory stream. (filp->f_pos indicates - * position in the stream.) - * dirent --- Will hold count directory entries. (Is in user space.) - * count --- Number of entries to be read. Should indicate the total - * buffer space available for filling with dirents. - * Return values: - * < 0 --- An error occurred (linux/errno.h). - * = 0 --- - * > 0 --- Success, amount of bytes written to dirent. * Notes: * Since we want to reduce directory lookups we revert into a * dircache. It is taken rather directly out of the nfs_readdir. @@ -462,45 +448,21 @@ assume that path is allocated for us. */ static struct inode * -smb_iget(struct inode *dir, char *path, struct smb_dirent *finfo) +smb_iget(struct inode *dir, char *path, struct smb_dirent *finfo, + struct smb_inode_info *new_inode_info) { - struct smb_dirent newent = { 0 }; struct inode *inode; - int error, len; - struct smb_inode_info *new_inode_info; + int len; struct smb_inode_info *root; - if (!dir) { - printk("smb_iget: dir is NULL\n"); + if ( (dir == NULL) || (path == NULL) || (finfo == NULL) + || (new_inode_info == NULL)) + { + printk("smb_iget: parameter is NULL\n"); return NULL; } - if (!path) { - printk("smb_iget: path is NULL\n"); - return NULL; - } - len = strlen(path); - - if (!finfo) { - error = smb_proc_getattr(&(SMB_SBP(dir->i_sb)->s_server), - path, len, &newent); - if (error) { - printk("smb_iget: getattr error = %d\n", -error); - return NULL; - } - finfo = &newent; - DPRINTK("smb_iget: Read finfo:\n"); - DPRINTK("smb_iget: finfo->attr = 0x%X\n", finfo->attr); - } - - new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info), - GFP_KERNEL); - - if (new_inode_info == NULL) { - printk("smb_iget: could not alloc mem for %s\n", path); - return NULL; - } new_inode_info->state = SMB_INODE_LOOKED_UP; new_inode_info->nused = 0; @@ -525,6 +487,10 @@ root->next = new_inode_info; if (!(inode = iget(dir->i_sb, (int)new_inode_info))) { + new_inode_info->next->prev = new_inode_info->prev; + new_inode_info->prev->next = new_inode_info->next; + SMB_INOP(dir)->nused -= 1; + printk("smb_iget: iget failed!"); return NULL; } @@ -665,6 +631,8 @@ int error; int found_in_cache; + struct smb_inode_info *new_inode_info = NULL; + *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { @@ -689,24 +657,29 @@ result_info = smb_find_inode(SMB_SERVER(dir), name); - if (result_info != 0) { - +in_tree: + if (result_info != NULL) { if (result_info->state == SMB_INODE_CACHED) result_info->state = SMB_INODE_LOOKED_UP; - put_pname(name); - /* Here we convert the inode_info address into an inode number */ *result = iget(dir->i_sb, (int)result_info); + + if (new_inode_info != NULL) + { + smb_kfree_s(new_inode_info, + sizeof(struct smb_inode_info)); + } + + put_pname(name); iput(dir); - if (*result == NULL) { - return -EACCES; - } else { - return 0; - } + if (*result == NULL) { + return -EACCES; + } + return 0; } /* Ok, now we have made our name. We have to build a new @@ -748,7 +721,19 @@ } } - if (!(*result = smb_iget(dir, name, &finfo))) { + new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info), + GFP_KERNEL); + + /* Here somebody else might have inserted the inode */ + result_info = smb_find_inode(SMB_SERVER(dir), name); + if (result_info != NULL) + { + goto in_tree; + } + + if ((*result = smb_iget(dir, name, &finfo, new_inode_info)) == NULL) + { + smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); put_pname(name); iput(dir); return -EACCES; @@ -766,6 +751,7 @@ int error; char *path = NULL; struct smb_dirent entry; + struct smb_inode_info *new_inode_info; *result = NULL; @@ -781,6 +767,15 @@ return error; } + new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info), + GFP_KERNEL); + if (new_inode_info == NULL) + { + put_pname(path); + iput(dir); + return -ENOMEM; + } + entry.attr = 0; entry.ctime = CURRENT_TIME; entry.atime = CURRENT_TIME; @@ -789,6 +784,7 @@ error = smb_proc_create(SMB_SERVER(dir), path, len, &entry); if (error < 0) { + smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); put_pname(path); iput(dir); return error; @@ -796,7 +792,9 @@ smb_invalid_dir_cache(dir->i_ino); - if (!(*result = smb_iget(dir, path, &entry)) < 0) { + if ((*result = smb_iget(dir, path, &entry, new_inode_info)) == NULL) + { + smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); put_pname(path); iput(dir); return error; diff -ur --new-file old/linux/fs/smbfs/file.c new/linux/fs/smbfs/file.c --- old/linux/fs/smbfs/file.c Wed Jun 5 12:22:47 1996 +++ new/linux/fs/smbfs/file.c Wed Sep 11 16:57:16 1996 @@ -32,7 +32,6 @@ smb_make_open(struct inode *i, int right) { struct smb_dirent *dirent; - int open_result; if (i == NULL) { printk("smb_make_open: got NULL inode\n"); @@ -45,13 +44,13 @@ if ((dirent->opened) == 0) { /* tries max. rights */ - open_result = smb_proc_open(SMB_SERVER(i), - dirent->path, dirent->len, - dirent); - if (open_result) + int open_result = smb_proc_open(SMB_SERVER(i), + dirent->path, dirent->len, + dirent); + if (open_result) + { return open_result; - - dirent->opened = 1; + } } if ( ((right == O_RDONLY) && ( (dirent->access == O_RDONLY) @@ -142,7 +141,8 @@ } static int -smb_file_write(struct inode *inode, struct file *file, const char *buf, int count) +smb_file_write(struct inode *inode, struct file *file, const char *buf, + int count) { int result, bufsize, to_write, already_written; off_t pos; diff -ur --new-file old/linux/fs/smbfs/inode.c new/linux/fs/smbfs/inode.c --- old/linux/fs/smbfs/inode.c Wed Jun 5 12:22:48 1996 +++ new/linux/fs/smbfs/inode.c Thu Nov 7 13:41:14 1996 @@ -121,28 +121,37 @@ static void smb_put_inode(struct inode *inode) { - struct smb_dirent *finfo = SMB_FINFO(inode); + struct smb_dirent *finfo = SMB_FINFO(inode); + struct smb_server *server = SMB_SERVER(inode); + struct smb_inode_info *info = SMB_INOP(inode); + + int opened = finfo->opened; + int mtime = inode->i_mtime; + int file_id = finfo->fileid; + int isdir = S_ISDIR(inode->i_mode); + unsigned long ino = inode->i_ino; - if (finfo->opened != 0) { + /* Remove the inode before closing the file, because the close + will sleep. This hopefully removes a race condition. */ - /* smb_proc_close wants mtime in finfo */ - finfo->mtime = inode->i_mtime; - - if (smb_proc_close(SMB_SERVER(inode), finfo)) { - /* We can't do anything but complain. */ - printk("smb_put_inode: could not close\n"); - } - } - - smb_free_inode_info(SMB_INOP(inode)); + clear_inode(inode); + smb_free_inode_info(info); - if (S_ISDIR(inode->i_mode)) { + if (isdir) + { DDPRINTK("smb_put_inode: put directory %ld\n", inode->i_ino); - smb_invalid_dir_cache(inode->i_ino); + smb_invalid_dir_cache(ino); } - clear_inode(inode); + if (opened != 0) + { + if (smb_proc_close(server, file_id, mtime)) + { + /* We can't do anything but complain. */ + DPRINTK("smb_put_inode: could not close\n"); + } + } } static void @@ -151,6 +160,7 @@ struct smb_server *server = &(SMB_SBP(sb)->s_server); smb_proc_disconnect(server); + smb_dont_catch_keepalive(server); close_fp(server->sock_file); lock_super(sb); @@ -299,6 +309,7 @@ fail: filp->f_count -= 1; + smb_dont_catch_keepalive(server); smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info)); return NULL; } diff -ur --new-file old/linux/fs/smbfs/proc.c new/linux/fs/smbfs/proc.c --- old/linux/fs/smbfs/proc.c Thu Jul 11 12:52:06 1996 +++ new/linux/fs/smbfs/proc.c Wed Sep 11 16:57:16 1996 @@ -545,6 +545,13 @@ smb_lock_server(server); + if (entry->opened != 0) + { + /* Somebody else opened the file while we slept */ + smb_unlock_server(server); + return 0; + } + retry: p = smb_setup_header(server, SMBopen, 2, 2 + len); WSET(buf, smb_vwv0, 0x42); /* read/write */ @@ -585,9 +592,11 @@ entry->size = DVAL(buf, smb_vwv4); entry->access = WVAL(buf, smb_vwv6); + entry->opened = 1; + entry->access &= 3; + smb_unlock_server(server); - entry->access &= 3; DPRINTK("smb_proc_open: entry->access = %d\n", entry->access); return 0; } @@ -595,13 +604,14 @@ /* smb_proc_close: in finfo->mtime we can send a modification time to the server */ int -smb_proc_close(struct smb_server *server, struct smb_dirent *finfo) +smb_proc_close(struct smb_server *server, + __u16 fileid, __u32 mtime) { char *buf = server->packet; smb_setup_header_exclusive(server, SMBclose, 3, 0); - WSET(buf, smb_vwv0, finfo->fileid); - DSET(buf, smb_vwv1, utc2local(finfo->mtime)); + WSET(buf, smb_vwv0, fileid); + DSET(buf, smb_vwv1, utc2local(mtime)); return smb_request_ok_unlock(server, SMBclose, 0, 0); } @@ -756,24 +766,25 @@ } -/* smb_proc_do_create: We expect entry->attry & entry->ctime to be set. */ +/* smb_proc_create: We expect entry->attr & entry->ctime to be set. */ -static int -smb_proc_do_create(struct smb_server *server, const char *path, int len, - struct smb_dirent *entry, word command) +int +smb_proc_create(struct smb_server *server, const char *path, int len, + struct smb_dirent *entry) { int error; char *p; char *buf = server->packet; + __u16 fileid; smb_lock_server(server); retry: - p = smb_setup_header(server, command, 3, len + 2); + p = smb_setup_header(server, SMBcreate, 3, len + 2); WSET(buf, smb_vwv0, entry->attr); DSET(buf, smb_vwv1, utc2local(entry->ctime)); smb_encode_ascii(p, path, len); - if ((error = smb_request_ok(server, command, 1, 0)) < 0) { + if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0) { if (smb_retry(server)) { goto retry; } @@ -781,28 +792,14 @@ return error; } - entry->opened = 1; - entry->fileid = WVAL(buf, smb_vwv0); + entry->opened = 0; + fileid = WVAL(buf, smb_vwv0); smb_unlock_server(server); - smb_proc_close(server, entry); + smb_proc_close(server, fileid, 0); return 0; } - -int -smb_proc_create(struct smb_server *server, const char *path, int len, - struct smb_dirent *entry) -{ - return smb_proc_do_create(server, path, len, entry, SMBcreate); -} - -int -smb_proc_mknew(struct smb_server *server, const char *path, int len, - struct smb_dirent *entry) -{ - return smb_proc_do_create(server, path, len, entry, SMBmknew); -} int smb_proc_mv(struct smb_server *server, @@ -1154,6 +1151,7 @@ char *p; char *lastname; + int lastname_len; int i; int first, total_count; struct smb_dirent *current_entry; @@ -1286,16 +1284,16 @@ p = resp_param; if (first != 0) { - ff_dir_handle = WVAL(p,0); + ff_dir_handle = WVAL(p,0); ff_searchcount = WVAL(p,2); - ff_eos = WVAL(p,4); - ff_lastname = WVAL(p,8); + ff_eos = WVAL(p,4); + ff_lastname = WVAL(p,8); } else { ff_searchcount = WVAL(p,0); - ff_eos = WVAL(p,2); - ff_lastname = WVAL(p,6); + ff_eos = WVAL(p,2); + ff_lastname = WVAL(p,6); } if (ff_searchcount == 0) @@ -1306,16 +1304,19 @@ /* we might need the lastname for continuations */ lastname = ""; + lastname_len = 0; if (ff_lastname > 0) { switch(info_level) { case 260: - lastname = p + ff_lastname + 94; + lastname = p + ff_lastname; + lastname_len = resp_data_len - ff_lastname; ff_resume_key = 0; break; case 1: lastname = p + ff_lastname + 1; + lastname_len = strlen(lastname); ff_resume_key = 0; break; } @@ -1330,12 +1331,14 @@ mask = smb_kmalloc(dirlen, GFP_KERNEL); if (mask == NULL) { - printk("smb_proc_readdir_long: Memory allocation failed\n"); + printk("smb_proc_readdir_long: " + "Memory allocation failed\n"); result = -ENOMEM; break; } - strcpy(mask, lastname); } + strncpy(mask, lastname, lastname_len); + mask[lastname_len] = '\0'; /* Now we are ready to parse smb directory entries. */ @@ -1475,6 +1478,8 @@ int result = 0; struct smb_dirent temp_entry; + memset(&temp_entry, 0, sizeof(temp_entry)); + if ((result=smb_proc_open(server,path,len, &temp_entry)) < 0) { /* We cannot open directories, so we try to use the @@ -1489,8 +1494,8 @@ entry->ctime = temp_entry.ctime; entry->size = temp_entry.size; } - - smb_proc_close(server, &temp_entry); + + smb_proc_close(server, temp_entry.fileid, temp_entry.mtime); return result; } else { @@ -1824,7 +1829,6 @@ smb_decode_word(server->packet+32, &(server->server_uid)); } else - { server->maxxmt = 0; server->maxmux = 0; @@ -1838,6 +1842,7 @@ smb_setup_header(server, SMBtcon, 0, 6 + strlen(server->m.service) + strlen(server->m.password) + strlen(dev)); + p = SMB_BUF(server->packet); p = smb_encode_ascii(p, server->m.service, strlen(server->m.service)); p = smb_encode_ascii(p,server->m.password, strlen(server->m.password)); @@ -1891,11 +1896,14 @@ { int result; smb_lock_server(server); + result = smb_proc_reconnect(server); + if ((result < 0) && (server->packet != NULL)) { smb_kfree_s(server->packet, server->max_xmit); server->packet = NULL; } + smb_unlock_server(server); return result; } diff -ur --new-file old/linux/fs/smbfs/sock.c new/linux/fs/smbfs/sock.c --- old/linux/fs/smbfs/sock.c Wed Jun 5 12:22:48 1996 +++ new/linux/fs/smbfs/sock.c Wed Sep 11 16:57:16 1996 @@ -24,8 +24,9 @@ #define _S(nr) (1<<((nr)-1)) -static int _recvfrom(struct socket *sock, unsigned char *ubuf, int size, int noblock, unsigned flags, - struct sockaddr_in *sa, int *addr_len) +static int +_recvfrom(struct socket *sock, unsigned char *ubuf, int size, + int noblock, unsigned flags, struct sockaddr_in *sa, int *addr_len) { struct iovec iov; struct msghdr msg; @@ -44,7 +45,10 @@ return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len); } -static int _send(struct socket *sock, const void *buff, int len, int nonblock, unsigned flags) { +static int +_send(struct socket *sock, const void *buff, int len, + int nonblock, unsigned flags) +{ struct iovec iov; struct msghdr msg; diff -ur --new-file old/linux/fs/super.c new/linux/fs/super.c --- old/linux/fs/super.c Tue Aug 6 08:10:48 1996 +++ new/linux/fs/super.c Wed Oct 30 00:58:59 1996 @@ -146,6 +146,8 @@ if (vfsmnttail->mnt_dev == dev) vfsmnttail = lptr; } + if (tofree == mru_vfsmnt) + mru_vfsmnt = NULL; kfree(tofree->mnt_devname); kfree(tofree->mnt_dirname); kfree_s(tofree, sizeof(struct vfsmount)); @@ -794,7 +796,7 @@ if (!data) return 0; - vma = find_vma(current, (unsigned long) data); + vma = find_vma(current->mm, (unsigned long) data); if (!vma || (unsigned long) data < vma->vm_start) return -EFAULT; if (!(vma->vm_flags & VM_READ)) @@ -948,7 +950,7 @@ current->fs->root = inode; ROOT_DEV = sb->s_dev; printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n"); - vfsmnt = add_vfsmnt(ROOT_DEV, "rootfs", "/"); + vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/"); if (!vfsmnt) panic("VFS: add_vfsmnt failed for NFS root.\n"); vfsmnt->mnt_sb = sb; @@ -1010,7 +1012,7 @@ printk ("VFS: Mounted root (%s filesystem)%s.\n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); - vfsmnt = add_vfsmnt(ROOT_DEV, "rootfs", "/"); + vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/"); if (!vfsmnt) panic("VFS: add_vfsmnt failed for root fs"); vfsmnt->mnt_sb = sb; @@ -1075,7 +1077,7 @@ } iput(old_root); /* sb->s_covered */ remove_vfsmnt(old_root_dev); - vfsmnt = add_vfsmnt(old_root_dev,"old_rootfs",put_old); + vfsmnt = add_vfsmnt(old_root_dev,"/dev/root.old",put_old); if (!vfsmnt) printk(KERN_CRIT "Trouble: add_vfsmnt failed\n"); else { vfsmnt->mnt_sb = old_root->i_sb; diff -ur --new-file old/linux/fs/umsdos/inode.c new/linux/fs/umsdos/inode.c --- old/linux/fs/umsdos/inode.c Mon Mar 11 10:25:58 1996 +++ new/linux/fs/umsdos/inode.c Sun Sep 8 18:50:21 1996 @@ -227,7 +227,7 @@ if (S_ISDIR(inode->i_mode) && (inode->u.umsdos_i.u.dir_info.creating != 0 || inode->u.umsdos_i.u.dir_info.looking != 0 - || inode->u.umsdos_i.u.dir_info.p != NULL)){ + || waitqueue_active(&inode->u.umsdos_i.u.dir_info.p))){ Printk (("read inode %d %d %p\n" ,inode->u.umsdos_i.u.dir_info.creating ,inode->u.umsdos_i.u.dir_info.looking diff -ur --new-file old/linux/include/asm-alpha/atomic.h new/linux/include/asm-alpha/atomic.h --- old/linux/include/asm-alpha/atomic.h Thu Apr 4 13:48:02 1996 +++ new/linux/include/asm-alpha/atomic.h Sun Sep 15 09:34:18 1996 @@ -51,11 +51,30 @@ } /* - * Same as above, but return true if we counted down to zero + * Same as above, but return the result value */ -extern __inline__ int atomic_sub_and_test(atomic_t i, atomic_t * v) +extern __inline__ long atomic_add_return(atomic_t i, atomic_t * v) { - unsigned long temp, result; + long temp, result; + __asm__ __volatile__( + "\n1:\t" + "ldl_l %0,%1\n\t" + "addl %0,%3,%0\n\t" + "bis %0,%0,%2\n\t" + "stl_c %0,%1\n\t" + "beq %0,1b\n" + "2:" + :"=&r" (temp), + "=m" (__atomic_fool_gcc(v)), + "=&r" (result) + :"Ir" (i), + "m" (__atomic_fool_gcc(v))); + return result; +} + +extern __inline__ long atomic_sub_return(atomic_t i, atomic_t * v) +{ + long temp, result; __asm__ __volatile__( "\n1:\t" "ldl_l %0,%1\n\t" @@ -69,11 +88,16 @@ "=&r" (result) :"Ir" (i), "m" (__atomic_fool_gcc(v))); - return result==0; + return result; } +#define atomic_dec_return(v) atomic_sub_return(1,(v)) +#define atomic_inc_return(v) atomic_add_return(1,(v)) + +#define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0) + #define atomic_inc(v) atomic_add(1,(v)) #define atomic_dec(v) atomic_sub(1,(v)) -#define atomic_dec_and_test(v) atomic_sub_and_test(1,(v)) #endif diff -ur --new-file old/linux/include/asm-alpha/processor.h new/linux/include/asm-alpha/processor.h --- old/linux/include/asm-alpha/processor.h Sun Aug 18 09:37:57 1996 +++ new/linux/include/asm-alpha/processor.h Sun Sep 15 09:34:18 1996 @@ -59,7 +59,7 @@ 0 \ } -#define alloc_kernel_stack() get_free_page(GFP_KERNEL) +#define alloc_kernel_stack() __get_free_page(GFP_KERNEL) #define free_kernel_stack(page) free_page((page)) #include diff -ur --new-file old/linux/include/asm-alpha/semaphore.h new/linux/include/asm-alpha/semaphore.h --- old/linux/include/asm-alpha/semaphore.h Thu Jan 1 01:00:00 1970 +++ new/linux/include/asm-alpha/semaphore.h Sun Sep 15 09:34:18 1996 @@ -0,0 +1,43 @@ +#ifndef _ALPHA_SEMAPHORE_H +#define _ALPHA_SEMAPHORE_H + +/* + * SMP- and interrupt-safe semaphores.. + * + * (C) Copyright 1996 Linus Torvalds + */ + +#include + +struct semaphore { + atomic_t count; + atomic_t waiting; + struct wait_queue * wait; +}; + +#define MUTEX ((struct semaphore) { 1, 0, NULL }) +#define MUTEX_LOCKED ((struct semaphore) { 0, 0, NULL }) + +extern void __down(struct semaphore * sem); +extern void __up(struct semaphore * sem); + +/* + * This isn't quite as clever as the x86 side, but the gp register + * makes things a bit more complicated on the alpha.. + */ +extern inline void down(struct semaphore * sem) +{ + for (;;) { + if (atomic_dec_return(&sem->count) >= 0) + break; + __down(sem); + } +} + +extern inline void up(struct semaphore * sem) +{ + if (atomic_inc_return(&sem->count) <= 0) + __up(sem); +} + +#endif diff -ur --new-file old/linux/include/asm-alpha/system.h new/linux/include/asm-alpha/system.h --- old/linux/include/asm-alpha/system.h Sun Aug 4 12:37:59 1996 +++ new/linux/include/asm-alpha/system.h Thu Aug 29 18:15:14 1996 @@ -38,11 +38,12 @@ * (assuming they are running OSF/1 PALcode, I guess). */ struct el_common { - unsigned int size; /* size in bytes of logout area */ - int sbz1 : 31; /* should be zero */ - char retry : 1; /* retry flag */ - unsigned int proc_offset; /* processor-specific offset */ - unsigned int sys_offset; /* system-specific offset */ + unsigned int size; /* size in bytes of logout area */ + int sbz1 : 31; /* should be zero */ + char retry : 1; /* retry flag */ + unsigned int proc_offset; /* processor-specific offset */ + unsigned int sys_offset; /* system-specific offset */ + unsigned long code; /* machine check code */ }; extern void wrent(void *, unsigned long); diff -ur --new-file old/linux/include/asm-i386/irq.h new/linux/include/asm-i386/irq.h --- old/linux/include/asm-i386/irq.h Sun Mar 10 08:28:56 1996 +++ new/linux/include/asm-i386/irq.h Wed Sep 11 16:57:17 1996 @@ -81,7 +81,7 @@ * a bit - without them it seems that the harddisk driver won't work on * all hardware. Arghh. */ -#define ACK_FIRST(mask) \ +#define ACK_FIRST(mask,nr) \ "inb $0x21,%al\n\t" \ "jmp 1f\n" \ "1:\tjmp 1f\n" \ @@ -93,7 +93,7 @@ "1:\tmovb $0x20,%al\n\t" \ "outb %al,$0x20\n\t" -#define ACK_SECOND(mask) \ +#define ACK_SECOND(mask,nr) \ "inb $0xA1,%al\n\t" \ "jmp 1f\n" \ "1:\tjmp 1f\n" \ @@ -207,7 +207,7 @@ "pushl $-"#nr"-2\n\t" \ SAVE_ALL \ ENTER_KERNEL \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\ "sti\n\t" \ "movl %esp,%ebx\n\t" \ @@ -224,7 +224,7 @@ SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \ SAVE_MOST \ ENTER_KERNEL \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ "incl "SYMBOL_NAME_STR(intr_count)"\n\t" \ "pushl $" #nr "\n\t" \ "call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \ @@ -238,7 +238,7 @@ SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \ SAVE_MOST \ ENTER_KERNEL \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ LEAVE_KERNEL \ RESTORE_MOST); @@ -255,7 +255,7 @@ "pushl $-"#nr"-2\n\t" \ SAVE_ALL \ ENTER_KERNEL \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\ "movl %esp,%ebx\n\t" \ "pushl %ebx\n\t" \ @@ -283,7 +283,7 @@ "pushl $-"#nr"-2\n\t" \ SAVE_ALL \ ENTER_KERNEL \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\ "sti\n\t" \ "movl %esp,%ebx\n\t" \ @@ -301,7 +301,7 @@ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \ SAVE_MOST \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ SMP_PROF_IPI_CNT \ "pushl $" #nr "\n\t" \ "call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \ @@ -312,7 +312,7 @@ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \ SAVE_MOST \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ RESTORE_MOST); #define BUILD_RESCHEDIRQ(nr) \ @@ -345,7 +345,7 @@ SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ "pushl $-"#nr"-2\n\t" \ SAVE_ALL \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\ "sti\n\t" \ "movl %esp,%ebx\n\t" \ @@ -360,7 +360,7 @@ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \ SAVE_MOST \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ "incl "SYMBOL_NAME_STR(intr_count)"\n\t" \ "pushl $" #nr "\n\t" \ "call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \ @@ -372,7 +372,7 @@ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \ SAVE_MOST \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ RESTORE_MOST); #define BUILD_TIMER_IRQ(chip,nr,mask) \ @@ -386,7 +386,7 @@ SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ "pushl $-"#nr"-2\n\t" \ SAVE_ALL \ - ACK_##chip(mask) \ + ACK_##chip(mask,(nr&7)) \ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\ "movl %esp,%ebx\n\t" \ "pushl %ebx\n\t" \ diff -ur --new-file old/linux/include/asm-i386/pgtable.h new/linux/include/asm-i386/pgtable.h --- old/linux/include/asm-i386/pgtable.h Wed Jul 17 09:35:40 1996 +++ new/linux/include/asm-i386/pgtable.h Thu Oct 31 11:06:09 1996 @@ -53,6 +53,7 @@ #define flush_tlb() __flush_tlb() #define flush_tlb_all() __flush_tlb() +#define local_flush_tlb() __flush_tlb() static inline void flush_tlb_mm(struct mm_struct *mm) { @@ -87,7 +88,7 @@ __flush_tlb() -#undef CLEVER_SMP_INVALIDATE +#define CLEVER_SMP_INVALIDATE #ifdef CLEVER_SMP_INVALIDATE /* @@ -96,9 +97,6 @@ * * These mean you can really definitely utterly forget about * writing to user space from interrupts. (Its not allowed anyway). - * - * Doesn't currently work as Linus makes flush tlb calls before - * stuff like current/current->mm are setup properly */ static inline void flush_tlb_current_task(void) diff -ur --new-file old/linux/include/asm-i386/processor.h new/linux/include/asm-i386/processor.h --- old/linux/include/asm-i386/processor.h Thu May 9 06:58:57 1996 +++ new/linux/include/asm-i386/processor.h Sun Sep 15 09:34:18 1996 @@ -131,7 +131,7 @@ NULL, 0, 0, 0, 0 /* vm86_info */ \ } -#define alloc_kernel_stack() get_free_page(GFP_KERNEL) +#define alloc_kernel_stack() __get_free_page(GFP_KERNEL) #define free_kernel_stack(page) free_page((page)) static inline void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp) diff -ur --new-file old/linux/include/asm-i386/semaphore.h new/linux/include/asm-i386/semaphore.h --- old/linux/include/asm-i386/semaphore.h Thu Jan 1 01:00:00 1970 +++ new/linux/include/asm-i386/semaphore.h Fri Oct 18 13:10:45 1996 @@ -0,0 +1,70 @@ +#ifndef _I386_SEMAPHORE_H +#define _I386_SEMAPHORE_H + +#include + +/* + * SMP- and interrupt-safe semaphores.. + * + * (C) Copyright 1996 Linus Torvalds + */ + +struct semaphore { + int count; + int waiting; + struct wait_queue * wait; +}; + +#define MUTEX ((struct semaphore) { 1, 0, NULL }) +#define MUTEX_LOCKED ((struct semaphore) { 0, 0, NULL }) + +asmlinkage void down_failed(void /* special register calling convention */); +asmlinkage void up_wakeup(void /* special register calling convention */); + +extern void __down(struct semaphore * sem); +extern void __up(struct semaphore * sem); + +/* + * This is ugly, but we want the default case to fall through. + * "down_failed" is a special asm handler that calls the C + * routine that actually waits. See arch/i386/lib/semaphore.S + */ +extern inline void down(struct semaphore * sem) +{ + __asm__ __volatile__( + "# atomic down operation\n" + "1:\n\t" + "movl $1b,%%eax\n\t" +#ifdef __SMP__ + "lock ; " +#endif + "decl 0(%0)\n\t" + "js " SYMBOL_NAME_STR(down_failed) + :/* no outputs */ + :"c" (sem) + :"ax","dx","memory"); +} + +/* + * Note! This is subtle. We jump to wake people up only if + * the semaphore was negative (== somebody was waiting on it). + * The default case (no contention) will result in NO + * jumps for both down() and up(). + */ +extern inline void up(struct semaphore * sem) +{ + __asm__ __volatile__( + "# atomic up operation\n\t" + "movl $1f,%%eax\n\t" +#ifdef __SMP__ + "lock ; " +#endif + "incl 0(%0)\n\t" + "jle " SYMBOL_NAME_STR(up_wakeup) + "\n1:" + :/* no outputs */ + :"c" (sem) + :"ax", "dx", "memory"); +} + +#endif diff -ur --new-file old/linux/include/linux/atmdev.h new/linux/include/linux/atmdev.h --- old/linux/include/linux/atmdev.h Sat Nov 16 00:48:03 1996 +++ new/linux/include/linux/atmdev.h Sat Nov 16 00:48:31 1996 @@ -235,6 +235,8 @@ void (*feedback)(struct atm_vcc *vcc,struct sk_buff *skb, unsigned long start,unsigned long dest,int len); int (*change_qos)(struct atm_vcc *vcc,struct atm_qos *qos); + void (*free_rx_skb)(struct atm_vcc *vcc, struct sk_buff *skb); + /* @@@ temporary hack */ }; diff -ur --new-file old/linux/include/linux/cdrom.h new/linux/include/linux/cdrom.h --- old/linux/include/linux/cdrom.h Thu May 9 09:35:12 1996 +++ new/linux/include/linux/cdrom.h Fri Sep 20 16:00:36 1996 @@ -280,6 +280,8 @@ /* * For controlling a changer. (Used by ATAPI driver.) + * This ioctl is depreciated in favor of CDROM_SELECT_DISC from + * ucdrom.h. It will probably be deleted during the 2.1 kernel series. */ #define CDROMLOADFROMSLOT 0x531a /* LOAD disk from slot*/ diff -ur --new-file old/linux/include/linux/fddidevice.h new/linux/include/linux/fddidevice.h --- old/linux/include/linux/fddidevice.h Thu Jan 1 01:00:00 1970 +++ new/linux/include/linux/fddidevice.h Wed Oct 30 02:42:41 1996 @@ -0,0 +1,42 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the FDDI handlers. + * + * Version: @(#)fddidevice.h 1.0.0 08/12/96 + * + * Author: Lawrence V. Stefani, + * + * fddidevice.h is based on previous trdevice.h work by + * Ross Biro, + * Fred N. van Kempen, + * Alan Cox, + * + * 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. + */ +#ifndef _LINUX_FDDIDEVICE_H +#define _LINUX_FDDIDEVICE_H + +#include + +#ifdef __KERNEL__ +extern int fddi_header(struct sk_buff *skb, + struct device *dev, + unsigned short type, + void *daddr, + void *saddr, + unsigned len); +extern int fddi_rebuild_header(void *buff, + struct device *dev, + unsigned long dest, + struct sk_buff *skb); +extern unsigned short fddi_type_trans(struct sk_buff *skb, + struct device *dev); +#endif + +#endif /* _LINUX_FDDIDEVICE_H */ diff -ur --new-file old/linux/include/linux/fs.h new/linux/include/linux/fs.h --- old/linux/include/linux/fs.h Tue Aug 20 17:09:33 1996 +++ new/linux/include/linux/fs.h Thu Oct 31 11:04:58 1996 @@ -48,8 +48,8 @@ #define READ 0 #define WRITE 1 -#define READA 2 /* read-ahead - don't pause */ -#define WRITEA 3 /* "write-ahead" - silly, but somewhat useful */ +#define READA 2 /* read-ahead - don't block if no resources */ +#define WRITEA 3 /* write-ahead - don't block if no resources */ #ifndef NULL #define NULL ((void *) 0) @@ -119,6 +119,7 @@ #ifdef __KERNEL__ +#include #include extern void buffer_init(void); @@ -653,7 +654,7 @@ extern int generic_readpage(struct inode *, struct page *); extern int generic_file_read(struct inode *, struct file *, char *, int); extern int generic_file_mmap(struct inode *, struct file *, struct vm_area_struct *); -extern int brw_page(int, unsigned long, kdev_t, int [], int, int); +extern int brw_page(int, struct page *, kdev_t, int [], int, int); extern void put_super(kdev_t dev); unsigned long generate_cluster(kdev_t dev, int b[], int size); diff -ur --new-file old/linux/include/linux/icmp.h new/linux/include/linux/icmp.h --- old/linux/include/linux/icmp.h Wed Jul 12 05:41:59 1995 +++ new/linux/include/linux/icmp.h Sun Oct 6 15:42:09 1996 @@ -46,6 +46,10 @@ #define ICMP_HOST_ANO 10 #define ICMP_NET_UNR_TOS 11 #define ICMP_HOST_UNR_TOS 12 +#define ICMP_PKT_FILTERED 13 /* Packet filtered */ +#define ICMP_PREC_VIOLATION 14 /* Precedence violation */ +#define ICMP_PREC_CUTOFF 15 /* Precedence cut off */ +#define NR_ICMP_UNREACH 15 /* instead of hardcoding immediate value */ /* Codes for REDIRECT. */ #define ICMP_REDIR_NET 0 /* Redirect Net */ diff -ur --new-file old/linux/include/linux/if_arp.h new/linux/include/linux/if_arp.h --- old/linux/include/linux/if_arp.h Tue Aug 20 17:11:38 1996 +++ new/linux/include/linux/if_arp.h Thu Oct 31 11:06:34 1996 @@ -5,14 +5,15 @@ * * Global definitions for the ARP (RFC 826) protocol. * - * Version: @(#)if_arp.h 1.0.1 04/16/93 + * Version: @(#)if_arp.h 1.0.2 08/12/96 * * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988 * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source. * Ross Biro, * Fred N. van Kempen, * Florian La Roche, - * Jonathan Layes + * Jonathan Layes, + * Lawrence V. Stefani, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,6 +53,7 @@ #define ARPHRD_SKIP 771 /* SKIP vif */ #define ARPHRD_LOOPBACK 772 /* Loopback device */ #define ARPHRD_LOCALTLK 773 /* Localtalk device */ +#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */ /* ARP protocol opcodes. */ #define ARPOP_REQUEST 1 /* ARP request */ diff -ur --new-file old/linux/include/linux/if_fddi.h new/linux/include/linux/if_fddi.h --- old/linux/include/linux/if_fddi.h Thu Jan 1 01:00:00 1970 +++ new/linux/include/linux/if_fddi.h Wed Oct 30 02:42:41 1996 @@ -0,0 +1,202 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the ANSI FDDI interface. + * + * Version: @(#)if_fddi.h 1.0.1 09/16/96 + * + * Author: Lawrence V. Stefani, + * + * if_fddi.h is based on previous if_ether.h and if_tr.h work by + * Fred N. van Kempen, + * Donald Becker, + * Alan Cox, + * Steve Whitehouse, + * Peter De Schrijver, + * + * 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. + */ +#ifndef _LINUX_IF_FDDI_H +#define _LINUX_IF_FDDI_H + +/* + * Define max and min legal sizes. The frame sizes do not include + * 4 byte FCS/CRC (frame check sequence). + */ +#define FDDI_K_ALEN 6 /* Octets in one FDDI address */ +#define FDDI_K_8022_HLEN 16 /* Total octets in 802.2 header */ +#define FDDI_K_SNAP_HLEN 21 /* Total octets in 802.2 SNAP header */ +#define FDDI_K_8022_ZLEN 16 /* Min octets in 802.2 frame sans FCS */ +#define FDDI_K_SNAP_ZLEN 21 /* Min octets in 802.2 SNAP frame sans FCS */ +#define FDDI_K_8022_DLEN 4475 /* Max octets in 802.2 payload */ +#define FDDI_K_SNAP_DLEN 4470 /* Max octets in 802.2 SNAP payload */ +#define FDDI_K_LLC_ZLEN 13 /* Min octets in LLC frame sans FCS */ +#define FDDI_K_LLC_LEN 4491 /* Max octets in LLC frame sans FCS */ + +/* Define FDDI Frame Control (FC) Byte values */ +#define FDDI_FC_K_VOID 0x00 +#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80 +#define FDDI_FC_K_RESTRICTED_TOKEN 0xC0 +#define FDDI_FC_K_SMT_MIN 0x41 +#define FDDI_FC_K_SMT_MAX 0x4F +#define FDDI_FC_K_MAC_MIN 0xC1 +#define FDDI_FC_K_MAC_MAX 0xCF +#define FDDI_FC_K_ASYNC_LLC_MIN 0x50 +#define FDDI_FC_K_ASYNC_LLC_DEF 0x54 +#define FDDI_FC_K_ASYNC_LLC_MAX 0x5F +#define FDDI_FC_K_SYNC_LLC_MIN 0xD0 +#define FDDI_FC_K_SYNC_LLC_MAX 0xD7 +#define FDDI_FC_K_IMPLEMENTOR_MIN 0x60 +#define FDDI_FC_K_IMPLEMENTOR_MAX 0x6F +#define FDDI_FC_K_RESERVED_MIN 0x70 +#define FDDI_FC_K_RESERVED_MAX 0x7F + +/* Define LLC and SNAP constants */ +#define FDDI_EXTENDED_SAP 0xAA +#define FDDI_UI_CMD 0x03 + +/* Define 802.2 Type 1 header */ +struct fddi_8022_1_hdr + { + __u8 dsap; /* destination service access point */ + __u8 ssap; /* source service access point */ + __u8 ctrl; /* control byte #1 */ + } __attribute__ ((packed)); + +/* Define 802.2 Type 2 header */ +struct fddi_8022_2_hdr + { + __u8 dsap; /* destination service access point */ + __u8 ssap; /* source service access point */ + __u8 ctrl_1; /* control byte #1 */ + __u8 ctrl_2; /* control byte #2 */ + } __attribute__ ((packed)); + +/* Define 802.2 SNAP header */ +#define FDDI_K_OUI_LEN 3 +struct fddi_snap_hdr + { + __u8 dsap; /* always 0xAA */ + __u8 ssap; /* always 0xAA */ + __u8 ctrl; /* always 0x03 */ + __u8 oui[FDDI_K_OUI_LEN]; /* organizational universal id */ + __u16 ethertype; /* packet type ID field */ + } __attribute__ ((packed)); + +/* Define FDDI LLC frame header */ +struct fddihdr + { + __u8 fc; /* frame control */ + __u8 daddr[FDDI_K_ALEN]; /* destination address */ + __u8 saddr[FDDI_K_ALEN]; /* source address */ + union + { + struct fddi_8022_1_hdr llc_8022_1; + struct fddi_8022_2_hdr llc_8022_2; + struct fddi_snap_hdr llc_snap; + } hdr; + } __attribute__ ((packed)); + +/* Define FDDI statistics structure */ +struct fddi_statistics + { + __u32 rx_packets; /* total packets received */ + __u32 tx_packets; /* total packets transmitted */ + __u32 rx_errors; /* bad packets received */ + __u32 tx_errors; /* packet transmit problems */ + __u32 rx_dropped; /* no space in linux buffers */ + __u32 tx_dropped; /* no space available in linux */ + __u32 multicast; /* multicast packets received */ + __u32 transmit_collision; /* always 0 for FDDI */ + + /* Detailed FDDI statistics. Adopted from RFC 1512 */ + + __u8 smt_station_id[8]; + __u32 smt_op_version_id; + __u32 smt_hi_version_id; + __u32 smt_lo_version_id; + __u8 smt_user_data[32]; + __u32 smt_mib_version_id; + __u32 smt_mac_cts; + __u32 smt_non_master_cts; + __u32 smt_master_cts; + __u32 smt_available_paths; + __u32 smt_config_capabilities; + __u32 smt_config_policy; + __u32 smt_connection_policy; + __u32 smt_t_notify; + __u32 smt_stat_rpt_policy; + __u32 smt_trace_max_expiration; + __u32 smt_bypass_present; + __u32 smt_ecm_state; + __u32 smt_cf_state; + __u32 smt_remote_disconnect_flag; + __u32 smt_station_status; + __u32 smt_peer_wrap_flag; + __u32 smt_time_stamp; + __u32 smt_transition_time_stamp; + __u32 mac_frame_status_functions; + __u32 mac_t_max_capability; + __u32 mac_tvx_capability; + __u32 mac_available_paths; + __u32 mac_current_path; + __u8 mac_upstream_nbr[FDDI_K_ALEN]; + __u8 mac_downstream_nbr[FDDI_K_ALEN]; + __u8 mac_old_upstream_nbr[FDDI_K_ALEN]; + __u8 mac_old_downstream_nbr[FDDI_K_ALEN]; + __u32 mac_dup_address_test; + __u32 mac_requested_paths; + __u32 mac_downstream_port_type; + __u8 mac_smt_address[FDDI_K_ALEN]; + __u32 mac_t_req; + __u32 mac_t_neg; + __u32 mac_t_max; + __u32 mac_tvx_value; + __u32 mac_frame_cts; + __u32 mac_copied_cts; + __u32 mac_transmit_cts; + __u32 mac_error_cts; + __u32 mac_lost_cts; + __u32 mac_frame_error_threshold; + __u32 mac_frame_error_ratio; + __u32 mac_rmt_state; + __u32 mac_da_flag; + __u32 mac_una_da_flag; + __u32 mac_frame_error_flag; + __u32 mac_ma_unitdata_available; + __u32 mac_hardware_present; + __u32 mac_ma_unitdata_enable; + __u32 path_tvx_lower_bound; + __u32 path_t_max_lower_bound; + __u32 path_max_t_req; + __u32 path_configuration[8]; + __u32 port_my_type[2]; + __u32 port_neighbor_type[2]; + __u32 port_connection_policies[2]; + __u32 port_mac_indicated[2]; + __u32 port_current_path[2]; + __u8 port_requested_paths[3*2]; + __u32 port_mac_placement[2]; + __u32 port_available_paths[2]; + __u32 port_pmd_class[2]; + __u32 port_connection_capabilities[2]; + __u32 port_bs_flag[2]; + __u32 port_lct_fail_cts[2]; + __u32 port_ler_estimate[2]; + __u32 port_lem_reject_cts[2]; + __u32 port_lem_cts[2]; + __u32 port_ler_cutoff[2]; + __u32 port_ler_alarm[2]; + __u32 port_connect_state[2]; + __u32 port_pcm_state[2]; + __u32 port_pc_withhold[2]; + __u32 port_ler_flag[2]; + __u32 port_hardware_present[2]; + }; + +#endif /* _LINUX_IF_FDDI_H */ diff -ur --new-file old/linux/include/linux/isdn.h new/linux/include/linux/isdn.h --- old/linux/include/linux/isdn.h Sat Jun 29 19:36:23 1996 +++ new/linux/include/linux/isdn.h Sun Sep 1 08:15:33 1996 @@ -1,4 +1,4 @@ -/* $Id: isdn.h,v 1.15 1996/06/15 14:56:57 fritz Exp $ +/* $Id: isdn.h,v 1.16 1996/08/12 16:20:56 hipp Exp $ * * Main header for the Linux ISDN subsystem (linklevel). * @@ -21,6 +21,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn.h,v $ + * Revision 1.16 1996/08/12 16:20:56 hipp + * renamed ppp_minor to ppp_slot + * * Revision 1.15 1996/06/15 14:56:57 fritz * Added version signatures for data structures used * by userlevel programs. @@ -319,7 +322,7 @@ struct enet_statistics stats; /* Ethernet Statistics */ int isdn_device; /* Index to isdn-device */ int isdn_channel; /* Index to isdn-channel */ - int ppp_minor; /* PPPD device minor number */ + int ppp_slot; /* PPPD device slot number */ int pre_device; /* Preselected isdn-device */ int pre_channel; /* Preselected isdn-channel */ int exclusive; /* If non-zero idx to reserved chan.*/ @@ -549,6 +552,7 @@ unsigned int maxcid; isdn_net_local *lp; int unit; + int minor; long last_link_seqno; long mp_seqno; long range; diff -ur --new-file old/linux/include/linux/keyboard.h new/linux/include/linux/keyboard.h --- old/linux/include/linux/keyboard.h Sun Oct 8 11:37:07 1995 +++ new/linux/include/linux/keyboard.h Thu Nov 7 10:25:22 1996 @@ -352,8 +352,9 @@ #define K_DCIRCM K(KT_DEAD,2) #define K_DTILDE K(KT_DEAD,3) #define K_DDIERE K(KT_DEAD,4) +#define K_DCEDIL K(KT_DEAD,5) -#define NR_DEAD 5 +#define NR_DEAD 6 #define K_DOWN K(KT_CUR,0) #define K_LEFT K(KT_CUR,1) diff -ur --new-file old/linux/include/linux/locks.h new/linux/include/linux/locks.h --- old/linux/include/linux/locks.h Tue Aug 20 17:09:33 1996 +++ new/linux/include/linux/locks.h Thu Nov 7 13:02:23 1996 @@ -28,7 +28,7 @@ extern inline void lock_buffer(struct buffer_head * bh) { - if (set_bit(BH_Lock, &bh->b_state)) + while (set_bit(BH_Lock, &bh->b_state)) __wait_on_buffer(bh); } diff -ur --new-file old/linux/include/linux/major.h new/linux/include/linux/major.h --- old/linux/include/linux/major.h Thu Aug 15 08:54:46 1996 +++ new/linux/include/linux/major.h Sun Sep 8 18:50:21 1996 @@ -77,7 +77,7 @@ ((M) == SCSI_DISK_MAJOR \ || (M) == SCSI_CDROM_MAJOR) -static inline int scsi_blk_major(int m) { +static __inline__ int scsi_blk_major(int m) { return SCSI_BLK_MAJOR(m); } diff -ur --new-file old/linux/include/linux/mm.h new/linux/include/linux/mm.h --- old/linux/include/linux/mm.h Tue Aug 20 17:09:33 1996 +++ new/linux/include/linux/mm.h Thu Oct 31 11:04:58 1996 @@ -285,8 +285,8 @@ /* mmap.c */ extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long off); -extern void merge_segments(struct task_struct *, unsigned long, unsigned long); -extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *); +extern void merge_segments(struct mm_struct *, unsigned long, unsigned long); +extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *); extern void remove_shared_vm_struct(struct vm_area_struct *); extern void build_mmap_avl(struct mm_struct *); extern void exit_mmap(struct mm_struct *); @@ -334,12 +334,12 @@ #define avl_empty (struct vm_area_struct *) NULL /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ -static inline struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr) +static inline struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr) { struct vm_area_struct * result = NULL; - if (task->mm) { - struct vm_area_struct * tree = task->mm->mmap_avl; + if (mm) { + struct vm_area_struct * tree = mm->mmap_avl; for (;;) { if (tree == avl_empty) break; @@ -357,13 +357,13 @@ /* Look up the first VMA which intersects the interval start_addr..end_addr-1, NULL if none. Assume start_addr < end_addr. */ -static inline struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr) +static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr) { struct vm_area_struct * vma; - vma = find_vma(task,start_addr); - if (!vma || end_addr <= vma->vm_start) - return NULL; + vma = find_vma(mm,start_addr); + if (vma && end_addr <= vma->vm_start) + vma = NULL; return vma; } diff -ur --new-file old/linux/include/linux/netdevice.h new/linux/include/linux/netdevice.h --- old/linux/include/linux/netdevice.h Sat Nov 16 00:48:03 1996 +++ new/linux/include/linux/netdevice.h Sat Nov 16 00:48:32 1996 @@ -5,7 +5,7 @@ * * Definitions for the Interfaces handler. * - * Version: @(#)dev.h 1.0.10 08/12/93 + * Version: @(#)dev.h 1.0.11 07/31/96 * * Authors: Ross Biro, * Fred N. van Kempen, @@ -13,6 +13,7 @@ * Donald J. Becker, * Alan Cox, * Bjorn Ekwall. + * Lawrence V. Stefani, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,6 +21,7 @@ * 2 of the License, or (at your option) any later version. * * Moved to /usr/include/linux for NET3 + * Added extern for fddi_setup() */ #ifndef _LINUX_NETDEVICE_H #define _LINUX_NETDEVICE_H @@ -290,6 +292,7 @@ extern int ether_arp(struct device *dev,unsigned int cmd, void *arg); extern void tr_setup(struct device *dev); +extern void fddi_setup(struct device *dev); extern int ether_config(struct device *dev, struct ifmap *map); /* Support for loadable net-drivers */ extern int register_netdev(struct device *dev); diff -ur --new-file old/linux/include/linux/pagemap.h new/linux/include/linux/pagemap.h --- old/linux/include/linux/pagemap.h Tue Aug 20 17:09:33 1996 +++ new/linux/include/linux/pagemap.h Thu Oct 31 11:04:58 1996 @@ -42,13 +42,10 @@ #undef s } -#define page_hash(inode,offset) page_hash_table[_page_hashfn(inode,offset)] +#define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset)) -static inline struct page * find_page(struct inode * inode, unsigned long offset) +static inline struct page * __find_page(struct inode * inode, unsigned long offset, struct page *page) { - struct page *page; - - page = page_hash(inode, offset); goto inside; for (;;) { page = page->next_hash; @@ -67,24 +64,32 @@ return page; } +static inline struct page *find_page(struct inode * inode, unsigned long offset) +{ + return __find_page(inode, offset, *page_hash(inode, offset)); +} + static inline void remove_page_from_hash_queue(struct page * page) { - struct page **p = &page_hash(page->inode,page->offset); + struct page **p; + struct page *next_hash, *prev_hash; - page_cache_size--; - if (page->next_hash) - page->next_hash->prev_hash = page->prev_hash; - if (page->prev_hash) - page->prev_hash->next_hash = page->next_hash; + next_hash = page->next_hash; + prev_hash = page->prev_hash; + page->next_hash = NULL; + page->prev_hash = NULL; + if (next_hash) + next_hash->prev_hash = prev_hash; + if (prev_hash) + prev_hash->next_hash = next_hash; + p = page_hash(page->inode,page->offset); if (*p == page) - *p = page->next_hash; - page->next_hash = page->prev_hash = NULL; + *p = next_hash; + page_cache_size--; } -static inline void add_page_to_hash_queue(struct inode * inode, struct page * page) +static inline void __add_page_to_hash_queue(struct page * page, struct page **p) { - struct page **p = &page_hash(inode,page->offset); - page_cache_size++; set_bit(PG_referenced, &page->flags); page->age = PAGE_AGE_VALUE; @@ -93,6 +98,12 @@ page->next_hash->prev_hash = page; *p = page; } + +static inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset) +{ + __add_page_to_hash_queue(page, page_hash(inode,offset)); +} + static inline void remove_page_from_inode_queue(struct page * page) { diff -ur --new-file old/linux/include/linux/pci.h new/linux/include/linux/pci.h --- old/linux/include/linux/pci.h Sat Aug 17 20:03:34 1996 +++ new/linux/include/linux/pci.h Sat Nov 16 00:48:31 1996 @@ -241,6 +241,9 @@ #define PCI_DEVICE_ID_VLSI_82C594 0x0007 #define PCI_DEVICE_ID_VLSI_82C597 0x0009 +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_DEVICE_ID_IDT_IDT77201 0x0001 + #define PCI_VENDOR_ID_ADL 0x1005 #define PCI_DEVICE_ID_ADL_2301 0x2301 @@ -264,6 +267,8 @@ #define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 #define PCI_DEVICE_ID_DEC_FDDI 0x000F #define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 +#define PCI_DEVICE_ID_DEC_21052_AB 0x0021 +#define PCI_DEVICE_ID_DEC_21152_AA 0x0024 #define PCI_VENDOR_ID_CIRRUS 0x1013 #define PCI_DEVICE_ID_CIRRUS_5430 0x00a0 @@ -555,12 +560,15 @@ #define PCI_DEVICE_ID_INTEL_82439 0x1250 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 +#define PCI_DEVICE_ID_INTEL_82437VX 0x7030 #define PCI_DEVICE_ID_INTEL_P6 0x84c4 #define PCI_VENDOR_ID_ADAPTEC 0x9004 #define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 #define PCI_DEVICE_ID_ADAPTEC_7855 0x5578 #define PCI_DEVICE_ID_ADAPTEC_7860 0x6078 +#define PCI_DEVICE_ID_ADAPTEC_7861 0x6178 #define PCI_DEVICE_ID_ADAPTEC_7870 0x7078 #define PCI_DEVICE_ID_ADAPTEC_7871 0x7178 #define PCI_DEVICE_ID_ADAPTEC_7872 0x7278 diff -ur --new-file old/linux/include/linux/raid0.h new/linux/include/linux/raid0.h --- old/linux/include/linux/raid0.h Mon Feb 26 12:51:46 1996 +++ new/linux/include/linux/raid0.h Tue Oct 29 14:20:35 1996 @@ -1,32 +1,3 @@ - -#ifndef _RAID0_H -#define _RAID0_H - -struct strip_zone -{ - int zone_offset; /* Zone offset in md_dev */ - int dev_offset; /* Zone offset in real dev */ - int size; /* Zone size */ - int nb_dev; /* Number of devices attached to the zone */ - struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */ -}; - -struct raid0_hash -{ - struct strip_zone *zone0, *zone1; -}; - -struct raid0_data -{ - struct raid0_hash *hash_table; /* Dynamically allocated */ - struct strip_zone *strip_zone; /* This one too */ - int nr_strip_zones; - struct strip_zone *smallest; - int nr_zones; -}; - -#endif - #ifndef _RAID0_H #define _RAID0_H diff -ur --new-file old/linux/include/linux/sbpcd.h new/linux/include/linux/sbpcd.h --- old/linux/include/linux/sbpcd.h Mon Aug 12 12:44:47 1996 +++ new/linux/include/linux/sbpcd.h Mon Sep 2 14:18:26 1996 @@ -134,16 +134,38 @@ /* runtime by use of the CDROMAUDIOBUFSIZ ioctl. */ #define READ_AUDIO 0 +/* Optimizations for the Teac CD-55A drive read performance. + * SBP_TEAC_SPEED can be changed here, or one can set the + * variable "teac" when loading as a module. + * Valid settings are: + * 0 - very slow - the recommended "DISTRIBUTION 1" setup. + * 1 - 2x performance with little overhead. No busy waiting. + * 2 - 4x performance with 5ms overhead per read. Busy wait. + * + * Setting SBP_TEAC_SPEED or the variable 'teac' to anything + * other than 0 may cause problems. If you run into them, first + * change SBP_TEAC_SPEED back to 0 and see if your drive responds + * normally. If yes, you are "allowed" to report your case - to help + * me with the driver, not to solve your hassle. Don´t mail if you + * simply are stuck into your own "tuning" experiments, you know? + */ +#define SBP_TEAC_SPEED 1 + /*==========================================================================*/ /*==========================================================================*/ /* - * nothing to change below here if you are not experimenting + * nothing to change below here if you are not fully aware what you're doing */ #ifndef _LINUX_SBPCD_H #define _LINUX_SBPCD_H /*==========================================================================*/ /*==========================================================================*/ +/* + * driver's own read_ahead, data mode + */ +#define SBP_BUFFER_FRAMES 8 + #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */ #undef FUTURE #undef SAFE_MIXED @@ -151,9 +173,14 @@ #define TEST_UPC 0 #define SPEA_TEST 0 #define TEST_STI 0 +#define OLD_BUSY 0 #undef PATH_CHECK #ifndef SOUND_BASE #define SOUND_BASE 0 +#endif +#if DISTRIBUTION +#undef SBP_TEAC_SPEED +#define SBP_TEAC_SPEED 0 #endif /*==========================================================================*/ /* diff -ur --new-file old/linux/include/linux/sched.h new/linux/include/linux/sched.h --- old/linux/include/linux/sched.h Tue Aug 20 17:09:33 1996 +++ new/linux/include/linux/sched.h Thu Oct 31 11:04:58 1996 @@ -17,7 +17,9 @@ #include #include #include + #include +#include #include #include @@ -146,6 +148,7 @@ unsigned long def_flags; struct vm_area_struct * mmap; struct vm_area_struct * mmap_avl; + struct semaphore mmap_sem; }; #define INIT_MM { \ @@ -157,7 +160,7 @@ 0, 0, 0, 0, \ 0, 0, 0, \ 0, \ - &init_mmap, &init_mmap } + &init_mmap, &init_mmap, MUTEX } struct signal_struct { int count; @@ -260,8 +263,8 @@ #define PF_DUMPCORE 0x00000200 /* dumped core */ #define PF_SIGNALED 0x00000400 /* killed by a signal */ -#define PF_STARTING 0x00000100 /* being created */ -#define PF_EXITING 0x00000200 /* getting shut down */ +#define PF_STARTING 0x00000002 /* being created */ +#define PF_EXITING 0x00000004 /* getting shut down */ #define PF_USEDFPU 0x00100000 /* Process used the FPU this quantum (SMP only) */ #define PF_DTRACE 0x00200000 /* delayed trace (used on m68k) */ @@ -398,70 +401,49 @@ * to keep them correct. Use only these two functions to add/remove * entries in the queues. */ +extern inline void __add_wait_queue(struct wait_queue ** p, struct wait_queue * wait) +{ + struct wait_queue *head = *p; + struct wait_queue *next = WAIT_QUEUE_HEAD(p); + + if (head) + next = head; + *p = wait; + wait->next = next; +} + extern inline void add_wait_queue(struct wait_queue ** p, struct wait_queue * wait) { unsigned long flags; -#ifdef DEBUG - if (wait->next) { - __label__ here; - unsigned long pc; - pc = (unsigned long) &&here; - here: - printk("add_wait_queue (%08lx): wait->next = %08lx\n",pc,(unsigned long) wait->next); - } -#endif save_flags(flags); cli(); - if (!*p) { - wait->next = wait; - *p = wait; - } else { - wait->next = (*p)->next; - (*p)->next = wait; - } + __add_wait_queue(p, wait); restore_flags(flags); } +extern inline void __remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait) +{ + struct wait_queue * next = wait->next; + struct wait_queue * head = next; + + for (;;) { + struct wait_queue * nextlist = head->next; + if (nextlist == wait) + break; + head = nextlist; + } + head->next = next; +} + extern inline void remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait) { unsigned long flags; - struct wait_queue * tmp; -#ifdef DEBUG - unsigned long ok = 0; -#endif save_flags(flags); cli(); - if ((*p == wait) && -#ifdef DEBUG - (ok = 1) && -#endif - ((*p = wait->next) == wait)) { - *p = NULL; - } else { - tmp = wait; - while (tmp->next != wait) { - tmp = tmp->next; -#ifdef DEBUG - if (tmp == *p) - ok = 1; -#endif - } - tmp->next = wait->next; - } - wait->next = NULL; + __remove_wait_queue(p, wait); restore_flags(flags); -#ifdef DEBUG - if (!ok) { - __label__ here; - ok = (unsigned long) &&here; - printk("removed wait_queue not on list.\n"); - printk("list = %08lx, queue = %08lx\n",(unsigned long) p, (unsigned long) wait); - here: - printk("eip = %08lx\n",ok); - } -#endif } extern inline void select_wait(struct wait_queue ** wait_address, select_table * p) @@ -479,24 +461,6 @@ add_wait_queue(wait_address,&entry->wait); p->nr++; } - -extern void __down(struct semaphore * sem); - -/* - * These are not yet interrupt-safe - */ -extern inline void down(struct semaphore * sem) -{ - if (sem->count <= 0) - __down(sem); - sem->count--; -} - -extern inline void up(struct semaphore * sem) -{ - sem->count++; - wake_up(&sem->wait); -} #define REMOVE_LINKS(p) do { unsigned long flags; \ save_flags(flags) ; cli(); \ diff -ur --new-file old/linux/include/linux/skbuff.h new/linux/include/linux/skbuff.h --- old/linux/include/linux/skbuff.h Sat Nov 16 00:48:03 1996 +++ new/linux/include/linux/skbuff.h Sat Nov 16 00:48:31 1996 @@ -102,7 +102,7 @@ #define PACKET_OTHERHOST 3 /* To someone else */ unsigned short users; /* User count - see datagram.c,tcp.c */ unsigned short protocol; /* Packet protocol from driver. */ - unsigned short truesize; /* Buffer size */ + unsigned int truesize; /* Buffer size */ atomic_t count; /* reference count */ struct sk_buff *data_skb; /* Link to the actual data skb */ diff -ur --new-file old/linux/include/linux/smb_fs.h new/linux/include/linux/smb_fs.h --- old/linux/include/linux/smb_fs.h Tue Aug 20 17:14:41 1996 +++ new/linux/include/linux/smb_fs.h Wed Oct 30 03:58:11 1996 @@ -124,7 +124,8 @@ byte *smb_encode_smb_length(byte *p, dword len); int smb_proc_open(struct smb_server *server, const char *pathname, int len, struct smb_dirent *entry); -int smb_proc_close(struct smb_server *server, struct smb_dirent *finfo); +int smb_proc_close(struct smb_server *server, + __u16 fileid, __u32 mtime); int smb_proc_read(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int fs); int smb_proc_read_raw(struct smb_server *server, struct smb_dirent *finfo, @@ -135,8 +136,6 @@ off_t offset, long count, const char *data); int smb_proc_create(struct smb_server *server, const char *path, int len, struct smb_dirent *entry); -int smb_proc_mknew(struct smb_server *server, const char *path, int len, - struct smb_dirent *entry); int smb_proc_mv(struct smb_server *server, const char *opath, const int olen, const char *npath, const int nlen); int smb_proc_mkdir(struct smb_server *server, const char *path, const int len); diff -ur --new-file old/linux/include/linux/soundcard.h new/linux/include/linux/soundcard.h --- old/linux/include/linux/soundcard.h Sun Jun 30 11:25:28 1996 +++ new/linux/include/linux/soundcard.h Sun Sep 1 08:15:33 1996 @@ -742,15 +742,13 @@ /* Note! Number 31 cannot be used since the sign bit is reserved */ -#ifdef NO_LONGER_AVAILABLE /* - * The following unsupported macros will be removed from the API in near - * future. + * The following unsupported macros are no longer functional. + * Use SOUND_MIXER_PRIVATE# macros in future. */ -#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */ -#define SOUND_MIXER_MUTE 28 /* 0 or 1 */ -#define SOUND_MIXER_LOUD 30 /* 0 or 1 */ -#endif +#define SOUND_MIXER_ENHANCE 31 +#define SOUND_MIXER_MUTE 31 +#define SOUND_MIXER_LOUD 31 #define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \ @@ -790,11 +788,10 @@ #define SOUND_MASK_LINE2 (1 << SOUND_MIXER_LINE2) #define SOUND_MASK_LINE3 (1 << SOUND_MIXER_LINE3) -#ifdef NO_LONGER_AVAILABLE +/* Obsolete macros */ #define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE) #define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE) #define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD) -#endif #define MIXER_READ(dev) _IOR('M', dev, int) #define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME) @@ -814,11 +811,11 @@ #define SOUND_MIXER_READ_LINE1 MIXER_READ(SOUND_MIXER_LINE1) #define SOUND_MIXER_READ_LINE2 MIXER_READ(SOUND_MIXER_LINE2) #define SOUND_MIXER_READ_LINE3 MIXER_READ(SOUND_MIXER_LINE3) -#ifdef NO_LONGER_AVAILABLE + +/* Obsolete macros */ #define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE) #define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE) #define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD) -#endif #define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC) #define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK) @@ -844,11 +841,11 @@ #define SOUND_MIXER_WRITE_LINE1 MIXER_WRITE(SOUND_MIXER_LINE1) #define SOUND_MIXER_WRITE_LINE2 MIXER_WRITE(SOUND_MIXER_LINE2) #define SOUND_MIXER_WRITE_LINE3 MIXER_WRITE(SOUND_MIXER_LINE3) -#ifdef NO_LONGER_AVAILABLE + +/* Obsolete macros */ #define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE) #define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE) #define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD) -#endif #define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC) diff -ur --new-file old/linux/include/linux/wait.h new/linux/include/linux/wait.h --- old/linux/include/linux/wait.h Tue Jul 19 12:10:15 1994 +++ new/linux/include/linux/wait.h Wed Sep 11 16:57:18 1996 @@ -13,13 +13,18 @@ struct wait_queue * next; }; -struct semaphore { - int count; - struct wait_queue * wait; -}; +#define WAIT_QUEUE_HEAD(x) ((struct wait_queue *)((x)-1)) -#define MUTEX ((struct semaphore) { 1, NULL }) -#define MUTEX_LOCKED ((struct semaphore) { 0, NULL }) +static inline void init_waitqueue(struct wait_queue **q) +{ + *q = WAIT_QUEUE_HEAD(q); +} + +static inline int waitqueue_active(struct wait_queue **q) +{ + struct wait_queue *head = *q; + return head && head != WAIT_QUEUE_HEAD(q); +} struct select_table_entry { struct wait_queue wait; diff -ur --new-file old/linux/include/net/ip_masq.h new/linux/include/net/ip_masq.h --- old/linux/include/net/ip_masq.h Tue Aug 20 17:15:34 1996 +++ new/linux/include/net/ip_masq.h Wed Oct 30 03:59:00 1996 @@ -14,7 +14,7 @@ * I used an extra 4K port-space */ -#define PORT_MASQ_BEGIN 60000 +#define PORT_MASQ_BEGIN 61000 #define PORT_MASQ_END (PORT_MASQ_BEGIN+4096) #define MASQUERADE_EXPIRE_TCP 15*60*HZ diff -ur --new-file old/linux/include/net/sock.h new/linux/include/net/sock.h --- old/linux/include/net/sock.h Sat Nov 16 00:48:04 1996 +++ new/linux/include/net/sock.h Sat Nov 16 00:48:32 1996 @@ -169,6 +169,7 @@ __u32 fin_seq; __u32 urg_seq; __u32 urg_data; + __u32 syn_seq; int users; /* user count */ /* * Not all are volatile, but some are, so we @@ -225,11 +226,7 @@ volatile unsigned long ato; /* ack timeout */ volatile unsigned long lrcvtime; /* jiffies at last data rcv */ volatile unsigned long idletime; /* jiffies at last rcv */ -#ifdef CONFIG_SCALED_WINDOWS - unsigned long bytes_rcv; -#else - unsigned short bytes_rcv; -#endif + unsigned int bytes_rcv; /* * mss is min(mtu, max_window) */ @@ -268,8 +265,8 @@ unsigned char max_ack_backlog; unsigned char priority; unsigned char debug; - unsigned long rcvbuf; - unsigned long sndbuf; + int rcvbuf; + int sndbuf; unsigned short type; unsigned char localroute; /* Route locally only */ #ifdef CONFIG_AX25 diff -ur --new-file old/linux/include/net/tcp.h new/linux/include/net/tcp.h --- old/linux/include/net/tcp.h Sat Nov 16 00:48:04 1996 +++ new/linux/include/net/tcp.h Sat Nov 16 00:48:32 1996 @@ -175,6 +175,7 @@ extern void tcp_enqueue_partial(struct sk_buff *, struct sock *); extern struct sk_buff * tcp_dequeue_partial(struct sock *); +extern void tcp_shrink_skb(struct sock *,struct sk_buff *,u32); /* tcp_input.c */ extern void tcp_cache_zap(void); diff -ur --new-file old/linux/init/main.c new/linux/init/main.c --- old/linux/init/main.c Sat Nov 16 00:48:04 1996 +++ new/linux/init/main.c Sat Nov 16 00:48:32 1996 @@ -36,6 +36,9 @@ #ifdef CONFIG_ROOT_NFS #include #endif +#ifdef CONFIG_BIGPHYS_AREA +#include +#endif #include @@ -50,7 +53,7 @@ #endif extern char _stext, _etext; -extern char *linux_banner; +extern const char *linux_banner; static char printbuf[1024]; @@ -65,7 +68,7 @@ extern long console_init(long, long); extern long kmalloc_init(long,long); extern void sock_init(void); -extern long pci_init(long, long); +extern unsigned long pci_init(unsigned long, unsigned long); extern void sysctl_init(void); extern void no_scroll(char *str, int *ints); @@ -94,9 +97,11 @@ extern void fdomain_setup(char *str, int *ints); extern void in2000_setup(char *str, int *ints); extern void NCR53c406a_setup(char *str, int *ints); +extern void wd7000_setup(char *str, int *ints); extern void ppa_setup(char *str, int *ints); extern void scsi_luns_setup(char *str, int *ints); extern void sound_setup(char *str, int *ints); +extern void reboot_setup(char *str, int *ints); #ifdef CONFIG_CDU31A extern void cdu31a_setup(char *str, int *ints); #endif CONFIG_CDU31A @@ -177,9 +182,6 @@ #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD) extern void ipc_init(void); #endif -#ifdef CONFIG_BIGPHYS_AREA -extern void bigphysarea_setup(char *str, int *ints); -#endif /* * Boot command-line arguments @@ -272,6 +274,7 @@ #ifdef CONFIG_BUGi386 { "no-hlt", no_halt }, { "no387", no_387 }, + { "reboot=", reboot_setup }, #endif #ifdef CONFIG_INET { "ether=", eth_setup }, @@ -335,6 +338,9 @@ #ifdef CONFIG_SCSI_IN2000 { "in2000=", in2000_setup}, #endif +#ifdef CONFIG_SCSI_7000FASST + { "wd7000=", wd7000_setup}, +#endif #ifdef CONFIG_SCSI_PPA { "ppa=", ppa_setup }, #endif @@ -804,6 +810,7 @@ prof_len = (unsigned long) &_etext - (unsigned long) &_stext; prof_len >>= prof_shift; memory_start += prof_len * sizeof(unsigned int); + memset(prof_buffer, 0, prof_len * sizeof(unsigned int)); } memory_start = console_init(memory_start,memory_end); #ifdef CONFIG_PCI @@ -967,8 +974,17 @@ } } #endif + + /* + * This keeps serial console MUCH cleaner, but does assume + * the console driver checks there really is a video device + * attached (Sparc effectively does). + */ - (void) open("/dev/tty1",O_RDWR,0); + if ((open("/dev/tty1",O_RDWR,0) < 0) && + (open("/dev/ttyS0",O_RDWR,0) < 0)) + printk("Unable to open an initial console.\n"); + (void) dup(0); (void) dup(0); diff -ur --new-file old/linux/ipc/msg.c new/linux/ipc/msg.c --- old/linux/ipc/msg.c Wed Jul 17 14:10:03 1996 +++ new/linux/ipc/msg.c Sun Sep 1 08:15:33 1996 @@ -193,8 +193,7 @@ msq->msg_lspid = current->pid; msq->msg_stime = CURRENT_TIME; restore_flags(flags); - if (msq->rwait) - wake_up (&msq->rwait); + wake_up (&msq->rwait); return 0; } @@ -350,8 +349,7 @@ msghdrs--; msq->msg_cbytes -= nmsg->msg_ts; restore_flags(flags); - if (msq->wwait) - wake_up (&msq->wwait); + wake_up (&msq->wwait); /* * Calls from kernel level (IPC_KERNELD set) * wants the message copied to kernel space! @@ -438,8 +436,7 @@ msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL); if (!msq) { msgque[id] = (struct msqid_ds *) IPC_UNUSED; - if (msg_lock) - wake_up (&msg_lock); + wake_up (&msg_lock); return -ENOMEM; } ipcp = &msq->msg_perm; @@ -459,8 +456,7 @@ max_msqid = id; msgque[id] = msq; used_queues++; - if (msg_lock) - wake_up (&msg_lock); + wake_up (&msg_lock); return (unsigned int) msq->msg_perm.seq * MSGMNI + id; } @@ -525,11 +521,9 @@ while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED)); msgque[id] = (struct msqid_ds *) IPC_UNUSED; used_queues--; - while (msq->rwait || msq->wwait) { - if (msq->rwait) - wake_up (&msq->rwait); - if (msq->wwait) - wake_up (&msq->wwait); + while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) { + wake_up (&msq->rwait); + wake_up (&msq->wwait); schedule(); } for (msgp = msq->msg_first; msgp; msgp = msgh ) { diff -ur --new-file old/linux/ipc/sem.c new/linux/ipc/sem.c --- old/linux/ipc/sem.c Fri Jun 16 19:03:03 1995 +++ new/linux/ipc/sem.c Sun Sep 1 08:15:34 1996 @@ -103,8 +103,7 @@ if (!sma) { semary[id] = (struct semid_ds *) IPC_UNUSED; used_sems -= nsems; - if (sem_lock) - wake_up (&sem_lock); + wake_up (&sem_lock); return -ENOMEM; } memset (sma, 0, size); @@ -124,8 +123,7 @@ max_semid = id; used_semids++; semary[id] = sma; - if (sem_lock) - wake_up (&sem_lock); + wake_up (&sem_lock); return (unsigned int) sma->sem_perm.seq * SEMMNI + id; } diff -ur --new-file old/linux/ipc/shm.c new/linux/ipc/shm.c --- old/linux/ipc/shm.c Tue Jul 23 09:26:53 1996 +++ new/linux/ipc/shm.c Wed Sep 11 16:57:18 1996 @@ -92,16 +92,14 @@ shp = (struct shmid_ds *) kmalloc (sizeof (*shp), GFP_KERNEL); if (!shp) { shm_segs[id] = (struct shmid_ds *) IPC_UNUSED; - if (shm_lock) - wake_up (&shm_lock); + wake_up (&shm_lock); return -ENOMEM; } shp->shm_pages = (ulong *) kmalloc (numpages*sizeof(ulong),GFP_KERNEL); if (!shp->shm_pages) { shm_segs[id] = (struct shmid_ds *) IPC_UNUSED; - if (shm_lock) - wake_up (&shm_lock); + wake_up (&shm_lock); kfree(shp); return -ENOMEM; } @@ -125,8 +123,7 @@ max_shmid = id; shm_segs[id] = shp; used_segs++; - if (shm_lock) - wake_up (&shm_lock); + wake_up (&shm_lock); return (unsigned int) shp->shm_perm.seq * SHMMNI + id; } @@ -428,8 +425,8 @@ /* add new mapping */ current->mm->total_vm += (shmd->vm_end - shmd->vm_start) >> PAGE_SHIFT; - insert_vm_struct(current, shmd); - merge_segments(current, shmd->vm_start, shmd->vm_end); + insert_vm_struct(current->mm, shmd); + merge_segments(current->mm, shmd->vm_start, shmd->vm_end); /* map page range */ error = 0; @@ -508,7 +505,7 @@ return -EINVAL; } if (!(shmflg & SHM_REMAP)) - if ((shmd = find_vma_intersection(current, addr, addr + shp->shm_segsz))) { + if ((shmd = find_vma_intersection(current->mm, addr, addr + shp->shm_segsz))) { /* printk("shmat() -> EINVAL because the interval [0x%lx,0x%lx) intersects an already mapped interval [0x%lx,0x%lx).\n", addr, addr + shp->shm_segsz, shmd->vm_start, shmd->vm_end); */ return -EINVAL; diff -ur --new-file old/linux/kernel/exit.c new/linux/kernel/exit.c --- old/linux/kernel/exit.c Fri Jul 5 12:48:39 1996 +++ new/linux/kernel/exit.c Wed Oct 30 02:42:42 1996 @@ -21,7 +21,7 @@ #include extern void sem_exit (void); -extern void acct_process (long exitcode); +extern int acct_process (long exitcode); extern void kerneld_exit(void); int getrusage(struct task_struct *, int, struct rusage *); diff -ur --new-file old/linux/kernel/fork.c new/linux/kernel/fork.c --- old/linux/kernel/fork.c Thu Jul 4 19:09:41 1996 +++ new/linux/kernel/fork.c Thu Nov 7 10:25:22 1996 @@ -100,12 +100,12 @@ mpnt->vm_next_share = tmp; tmp->vm_prev_share = mpnt; } - if (tmp->vm_ops && tmp->vm_ops->open) - tmp->vm_ops->open(tmp); if (copy_page_range(mm, current->mm, tmp)) { exit_mmap(mm); return -ENOMEM; } + if (tmp->vm_ops && tmp->vm_ops->open) + tmp->vm_ops->open(tmp); *p = tmp; p = &tmp->vm_next; } @@ -247,12 +247,13 @@ p->prev_run = NULL; p->p_pptr = p->p_opptr = current; p->p_cptr = NULL; + init_waitqueue(&p->wait_chldexit); p->signal = 0; p->it_real_value = p->it_virt_value = p->it_prof_value = 0; p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0; init_timer(&p->real_timer); p->real_timer.data = (unsigned long) p; - p->leader = 0; /* process leadership doesn't inherit */ + p->leader = 0; /* session leadership doesn't inherit */ p->tty_old_pgrp = 0; p->utime = p->stime = 0; p->cutime = p->cstime = 0; diff -ur --new-file old/linux/kernel/ksyms.c new/linux/kernel/ksyms.c --- old/linux/kernel/ksyms.c Sat Nov 16 00:48:03 1996 +++ new/linux/kernel/ksyms.c Sat Nov 16 00:48:28 1996 @@ -71,13 +71,13 @@ #endif extern char *get_options(char *str, int *ints); -extern void set_device_ro(int dev,int flag); +extern void set_device_ro(kdev_t dev,int flag); extern struct file_operations * get_blkfops(unsigned int); extern void blkdev_release(struct inode * inode); extern void *sys_call_table; -extern int sys_tz; +extern struct timezone sys_tz; extern int request_dma(unsigned int dmanr, char * deviceID); extern void free_dma(unsigned int dmanr); @@ -131,8 +131,6 @@ X(verify_area), X(do_mmap), X(do_munmap), - X(insert_vm_struct), - X(merge_segments), X(exit_mm), /* internal kernel memory management */ @@ -352,6 +350,7 @@ X(insert_inode_hash), X(event), X(__down), + X(__up), X(securelevel), /* all busmice */ X(add_mouse_randomness), diff -ur --new-file old/linux/kernel/sched.c new/linux/kernel/sched.c --- old/linux/kernel/sched.c Sun Aug 18 09:37:57 1996 +++ new/linux/kernel/sched.c Sun Sep 15 09:34:18 1996 @@ -43,7 +43,7 @@ int securelevel = 0; /* system security level */ -long tick = 1000000 / HZ; /* timer interrupt period */ +long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */ volatile struct timeval xtime; /* The current time */ int tickadj = 500/HZ; /* microsecs */ @@ -64,7 +64,7 @@ long time_maxerror = MAXPHASE; /* maximum error (us) */ long time_esterror = MAXPHASE; /* estimated error (us) */ long time_phase = 0; /* phase offset (scaled us) */ -long time_freq = 0; /* frequency offset (scaled ppm) */ +long time_freq = ((1000000 + HZ/2) % HZ - HZ/2) << SHIFT_USEC; /* frequency offset (scaled ppm) */ long time_adj = 0; /* tick adjust (scaled 1 / HZ) */ long time_reftime = 0; /* time at last adjustment (s) */ @@ -431,64 +431,139 @@ */ void wake_up(struct wait_queue **q) { - struct wait_queue *tmp; - struct task_struct * p; + struct wait_queue *next; + struct wait_queue *head; - if (!q || !(tmp = *q)) + if (!q || !(next = *q)) return; - do { - if ((p = tmp->task) != NULL) { + head = WAIT_QUEUE_HEAD(q); + while (next != head) { + struct task_struct *p = next->task; + next = next->next; + if (p != NULL) { if ((p->state == TASK_UNINTERRUPTIBLE) || (p->state == TASK_INTERRUPTIBLE)) wake_up_process(p); } - if (!tmp->next) { - printk("wait_queue is bad (eip = %p)\n", - __builtin_return_address(0)); - printk(" q = %p\n",q); - printk(" *q = %p\n",*q); - printk(" tmp = %p\n",tmp); - break; - } - tmp = tmp->next; - } while (tmp != *q); + if (!next) + goto bad; + } + return; +bad: + printk("wait_queue is bad (eip = %p)\n", + __builtin_return_address(0)); + printk(" q = %p\n",q); + printk(" *q = %p\n",*q); } void wake_up_interruptible(struct wait_queue **q) { - struct wait_queue *tmp; - struct task_struct * p; + struct wait_queue *next; + struct wait_queue *head; - if (!q || !(tmp = *q)) + if (!q || !(next = *q)) return; - do { - if ((p = tmp->task) != NULL) { + head = WAIT_QUEUE_HEAD(q); + while (next != head) { + struct task_struct *p = next->task; + next = next->next; + if (p != NULL) { if (p->state == TASK_INTERRUPTIBLE) wake_up_process(p); } - if (!tmp->next) { - printk("wait_queue is bad (eip = %p)\n", - __builtin_return_address(0)); - printk(" q = %p\n",q); - printk(" *q = %p\n",*q); - printk(" tmp = %p\n",tmp); - break; - } - tmp = tmp->next; - } while (tmp != *q); + if (!next) + goto bad; + } + return; +bad: + printk("wait_queue is bad (eip = %p)\n", + __builtin_return_address(0)); + printk(" q = %p\n",q); + printk(" *q = %p\n",*q); +} + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to sleep, while the "waiting" variable is + * incremented _while_ the process is sleeping on that + * semaphore. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + */ +static inline void normalize_semaphore(struct semaphore *sem) +{ + atomic_add(xchg(&sem->waiting,0), &sem->count); +} + +/* + * When __up() is called, the count was negative before + * incrementing it, and we need to wake up somebody. In + * most cases "waiting" will be positive, and the normalization + * will allow things to continue. However, if somebody has + * /just/ done a down(), it may be that count was negative + * without waiting being positive (or in the generic case + * "count is more negative than waiting is positive"), and + * the waiter needs to check this itself (see __down). + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in + * where we want to avoid any extra jumps and calls. + */ +void __up(struct semaphore *sem) +{ + normalize_semaphore(sem); + wake_up(&sem->wait); } void __down(struct semaphore * sem) { - struct wait_queue wait = { current, NULL }; + struct task_struct *tsk = current; + struct wait_queue wait = { tsk, NULL }; + + /* + * The order here is important. We add ourselves to the + * wait queues and mark ourselves sleeping _first_. That + * way, if a "up()" comes in here, we'll either get + * woken up (up happens after the wait queues are set up) + * OR we'll have "waiting > 0". + */ + tsk->state = TASK_UNINTERRUPTIBLE; add_wait_queue(&sem->wait, &wait); - current->state = TASK_UNINTERRUPTIBLE; - while (sem->count <= 0) { - schedule(); - current->state = TASK_UNINTERRUPTIBLE; + atomic_inc(&sem->waiting); + + /* + * Ok, we're set up. The only race here is really that + * an "up()" might have incremented count before we got + * here, so we check "count+waiting". If that is larger + * than zero, we shouldn't sleep, but re-try the lock. + */ + if (sem->count+sem->waiting <= 0) { + /* + * If "count+waiting" <= 0, we have to wait + * for a up(), which will normalize the count. + * Remember, at this point we have decremented + * count, and incremented up, so if count is + * zero or positive we need to return to re-try + * the lock. It _may_ be that both count and + * waiting is zero and that it is still locked, + * but we still want to re-try the lock in that + * case to make count go negative again so that + * the optimized "up()" wake_up sequence works. + */ + do { + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + } while (sem->count < 0); } - current->state = TASK_RUNNING; + tsk->state = TASK_RUNNING; remove_wait_queue(&sem->wait, &wait); + normalize_semaphore(sem); } static inline void __sleep_on(struct wait_queue **p, int state) @@ -501,11 +576,13 @@ if (current == task[0]) panic("task[0] trying to sleep"); current->state = state; - add_wait_queue(p, &wait); save_flags(flags); + cli(); + __add_wait_queue(p, &wait); sti(); schedule(); - remove_wait_queue(p, &wait); + cli(); + __remove_wait_queue(p, &wait); restore_flags(flags); } @@ -1174,8 +1251,10 @@ p->policy = policy; p->rt_priority = lp.sched_priority; + cli(); if (p->next_run) move_last_runqueue(p); + sti(); schedule(); return 0; @@ -1231,8 +1310,9 @@ asmlinkage int sys_sched_yield(void) { + cli(); move_last_runqueue(current); - + sti(); return 0; } diff -ur --new-file old/linux/mm/filemap.c new/linux/mm/filemap.c --- old/linux/mm/filemap.c Fri Aug 9 13:55:35 1996 +++ new/linux/mm/filemap.c Wed Sep 11 16:57:19 1996 @@ -255,13 +255,14 @@ } static inline void add_to_page_cache(struct page * page, - struct inode * inode, unsigned long offset) + struct inode * inode, unsigned long offset, + struct page **hash) { page->count++; page->flags &= ~((1 << PG_uptodate) | (1 << PG_error)); page->offset = offset; add_page_to_inode_queue(inode, page); - add_page_to_hash_queue(inode, page); + __add_page_to_hash_queue(page, hash); } /* @@ -272,32 +273,31 @@ static unsigned long try_to_read_ahead(struct inode * inode, unsigned long offset, unsigned long page_cache) { struct page * page; + struct page ** hash; offset &= PAGE_MASK; - if (!page_cache) { + switch (page_cache) { + case 0: page_cache = __get_free_page(GFP_KERNEL); if (!page_cache) - return 0; - } - if (offset >= inode->i_size) - return page_cache; -#if 1 - page = find_page(inode, offset); - if (page) { + break; + default: + if (offset >= inode->i_size) + break; + hash = page_hash(inode, offset); + page = __find_page(inode, offset, *hash); + if (!page) { + /* + * Ok, add the new page to the hash-queues... + */ + page = mem_map + MAP_NR(page_cache); + add_to_page_cache(page, inode, offset, hash); + inode->i_op->readpage(inode, page); + page_cache = 0; + } release_page(page); - return page_cache; } - /* - * Ok, add the new page to the hash-queues... - */ - page = mem_map + MAP_NR(page_cache); - add_to_page_cache(page, inode, offset); - inode->i_op->readpage(inode, page); - free_page(page_cache); - return 0; -#else return page_cache; -#endif } /* @@ -457,13 +457,12 @@ #endif static inline unsigned long generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode, - unsigned long pos, struct page * page, + unsigned long ppos, struct page * page, unsigned long page_cache) { unsigned long max_ahead, ahead; - unsigned long raend, ppos; + unsigned long raend; - ppos = pos & PAGE_MASK; raend = filp->f_raend & PAGE_MASK; max_ahead = 0; @@ -607,7 +606,7 @@ } else { unsigned long needed; - needed = ((pos + count) & PAGE_MASK) - (pos & PAGE_MASK); + needed = ((pos + count) & PAGE_MASK) - ppos; if (filp->f_ramax < needed) filp->f_ramax = needed; @@ -619,7 +618,7 @@ } for (;;) { - struct page *page; + struct page *page, **hash; if (pos >= inode->i_size) break; @@ -627,7 +626,8 @@ /* * Try to find the data in the page cache.. */ - page = find_page(inode, pos & PAGE_MASK); + hash = page_hash(inode, pos & PAGE_MASK); + page = __find_page(inode, pos & PAGE_MASK, *hash); if (!page) goto no_cached_page; @@ -640,7 +640,7 @@ * the page has been rewritten. */ if (PageUptodate(page) || PageLocked(page)) - page_cache = generic_file_readahead(reada_ok, filp, inode, pos, page, page_cache); + page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_MASK, page, page_cache); else if (reada_ok && filp->f_ramax > MIN_READAHEAD) filp->f_ramax = MIN_READAHEAD; @@ -696,7 +696,7 @@ */ page = mem_map + MAP_NR(page_cache); page_cache = 0; - add_to_page_cache(page, inode, pos & PAGE_MASK); + add_to_page_cache(page, inode, pos & PAGE_MASK, hash); /* * Error handling is tricky. If we get a read error, @@ -764,7 +764,7 @@ static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share) { unsigned long offset; - struct page * page; + struct page * page, **hash; struct inode * inode = area->vm_inode; unsigned long old_page, new_page; @@ -776,7 +776,8 @@ /* * Do we have something in the page cache already? */ - page = find_page(inode, offset); + hash = page_hash(inode, offset); + page = __find_page(inode, offset, *hash); if (!page) goto no_cached_page; @@ -841,7 +842,7 @@ */ page = mem_map + MAP_NR(new_page); new_page = 0; - add_to_page_cache(page, inode, offset); + add_to_page_cache(page, inode, offset, hash); if (inode->i_op->readpage(inode, page) != 0) goto failure; @@ -1229,7 +1230,7 @@ * If the interval [start,end) covers some unmapped address ranges, * just ignore them, but return -EFAULT at the end. */ - vma = find_vma(current, start); + vma = find_vma(current->mm, start); unmapped_error = 0; for (;;) { /* Still start < end. */ diff -ur --new-file old/linux/mm/memory.c new/linux/mm/memory.c --- old/linux/mm/memory.c Wed Aug 14 09:28:06 1996 +++ new/linux/mm/memory.c Wed Sep 11 16:57:19 1996 @@ -686,7 +686,7 @@ if (!size || get_fs() == KERNEL_DS) return 0; - vma = find_vma(current, start); + vma = find_vma(current->mm, start); if (!vma) goto bad_area; if (vma->vm_start > start) diff -ur --new-file old/linux/mm/mlock.c new/linux/mm/mlock.c --- old/linux/mm/mlock.c Sun Feb 25 10:17:59 1996 +++ new/linux/mm/mlock.c Wed Sep 11 16:57:19 1996 @@ -40,7 +40,7 @@ n->vm_inode->i_count++; if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); - insert_vm_struct(current, n); + insert_vm_struct(current->mm, n); return 0; } @@ -61,7 +61,7 @@ n->vm_inode->i_count++; if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); - insert_vm_struct(current, n); + insert_vm_struct(current->mm, n); return 0; } @@ -93,8 +93,8 @@ vma->vm_ops->open(left); vma->vm_ops->open(right); } - insert_vm_struct(current, left); - insert_vm_struct(current, right); + insert_vm_struct(current->mm, left); + insert_vm_struct(current->mm, right); return 0; } @@ -148,7 +148,7 @@ return -EINVAL; if (end == start) return 0; - vma = find_vma(current, start); + vma = find_vma(current->mm, start); if (!vma || vma->vm_start > start) return -ENOMEM; @@ -178,7 +178,7 @@ break; } } - merge_segments(current, start, end); + merge_segments(current->mm, start, end); return error; } @@ -240,7 +240,7 @@ if (error) break; } - merge_segments(current, 0, TASK_SIZE); + merge_segments(current->mm, 0, TASK_SIZE); return error; } diff -ur --new-file old/linux/mm/mmap.c new/linux/mm/mmap.c --- old/linux/mm/mmap.c Wed Jul 31 14:58:59 1996 +++ new/linux/mm/mmap.c Wed Sep 11 16:57:19 1996 @@ -66,19 +66,20 @@ { unsigned long rlim; unsigned long newbrk, oldbrk; + struct mm_struct *mm = current->mm; - if (brk < current->mm->end_code) - return current->mm->brk; + if (brk < mm->end_code) + return mm->brk; newbrk = PAGE_ALIGN(brk); - oldbrk = PAGE_ALIGN(current->mm->brk); + oldbrk = PAGE_ALIGN(mm->brk); if (oldbrk == newbrk) - return current->mm->brk = brk; + return mm->brk = brk; /* * Always allow shrinking brk */ - if (brk <= current->mm->brk) { - current->mm->brk = brk; + if (brk <= mm->brk) { + mm->brk = brk; do_munmap(newbrk, oldbrk-newbrk); return brk; } @@ -88,25 +89,25 @@ rlim = current->rlim[RLIMIT_DATA].rlim_cur; if (rlim >= RLIM_INFINITY) rlim = ~0; - if (brk - current->mm->end_code > rlim) - return current->mm->brk; + if (brk - mm->end_code > rlim) + return mm->brk; /* * Check against existing mmap mappings. */ - if (find_vma_intersection(current, oldbrk, newbrk+PAGE_SIZE)) - return current->mm->brk; + if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE)) + return mm->brk; /* * Check if we have enough memory.. */ if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT)) - return current->mm->brk; + return mm->brk; /* * Ok, looks good - let it rip. */ - current->mm->brk = brk; + mm->brk = brk; do_mmap(NULL, oldbrk, newbrk-oldbrk, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); @@ -139,12 +140,13 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long off) { + struct mm_struct * mm = current->mm; struct vm_area_struct * vma; if ((len = PAGE_ALIGN(len)) == 0) return addr; - if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len) + if (len > TASK_SIZE || addr > TASK_SIZE-len) return -EINVAL; /* offset overflow? */ @@ -152,8 +154,8 @@ return -EINVAL; /* mlock MCL_FUTURE? */ - if (current->mm->def_flags & VM_LOCKED) { - unsigned long locked = current->mm->locked_vm << PAGE_SHIFT; + if (mm->def_flags & VM_LOCKED) { + unsigned long locked = mm->locked_vm << PAGE_SHIFT; locked += len; if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur) return -EAGAIN; @@ -199,8 +201,6 @@ if (flags & MAP_FIXED) { if (addr & ~PAGE_MASK) return -EINVAL; - if (len > TASK_SIZE || addr > TASK_SIZE - len) - return -EINVAL; } else { addr = get_unmapped_area(addr, len); if (!addr) @@ -220,10 +220,10 @@ if (!vma) return -ENOMEM; - vma->vm_mm = current->mm; + vma->vm_mm = mm; vma->vm_start = addr; vma->vm_end = addr + len; - vma->vm_flags = vm_flags(prot,flags) | current->mm->def_flags; + vma->vm_flags = vm_flags(prot,flags) | mm->def_flags; if (file) { if (file->f_mode & 1) @@ -271,14 +271,14 @@ } flags = vma->vm_flags; - insert_vm_struct(current, vma); - merge_segments(current, vma->vm_start, vma->vm_end); + insert_vm_struct(mm, vma); + merge_segments(mm, vma->vm_start, vma->vm_end); /* merge_segments might have merged our vma, so we can't use it any more */ - current->mm->total_vm += len >> PAGE_SHIFT; + mm->total_vm += len >> PAGE_SHIFT; if (flags & VM_LOCKED) { unsigned long start = addr; - current->mm->locked_vm += len >> PAGE_SHIFT; + mm->locked_vm += len >> PAGE_SHIFT; do { char c = get_user((char *) start); len -= PAGE_SIZE; @@ -304,7 +304,7 @@ addr = TASK_SIZE / 3; addr = PAGE_ALIGN(addr); - for (vmm = find_vma(current, addr); ; vmm = vmm->vm_next) { + for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { /* At this point: (!vmm || addr < vmm->vm_end). */ if (TASK_SIZE - len < addr) return 0; @@ -754,7 +754,7 @@ if (mpnt->vm_ops && mpnt->vm_ops->open) mpnt->vm_ops->open(mpnt); area->vm_end = addr; /* Truncate area */ - insert_vm_struct(current, mpnt); + insert_vm_struct(current->mm, mpnt); } /* construct whatever mapping is needed */ @@ -768,7 +768,7 @@ area->vm_end = area->vm_start; area->vm_ops->close(area); } - insert_vm_struct(current, mpnt); + insert_vm_struct(current->mm, mpnt); } asmlinkage int sys_munmap(unsigned long addr, size_t len) @@ -798,7 +798,7 @@ * every area affected in some way (by any overlap) is put * on the list. If nothing is put on, nothing is affected. */ - mpnt = find_vma(current, addr); + mpnt = find_vma(current->mm, addr); if (!mpnt) return 0; avl_neighbours(mpnt, current->mm->mmap_avl, &prev, &next); @@ -889,7 +889,7 @@ * Insert vm structure into process list sorted by address * and into the inode's i_mmap ring. */ -void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp) +void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp) { struct vm_area_struct *share; struct inode * inode; @@ -897,7 +897,7 @@ #if 0 /* equivalent, but slow */ struct vm_area_struct **p, *mpnt; - p = &t->mm->mmap; + p = &mm->mmap; while ((mpnt = *p) != NULL) { if (mpnt->vm_start > vmp->vm_start) break; @@ -910,13 +910,13 @@ #else struct vm_area_struct * prev, * next; - avl_insert_neighbours(vmp, &t->mm->mmap_avl, &prev, &next); - if ((prev ? prev->vm_next : t->mm->mmap) != next) + avl_insert_neighbours(vmp, &mm->mmap_avl, &prev, &next); + if ((prev ? prev->vm_next : mm->mmap) != next) printk("insert_vm_struct: tree inconsistent with list\n"); if (prev) prev->vm_next = vmp; else - t->mm->mmap = vmp; + mm->mmap = vmp; vmp->vm_next = next; #endif @@ -965,14 +965,16 @@ * We don't need to traverse the entire list, only those segments * which intersect or are adjacent to a given interval. */ -void merge_segments (struct task_struct * task, unsigned long start_addr, unsigned long end_addr) +void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr) { struct vm_area_struct *prev, *mpnt, *next; - mpnt = find_vma(task, start_addr); + down(&mm->mmap_sem); + mpnt = find_vma(mm, start_addr); if (!mpnt) - return; - avl_neighbours(mpnt, task->mm->mmap_avl, &prev, &next); + goto no_vma; + + avl_neighbours(mpnt, mm->mmap_avl, &prev, &next); /* we have prev->vm_next == mpnt && mpnt->vm_next = next */ if (!prev) { @@ -1016,7 +1018,7 @@ * big segment can possibly merge with the next one. * The old unused mpnt is freed. */ - avl_remove(mpnt, &task->mm->mmap_avl); + avl_remove(mpnt, &mm->mmap_avl); prev->vm_end = mpnt->vm_end; prev->vm_next = mpnt->vm_next; if (mpnt->vm_ops && mpnt->vm_ops->close) { @@ -1030,4 +1032,6 @@ kfree_s(mpnt, sizeof(*mpnt)); mpnt = prev; } +no_vma: + up(&mm->mmap_sem); } diff -ur --new-file old/linux/mm/mprotect.c new/linux/mm/mprotect.c --- old/linux/mm/mprotect.c Wed Apr 3 09:59:33 1996 +++ new/linux/mm/mprotect.c Wed Sep 11 16:57:19 1996 @@ -112,7 +112,7 @@ n->vm_inode->i_count++; if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); - insert_vm_struct(current, n); + insert_vm_struct(current->mm, n); return 0; } @@ -135,7 +135,7 @@ n->vm_inode->i_count++; if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); - insert_vm_struct(current, n); + insert_vm_struct(current->mm, n); return 0; } @@ -169,8 +169,8 @@ vma->vm_ops->open(left); vma->vm_ops->open(right); } - insert_vm_struct(current, left); - insert_vm_struct(current, right); + insert_vm_struct(current->mm, left); + insert_vm_struct(current->mm, right); return 0; } @@ -216,7 +216,7 @@ return -EINVAL; if (end == start) return 0; - vma = find_vma(current, start); + vma = find_vma(current->mm, start); if (!vma || vma->vm_start > start) return -EFAULT; @@ -248,6 +248,6 @@ break; } } - merge_segments(current, start, end); + merge_segments(current->mm, start, end); return error; } diff -ur --new-file old/linux/mm/mremap.c new/linux/mm/mremap.c --- old/linux/mm/mremap.c Wed Jun 26 08:44:52 1996 +++ new/linux/mm/mremap.c Wed Sep 11 16:57:19 1996 @@ -143,8 +143,8 @@ new_vma->vm_inode->i_count++; if (new_vma->vm_ops && new_vma->vm_ops->open) new_vma->vm_ops->open(new_vma); - insert_vm_struct(current, new_vma); - merge_segments(current, new_vma->vm_start, new_vma->vm_end); + insert_vm_struct(current->mm, new_vma); + merge_segments(current->mm, new_vma->vm_start, new_vma->vm_end); do_munmap(addr, old_len); return new_addr; } @@ -180,7 +180,7 @@ /* * Ok, we need to grow.. */ - vma = find_vma(current, addr); + vma = find_vma(current->mm, addr); if (!vma || vma->vm_start > addr) return -EFAULT; /* We can't remap across vm area boundaries */ diff -ur --new-file old/linux/mm/page_io.c new/linux/mm/page_io.c --- old/linux/mm/page_io.c Mon Jun 3 14:38:37 1996 +++ new/linux/mm/page_io.c Sun Sep 8 18:50:22 1996 @@ -168,9 +168,10 @@ * asynchronous function now --- we must call wait_on_page afterwards * if synchronous IO is required. */ -void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer) +void ll_rw_page(int rw, kdev_t dev, unsigned long offset, char * buffer) { - int block = page; + int block = offset; + struct page *page; switch (rw) { case READ: @@ -185,7 +186,8 @@ default: panic("ll_rw_page: bad block dev cmd, must be R/W"); } - if (set_bit(PG_locked, &mem_map[MAP_NR(buffer)].flags)) + page = mem_map + MAP_NR(buffer); + if (set_bit(PG_locked, &page->flags)) panic ("ll_rw_page: page already locked"); - brw_page(rw, (unsigned long) buffer, dev, &block, PAGE_SIZE, 0); + brw_page(rw, page, dev, &block, PAGE_SIZE, 0); } diff -ur --new-file old/linux/mm/vmscan.c new/linux/mm/vmscan.c --- old/linux/mm/vmscan.c Tue Aug 13 14:00:33 1996 +++ new/linux/mm/vmscan.c Sun Sep 15 09:34:18 1996 @@ -108,8 +108,13 @@ } else { if (page_map->count != 1) return 0; - if (!(entry = get_swap_page())) - return -1; /* Aieee!!! Out of swap space! */ + if (!(entry = get_swap_page())) { + /* Aieee!!! Out of swap space! */ + int retval = -1; + if (nr_swapfiles == 0) + retval = 0; + return retval; + } vma->vm_mm->rss--; flush_cache_page(vma, address); set_pte(page_table, __pte(entry)); @@ -253,7 +258,7 @@ /* * Find the proper vm-area */ - vma = find_vma(p, address); + vma = find_vma(p->mm, address); if (!vma) return 0; if (address < vma->vm_start) @@ -312,6 +317,7 @@ if (!--p->swap_cnt) swap_task++; switch (swap_out_process(p, dma, wait)) { + /* out of swap space? */ case -1: return 0; case 0: diff -ur --new-file old/linux/net/802/Makefile new/linux/net/802/Makefile --- old/linux/net/802/Makefile Tue Apr 9 13:36:31 1996 +++ new/linux/net/802/Makefile Wed Oct 30 02:42:42 1996 @@ -14,6 +14,10 @@ O_OBJS += tr.o endif +ifdef CONFIG_FDDI +O_OBJS += fddi.o +endif + ifdef CONFIG_IPX OX_OBJS += p8022.o psnap.o p8022tr.o endif diff -ur --new-file old/linux/net/802/fddi.c new/linux/net/802/fddi.c --- old/linux/net/802/fddi.c Thu Jan 1 01:00:00 1970 +++ new/linux/net/802/fddi.c Wed Oct 30 03:36:35 1996 @@ -0,0 +1,162 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * FDDI-type device handling. + * + * Version: @(#)fddi.c 1.0.0 08/12/96 + * + * Authors: Lawrence V. Stefani, + * + * fddi.c is based on previous eth.c and tr.c work by + * Ross Biro, + * Fred N. van Kempen, + * Mark Evans, + * Florian La Roche, + * Alan Cox, + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Create the FDDI MAC header for an arbitrary protocol layer + * + * saddr=NULL means use device source address + * daddr=NULL means leave destination address (eg unresolved arp) + */ + +int fddi_header( + struct sk_buff *skb, + struct device *dev, + unsigned short type, + void *daddr, + void *saddr, + unsigned len + ) + + { + struct fddihdr *fddi = (struct fddihdr *)skb_push(skb, FDDI_K_SNAP_HLEN); + + /* Fill in frame header - assume 802.2 SNAP frames for now */ + + fddi->fc = FDDI_FC_K_ASYNC_LLC_DEF; + fddi->hdr.llc_snap.dsap = FDDI_EXTENDED_SAP; + fddi->hdr.llc_snap.ssap = FDDI_EXTENDED_SAP; + fddi->hdr.llc_snap.ctrl = FDDI_UI_CMD; + fddi->hdr.llc_snap.oui[0] = 0x00; + fddi->hdr.llc_snap.oui[1] = 0x00; + fddi->hdr.llc_snap.oui[2] = 0x00; + fddi->hdr.llc_snap.ethertype = htons(type); + + /* Set the source and destination hardware addresses */ + + if (saddr != NULL) + memcpy(fddi->saddr, saddr, dev->addr_len); + else + memcpy(fddi->saddr, dev->dev_addr, dev->addr_len); + + if (daddr != NULL) + { + memcpy(fddi->daddr, daddr, dev->addr_len); + return(FDDI_K_SNAP_HLEN); + } + return(-FDDI_K_SNAP_HLEN); + } + + +/* + * Rebuild the FDDI MAC header. This is called after an ARP + * (or in future other address resolution) has completed on + * this sk_buff. We now let ARP fill in the other fields. + */ + +int fddi_rebuild_header( + void *buff, + struct device *dev, + unsigned long dest, + struct sk_buff *skb + ) + + { + struct fddihdr *fddi = (struct fddihdr *)buff; + + /* Only ARP/IP is currently supported */ + + if (fddi->hdr.llc_snap.ethertype != htons(ETH_P_IP)) + { + printk("fddi_rebuild_header: Don't know how to resolve type %04X addresses?\n", (unsigned int)htons(fddi->hdr.llc_snap.ethertype)); + return(0); + } + + /* Try to get ARP to resolve the header and fill destination address */ + + if (arp_find(fddi->daddr, dest, dev, dev->pa_addr, skb)) + return(1); + else + return(0); + } + + +/* + * Determine the packet's protocol ID and fill in skb fields. + * This routine is called before an incoming packet is passed + * up. It's used to fill in specific skb fields and to set + * the proper pointer to the start of packet data (skb->data). + */ + +unsigned short fddi_type_trans( + struct sk_buff *skb, + struct device *dev + ) + + { + struct fddihdr *fddi = (struct fddihdr *)skb->data; + + /* + * Set mac.raw field to point to FC byte, set data field to point + * to start of packet data. Assume 802.2 SNAP frames for now. + */ + + skb->mac.raw = skb->data; /* point to frame control (FC) */ + skb_pull(skb, FDDI_K_SNAP_HLEN); /* adjust for 21 byte header */ + + /* Set packet type based on destination address and flag settings */ + + if (*fddi->daddr & 0x01) + { + if (memcmp(fddi->daddr, dev->broadcast, FDDI_K_ALEN) == 0) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } + + else if (dev->flags & IFF_PROMISC) + { + if (memcmp(fddi->daddr, dev->dev_addr, FDDI_K_ALEN)) + skb->pkt_type = PACKET_OTHERHOST; + } + + /* Assume 802.2 SNAP frames, for now */ + + return(fddi->hdr.llc_snap.ethertype); + } diff -ur --new-file old/linux/net/atm/common.c new/linux/net/atm/common.c --- old/linux/net/atm/common.c Sat Nov 16 00:48:03 1996 +++ new/linux/net/atm/common.c Sat Nov 16 00:48:31 1996 @@ -123,7 +123,9 @@ if (vcc->push) vcc->push(vcc,NULL); /* atmarpd has no push */ while ((skb = skb_dequeue(&vcc->recvq))) { atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->rx_inuse); - kfree_skb(skb,FREE_READ); + if (vcc->dev->ops->free_rx_skb) + vcc->dev->ops->free_rx_skb(vcc,skb); + else kfree_skb(skb,FREE_READ); } if (vcc->rx_inuse) printk(KERN_WARNING "atm_release_vcc: strange ... " @@ -383,6 +385,24 @@ (unsigned long) buff,eff_len); DPRINTK("RcvM %d -= %d\n",vcc->rx_inuse,skb->truesize); atomic_sub(skb->truesize+ATM_PDU_OVHD,&vcc->rx_inuse); + if (skb->atm.iovcnt) { /* @@@ hack */ + /* iovcnt set, use scatter-gather for receive */ + int el, cnt; + struct iovec *iov = (struct iovec *)skb->data; + unsigned char *p = (unsigned char *)buff; + + el = eff_len; + for (cnt = 0; (cnt < skb->atm.iovcnt) && el; cnt++) { + memcpy_tofs(p, iov->iov_base, + (iov->iov_len > el) ? el : iov->iov_len); + p += iov->iov_len; + el -= (iov->iov_len > el)?el:iov->iov_len; + iov++; + } + if (!vcc->dev->ops->free_rx_skb) kfree_skb(skb,FREE_READ); + else vcc->dev->ops->free_rx_skb(vcc, skb); + return eff_len; + } #ifdef CONFIG_MMU_HACKS if (vcc->flags & ATM_VF_SCRX) mmucp_tofs((unsigned long) buff,eff_len,skb, diff -ur --new-file old/linux/net/bridge/br.c new/linux/net/bridge/br.c --- old/linux/net/bridge/br.c Fri Jul 19 07:24:05 1996 +++ new/linux/net/bridge/br.c Fri Nov 8 07:44:27 1996 @@ -267,7 +267,7 @@ (((port_info[port_no].designated_cost + port_info[port_no].path_cost ) - == + < (port_info[root_port].designated_cost + port_info[root_port].path_cost ) /* (4.6.8.3.1(2)) */ @@ -1137,7 +1137,6 @@ /* happen in net_bh() in dev.c) */ } /* ok, forward this frame... */ - skb_device_lock(skb); return(br_forward(skb, port)); default: printk(KERN_DEBUG "br_receive_frame: port [%i] unknown state [%i]\n", diff -ur --new-file old/linux/net/core/dev.c new/linux/net/core/dev.c --- old/linux/net/core/dev.c Thu Aug 1 14:43:04 1996 +++ new/linux/net/core/dev.c Wed Nov 6 13:39:47 1996 @@ -47,6 +47,9 @@ * 1 device. * Thomas Bogendoerfer : Return ENODEV for dev_open, if there * is no device open function. + * Lawrence V. Stefani : Changed set MTU ioctl to not assume + * min MTU of 68 bytes for devices + * that have change MTU functions. * */ @@ -1107,7 +1110,7 @@ #ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) - net_alias_dev_rehash(dev ,&ifr.ifr_addr); + net_alias_dev_rehash(dev ,&ifr.ifr_addr); #endif dev->pa_addr = new_pa_addr; dev->family = new_family; @@ -1196,17 +1199,17 @@ case SIOCSIFMTU: /* Set the MTU of a device */ - /* - * MTU must be positive. - */ - - if(ifr.ifr_mtu<68) - return -EINVAL; - if (dev->change_mtu) - ret = (*dev->change_mtu)(dev, ifr.ifr_mtu); + ret = dev->change_mtu(dev, ifr.ifr_mtu); else { + /* + * MTU must be positive. + */ + + if(ifr.ifr_mtu<68) + return -EINVAL; + dev->mtu = ifr.ifr_mtu; ret = 0; } @@ -1364,10 +1367,9 @@ * */ extern int lance_init(void); -extern int ni65_init(void); extern int pi_init(void); extern void sdla_setup(void); -extern void dlci_setup(void); +extern int dlci_setup(void); int net_dev_init(void) { @@ -1394,9 +1396,6 @@ */ #if defined(CONFIG_LANCE) lance_init(); -#endif -#if defined(CONFIG_NI65) - ni65_init(); #endif #if defined(CONFIG_PI) pi_init(); diff -ur --new-file old/linux/net/core/net_alias.c new/linux/net/core/net_alias.c --- old/linux/net/core/net_alias.c Sat May 18 19:16:23 1996 +++ new/linux/net/core/net_alias.c Sun Sep 1 08:15:34 1996 @@ -1298,6 +1298,7 @@ */ #ifndef ALIAS_USER_LAND_DEBUG +#ifdef CONFIG_PROC_FS proc_net_register(&(struct proc_dir_entry) { PROC_NET_ALIAS_TYPES, 11, "alias_types", S_IFREG | S_IRUGO, 1, 0, 0, @@ -1310,6 +1311,7 @@ 0, &proc_net_inode_operations, net_alias_getinfo }); +#endif #endif } diff -ur --new-file old/linux/net/core/skbuff.c new/linux/net/core/skbuff.c --- old/linux/net/core/skbuff.c Thu Jun 6 08:45:39 1996 +++ new/linux/net/core/skbuff.c Sat Nov 16 00:48:32 1996 @@ -702,6 +702,9 @@ skb->end=bptr+len; skb->len=0; skb->destructor=NULL; +#ifdef CONFIG_ATM + skb->atm.iovcnt = 0; +#endif return skb; } diff -ur --new-file old/linux/net/core/sock.c new/linux/net/core/sock.c --- old/linux/net/core/sock.c Sat Nov 16 00:48:04 1996 +++ new/linux/net/core/sock.c Sat Nov 16 00:48:32 1996 @@ -453,7 +453,7 @@ { struct sk_buff *skb; int err; - + unsigned long mem; do { if(sk->err!=0) @@ -472,6 +472,9 @@ return NULL; } + + mem=sk->wmem_alloc; + if(!fallback) skb = sock_wmalloc(sk, size, 0, sk->allocation); else @@ -489,8 +492,6 @@ if(skb==NULL) { - unsigned long tmp; - sk->socket->flags |= SO_NOSPACE; if(noblock) { @@ -502,7 +503,6 @@ *errcode=-EPIPE; return NULL; } - tmp = sk->wmem_alloc; cli(); if(sk->shutdown&SEND_SHUTDOWN) { @@ -511,19 +511,7 @@ return NULL; } -#if 1 - if( tmp <= sk->wmem_alloc) -#else - /* ANK: Line above seems either incorrect - * or useless. sk->wmem_alloc has a tiny chance to change - * between tmp = sk->w... and cli(), - * but it might(?) change earlier. In real life - * it does not (I never seen the message). - * In any case I'd delete this check at all, or - * change it to: - */ - if (sk->wmem_alloc + size >= sk->sndbuf) -#endif + if (sk->wmem_alloc==mem) { sk->socket->flags &= ~SO_NOSPACE; interruptible_sleep_on(sk->sleep); diff -ur --new-file old/linux/net/ipv4/arp.c new/linux/net/ipv4/arp.c --- old/linux/net/ipv4/arp.c Sat Nov 16 00:48:04 1996 +++ new/linux/net/ipv4/arp.c Sat Nov 16 00:48:32 1996 @@ -60,6 +60,7 @@ * Mike Shaver : /proc/sys/net/ipv4/arp_* support * Stuart Cheshire : Metricom and grat arp fixes * *** FOR 2.1 clean this up *** + * Lawrence V. Stefani: (08/12/96) Added FDDI support. */ /* RFC1122 Status: @@ -86,6 +87,7 @@ #include #include #include +#include #include #include #include @@ -1289,7 +1291,8 @@ return 1; #ifdef CONFIG_IP_MULTICAST case IS_MULTICAST: - if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802) + if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802 + || dev->type==ARPHRD_FDDI) { u32 taddr; haddr[0]=0x01; @@ -1702,11 +1705,23 @@ /* * Fill the device header for the ARP frame */ - dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len); - /* Fill out the arp protocol part. */ + /* + * Fill out the arp protocol part. + * + * The arp hardware type should match the device type, except for FDDI, + * which (according to RFC 1390) should always equal 1 (Ethernet). + */ +#ifdef CONFIG_FDDI + arp->ar_hrd = (dev->type == ARPHRD_FDDI) ? htons(ARPHRD_ETHER) : htons(dev->type); +#else arp->ar_hrd = htons(dev->type); +#endif + /* + * Exceptions everywhere. AX.25 uses the AX.25 PID value not the + * DIX code for the protocol. Make these device structure fields. + */ #ifdef CONFIG_AX25 #ifdef CONFIG_NETROM arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP); @@ -1751,7 +1766,7 @@ unsigned char *arp_ptr= (unsigned char *)(arp+1); unsigned char *sha,*tha; u32 sip,tip; - + /* * The hardware length of the packet should match the hardware length * of the device. Similarly, the hardware types should match. The @@ -1759,16 +1774,44 @@ * is not from an IP number. We can't currently handle this, so toss * it. */ +#ifdef CONFIG_FDDI + if (dev->type == ARPHRD_FDDI) + { + /* + * According to RFC 1390, FDDI devices should accept ARP hardware types + * of 1 (Ethernet). However, to be more robust, we'll accept hardware + * types of either 1 (Ethernet) or 6 (IEEE 802.2). + */ + if (arp->ar_hln != dev->addr_len || + ((ntohs(arp->ar_hrd) != ARPHRD_ETHER) && (ntohs(arp->ar_hrd) != ARPHRD_IEEE802)) || + dev->flags & IFF_NOARP || + arp->ar_pln != 4) + { + kfree_skb(skb, FREE_READ); + return 0; + } + } + else + { + if (arp->ar_hln != dev->addr_len || + dev->type != ntohs(arp->ar_hrd) || + dev->flags & IFF_NOARP || + arp->ar_pln != 4) + { + kfree_skb(skb, FREE_READ); + return 0; + } + } +#else if (arp->ar_hln != dev->addr_len || - dev->type != ntohs(arp->ar_hrd) || + dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP || arp->ar_pln != 4) { kfree_skb(skb, FREE_READ); return 0; - /* Should this be an error/printk? Seems like something */ - /* you'd want to know about. Unless it's just !IFF_NOARP. -- MS */ } +#endif /* * Another test. @@ -1776,7 +1819,6 @@ * match the protocol the device speaks. If it doesn't, there is a * problem, so toss the packet. */ -/* Again, should this be an error/printk? -- MS */ switch (dev->type) { @@ -1801,14 +1843,8 @@ case ARPHRD_ETHER: case ARPHRD_ARCNET: case ARPHRD_METRICOM: - if(arp->ar_pro != htons(ETH_P_IP)) - { - kfree_skb(skb, FREE_READ); - return 0; - } - break; - case ARPHRD_IEEE802: + case ARPHRD_FDDI: if(arp->ar_pro != htons(ETH_P_IP)) { kfree_skb(skb, FREE_READ); @@ -1984,13 +2020,14 @@ { if (!mask && ip) return -EINVAL; - if (!dev) + if (!dev) { dev = dev_getbytype(r->arp_ha.sa_family); + if (!dev) + return -ENODEV; + } } else { - if (ip_chk_addr(ip) && dev->type != ARPHRD_METRICOM) - return -EINVAL; if (!dev) { struct rtable * rt; @@ -1999,9 +2036,13 @@ return -ENETUNREACH; dev = rt->rt_dev; ip_rt_put(rt); + if (!dev) + return -ENODEV; } + if (dev->type != ARPHRD_METRICOM && ip_chk_addr(ip)) + return -EINVAL; } - if (!dev || (dev->flags&(IFF_LOOPBACK|IFF_NOARP))) + if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) return -ENODEV; if (r->arp_ha.sa_family != dev->type) diff -ur --new-file old/linux/net/ipv4/icmp.c new/linux/net/ipv4/icmp.c --- old/linux/net/ipv4/icmp.c Mon Jun 3 12:23:34 1996 +++ new/linux/net/ipv4/icmp.c Wed Nov 6 13:39:47 1996 @@ -36,6 +36,9 @@ * Willy Konynenberg : Transparent proxying support. * Keith Owens : RFC1191 correction for 4.2BSD based * path MTU bug. + * Thomas Quinot : ICMP Dest Unreach codes up to 15 are + * valid (RFC 1812). + * Alan Cox : Spoofing and junk icmp protections. * * * RFC1122 (Host Requirements -- Comm. Layer) Status: @@ -280,8 +283,11 @@ { ENONET, 1 }, /* ICMP_HOST_ISOLATED */ { ENETUNREACH, 1 }, /* ICMP_NET_ANO */ { EHOSTUNREACH, 1 }, /* ICMP_HOST_ANO */ - { EOPNOTSUPP, 0 }, /* ICMP_NET_UNR_TOS */ - { EOPNOTSUPP, 0 } /* ICMP_HOST_UNR_TOS */ + { ENETUNREACH, 0 }, /* ICMP_NET_UNR_TOS */ + { EHOSTUNREACH, 0 }, /* ICMP_HOST_UNR_TOS */ + { EHOSTUNREACH, 1 }, /* ICMP_PKT_FILTERED */ + { EHOSTUNREACH, 1 }, /* ICMP_PREC_VIOLATION */ + { EHOSTUNREACH, 1 } /* ICMP_PREC_CUTOFF */ }; /* @@ -618,7 +624,9 @@ * Build and send the packet. */ - icmp_build_xmit(&icmp_param, saddr, iph->saddr, ((iph->tos & 0x38) | 6)); + icmp_build_xmit(&icmp_param, saddr, iph->saddr, + icmp_pointers[type].error ? + (iph->tos & 0x1E) | 0xC0 : iph->tos); } @@ -632,6 +640,7 @@ int hash; struct inet_protocol *ipprot; unsigned char *dp; + int match_addr=0; iph = (struct iphdr *) (icmph + 1); @@ -648,8 +657,9 @@ case ICMP_PROT_UNREACH: /* printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n", in_ntoa(iph->daddr), (int)iph->protocol);*/ - break; + /* Drop through */ case ICMP_PORT_UNREACH: + match_addr=1; break; case ICMP_FRAG_NEEDED: #ifdef CONFIG_NO_PATH_MTU_DISCOVERY @@ -730,7 +740,7 @@ default: break; } - if(icmph->code>12) /* Invalid type */ + if(icmph->code>NR_ICMP_UNREACH) /* Invalid type */ return; } @@ -740,42 +750,46 @@ * RFC 1122: 3.2.2 MUST extract the protocol ID from the passed header. * RFC 1122: 3.2.2.1 MUST pass ICMP unreach messages to the transport layer. * RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer. - */ - - /* - * Get the protocol(s). - */ - - hash = iph->protocol & (MAX_INET_PROTOS -1); - - /* - * This can't change while we are doing it. * - * FIXME: Deliver to appropriate raw sockets too. + * Rule: Require port unreachable and protocol unreachable come + * from the host in question. Stop junk spoofs. */ - - ipprot = (struct inet_protocol *) inet_protos[hash]; - while(ipprot != NULL) - { - struct inet_protocol *nextip; - - nextip = (struct inet_protocol *) ipprot->next; - /* - * Pass it off to everyone who wants it. + if(!match_addr || saddr == iph->daddr) + { + /* + * Get the protocol(s). */ + + hash = iph->protocol & (MAX_INET_PROTOS -1); - /* RFC1122: OK. Passes appropriate ICMP errors to the */ - /* appropriate protocol layer (MUST), as per 3.2.2. */ - - if (iph->protocol == ipprot->protocol && ipprot->err_handler) + /* + * This can't change while we are doing it. + */ + + ipprot = (struct inet_protocol *) inet_protos[hash]; + while(ipprot != NULL) { - ipprot->err_handler(icmph->type, icmph->code, dp, + struct inet_protocol *nextip; + + nextip = (struct inet_protocol *) ipprot->next; + + /* + * Pass it off to everyone who wants it. + */ + + /* RFC1122: OK. Passes appropriate ICMP errors to the */ + /* appropriate protocol layer (MUST), as per 3.2.2. */ + + if (iph->protocol == ipprot->protocol && ipprot->err_handler) + { + ipprot->err_handler(icmph->type, icmph->code, dp, iph->daddr, iph->saddr, ipprot); - } + } - ipprot = nextip; - } + ipprot = nextip; + } + } kfree_skb(skb, FREE_READ); } @@ -1023,6 +1037,14 @@ int r; #endif icmp_statistics.IcmpInMsgs++; + + if(len < sizeof(struct icmphdr)) + { + icmp_statistics.IcmpInErrors++; + printk(KERN_INFO "ICMP: runt packet\n"); + kfree_skb(skb, FREE_READ); + return 0; + } /* * Validate the packet diff -ur --new-file old/linux/net/ipv4/igmp.c new/linux/net/ipv4/igmp.c --- old/linux/net/ipv4/igmp.c Sat Aug 17 19:28:10 1996 +++ new/linux/net/ipv4/igmp.c Wed Oct 30 02:42:42 1996 @@ -248,7 +248,7 @@ if(skb==NULL) return; - tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL, + tmp=ip_build_header(skb, dev->pa_addr, address, &dev, IPPROTO_IGMP, NULL, 28 , 0, 1, NULL); if(tmp<0) { @@ -452,7 +452,7 @@ } ih=(struct igmphdr *)skb->h.raw; - if(skb->len ip_hdr->ttl<1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr))) + if(len ip_hdr->ttl<1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr))) { kfree_skb(skb, FREE_READ); return 0; diff -ur --new-file old/linux/net/ipv4/ip_forward.c new/linux/net/ipv4/ip_forward.c --- old/linux/net/ipv4/ip_forward.c Sat Nov 16 00:48:03 1996 +++ new/linux/net/ipv4/ip_forward.c Sat Nov 16 00:48:32 1996 @@ -237,9 +237,10 @@ /* This is for ip encap. Anand, ernet.*/ - if (is_frag&IPFWD_MULTITUNNEL) { + if (is_frag&IPFWD_MULTITUNNEL) + { encap=20; - } + } } else { @@ -271,8 +272,12 @@ if (iph->protocol == IPPROTO_ICMP) { if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0) + { + if (rt) + ip_rt_put(rt); /* Problem - ie bad checksum */ return -1; + } if (fw_res) /* ICMP matched - skip firewall */ diff -ur --new-file old/linux/net/ipv4/ip_fragment.c new/linux/net/ipv4/ip_fragment.c --- old/linux/net/ipv4/ip_fragment.c Wed Aug 7 13:00:08 1996 +++ new/linux/net/ipv4/ip_fragment.c Wed Oct 30 02:42:42 1996 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,8 @@ atomic_t ip_frag_mem = 0; /* Memory used for fragments */ +char *in_ntoa(unsigned long in); + /* * Memory Tracking Functions */ @@ -337,7 +340,15 @@ * Allocate a new buffer for the datagram. */ len = qp->ihlen + qp->len; - + + if(len>65535) + { + printk("Oversized IP packet from %s.\n", in_ntoa(qp->iph->saddr)); + ip_statistics.IpReasmFails++; + ip_free(qp); + return NULL; + } + if ((skb = dev_alloc_skb(len)) == NULL) { ip_statistics.IpReasmFails++; @@ -366,7 +377,7 @@ { NETDEBUG(printk("Invalid fragment list: Fragment over size.\n")); ip_free(qp); - frag_kfree_skb(skb,FREE_WRITE); + kfree_skb(skb,FREE_WRITE); ip_statistics.IpReasmFails++; return NULL; } @@ -424,7 +435,7 @@ if (((flags & IP_MF) == 0) && (offset == 0)) { if (qp != NULL) - ip_free(qp); /* Huh? How could this exist?? */ + ip_free(qp); /* Fragmented frame replaced by full unfragmented copy */ return(skb); } @@ -461,11 +472,24 @@ if ((qp = ip_create(skb, iph, dev)) == NULL) { skb->sk = NULL; - frag_kfree_skb(skb, FREE_READ); + kfree_skb(skb, FREE_READ); ip_statistics.IpReasmFails++; return NULL; } } + + /* + * Attempt to construct an oversize packet. + */ + + if(ntohs(iph->tot_len)+(int)offset>65535) + { + skb->sk = NULL; + printk("Oversized packet received from %s\n",in_ntoa(iph->saddr)); + frag_kfree_skb(skb, FREE_READ); + ip_statistics.IpReasmFails++; + return NULL; + } /* * Determine the position of this fragment. diff -ur --new-file old/linux/net/ipv4/ip_fw.c new/linux/net/ipv4/ip_fw.c --- old/linux/net/ipv4/ip_fw.c Sat Jun 29 11:00:48 1996 +++ new/linux/net/ipv4/ip_fw.c Thu Aug 29 18:15:15 1996 @@ -117,7 +117,7 @@ * Implement IP packet firewall */ -#ifdef CONFIG_IP_FIREWALL_DEBUG +#ifdef DEBUG_IP_FIREWALL #define dprintf1(a) printk(a) #define dprintf2(a1,a2) printk(a1,a2) #define dprintf3(a1,a2,a3) printk(a1,a2,a3) @@ -134,7 +134,7 @@ (ntohl(a)>>8)&0xFF,\ (ntohl(a))&0xFF); -#ifdef CONFIG_IP_FIREWALL_DEBUG +#ifdef DEBUG_IP_FIREWALL #define dprint_ip(a) print_ip(a) #else #define dprint_ip(a) @@ -291,7 +291,7 @@ if (!offset) { src_port=ntohs(tcp->source); dst_port=ntohs(tcp->dest); - if(!tcp->ack) + if(!tcp->ack && !tcp->rst) /* We do NOT have ACK, value TRUE */ notcpack=1; if(!tcp->syn || !notcpack) @@ -321,7 +321,7 @@ prt=IP_FW_F_ALL; break; } -#ifdef CONFIG_IP_FIREWALL_DEBUG +#ifdef DEBUG_IP_FIREWALL dprint_ip(ip->saddr); if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) @@ -610,7 +610,7 @@ ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC ); if ( ftmp == NULL ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: malloc said no\n"); #endif return( ENOMEM ); @@ -652,7 +652,7 @@ ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC ); if ( ftmp == NULL ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: malloc said no\n"); #endif return( ENOMEM ); @@ -704,7 +704,7 @@ if ( ftmp == NULL ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: chain is empty\n"); #endif restore_flags(flags); @@ -773,7 +773,7 @@ if ( len != sizeof(struct ip_fw) ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw)); #endif return(NULL); @@ -781,7 +781,7 @@ if ( (frwl->fw_flg & ~IP_FW_F_MASK) != 0 ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: undefined flag bits set (flags=%x)\n", frwl->fw_flg); #endif @@ -790,7 +790,7 @@ #ifndef CONFIG_IP_TRANSPARENT_PROXY if (frwl->fw_flg & IP_FW_F_REDIR) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: unsupported flag IP_FW_F_REDIR\n"); #endif return(NULL); @@ -799,7 +799,7 @@ #ifndef CONFIG_IP_MASQUERADE if (frwl->fw_flg & IP_FW_F_MASQ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: unsupported flag IP_FW_F_MASQ\n"); #endif return(NULL); @@ -808,7 +808,7 @@ if ( (frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2 ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: src range set but fw_nsp=%d\n", frwl->fw_nsp); #endif @@ -817,7 +817,7 @@ if ( (frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2 ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: dst range set but fw_ndp=%d\n", frwl->fw_ndp); #endif @@ -826,7 +826,7 @@ if ( frwl->fw_nsp + frwl->fw_ndp > (frwl->fw_flg & IP_FW_F_REDIR ? IP_FW_MAX_PORTS - 1 : IP_FW_MAX_PORTS) ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: too many ports (%d+%d)\n", frwl->fw_nsp,frwl->fw_ndp); #endif @@ -873,13 +873,13 @@ /* * Should be panic but... (Why ??? - AC) */ -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_acct_ctl: unknown request %d\n",stage); #endif return(EINVAL); } } -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_acct_ctl: unknown request %d\n",stage); #endif return(EINVAL); @@ -922,7 +922,7 @@ if ( len != sizeof(struct ip_fwpkt) ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: length=%d, expected %d\n", len, sizeof(struct ip_fwpkt)); #endif @@ -933,18 +933,18 @@ ip = &(ipfwp->fwp_iph); if ( !(viadev = dev_get(ipfwp->fwp_vianame)) ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: invalid device \"%s\"\n", ipfwp->fwp_vianame); #endif return(EINVAL); } else if ( viadev->pa_addr != ipfwp->fwp_via.s_addr ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: device \"%s\" has another IP address\n", ipfwp->fwp_vianame); #endif return(EINVAL); } else if ( ip->ihl != sizeof(struct iphdr) / sizeof(int)) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl, sizeof(struct iphdr)/sizeof(int)); #endif @@ -974,7 +974,7 @@ if ( len != sizeof(struct ip_fw_masq) ) { -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl (masq): length %d, expected %d\n", len, sizeof(struct ip_fw_masq)); @@ -1032,14 +1032,14 @@ /* * Should be panic but... (Why are BSD people panic obsessed ??) */ -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: unknown request %d\n",stage); #endif return(EINVAL); } } -#ifdef DEBUG_CONFIG_IP_FIREWALL +#ifdef DEBUG_IP_FIREWALL printk("ip_fw_ctl: unknown request %d\n",stage); #endif return(EINVAL); diff -ur --new-file old/linux/net/ipv4/ip_output.c new/linux/net/ipv4/ip_output.c --- old/linux/net/ipv4/ip_output.c Tue Jul 9 13:28:32 1996 +++ new/linux/net/ipv4/ip_output.c Wed Oct 30 02:42:42 1996 @@ -123,13 +123,13 @@ skb->dev = dev; skb->arp = 1; skb->protocol = htons(ETH_P_IP); + skb_reserve(skb,(dev->hard_header_len+15)&~15); /* 16 byte aligned IP headers are always good */ if (dev->hard_header) { /* * Build a hardware header. Source address is our mac, destination unknown * (rebuild header will sort this out) */ - skb_reserve(skb,(dev->hard_header_len+15)&~15); /* 16 byte aligned IP headers are good */ if (rt && dev == rt->rt_dev && rt->rt_hh) { memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len); diff -ur --new-file old/linux/net/ipv4/ipmr.c new/linux/net/ipv4/ipmr.c --- old/linux/net/ipv4/ipmr.c Fri Jul 19 07:24:05 1996 +++ new/linux/net/ipv4/ipmr.c Wed Oct 30 02:42:42 1996 @@ -17,6 +17,8 @@ * Malcolm Beattie : Buffer handling fixes. * Alexey Kuznetsov : Double buffer free and other fixes. * SVR Anand : Fixed several multicast bugs and problems. + * Alexey Kuznetsov : Subset of bugfixes/changes pending for + * 2.1. Doesn't include Alexey's PIM support. * * Status: * Cache manager under test. Forwarding in vague test mode @@ -60,8 +62,8 @@ static struct vif_device vif_table[MAXVIFS]; /* Devices */ static unsigned long vifc_map; /* Active device map */ int mroute_do_pim = 0; /* Set in PIM assert */ -static struct mfc_cache *mfc_cache_array[MFC_LINES]; /* Forwarding cache */ -static struct mfc_cache *cache_resolve_queue; /* Unresolved cache */ +static struct mfc_cache *mfc_cache_array[MFC_LINES+1]; /* Forwarding cache */ +#define cache_resolve_queue (mfc_cache_array[MFC_LINES])/* Unresolved cache */ int cache_resolve_queue_len = 0; /* Size of unresolved */ /* @@ -79,22 +81,6 @@ } /* - * Find a vif - */ - -static int ipmr_vifi_find(struct device *dev) -{ - struct vif_device *v=&vif_table[0]; - int ct; - for(ct=0;ctdev==dev) - return ct; - } - return -1; -} - -/* * Delete a multicast route cache entry */ @@ -388,6 +374,8 @@ */ cache->mfc_flags|=MFC_RESOLVED; + cache->mfc_parent=mfc->mfcc_parent; + memcpy(cache->mfc_ttls, mfc->mfcc_ttls,sizeof(cache->mfc_ttls)); /* @@ -733,12 +721,7 @@ struct mfc_cache *cache; struct sk_buff *skb2; int psend = -1; - int vif=ipmr_vifi_find(skb->dev); - if(vif==-1) - { - kfree_skb(skb, FREE_WRITE); - return; - } + int vif,ct=0; /* * Without the following addition, skb->h.iph points to something @@ -747,56 +730,63 @@ skb->h.iph = skb->ip_hdr; /* Anand, ernet. */ - vif_table[vif].pkt_in++; - vif_table[vif].bytes_in+=skb->len; - cache=ipmr_cache_find(skb->ip_hdr->saddr,skb->ip_hdr->daddr); - + /* * No usable cache entry */ if(cache==NULL || (cache->mfc_flags&MFC_QUEUED)) - ipmr_cache_unresolved(cache,vif,skb, is_frag); - else { - /* - * Forward the frame - */ - int ct=0; - while(ctip_hdr->ttl > cache->mfc_ttls[ct] && cache->mfc_ttls[ct]>0) - { - if(psend!=-1) - { - /* - * May get variant mac headers - * so must copy -- boo hoo. - */ - skb2=skb_copy(skb, GFP_ATOMIC); - if(skb2) - { - skb2->free=1; - ipmr_queue_xmit(skb2, &vif_table[psend], skb->dev, is_frag); - } - } - psend=ct; + ipmr_cache_unresolved(cache,ALL_VIFS,skb, is_frag); + return; + } + + vif=cache->mfc_parent; + + if(vif>=MAXVIFS || !(vifc_map&(1<dev) + { + kfree_skb(skb, FREE_READ); + return; + } + + vif_table[vif].pkt_in++; + vif_table[vif].bytes_in+=skb->len; + + /* + * Forward the frame + */ + + while(ctip_hdr->ttl > cache->mfc_ttls[ct] && cache->mfc_ttls[ct]>0) + { + if(psend!=-1) + { + /* + * May get variant mac headers + * so must copy -- boo hoo. + */ + skb2=skb_copy(skb, GFP_ATOMIC); + if(skb2) + { + skb2->free=1; + ipmr_queue_xmit(skb2, &vif_table[psend], skb->dev, is_frag); + } } - ct++; - } - if(psend==-1) - kfree_skb(skb, FREE_WRITE); - else - { - ipmr_queue_xmit(skb, &vif_table[psend], skb->dev, is_frag); + psend=ct; } - /* - * Adjust the stats - */ + ct++; + } + if(psend==-1) + kfree_skb(skb, FREE_WRITE); + else + { + ipmr_queue_xmit(skb, &vif_table[psend], skb->dev, is_frag); } } @@ -855,40 +845,38 @@ int ct; len += sprintf(buffer, - "Group Origin SrcIface \n"); + "Group Origin SrcIface VifTtls\n"); pos=len; - for (ct=0;ctmfc_parent)) + if(mfc->mfc_parent < MAXVIFS && vifc_map&(1<mfc_parent)) name=vif_table[mfc->mfc_parent].dev->name; - /* - * Interface forwarding map - */ - for(n=0;nmfc_ttls[ct]) - vifmap[n]='X'; - else - vifmap[n]='-'; - vifmap[n]=0; - /* - * Now print it out - */ - size = sprintf(buffer+len, "%08lX %08lX %-8s %s\n", + + size = sprintf(buffer+len, "%08lX %08lX %-8s", (unsigned long)mfc->mfc_mcastgrp, (unsigned long)mfc->mfc_origin, - name, - vifmap); + name); + + for(n=0;nmfc_ttls[n]); + else + size+=sprintf(buffer+len+size, + " --- "); + } + size+=sprintf(buffer+len+size,"\n"); len+=size; pos+=size; if(pos @@ -204,13 +204,40 @@ /* * If this test doesn't pass, it's not IP, or we should ignore it anyway */ - +#ifdef CONFIG_FDDI + if (dev->type == ARPHRD_FDDI) + { + /* + * Since the dev->type for FDDI is "made up", compare the rarp->ar_hrd + * field against ARPHRD_ETHER and ARPHRD_IEEE802. + * + * Ought to move to a device specifc 'arp_type_ok()' + */ + if (rarp->ar_hln != dev->addr_len + || ((ntohs(rarp->ar_hrd) != ARPHRD_ETHER) && (ntohs(rarp->ar_hrd) != ARPHRD_IEEE802)) + || dev->flags&IFF_NOARP) + { + kfree_skb(skb, FREE_READ); + return 0; + } + } + else + { + if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd) + || dev->flags&IFF_NOARP) + { + kfree_skb(skb, FREE_READ); + return 0; + } + } +#else if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd) || dev->flags&IFF_NOARP) { kfree_skb(skb, FREE_READ); return 0; } +#endif /* * If it's not a RARP request, delete it. diff -ur --new-file old/linux/net/ipv4/raw.c new/linux/net/ipv4/raw.c --- old/linux/net/ipv4/raw.c Sat Jun 8 19:06:45 1996 +++ new/linux/net/ipv4/raw.c Sun Oct 6 15:42:09 1996 @@ -100,7 +100,7 @@ sk->error_report(sk); } - if(code<13) + if(code<=NR_ICMP_UNREACH) { sk->err = icmp_err_convert[code & 0xff].errno; sk->error_report(sk); @@ -236,6 +236,10 @@ memcpy(&sin, usin, sizeof(sin)); if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL); + /* + * Protocol type is host ordered byte. + */ + sin.sin_port=ntohs(sin.sin_port); } else { diff -ur --new-file old/linux/net/ipv4/tcp.c new/linux/net/ipv4/tcp.c --- old/linux/net/ipv4/tcp.c Sat Nov 16 00:48:03 1996 +++ new/linux/net/ipv4/tcp.c Sat Nov 16 00:48:32 1996 @@ -573,7 +573,7 @@ * until we time out, or the user gives up. */ - if (code < 13) + if(code<=NR_ICMP_UNREACH) { if(icmp_err_convert[code].fatal || sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) { @@ -867,15 +867,26 @@ static void wait_for_tcp_memory(struct sock * sk) { release_sock(sk); - cli(); - if (!tcp_memory_free(sk) && - (sk->state == TCP_ESTABLISHED||sk->state == TCP_CLOSE_WAIT) - && sk->err == 0 /* && check shutdown ?? */) - { + if (!tcp_memory_free(sk)) { + struct wait_queue wait = { current, NULL }; + sk->socket->flags &= ~SO_NOSPACE; - interruptible_sleep_on(sk->sleep); + add_wait_queue(sk->sleep, &wait); + for (;;) { + if (current->signal & ~current->blocked) + break; + current->state = TASK_INTERRUPTIBLE; + if (tcp_memory_free(sk)) + break; + if (sk->shutdown & SEND_SHUTDOWN) + break; + if (sk->err) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); } - sti(); lock_sock(sk); } @@ -949,6 +960,7 @@ { if (copied) return copied; + send_sig(SIGPIPE,current,0); return -EPIPE; } @@ -2058,21 +2070,6 @@ sk->mtu = 32; /* Sanity limit */ sk->mtu = min(sk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); - -#ifdef CONFIG_SKIP - - /* - * SKIP devices set their MTU to 65535. This is so they can take packets - * unfragmented to security process then fragment. They could lie to the - * TCP layer about a suitable MTU, but it's easier to let skip sort it out - * simply because the final package we want unfragmented is going to be - * - * [IPHDR][IPSP][Security data][Modified TCP data][Security data] - */ - - if(skip_pick_mtu!=NULL) /* If SKIP is loaded.. */ - sk->mtu=skip_pick_mtu(sk->mtu,dev); -#endif /* * Put in the TCP options to say MTU. diff -ur --new-file old/linux/net/ipv4/tcp_input.c new/linux/net/ipv4/tcp_input.c --- old/linux/net/ipv4/tcp_input.c Sat Nov 16 00:48:04 1996 +++ new/linux/net/ipv4/tcp_input.c Sat Nov 16 00:48:32 1996 @@ -460,6 +460,7 @@ } } #endif + skb->when = jiffies; /* For timeout */ skb_queue_head_init(&newsk->write_queue); skb_queue_head_init(&newsk->receive_queue); newsk->send_head = NULL; @@ -507,6 +508,7 @@ newsk->delay_acks = 1; newsk->copied_seq = skb->seq+1; newsk->fin_seq = skb->seq; + newsk->syn_seq = skb->seq; newsk->state = TCP_SYN_RECV; newsk->timeout = 0; newsk->ip_xmit_timeout = 0; @@ -1019,10 +1021,11 @@ /* * Maybe we can take some stuff off of the write queue, * and put it onto the xmit queue. - * FIXME: (?) There is bizarre case being tested here, to check if + * There is bizarre case being tested here, to check if * the data at the head of the queue ends before the start of - * the sequence we already ACKed. This does not appear to be - * a case that can actually occur. Why are we testing it? + * the sequence we already ACKed. This is not an error, + * it can occur when we send a packet directly off of the write_queue + * in a zero window probe. */ if (!skb_queue_empty(&sk->write_queue) && @@ -1631,6 +1634,9 @@ * moved inline now as tcp_urg is only called from one * place. We handle URGent data wrong. We have to - as * BSD still doesn't use the correction from RFC961. + * + * For 1003.1g we should support a new option TCP_STDURG to permit + * either form. */ static void tcp_check_urg(struct sock * sk, struct tcphdr * th) @@ -1657,6 +1663,15 @@ kill_pg(-sk->proc, SIGURG, 1); } } + /* + * We may be adding urgent data when the last byte read was + * urgent. To do this requires some care. We cannot just ignore + * sk->copied_seq since we would read the last urgent byte again + * as data, nor can we alter copied_seq until this data arrives + * or we break the sematics of SIOCATMARK (and thus sockatmark()) + */ + if (sk->urg_seq == sk->copied_seq) + sk->copied_seq++; /* Move the copied sequence on correctly */ sk->urg_data = URG_NOTYET; sk->urg_seq = ptr; } @@ -1779,7 +1794,6 @@ { struct tcphdr *th; struct sock *sk; - int syn_ok=0; __u32 seq; #ifdef CONFIG_IP_TRANSPARENT_PROXY int r; @@ -2010,7 +2024,6 @@ * Ok.. it's good. Set up sequence numbers and * move to established. */ - syn_ok=1; /* Don't reset this connection for the syn */ sk->acked_seq = skb->seq+1; sk->lastwin_seq = skb->seq+1; sk->fin_seq = skb->seq; @@ -2125,10 +2138,21 @@ return tcp_reset(sk,skb); /* - * !syn_ok is effectively the state test in RFC793. + * Check for a SYN, and ensure it matches the SYN we were + * first sent. We have to handle the rather unusual (but valid) + * sequence that KA9Q derived products may generate of + * + * SYN + * SYN|ACK Data + * ACK (lost) + * SYN|ACK Data + More Data + * .. we must ACK not RST... + * + * We keep syn_seq as the sequence space occupied by the + * original syn. */ - if(th->syn && !syn_ok) + if(th->syn && skb->seq!=sk->syn_seq) { tcp_send_reset(daddr,saddr,th, &tcp_prot, opt, dev, skb->ip_hdr->tos, 255); return tcp_reset(sk,skb); diff -ur --new-file old/linux/net/ipv4/tcp_output.c new/linux/net/ipv4/tcp_output.c --- old/linux/net/ipv4/tcp_output.c Sat Nov 16 00:48:04 1996 +++ new/linux/net/ipv4/tcp_output.c Sat Nov 16 00:48:32 1996 @@ -340,35 +340,38 @@ skb_unlink(skb); /* - * See if we really need to send the packet. + * See if we really need to send the whole packet. */ - if (before(skb->end_seq, sk->rcv_ack_seq +1)) - { + if (before(skb->end_seq, sk->rcv_ack_seq +1)) { /* - * This is acked data. We can discard it. This - * cannot currently occur. + * This is acked data. We can discard it. + * This implies the packet was sent out + * of the write queue by a zero window probe. */ sk->retransmits = 0; kfree_skb(skb, FREE_WRITE); if (!sk->dead) sk->write_space(sk); - } - else - { + } else { struct tcphdr *th; struct iphdr *iph; int size; -/* - * put in the ack seq and window at this point rather than earlier, - * in order to keep them monotonic. We really want to avoid taking - * back window allocations. That's legal, but RFC1122 says it's frowned on. - * Ack and window will in general have changed since this packet was put - * on the write queue. - */ + iph = skb->ip_hdr; th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2)); + + /* See if we need to shrink the leading packet on + * the retransmit queue. Strictly speaking, we + * should never need to do this, but some buggy TCP + * implementations get confused if you send them + * a packet that contains both old and new data. (Feh!) + * Soooo, we have this uglyness here. + */ + if (after(sk->rcv_ack_seq,skb->seq+th->syn+th->fin)) + tcp_shrink_skb(sk,skb,sk->rcv_ack_seq); + size = skb->len - (((unsigned char *) th) - skb->data); #ifndef CONFIG_NO_PATH_MTU_DISCOVERY if (size > sk->mtu - sizeof(struct iphdr)) @@ -378,6 +381,13 @@ } #endif +/* + * put in the ack seq and window at this point rather than earlier, + * in order to keep them monotonic. We really want to avoid taking + * back window allocations. That's legal, but RFC1122 says it's frowned on. + * Ack and window will in general have changed since this packet was put + * on the write queue. + */ th->ack_seq = htonl(sk->acked_seq); th->window = htons(tcp_select_window(sk)); @@ -1009,9 +1019,11 @@ sock_wfree(sk, buff); return; } +#if 0 /* why does this result in problems? */ #ifndef CONFIG_NO_PATH_MTU_DISCOVERY buff->ip_hdr->frag_off |= htons(IP_DF); #endif +#endif t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr)); @@ -1230,4 +1242,57 @@ sk->retransmits++; sk->prot->retransmits ++; tcp_reset_xmit_timer (sk, TIME_PROBE0, sk->rto); +} + +/* + * Remove the portion of a packet that has already been sent. + * Needed to deal with buggy TCP implementations that can't deal + * with seeing a packet that contains some data that has already + * been received. + */ +void tcp_shrink_skb(struct sock *sk, struct sk_buff *skb, u32 ack) +{ + struct iphdr *iph; + struct tcphdr *th; + unsigned char *old, *new; + unsigned long len; + int diff; + + /* + * Recover the buffer pointers + */ + + iph = (struct iphdr *)skb->ip_hdr; + th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2)); + + /* how much data are we droping from the tcp frame */ + diff = ack - skb->seq; + /* how much data are we keeping in the tcp frame */ + len = (skb->end_seq - (th->fin + th->syn)) - ack; + + /* pointers to new start of remaining data, and old start */ + new = (unsigned char *)th + th->doff*4; + old = new+diff; + + /* Update our starting seq number */ + skb->seq = ack; + th->seq = htonl(ack); + iph->tot_len = htons(ntohs(iph->tot_len)-diff); + + /* Get the partial checksum for the IP options */ + if (th->doff*4 - sizeof(*th) > 0) + skb->csum = csum_partial((void *)(th+1), + th->doff*4-sizeof(*th),0); + else + skb->csum = 0; + + /* Copy the good data down and get it's checksum */ + skb->csum = csum_partial_copy((void *)old,(void *)new,len,skb->csum); + + /* shorten the skb */ + skb_trim(skb,skb->len-diff); + + /* Checksum the shrunk buffer */ + tcp_send_check(th, sk->saddr, sk->daddr, + th->doff * 4 + len , skb); } diff -ur --new-file old/linux/net/ipv4/udp.c new/linux/net/ipv4/udp.c --- old/linux/net/ipv4/udp.c Sat Nov 16 00:48:03 1996 +++ new/linux/net/ipv4/udp.c Sat Nov 16 00:48:32 1996 @@ -198,7 +198,7 @@ /* 4.1.3.3. */ /* After the comment above, that should be no surprise. */ - if (code < 13 && icmp_err_convert[code].fatal) + if(code<=NR_ICMP_UNREACH && icmp_err_convert[code].fatal) { /* * 4.x BSD compatibility item. Break RFC1122 to diff -ur --new-file old/linux/net/unix/af_unix.c new/linux/net/unix/af_unix.c --- old/linux/net/unix/af_unix.c Fri Jul 5 15:57:33 1996 +++ new/linux/net/unix/af_unix.c Sat Oct 5 14:50:45 1996 @@ -863,6 +863,12 @@ return -EINVAL; } + if(sk->shutdown&SEND_SHUTDOWN) + { + send_sig(SIGPIPE,current,0); + return -EPIPE; + } + if(sunaddr!=NULL) { if(sock->type==SOCK_STREAM) @@ -925,9 +931,9 @@ * have suggested. Big mallocs stress the vm too * much. */ - - if(size > 4000 && sock->type!=SOCK_DGRAM) - limit = 4000; /* Fall back to 4K if we can't grab a big buffer this instant */ +#define MAX_ALLOC (PAGE_SIZE*7/8) + if(size > MAX_ALLOC && sock->type!=SOCK_DGRAM) + limit = MAX_ALLOC; /* Fall back to 4K if we can't grab a big buffer this instant */ else limit = 0; /* Otherwise just grab and wait */ @@ -973,10 +979,27 @@ sock->state=SS_UNCONNECTED; sti(); kfree_skb(skb, FREE_WRITE); + /* + * Check with 1003.1g - what should + * datagram error + */ + if (!sent) + sent = -ECONNRESET; + return sent; + } + /* + * Stream sockets SIGPIPE + */ + if(sock->type==SOCK_STREAM && other->dead) + { + kfree_skb(skb, FREE_WRITE); + sti(); if(!sent) - return -ECONNRESET; - else - return sent; + { + send_sig(SIGPIPE,current,0); + sent = -EPIPE; + } + return sent; } } else diff -ur --new-file old/linux/scripts/Menuconfig new/linux/scripts/Menuconfig --- old/linux/scripts/Menuconfig Wed Jul 10 06:48:35 1996 +++ new/linux/scripts/Menuconfig Tue Oct 29 16:38:17 1996 @@ -546,7 +546,7 @@ printf("}\n") >>menu return } - else if ($0 ~ /^#|$MAKE|mainmenu_name/) { + else if ($0 ~ /^#|\$MAKE|mainmenu_name/) { printf("") >>menu } else if ($1 == "source") { @@ -860,11 +860,11 @@ # # Load config options from a file. -# Converts all "# OPTION is not set" lines to "OPTION=" lines +# Converts all "# OPTION is not set" lines to "OPTION=n" lines # function load_config_file () { awk ' - /# .* is not set.*/ { printf("%s=\n", $2) } + /# .* is not set.*/ { printf("%s=n\n", $2) } ! /# .* is not set.*/ { print } ' $1 >.tmpconfig @@ -1119,12 +1119,12 @@ Please enter a hexadecimal value. \ Use the key to move from the input field to the buttons below it." -backtitle="Linux Kernel Configuration" - DIALOG="./scripts/lxdialog/lxdialog" kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}" +backtitle="Linux Kernel v$kernel_version Configuration" + trap "cleanup ; rm -f .menuconfig ; exit 1" 1 2 15 @@ -1146,7 +1146,7 @@ echo "#" echo "# Using defaults found in" $DEFAULTS echo "#" - . $DEFAULTS + load_config_file $DEFAULTS else echo "#" echo "# No defaults found" diff -ur --new-file old/linux/scripts/mkdep.c new/linux/scripts/mkdep.c --- old/linux/scripts/mkdep.c Thu Jan 1 01:00:00 1970 +++ new/linux/scripts/mkdep.c Tue Oct 8 17:33:56 1996 @@ -0,0 +1,292 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +char *filename, *command, __depname[256] = "\n\t@touch "; +int needsconfig, hasconfig, hasdep; + +#define depname (__depname+9) + +struct path_struct { + int len; + char buffer[256-sizeof(int)]; +} path_array[2] = { + { 23, "/usr/src/linux/include/" }, + { 0, "" } +}; + +static void handle_include(int type, char *name, int len) +{ + int plen; + struct path_struct *path = path_array+type; + + if (len == 14 && !memcmp(name, "linux/config.h", len)) + hasconfig = 1; + + plen = path->len; + memcpy(path->buffer+plen, name, len); + len += plen; + path->buffer[len] = '\0'; + if (access(path->buffer, F_OK)) + return; + + if (!hasdep) { + hasdep = 1; + printf("%s:", depname); + } + printf(" \\\n %s", path->buffer); +} + +static void handle_config(void) +{ + needsconfig = 1; + if (!hasconfig) + fprintf(stderr, + "%s needs config but has not included config file\n", + filename); +} + +#if defined(__alpha__) || defined(__i386__) +#define LE_MACHINE +#endif + +#ifdef LE_MACHINE +#define next_byte(x) (x >>= 8) +#define current ((unsigned char) __buf) +#else +#define next_byte(x) (x <<= 8) +#define current (__buf >> 8*(sizeof(unsigned long)-1)) +#endif + +#define GETNEXT { \ +next_byte(__buf); \ +if (!__nrbuf) { \ + __buf = *(unsigned long *) next; \ + __nrbuf = sizeof(unsigned long); \ + if (!__buf) \ + break; \ +} next++; __nrbuf--; } +#define CASE(c,label) if (current == c) goto label +#define NOTCASE(c,label) if (current != c) goto label + +static void state_machine(register char *next) +{ + for(;;) { + register unsigned long __buf = 0; + register unsigned long __nrbuf = 0; + +normal: + GETNEXT +__normal: + CASE('/',slash); + CASE('"',string); + CASE('\'',char_const); + CASE('#',preproc); + goto normal; + +slash: + GETNEXT + CASE('*',comment); + goto __normal; + +string: + GETNEXT + CASE('"',normal); + NOTCASE('\\',string); + GETNEXT + goto string; + +char_const: + GETNEXT + CASE('\'',normal); + NOTCASE('\\',char_const); + GETNEXT + goto char_const; + +comment: + GETNEXT +__comment: + NOTCASE('*',comment); + GETNEXT + CASE('/',normal); + goto __comment; + +preproc: + GETNEXT + CASE('\n',normal); + CASE(' ',preproc); + CASE('\t',preproc); + CASE('i',i_preproc); + GETNEXT + +skippreproc: + CASE('\n',normal); + CASE('\\',skippreprocslash); + GETNEXT + goto skippreproc; + +skippreprocslash: + GETNEXT; + GETNEXT; + goto skippreproc; + +i_preproc: + GETNEXT + CASE('f',if_line); + NOTCASE('n',skippreproc); + GETNEXT + NOTCASE('c',skippreproc); + GETNEXT + NOTCASE('l',skippreproc); + GETNEXT + NOTCASE('u',skippreproc); + GETNEXT + NOTCASE('d',skippreproc); + GETNEXT + NOTCASE('e',skippreproc); + +/* "# include" found */ +include_line: + GETNEXT + CASE('\n',normal); + CASE('<', std_include_file); + NOTCASE('"', include_line); + +/* "local" include file */ +{ + char *incname = next; +local_include_name: + GETNEXT + CASE('\n',normal); + NOTCASE('"', local_include_name); + handle_include(1, incname, next-incname-1); + goto skippreproc; +} + +/* include file */ +std_include_file: +{ + char *incname = next; +std_include_name: + GETNEXT + CASE('\n',normal); + NOTCASE('>', std_include_name); + handle_include(0, incname, next-incname-1); + goto skippreproc; +} + +if_line: + if (needsconfig) + goto skippreproc; +if_start: + GETNEXT + CASE('C', config); + CASE('\n', normal); + CASE('_', if_middle); + if (current >= 'a' && current <= 'z') + goto if_middle; + if (current < 'A' || current > 'Z') + goto if_start; +config: + GETNEXT + NOTCASE('O', __if_middle); + GETNEXT + NOTCASE('N', __if_middle); + GETNEXT + NOTCASE('F', __if_middle); + GETNEXT + NOTCASE('I', __if_middle); + GETNEXT + NOTCASE('G', __if_middle); + GETNEXT + NOTCASE('_', __if_middle); + handle_config(); + goto skippreproc; + +if_middle: + GETNEXT +__if_middle: + CASE('\n', normal); + CASE('_', if_middle); + if (current >= 'a' && current <= 'z') + goto if_middle; + if (current < 'A' || current > 'Z') + goto if_start; + goto if_middle; + } +} + +static void do_depend(void) +{ + char *map; + int mapsize; + int pagesizem1 = getpagesize()-1; + int fd = open(filename, O_RDONLY); + struct stat st; + + if (fd < 0) { + perror("mkdep: open"); + return; + } + fstat(fd, &st); + mapsize = st.st_size + 2*sizeof(unsigned long); + mapsize = (mapsize+pagesizem1) & ~pagesizem1; + map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0); + if (-1 == (long)map) { + perror("mkdep: mmap"); + close(fd); + return; + } + close(fd); + state_machine(map); + munmap(map, mapsize); + if (hasdep) + puts(command); +} + +int main(int argc, char **argv) +{ + int len; + char * hpath; + + hpath = getenv("HPATH"); + if (!hpath) + hpath = "/usr/src/linux/include"; + len = strlen(hpath); + memcpy(path_array[0].buffer, hpath, len); + if (len && hpath[len-1] != '/') { + path_array[0].buffer[len] = '/'; + len++; + } + path_array[0].buffer[len] = '\0'; + path_array[0].len = len; + + while (--argc > 0) { + int len; + char *name = *++argv; + + filename = name; + len = strlen(name); + memcpy(depname, name, len+1); + command = __depname; + if (len > 2 && name[len-2] == '.') { + switch (name[len-1]) { + case 'c': + case 'S': + depname[len-1] = 'o'; + command = ""; + } + } + needsconfig = hasconfig = hasdep = 0; + do_depend(); + if (hasconfig && !needsconfig) + fprintf(stderr, "%s doesn't need config\n", filename); + } + return 0; +} .