diff -u --recursive --new-file v2.3.18/linux/CREDITS linux/CREDITS
--- v2.3.18/linux/CREDITS	Fri Sep 10 23:57:27 1999
+++ linux/CREDITS	Wed Sep 29 13:59:12 1999
@@ -157,12 +157,6 @@
 S: Provo, Utah 84606
 S: USA
 
-N: Paul Barton-Davis
-E: pbd@op.net
-D: Driver for WaveFront soundcards (Turtle Beach Maui, Tropez, Tropez+)
-D: Various bugfixes and changes to sound drivers
-S: USA 
-
 N: Krzysztof G. Baranowski
 E: kgb@manjak.knm.org.pl
 P: 1024/FA6F16D1 96 D1 1A CF 5F CA 69 EC  F9 4F 36 1F 6D 60 7B DA
@@ -174,6 +168,12 @@
 S: 62-300 Wrzesnia
 S: Poland
 
+N: Paul Barton-Davis
+E: pbd@op.net
+D: Driver for WaveFront soundcards (Turtle Beach Maui, Tropez, Tropez+)
+D: Various bugfixes and changes to sound drivers
+S: USA 
+
 N: Carlos Henrique Bauer
 E: chbauer@acm.org
 E: bauer@atlas.unisinos.br
@@ -482,11 +482,11 @@
 S: USA
 
 N: Alex deVries
-E: puffin@redhat.com
-D: Various SGI parts, bits of HAL2 and Newport
-S: 18 Bernier Terrace
-S: Kanata, Ontario
-S: K2L 2V@
+E: adevries@thepuffingroup.com
+D: Various SGI parts, bits of HAL2 and Newport, PA-RISC Linux.
+S: 41.5 William Street
+S: Ottawa, Ontario
+S: K1N 6Z9
 S: CANADA
 
 N: Eddie C. Dost
@@ -794,6 +794,10 @@
 S: London SE16 1GD
 S: United Kingdom
 
+N: Kai Harrekilde-Petersen
+E: khp@olicom.dk
+D: Original author of the ftape-HOWTO, i82078 fdc detection code.
+
 N: Bart Hartgers
 E: bart@etpmod.phys.tue.nl
 D: MTRR emulation with Centaur MCRs
@@ -801,10 +805,6 @@
 S: 5623 HZ Eindhoven
 S: The Netherlands
 
-N: Kai Harrekilde-Petersen
-E: khp@olicom.dk
-D: Original author of the ftape-HOWTO, i82078 fdc detection code.
-
 N: Andrew Haylett
 E: ajh@primag.co.uk
 D: Selection mechanism
@@ -858,6 +858,14 @@
 S: 14059 Berlin
 S: Germany
 
+N: David Hinds
+E: dhinds@zen.stanford.edu
+W: http://hyper.stanford.edu/~dhinds
+D: PCMCIA and CardBus stuff, PCMCIA-HOWTO, PCMCIA client drivers
+S: 2019 W. Middlefield Rd #1
+S: Mountain View, CA  94043
+S: USA
+
 N: Michael Hipp
 E: hippm@informatik.uni-tuebingen.de
 D: drivers for the racal ni5210 & ni6510 Ethernet-boards
@@ -1018,19 +1026,6 @@
 S: Mid Glamorgan, CF82 8EJ,
 S: Wales, United Kingdom
 
-N: Andreas S. Krebs
-E: akrebs@altavista.net
-D: CYPRESS CY82C693 chipset IDE, Digital's PC-Alpha 164SX boards
-
-N: Andrzej M. Krzysztofowicz
-E: ankry@mif.pg.gda.pl
-D: XT disk driver
-D: Aladdin 1533/1543(C) chipset IDE
-D: PIIX chipset IDE
-S: ul. Matemblewska 1B/10
-S: 80-283 Gdansk
-S: Poland
-
 N: Bernhard Kaindl
 E: bkaindl@netway.at
 E: edv@bartelt.via.at
@@ -1154,6 +1149,10 @@
 S: L3R 8B2
 S: Canada
 
+N: Andreas S. Krebs
+E: akrebs@altavista.net
+D: CYPRESS CY82C693 chipset IDE, Digital's PC-Alpha 164SX boards
+
 N: Russell Kroll
 E: rkroll@exploits.org
 W: http://www.exploits.org/
@@ -1162,6 +1161,15 @@
 S: Colorado Springs, Colorado 80949-9458
 S: USA
 
+N: Andrzej M. Krzysztofowicz
+E: ankry@mif.pg.gda.pl
+D: XT disk driver
+D: Aladdin 1533/1543(C) chipset IDE
+D: PIIX chipset IDE
+S: ul. Matemblewska 1B/10
+S: 80-283 Gdansk
+S: Poland
+
 N: Gero Kuhlmann
 E: gero@gkminix.han.de
 D: mounting root via NFS
@@ -1253,10 +1261,14 @@
 S: University of Stuttgart, Germany and
 S: Ecole Nationale Superieure des Telecommunications, Paris
 
-N: Martin von Löwis
-E: loewis@informatik.hu-berlin.de
-D: script binary format
-D: NTFS driver
+N: Jamie Lokier
+E: jamie@imbolc.ucc.ie
+D: Reboot-through-BIOS for broken 486 motherboards
+S: 11 Goodson Walk
+S: Marston
+S: Oxford
+S: OX3 0HX
+S: United Kingdom
 
 N: Mark Lord
 E: mlord@pobox.com
@@ -1267,15 +1279,6 @@
 S: Nepean, Ontario
 S: Canada K2H 6S3
 
-N: Jamie Lokier
-E: jamie@imbolc.ucc.ie
-D: Reboot-through-BIOS for broken 486 motherboards
-S: 11 Goodson Walk
-S: Marston
-S: Oxford
-S: OX3 0HX
-S: United Kingdom
-
 N: Warner Losh
 E: imp@village.org
 D: Linux/MIPS Deskstation support, Provided OI/OB for Linux
@@ -1283,6 +1286,11 @@
 S: Niwot, Colorado 80503
 S: USA
 
+N: Martin von Löwis
+E: loewis@informatik.hu-berlin.de
+D: script binary format
+D: NTFS driver
+
 N: H.J. Lu
 E: hjl@gnu.ai.mit.edu
 D: GCC + libraries hacker
@@ -1295,10 +1303,6 @@
 S: 00200 Helsinki
 S: Finland
 
-N: Kai Mäkisara
-E: Kai.Makisara@metla.fi
-D: SCSI Tape Driver
-
 N: Hamish Macdonald
 E: hamishm@lucent.com
 D: Linux/68k port
@@ -1343,6 +1347,10 @@
 S: Halifax, Nova Scotia
 S: Canada B3J 3C8
 
+N: Kai Mäkisara
+E: Kai.Makisara@metla.fi
+D: SCSI Tape Driver
+
 N: Martin Mares
 E: mj@atrey.karlin.mff.cuni.cz
 W: http://atrey.karlin.mff.cuni.cz/~mj/
@@ -1399,11 +1407,13 @@
 E: acme@conectiva.com.br
 W: http://www.conectiva.com.br/~acme
 D: wanrouter hacking
+D: USB hacking
+D: miscellaneous Makefile & Config.in fixes
 D: Cyclom 2X synchronous card driver
 D: i18n for minicom, net-tools, util-linux, fetchmail, etc
-S: Conectiva Informatica LTDA
+S: Conectiva Informática LTDA
 S: R. Prof. Rubens Elke Braga, 558 - Parolin
-S: 80220-320 Curitiba - Parana
+S: 80220-320 Curitiba - Paraná
 S: Brazil
 
 N: Michael Meskes
@@ -1500,13 +1510,6 @@
 S: Lafayette, Indiana 47905
 S: USA
 
-N: Johan Myreen
-E: jem@iki.fi
-D: PS/2 mouse driver writer etc.
-S: Dragonvagen 1 A 13
-S: FIN-00330 Helsingfors
-S: Finland
-
 N: Trond Myklebust
 E: trond.myklebust@fys.uio.no
 D: current NFS client hacker.
@@ -1514,6 +1517,13 @@
 S: N-0391 Oslo
 S: Norway
 
+N: Johan Myreen
+E: jem@iki.fi
+D: PS/2 mouse driver writer etc.
+S: Dragonvagen 1 A 13
+S: FIN-00330 Helsingfors
+S: Finland
+
 N: Matija Nalis
 E: mnalis@jagor.srce.hr
 E: mnalis@voyager.hr
@@ -1628,6 +1638,13 @@
 S: Tula 300000
 S: Russia
 
+N: Johnnie Peters
+E: jpeters@phx.mcd.mot.com
+D: Motorola PowerPC changes for PReP
+S: 2900 S. Diable Way
+S: Tempe, Arizona 85282
+S: USA
+
 N: Kirk Petersen
 E: kirk@speakeasy.org
 W: http://www.speakeasy.org/~kirk/
@@ -1818,13 +1835,6 @@
 S: S-226 64 LUND
 S: Sweden
 
-N: Peter De Schrijver
-E: stud11@cc4.kuleuven.ac.be
-D: Mitsumi CD-ROM driver patches March version
-S: Molenbaan 29
-S: B2240 Zandhoven
-S: Belgium
-
 N: Henning P. Schmiedehausen
 E: hps@tanstaafl.de
 D: added PCI support to the serial driver
@@ -1834,6 +1844,13 @@
 E:
 D: Macintosh IDE Driver
 
+N: Peter De Schrijver
+E: stud11@cc4.kuleuven.ac.be
+D: Mitsumi CD-ROM driver patches March version
+S: Molenbaan 29
+S: B2240 Zandhoven
+S: Belgium
+
 N: Martin Schulze
 E: joey@linux.de
 W: http://home.pages.de/~joey/
@@ -1954,13 +1971,21 @@
 S: Canada M2N 2Z1
 
 N: Adrian Sun
-E: asun@u.washington.edu
+E: asun@cobaltnet.com
 D: hfs support
 D: alpha rtc port, random appletalk fixes
 S: Department of Zoology, University of Washington
 S: Seattle, WA  98195-1800
 S: USA
 
+N: Corey Thomas
+E: corey@world.std.com
+W: http://world.std.com/~corey/index.html
+D: Raylink/WebGear wireless LAN device driver (ray_cs) author
+S: 145 Howard St.
+S: Northborough, MA 01532
+S: USA
+
 N: Tommy Thorn
 E: Tommy.Thorn@irisa.fr
 W: http://www.irisa.fr/prive/thorn/index.html
@@ -1997,9 +2022,9 @@
 D: Miscellaneous kernel hacker
 D: Cyclom 2X driver hacker
 D: linuxconf apache & proftpd module maintainer
-S: Conectiva Informatica LTDA
+S: Conectiva Informática LTDA
 S: R. Prof. Rubens Elke Braga, 558 - Parolin
-S: 80220-320 Curitiba - Parana
+S: 80220-320 Curitiba - Paraná
 S: Brazil
 
 N: Stefan Traby
@@ -2036,6 +2061,17 @@
 S: Tacitusstr. 6
 S: D-50968 Köln
 
+N: Tsu-Sheng Tsao
+E: tsusheng@scf.usc.edu
+D: IGMP(Internet Group Management Protocol) version 2
+S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD
+S: Taipei 
+S: Taiwan 112
+S: Republic of China
+S: 24335 Delta Drive
+S: Diamond Bar, California 91765
+S: USA
+
 N: Theodore Ts'o
 E: tytso@mit.edu
 D: Random Linux hacker
@@ -2051,17 +2087,6 @@
 S: Cambridge, Massachusetts 02139
 S: USA
 
-N: Tsu-Sheng Tsao
-E: tsusheng@scf.usc.edu
-D: IGMP(Internet Group Management Protocol) version 2
-S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD
-S: Taipei 
-S: Taiwan 112
-S: Republic of China
-S: 24335 Delta Drive
-S: Diamond Bar, California 91765
-S: USA
-
 N: Simmule Turner
 E: sturner@tele-tv.com
 D: Added swapping to filesystem
@@ -2193,8 +2218,14 @@
 
 N: David Weinehall
 E: tao@acc.umu.se
-D: NE/2-driver maintainer
+W: http://www.acc.umu.se/~tao/
+W: http://www.acc.umu.se/~mcalinux/
+D: Fixes for the NE/2-driver
 D: Miscellaneous MCA-support
+D: Cleanup of the Config-files
+S: Axtorpsvagen 40:20
+S: S-903 37  UMEA
+S: Sweden
 
 N: Matt Welsh
 E: mdw@metalab.unc.edu
diff -u --recursive --new-file v2.3.18/linux/Documentation/CodingStyle linux/Documentation/CodingStyle
--- v2.3.18/linux/Documentation/CodingStyle	Wed Jun 24 14:30:07 1998
+++ linux/Documentation/CodingStyle	Wed Sep 29 13:59:12 1999
@@ -210,3 +210,26 @@
 "indent" has a lot of options, and especially when it comes to comment
 re-formatting you may want to take a look at the manual page.  But
 remember: "indent" is not a fix for bad programming. 
+
+
+		Chapter 7: Configuration-files
+
+For configuration options (arch/xxx/config.in, and all the Config.in files),
+somewhat different indentation is used.
+
+An indention level of 3 is used in the code, while the text in the config-
+options should have an indention-level of 2 to indicate dependencies. The
+latter only applies to bool/tristate options. For other options, just use
+common sense. An example:
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   tristate 'Apply nitroglycerine inside the keyboard (DANGEROUS)' CONFIG_BOOM
+   if [ "$CONFIG_BOOM" != "n" ]; then
+      bool '  Output nice messages when you explode' CONFIG_CHEER
+   fi
+fi
+
+Generally, CONFIG_EXPERIMENTAL should surround all options not considered
+stable. All options that are known to trash data (experimental write-
+support for file-systems, for instance) should be denoted (DANGEROUS), other
+Experimental options should be denoted (EXPERIMENTAL).
diff -u --recursive --new-file v2.3.18/linux/Documentation/filesystems/proc.txt linux/Documentation/filesystems/proc.txt
--- v2.3.18/linux/Documentation/filesystems/proc.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/proc.txt	Sat Oct  2 07:35:14 1999
@@ -0,0 +1,1379 @@
+
+-----------------------------------------------------------------------
+
+                T H E  /proc   F I L E S Y S T E M
+
+-----------------------------------------------------------------------
+/proc/sys      Terrehon Bowden <terrehon@wpi.com>       January 27 1999
+               Bodo Bauer <bb@ricochet.net>                
+-----------------------------------------------------------------------
+Version 1.1                                         Kernel version 2.2
+-----------------------------------------------------------------------
+Contents
+
+1   Introduction/Credits
+
+1.1  Legal Issues
+
+2   The /proc file system
+
+2.1  Process specific subdirectories
+2.2  Kernel data
+2.3  IDE devices in /proc/ide
+2.4  Networking info in /proc/net
+2.5  SCSI info
+2.6  Parallel port info in /proc/parport
+2.7  TTY info in /proc/tty
+
+3   Reading and modifying kernel parameters
+
+3.1  /proc/sys/debug and /proc/sys/proc
+3.2  /proc/fs - File system data
+3.3  /proc/fs/binfmt_misc - Miscellaneous binary formats
+3.4  /proc/sys/kernel - General kernel parameters
+3.5  /proc/sys/vm - The virtual memory subsystem
+3.6  /proc/sys/dev - Device specific parameters
+3.7  /proc/sys/sunrpc - Remote procedure calls
+3.8  /proc/sys/net - Networking stuff
+3.9  /proc/sys/net/ipv4 - IPV4 settings=20
+3.10 Appletalk
+3.11 IPX
+
+-----------------------------------------------------------------------
+
+1   Introduction/Credits
+
+This documentation is part of a soon to be released book published by
+IDG Books on the SuSE Linux distribution. As there is no complete
+documentation for the /proc file system and we've used many freely
+available sources to write this chapter, it seems only fair to give
+the work back to the Linux community. This work is based on the
+2.1.132 and 2.2.0-pre-kernel versions. I'm afraid it's still far from
+complete, but we hope it will be useful. As far as we know, it is the
+first 'all-in-one' document about the /proc file system. It is
+focused on the Intel x86 hardware, so if you are looking for PPC, ARM,
+SPARC, APX, etc., features, you probably won't find what you are
+looking for. It also only covers IPv4 networking, not IPv6 nor other
+protocols - sorry.
+
+We'd like to thank Alan Cox, Rik van Riel, and Alexey Kuznetsov.  We'd
+also like to extend a special thank you to Andi Kleen for
+documentation, which we relied on heavily to create this document, as
+well as the additional information he provided. Thanks to everybody
+else who contributed source or docs to the Linux kernel and helped
+create a great piece of software... :)
+
+If you have any comments, corrections or additions, please don't
+hesitate to contact Bodo Bauer at bb@ricochet.net. We'll be happy to
+add them to this document.
+
+The latest version of this document is available online at
+http://www.suse.com/~bb/Docs/proc.html in HTML, ASCII, and as 
+Postscript file. 
+
+1.1  Legal Stuff
+
+We don't guarantee the correctness of this document, and if you come
+to us complaining about how you screwed up your system because of
+incorrect documentation, we won't feel responsible...
+
+-----------------------------------------------------------------------
+
+2   The /proc file system
+
+The proc file system acts as an interface to internal data structures
+in the kernel. It can be used to obtain information about the system
+and to change certain kernel parameters at runtime.  It contains
+(among other things) one subdirectory for each process running on the
+system which is named after the process id (PID) of the process. The
+link self points to the process reading the file system.
+
+2.1  Process specific subdirectories
+
+Each process subdirectory has the in table 1.1 listed entries.
+
+      _________________________________________________
+      cmdline Command line arguments
+      environ Values of environment variables
+      fd    Directory, which contains all file descriptors
+      mem   Memory held by this process
+      stat   Process status
+      status  Process status in human readable form
+      cwd   Link to the current working directory
+      exe   Link to the executable of this process
+      maps   Memory maps
+      root   Link to the root directory of this process
+      statm  Process memory status information
+     _________________________________________________
+      Table 1.1: Process specific entries in /proc
+
+For example, to get the status information of a process, all you have
+to do is read the file /proc/PID/status:
+
+> cat /proc/self/status
+Name:   cat
+State:  R (running)
+Pid:    5633
+PPid:   5609
+Uid:    501     501     501     501
+Gid:    100     100     100     100
+Groups: 100 16 
+VmSize:      804 kB
+VmLck:         0 kB
+VmRSS:       344 kB
+VmData:       68 kB
+VmStk:        20 kB
+VmExe:        12 kB
+VmLib:       660 kB
+SigPnd: 0000000000000000
+SigBlk: 0000000000000000
+SigIgn: 0000000000000000
+SigCgt: 0000000000000000
+CapInh: 00000000fffffeff
+CapPrm: 0000000000000000
+CapEff: 0000000000000000
+
+This shows you almost the same information as you would get if you
+viewed it with the ps command. In fact, ps uses the proc file system
+to obtain its information.
+
+The statm file contains more detailed information about the process
+memory usage. It contains seven values with the following meanings:
+
+size       total program size
+resident   size of in memory portions
+shared     number of the pages that are shared
+trs        number of pages that are 'code'
+drs        number of pages of data/stack
+lrs        number of pages of library
+dt	   number of dirty pages
+
+The ratio text/data/library is approximate only by heuristics.
+
+2.2  Kernel data
+
+Similar to the process entries, these are files which give information
+about the running kernel. The files used to obtain this information
+are contained in /proc and are listed in table 1.2. Not all of these
+will be present in your system. It depends on the kernel configuration
+and the loaded modules, which files are there, and which are missing.
+
+      ________________________________________________
+      apm           Advanced power management info
+      cmdline       Kernel command line
+      cpuinfo       Info about the CPU
+      devices       Available devices (block and character)
+      dma           Used DMS channels
+      filesystems   Supported filesystems
+      interrupts    Interrupt usage
+      ioports       I/O port usage
+      kcore         Kernel core image
+      kmsg          Kernel messages
+      ksyms         Kernel symbol table
+      loadavg       Load average
+      locks         Kernel locks
+      meminfo       Memory info
+      misc          Miscellaneous
+      modules       List of loaded modules
+      mounts        Mounted filesystems
+      partitions    Table of partitions known to the system
+      rtc           Real time clock
+      slabinfo      Slab pool info
+      stat          Overall statistics
+      swaps         Swap space utilization
+      uptime        System uptime
+      version       Kernel version
+      ________________________________________________
+           Table 1.2: Kernel info in /proc
+
+You can, for example, check which interrupts are currently in use and
+what they are used for by looking in the file /proc/interrupts:
+
+> cat /proc/interrupts
+           CPU0       
+  0:    8728810          XT-PIC  timer
+  1:        895          XT-PIC  keyboard
+  2:          0          XT-PIC  cascade
+  3:     531695          XT-PIC  aha152x
+  4:    2014133          XT-PIC  serial
+  5:      44401          XT-PIC  pcnet_cs
+  8:          2          XT-PIC  rtc
+ 11:          8          XT-PIC  i82365
+ 12:     182918          XT-PIC  PS/2 Mouse
+ 13:          1          XT-PIC  fpu
+ 14:    1232265          XT-PIC  ide0
+ 15:          7          XT-PIC  ide1
+NMI:          0
+
+There three more important subdirectories in /proc: net, scsi and
+sys. The general rule is that the contents, or even the existence of
+these directories, depends on your kernel configuration. If SCSI is
+not enabled, the directory scsi may not exist. The same is true with
+the net, which is only there when networking support is present in the
+running kernel.
+
+The slabinfo file gives information about memory usage on the slab
+level.  Linux uses slab pools for memory management above page level
+in version 2.2. Commonly used objects have their own slab pool (like
+network buffers, directory cache, etc.).
+
+2.3  IDE devices in /proc/ide
+
+This subdirectory contains information about all IDE devices that the
+kernel is aware of.  There is one subdirectory for each device
+(i.e. hard disk) containing the following files:
+
+       cache             The cache
+       capacity          Capacity of the medium
+       driver            Driver and version
+       geometry          Physical and logical geometry
+       identify          Device identify block
+       media             Media type
+       model             Device identifier
+       settings          Device setup
+       smart_thresholds  IDE disk management thresholds
+       smart_values      IDE disk management values
+
+2.4  Networking info in /proc/net
+
+This directory follows the usual pattern. Table 1.3 lists the files
+and their meaning.
+
+     ____________________________________________________
+     arp             Kernel ARP table
+     dev             network devices with statistics
+     dev_mcast       Lists the Layer2 multicast groups a
+                     device is listening to (interface index,
+                     label, number of references, number of
+                     bound addresses).
+     dev_stat        network device status
+     ip_fwchains     Firewall chain linkage
+     ip_fwnames      Firewall chains
+     ip_masq         Directory containing the masquerading
+                     tables.
+     ip_masquerade   Major masquerading table
+     netstat         Network statistics
+     raw             Raw device statistics
+     route           Kernel routing table
+     rpc             Directory containing rpc info
+     rt_cache        Routing cache
+     snmp            SNMP data
+     sockstat        Socket statistics
+     tcp             TCP sockets
+     tr_rif          Token ring RIF routing table
+     udp             UDP sockets
+     unix            UNIX domain sockets
+     wireless        Wireless interface data (Wavelan etc)
+     igmp            IP multicast addresses, which this host joined
+     psched          Global packet scheduler parameters.
+     netlink         List of PF_NETLINK sockets.
+     ip_mr_vifs      List of multicast virtual interfaces.
+     ip_mr_cache     List of multicast routing cache.
+     udp6            UDP sockets (IPv6)
+     tcp6            TCP sockets (IPv6)
+     raw6            Raw device statistics (IPv6)
+     igmp6	     IP multicast addresses, which this host joineed (IPv6)
+     if_inet6        List of IPv6 interface addresses.
+     ipv6_route      Kernel routing table for IPv6
+     rt6_stats       global IPv6 routing tables statistics.
+     sockstat6       Socket statistics (IPv6)
+     snmp6           Snmp data (IPv6)
+     ____________________________________________________
+         Table 1.3: Network info in /proc/net
+
+You can use this information to see which network devices are
+available in your system and how much traffic was routed over those
+devices:
+
+> cat /proc/net/dev
+Inter-|Receive                                                   |[...
+ face |bytes    packets errs drop fifo frame compressed multicast|[...
+    lo:  908188   5596     0    0    0     0          0         0 [...        
+  ppp0:15475140  20721   410    0    0   410          0         0 [... 
+  eth0:  614530   7085     0    0    0     0          0         1 [...
+
+...] Transmit
+...] bytes    packets errs drop fifo colls carrier compressed
+...]  908188     5596    0    0    0     0       0          0
+...] 1375103    17405    0    0    0     0       0          0
+...] 1703981     5535    0    0    0     3       0          0
+
+2.5  SCSI info
+
+If you have a SCSI host adapter in your system, you'll find a
+subdirectory named after the driver for this adapter in /proc/scsi. 
+You'll also see a list of all recognized SCSI devices in /proc/scsi:
+
+>cat /proc/scsi/scsi
+Attached devices: 
+Host: scsi0 Channel: 00 Id: 00 Lun: 00
+  Vendor: QUANTUM  Model: XP34550W         Rev: LXY4
+  Type:   Direct-Access                    ANSI SCSI revision: 02
+Host: scsi0 Channel: 00 Id: 01 Lun: 00
+  Vendor: SEAGATE  Model: ST34501W         Rev: 0018
+  Type:   Direct-Access                    ANSI SCSI revision: 02
+Host: scsi0 Channel: 00 Id: 02 Lun: 00
+  Vendor: SEAGATE  Model: ST34501W         Rev: 0017
+  Type:   Direct-Access                    ANSI SCSI revision: 02
+Host: scsi0 Channel: 00 Id: 04 Lun: 00
+  Vendor: ARCHIVE  Model: Python 04106-XXX Rev: 703b
+  Type:   Sequential-Access                ANSI SCSI revision: 02
+
+The directory named after the driver has one file for each adapter
+found in the system. These files contain information about
+the controller, including the used IRQ and the IO address range:
+
+>cat /proc/scsi/ncr53c8xx/0
+General information:
+ Chip NCR53C875, device id 0xf, revision id 0x4
+ IO port address 0xec00, IRQ number 11
+ Synchronous period factor 12, max commands per lun 4
+
+2.6  Parallel port info in /proc/parport
+
+The directory /proc/parport contains information about the parallel
+ports of your system. It has one subdirectory for each port, named
+after the port number (0,1,2,...).
+
+This directory contains four files:
+
+    autoprobe   Autoprobe results of this port
+    devices     Connected device modules
+    hardware    Hardware info (port type, io-port, DMA, IRQ, etc.)
+    irq         Used interrupt, if any
+
+2.7  TTY info in /proc/tty
+
+Information about the available and the actually used tty's can be
+found in /proc/tty. You'll find entries for drivers and line
+disciplines in this directory, as shown in the table below:
+
+     drivers       List of drivers and their usage
+     ldiscs        Registered line disciplines
+     driver/serial Usage statistic and status of single tty lines
+
+To see which tty's are currently in use, you can simply look into the
+file /proc/tty/drivers:
+
+>cat /proc/tty/drivers
+pty_slave            /dev/pts      136   0-255 pty:slave
+pty_master           /dev/ptm      128   0-255 pty:master
+pty_slave            /dev/ttyp       3   0-255 pty:slave
+pty_master           /dev/pty        2   0-255 pty:master
+serial               /dev/cua        5   64-67 serial:callout
+serial               /dev/ttyS       4   64-67 serial
+/dev/tty0            /dev/tty0       4       0 system:vtmaster
+/dev/ptmx            /dev/ptmx       5       2 system
+/dev/console         /dev/console    5       1 system:console
+/dev/tty             /dev/tty        5       0 system:/dev/tty
+unknown              /dev/tty        4    1-63 console
+
+-----------------------------------------------------------------------
+
+3   Reading and modifying kernel parameters
+
+A very interesting part of /proc is the directory /proc/sys. This not
+only provides information, it also allows you to change parameters
+within the kernel. Be very careful when trying this. You can optimize
+your system, but you also can crash it. Never play around with kernel
+parameters on a production system. Set up a development machine and
+test to make sure that everything works the way you want it to.  You
+may have no alternative but to reboot the machine once an error has
+been made.
+
+To change a value, simply echo the new value into the file. An example
+is given below in the section on the file system data. You need to be
+root to do this. You can create your own boot script to get this done
+every time your system boots. 
+
+The files in /proc/sys can be used to tune and monitor miscellaneous
+and general things in the operation of the Linux kernel.  Since some
+of the files can inadvertently disrupt your system, it is advisable to
+read both documentation and source before actually making
+adjustments. In any case, be very careful when writing to any of these
+files. The entries in /proc may change slightly between the 2.1.* and
+the 2.2 kernel, so review the kernel documentation if there is any
+doubt. You'll find the documentation in the directory
+/usr/src/linux/Documentation/sys. This chapter is heavily based on the
+documentation included in the pre 2.2 kernels. Thanks to Rick van Riel
+for providing this information.
+
+3.1  /proc/sys/debug and /proc/sys/proc
+
+These two subdirectories are empty.
+
+3.2  /proc/fs - File system data
+
+This subdirectory contains specific file system, file handle, inode,
+dentry and quota information.
+
+Currently, these files are in /proc/sys/fs:
+
+dentry-state
+   Status of the directory cache. Since directory entries are
+   dynamically allocated and deallocated, this file gives information
+   about the current status. It holds six values, in which the last
+   two are not used and are always zero. The other four mean:
+
+       nr_dentry   Seems to be zero all the time
+       nr_unused   Number of unused cache entries
+       age_limit   Age in seconds after the entry may be
+                   reclaimed, when memory is short
+       want_pages  internal
+
+dquot-nr and dquot-max
+   The file dquot-max shows the maximum number of cached disk quota
+   entries.
+
+   The file dquot-nr shows the number of allocated disk quota
+   entries and the number of free disk quota entries.
+
+   If the number of free cached disk quotas is very low and you have
+   a large number of simultaneous system users, you might want
+   to raise the limit.
+
+file-nr and file-max
+   The kernel allocates file handles dynamically, but as yet
+   doesn't free them again.
+
+   The value in file-max denotes the maximum number of file handles
+   that the Linux kernel will allocate. When you get a lot of error
+   messages about running out of file handles, you might want to raise
+   this limit. The default value is 4096. To change it, just write the
+   new number into the file:
+
+   # cat /proc/sys/fs/file-max
+   4096
+   # echo 8192 > /proc/sys/fs/file-max
+   # cat /proc/sys/fs/file-max
+   8192
+
+   This method of revision is useful for all customizable parameters
+   of the kernel - simply echo the new value to the corresponding
+   file.
+  
+   The three values in file-nr denote the number of allocated file
+   handles, the number of used file handles, and the maximum number of
+   file handles. When the allocated file handles come close to the
+   maximum, but the number of actually used ones is far behind, you've
+   encountered a peak in your usage of file handles and you don't need
+   to increase the maximum.
+
+   However, there is still a per process limit of open files, which
+   unfortunatly can't be changed that easily. It is set to 1024 by
+   default. To change this you have to edit the files limits.h and
+   fs.h in the directory /usr/src/linux/include/linux. Change the
+   definition of NR_OPEN and recompile the kernel.
+
+inode-state, inode-nr and inode-max
+   As with file handles, the kernel allocates the inode structures
+   dynamically, but can't free them yet.
+
+   The value in inode-max denotes the maximum number of inode
+   handlers. This value should be 3 to 4 times larger than the value
+   in file-max, since stdin, stdout, and network sockets also need an
+   inode struct to handle them. If you regularly run out of inodes,
+   you should increase this value.
+
+   The file inode-nr contains the first two items from inode-state, so
+   we'll skip to that file...
+
+   inode-state contains three actual numbers and four dummy values. The 
+   actual numbers are (in order of appearance) nr_inodes, nr_free_inodes,
+   and preshrink.
+
+   nr_inodes 
+     Denotes the number of inodes the system has allocated. This can
+     be slightly more than inode-max because Linux allocates them one
+     pageful at a time.
+
+   nr_free_inodes 
+     Represents the number of free inodes and pre shrink is nonzero
+     when the nr_inodes > inode-max and the system needs to prune the
+     inode list instead of allocating more.
+
+super-nr and super-max
+   Again, super block structures are allocated by the kernel,
+   but not freed. The file super-max contains the maximum number of
+   super block handlers, where super-nr shows the number of
+   currently allocated ones.
+
+   Every mounted file system needs a super block, so if you plan to
+   mount lots of file systems, you may want to increase these
+   numbers.
+
+3.3  /proc/fs/binfmt_misc - Miscellaneous binary formats
+
+Besides these files, there is the subdirectory
+/proc/sys/fs/binfmt_misc. This handles the kernel support for
+miscellaneous binary formats.
+
+Binfmt_misc provides the ability to register additional binary formats
+to the Kernel without compiling an additional module/kernel. Therefore
+binfmt_misc needs to know magic numbers at the beginning or the
+filename extension of the binary.
+
+It works by maintaining a linked list of structs, that contain a
+description of a binary format, including a magic with size (or the
+filename extension), offset and mask, and the interpreter name. On
+request it invokes the given interpreter with the original program as
+argument, as binfmt_java and binfmt_em86 and binfmt_mz do.
+Since binfmt_misc does not define any default binary-formats, you have to
+register an additional binary-format.
+
+There are two general files in binfmt_misc and one file per registered
+format. The two general files are register and status.
+
+Registering a new binary format
+
+echo :name:type:offset:magic:mask:interpreter: > /proc/sys/fs/binfmt_misc/register 
+
+with appropriate name (the name for the /proc-dir entry), offset
+(defaults to 0, if omitted), magic and mask (which can be omitted,
+defaults to all 0xff) and last but not least, the interpreter that is
+to be invoked (for example and testing '/bin/echo'). Type can be M for
+usual magic matching or E for filename extension matching (give
+extension in place of magic).
+
+To check or reset the status of the binary format handler:
+
+If you do a cat on the file /proc/sys/fs/binfmt_misc/status, you will
+get the current status (enabled/disabled) of binfmt_misc. Change the
+status by echoing 0 (disables) or 1 (enables) or -1 (caution: this
+clears all previously registered binary formats) to status. For
+example echo 0 > status to disable binfmt_misc (temporarily).
+
+Status of a single handler
+
+Each registered handler has an entry in /proc/sys/fs/binfmt_misc.
+These files perform the same function as status, but their scope is
+limited to the actual binary format. By cating this file, you also
+receive all related information about the interpreter/magic of the
+binfmt.
+
+Example usage of binfmt_misc (emulate binfmt_java)
+
+cd /proc/sys/fs/binfmt_misc
+echo ':Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/javawrapper:' > register
+echo ':HTML:E::html::/usr/local/java/bin/appletviewer:' > register
+echo ':Applet:M::<!--applet::/usr/local/java/bin/appletviewer:' > register
+echo ':DEXE:M::\x0eDEX::/usr/bin/dosexec:' > register
+
+These three lines add support for Java executables and Java applets
+(like binfmt_java, additionally recognizing the .html extension with
+no need to put <!--applet> to every applet file). You have to install
+the JDK and the shell-script /usr/local/java/bin/javawrapper too. It
+works around the brokenness of the Java filename handling. To add a
+Java binary, just create a link to the class-file somewhere in the
+path.
+
+3.4  /proc/sys/kernel - general kernel parameters
+
+This directory reflects general kernel behaviors. As I've said before,
+the contents are depend on your configuration. I'll list the most
+important files, along with descriptions of what they mean and how to
+use them.
+
+acct
+   The file contains three values; highwater, lowwater, and
+   frequency.
+
+   It exists only when BSD-style process accounting is enabled. These
+   values control its behavior. If the free space on the file system
+   where the log lives goes below lowwater%, accounting suspends. If
+   it goes above highwater%, accounting resumes. Frequency determines
+   how often you check the amount of free space (value is in
+   seconds). Default settings are: 4, 2, and 30. That is, suspend
+   accounting if there left <= 2% free; resume it if we have a value
+   >=3%; consider information about the amount of free space valid
+   for 30 seconds
+
+ctrl-alt-del
+   When the value in this file is 0, ctrl-alt-del is trapped and sent
+   to the init(1) program to handle a graceful restart. However, when
+   the value is > 0, Linux's reaction to this key combination will be
+   an immediate reboot, without syncing its dirty buffers.
+
+   Note: when a program (like dosemu) has the keyboard in raw mode,
+   the ctrl-alt-del is intercepted by the program before it ever
+   reaches the kernel tty layer, and it is up to the program to decide
+   what to do with it.
+
+domainname and hostname
+   These files can be controlled to set the NIS domainname and
+   hostname of your box. For the classic darkstar.frop.org a simple:
+
+   # echo "darkstar" > /proc/sys/kernel/hostname
+   # echo "frop.org" > /proc/sys/kernel/domainname
+
+   would suffice to set your hostname and NIS domainname.
+
+osrelease, ostype and version
+
+   The names make it pretty obvious what these fields contain:
+
+   >cat /proc/sys/kernel/osrelease
+   2.1.131
+   >cat /proc/sys/kernel/ostype
+   Linux
+   >cat /proc/sys/kernel/version
+   #8 Mon Jan 25 19:45:02 PST 1999
+ 
+   The files osrelease and ostype should be clear enough. Version
+   needs a little more clarification however. The #8 means that this
+   is the 8th kernel built from this source base and the date behind
+   it indicates the time the kernel was built. The only way to tune
+   these values is to rebuild the kernel.
+
+panic 
+   The value in this file represents the number of seconds the kernel
+   waits before rebooting on a panic. When you use the software
+   watchdog, the recommended setting is 60. If set to 0, the auto
+   reboot after a kernel panic is disabled, this is the default
+   setting.
+
+printk
+   The four values in printk denote console_loglevel,
+   default_message_loglevel, minimum_console_level, and
+   default_console_loglevel respectively.
+
+   These values influence printk() behavior when printing or logging
+   error messages, which come from inside the kernel. See syslog(2)
+   for more information on the different log levels.
+
+   console_loglevel
+     Messages with a higher priority than this will be printed to
+     the console.
+
+   default_message_level
+     Messages without an explicit priority will be printed with
+     this priority.
+
+   minimum_console_loglevel
+     Minimum (highest) value to which the console_loglevel can be set.
+
+   default_console_loglevel
+     Default value for console_loglevel.
+
+sg-big-buff
+   This file shows the size of the generic SCSI (sg) buffer. At this
+   point, you can't tune it yet, but you can change it at compile time
+   by editing include/scsi/sg.h and changing the value of
+   SG_BIG_BUFF.
+
+   If you use a scanner with SANE (Scanner Access now easy) you
+   might want to set this to a higher value. Look into the SANE
+   documentation on this issue.
+
+modprobe
+   The location where the modprobe binary is located. The kernel
+   uses this program to load modules on demand.
+
+3.5  /proc/sys/vm - The virtual memory subsystem
+
+The files in this directory can be used to tune the operation of the
+virtual memory (VM) subsystem of the Linux kernel. In addition, one of
+the files (bdflush) has a little influence on disk usage.
+
+bdflush
+   This file controls the operation of the bdflush kernel daemon. It
+   currently contains 9 integer values, 6 of which are actually used
+   by the kernel:
+
+    nfract      Percentage of buffer cache dirty to
+                activate bdflush
+    ndirty      Maximum number of dirty blocks to
+                write out per-wake-cycle
+    nrefill     Number of clean buffers to try to obtain
+                each time we call refill
+    nref_dirt   Dirty buffer threshold for activating bdflush
+                when trying to refill buffers.
+    dummy       unused
+    age_buffer  Time for normal buffer to age before you flush it
+    age_super   Time for superblock to age before you flush it
+    dummy       unused
+    dummy       unused
+
+   nfract
+     This parameter governs the maximum number of dirty buffers
+     in the buffer cache. Dirty means that the contents of the
+     buffer still have to be written to disk (as opposed to a
+     clean buffer, which can just be forgotten about). Setting
+     this to a high value means that Linux can delay disk writes
+     for a long time, but it also means that it will have to do a
+     lot of I/O at once when memory becomes short. A low value
+     will spread out disk I/O more evenly.
+
+   ndirty
+     Ndirty gives the maximum number of dirty buffers that
+     bdflush can write to the disk at one time. A high value will
+     mean delayed, bursty I/O, while a small value can lead to
+     memory shortage when bdflush isn't woken up often enough.
+
+   nrefill
+     This the number of buffers that bdflush will add to the list
+     of free buffers when refill_freelist() is called. It is
+     necessary to allocate free buffers beforehand, since the
+     buffers are often different sizes than the memory pages
+     and some bookkeeping needs to be done beforehand. The
+     higher the number, the more memory will be wasted and the
+     less often refill_freelist() will need to run.
+
+   nref_dirt
+     When refill_freelist() comes across more than nref_dirt
+     dirty buffers, it will wake up bdflush.
+
+   age_buffer and age_super
+     Finally, the age_buffer and age_super parameters govern the
+     maximum time Linux waits before writing out a dirty buffer
+     to disk. The value is expressed in jiffies (clockticks), the
+     number of jiffies per second is 100. Age_buffer is the
+     maximum age for data blocks, while age_super is for
+     filesystems meta data.
+
+buffermem
+   The three values in this file control how much memory should be
+   used for buffer memory. The percentage is calculated as a
+   percentage of total system memory.
+
+   The values are:
+
+   min_percent
+     This is the minimum percentage of memory that should be
+     spent on buffer memory.
+
+   borrow_percent
+     When Linux is short on memory, and the buffer cache uses more
+     than it has been allotted, the memory mangement (MM) subsystem
+     will prune the buffer cache more heavily than other memory to
+     compensate.
+
+   max_percent
+     This is the maximum amount of memory that can be used for
+     buffer memory.
+
+freepages
+   This file contains three values: min, low and high:
+
+   min
+     When the number of free pages in the system reaches this number,
+     only the kernel can allocate more memory.
+
+   low
+     If the number of free pages gets below this point, the kernel
+     starts swapping aggressively.
+
+   high
+     The kernel tries to keep up to this amount of memory free; if
+     memory comes below this point, the kernel gently starts swapping
+     in the hopes that it never has to do really aggressive swapping.
+
+kswapd
+   Kswapd is the kernel swap out daemon. That is, kswapd is that piece
+   of the kernel that frees memory when it gets fragmented or
+   full. Since every system is different, you'll probably want some
+   control over this piece of the system.
+
+   The file contains three numbers:
+
+   tries_base
+     The maximum number of pages kswapd tries to free in one round is
+     calculated from this number. Usually this number will be divided
+     by 4 or 8 (see mm/vmscan.c), so it isn't as big as it looks.
+
+     When you need to increase the bandwidth to/from swap, you'll want
+     to increase this number.
+
+   tries_min
+     This is the minimum number of times kswapd tries to free a page
+     each time it is called. Basically it's just there to make sure
+     that kswapd frees some pages even when it's being called with
+     minimum priority.
+
+
+  swap_cluster
+     This is probably the greatest influence on system
+     performance. swap_cluster is the number of pages kswapd writes in
+     one turn. You'll want this value to be large so that kswapd does
+     its I/O in large chunks and the disk doesn't have to seek as
+     often., but you don't want it to be too large since that would
+     flood the request queue.
+
+overcommit_memory
+   This file contains one value. The following algorithm is used to
+   decide if there's enough memory: if the value of overcommit_memory
+   is positive, then there's always enough memory. This is a useful
+   feature, since programs often malloc() huge amounts of memory 'just
+   in case', while they only use a small part of it. Leaving this
+   value at 0 will lead to the failure of such a huge malloc(), when
+   in fact the system has enough memory for the program to run.
+
+   On the other hand, enabling this feature can cause you to run out
+   of memory and thrash the system to death, so large and/or important
+   servers will want to set this value to 0.
+
+pagecache
+   This file does exactly the same as buffermem, only this file
+   controls the amount of memory allowed for memory mapping and
+   generic caching of files.
+
+   You don't want the minimum level to be too low, otherwise your
+   system might thrash when memory is tight or fragmentation is
+   high.
+
+pagetable_cache
+   The kernel keeps a number of page tables in a per-processor cache
+   (this helps a lot on SMP systems). The cache size for each
+   processor will be between the low and the high value.
+
+   On a low-memory, single CPU system, you can safely set these values
+   to 0 so you don't waste memory. It is used on SMP systems so that
+   the system can perform fast pagetable allocations without having to
+   aquire the kernel memory lock.
+
+   For large systems, the settings are probably fine. For normal
+   systems they won't hurt a bit. For small systems (<16MB ram) it
+   might be advantageous to set both values to 0.
+
+swapctl
+   This file contains no less than 8 variables. All of these values
+   are used by kswapd.
+
+   The first four variables sc_max_page_age, sc_page_advance,
+   sc_page_decline and sc_page_initial_age are used to keep track of
+   Linux's page aging. Page aging is a bookkeeping method to track
+   which pages of memory are often used, and which pages can be
+   swapped out without consequences.
+
+   When a page is swapped in, it starts at sc_page_initial_age
+   (default 3) and when the page is scanned by kswapd, its age is
+   adjusted according to the following scheme:
+
+    o If the page was used since the last time we scanned, its age
+      is increased by sc_page_advance (default 3) up to a
+      maximum of sc_max_page_age (default 20).
+
+    o Else (meaning it wasn't used) its age is decreased by
+      sc_page_decline (default 1).
+
+   When a page reaches age 0, it's ready to be swapped out.
+
+   The next four variables sc_age_cluster_fract, sc_age_cluster_min,
+   sc_pageout_weight and sc_bufferout_weight, can be used to control
+   kswapd's aggressiveness in swapping out pages.
+
+   Sc_age_cluster_fract is used to calculate how many pages from a
+   process are to be scanned by kswapd. The formula used is
+
+           sc_age_cluster_fract
+           -------------------- * resident set size
+              1024   =20
+
+   So if you want kswapd to scan the whole process,
+   sc_age_cluster_fract needs to have a value of 1024. The minimum
+   number of pages kswapd will scan is represented by
+   sc_age_cluster_min, this is done so kswapd will also scan small
+   processes.
+
+   The values of sc_pageout_weight and sc_bufferout_weight are used
+   to control how many tries kswapd will make in order to swap out
+   one page/buffer. These values can be used to fine-tune the ratio
+   between user pages and buffer/cache memory. When you find that
+   your Linux system is swapping out too many process pages in order
+   to satisfy buffer memory demands, you might want to either
+   increase sc_bufferout_weight, or decrease the value of
+   sc_pageout_weight.
+
+3.6  /proc/sys/dev - Device specific parameters
+
+Currently there is only support for CDROM drives, but other drivers may
+wish to register themselves in here in the future. The cdrom/ directory
+contains several files that either control or supply information about
+the CDROM subsystem. 
+
+>cat /proc/sys/dev/cdrom/info
+CD-ROM information, Id: cdrom.c 3.04 1999/09/12
+
+drive name:		hdd	hdc	hdb	hda
+drive speed:		32	24	10	0
+drive # of slots:	1	1	1	1
+Can close tray:		1	1	1	1
+Can open tray:		1	1	1	1
+Can lock tray:		1	1	1	1
+Can change speed:	1	1	1	1
+Can select disk:	0	0	0	0
+Can read multisession:	1	1	1	1
+Can read MCN:		1	1	1	1
+Reports media changed:	1	1	1	1
+Can play audio:		1	1	1	1
+Can write CD-R:		0	1	0	0
+Can write CD-RW:	0	1	0	0
+Can read DVD:		0	0	0	1
+Can write DVD-R:	0	0	0	0
+Can write DVD-RAM:	0	0	0	0
+
+You see four drives and their lists of features. These are all ATAPI
+drives - SCSI drives will be numbered sr0, sr1, and so forth.
+
+The remaining files all set options in the driver.
+
+autoclose
+    Close the drive tray when the drive is accessed.
+
+autoeject
+    Eject the tray when the drive is umounted.
+
+check_media
+    Verify the media type when opening the device. This is generally
+    meant for audio CD's.
+
+debug
+    Print debugging messages.
+
+lock
+    Lock the tray when the drive is in use.
+
+
+3.7  /proc/sys/sunrpc - Remote procedure calls
+
+This directory contains four files, which enable or disable debugging
+for the RPC functions NFS, NFS-daemon, RPC and NLM. The default values
+are 0. They can be set to one, to turn debugging on.  (The default
+value is 0 for each)
+
+3.8  /proc/sys/net - Networking stuff
+
+The interface to the networking parts of the kernel is located in
+/proc/sys/net. The table below shows all possible subdirectories. You
+may see only some of them, depending on the configuration of your
+kernel:
+
++-------------------------------------------------------------+
+| core     General parameter   |appletalk  Appletalk protocol |
+| unix     Unix domain sockets |netrom     NET/ROM            |
+| 802      E802 protocol       |ax25       AX25               |
+| ethernet Ethernet protocol   |rose       X.25 PLP layer     |
+| ipv4     IP version 4        |x25        X.25 protocol      |
+| ipx      IPX                 |token-ring IBM token ring     |
+| bridge   Bridging            |decnet     DEC net            |
+| ipv6     IP version 6        |                              |
++-------------------------------------------------------------+
+
+We will concentrate on IP networking here. As AX15, X.25, and DEC Net
+are only minor players in the Linux world, we'll skip them in this
+chapter. You'll find some short info to Appletalk and IPX further down
+in section 3.10 and 3.11. Please look in the online documentation and
+the kernel source to get a detailed view of the parameters for those
+protocols. In this section we'll discuss the subdirectories printed in
+bold letters in the table above. As default values are suitable for
+most needs, there is no need to change these values.
+
+/proc/sys/net/core - Network core options
+
+rmem_default
+   The default setting of the socket receive buffer in bytes.
+
+rmem_max
+   The maximum receive socket buffer size in bytes.
+
+wmem_default
+   The default setting (in bytes) of the socket send buffer.
+
+wmem_max
+   The maximum send socket buffer size in bytes.
+
+message_burst and message_cost
+   These parameters are used to limit the warning messages written to
+   the kernel log from the networking code. They enforce a rate limit
+   to make a denial-of-service attack impossible. The higher the
+   message_cost factor is, the less messages will be
+   written. Message_burst controls when messages will be dropped. The
+   default settings limit warning messages to one every five seconds.
+
+netdev_max_backlog
+   Maximal number of packets, queued on INPUT side, when the interface
+   receives packets faster than kernel can process them.
+
+optmem_max
+   Maximum ancillary buffer size allowed per socket. Ancillary data is
+   a sequence of struct cmsghdr structures with appended data.
+
+/proc/sys/net/unix - Parameters for UNIX domain sockets
+
+There are only two files in this subdirectory. They control the delays
+for deleting and destroying socket descriptors.
+
+3.9  /proc/sys/net/ipv4 - IPV4 settings
+
+IP version 4 is still the most used protocol in Unix networking. It
+will be replaced by IP version 6 in the next couple of years, but for
+the moment it's the de facto standard for the internet and is used in
+most networking environments around the world. Because of the
+importance of this protocol, we'll have a deeper look into the subtree
+controlling the behavior of the IPv4 subsystem of the Linux kernel.
+
+Let's start with the entries in /proc/sys/net/ipv4 itself.
+
+ICMP settings
+
+icmp_echo_ignore_all and icmp_echo_ignore_broadcasts
+   Turn on (1) or off (0), if the kernel should ignore all ICMP ECHO
+   requests, or just those to broadcast and multicast addresses.
+
+   Please note that if you accept ICMP echo requests with a
+   broadcast/multicast destination address your network may be used
+   as an exploder for denial of service packet flooding attacks to
+   other hosts.
+
+icmp_destunreach_rate, icmp_echoreply_rate,
+icmp_paramprob_rate and icmp_timeexeed_rate
+   Sets limits for sending ICMP packets to specific targets. A value of
+   zero disables all limiting. Any positive value sets the maximum
+   package rate in hundredths of a second (on Intel systems).
+
+IP settings
+
+ip_autoconfig
+   This file contains one, if the host got its IP configuration by
+   RARP, BOOTP, DHCP or a similar mechanism. Otherwise it is zero.
+
+ip_default_ttl
+   TTL (Time To Live) for IPv4 interfaces. This is simply the
+   maximum number of hops a packet may travel.
+
+ip_dynaddr
+   Enable dynamic socket address rewriting on interface address change. This
+   is useful for dialup interface with changing IP addresses.
+
+ip_forward
+   Enable or disable forwarding of IP packages between interfaces. A
+   change of this value resets all other parameters to their default
+   values. They differ if the kernel is configured as host or router.
+
+ip_local_port_range
+   Range of ports used by TCP and UDP to choose the local
+   port. Contains two numbers, the first number is the lowest port,
+   the second number the highest local port. Default is 1024-4999.
+   Should be changed to 32768-61000 for high-usage systems.
+
+ip_no_pmtu_disc
+   Global switch to turn path MTU discovery off. It can also be set
+   on a per socket basis by the applications or on a per route
+   basis.
+
+ip_masq_debug
+   Enable/disable debugging of IP masquerading.
+
+
+IP fragmentation settings
+
+ipfrag_high_trash and ipfrag_low_trash
+   Maximum memory used to reassemble IP fragments. When
+   ipfrag_high_thresh bytes of memory is allocated for this purpose,
+   the fragment handler will toss packets until ipfrag_low_thresh is
+   reached.
+
+
+ipfrag_time
+   Time in seconds to keep an IP fragment in memory.
+
+TCP settings
+
+tcp_retrans_collapse
+   Bug-to-bug compatibility with some broken printers. On retransmit
+   try to send bigger packets to work around bugs in certain TCP
+   stacks. Can be turned off by setting it to zero.
+
+tcp_keepalive_probes
+   Number of keep alive probes TCP sends out, until it decides that the
+   connection is broken.
+
+tcp_keepalive_time
+   How often TCP sends out keep alive messages, when keep alive is
+   enabled. The default is 2 hours.
+
+tcp_syn_retries
+   Number of times initial SYNs for a TCP connection attempt will be
+   retransmitted. Should not be higher than 255. This is only the
+   timeout for outgoing connections, for incoming connections the
+   number of retransmits is defined by tcp_retries1.
+
+tcp_sack
+   Enable select acknowledgments after RFC2018.
+
+tcp_timestamps
+   Enable timestamps as defined in RFC1323.
+
+tcp_stdurg
+   Enable the strict RFC793 interpretation of the TCP urgent pointer
+   field. The default is to use the BSD compatible interpretation
+   of the urgent pointer pointing to the first byte after the urgent
+   data. The RFC793 interpretation is to have it point to the last
+   byte of urgent data. Enabling this option may lead to
+   interoperatibility problems. Disabled by default.
+
+tcp_syncookies
+   Only valid when the kernel was compiled with
+   CONFIG_SYNCOOKIES. Send out syncookies when the syn backlog queue
+   of a socket overflows. This is to prevent against the common 'syn
+   flood attack'. Disabled by default.
+
+   Note that the concept of a socket backlog is abandoned, this
+   means the peer may not receive reliable error messages from an
+   over loaded server with syncookies enabled.
+
+tcp_window_scaling
+   Enable window scaling as defined in RFC1323.
+
+tcp_fin_timeout
+   How many seconds to wait for a final FIN before the socket is
+   always closed. This is strictly a violation of the TCP
+   specification, but required to prevent denial-of-service attacks.
+
+tcp_max_ka_probes
+   How many keepalive probes are sent per slow timer run. Shouldn't be
+   set too high to prevent bursts.
+
+tcp_max_syn_backlog
+   Length of the per socket backlog queue. Since Linux 2.2 the backlog
+   specified in listen(2) only specifies the length of the backlog
+   queue of already established sockets. When more connection requests
+   arrive Linux starts to drop packets. When syncookies are enabled
+   the packets are still answered and the maximum queue is effectively
+   ignored.
+
+tcp_retries1
+   Defines how often an answer to a TCP connection request is
+   retransmitted before giving up.
+
+tcp_retries2
+   Defines how often a TCP packet is retransmitted before giving up.
+
+Interface specific settings
+
+In the directory /proc/sys/net/ipv4/conf you'll find one subdirectory
+for each interface the system knows about and one directory calls
+all. Changes in the all subdirectory affect all interfaces, where
+changes in the other subdirectories affect only one interface.
+
+All directories have the same entries:
+
+accept_redirects
+   This switch decides if the kernel accepts ICMP redirect messages
+   or not. The default is 'yes', if the kernel is configured for a
+   regular host; and 'no' for a router configuration.
+
+accept_source_route
+   Should source routed packages be accepted or declined. The
+   default is dependent on the kernel configuration. It's 'yes' for
+   routers and 'no' for hosts.
+
+bootp_relay
+   Accept packets with source address 0.b.c.d destined not to this
+   host as local ones. It is supposed that BOOTP relay daemon will
+   catch and forward such packets.
+
+   The default is 'no', as this feature is not implemented yet
+   (kernel version 2.2.0-pre?).
+
+forwarding
+   Enable or disable IP forwarding on this interface.
+
+log_martians
+   Log packets with source addresses with no known route to kernel log.
+
+mc_forwarding
+   Do multicast routing. The kernel needs to be compiled with
+   CONFIG_MROUTE and a multicast routing daemon is required.
+
+proxy_arp
+   Do (1) or don't (0) do proxy ARP.
+
+rp_filter
+   Integer value deciding if source validation should be made. 
+   1 means yes, 0 means no. Disabled by default, but 
+   local/broadcast address spoofing is always on.
+
+   If you set this to 1 on a router that is the only connection
+   for a network to the net , it evidently prevents spoofing attacks
+   against your internal networks (external addresses can still be
+   spoofed), without the need for additional firewall rules.
+
+secure_redirects
+   Accept ICMP redirect messages only for gateways, listed in
+   default gateway list. Enabled by default.
+
+shared_media
+   If it is not set the kernel does not assume that different subnets
+   on this device can communicate directly. Default setting is 'yes'.  
+
+send_redirects
+   Determines if or if not to send ICMP redirects to other hosts.
+
+
+Routing settings
+
+The directory /proc/sys/net/ipv4/route contains several file to
+control routing issues.
+
+error_burst and error_cost
+   These parameters are used to limit the warning messages written to
+   the kernel log from the routing code. The higher the error_cost
+   factor is, the fewer messages will be written. Error_burst controls
+   when messages will be dropped. The default settings limit warning
+   messages to one every five seconds.
+
+flush
+   Writing to this file results in a flush of the routing cache.
+
+gc_elastic, gc_interval, gc_min_interval, gc_tresh, gc_timeout
+   Values to control the frequency and behavior of the garbage
+   collection algorithm for the routing cache.
+
+max_size
+   Maximum size of the routing cache. Old entries will be purged
+   once the cache has this size.
+
+max_delay, min_delay
+   Delays for flushing the routing cache.
+
+redirect_load, redirect_number
+   Factors which determine if more ICPM redirects should be sent to
+   a specific host. No redirects will be sent once the load limit or
+   the maximum number of redirects has been reached.
+
+redirect_silence
+
+   Timeout for redirects. After this period redirects will be sent
+   again, even if this has been stopped, because the load or number
+   limit has been reached.
+
+Network Neighbor handling
+
+Settings about how to handle connections with direct neighbors (nodes
+attached to the same link) can be found in the directory
+/proc/sys/net/ipv4/neigh.
+
+As we saw it in the conf directory, there is a default subdirectory
+which holds the default values, and one directory for each
+interface. The contents of the directories are identical, with the
+single exception that the default settings contain additional options
+to set garbage collection parameters.
+
+In the interface directories you'll find the following entries:
+
+base_reachable_time
+   A base value used for computing the random reachable time value
+   as specified in RFC2461.
+
+retrans_time
+   The time, expressed in jiffies (1/100 sec), between retransmitted
+   Neighbor Solicitation messages. Used for address resolution and to
+   determine if a neighbor is unreachable.
+
+unres_qlen
+   Maximum queue length for a pending arp request - how many packets
+   are accepted from other layers while the arp address is still
+   resolved.
+
+anycast_delay
+   Maximum for random delay of answers to neighbor solicitation
+   messages in jiffies (1/100 sec). Not yet implemented (Linux does
+   not have anycast support yet).
+
+ucast_solicit
+   Maximum number of retries for unicast solicitation.
+
+mcast_solicit
+   Maximum number of retries for multicast solicitation.
+
+delay_first_probe_time
+   Delay for the first time probe if the neighbor is reachable. (see
+   gc_stale_time).
+
+locktime
+   An ARP/neighbor entry is only replaced with a new one if the old
+   is at least locktime old. This prevents ARP cache thrashing.
+
+proxy_delay
+   Maximum time (real time is random [0..proxytime]) before
+   answering to an arp request for which we have an proxy arp entry.
+   In some cases, this is used to prevent network flooding.
+
+proxy_qlen
+   Maximum queue length of the delayed proxy arp timer (see
+   proxy_delay).
+
+app_solcit
+   Determines the number of requests to send to the user level arp
+   daemon. 0 to turn off.
+
+gc_stale_time
+   Determines how often to check for stale ARP entries. After an ARP
+   entry is stale it will be resolved again (useful when an IP address
+   migrates to another machine). When ucast_solicit is > 0 it first
+   tries to send an ARP packet directly to the known host, when that
+   fails and mcast_solicit is > 0, an ARP request is broadcasted.
+
+3.10  Appletalk
+
+The /proc/sys/net/appletalk directory holds the Appletalk
+configuration data when Appletalk is loaded. The configurable
+parameters are:
+
+aarp-expiry-time
+   The amount of time we keep an AARP entry before expiring
+   it. Used to age out old hosts.
+
+aarp-resolve-time
+   The amount of time we will spend trying to resolve an Appletalk
+   address.
+
+aarp-retransmit-limit
+   The number of times we will retransmit a query before giving up.
+
+aarp-tick-time
+   Controls the rate at which expiries are checked.
+
+
+The directory /proc/net/appletalk holds the list of active appletalk
+sockets on a machine.
+
+The fields indicate the DDP type, the local address (in network:node
+format) the remote address, the size of the transmit pending queue,
+the size of the received queue (bytes waiting for applications to
+read) the state and the uid owning the socket.
+
+/proc/net/atalk_iface lists all the interfaces configured for
+appletalk.It shows the name of the interface, its appletalk address,
+the network range on that ad- dress (or network number for phase 1
+networks), and the status of the interface.
+
+/proc/net/atalk_route lists each known network route. It lists the
+target (network) that the route leads to, the router (may be directly
+connected), the route flags, and the device the route is via.
+
+3.11  IPX
+
+The IPX protocol has no tunable values in /proc/sys/net.
+
+The IPX protocol does, however, provide /proc/net/ipx. This lists each
+IPX socket giving the local and remote addresses in Novell format
+(that is network:node:port). In accordance with the strange Novell
+tradition, everything but the port is in hex. Not_Connected is
+displayed for sockets that are not tied to a specific remote
+address. The Tx and Rx queue sizes indicate the number of bytes
+pending for transmit and receive. The state indicates the state the
+socket is in and the uid is the owning uid of the socket.
+
+The /proc/net/ipx_interface file lists all IPX interfaces. For each
+interface it gives the network number, the node number, and indicates
+if the network is the primary network. It also indicates which device it is bound to (or
+Internal for internal networks) and the Frame Type if
+appropriate. Linux supports 802.3, 802.2, 802.2 SNAP and DIX (Blue
+Book) ethernet framing for IPX.
+
+The /proc/net/ipx_route table holds a list of IPX routes. For each
+route it gives the destination network, the router node (or Directly)
+and the network address of the router (or Connected) for internal
+networks.
diff -u --recursive --new-file v2.3.18/linux/Documentation/networking/README.sb1000 linux/Documentation/networking/README.sb1000
--- v2.3.18/linux/Documentation/networking/README.sb1000	Thu Aug 26 13:05:34 1999
+++ linux/Documentation/networking/README.sb1000	Wed Sep 29 13:59:12 1999
@@ -1,133 +1,207 @@
-This is the new release of a module network device driver for General
-Instruments (also known as NextLevel) SB1000 cable modem board (also
-called internal SURFboard).
-I have tested and I am running this module on kernel 2.0.33.
-Steven N. Hirsch <shirsch@adelphia.net> gave me a diff patch
-(sb1000-1.1.2_127.patch to be applied with 'patch -p') that allows
-you to compile and run the driver with kernel versions 2.1.x.
-Thanks very much Steve!
-
-Here you'll the following files:
-
-- README
-- Makefile
-- sb1000.c      the actual device driver
-- cmconfig.c    an ifconfig-like program to correctly set the SB1000
-- cmping.c      a ping-like program to test your connection
-- ftptest.c     a small program to test your connection speed with FTP
-                (requires ftplib version >= 3.0)
-
-The directory ppp/ contains the files I am using to connect to MediaOne
-here in Jacksonville, to show how you can use the driver and the cmconfig
-program.
-
-Clemmitt Sigler wrote a very good and useful web page and installation
-script (for Adelphia PowerLink users but easy to port to other ISPs)
-about this SB1000 driver for Linux.
-You can find it here:
-     http://home.adelphia.net/~siglercm/sb1000.html
-Thanks very much Clemmitt!
-
-To install and run it:
-- cd sb1000-1.1.2
-- make
-- make install
-- configure the SB1000 card using the isapnp tools setting the correct
-  I/O's and IRQ. You can find more info about the isapnp tools at:
-     http://www.roestock.demon.co.uk/isapnptools/
-  (once you're happy with it, you may want to set the configuration
-   at boot time in one of the /etc/init.d/ scripts)
-  IMPORTANT NOTICE: after configuring isapnp, please look in the file
-  /etc/isapnp.conf for the line beginning with:
-     (READPORT
-  if it says: '(READPORT 0x0203)' (which should be the default), then
-  go to the next step. If instead it says something like:
-  '(READPORT 0x020b)' or '(READPORT 0x<another number>)', then please
-  follow the instructions at the end of this README file before going
-  to the next step.
-- install the PPP configuration files in the /etc/ppp directory; you
-  definitely have to change the file ppp@gi-on (the start up file),
-  setting the correct login name, the phone number for the PPP connection
-  and the frequency (k stands for kHz, M for MHz) for the cablemodem.
-  You'll also have to change the last line in the pap-secrets file
-  writing your login and password there (read the whole file for more
-  site specific configurations).
-  You may have to change the firewall file to suit your needs (IPs and
-  ports allowed to connect).
-- start the PPP connection with ppp@gi-on and see what happens; you may
-  want to set 'pppd' (the last command in the ppp@gi-on file) to debug
-  mode adding 'debug' at the end of the command ('pppd' prints its info in
-  the file /var/log/messages and/or /var/log/ppp.log). To make sure 'chat'
-  s not having problems you may want to add a '-v' (verbose) after the 'chat'
-  command in 'ppp-on-dialer' and check the results in /var/log/messages)
-- if everything is working fine you should see after a few seconds a message
-  likes this:
-            cm0: sb1000 at (0x120,0x310), csn 1, S/N 0x2a0d16d8, IRQ 9. 
-            sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net) 
-  and ifconfig -a should show you two new interfaces: ppp0 and cm0.
-  Typing 'cmconfig cm0' will show you more info about the cablemodem interface.
-- please let me know if you see any other message coming from 'cm0' in
-  your '/var/log/messages' or '/var/log/debug' file, to help me debug the
-  code
-- also let me know if you have any problem; if everything works (hopefully),
-  let me know which speed you can reach downloading the 5Mb test file using
-  this driver.
-- if everything seems to work fine but your computer locks up after a while
-  (and typically during a lengthy download through the cablemodem), you may
-  need to add a short delay in the driver to 'slow down' the SURFboard
-  because your PC might not be able to keep up with the transfer rate of
-  the SB1000. To do this, edit the 'Makefile' and look for the 'SB1000_DELAY'
-  define: uncomment those 'CFLAGS' lines (and comment the default ones)
-  and try setting the delay to something like 60 microseconds with:
-  '-DSB1000_DELAY=60'; if it still doesn't work or you like playing with
-  the driver, you may try other numbers: remember though that the higher
-  the delay, the slower the driver (which slows down the rest of the PC
-  too when it is actively used). Thanks to Ed Daiga for this tip!
-
-This release has a few things fixed and a better handling of the frame errors.
-
-I also have slightly modified the 'cablemodem' script in the 'ppp' so
-you may interested in having a look at it: I basically have added filtering
-out broadcast messages (you shouldn't get them with a point-to-point
-interface and so far the ones I have received were from hosts trying to
-attack my system) and lower debug levels for the driver to avoid seeing
-the 'frame error' messages (so far noone has noticed any problem with the
-driver even with those messages).
-
-For questions, infos, etc, feel free to email me at:
-		fventuri@mediaone.net
-
-Good luck,
-Franco Venturi
-
-Thanks to Edge for his useful comments and to Steven N. Hirsch
-for his patch to run the driver with 2.1.x kernels.
-Thanks also to Ed Daiga for his help in finding that adding a delay
-in the driver fixes some lock up problems with some slow PCs.
-Many thanks to Clemmitt Sigler for his (much needed) web page about the
-SB1000 driver for Linux.
-An interesting URL for Linux + MediaOne (although with an external modem)
-is: http://rlz.ne.mediaone.net/linux/home.shtml
-Today, Sep 22 1998, I finished writing a short utility (ftptest.c) to
-'measure' your cablemodem speed connection in Linux, downloading to
-memory the same file several times. To compile it you need ftplib, version
-3 or above. To run it, type:
-  ftptest URL n
-where URL is in the form: ftp://ftp.site.local.isp/pub/testfile
-and n is the number of simultaneous FTP connections that you want to run
-
----------------------------------------------------------------------------
-IMPORTANT NOTICE: if in your file /etc/isapnp.conf, you found something
-like '(READPORT 0x020b)' or '(READPORT 0x<another number>)', (anything
-different from the default 0x0203), you have to change the READ_DATA_PORT
-parameter in the sb1000.c file as follows.
-  - edit the sb1000.c file, look for:
-       READ_DATA_PORT
-    (it should be around line 127 in version 1.1.2)
-  - change the value of READ_DATA_PORT to the same value found in
-    /etc/isapnp.conf; for instance, if you found a: '(READPORT 0x020b)',
-    then in sb1000.c you should have:
-          static const int READ_DATA_PORT = 0x20b; 
-  - save sb1000.c
-  - compile and install again the driver
-    (i.e. do a 'make' and a 'make install').
+sb1000 is a module network device driver for the General Instrument (also known
+as NextLevel) SURFboard1000 internal cable modem board.  This is an ISA card
+which is used by a number of cable TV companies to provide cable modem access.
+It's a one-way downstream-only cable modem, meaning that your upstream net link
+is provided by your regular phone modem.
+
+This driver was written by Franco Venturi <fventuri@mediaone.net>.  He deserves
+a great deal of thanks for this wonderful piece of code!
+
+-----------------------------------------------------------------------------
+
+Support for this device is now a part of the standard Linux kernel.  The
+driver source code file is drivers/net/sb1000.c.  In addition to this
+you will need:
+
+1.) The "cmconfig" program.  This is a utility which supplements "ifconfig"
+to configure the cable modem and network interface (usually called "cm0");
+and
+
+2.) Several PPP scripts which live in /etc/ppp to make connecting via your
+cable modem easy.
+
+   These utilities can be obtained from:
+
+      http://www.jacksonville.net/~fventuri/
+
+   in Franco's original source code distribution .tar.gz file.  Support for
+   the sb1000 driver can be found at:
+
+      http://home.adelphia.net/~siglercm/sb1000.html
+      http://linuxpower.cx/~cable/
+
+   along with these utilties.
+
+3.) The standard isapnp tools.  These are necessary to configure your SB1000
+card at boot time (or afterwards by hand) since it's a PnP card.
+
+   If you don't have these installed as a standard part of your Linux
+   distribution, you can find them at:
+
+      http://www.roestock.demon.co.uk/isapnptools/
+
+   or check your Linux distribution binary CD or their web site.  For help with
+   isapnp, pnpdump, or /etc/isapnp.conf, go to:
+
+      http://www.roestock.demon.co.uk/isapnptools/isapnpfaq.html
+
+-----------------------------------------------------------------------------
+
+To make the SB1000 card work, follow these steps:
+
+1.) Run `make config', or `make menuconfig', or `make xconfig', whichever
+you prefer, in the top kernel tree directory to set up your kernel
+configuration.  Make sure to say "Y" to "Prompt for development drivers"
+and to say "M" to the sb1000 driver.  Also say "Y" or "M" to all the standard
+networking questions to get TCP/IP and PPP networking support.
+
+2.) *BEFORE* you build the kernel, edit drivers/net/sb1000.c.  Make sure
+to redefine the value of READ_DATA_PORT to match the I/O address used
+by isapnp to access your PnP cards.  This is the value of READPORT in
+/etc/isapnp.conf or given by the output of pnpdump.
+
+3.) Build and install the kernel and modules as usual.
+
+4.) Boot your new kernel following the usual procedures.
+
+5.) Set up to configure the new SB1000 PnP card by capturing the output
+of "pnpdump" to a file and editing this file to set the correct I/O ports,
+IRQ, and DMA settings for all your PnP cards.  Make sure none of the settings
+conflict with one another.  Then test this configuration by running the
+"isapnp" command with your new config file as the input.  Check for
+errors and fix as necessary.  (As an aside, I use I/O ports 0x110 and
+0x310 and IRQ 11 for my SB1000 card and these work well for me.  YMMV.)
+Then save the finished config file as /etc/isapnp.conf for proper configuration
+on subsequent reboots.
+
+6.) Download the original file sb1000-1.1.2.tar.gz from Franco's site or one of
+the others referenced above.  As root, unpack it into a temporary directory and
+do a `make cmconfig' and then `install -c cmconfig /usr/local/sbin'.  Don't do
+`make install' because it expects to find all the utilities built and ready for
+installation, not just cmconfig.
+
+7.) As root, copy all the files under the ppp/ subdirectory in Franco's
+tar file into /etc/ppp, being careful not to overwrite any files that are
+already in there.  Then modify ppp@gi-on to set the correct login name,
+phone number, and frequency for the cable modem.  Also edit pap-secrets
+to specify your login name and password and any site-specific information
+you need.
+
+8.) Be sure to modify /etc/ppp/firewall to use ipchains instead of
+the older ipfwadm commands from the 2.0.x kernels.  There's a neat utility to
+convert ipfwadm commands to ipchains commands:
+
+   http://users.dhp.com/~whisper/ipfwadm2ipchains/
+
+You may also wish to modify the firewall script to implement a different
+firewalling scheme.
+
+9.) Start the PPP connection via the script /etc/ppp/ppp@gi-on.  You must be
+root to do this.  It's better to use a utility like sudo to execute
+frequently used commands like this with root permissions if possible.  If you
+connect successfully the cable modem interface will come up and you'll see a
+driver message like this at the console:
+
+         cm0: sb1000 at (0x110,0x310), csn 1, S/N 0x2a0d16d8, IRQ 11.
+         sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)
+
+The "ifconfig" command should show two new interfaces, ppp0 and cm0.
+The command "cmconfig cm0" will give you information about the cable modem
+interface.
+
+10.) Try pinging a site via `ping -c 5 www.yahoo.com', for example.  You should
+see packets received.
+
+11.) If you can't get site names (like www.yahoo.com) to resolve into
+IP addresses (like 204.71.200.67), be sure your /etc/resolv.conf file
+has no syntax errors and has the right nameserver IP addresses in it.
+If this doesn't help, try something like `ping -c 5 204.71.200.67' to
+see if the networking is running but the DNS resolution is where the
+problem lies.
+
+12.) If you still have problems, go to the support web sites mentioned above
+and read the information and documentation there.
+
+-----------------------------------------------------------------------------
+
+Common problems:
+
+1.) Packets go out on the ppp0 interface but don't come back on the cm0
+interface.  It looks like I'm connected but I can't even ping any
+numerical IP addresses.  (This happens predominantly on Debian systems due
+to a default boot-time configuration script.)
+
+Solution -- As root `echo 0 > /proc/sys/net/ipv4/conf/cm0/rp_filter' so it
+can share the same IP address as the ppp0 interface.  Note that this
+command should probably be added to the /etc/ppp/cablemodem script
+*right*between* the "/sbin/ifconfig" and "/sbin/cmconfig" commands.
+You may need to do this to /proc/sys/net/ipv4/conf/ppp0/rp_filter as well.
+If you do this to /proc/sys/net/ipv4/conf/default/rp_filter on each reboot
+(in rc.local or some such) then any interfaces can share the same IP
+addresses.
+
+2.) I get "unresolved symbol" error messages on executing `insmod sb1000.o'.
+
+Solution -- You probably have a non-matching kernel source tree and
+/usr/include/linux and /usr/include/asm header files.  Make sure you
+install the correct versions of the header files in these two directories.
+Then rebuild and reinstall the kernel.
+
+3.) When isapnp runs it reports an error, and my SB1000 card isn't working.
+
+Solution -- There's a problem with later versions of isapnp using the "(CHECK)"
+option in the lines that allocate the two I/O addresses for the SB1000 card.
+This first popped up on RH 6.0.  Delete "(CHECK)" for the SB1000 I/O addresses.
+Make sure they don't conflict with any other pieces of hardware first!  Then
+rerun isapnp and go from there.
+
+4.) I can't execute the /etc/ppp/ppp@gi-on file.
+
+Solution -- As root do `chmod ug+x /etc/ppp/ppp@gi-on'.
+
+5.) The firewall script isn't working (with 2.2.x and higher kernels).
+
+Solution -- Use the ipfwadm2ipchains script referenced above to convert the
+/etc/ppp/firewall script from the deprecated ipfwadm commands to ipchains.
+
+6.) I'm getting *tons* of firewall deny messages in the /var/kern.log,
+/var/messages, and/or /var/syslog files, and they're filling up my /var
+partition!!!
+
+Solution -- First, tell your ISP that you're receiving DoS (Denial of Service)
+and/or portscanning (UDP connection attempts) attacks!  Look over the deny
+messages to figure out what the attack is and where it's coming from.  Next,
+edit /etc/ppp/cablemodem and make sure the ",nobroadcast" option is turned on
+to the "cmconfig" command (uncomment that line).  If you're not receiving these
+denied packets on your broadcast interface (IP address xxx.yyy.zzz.255
+typically), then someone is attacking your machine in particular.  Be careful
+out there....
+
+7.) Everything seems to work fine but my computer locks up after a while
+(and typically during a lengthy download through the cable modem)!
+
+Solution -- You may need to add a short delay in the driver to 'slow down' the
+SURFboard because your PC might not be able to keep up with the transfer rate
+of the SB1000. To do this, it's probably best to download Franco's
+sb1000-1.1.2.tar.gz archive and build and install sb1000.o manually.  You'll
+want to edit the 'Makefile' and look for the 'SB1000_DELAY'
+define.  Uncomment those 'CFLAGS' lines (and comment out the default ones)
+and try setting the delay to something like 60 microseconds with:
+'-DSB1000_DELAY=60'.  Then do `make' and as root `make install' and try
+it out.  If it still doesn't work or you like playing with the driver, you may
+try other numbers.  Remember though that the higher the delay, the slower the
+driver (which slows down the rest of the PC too when it is actively
+used). Thanks to Ed Daiga for this tip!
+
+-----------------------------------------------------------------------------
+
+Credits:  This README came from Franco Venturi's original README file which is
+still supplied with his driver .tar.gz archive.  I and all other sb1000 users
+owe Franco a tremendous "Thank you!"  Additional thanks goes to Carl Patten
+and Ralph Bonnell who are now managing the Linux SB1000 web site, and to
+the SB1000 users who reported and helped debug the common problems listed
+above.
+
+
+					Clemmitt Sigler
+					csigler@vt.edu
diff -u --recursive --new-file v2.3.18/linux/Documentation/oops-tracing.txt linux/Documentation/oops-tracing.txt
--- v2.3.18/linux/Documentation/oops-tracing.txt	Thu Jul  1 10:54:31 1999
+++ linux/Documentation/oops-tracing.txt	Sat Oct  2 07:32:45 1999
@@ -20,7 +20,7 @@
 Full Information
 ----------------
 
-From: Linus Torvalds <torvalds@cs.helsinki.fi>
+From: Linus Torvalds <torvalds@transmeta.com>
 
 How to track down an Oops.. [originally a mail to linux-kernel]
 
diff -u --recursive --new-file v2.3.18/linux/Documentation/proc.txt linux/Documentation/proc.txt
--- v2.3.18/linux/Documentation/proc.txt	Sat Feb  6 12:46:20 1999
+++ linux/Documentation/proc.txt	Wed Dec 31 16:00:00 1969
@@ -1,1353 +0,0 @@
-
------------------------------------------------------------------------
-
-                T H E  /proc   F I L E S Y S T E M
-
------------------------------------------------------------------------
-/proc/sys      Terrehon Bowden <terrehon@wpi.com>       January 27 1999
-               Bodo Bauer <bb@ricochet.net>                
------------------------------------------------------------------------
-Version 1.1                                         Kernel version 2.2
------------------------------------------------------------------------
-Contents
-
-1   Introduction/Credits
-
-1.1  Legal Issues
-
-2   The /proc file system
-
-2.1  Process specific subdirectories
-2.2  Kernel data
-2.3  IDE devices in /proc/ide
-2.4  Networking info in /proc/net
-2.5  SCSI info
-2.6  Parallel port info in /proc/parport
-2.7  TTY info in /proc/tty
-
-3   Reading and modifying kernel parameters
-
-3.1  /proc/sys/debug and /proc/sys/proc
-3.2  /proc/fs - File system data
-3.3  /proc/fs/binfmt_misc - Miscellaneous binary formats
-3.4  /proc/sys/kernel - General kernel parameters
-3.5  /proc/sys/vm - The virtual memory subsystem
-3.6  /proc/sys/dev - Device specific parameters
-3.7  /proc/sys/sunrpc - Remote procedure calls
-3.8  /proc/sys/net - Networking stuff
-3.9  /proc/sys/net/ipv4 - IPV4 settings=20
-3.10 Appletalk
-3.11 IPX
-
------------------------------------------------------------------------
-
-1   Introduction/Credits
-
-This documentation is part of a soon to be released book published by
-IDG Books on the SuSE Linux distribution. As there is no complete
-documentation for the /proc file system and we've used many freely
-available sources to write this chapter, it seems only fair to give
-the work back to the Linux community. This work is based on the
-2.1.132 and 2.2.0-pre-kernel versions. I'm afraid it's still far from
-complete, but we hope it will be useful. As far as we know, it is the
-first 'all-in-one' document about the /proc file system. It is
-focused on the Intel x86 hardware, so if you are looking for PPC, ARM,
-SPARC, APX, etc., features, you probably won't find what you are
-looking for. It also only covers IPv4 networking, not IPv6 nor other
-protocols - sorry.
-
-We'd like to thank Alan Cox, Rik van Riel, and Alexey Kuznetsov.  We'd
-also like to extend a special thank you to Andi Kleen for
-documentation, which we relied on heavily to create this document, as
-well as the additional information he provided. Thanks to everybody
-else who contributed source or docs to the Linux kernel and helped
-create a great piece of software... :)
-
-If you have any comments, corrections or additions, please don't
-hesitate to contact Bodo Bauer at bb@ricochet.net. We'll be happy to
-add them to this document.
-
-The latest version of this document is available online at
-http://www.suse.com/~bb/Docs/proc.html in HTML, ASCII, and as 
-Postscript file. 
-
-1.1  Legal Stuff
-
-We don't guarantee the correctness of this document, and if you come
-to us complaining about how you screwed up your system because of
-incorrect documentation, we won't feel responsible...
-
------------------------------------------------------------------------
-
-2   The /proc file system
-
-The proc file system acts as an interface to internal data structures
-in the kernel. It can be used to obtain information about the system
-and to change certain kernel parameters at runtime.  It contains
-(among other things) one subdirectory for each process running on the
-system which is named after the process id (PID) of the process. The
-link self points to the process reading the file system.
-
-2.1  Process specific subdirectories
-
-Each process subdirectory has the in table 1.1 listed entries.
-
-      _________________________________________________
-      cmdline Command line arguments
-      environ Values of environment variables
-      fd    Directory, which contains all file descriptors
-      mem   Memory held by this process
-      stat   Process status
-      status  Process status in human readable form
-      cwd   Link to the current working directory
-      exe   Link to the executable of this process
-      maps   Memory maps
-      root   Link to the root directory of this process
-      statm  Process memory status information
-     _________________________________________________
-      Table 1.1: Process specific entries in /proc
-
-For example, to get the status information of a process, all you have
-to do is read the file /proc/PID/status:
-
-> cat /proc/self/status
-Name:   cat
-State:  R (running)
-Pid:    5633
-PPid:   5609
-Uid:    501     501     501     501
-Gid:    100     100     100     100
-Groups: 100 16 
-VmSize:      804 kB
-VmLck:         0 kB
-VmRSS:       344 kB
-VmData:       68 kB
-VmStk:        20 kB
-VmExe:        12 kB
-VmLib:       660 kB
-SigPnd: 0000000000000000
-SigBlk: 0000000000000000
-SigIgn: 0000000000000000
-SigCgt: 0000000000000000
-CapInh: 00000000fffffeff
-CapPrm: 0000000000000000
-CapEff: 0000000000000000
-
-This shows you almost the same information as you would get if you
-viewed it with the ps command. In fact, ps uses the proc file system
-to obtain its information.
-
-The statm file contains more detailed information about the process
-memory usage. It contains seven values with the following meanings:
-
-size       total program size
-resident   size of in memory portions
-shared     number of the pages that are shared
-trs        number of pages that are 'code'
-drs        number of pages of data/stack
-lrs        number of pages of library
-dt	   number of dirty pages
-
-The ratio text/data/library is approximate only by heuristics.
-
-2.2  Kernel data
-
-Similar to the process entries, these are files which give information
-about the running kernel. The files used to obtain this information
-are contained in /proc and are listed in table 1.2. Not all of these
-will be present in your system. It depends on the kernel configuration
-and the loaded modules, which files are there, and which are missing.
-
-      ________________________________________________
-      apm           Advanced power management info
-      cmdline       Kernel command line
-      cpuinfo       Info about the CPU
-      devices       Available devices (block and character)
-      dma           Used DMS channels
-      filesystems   Supported filesystems
-      interrupts    Interrupt usage
-      ioports       I/O port usage
-      kcore         Kernel core image
-      kmsg          Kernel messages
-      ksyms         Kernel symbol table
-      loadavg       Load average
-      locks         Kernel locks
-      meminfo       Memory info
-      misc          Miscellaneous
-      modules       List of loaded modules
-      mounts        Mounted filesystems
-      partitions    Table of partitions known to the system
-      rtc           Real time clock
-      slabinfo      Slab pool info
-      stat          Overall statistics
-      swaps         Swap space utilization
-      uptime        System uptime
-      version       Kernel version
-      ________________________________________________
-           Table 1.2: Kernel info in /proc
-
-You can, for example, check which interrupts are currently in use and
-what they are used for by looking in the file /proc/interrupts:
-
-> cat /proc/interrupts
-           CPU0       
-  0:    8728810          XT-PIC  timer
-  1:        895          XT-PIC  keyboard
-  2:          0          XT-PIC  cascade
-  3:     531695          XT-PIC  aha152x
-  4:    2014133          XT-PIC  serial
-  5:      44401          XT-PIC  pcnet_cs
-  8:          2          XT-PIC  rtc
- 11:          8          XT-PIC  i82365
- 12:     182918          XT-PIC  PS/2 Mouse
- 13:          1          XT-PIC  fpu
- 14:    1232265          XT-PIC  ide0
- 15:          7          XT-PIC  ide1
-NMI:          0
-
-There three more important subdirectories in /proc: net, scsi and
-sys. The general rule is that the contents, or even the existence of
-these directories, depends on your kernel configuration. If SCSI is
-not enabled, the directory scsi may not exist. The same is true with
-the net, which is only there when networking support is present in the
-running kernel.
-
-The slabinfo file gives information about memory usage on the slab
-level.  Linux uses slab pools for memory management above page level
-in version 2.2. Commonly used objects have their own slab pool (like
-network buffers, directory cache, etc.).
-
-2.3  IDE devices in /proc/ide
-
-This subdirectory contains information about all IDE devices that the
-kernel is aware of.  There is one subdirectory for each device
-(i.e. hard disk) containing the following files:
-
-       cache             The cache
-       capacity          Capacity of the medium
-       driver            Driver and version
-       geometry          Physical and logical geometry
-       identify          Device identify block
-       media             Media type
-       model             Device identifier
-       settings          Device setup
-       smart_thresholds  IDE disk management thresholds
-       smart_values      IDE disk management values
-
-2.4  Networking info in /proc/net
-
-This directory follows the usual pattern. Table 1.3 lists the files
-and their meaning.
-
-     ____________________________________________________
-     arp             Kernel ARP table
-     dev             network devices with statistics
-     dev_mcast       Lists the Layer2 multicast groups a
-                     device is listening to (interface index,
-                     label, number of references, number of
-                     bound addresses).
-     dev_stat        network device status
-     ip_fwchains     Firewall chain linkage
-     ip_fwnames      Firewall chains
-     ip_masq         Directory containing the masquerading
-                     tables.
-     ip_masquerade   Major masquerading table
-     netstat         Network statistics
-     raw             Raw device statistics
-     route           Kernel routing table
-     rpc             Directory containing rpc info
-     rt_cache        Routing cache
-     snmp            SNMP data
-     sockstat        Socket statistics
-     tcp             TCP sockets
-     tr_rif          Token ring RIF routing table
-     udp             UDP sockets
-     unix            UNIX domain sockets
-     wireless        Wireless interface data (Wavelan etc)
-     igmp            IP multicast addresses, which this host joined
-     psched          Global packet scheduler parameters.
-     netlink         List of PF_NETLINK sockets.
-     ip_mr_vifs      List of multicast virtual interfaces.
-     ip_mr_cache     List of multicast routing cache.
-     udp6            UDP sockets (IPv6)
-     tcp6            TCP sockets (IPv6)
-     raw6            Raw device statistics (IPv6)
-     igmp6	     IP multicast addresses, which this host joineed (IPv6)
-     if_inet6        List of IPv6 interface addresses.
-     ipv6_route      Kernel routing table for IPv6
-     rt6_stats       global IPv6 routing tables statistics.
-     sockstat6       Socket statistics (IPv6)
-     snmp6           Snmp data (IPv6)
-     ____________________________________________________
-         Table 1.3: Network info in /proc/net
-
-You can use this information to see which network devices are
-available in your system and how much traffic was routed over those
-devices:
-
-> cat /proc/net/dev
-Inter-|Receive                                                   |[...
- face |bytes    packets errs drop fifo frame compressed multicast|[...
-    lo:  908188   5596     0    0    0     0          0         0 [...        
-  ppp0:15475140  20721   410    0    0   410          0         0 [... 
-  eth0:  614530   7085     0    0    0     0          0         1 [...
-
-...] Transmit
-...] bytes    packets errs drop fifo colls carrier compressed
-...]  908188     5596    0    0    0     0       0          0
-...] 1375103    17405    0    0    0     0       0          0
-...] 1703981     5535    0    0    0     3       0          0
-
-2.5  SCSI info
-
-If you have a SCSI host adapter in your system, you'll find a
-subdirectory named after the driver for this adapter in /proc/scsi. 
-You'll also see a list of all recognized SCSI devices in /proc/scsi:
-
->cat /proc/scsi/scsi
-Attached devices: 
-Host: scsi0 Channel: 00 Id: 00 Lun: 00
-  Vendor: QUANTUM  Model: XP34550W         Rev: LXY4
-  Type:   Direct-Access                    ANSI SCSI revision: 02
-Host: scsi0 Channel: 00 Id: 01 Lun: 00
-  Vendor: SEAGATE  Model: ST34501W         Rev: 0018
-  Type:   Direct-Access                    ANSI SCSI revision: 02
-Host: scsi0 Channel: 00 Id: 02 Lun: 00
-  Vendor: SEAGATE  Model: ST34501W         Rev: 0017
-  Type:   Direct-Access                    ANSI SCSI revision: 02
-Host: scsi0 Channel: 00 Id: 04 Lun: 00
-  Vendor: ARCHIVE  Model: Python 04106-XXX Rev: 703b
-  Type:   Sequential-Access                ANSI SCSI revision: 02
-
-The directory named after the driver has one file for each adapter
-found in the system. These files contain information about
-the controller, including the used IRQ and the IO address range:
-
->cat /proc/scsi/ncr53c8xx/0
-General information:
- Chip NCR53C875, device id 0xf, revision id 0x4
- IO port address 0xec00, IRQ number 11
- Synchronous period factor 12, max commands per lun 4
-
-2.6  Parallel port info in /proc/parport
-
-The directory /proc/parport contains information about the parallel
-ports of your system. It has one subdirectory for each port, named
-after the port number (0,1,2,...).
-
-This directory contains four files:
-
-    autoprobe   Autoprobe results of this port
-    devices     Connected device modules
-    hardware    Hardware info (port type, io-port, DMA, IRQ, etc.)
-    irq         Used interrupt, if any
-
-2.7  TTY info in /proc/tty
-
-Information about the available and the actually used tty's can be
-found in /proc/tty. You'll find entries for drivers and line
-disciplines in this directory, as shown in the table below:
-
-     drivers       List of drivers and their usage
-     ldiscs        Registered line disciplines
-     driver/serial Usage statistic and status of single tty lines
-
-To see which tty's are currently in use, you can simply look into the
-file /proc/tty/drivers:
-
->cat /proc/tty/drivers
-pty_slave            /dev/pts      136   0-255 pty:slave
-pty_master           /dev/ptm      128   0-255 pty:master
-pty_slave            /dev/ttyp       3   0-255 pty:slave
-pty_master           /dev/pty        2   0-255 pty:master
-serial               /dev/cua        5   64-67 serial:callout
-serial               /dev/ttyS       4   64-67 serial
-/dev/tty0            /dev/tty0       4       0 system:vtmaster
-/dev/ptmx            /dev/ptmx       5       2 system
-/dev/console         /dev/console    5       1 system:console
-/dev/tty             /dev/tty        5       0 system:/dev/tty
-unknown              /dev/tty        4    1-63 console
-
------------------------------------------------------------------------
-
-3   Reading and modifying kernel parameters
-
-A very interesting part of /proc is the directory /proc/sys. This not
-only provides information, it also allows you to change parameters
-within the kernel. Be very careful when trying this. You can optimize
-your system, but you also can crash it. Never play around with kernel
-parameters on a production system. Set up a development machine and
-test to make sure that everything works the way you want it to.  You
-may have no alternative but to reboot the machine once an error has
-been made.
-
-To change a value, simply echo the new value into the file. An example
-is given below in the section on the file system data. You need to be
-root to do this. You can create your own boot script to get this done
-every time your system boots. 
-
-The files in /proc/sys can be used to tune and monitor miscellaneous
-and general things in the operation of the Linux kernel.  Since some
-of the files can inadvertently disrupt your system, it is advisable to
-read both documentation and source before actually making
-adjustments. In any case, be very careful when writing to any of these
-files. The entries in /proc may change slightly between the 2.1.* and
-the 2.2 kernel, so review the kernel documentation if there is any
-doubt. You'll find the documentation in the directory
-/usr/src/linux/Documentation/sys. This chapter is heavily based on the
-documentation included in the pre 2.2 kernels. Thanks to Rick van Riel
-for providing this information.
-
-3.1  /proc/sys/debug and /proc/sys/proc
-
-These two subdirectories are empty.
-
-3.2  /proc/fs - File system data
-
-This subdirectory contains specific file system, file handle, inode,
-dentry and quota information.
-
-Currently, these files are in /proc/sys/fs:
-
-dentry-state
-   Status of the directory cache. Since directory entries are
-   dynamically allocated and deallocated, this file gives information
-   about the current status. It holds six values, in which the last
-   two are not used and are always zero. The other four mean:
-
-       nr_dentry   Seems to be zero all the time
-       nr_unused   Number of unused cache entries
-       age_limit   Age in seconds after the entry may be
-                   reclaimed, when memory is short
-       want_pages  internal
-
-dquot-nr and dquot-max
-   The file dquot-max shows the maximum number of cached disk quota
-   entries.
-
-   The file dquot-nr shows the number of allocated disk quota
-   entries and the number of free disk quota entries.
-
-   If the number of free cached disk quotas is very low and you have
-   a large number of simultaneous system users, you might want
-   to raise the limit.
-
-file-nr and file-max
-   The kernel allocates file handles dynamically, but as yet
-   doesn't free them again.
-
-   The value in file-max denotes the maximum number of file handles
-   that the Linux kernel will allocate. When you get a lot of error
-   messages about running out of file handles, you might want to raise
-   this limit. The default value is 4096. To change it, just write the
-   new number into the file:
-
-   # cat /proc/sys/fs/file-max
-   4096
-   # echo 8192 > /proc/sys/fs/file-max
-   # cat /proc/sys/fs/file-max
-   8192
-
-   This method of revision is useful for all customizable parameters
-   of the kernel - simply echo the new value to the corresponding
-   file.
-  
-   The three values in file-nr denote the number of allocated file
-   handles, the number of used file handles, and the maximum number of
-   file handles. When the allocated file handles come close to the
-   maximum, but the number of actually used ones is far behind, you've
-   encountered a peak in your usage of file handles and you don't need
-   to increase the maximum.
-
-   However, there is still a per process limit of open files, which
-   unfortunatly can't be changed that easily. It is set to 1024 by
-   default. To change this you have to edit the files limits.h and
-   fs.h in the directory /usr/src/linux/include/linux. Change the
-   definition of NR_OPEN and recompile the kernel.
-
-inode-state, inode-nr and inode-max
-   As with file handles, the kernel allocates the inode structures
-   dynamically, but can't free them yet.
-
-   The value in inode-max denotes the maximum number of inode
-   handlers. This value should be 3 to 4 times larger than the value
-   in file-max, since stdin, stdout, and network sockets also need an
-   inode struct to handle them. If you regularly run out of inodes,
-   you should increase this value.
-
-   The file inode-nr contains the first two items from inode-state, so
-   we'll skip to that file...
-
-   inode-state contains three actual numbers and four dummy values. The 
-   actual numbers are (in order of appearance) nr_inodes, nr_free_inodes,
-   and preshrink.
-
-   nr_inodes 
-     Denotes the number of inodes the system has allocated. This can
-     be slightly more than inode-max because Linux allocates them one
-     pageful at a time.
-
-   nr_free_inodes 
-     Represents the number of free inodes and pre shrink is nonzero
-     when the nr_inodes > inode-max and the system needs to prune the
-     inode list instead of allocating more.
-
-super-nr and super-max
-   Again, super block structures are allocated by the kernel,
-   but not freed. The file super-max contains the maximum number of
-   super block handlers, where super-nr shows the number of
-   currently allocated ones.
-
-   Every mounted file system needs a super block, so if you plan to
-   mount lots of file systems, you may want to increase these
-   numbers.
-
-3.3  /proc/fs/binfmt_misc - Miscellaneous binary formats
-
-Besides these files, there is the subdirectory
-/proc/sys/fs/binfmt_misc. This handles the kernel support for
-miscellaneous binary formats.
-
-Binfmt_misc provides the ability to register additional binary formats
-to the Kernel without compiling an additional module/kernel. Therefore
-binfmt_misc needs to know magic numbers at the beginning or the
-filename extension of the binary.
-
-It works by maintaining a linked list of structs, that contain a
-description of a binary format, including a magic with size (or the
-filename extension), offset and mask, and the interpreter name. On
-request it invokes the given interpreter with the original program as
-argument, as binfmt_java and binfmt_em86 and binfmt_mz do.
-Since binfmt_misc does not define any default binary-formats, you have to
-register an additional binary-format.
-
-There are two general files in binfmt_misc and one file per registered
-format. The two general files are register and status.
-
-Registering a new binary format
-
-echo :name:type:offset:magic:mask:interpreter: > /proc/sys/fs/binfmt_misc/register 
-
-with appropriate name (the name for the /proc-dir entry), offset
-(defaults to 0, if omitted), magic and mask (which can be omitted,
-defaults to all 0xff) and last but not least, the interpreter that is
-to be invoked (for example and testing '/bin/echo'). Type can be M for
-usual magic matching or E for filename extension matching (give
-extension in place of magic).
-
-To check or reset the status of the binary format handler:
-
-If you do a cat on the file /proc/sys/fs/binfmt_misc/status, you will
-get the current status (enabled/disabled) of binfmt_misc. Change the
-status by echoing 0 (disables) or 1 (enables) or -1 (caution: this
-clears all previously registered binary formats) to status. For
-example echo 0 > status to disable binfmt_misc (temporarily).
-
-Status of a single handler
-
-Each registered handler has an entry in /proc/sys/fs/binfmt_misc.
-These files perform the same function as status, but their scope is
-limited to the actual binary format. By cating this file, you also
-receive all related information about the interpreter/magic of the
-binfmt.
-
-Example usage of binfmt_misc (emulate binfmt_java)
-
-cd /proc/sys/fs/binfmt_misc
-echo ':Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/javawrapper:' > register
-echo ':HTML:E::html::/usr/local/java/bin/appletviewer:' > register
-echo ':Applet:M::<!--applet::/usr/local/java/bin/appletviewer:' > register
-echo ':DEXE:M::\x0eDEX::/usr/bin/dosexec:' > register
-
-These three lines add support for Java executables and Java applets
-(like binfmt_java, additionally recognizing the .html extension with
-no need to put <!--applet> to every applet file). You have to install
-the JDK and the shell-script /usr/local/java/bin/javawrapper too. It
-works around the brokenness of the Java filename handling. To add a
-Java binary, just create a link to the class-file somewhere in the
-path.
-
-3.4  /proc/sys/kernel - general kernel parameters
-
-This directory reflects general kernel behaviors. As I've said before,
-the contents are depend on your configuration. I'll list the most
-important files, along with descriptions of what they mean and how to
-use them.
-
-acct
-   The file contains three values; highwater, lowwater, and
-   frequency.
-
-   It exists only when BSD-style process accounting is enabled. These
-   values control its behavior. If the free space on the file system
-   where the log lives goes below lowwater%, accounting suspends. If
-   it goes above highwater%, accounting resumes. Frequency determines
-   how often you check the amount of free space (value is in
-   seconds). Default settings are: 4, 2, and 30. That is, suspend
-   accounting if there left <= 2% free; resume it if we have a value
-   >=3%; consider information about the amount of free space valid
-   for 30 seconds
-
-ctrl-alt-del
-   When the value in this file is 0, ctrl-alt-del is trapped and sent
-   to the init(1) program to handle a graceful restart. However, when
-   the value is > 0, Linux's reaction to this key combination will be
-   an immediate reboot, without syncing its dirty buffers.
-
-   Note: when a program (like dosemu) has the keyboard in raw mode,
-   the ctrl-alt-del is intercepted by the program before it ever
-   reaches the kernel tty layer, and it is up to the program to decide
-   what to do with it.
-
-domainname and hostname
-   These files can be controlled to set the NIS domainname and
-   hostname of your box. For the classic darkstar.frop.org a simple:
-
-   # echo "darkstar" > /proc/sys/kernel/hostname
-   # echo "frop.org" > /proc/sys/kernel/domainname
-
-   would suffice to set your hostname and NIS domainname.
-
-osrelease, ostype and version
-
-   The names make it pretty obvious what these fields contain:
-
-   >cat /proc/sys/kernel/osrelease
-   2.1.131
-   >cat /proc/sys/kernel/ostype
-   Linux
-   >cat /proc/sys/kernel/version
-   #8 Mon Jan 25 19:45:02 PST 1999
- 
-   The files osrelease and ostype should be clear enough. Version
-   needs a little more clarification however. The #8 means that this
-   is the 8th kernel built from this source base and the date behind
-   it indicates the time the kernel was built. The only way to tune
-   these values is to rebuild the kernel.
-
-panic 
-   The value in this file represents the number of seconds the kernel
-   waits before rebooting on a panic. When you use the software
-   watchdog, the recommended setting is 60. If set to 0, the auto
-   reboot after a kernel panic is disabled, this is the default
-   setting.
-
-printk
-   The four values in printk denote console_loglevel,
-   default_message_loglevel, minimum_console_level, and
-   default_console_loglevel respectively.
-
-   These values influence printk() behavior when printing or logging
-   error messages, which come from inside the kernel. See syslog(2)
-   for more information on the different log levels.
-
-   console_loglevel
-     Messages with a higher priority than this will be printed to
-     the console.
-
-   default_message_level
-     Messages without an explicit priority will be printed with
-     this priority.
-
-   minimum_console_loglevel
-     Minimum (highest) value to which the console_loglevel can be set.
-
-   default_console_loglevel
-     Default value for console_loglevel.
-
-sg-big-buff
-   This file shows the size of the generic SCSI (sg) buffer. At this
-   point, you can't tune it yet, but you can change it at compile time
-   by editing include/scsi/sg.h and changing the value of
-   SG_BIG_BUFF.
-
-   If you use a scanner with SANE (Scanner Access now easy) you
-   might want to set this to a higher value. Look into the SANE
-   documentation on this issue.
-
-modprobe
-   The location where the modprobe binary is located. The kernel
-   uses this program to load modules on demand.
-
-3.5  /proc/sys/vm - The virtual memory subsystem
-
-The files in this directory can be used to tune the operation of the
-virtual memory (VM) subsystem of the Linux kernel. In addition, one of
-the files (bdflush) has a little influence on disk usage.
-
-bdflush
-   This file controls the operation of the bdflush kernel daemon. It
-   currently contains 9 integer values, 6 of which are actually used
-   by the kernel:
-
-    nfract      Percentage of buffer cache dirty to
-                activate bdflush
-    ndirty      Maximum number of dirty blocks to
-                write out per-wake-cycle
-    nrefill     Number of clean buffers to try to obtain
-                each time we call refill
-    nref_dirt   Dirty buffer threshold for activating bdflush
-                when trying to refill buffers.
-    dummy       unused
-    age_buffer  Time for normal buffer to age before you flush it
-    age_super   Time for superblock to age before you flush it
-    dummy       unused
-    dummy       unused
-
-   nfract
-     This parameter governs the maximum number of dirty buffers
-     in the buffer cache. Dirty means that the contents of the
-     buffer still have to be written to disk (as opposed to a
-     clean buffer, which can just be forgotten about). Setting
-     this to a high value means that Linux can delay disk writes
-     for a long time, but it also means that it will have to do a
-     lot of I/O at once when memory becomes short. A low value
-     will spread out disk I/O more evenly.
-
-   ndirty
-     Ndirty gives the maximum number of dirty buffers that
-     bdflush can write to the disk at one time. A high value will
-     mean delayed, bursty I/O, while a small value can lead to
-     memory shortage when bdflush isn't woken up often enough.
-
-   nrefill
-     This the number of buffers that bdflush will add to the list
-     of free buffers when refill_freelist() is called. It is
-     necessary to allocate free buffers beforehand, since the
-     buffers are often different sizes than the memory pages
-     and some bookkeeping needs to be done beforehand. The
-     higher the number, the more memory will be wasted and the
-     less often refill_freelist() will need to run.
-
-   nref_dirt
-     When refill_freelist() comes across more than nref_dirt
-     dirty buffers, it will wake up bdflush.
-
-   age_buffer and age_super
-     Finally, the age_buffer and age_super parameters govern the
-     maximum time Linux waits before writing out a dirty buffer
-     to disk. The value is expressed in jiffies (clockticks), the
-     number of jiffies per second is 100. Age_buffer is the
-     maximum age for data blocks, while age_super is for
-     filesystems meta data.
-
-buffermem
-   The three values in this file control how much memory should be
-   used for buffer memory. The percentage is calculated as a
-   percentage of total system memory.
-
-   The values are:
-
-   min_percent
-     This is the minimum percentage of memory that should be
-     spent on buffer memory.
-
-   borrow_percent
-     When Linux is short on memory, and the buffer cache uses more
-     than it has been allotted, the memory mangement (MM) subsystem
-     will prune the buffer cache more heavily than other memory to
-     compensate.
-
-   max_percent
-     This is the maximum amount of memory that can be used for
-     buffer memory.
-
-freepages
-   This file contains three values: min, low and high:
-
-   min
-     When the number of free pages in the system reaches this number,
-     only the kernel can allocate more memory.
-
-   low
-     If the number of free pages gets below this point, the kernel
-     starts swapping aggressively.
-
-   high
-     The kernel tries to keep up to this amount of memory free; if
-     memory comes below this point, the kernel gently starts swapping
-     in the hopes that it never has to do really aggressive swapping.
-
-kswapd
-   Kswapd is the kernel swap out daemon. That is, kswapd is that piece
-   of the kernel that frees memory when it gets fragmented or
-   full. Since every system is different, you'll probably want some
-   control over this piece of the system.
-
-   The file contains three numbers:
-
-   tries_base
-     The maximum number of pages kswapd tries to free in one round is
-     calculated from this number. Usually this number will be divided
-     by 4 or 8 (see mm/vmscan.c), so it isn't as big as it looks.
-
-     When you need to increase the bandwidth to/from swap, you'll want
-     to increase this number.
-
-   tries_min
-     This is the minimum number of times kswapd tries to free a page
-     each time it is called. Basically it's just there to make sure
-     that kswapd frees some pages even when it's being called with
-     minimum priority.
-
-
-  swap_cluster
-     This is probably the greatest influence on system
-     performance. swap_cluster is the number of pages kswapd writes in
-     one turn. You'll want this value to be large so that kswapd does
-     its I/O in large chunks and the disk doesn't have to seek as
-     often., but you don't want it to be too large since that would
-     flood the request queue.
-
-overcommit_memory
-   This file contains one value. The following algorithm is used to
-   decide if there's enough memory: if the value of overcommit_memory
-   is positive, then there's always enough memory. This is a useful
-   feature, since programs often malloc() huge amounts of memory 'just
-   in case', while they only use a small part of it. Leaving this
-   value at 0 will lead to the failure of such a huge malloc(), when
-   in fact the system has enough memory for the program to run.
-
-   On the other hand, enabling this feature can cause you to run out
-   of memory and thrash the system to death, so large and/or important
-   servers will want to set this value to 0.
-
-pagecache
-   This file does exactly the same as buffermem, only this file
-   controls the amount of memory allowed for memory mapping and
-   generic caching of files.
-
-   You don't want the minimum level to be too low, otherwise your
-   system might thrash when memory is tight or fragmentation is
-   high.
-
-pagetable_cache
-   The kernel keeps a number of page tables in a per-processor cache
-   (this helps a lot on SMP systems). The cache size for each
-   processor will be between the low and the high value.
-
-   On a low-memory, single CPU system, you can safely set these values
-   to 0 so you don't waste memory. It is used on SMP systems so that
-   the system can perform fast pagetable allocations without having to
-   aquire the kernel memory lock.
-
-   For large systems, the settings are probably fine. For normal
-   systems they won't hurt a bit. For small systems (<16MB ram) it
-   might be advantageous to set both values to 0.
-
-swapctl
-   This file contains no less than 8 variables. All of these values
-   are used by kswapd.
-
-   The first four variables sc_max_page_age, sc_page_advance,
-   sc_page_decline and sc_page_initial_age are used to keep track of
-   Linux's page aging. Page aging is a bookkeeping method to track
-   which pages of memory are often used, and which pages can be
-   swapped out without consequences.
-
-   When a page is swapped in, it starts at sc_page_initial_age
-   (default 3) and when the page is scanned by kswapd, its age is
-   adjusted according to the following scheme:
-
-    o If the page was used since the last time we scanned, its age
-      is increased by sc_page_advance (default 3) up to a
-      maximum of sc_max_page_age (default 20).
-
-    o Else (meaning it wasn't used) its age is decreased by
-      sc_page_decline (default 1).
-
-   When a page reaches age 0, it's ready to be swapped out.
-
-   The next four variables sc_age_cluster_fract, sc_age_cluster_min,
-   sc_pageout_weight and sc_bufferout_weight, can be used to control
-   kswapd's aggressiveness in swapping out pages.
-
-   Sc_age_cluster_fract is used to calculate how many pages from a
-   process are to be scanned by kswapd. The formula used is
-
-           sc_age_cluster_fract
-           -------------------- * resident set size
-              1024   =20
-
-   So if you want kswapd to scan the whole process,
-   sc_age_cluster_fract needs to have a value of 1024. The minimum
-   number of pages kswapd will scan is represented by
-   sc_age_cluster_min, this is done so kswapd will also scan small
-   processes.
-
-   The values of sc_pageout_weight and sc_bufferout_weight are used
-   to control how many tries kswapd will make in order to swap out
-   one page/buffer. These values can be used to fine-tune the ratio
-   between user pages and buffer/cache memory. When you find that
-   your Linux system is swapping out too many process pages in order
-   to satisfy buffer memory demands, you might want to either
-   increase sc_bufferout_weight, or decrease the value of
-   sc_pageout_weight.
-
-3.6  /proc/sys/dev - Device specific parameters
-
-Currently there is only support for CDROM drives, and for those, there
-is only one read only file containing information about the CD-ROM
-drives attached to the system:
-
->cat /proc/sys/dev/cdrom/info
-CD-ROM information
-
-drive name:           sr0  hdc
-drive speed:           0    6
-drive # of slots:      1    0
-Can close tray:        1    1
-Can open tray:         1    1
-Can lock tray:         1    1
-Can change speed:      1    1
-Can select disk:       0    1
-Can read multisession: 1    1
-Can read MCN:          1    1
-Reports media changed: 1    1
-Can play audio:        1    1
-
-You see two drives, sr0 and hdc, and their lists of features.
-
-3.7  /proc/sys/sunrpc - Remote procedure calls
-
-This directory contains four files, which enable or disable debugging
-for the RPC functions NFS, NFS-daemon, RPC and NLM. The default values
-are 0. They can be set to one, to turn debugging on.  (The default
-value is 0 for each)
-
-3.8  /proc/sys/net - Networking stuff
-
-The interface to the networking parts of the kernel is located in
-/proc/sys/net. The table below shows all possible subdirectories. You
-may see only some of them, depending on the configuration of your
-kernel:
-
-+-------------------------------------------------------------+
-| core     General parameter   |appletalk  Appletalk protocol |
-| unix     Unix domain sockets |netrom     NET/ROM            |
-| 802      E802 protocol       |ax25       AX25               |
-| ethernet Ethernet protocol   |rose       X.25 PLP layer     |
-| ipv4     IP version 4        |x25        X.25 protocol      |
-| ipx      IPX                 |token-ring IBM token ring     |
-| bridge   Bridging            |decnet     DEC net            |
-| ipv6     IP version 6        |                              |
-+-------------------------------------------------------------+
-
-We will concentrate on IP networking here. As AX15, X.25, and DEC Net
-are only minor players in the Linux world, we'll skip them in this
-chapter. You'll find some short info to Appletalk and IPX further down
-in section 3.10 and 3.11. Please look in the online documentation and
-the kernel source to get a detailed view of the parameters for those
-protocols. In this section we'll discuss the subdirectories printed in
-bold letters in the table above. As default values are suitable for
-most needs, there is no need to change these values.
-
-/proc/sys/net/core - Network core options
-
-rmem_default
-   The default setting of the socket receive buffer in bytes.
-
-rmem_max
-   The maximum receive socket buffer size in bytes.
-
-wmem_default
-   The default setting (in bytes) of the socket send buffer.
-
-wmem_max
-   The maximum send socket buffer size in bytes.
-
-message_burst and message_cost
-   These parameters are used to limit the warning messages written to
-   the kernel log from the networking code. They enforce a rate limit
-   to make a denial-of-service attack impossible. The higher the
-   message_cost factor is, the less messages will be
-   written. Message_burst controls when messages will be dropped. The
-   default settings limit warning messages to one every five seconds.
-
-netdev_max_backlog
-   Maximal number of packets, queued on INPUT side, when the interface
-   receives packets faster than kernel can process them.
-
-optmem_max
-   Maximum ancillary buffer size allowed per socket. Ancillary data is
-   a sequence of struct cmsghdr structures with appended data.
-
-/proc/sys/net/unix - Parameters for UNIX domain sockets
-
-There are only two files in this subdirectory. They control the delays
-for deleting and destroying socket descriptors.
-
-3.9  /proc/sys/net/ipv4 - IPV4 settings
-
-IP version 4 is still the most used protocol in Unix networking. It
-will be replaced by IP version 6 in the next couple of years, but for
-the moment it's the de facto standard for the internet and is used in
-most networking environments around the world. Because of the
-importance of this protocol, we'll have a deeper look into the subtree
-controlling the behavior of the IPv4 subsystem of the Linux kernel.
-
-Let's start with the entries in /proc/sys/net/ipv4 itself.
-
-ICMP settings
-
-icmp_echo_ignore_all and icmp_echo_ignore_broadcasts
-   Turn on (1) or off (0), if the kernel should ignore all ICMP ECHO
-   requests, or just those to broadcast and multicast addresses.
-
-   Please note that if you accept ICMP echo requests with a
-   broadcast/multicast destination address your network may be used
-   as an exploder for denial of service packet flooding attacks to
-   other hosts.
-
-icmp_destunreach_rate, icmp_echoreply_rate,
-icmp_paramprob_rate and icmp_timeexeed_rate
-   Sets limits for sending ICMP packets to specific targets. A value of
-   zero disables all limiting. Any positive value sets the maximum
-   package rate in hundredths of a second (on Intel systems).
-
-IP settings
-
-ip_autoconfig
-   This file contains one, if the host got its IP configuration by
-   RARP, BOOTP, DHCP or a similar mechanism. Otherwise it is zero.
-
-ip_default_ttl
-   TTL (Time To Live) for IPv4 interfaces. This is simply the
-   maximum number of hops a packet may travel.
-
-ip_dynaddr
-   Enable dynamic socket address rewriting on interface address change. This
-   is useful for dialup interface with changing IP addresses.
-
-ip_forward
-   Enable or disable forwarding of IP packages between interfaces. A
-   change of this value resets all other parameters to their default
-   values. They differ if the kernel is configured as host or router.
-
-ip_local_port_range
-   Range of ports used by TCP and UDP to choose the local
-   port. Contains two numbers, the first number is the lowest port,
-   the second number the highest local port. Default is 1024-4999.
-   Should be changed to 32768-61000 for high-usage systems.
-
-ip_no_pmtu_disc
-   Global switch to turn path MTU discovery off. It can also be set
-   on a per socket basis by the applications or on a per route
-   basis.
-
-ip_masq_debug
-   Enable/disable debugging of IP masquerading.
-
-
-IP fragmentation settings
-
-ipfrag_high_trash and ipfrag_low_trash
-   Maximum memory used to reassemble IP fragments. When
-   ipfrag_high_thresh bytes of memory is allocated for this purpose,
-   the fragment handler will toss packets until ipfrag_low_thresh is
-   reached.
-
-
-ipfrag_time
-   Time in seconds to keep an IP fragment in memory.
-
-TCP settings
-
-tcp_retrans_collapse
-   Bug-to-bug compatibility with some broken printers. On retransmit
-   try to send bigger packets to work around bugs in certain TCP
-   stacks. Can be turned off by setting it to zero.
-
-tcp_keepalive_probes
-   Number of keep alive probes TCP sends out, until it decides that the
-   connection is broken.
-
-tcp_keepalive_time
-   How often TCP sends out keep alive messages, when keep alive is
-   enabled. The default is 2 hours.
-
-tcp_syn_retries
-   Number of times initial SYNs for a TCP connection attempt will be
-   retransmitted. Should not be higher than 255. This is only the
-   timeout for outgoing connections, for incoming connections the
-   number of retransmits is defined by tcp_retries1.
-
-tcp_sack
-   Enable select acknowledgments after RFC2018.
-
-tcp_timestamps
-   Enable timestamps as defined in RFC1323.
-
-tcp_stdurg
-   Enable the strict RFC793 interpretation of the TCP urgent pointer
-   field. The default is to use the BSD compatible interpretation
-   of the urgent pointer pointing to the first byte after the urgent
-   data. The RFC793 interpretation is to have it point to the last
-   byte of urgent data. Enabling this option may lead to
-   interoperatibility problems. Disabled by default.
-
-tcp_syncookies
-   Only valid when the kernel was compiled with
-   CONFIG_SYNCOOKIES. Send out syncookies when the syn backlog queue
-   of a socket overflows. This is to prevent against the common 'syn
-   flood attack'. Disabled by default.
-
-   Note that the concept of a socket backlog is abandoned, this
-   means the peer may not receive reliable error messages from an
-   over loaded server with syncookies enabled.
-
-tcp_window_scaling
-   Enable window scaling as defined in RFC1323.
-
-tcp_fin_timeout
-   How many seconds to wait for a final FIN before the socket is
-   always closed. This is strictly a violation of the TCP
-   specification, but required to prevent denial-of-service attacks.
-
-tcp_max_ka_probes
-   How many keepalive probes are sent per slow timer run. Shouldn't be
-   set too high to prevent bursts.
-
-tcp_max_syn_backlog
-   Length of the per socket backlog queue. Since Linux 2.2 the backlog
-   specified in listen(2) only specifies the length of the backlog
-   queue of already established sockets. When more connection requests
-   arrive Linux starts to drop packets. When syncookies are enabled
-   the packets are still answered and the maximum queue is effectively
-   ignored.
-
-tcp_retries1
-   Defines how often an answer to a TCP connection request is
-   retransmitted before giving up.
-
-tcp_retries2
-   Defines how often a TCP packet is retransmitted before giving up.
-
-Interface specific settings
-
-In the directory /proc/sys/net/ipv4/conf you'll find one subdirectory
-for each interface the system knows about and one directory calls
-all. Changes in the all subdirectory affect all interfaces, where
-changes in the other subdirectories affect only one interface.
-
-All directories have the same entries:
-
-accept_redirects
-   This switch decides if the kernel accepts ICMP redirect messages
-   or not. The default is 'yes', if the kernel is configured for a
-   regular host; and 'no' for a router configuration.
-
-accept_source_route
-   Should source routed packages be accepted or declined. The
-   default is dependent on the kernel configuration. It's 'yes' for
-   routers and 'no' for hosts.
-
-bootp_relay
-   Accept packets with source address 0.b.c.d destined not to this
-   host as local ones. It is supposed that BOOTP relay daemon will
-   catch and forward such packets.
-
-   The default is 'no', as this feature is not implemented yet
-   (kernel version 2.2.0-pre?).
-
-forwarding
-   Enable or disable IP forwarding on this interface.
-
-log_martians
-   Log packets with source addresses with no known route to kernel log.
-
-mc_forwarding
-   Do multicast routing. The kernel needs to be compiled with
-   CONFIG_MROUTE and a multicast routing daemon is required.
-
-proxy_arp
-   Do (1) or don't (0) do proxy ARP.
-
-rp_filter
-   Integer value deciding if source validation should be made. 
-   1 means yes, 0 means no. Disabled by default, but 
-   local/broadcast address spoofing is always on.
-
-   If you set this to 1 on a router that is the only connection
-   for a network to the net , it evidently prevents spoofing attacks
-   against your internal networks (external addresses can still be
-   spoofed), without the need for additional firewall rules.
-
-secure_redirects
-   Accept ICMP redirect messages only for gateways, listed in
-   default gateway list. Enabled by default.
-
-shared_media
-   If it is not set the kernel does not assume that different subnets
-   on this device can communicate directly. Default setting is 'yes'.  
-
-send_redirects
-   Determines if or if not to send ICMP redirects to other hosts.
-
-
-Routing settings
-
-The directory /proc/sys/net/ipv4/route contains several file to
-control routing issues.
-
-error_burst and error_cost
-   These parameters are used to limit the warning messages written to
-   the kernel log from the routing code. The higher the error_cost
-   factor is, the fewer messages will be written. Error_burst controls
-   when messages will be dropped. The default settings limit warning
-   messages to one every five seconds.
-
-flush
-   Writing to this file results in a flush of the routing cache.
-
-gc_elastic, gc_interval, gc_min_interval, gc_tresh, gc_timeout
-   Values to control the frequency and behavior of the garbage
-   collection algorithm for the routing cache.
-
-max_size
-   Maximum size of the routing cache. Old entries will be purged
-   once the cache has this size.
-
-max_delay, min_delay
-   Delays for flushing the routing cache.
-
-redirect_load, redirect_number
-   Factors which determine if more ICPM redirects should be sent to
-   a specific host. No redirects will be sent once the load limit or
-   the maximum number of redirects has been reached.
-
-redirect_silence
-
-   Timeout for redirects. After this period redirects will be sent
-   again, even if this has been stopped, because the load or number
-   limit has been reached.
-
-Network Neighbor handling
-
-Settings about how to handle connections with direct neighbors (nodes
-attached to the same link) can be found in the directory
-/proc/sys/net/ipv4/neigh.
-
-As we saw it in the conf directory, there is a default subdirectory
-which holds the default values, and one directory for each
-interface. The contents of the directories are identical, with the
-single exception that the default settings contain additional options
-to set garbage collection parameters.
-
-In the interface directories you'll find the following entries:
-
-base_reachable_time
-   A base value used for computing the random reachable time value
-   as specified in RFC2461.
-
-retrans_time
-   The time, expressed in jiffies (1/100 sec), between retransmitted
-   Neighbor Solicitation messages. Used for address resolution and to
-   determine if a neighbor is unreachable.
-
-unres_qlen
-   Maximum queue length for a pending arp request - how many packets
-   are accepted from other layers while the arp address is still
-   resolved.
-
-anycast_delay
-   Maximum for random delay of answers to neighbor solicitation
-   messages in jiffies (1/100 sec). Not yet implemented (Linux does
-   not have anycast support yet).
-
-ucast_solicit
-   Maximum number of retries for unicast solicitation.
-
-mcast_solicit
-   Maximum number of retries for multicast solicitation.
-
-delay_first_probe_time
-   Delay for the first time probe if the neighbor is reachable. (see
-   gc_stale_time).
-
-locktime
-   An ARP/neighbor entry is only replaced with a new one if the old
-   is at least locktime old. This prevents ARP cache thrashing.
-
-proxy_delay
-   Maximum time (real time is random [0..proxytime]) before
-   answering to an arp request for which we have an proxy arp entry.
-   In some cases, this is used to prevent network flooding.
-
-proxy_qlen
-   Maximum queue length of the delayed proxy arp timer (see
-   proxy_delay).
-
-app_solcit
-   Determines the number of requests to send to the user level arp
-   daemon. 0 to turn off.
-
-gc_stale_time
-   Determines how often to check for stale ARP entries. After an ARP
-   entry is stale it will be resolved again (useful when an IP address
-   migrates to another machine). When ucast_solicit is > 0 it first
-   tries to send an ARP packet directly to the known host, when that
-   fails and mcast_solicit is > 0, an ARP request is broadcasted.
-
-3.10  Appletalk
-
-The /proc/sys/net/appletalk directory holds the Appletalk
-configuration data when Appletalk is loaded. The configurable
-parameters are:
-
-aarp-expiry-time
-   The amount of time we keep an AARP entry before expiring
-   it. Used to age out old hosts.
-
-aarp-resolve-time
-   The amount of time we will spend trying to resolve an Appletalk
-   address.
-
-aarp-retransmit-limit
-   The number of times we will retransmit a query before giving up.
-
-aarp-tick-time
-   Controls the rate at which expiries are checked.
-
-
-The directory /proc/net/appletalk holds the list of active appletalk
-sockets on a machine.
-
-The fields indicate the DDP type, the local address (in network:node
-format) the remote address, the size of the transmit pending queue,
-the size of the received queue (bytes waiting for applications to
-read) the state and the uid owning the socket.
-
-/proc/net/atalk_iface lists all the interfaces configured for
-appletalk.It shows the name of the interface, its appletalk address,
-the network range on that ad- dress (or network number for phase 1
-networks), and the status of the interface.
-
-/proc/net/atalk_route lists each known network route. It lists the
-target (network) that the route leads to, the router (may be directly
-connected), the route flags, and the device the route is via.
-
-3.11  IPX
-
-The IPX protocol has no tunable values in /proc/sys/net.
-
-The IPX protocol does, however, provide /proc/net/ipx. This lists each
-IPX socket giving the local and remote addresses in Novell format
-(that is network:node:port). In accordance with the strange Novell
-tradition, everything but the port is in hex. Not_Connected is
-displayed for sockets that are not tied to a specific remote
-address. The Tx and Rx queue sizes indicate the number of bytes
-pending for transmit and receive. The state indicates the state the
-socket is in and the uid is the owning uid of the socket.
-
-The /proc/net/ipx_interface file lists all IPX interfaces. For each
-interface it gives the network number, the node number, and indicates
-if the network is the primary network. It also indicates which device it is bound to (or
-Internal for internal networks) and the Frame Type if
-appropriate. Linux supports 802.3, 802.2, 802.2 SNAP and DIX (Blue
-Book) ethernet framing for IPX.
-
-The /proc/net/ipx_route table holds a list of IPX routes. For each
-route it gives the destination network, the router node (or Directly)
-and the network address of the router (or Connected) for internal
-networks.
diff -u --recursive --new-file v2.3.18/linux/Documentation/sound/NM256 linux/Documentation/sound/NM256
--- v2.3.18/linux/Documentation/sound/NM256	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/NM256	Sat Oct  2 07:35:14 1999
@@ -0,0 +1,229 @@
+=======================================================
+Documentation for the NeoMagic 256AV/256ZX sound driver
+=======================================================
+
+You're looking at version 1.0 of the driver.  (Woohoo!) It has been
+successfully tested against the following laptop models:
+
+	Sony Z505S/Z505SX/Z505DX
+	Sony F150, F160, F180, F250, F270, F280, PCG-F26
+	Dell Latitude CPi, CPt (various submodels)
+
+There are a few caveats, which is why you should read the entirety of
+this document first.
+
+This driver was developed without any support or assistance from
+NeoMagic.  There is no warranty, expressed, implied, or otherwise.  It
+is free software in the public domain; feel free to use it, sell it,
+give it to your best friends, even claim that you wrote it (but why?!)
+but don't come whining to me, NeoMagic, Sony, Dell, or anyone else
+when it blows up your computer.
+
+============
+Installation
+============
+
+Enable the sound drivers, the OSS sound drivers, and then the NM256
+driver.  The NM256 driver *must* be configured as a module (it won't
+give you any other choice).
+
+Next, do the usual "make modules" and "make modules_install".
+Finally, insmod the soundcore, sound and nm256 modules.
+
+When the nm256 driver module is loaded, you should see a couple of
+confirmation messages in the kernel logfile indicating that it found
+the device (the device does *not* use any I/O ports or DMA channels).
+Now try playing a wav file, futz with the CD-ROM if you have one, etc.
+
+The NM256 is entirely a PCI-based device, and all the necessary
+information is automatically obtained from the card.  It can only be
+configured as a module in a vain attempt to prevent people from
+hurting themselves.  It works correctly if it shares an IRQ with
+another device (it normally shares IRQ 9 with the builtin eepro100
+ethernet on the Sony Z505 laptops). 
+
+It does not run the card in any sort of compatibility mode. Thus it
+almost certainly will not work on laptops that have the
+SB16-compatible codec/mixer; you will want to use the standard SB16
+OSS driver with these chipsets.  I cannot provide any assistance with
+machines using the SB-16 compatible version.
+
+The sound support is very basic, but it does include simultaneous
+playback and record capability.  The mixer support is also quite
+simple, although this is in keeping with the rather limited
+functionality of the chipset.  
+
+There is no hardware synthesizer available, as the Losedows OPL-3 and
+MIDI support is done via hardware emulation.
+
+Only three recording devices are available on the Sony: the
+microphone, the CD-ROM input, and the volume device (which corresponds
+to the stereo output).  (Other devices may be available on other
+models of laptops.)  The Z505 series does not have a builtin CD-ROM,
+so of course the CD-ROM input doesn't work.  It does work on laptops
+with a builtin CD-ROM drive.
+
+Recording is mono 8-bit only.
+
+The mixer device does not appear to have any tone controls, at least
+on the Z505 series.  The mixer module checks for tone controls in the
+AC97 mixer, and will enable them if they are available.
+
+==============
+Known problems
+==============
+
+  * There are known problems with PCMCIA cards and the eepro100 ethernet 
+    driver on the Z505S/Z505SX/Z505DX.  Keep reading.
+
+  * There are also potential problems with using a virtual X display, and
+    also problems loading the module after the X server has been started. 
+    Keep reading.
+
+  * The volume control isn't anywhere near linear.  Sorry.  This will be
+    fixed eventually, when I get sufficiently annoyed with it.  (I doubt
+    it will ever be fixed now, since I've never gotten sufficiently
+    annoyed with it and nobody else seems to care.)
+
+  * There are reports that the CD-ROM volume is very low.  Since I do not
+    have a CD-ROM equipped laptop, I cannot test this (it's kinda hard to
+    do remotely).
+
+  * Only 8 fixed-rate speeds are supported.  This is mainly a chipset
+    limitation.  It may be possible to support other speeds in the future.
+
+  * There is no support for the telephone mixer/codec.  There is support
+    for a phonein/phoneout device if your mixer program supports it;
+    whether or not it does anything is anyone's guess.  (Reports on this
+    would be appreciated.)
+  
+  * This driver was not written with any cooperation or support from
+    NeoMagic.  If you have any questions about this, see their website
+    for their official stance on supporting open source drivers.
+
+============
+Video memory
+============
+
+The NeoMagic sound engine uses a portion of the display memory to hold
+the sound buffer.  (Crazy, eh?)  The NeoMagic video BIOS sets up a
+special pointer at the top of video RAM to indicate where the top of
+the audio buffer should be placed.
+
+At the present time XFree86 is apparently not aware of this.  It will
+thus write over either the pointer or the sound buffer with abandon.
+(Accelerated-X seems to do a better job here.)
+
+This implies a few things:
+
+  * Sometimes the NM256 driver has to guess at where the buffer 
+    should be placed, especially if the module is loaded after the
+    X server is started.  It's usually correct, but it will fail on
+    the Sony F250.
+
+  * Virtual screens greater than 1024x768x16 under XFree86 are
+    problematic on laptops with only 2.5MB of screen RAM. This
+    includes all of the 256AV-equipped laptops.  (Virtual displays
+    may or may not work on the 256ZX, which has at least 4MB of
+    video RAM.)
+
+If you start having problems with random noise being output either
+constantly (this is the usual symptom on the F250), or when windows
+are moved around (this is the usual symptom when using a virtual
+screen), the best fix is to
+
+  * Don't use a virtual frame buffer.
+  * Make sure you load the NM256 module before the X server is
+    started.
+
+On the F250, it is possible to force the driver to load properly even
+after the XFree86 server is started by doing:
+
+	insmod nm256.o buffertop=0x25a800
+
+This forces the audio buffers to the correct offset in screen RAM.
+
+=================
+Official WWW site
+=================
+
+The official site for the NM256 driver is:
+
+	http://www.uglx.org/sony.html
+
+You should always be able to get the latest version of the driver there,
+and the driver will be supported for the foreseeable future.
+
+==============================
+Z505S/Z505SX on-board Ethernet
+==============================
+
+If you're using the on-board Ethernet Pro/100 ethernet support on the Z505
+series, I strongly encourage you to download the latest eepro100 driver from
+Donald Becker's site:
+
+	ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/test/eepro100.c
+
+There was a reported problem on the Z505SX that if the ethernet
+interface is disabled and reenabled while the sound driver is loaded,
+the machine would lock up.  I have included a workaround that is
+working satisfactorily.  However, you may occasionally see a message
+about "Releasing interrupts, over 1000 bad interrupts" which indicates
+that the workaround is doing its job.
+
+==================================
+PCMCIA and the Z505S/Z505SX/Z505DX
+==================================
+
+There is also a known problem with the Sony Z505S and Z505SX hanging
+if a PCMCIA card is inserted while the ethernet driver is loaded.
+This is caused by tons of spurious IRQ 9s, probably generated from the
+PCMCIA or ACPI bridges.  There is currently no fix for the problem,
+and the only known workaround is to disable the ethernet interface
+before inserting or removing a PCMCIA card.
+
+======
+Thanks
+======
+
+First, I want to thank everyone (except NeoMagic of course) for their
+generous support and encouragement.  I'd like to list everyone's name
+here that replied during the development phase, but the list is
+amazingly long.
+
+I will be rather unfair and single out a few people, however:
+
+	Justin Maurer, for being the first random net.person to try it,
+	and for letting me login to his Z505SX to get it working there
+
+	Edi Weitz for trying out several different versions, and giving
+	me a lot of useful feedback
+
+	Greg Rumple for letting me login remotely to get the driver 
+	functional on the 256ZX, for his assistance on tracking
+	down all sorts of random stuff, and for trying out Accel-X
+
+	Zach Brown, for the initial AC97 mixer interface design
+
+	Jeff Garzik, for various helpful suggestions on the AC97
+	interface
+
+=================
+Previous versions
+=================
+
+Versions prior to 0.3 (aka `noname') had problems with weird artifacts
+in the output and failed to set the recording rate properly.  These
+problems have long since been fixed.
+
+Versions prior to 0.5 had problems with clicks in the output when
+anything other than 16-bit stereo sound was being played, and also had
+periodic clicks when recording.
+
+Version 0.7 first incorporated support for the NM256ZX chipset, which
+is found on some Dell Latitude laptops (the CPt, and apparently
+some CPi models as well).  It also included the generic AC97
+mixer module.
+
+Version 0.75 renamed all the functions and files with slightly more
+generic names.
diff -u --recursive --new-file v2.3.18/linux/MAINTAINERS linux/MAINTAINERS
--- v2.3.18/linux/MAINTAINERS	Tue Sep  7 12:14:05 1999
+++ linux/MAINTAINERS	Wed Sep 29 13:59:12 1999
@@ -29,7 +29,7 @@
 	job the maintainers (and especially Linus) do is to keep things
 	looking the same. Sometimes this means that the clever hack in
 	your driver to get around a problem actually needs to become a
-	generalized kernel feature ready for next time. See 
+	generalized kernel feature ready for next time. See
 	Documentation/CodingStyle for guidance here.
 
 	PLEASE try to include any credit lines you want added with the
@@ -63,7 +63,7 @@
 	Maintained:	Someone actually looks after it.
 	Odd Fixes:	It has a maintainer but they don't have time to do
 			much other than throw the odd patch in. See below..
-	Orphan:		No current maintainer [but maybe you could take the 
+	Orphan:		No current maintainer [but maybe you could take the
 			role as you write your new code].
 	Obsolete:	Old code. Something tagged obsolete generally means
 			it has been replaced by a better system and you
@@ -131,12 +131,6 @@
 L:	linux-atalk@netspace.org
 S:	Maintained
 
-ARPD SUPPORT
-P:	Jonathan Layes
-M:	layes@loran.com
-L:	linux-net@vger.rutgers.edu
-S:	Maintained
-
 ARM MFM AND FLOPPY DRIVERS
 P:	Dave Gilbert
 M:	linux@treblig.org
@@ -150,6 +144,12 @@
 W:	http://www.arm.uk.linux.org/~rmk/armlinux.html
 S:	Maintained
 
+ARPD SUPPORT
+P:	Jonathan Layes
+M:	layes@loran.com
+L:	linux-net@vger.rutgers.edu
+S:	Maintained
+
 AX.25 NETWORK LAYER
 P:	Matthias Welwarsky
 M:	dg2fef@afthd.tu-darmstadt.de
@@ -182,6 +182,12 @@
 L:	linux-fbdev@vuser.vu.union.edu
 S:	Maintained
 
+COMPAQ SMART2 RAID DRIVER
+P:	Charles White	
+M:	Charles White <arrays@compaq.com>
+L:	compaqandlinux@yps.org	
+S:	Maintained
+
 COMPUTONE INTELLIPORT MULTIPORT CARD
 P:     Doug McNash
 P:     Michael H. Warfield
@@ -287,12 +293,6 @@
 L:	linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
 S:	Maintained
 
-COMPAQ SMART2 RAID DRIVER
-P:	Charles White	
-M:	Charles White <arrays@compaq.com>
-L:	compaqandlinux@yps.org	
-S:	Maintained
-
 EATA ISA/EISA/PCI SCSI DRIVER
 P:	Dario Ballabio
 M:	dario@milano.europe.dg.com
@@ -369,7 +369,7 @@
 
 HFS FILESYSTEM
 P:      Adrian Sun
-M:      asun@u.washington.edu
+M:      asun@cobaltnet.com
 L:      linux-kernel@vger.rutgers.edu
 S:      Maintained
 
@@ -390,17 +390,23 @@
 M:	perex@suse.cz
 S:	Maintained
 
+HPFS FILESYSTEM
+P:	Mikulas Patocka
+M:	mikulas@artax.karlin.mff.cuni.cz
+W:	http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
+S:	Maintained
+
 IBM MCA SCSI SUBSYSTEM DRIVER
 P:	Michael Lang
 M:	langa2@kph.uni-mainz.de
 W:	http://www.uni-mainz.de/~langm000/linux.html
 S:	Maintained
 
-HPFS FILESYSTEM
-P:	Mikulas Patocka
-M:	mikulas@artax.karlin.mff.cuni.cz
-W:	http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
-S:	Maintained
+IBM ServeRAID RAID DRIVER
+P:      Keith Mitchell
+M:      ipslinux@us.ibm.com
+W:      http://www.developer.ibm.com/welcome/netfinity/serveraid_beta.html
+S:      Supported
 
 IBM ServeRAID RAID DRIVER
 P:      Keith Mitchell
@@ -415,7 +421,7 @@
 W:	http://linux.kernel.org/pub/linux/kernel/people/hedrick/
 S:	Supported
 
-IDE/ATAPI CDROM DRIVER 
+IDE/ATAPI CDROM DRIVER
 P:	Jens Axboe
 M:	axboe@image.dk
 L:	linux-kernel@vger.rutgers.edu
@@ -524,8 +530,9 @@
 
 MISCELLANEOUS MCA-SUPPORT
 P:	David Weinehall
-M:	mcalinux@acc.umu.se (project MCA-team)
 M:	tao@acc.umu.se (personal)
+W:	http://www.acc.umu.se/~tao/
+W:	http://www.acc.umu.se/~mcalinux/
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
@@ -571,7 +578,7 @@
 S:	Supported
 
 NETROM NETWORK LAYER
-P:	Tomi Manninen 
+P:	Tomi Manninen
 M:	Tomi.Manninen@hut.fi
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
@@ -627,16 +634,6 @@
 W:	http://www.linuxtr.net
 S:	Maintained
 
-OLYMPIC NETWORK DRIVER
-P:	Peter De Shrijver
-M:	p2@ace.ulyssis.sutdent.kuleuven.ac.be	
-P:	Mike Phillips
-M:	phillim@amtrak.com
-L:	linux-net@vger.rutgers.edu
-L:	linux-tr@emissary.aus-etc.com
-W:	http://www.linuxtr.net
-S:	Maintained
-
 OPL3-SA2, SA3, and SAx DRIVER
 P:	Scott Murray
 M:	scottm@interlog.com
@@ -668,17 +665,23 @@
 M:	jmaurer@cck.uni-kl.de
 S:	Maintained
 
+PCI SOUND DRIVERS (ES1370, ES1371 and SONICVIBES)
+P:	Thomas Sailer
+M:	sailer@ife.ee.ethz.ch
+L:	linux-sound@vger.rutgers.edu
+W:	http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html
+S:	Maintained
+
 PCI SUBSYSTEM
 P:	Martin Mares
 M:	mj@atrey.karlin.mff.cuni.cz
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-PCI SOUND DRIVERS (ES1370, ES1371 and SONICVIBES)
-P:	Thomas Sailer
-M:	sailer@ife.ee.ethz.ch
-L:	linux-sound@vger.rutgers.edu
-W:	http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html
+PCMCIA SUBSYSTEM
+P:	David Hinds
+M:	dhinds@zen.stanford.edu
+L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
 PCNET32 NETWORK DRIVER
@@ -701,6 +704,12 @@
 L:	linux-ppp@vger.rutgers.edu
 S:	Maintained
 
+RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
+P:	Corey Thomas
+M:	corey@world.std.com
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 REAL TIME CLOCK DRIVER
 P:	Paul Gortmaker
 M:	p_gortmaker@yahoo.com
@@ -725,7 +734,7 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-SCSI CDROM DRIVER 
+SCSI CDROM DRIVER
 P:	Jens Axboe
 M:	axboe@image.dk
 L:	linux-kernel@vger.rutgers.edu
@@ -776,7 +785,7 @@
 S:	Maintained
 
 SOUND
-P:	Alan Cox 
+P:	Alan Cox
 M:	alan@redhat.com
 S:	Supported
 
@@ -860,7 +869,7 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-UNIFORM CDROM DRIVER 
+UNIFORM CDROM DRIVER
 P:	Jens Axboe
 M:	axboe@image.dk
 L:	linux-kernel@vger.rutgers.edu
@@ -886,6 +895,11 @@
 M:	chaffee@cs.berkeley.edu
 L:	linux-kernel@vger.rutgers.edu
 W:	http://bmrc.berkeley.edu/people/chaffee
+S:	Maintained
+
+VIA 82Cxxx AUDIO DRIVER
+P:	Jeff Garzik
+M:	jgarzik@pobox.com
 S:	Maintained
 
 VIDEO FOR LINUX
diff -u --recursive --new-file v2.3.18/linux/Makefile linux/Makefile
--- v2.3.18/linux/Makefile	Fri Sep 10 23:57:27 1999
+++ linux/Makefile	Sat Oct  2 07:49:30 1999
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 18
+SUBLEVEL = 19
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
@@ -146,8 +146,11 @@
 DRIVERS := $(DRIVERS) drivers/pci/pci.a
 endif
 
-ifdef CONFIG_PCMCIA
+ifeq ($(CONFIG_PCMCIA),y)
 DRIVERS := $(DRIVERS) drivers/pcmcia/pcmcia.o
+endif
+
+ifeq ($(CONFIG_PCMCIA_NETCARD),y)
 DRIVERS := $(DRIVERS) drivers/net/pcmcia/pcmcia_net.o
 endif
 
@@ -345,6 +348,7 @@
 	if [ -f FC4_MODULES   ]; then inst_mod FC4_MODULES   fc4;   fi; \
 	if [ -f IRDA_MODULES  ]; then inst_mod IRDA_MODULES  net;   fi; \
 	if [ -f USB_MODULES   ]; then inst_mod USB_MODULES   usb;   fi; \
+	if [ -f PCMCIA_MODULES ]; then inst_mod PCMCIA_MODULES pcmcia; fi; \
 	\
 	ls *.o > $$MODLIB/.allmods; \
 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \
@@ -366,9 +370,9 @@
 
 clean:	archclean
 	rm -f kernel/ksyms.lst include/linux/compile.h
-	rm -f core `find . -name '*.[oas]' ! -regex '.*lxdialog/.*' -print`
+	find . -name '*.[oas]' -type f -print | grep -v lxdialog/ | xargs rm -f
 	rm -f core `find . -type f -name 'core' -print`
-	rm -f core `find . -name '.*.flags' -print`
+	rm -f core `find . -type f -name '.*.flags' -print`
 	rm -f vmlinux System.map
 	rm -f .tmp*
 	rm -f drivers/char/consolemap_deftbl.c drivers/video/promcon_tbl.c
@@ -395,8 +399,8 @@
 	rm -f .menuconfig.log
 	rm -f include/asm
 	rm -rf include/config
-	rm -f .depend `find . -name .depend -print`
-	rm -f core `find . -size 0 -print`
+	rm -f .depend `find . -type f -name .depend -print`
+	rm -f core `find . -type f -size 0 -print`
 	rm -f .hdepend scripts/mkdep scripts/split-include
 	rm -f $(TOPDIR)/include/linux/modversions.h
 	rm -rf $(TOPDIR)/include/linux/modules
diff -u --recursive --new-file v2.3.18/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v2.3.18/linux/arch/i386/boot/setup.S	Tue Aug 31 17:29:12 1999
+++ linux/arch/i386/boot/setup.S	Fri Oct  1 10:23:54 1999
@@ -461,7 +461,7 @@
 	int	0x15		! ignore return code
 
 	mov	ax,#0x05303	! 32 bit connect
-	xor	bx,bx
+	xor	ebx,ebx
 	int	0x15
 	jc	no_32_apm_bios	! error
 
diff -u --recursive --new-file v2.3.18/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.18/linux/arch/i386/defconfig	Fri Sep 10 23:57:27 1999
+++ linux/arch/i386/defconfig	Sat Oct  2 09:07:58 1999
@@ -285,7 +285,9 @@
 # PCMCIA network devices
 #
 # CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_3C589 is not set
 CONFIG_PCMCIA_RAYCS=y
+CONFIG_PCMCIA_NETCARD=y
 
 #
 # Amateur Radio support
diff -u --recursive --new-file v2.3.18/linux/arch/i386/kernel/i8259.c linux/arch/i386/kernel/i8259.c
--- v2.3.18/linux/arch/i386/kernel/i8259.c	Thu Aug 26 13:05:34 1999
+++ linux/arch/i386/kernel/i8259.c	Wed Sep 29 13:59:12 1999
@@ -385,7 +385,7 @@
 #endif	
 
 	/*
-	 * Set the clock to 100 Hz, we already have a valid
+	 * Set the clock to HZ Hz, we already have a valid
 	 * vector now:
 	 */
 	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
diff -u --recursive --new-file v2.3.18/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- v2.3.18/linux/arch/i386/kernel/mtrr.c	Wed Aug 18 11:27:04 1999
+++ linux/arch/i386/kernel/mtrr.c	Wed Sep 29 13:59:12 1999
@@ -201,6 +201,28 @@
     19990512   Richard Gooch <rgooch@atnf.csiro.au>
 	       Minor cleanups.
   v1.35
+    19990707   Zoltan Boszormenyi <zboszor@mol.hu>
+               Check whether ARR3 is protected in cyrix_get_free_region()
+               and mtrr_del(). The code won't attempt to delete or change it
+               from now on if the BIOS protected ARR3. It silently skips ARR3
+               in cyrix_get_free_region() or returns with an error code from
+               mtrr_del().
+    19990711   Zoltan Boszormenyi <zboszor@mol.hu>
+               Reset some bits in the CCRs in cyrix_arr_init() to disable SMM
+               if ARR3 isn't protected. This is needed because if SMM is active
+               and ARR3 isn't protected then deleting and setting ARR3 again
+               may lock up the processor. With SMM entirely disabled, it does
+               not happen.
+    19990812   Zoltan Boszormenyi <zboszor@mol.hu>
+               Rearrange switch() statements so the driver accomodates to
+               the fact that the AMD Athlon handles its MTRRs the same way
+               as Intel does.
+    19990814   Zoltan Boszormenyi <zboszor@mol.hu>
+	       Double check for Intel in mtrr_add()'s big switch() because
+	       that revision check is only valid for Intel CPUs.
+    19990819   Alan Cox <alan@redhat.com>
+               Tested Zoltan's changes on a pre production Athlon - 100%
+               success.
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -309,6 +331,7 @@
     unsigned long ccr3;
 };
 
+static int arr3_protected;
 
 /*  Put the processor into a state where MTRRs can be safely set  */
 static void set_mtrr_prepare (struct set_mtrr_context *ctxt)
@@ -321,6 +344,8 @@
     switch (boot_cpu_data.x86_vendor)
     {
       case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */
+	/* else fall through */
       case X86_VENDOR_CENTAUR:
 	return;
 	/*break;*/
@@ -344,6 +369,7 @@
 
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
       case X86_VENDOR_INTEL:
 	/*  Disable MTRRs, and set the default type to uncached  */
 	rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
@@ -365,6 +391,8 @@
     switch (boot_cpu_data.x86_vendor)
     {
       case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */
+	/* else fall through */
       case X86_VENDOR_CENTAUR:
 	__restore_flags (ctxt->flags);
 	return;
@@ -376,6 +404,7 @@
     /*  Restore MTRRdefType  */
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
       case X86_VENDOR_INTEL:
 	wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
 	break;
@@ -406,6 +435,9 @@
 
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 < 6) return 2; /* pre-Athlon CPUs */
+	/* else fall through */
       case X86_VENDOR_INTEL:
 	rdmsr (MTRRcap_MSR, config, dummy);
 	return (config & 0xff);
@@ -416,9 +448,6 @@
         /*  and Centaur has 8 MCR's  */
 	return 8;
 	/*break;*/
-      case X86_VENDOR_AMD:
-	return 2;
-	/*break;*/
     }
     return 0;
 }   /*  End Function get_num_var_ranges  */
@@ -430,12 +459,14 @@
 
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 < 6) return 1; /* pre-Athlon CPUs */
+	/* else fall through */
       case X86_VENDOR_INTEL:
 	rdmsr (MTRRcap_MSR, config, dummy);
 	return (config & (1<<10));
 	/*break;*/
       case X86_VENDOR_CYRIX:
-      case X86_VENDOR_AMD:
       case X86_VENDOR_CENTAUR:
 	return 1;
 	/*break;*/
@@ -1030,6 +1061,7 @@
 	for (i = 0; i < 7; i++)
 	{
 	    cyrix_get_arr (i, &lbase, &lsize, &ltype);
+	    if ((i == 3) && arr3_protected) continue;
 	    if (lsize < 1) return i;
 	}
 	/* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */
@@ -1062,13 +1094,30 @@
     if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV;
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */
+	  /* Apply the K6 block alignment and size rules
+	     In order
+		o Uncached or gathering only
+		o 128K or bigger block
+		o Power of 2 block
+		o base suitably aligned to the power
+	    */
+	  if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) ||
+	      (size & ~(size-1))-size || (base & (size-1)))
+	      return -EINVAL;
+	  break;
+	} /* else fall through */
       case X86_VENDOR_INTEL:
-	/*  For Intel PPro stepping <= 7, must be 4 MiB aligned  */
-	if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
-	     (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) )
-	{
-	    printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
-	    return -EINVAL;
+	/*  Double check for Intel, we may run on Athlon. */
+	if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) {
+	  /*  For Intel PPro stepping <= 7, must be 4 MiB aligned  */
+	  if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
+	       (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) )
+	  {
+	      printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
+	      return -EINVAL;
+	  }
 	}
 	/*  Fall through  */
       case X86_VENDOR_CYRIX:
@@ -1105,18 +1154,6 @@
 	    return -EINVAL;
 	}
 	break;
-      case X86_VENDOR_AMD:
-    	/* Apply the K6 block alignment and size rules
-    	   In order
-    	      o Uncached or gathering only
-    	      o 128K or bigger block
-    	      o Power of 2 block
-    	      o base suitably aligned to the power
-    	  */
-    	if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) ||
-	    (size & ~(size-1))-size || (base & (size-1)))
-	    return -EINVAL;
-	break;
       default:
 	return -EINVAL;
 	/*break;*/
@@ -1221,6 +1258,15 @@
 	printk ("mtrr: register: %d too big\n", reg);
 	return -EINVAL;
     }
+    if (boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX)
+    {
+	if ((reg == 3) && arr3_protected)
+	{
+	    spin_unlock (&main_lock);
+	    printk ("mtrr: ARR3 cannot be changed\n");
+	    return -EINVAL;
+	}
+    }
     (*get_mtrr) (reg, &lbase, &lsize, &ltype);
     if (lsize < 1)
     {
@@ -1585,22 +1631,22 @@
     ccr[5] = getCx86 (CX86_CCR5);
     ccr[6] = getCx86 (CX86_CCR6);
 
-    if (ccr[3] & 1)
+    if (ccr[3] & 1) {
       ccrc[3] = 1;
-    else {
+      arr3_protected = 1;
+    } else {
       /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and
        * access to SMM memory through ARR3 (bit 7).
        */
-/*
       if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; }
       if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; }
       if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; }
-*/
+      arr3_protected = 0;
       if (ccr[6] & 0x02) {
         ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3. */
         setCx86 (CX86_CCR6, ccr[6]);
       }
-      /* Disable ARR3. */
+      /* Disable ARR3. This is safe now that we disabled SMM. */
       /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */
     }
     /* If we changed CCR1 in memory, change it in the processor, too. */
@@ -1660,6 +1706,12 @@
     printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION);
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */
+	  get_mtrr = amd_get_mtrr;
+	  set_mtrr_up = amd_set_mtrr_up;
+	  break;
+	} /* else fall through */
       case X86_VENDOR_INTEL:
 	get_mtrr = intel_get_mtrr;
 	set_mtrr_up = intel_set_mtrr_up;
@@ -1669,10 +1721,6 @@
 	set_mtrr_up = cyrix_set_arr_up;
 	get_free_region = cyrix_get_free_region;
 	break;
-      case X86_VENDOR_AMD:
-	get_mtrr = amd_get_mtrr;
-	set_mtrr_up = amd_set_mtrr_up;
-	break;
      case X86_VENDOR_CENTAUR:
         get_mtrr = centaur_get_mcr;
         set_mtrr_up = centaur_set_mcr_up;
@@ -1691,6 +1739,8 @@
     mtrr_setup ();
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */
       case X86_VENDOR_INTEL:
 	get_mtrr_state (&smp_mtrr_state);
 	break;
@@ -1727,6 +1777,9 @@
     if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return;
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+	/* Just for robustness: pre-Athlon CPUs cannot do SMP. */
+	if (boot_cpu_data.x86 < 6) break;
       case X86_VENDOR_INTEL:
 	intel_mtrr_init_secondary_cpu ();
 	break;
@@ -1752,6 +1805,8 @@
 #  ifdef __SMP__
     switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+	if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */
       case X86_VENDOR_INTEL:
 	finalize_mtrr_state (&smp_mtrr_state);
 	mtrr_state_warn (smp_changes_mask);
diff -u --recursive --new-file v2.3.18/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.3.18/linux/arch/i386/kernel/setup.c	Tue Sep  7 12:14:06 1999
+++ linux/arch/i386/kernel/setup.c	Wed Sep 29 13:59:13 1999
@@ -61,6 +61,7 @@
 #include <asm/msr.h>
 #include <asm/desc.h>
 #include <asm/e820.h>
+#include <asm/dma.h>
 
 /*
  * Machine setup..
@@ -401,18 +402,49 @@
 
 void __init setup_memory_region(void)
 {
+#define E820_DEBUG	0
+#ifdef E820_DEBUG
+	int i;
+#endif
+
 	/*
 	 * If we're lucky and live on a modern system, the setup code
 	 * will have given us a memory map that we can use to properly
 	 * set up memory.  If we aren't, we'll fake a memory map.
+	 *
+	 * We check to see that the memory map contains at least 2 elements
+	 * before we'll use it, because the detection code in setup.S may
+	 * not be perfect and most every PC known to man has two memory
+	 * regions: one from 0 to 640k, and one from 1mb up.  (The IBM
+	 * thinkpad 560x, for example, does not cooperate with the memory
+	 * detection code.)
 	 */
-	if (E820_MAP_NR) {
+	if (E820_MAP_NR > 1) {
 		/* got a memory map; copy it into a safe place.
 		 */
 		e820.nr_map = E820_MAP_NR;
 		if (e820.nr_map > E820MAX)
 			e820.nr_map = E820MAX;
 		memcpy(e820.map, E820_MAP, e820.nr_map * sizeof e820.map[0]);
+#ifdef E820_DEBUG
+		for (i=0; i < e820.nr_map; i++) {
+			printk("e820: %ld @ %08lx ",
+				(unsigned long)(e820.map[i].size),
+				(unsigned long)(e820.map[i].addr));
+			switch (e820.map[i].type) {
+			case E820_RAM:	printk("(usable)\n");
+					break;
+			case E820_RESERVED:
+					printk("(reserved)\n");
+					break;
+			case E820_ACPI:
+					printk("(ACPI data)\n");
+					break;
+			default:	printk("type %lu\n", e820.map[i].type);
+					break;
+			}
+		}
+#endif
 	}
 	else {
 		/* otherwise fake a memory map; one section from 0k->640k,
@@ -422,8 +454,8 @@
 
 		mem_size = (ALT_MEM_K < EXT_MEM_K) ? EXT_MEM_K : ALT_MEM_K;
 
-		add_memory_region(0, LOWMEMSIZE(), 1);
-		add_memory_region(HIGH_MEMORY, mem_size << 10, 1);
+		add_memory_region(0, LOWMEMSIZE(), E820_RAM);
+		add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM);
   	}
 } /* setup_memory_region */
 
@@ -503,7 +535,7 @@
 					 */
 					e820.nr_map = 0;
 					usermem = 1;
-					add_memory_region(0, LOWMEMSIZE(), 1);
+					add_memory_region(0, LOWMEMSIZE(), E820_RAM);
 				}
 				mem_size = memparse(from+4, &from);
 				if (*from == '@')
@@ -512,7 +544,7 @@
 					start_at = HIGH_MEMORY;
 					mem_size -= HIGH_MEMORY;
 				}
-				add_memory_region(start_at, mem_size, 1);
+				add_memory_region(start_at, mem_size, E820_RAM);
 			}
 		}
 		c = *(from++);
@@ -531,7 +563,7 @@
 	memory_end = 0;
 	for (i=0; i < e820.nr_map; i++) {
 		/* RAM? */
-		if (e820.map[i].type == 1) {
+		if (e820.map[i].type == E820_RAM) {
 			unsigned long end = e820.map[i].addr + e820.map[i].size;
 
 			if (end > memory_end)
@@ -856,6 +888,15 @@
 			c->x86_model = (dir1 & 0x20) ? 1 : 2;
 			c->x86_capability&=~X86_FEATURE_TSC;
 		}
+#ifdef CONFIG_PCI
+		/* It isnt really a PCI quirk directly, but the cure is the
+		   same. The MediaGX has deep magic SMM stuff that handles the
+		   SB emulation. It thows away the fifo on disable_dma() which
+		   is wrong and ruins the audio. */
+		
+		printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bug.\n");
+		isa_dma_bridge_buggy = 1;
+#endif		
 		break;
 
         case 5: /* 6x86MX/M II */
@@ -881,8 +922,8 @@
 			dir0_msn = 0;
 			p = Cx486S_name[0];
 			break;
-		break;
 		}
+		break;
 
 	default:  /* unknown (shouldn't happen, we know everyone ;-) */
 		dir0_msn = 7;
diff -u --recursive --new-file v2.3.18/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.3.18/linux/arch/i386/mm/init.c	Tue Sep  7 12:14:06 1999
+++ linux/arch/i386/mm/init.c	Wed Sep 29 14:02:59 1999
@@ -414,25 +414,23 @@
 	for (avail = i = 0; i < e820.nr_map; i++) {
 		unsigned long addr, end, size;
 
-		if (e820.map[i].type != 1)	/* not usable memory */
+		if (e820.map[i].type != E820_RAM)	/* not usable memory */
 			continue;
 		addr = e820.map[i].addr;
 		size = e820.map[i].size;
 
-		/* Overflow large memory reasonably gracefully */
+		/* Silently ignore memory regions starting above 4gb */
 		if (addr != e820.map[i].addr)
 			continue;
 
 		printk("memory region: %luk @ %08lx\n", size >> 10, addr );
 
 		/* Make sure we don't get fractional pages */
-		end = (addr + size) & PAGE_MASK;
-		addr = PAGE_ALIGN(addr);
-		if (end <= addr)
-			continue;
+		end = PAGE_OFFSET + ((addr + size) & PAGE_MASK);
+		addr= PAGE_OFFSET + PAGE_ALIGN(addr);
+
+		for ( ; addr < end; addr += PAGE_SIZE) {
 
-		size = end - addr;
-		for (addr = addr + PAGE_OFFSET ; size ; addr += PAGE_SIZE, size -= PAGE_SIZE) {
 			/* this little bit of grossness is for dealing
 			 * with memory borrowing for system bookkeeping
 			 * (smp stacks, zero page, kernel code, etc)
@@ -446,14 +444,11 @@
 			 * in any case, we don't want to hack mem_map
 			 * entries above end_mem.
 			 */
-			if ( addr < start_low_mem )
-				continue;
-			if ( addr > end_mem )
-				continue;
-			if ( addr >= HIGH_MEMORY && addr <= start_mem )
+			if ( (addr < start_low_mem)
+			  || (addr >= (HIGH_MEMORY + PAGE_OFFSET)&& addr <= start_mem)
+			  || (addr > end_mem) )
 				continue;
 
-			avail++;
 			clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
 		}
 	}
diff -u --recursive --new-file v2.3.18/linux/drivers/atm/ambassador.c linux/drivers/atm/ambassador.c
--- v2.3.18/linux/drivers/atm/ambassador.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/atm/ambassador.c	Wed Sep 29 14:02:59 1999
@@ -674,7 +674,7 @@
     return 0;
   } else {
     
-    spin_unlock_irqrestore (&dev->lock, flags);
+    spin_unlock_irqrestore (&txq->lock, flags);
     return -1;
   }
 }
diff -u --recursive --new-file v2.3.18/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v2.3.18/linux/drivers/block/hd.c	Thu Aug 12 11:50:14 1999
+++ linux/drivers/block/hd.c	Sat Oct  2 07:35:14 1999
@@ -40,6 +40,7 @@
 #include <linux/ioport.h>
 #include <linux/mc146818rtc.h> /* CMOS defines */
 #include <linux/init.h>
+#include <linux/blkpg.h>
 
 #define REALLY_SLOW_IO
 #include <asm/system.h>
diff -u --recursive --new-file v2.3.18/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.3.18/linux/drivers/block/ide-cd.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/block/ide-cd.c	Sat Oct  2 07:35:14 1999
@@ -12,11 +12,11 @@
  *
  * Suggestions are welcome. Patches that work are more welcome though. ;-)
  * For those wishing to work on this driver, please be sure you download
- * and comply with the latest Mt. Fuji (SFF8090 version 3) and ATAPI 
+ * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI 
  * (SFF-8020i rev 2.6) standards. These documents can be obtained by 
  * anonymous ftp from:
  * ftp://fission.dt.wdc.com/pub/standards/SFF/specs/INF-8020.PDF
- * ftp://fission.dt.wdc.com/pub/standards/SFF/specs/INF-8090.PDF
+ * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r01.pdf
  *
  * Drives that deviate from these standards will be accomodated as much
  * as possible via compile time or command-line options.  Since I only have
@@ -29,14 +29,8 @@
  *   This will allow us to get automagically notified when the media changes
  *   on ATAPI drives (something the stock ATAPI spec is lacking).  Looks
  *   very cool.  I discovered its existance the other day at work...
- * -Query the drive to find what features are available before trying to
- *   use them (like trying to close the tray in drives that can't).
  * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
  *   boot
- * -Integrate DVD-ROM support in driver. Thanks to Merete Gotsćd-Petersen
- *   of Pioneer Denmark for providing me with a drive for testing.
- * -Implement Features and Profiles.
- *
  *
  * ----------------------------------
  * 1.00  Oct 31, 1994 -- Initial version.
@@ -274,9 +268,18 @@
  *			  commands across the various drivers and how
  *			  sense errors are handled.
  *
+ * 4.56  Sep 12, 1999	- Removed changer support - it is now in the
+ *			  Uniform layer.
+ *			- Added partition based multisession handling.
+ *			- Mode sense and mode select moved to the
+ *			  Uniform layer.
+ *			- Fixed a problem with WPI CDS-32X drive - it
+ *			  failed the capabilities 
+ *
+ *
  *************************************************************************/
  
-#define IDECD_VERSION "4.55"
+#define IDECD_VERSION "4.56"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -548,6 +551,7 @@
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	int stat, cmd, err, sense_key;
+	struct packet_command *pc = (struct packet_command *) rq->buffer;
 	
 	/* Check for errors. */
 	stat = GET_STAT();
@@ -571,8 +575,6 @@
 			   from the drive (probably while trying
 			   to recover from a former error).  Just give up. */
 
-			struct packet_command *pc = (struct packet_command *)
-				                      rq->buffer;
 			pc->stat = 1;
 			cdrom_end_request (1, drive);
 			ide_error (drive, "request sense failure", stat);
@@ -581,23 +583,11 @@
 		} else if (cmd == PACKET_COMMAND) {
 			/* All other functions, except for READ. */
 
-			struct packet_command *pc = (struct packet_command *)
-				                      rq->buffer;
 			struct semaphore *sem = NULL;
 
 			/* Check for tray open. */
 			if (sense_key == NOT_READY) {
 				cdrom_saw_media_change (drive);
-#if 0	/* let the upper layers do the complaining */
-				/* Print an error message to the syslog.
-				   Exception: don't print anything if this
-				   is a read subchannel command.  This is
-				   because workman constantly polls the drive
-				   with this command, and we don't want
-				   to uselessly fill up the syslog. */
-				if (pc->c[0] != GPCMD_READ_SUBCHANNEL)
-					printk ("%s: tray open or drive not ready\n", drive->name);
-#endif
 			} else if (sense_key == UNIT_ATTENTION) {
 				/* Check for media change. */
 				cdrom_saw_media_change (drive);
@@ -1344,7 +1334,6 @@
 	if (pc->sense_data == NULL)
 		pc->sense_data = &my_reqbuf;
 	pc->sense_data->sense_key = 0;
-
 	/* Start of retry loop. */
 	do {
 		ide_init_drive_cmd (&req);
@@ -1386,8 +1375,8 @@
 	else {
 		/* The command succeeded.  If it was anything other than
 		   a request sense, eject, or door lock command,
-		   and we think that the door is presently, lock it again.
-		   (The door was probably unlocked via an explicit
+		   and we think that the door is presently unlocked, lock it
+		   again. (The door was probably unlocked via an explicit
 		   CDROMEJECT ioctl.) */
 		if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
 		    (pc->c[0] != GPCMD_TEST_UNIT_READY &&
@@ -1502,6 +1491,8 @@
 		    struct atapi_request_sense *reqbuf)
 {
 	struct packet_command pc;
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
 
 	memset (&pc, 0, sizeof (pc));
 
@@ -1512,7 +1503,7 @@
         /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
            switch CDs instead of supporting the LOAD_UNLOAD opcode   */
 
-        pc.c[7] = CDROM_STATE_FLAGS (drive)->sanyo_slot % 3;
+        pc.c[7] = cdi->sanyo_slot % 3;
 #endif /* not STANDARD_ATAPI */
 
 	return cdrom_queue_packet_command (drive, &pc);
@@ -1639,12 +1630,12 @@
 
 /* Try to read the entire TOC for the disk into our internal buffer. */
 static int
-cdrom_read_toc (ide_drive_t *drive,
-		struct atapi_request_sense *reqbuf)
+cdrom_read_toc (ide_drive_t *drive, struct atapi_request_sense *reqbuf)
 {
 	int stat, ntracks, i;
 	struct cdrom_info *info = drive->driver_data;
 	struct atapi_toc *toc = info->toc;
+	int minor = drive->select.b.unit << PARTN_BITS;
 	struct {
 		struct atapi_toc_header hdr;
 		struct atapi_toc_entry  ent;
@@ -1685,10 +1676,9 @@
 	if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
 
 	/* Now read the whole schmeer. */
-	stat = cdrom_read_tocentry (drive, toc->hdr.first_track-1, 1, 0,
-				   (char *)&toc->hdr,
+	stat = cdrom_read_tocentry (drive, toc->hdr.first_track, 1, 0, (char *)&toc->hdr,
 				    sizeof (struct atapi_toc_header) +
-				    (ntracks+1) *
+				    (ntracks + 1) *
 				    sizeof (struct atapi_toc_entry), reqbuf);
 
 	if (stat && toc->hdr.first_track > 1) {
@@ -1724,6 +1714,7 @@
 	} else if (stat) {
 		return stat;
 	}
+	if (stat) return stat;
 
 	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
 
@@ -1750,7 +1741,7 @@
 	/* Read the multisession information. */
 	if (toc->hdr.first_track != CDROM_LEADOUT) {
 		/* Read the multisession information. */
-		stat = cdrom_read_tocentry (drive, toc->hdr.first_track-1, 1, 1,
+		stat = cdrom_read_tocentry (drive, 0, 1, 1,
 					   (char *)&ms_tmp, sizeof (ms_tmp),
 					    reqbuf);
 		if (stat) return stat;
@@ -1774,21 +1765,38 @@
 
 	/* Now try to get the total cdrom capacity. */
 #if 0
-	stat = cdrom_get_last_written(MKDEV(HWIF(drive)->major,
-				      drive->select.b.unit << PARTN_BITS),
+	stat = cdrom_get_last_written(MKDEV(HWIF(drive)->major, minor,
 				     (long *)&toc->capacity);
 	if (stat)
 #endif
 	stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
 	if (stat) toc->capacity = 0x1fffff;
 
-	HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
-		= (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
-	drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+	/* for general /dev/cdrom like mounting, one big disc */
+	HWIF(drive)->gd->sizes[minor] = (toc->capacity * SECTORS_PER_FRAME) >>
+					(BLOCK_SIZE_BITS - 9);
 
 	/* Remember that we've read this stuff. */
 	CDROM_STATE_FLAGS (drive)->toc_valid = 1;
 
+	/* should be "if multisession", but it does no harm. */
+	if (ntracks == 1)
+		return 0;
+
+	/* setup each minor to respond to a session */
+	minor++;
+	i = toc->hdr.first_track;
+	while ((i <= ntracks) && ((minor & CD_PART_MASK) < CD_PART_MAX)) {
+		drive->part[minor & PARTN_MASK].start_sect = 0;
+ 		drive->part[minor & PARTN_MASK].nr_sects = (toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) << (BLOCK_SIZE_BITS - 9);
+		HWIF(drive)->gd->sizes[minor] = (toc->ent[i].addr.lba *
+			SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
+		blksize_size[HWIF(drive)->major][minor] = CD_FRAMESIZE;
+		i++;
+		minor++;
+	}
+
 	return 0;
 }
 
@@ -1814,46 +1822,6 @@
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
-
-/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */
-static int
-cdrom_mode_sense (ide_drive_t *drive, int pageno, int modeflag,
-                  char *buf, int buflen,
-		  struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.buffer =  buf;
-	pc.buflen = buflen;
-	pc.c[0] = GPCMD_MODE_SENSE_10;
-	pc.c[2] = pageno | (modeflag << 6);
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
-static int
-cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen,
-		   struct atapi_request_sense *reqbuf)
-{
-	struct packet_command pc;
-
-	memset (&pc, 0, sizeof (pc));
-	pc.sense_data = reqbuf;
-
-	pc.buffer =  buf;
-	pc.buflen = buflen;
-	pc.c[0] = GPCMD_MODE_SELECT_10;
-	pc.c[1] = 0x10;
-	pc.c[2] = pageno;
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	return cdrom_queue_packet_command (drive, &pc);
-}
-
 /* ATAPI cdrom drives are free to select the speed you request or any slower
    rate :-( Requesting too fast a speed will _not_ produce an error. */
 static int
@@ -1910,48 +1878,6 @@
 }
 
 
-/* If SLOT<0, unload the current slot.  Otherwise, try to load SLOT. */
-static int
-cdrom_load_unload (ide_drive_t *drive, int slot,
-		   struct atapi_request_sense *reqbuf)
-{
-#if ! STANDARD_ATAPI
-	/* if the drive is a Sanyo 3 CD changer then TEST_UNIT_READY
-           (used in the cdrom_check_status function) is used to 
-           switch CDs instead of LOAD_UNLOAD */
-
-	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
-
-        	if ((slot == 1) || (slot == 2))
-			CDROM_STATE_FLAGS (drive)->sanyo_slot = slot;
-		else if (slot >= 0)
-			CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
-		else
-			return 0;
-
-		return cdrom_check_status (drive, reqbuf);
-
-	}
-	else
-#endif /*not STANDARD_ATAPI */
-	{
-
-		/* ATAPI Rev. 2.2+ standard for requesting switching of
-                   CDs in a multiplatter device */
-
-		struct packet_command pc;
-
-		memset (&pc, 0, sizeof (pc));
-		pc.sense_data = reqbuf;
-
-		pc.c[0] = GPCMD_LOAD_UNLOAD;
-		pc.c[4] = 2 + (slot >= 0);
-		pc.c[8] = slot;
-		return cdrom_queue_packet_command (drive, &pc);
-
-	}
-}
-
 
 /* This gets the mechanism status per ATAPI draft spec 2.6 */
 static int
@@ -2033,37 +1959,34 @@
 static
 int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
 			 unsigned int cmd, unsigned long arg)
-			 
 {
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct cdrom_generic_command cgc;
+	char buffer[16];
+	int stat;
+
+	init_cdrom_command(&cgc, buffer, sizeof(buffer));
 
+	/* These will be moved into the Uniform layer shortly... */
 	switch (cmd) {
  	case CDROMSETSPINDOWN: {
  		char spindown;
- 		char buffer[16];
- 		int stat;
  
  		if (copy_from_user(&spindown, (void *) arg, sizeof(char)))
 			return -EFAULT;
  
- 		stat = cdrom_mode_sense (drive, GPMODE_CDROM_PAGE, 0, buffer,
- 					 sizeof (buffer), NULL);
- 		if (stat) return stat;
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
 
  		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
 
- 		return cdrom_mode_select (drive, GPMODE_CDROM_PAGE, buffer,
- 					  sizeof (buffer), NULL);			
+ 		return cdrom_mode_select(cdi, &cgc);
  	} 
  
  	case CDROMGETSPINDOWN: {
  		char spindown;
- 		char buffer[16];
- 		int stat;
  
- 		stat = cdrom_mode_sense (drive, GPMODE_CDROM_PAGE, 0, buffer,
-                                         sizeof (buffer), NULL);
- 		if (stat) return stat;
+                if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
+			return stat;
  
  		spindown = buffer[11] & 0x0f;
  
@@ -2168,22 +2091,23 @@
 int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
 {
         int stat, attempts = 3;
-        struct {
-                char pad[8];
-                struct atapi_capabilities_page cap;
-        } buf;
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	struct atapi_request_sense reqbuf;
+	struct cdrom_generic_command cgc;
+	struct {
+		char pad[8];
+		struct atapi_capabilities_page cap;
+	} buf;
 	stat=cdrom_select_speed (drive, speed, &reqbuf);
 	if (stat<0)
 		return stat;
 
+	init_cdrom_command(&cgc, &buf, sizeof(buf));
 	/* Now with that done, update the speed fields */
         do {    /* we seem to get stat=0x01,err=0x00 the first time (??) */
                 if (attempts-- <= 0)
                         return 0;
-                stat = cdrom_mode_sense (drive, GPMODE_CAPABILITIES_PAGE, 0,
-                                        (char *)&buf, sizeof (buf), NULL);
+                stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
         } while (stat);
 
         /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
@@ -2202,87 +2126,6 @@
         return 0;
 }
 
-
-static
-int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
-{
-	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	struct cdrom_info *info = drive->driver_data;
-
-	struct atapi_request_sense my_reqbuf;
-	int stat;
-	int nslots, curslot;
-
-	if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) 
-		return -EDRIVE_CANT_DO_THIS;
-
-#if ! STANDARD_ATAPI
-	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
-		nslots = 3;
-		curslot = CDROM_STATE_FLAGS (drive)->sanyo_slot;
-		if (curslot == 3)
-			curslot = 0;
-	} else
-#endif /* not STANDARD_ATAPI */
-	{
-		stat = cdrom_read_changer_info (drive);
-		if (stat)
-			return stat;
-
-		nslots = info->changer_info->hdr.nslots;
-		curslot = info->changer_info->hdr.curslot;
-	}
-
-	if (slot == curslot)
-		return curslot;
-
-	if (slot == CDSL_CURRENT)
-		return curslot;
-
-	if (slot != CDSL_NONE && (slot < 0 || slot >= nslots))
-		return -EINVAL;
-
-	if (drive->usage > 1)
-		return -EBUSY;
-
-	if (slot == CDSL_NONE) {
-		(void) cdrom_load_unload (drive, -1, NULL);
-		cdrom_saw_media_change (drive);
-		(void) cdrom_lockdoor (drive, 0, NULL);
-		return 0;
-	}
-	else {
-		int was_locked;
-
-		if (
-#if ! STANDARD_ATAPI
-		    CDROM_STATE_FLAGS (drive)->sanyo_slot == 0 &&
-#endif
-		    info->changer_info->slots[slot].disc_present == 0) {
-			return -ENOMEDIUM;
-		}
-
-		was_locked = CDROM_STATE_FLAGS (drive)->door_locked;
-		if (was_locked)
-			(void) cdrom_lockdoor (drive, 0, NULL);
-
-		stat = cdrom_load_unload (drive, slot, NULL);
-		cdrom_saw_media_change (drive);
-		if (stat)
-			return stat;
-			
-		stat = cdrom_check_status (drive, &my_reqbuf);
-		if (stat && my_reqbuf.sense_key == NOT_READY)
-			return -ENOENT;
-
-		if (was_locked)
-			(void) cdrom_lockdoor (drive, 1, NULL);
-
-		return slot;
-	}
-}
-
-
 static
 int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
 {
@@ -2307,7 +2150,7 @@
 	}
 
 #if ! STANDARD_ATAPI
-	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0)
+	else if (cdi->sanyo_slot > 0)
 		return CDS_NO_INFO;
 #endif /* not STANDARD_ATAPI */
 
@@ -2383,7 +2226,7 @@
 	}
 
 #if ! STANDARD_ATAPI
-	else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+	else if (cdi->sanyo_slot > 0) {
 		retval = 0;
 	}
 #endif /* not STANDARD_ATAPI */
@@ -2447,7 +2290,7 @@
 	ide_cdrom_tray_move,    /* tray_move */
 	ide_cdrom_lock_door,    /* lock_door */
 	ide_cdrom_select_speed, /* select_speed */
-	ide_cdrom_select_disc, /* select_disc */
+	NULL,			/* select_disc */
 	ide_cdrom_get_last_session, /* get_last_session */
 	ide_cdrom_get_mcn, /* get_mcn */
 	ide_cdrom_reset, /* reset */
@@ -2468,7 +2311,7 @@
 	struct cdrom_device_info *devinfo = &info->devinfo;
 	int minor = (drive->select.b.unit)<<PARTN_BITS;
 
-	devinfo->dev = MKDEV (HWIF(drive)->major, minor);
+	devinfo->dev = MKDEV (HWIF(drive)->major, minor | CD_PART_MASK);
 	devinfo->ops = &ide_cdrom_dops;
 	devinfo->mask = 0;
 	*(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
@@ -2501,8 +2344,11 @@
 static
 int ide_cdrom_probe_capabilities (ide_drive_t *drive)
 {
+	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
 	int stat, nslots = 1, attempts = 3;
- 	struct {
+	struct cdrom_generic_command cgc;
+	struct {
 		char pad[8];
 		struct atapi_capabilities_page cap;
 	} buf;
@@ -2510,11 +2356,19 @@
 	if (CDROM_CONFIG_FLAGS (drive)->nec260)
 		return nslots;
 
+	init_cdrom_command(&cgc, &buf, sizeof(buf));
+	/* we have to cheat a little here. the packet will eventually
+	 * be queued with ide_cdrom_packet(), which extracts the
+	 * drive from cdi->handle. Since this device hasn't been
+	 * registered with the Uniform layer yet, it can't do this.
+	 * Same goes cdi->ops.
+	 */
+	cdi->handle = (ide_drive_t *) drive;
+	cdi->ops = &ide_cdrom_dops;
 	do {	/* we seem to get stat=0x01,err=0x00 the first time (??) */
 		if (attempts-- <= 0)
 			return 0;
-		stat = cdrom_mode_sense (drive, GPMODE_CAPABILITIES_PAGE, 0,
-				 	(char *)&buf, sizeof (buf), NULL);
+		stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
 	} while (stat);
 
 	if (buf.cap.lock == 0)
@@ -2539,7 +2393,7 @@
 		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
 
 #if ! STANDARD_ATAPI
-	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+	if (cdi->sanyo_slot > 0) {
 		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
 		nslots = 3;
 	}
@@ -2619,66 +2473,19 @@
 	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
 }
 
-#ifdef CONFIG_IDECD_SLOTS
-static void ide_cdrom_slot_check (ide_drive_t *drive, int nslots)
-{
-	tracktype tracks;
-	struct cdrom_info *info = drive->driver_data;
-	struct cdrom_device_info *devinfo = &info->devinfo;
-	int slot_count = 0, drive_stat = 0, tmp;
-
-	for (slot_count=0;slot_count<nslots;slot_count++) {
-		(void) ide_cdrom_select_disc(devinfo, slot_count);
-		printk("     CD Slot %d ", slot_count+1);
-
-		drive_stat = ide_cdrom_drive_status(devinfo, slot_count);
-		if (drive_stat<0) {
-			continue;
-		} else switch(drive_stat) {
-			case CDS_DISC_OK:
-				/* use routine in Uniform CD-ROM driver */
-				cdrom_count_tracks(devinfo, &tracks);
-				tmp = tracks.audio + tracks.data +
-					tracks.cdi + tracks.xa;
-				printk(": Disc has %d track%s: ", tmp,
-					(tmp == 1)? "" : "s");
-				printk("%d=data %d=audio %d=Cd-I %d=XA\n",
-					tracks.data, tracks.audio,
-					tracks.cdi, tracks.xa);
-				break;
-			case CDS_NO_DISC:
-				printk("Empty slot.\n");
-				break;
-			case CDS_TRAY_OPEN:
-				printk("CD-ROM tray open.\n");
-				break;
-			case CDS_DRIVE_NOT_READY:
-				printk("CD-ROM drive not ready.\n");
-				break;
-			case CDS_NO_INFO:
-				printk("No Information available.\n");
-				break;
-			default:
-				printk("This Should not happen!\n");
-				break;
-		}
-	}
-	(void) ide_cdrom_select_disc(devinfo, 0);
-}
-#endif /* CONFIG_IDECD_SLOTS */
 
 static
 int ide_cdrom_setup (ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
+	struct cdrom_device_info *cdi = &info->devinfo;
+	int minor = drive->select.b.unit << PARTN_BITS;
 	int nslots;
 
-	kdev_t dev = MKDEV (HWIF (drive)->major,
-			    drive->select.b.unit << PARTN_BITS);
+	kdev_t dev = MKDEV(HWIF(drive)->major, minor);
 
 	set_device_ro (dev, 1);
-	blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] =
-		CD_FRAMESIZE;
+	blksize_size[HWIF(drive)->major][minor] = CD_FRAMESIZE;
 
 	drive->special.all = 0;
 	drive->ready_stat = 0;
@@ -2723,7 +2530,7 @@
 #if ! STANDARD_ATAPI
 	/* by default Sanyo 3 CD changer support is turned off and
            ATAPI Rev 2.2+ standard support for CD changers is used */
-	CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
+	cdi->sanyo_slot = 0;
 
 	CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
 	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
@@ -2775,7 +2582,7 @@
                          (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) ||
                          (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) {
                         /* uses CD in slot 0 when value is set to 3 */
-                        CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
+                        cdi->sanyo_slot = 3;
                 }
 
 
@@ -2796,11 +2603,6 @@
 		return 1;
 	}
 	ide_cdrom_add_settings(drive);
-#ifdef CONFIG_IDECD_SLOTS
-	if (CDROM_CONFIG_FLAGS (drive)->is_changer) {
-		ide_cdrom_slot_check(drive, nslots);
-	}
-#endif /* CONFIG_IDECD_SLOTS */
 	return 0;
 }
 
@@ -2927,11 +2729,12 @@
 	MOD_INC_USE_COUNT;
 	while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
 		/* skip drives that we were told to ignore */
-		if (ignore != NULL)
+		if (ignore != NULL) {
 			if (strstr(ignore, drive->name)) {
 				printk("ide-cd: ignoring drive %s\n", drive->name);
 				continue;
 			}
+		}
 		info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
 		if (info == NULL) {
 			printk ("%s: Can't allocate a cdrom structure\n", drive->name);
diff -u --recursive --new-file v2.3.18/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.3.18/linux/drivers/block/ide-cd.h	Tue Aug 31 17:29:13 1999
+++ linux/drivers/block/ide-cd.h	Mon Oct  4 14:33:28 1999
@@ -96,8 +96,7 @@
 	__u8 media_changed : 1; /* Driver has noticed a media change. */
 	__u8 toc_valid     : 1; /* Saved TOC information is current. */
 	__u8 door_locked   : 1; /* We think that the drive door is locked. */
-	__u8 sanyo_slot    : 2; /* Sanyo 3 CD changer support */
-	__u8 reserved      : 3;
+	__u8 reserved      : 5;
 	byte current_speed;	/* Current speed of the drive */
 };
 #define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags))
@@ -218,14 +217,6 @@
 	} acdsc_reladdr;
 };
 
-
-typedef enum {
-	mechtype_caddy = 0,
-	mechtype_tray  = 1,
-	mechtype_popup = 2,
-	mechtype_individual_changer = 4,
-	mechtype_cartridge_changer  = 5
-} mechtype_t;
 
 
 /* This should probably go into cdrom.h along with the other
diff -u --recursive --new-file v2.3.18/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.3.18/linux/drivers/block/ide-tape.c	Thu Aug  5 18:48:45 1999
+++ linux/drivers/block/ide-tape.c	Sat Oct  2 07:35:14 1999
@@ -3583,8 +3583,9 @@
 	/*
 	 *  These two ide-pci host adapters appear to need this disabled.
 	 */
-	if ((HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) ||
-	    (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343)) {
+	if (HWIF(drive)->pci_dev != NULL && (
+	    (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) ||
+	    (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343))) {
 		drive->dsc_overlap = 0;
 	} else
 #endif  /* CONFIG_BLK_DEV_IDEPCI */
diff -u --recursive --new-file v2.3.18/linux/drivers/block/paride/paride.c linux/drivers/block/paride/paride.c
--- v2.3.18/linux/drivers/block/paride/paride.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/block/paride/paride.c	Sat Oct  2 07:35:14 1999
@@ -25,6 +25,7 @@
 #include <linux/ioport.h>
 #include <linux/string.h>
 #include <linux/spinlock.h>
+#include <linux/wait.h>
 
 #ifdef CONFIG_PARPORT_MODULE
 #define CONFIG_PARPORT
diff -u --recursive --new-file v2.3.18/linux/drivers/block/ps2esdi.c linux/drivers/block/ps2esdi.c
--- v2.3.18/linux/drivers/block/ps2esdi.c	Mon Aug  9 10:23:09 1999
+++ linux/drivers/block/ps2esdi.c	Sat Oct  2 07:35:14 1999
@@ -124,7 +124,7 @@
 static int reset_status;
 static int ps2esdi_slot = -1;
 int tp720esdi = 0;		/* Is it Integrated ESDI of ThinkPad-720? */
-
+int intg_esdi = 0;              /* If integrated adapter */
 struct ps2esdi_i_struct {
 	unsigned int head, sect, cyl, wpcom, lzone, ctl;
 };
@@ -387,7 +387,7 @@
 	reset_status = 0;
 	reset_start = jiffies;
 	while (!reset_status) {
-		esdi_timer.expires = 100;
+		esdi_timer.expires = HZ;
 		esdi_timer.data = 0;
 		esdi_timer.next = esdi_timer.prev = NULL;
 		add_timer(&esdi_timer);
@@ -401,8 +401,9 @@
 
 
 	/* Integrated ESDI Disk and Controller has only one drive! */
-	if (adapterID == INTG_ESDI_ID)	/* if not "normal" PS2 ESDI adapter */
-		ps2esdi_drives = 1;	/* then we have only one physical disk! */
+	if (adapterID == INTG_ESDI_ID) {/* if not "normal" PS2 ESDI adapter */
+		ps2esdi_drives = 1;	/* then we have only one physical disk! */		intg_esdi = 1;
+	}
 
 
 
@@ -560,7 +561,7 @@
 		/*BA */
 		printk("%s: hard reset...\n", DEVICE_NAME);
 		outb_p(CTRL_HARD_RESET, ESDI_CONTROL);
-		expire = jiffies + 200;
+		expire = jiffies + 2*HZ;
 		while (time_before(jiffies, expire));
 		outb_p(1, ESDI_CONTROL);
 	}			/* hard reset */
@@ -812,7 +813,8 @@
 						ps2esdi_info[0].wpcom = 0;
 						ps2esdi_info[0].lzone = reply[3];
 					} else {
-						ps2esdi_drives++;
+						if (!intg_esdi)
+							ps2esdi_drives++;
 					}
 				}
 #ifdef OBSOLETE
diff -u --recursive --new-file v2.3.18/linux/drivers/block/rz1000.c linux/drivers/block/rz1000.c
--- v2.3.18/linux/drivers/block/rz1000.c	Mon Aug  9 10:23:09 1999
+++ linux/drivers/block/rz1000.c	Sat Oct  2 07:35:15 1999
@@ -87,9 +87,9 @@
 {
 	struct pci_dev *dev = NULL;
 
-	while (dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev))
+	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev))!=NULL)
 		init_rz1000 (dev, "RZ1000");
-	while (dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev))
+	while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev))!=NULL)
 		init_rz1000 (dev, "RZ1001");
 }
 
diff -u --recursive --new-file v2.3.18/linux/drivers/block/xd.c linux/drivers/block/xd.c
--- v2.3.18/linux/drivers/block/xd.c	Tue Aug 31 17:29:13 1999
+++ linux/drivers/block/xd.c	Sat Oct  2 07:35:15 1999
@@ -55,7 +55,7 @@
 
 #define XD_DONT_USE_DMA		0  /* Initial value. may be overriden using
 				      "nodma" module option */
-#define XD_INIT_DISK_DELAY	3  /* 30 ms delay during disk initialization */
+#define XD_INIT_DISK_DELAY	(30*HZ/1000)  /* 30 ms delay during disk initialization */
 
 /* Above may need to be increased if a problem with the 2nd drive detection
    (ST11M controller) or resetting a controler (WD) appears */
diff -u --recursive --new-file v2.3.18/linux/drivers/cdrom/Config.in linux/drivers/cdrom/Config.in
--- v2.3.18/linux/drivers/cdrom/Config.in	Mon Apr 12 16:18:28 1999
+++ linux/drivers/cdrom/Config.in	Sat Oct  2 07:35:15 1999
@@ -1,27 +1,27 @@
 #
 # CDROM driver configuration
 #
-tristate 'Aztech/Orchid/Okano/Wearnes/TXC/CyDROM  CDROM support' CONFIG_AZTCD
-tristate 'Goldstar R420 CDROM support' CONFIG_GSCD
-tristate 'Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD
+tristate '  Aztech/Orchid/Okano/Wearnes/TXC/CyDROM  CDROM support' CONFIG_AZTCD
+tristate '  Goldstar R420 CDROM support' CONFIG_GSCD
+tristate '  Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support' CONFIG_SBPCD
 if [ "$CONFIG_SBPCD" = "y" ]; then
-  bool 'Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2
-  if [ "$CONFIG_SBPCD2" = "y" ]; then
-    bool 'Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3
-    if [ "$CONFIG_SBPCD3" = "y" ]; then
-      bool 'Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4
-    fi
-  fi
+   bool '    Matsushita/Panasonic, ... second CDROM controller support' CONFIG_SBPCD2
+   if [ "$CONFIG_SBPCD2" = "y" ]; then
+      bool '      Matsushita/Panasonic, ... third CDROM controller support' CONFIG_SBPCD3
+      if [ "$CONFIG_SBPCD3" = "y" ]; then
+	 bool '        Matsushita/Panasonic, ... fourth CDROM controller support' CONFIG_SBPCD4
+      fi
+   fi
 fi
-tristate 'Mitsumi (standard) [no XA/Multisession] CDROM support' CONFIG_MCD
+tristate '  Mitsumi (standard) [no XA/Multisession] CDROM support' CONFIG_MCD
 if [ "$CONFIG_MCD" != "n" ]; then
    int 'MCD IRQ' CONFIG_MCD_IRQ 11
    hex 'MCD I/O base' CONFIG_MCD_BASE 300
 fi
-tristate 'Mitsumi [XA/MultiSession] CDROM support' CONFIG_MCDX
-tristate 'Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD
-tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206
-tristate 'Sanyo CDR-H94A CDROM support' CONFIG_SJCD
-tristate 'ISP16/MAD16/Mozart soft configurable cdrom interface support' CONFIG_ISP16_CDI
-tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A
-tristate 'Sony CDU535 CDROM support' CONFIG_CDU535
+tristate '  Mitsumi [XA/MultiSession] CDROM support' CONFIG_MCDX
+tristate '  Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD
+tristate '  Philips/LMS CM206 CDROM support' CONFIG_CM206
+tristate '  Sanyo CDR-H94A CDROM support' CONFIG_SJCD
+tristate '  ISP16/MAD16/Mozart soft configurable cdrom interface support' CONFIG_ISP16_CDI
+tristate '  Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A
+tristate '  Sony CDU535 CDROM support' CONFIG_CDU535
diff -u --recursive --new-file v2.3.18/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.3.18/linux/drivers/cdrom/cdrom.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/cdrom/cdrom.c	Sat Oct  2 07:35:15 1999
@@ -164,10 +164,23 @@
   drivers are updated as well.
   -- Various other cleanups.
 
+  3.04 Sep 12, 1999 - Jens Axboe <axboe@image.dk>
+  -- Fixed a couple of possible memory leaks (if an operation failed and
+  we didn't free the buffer before returning the error).
+  -- Integrated Uniform CD Changer handling from Richard Sharman
+  <rsharman@pobox.com>.
+  -- Defined CD_DVD and CD_CHANGER log levels.
+  -- Fixed the CDROMREADxxx ioctls.
+  -- CDROMPLAYTRKIND uses the GPCMD_PLAY_AUDIO_MSF command - too few
+  drives supported it. We loose the index part, however.
+  -- Small modifications to accomodate opens of /dev/hdc1, required
+  for ide-cd to handle multisession discs.
+  -- Export cdrom_mode_sense and cdrom_mode_select.
+  -- init_cdrom_command() for setting up a cgc command.
 -------------------------------------------------------------------------*/
 
-#define REVISION "Revision: 3.03"
-#define VERSION "Id: cdrom.c 3.03 1999/09/01"
+#define REVISION "Revision: 3.04"
+#define VERSION "Id: cdrom.c 3.04 1999/09/14"
 
 /* I use an error-log mask to give fine grain control over the type of
    messages dumped to the system logs.  The available masks include: */
@@ -178,6 +191,8 @@
 #define CD_OPEN		0x8
 #define CD_CLOSE	0x10
 #define CD_COUNT_TRACKS 0x20
+#define CD_CHANGER	0x40
+#define CD_DVD		0x80
 
 /* Define this to remove _all_ the debugging messages */
 /* #define ERRLOGMASK CD_NOTHING */
@@ -210,7 +225,8 @@
 static int autoclose=1;
 static int autoeject=0;
 static int lockdoor = 1;
-static int check_media_type = 1;
+/* will we ever get to use this... sigh. */
+static int check_media_type = 0;
 MODULE_PARM(debug, "i");
 MODULE_PARM(autoclose, "i");
 MODULE_PARM(autoeject, "i");
@@ -311,7 +327,6 @@
 	ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
 	ENSURE(lock_door, CDC_LOCK);
 	ENSURE(select_speed, CDC_SELECT_SPEED);
-	ENSURE(select_disc, CDC_SELECT_DISC);
 	ENSURE(get_last_session, CDC_MULTI_SESSION);
 	ENSURE(get_mcn, CDC_MCN);
 	ENSURE(reset, CDC_RESET);
@@ -374,6 +389,18 @@
 	cdi = topCdromPtr;
 	while (cdi != NULL && cdi->dev != dev)
 		cdi = cdi->next;
+
+	/* we need to find the device this way when IDE devices such
+	 * as /dev/hdc2 are opened. SCSI drives will be found above and
+	 * so will /dev/hdc, for instance.
+	 */
+	if (cdi == NULL) {
+		kdev_t cd_dev = MKDEV(MAJOR(dev), MINOR(dev) | CD_PART_MASK);
+		cdi = topCdromPtr;
+		while (cdi != NULL && cdi->dev != cd_dev)
+			cdi = cdi->next;
+	}
+	
 	return cdi;
 }
 
@@ -579,7 +606,7 @@
 int cdrom_release(struct inode *ip, struct file *fp)
 {
 	kdev_t dev = ip->i_rdev;
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_device_ops *cdo = cdi->ops;
 	int opened_for_data;
 
@@ -610,6 +637,133 @@
 	return 0;
 }
 
+static int cdrom_read_mech_status(struct cdrom_device_info *cdi, 
+				  struct cdrom_changer_info *buf)
+{
+	struct cdrom_generic_command cgc;
+	struct cdrom_device_ops *cdo = cdi->ops;
+	int length;
+
+	length = sizeof(struct cdrom_mechstat_header) +
+		 cdi->capacity * sizeof(struct cdrom_slot);
+
+	init_cdrom_command(&cgc, buf, length);
+	cgc.cmd[0] = GPCMD_MECHANISM_STATUS;
+	cgc.cmd[8] = (length >> 8) & 0xff;
+	cgc.cmd[9] = length & 0xff;
+	return cdo->generic_packet(cdi, &cgc);
+}
+
+static int cdrom_slot_status(struct cdrom_device_info *cdi, int slot)
+{
+	struct cdrom_changer_info info;
+	int ret;
+
+	cdinfo(CD_CHANGER, "entering cdrom_slot_status()\n"); 
+	if (cdi->sanyo_slot)
+		return CDS_NO_INFO;
+	
+	if ((ret = cdrom_read_mech_status(cdi, &info)))
+		return ret;
+
+	if (info.slots[slot].disc_present)
+		return CDS_DISC_OK;
+	else
+		return CDS_NO_DISC;
+
+}
+
+/* Return the number of slots for an ATAPI/SCSI cdrom, 
+ * return 1 if not a changer. 
+ */
+int cdrom_number_of_slots(struct cdrom_device_info *cdi) 
+{
+	int status;
+	int nslots = 1;
+	struct cdrom_changer_info info;
+
+	cdinfo(CD_CHANGER, "entering cdrom_number_of_slots()\n"); 
+	/* cdrom_read_mech_status requires a valid value for capacity: */
+	cdi->capacity = 0; 
+
+	if ((status = cdrom_read_mech_status(cdi, &info)) == 0)
+		nslots = info.hdr.nslots;
+
+	return nslots;
+}
+
+
+/* If SLOT < 0, unload the current slot.  Otherwise, try to load SLOT. */
+static int cdrom_load_unload(struct cdrom_device_info *cdi, int slot) 
+{
+	struct cdrom_generic_command cgc;
+
+	cdinfo(CD_CHANGER, "entering cdrom_load_unload()\n"); 
+	if (cdi->sanyo_slot && slot < 0)
+		return 0;
+
+	init_cdrom_command(&cgc, NULL, 0);
+	cgc.cmd[0] = GPCMD_LOAD_UNLOAD;
+	cgc.cmd[4] = 2 + (slot >= 0);
+	cgc.cmd[8] = slot;
+
+	/* The Sanyo 3 CD changer uses byte 7 of the 
+	GPCMD_TEST_UNIT_READY to command to switch CDs instead of
+	using the GPCMD_LOAD_UNLOAD opcode. */
+	if (cdi->sanyo_slot && slot) {
+		cgc.cmd[0] = GPCMD_TEST_UNIT_READY;
+		cgc.cmd[7] = slot;
+		cdi->sanyo_slot = slot ? slot : 3;
+	}
+
+	return cdi->ops->generic_packet(cdi, &cgc);
+}
+
+int cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
+{
+	struct cdrom_changer_info info;
+	int curslot;
+	int ret;
+
+	cdinfo(CD_CHANGER, "entering cdrom_select_disc()\n"); 
+	if (!CDROM_CAN(CDC_SELECT_DISC))
+		return -EDRIVE_CANT_DO_THIS;
+
+	if (slot == CDSL_NONE) {
+		/* set media changed bits, on both queues */
+		cdi->mc_flags = 0x3;
+		return cdrom_load_unload(cdi, -1);
+	}
+
+	if ((ret = cdrom_read_mech_status(cdi, &info)))
+		return ret;
+
+	curslot = info.hdr.curslot;
+
+	if (cdi->use_count > 1 || keeplocked) {
+		if (slot == CDSL_CURRENT) {
+	    		return curslot;
+		} else {
+			return -EBUSY;
+		}
+	}
+
+	/* Specifying CDSL_CURRENT will attempt to load the currnet slot,
+	which is useful if it had been previously unloaded.
+	Whether it can or not, it returns the current slot. 
+	Similarly,  if slot happens to be the current one, we still
+	try and load it. */
+	if (slot == CDSL_CURRENT)
+		slot = curslot;
+
+	/* set media changed bits on both queues */
+	cdi->mc_flags = 0x3;
+	if ((ret = cdrom_load_unload(cdi, slot)))
+		return ret;
+
+	return slot;
+}
+
 /* We want to make media_changed accessible to the user through an
  * ioctl. The main problem now is that we must double-buffer the
  * low-level implementation, to assure that the VFS and the user both
@@ -636,7 +790,7 @@
 static
 int cdrom_media_changed(kdev_t dev)
 {
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	/* This talks to the VFS, which doesn't like errors - just 1 or 0.  
 	 * Returning "0" is always safe (media hasn't been changed). Do that 
 	 * if the low-level cdrom driver dosn't support media changed. */ 
@@ -734,6 +888,14 @@
 	*curr = requested;
 }
 
+void init_cdrom_command(struct cdrom_generic_command *cgc,
+			void *buffer, int len)
+{
+	memset(cgc, 0, sizeof(*cgc));
+	cgc->buffer = (char *) buffer;
+	cgc->buflen = len;
+}
+
 /* DVD handling */
 
 #define copy_key(dest,src)	memcpy((dest), (src), sizeof(dvd_key))
@@ -758,19 +920,16 @@
 	struct cdrom_generic_command cgc;
 	struct cdrom_device_ops *cdo = cdi->ops;
 
-	memset(&cgc, 0, sizeof(cgc));
-	memset(buf, 0x93, sizeof(buf));
-	cgc.buffer = buf;
+	init_cdrom_command(&cgc, buf, 0);
 
 	switch (ai->type) {
 	/* LU data send */
 	case DVD_LU_SEND_AGID:
-		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_AGID\n"); 
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_AGID\n"); 
 		setup_report_key (&cgc, 0, 0);
 		cgc.buflen = cgc.cmd[9] = 8;
 
-		ret = cdo->generic_packet(cdi, &cgc);
-		if (ret)
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
 			return ret;
 
 		ai->lsa.agid = buf[7] >> 6;
@@ -778,24 +937,24 @@
 		break;
 
 	case DVD_LU_SEND_KEY1:
-		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_KEY1\n"); 
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_KEY1\n"); 
 		setup_report_key (&cgc, ai->lsk.agid, 2);
 		cgc.buflen = cgc.cmd[9] = 12;
 
-		ret = cdo->generic_packet(cdi, &cgc);
-		if (ret) return ret;
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
 
 		copy_key(ai->lsk.key, &buf[4]);
 		/* Returning data, let host change state */
 		break;
 
 	case DVD_LU_SEND_CHALLENGE:
-		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_CHALLENGE\n"); 
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_CHALLENGE\n"); 
 		setup_report_key (&cgc, ai->lsc.agid, 1);
 		cgc.buflen = cgc.cmd[9] = 16;
 
-		ret = cdo->generic_packet(cdi, &cgc);
-		if (ret) return ret;
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
 
 		copy_chal(ai->lsc.chal, &buf[4]);
 		/* Returning data, let host change state */
@@ -803,7 +962,7 @@
 
 	/* Post-auth key */
 	case DVD_LU_SEND_TITLE_KEY:
-		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_TITLE_KEY\n"); 
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_TITLE_KEY\n"); 
 		setup_report_key (&cgc, ai->lstk.agid, 4);
 		cgc.cmd[5] = ai->lstk.lba;
 		cgc.cmd[4] = ai->lstk.lba >> 8;
@@ -811,8 +970,8 @@
 		cgc.cmd[2] = ai->lstk.lba >> 24;
 		cgc.buflen = cgc.cmd[9] = 12;
 
-		ret = cdo->generic_packet(cdi, &cgc);
-		if (ret) return ret;
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
 
 		ai->lstk.cpm = (buf[4] >> 7) & 1;
 		ai->lstk.cp_sec = (buf[4] >> 6) & 1;
@@ -822,32 +981,32 @@
 		break;
 
 	case DVD_LU_SEND_ASF:
-		cdinfo(CD_DO_IOCTL, "entering DVD_LU_SEND_ASF\n"); 
+		cdinfo(CD_DVD, "entering DVD_LU_SEND_ASF\n"); 
 		setup_report_key (&cgc, ai->lsasf.asf, 5);
 		cgc.buflen = cgc.cmd[9] = 8;
-
-		ret = cdo->generic_packet(cdi, &cgc);
-		if (ret) return ret;
+		
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
 
 		ai->lsasf.asf = buf[7] & 1;
 		break;
 
 	/* LU data receive (LU changes state) */
 	case DVD_HOST_SEND_CHALLENGE:
-		cdinfo(CD_DO_IOCTL, "entering DVD_HOST_SEND_CHALLENGE\n"); 
+		cdinfo(CD_DVD, "entering DVD_HOST_SEND_CHALLENGE\n"); 
 		setup_send_key (&cgc, ai->hsc.agid, 1);
 		cgc.buflen = cgc.cmd[9] = 16;
 		buf[1] = 14;
 		copy_chal (&buf[4], ai->hsc.chal);
 
-		ret = cdo->generic_packet(cdi, &cgc);
-		if (ret) return ret;
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
 
 		ai->type = DVD_LU_SEND_KEY1;
 		break;
 
 	case DVD_HOST_SEND_KEY2:
-		cdinfo(CD_DO_IOCTL, "entering DVD_HOST_SEND_KEY2\n"); 
+		cdinfo(CD_DVD, "entering DVD_HOST_SEND_KEY2\n"); 
 		setup_send_key (&cgc, ai->hsk.agid, 3);
 		cgc.buflen = cgc.cmd[9] = 12;
 		buf[1] = 10;
@@ -863,10 +1022,10 @@
 
 	/* Misc */
 	case DVD_INVALIDATE_AGID:
-		cdinfo(CD_DO_IOCTL, "entering DVD_INVALIDATE_AGID\n"); 
+		cdinfo(CD_DVD, "entering DVD_INVALIDATE_AGID\n"); 
 		setup_report_key (&cgc, ai->lsa.agid, 0x3f);
-		ret = cdo->generic_packet(cdi, &cgc);
-		if (ret) return ret;
+		if ((ret = cdo->generic_packet(cdi, &cgc)))
+			return ret;
 		break;
 
 	default:
@@ -886,16 +1045,14 @@
 	struct cdrom_device_ops *cdo = cdi->ops;
 
 	memset(buf, 0, sizeof(buf));
-	memset(&cgc, 0, sizeof(cgc));
-	cgc.buffer = buf;
-	cgc.buflen = sizeof(buf);
+	init_cdrom_command(&cgc, buf, sizeof(buf));
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[6] = s->physical.layer_num;
 	cgc.cmd[7] = s->type;
 	cgc.cmd[9] = cgc.buflen & 0xff;
 
-	ret = cdo->generic_packet(cdi, &cgc);
-	if (ret) return ret;
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		return ret;
 
 	base = &buf[4];
 	layer = &s->physical.layer[0];
@@ -930,17 +1087,14 @@
 	struct cdrom_device_ops *cdo = cdi->ops;
 
 	memset(buf, 0, sizeof(buf));
-	memset(&cgc, 0, sizeof(cgc));
-	cgc.buffer = buf;
-	cgc.buflen = sizeof(buf);
+	init_cdrom_command(&cgc, buf, sizeof(buf));
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[6] = s->copyright.layer_num;
 	cgc.cmd[7] = s->type;
 	cgc.cmd[8] = cgc.buflen >> 8;
 	cgc.cmd[9] = cgc.buflen & 0xff;
 
-	ret = cdo->generic_packet(cdi, &cgc);
-	if (ret)
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
 		return ret;
 
 	s->copyright.cpst = buf[4];
@@ -957,20 +1111,17 @@
 	struct cdrom_device_ops *cdo = cdi->ops;
 
 	memset(buf, 0, sizeof (buf));
-	memset(&cgc, 0, sizeof (cgc));
-
-	cgc.buffer = buf;
-	cgc.buflen = sizeof(buf);
+	init_cdrom_command(&cgc, buf, sizeof(buf));
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[7] = s->type;
 	cgc.cmd[8] = sizeof(buf) >> 8;
 	cgc.cmd[9] = cgc.buflen & 0xff;
 	cgc.cmd[10] = s->disckey.agid << 6;
 
-	ret = cdo->generic_packet(cdi, &cgc);
-	if (ret) return ret;
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		return ret;
 
-	memcpy (s->disckey.value, &buf[4], 2048);
+	memcpy(s->disckey.value, &buf[4], 2048);
 
 	return 0;
 }
@@ -983,15 +1134,13 @@
 	struct cdrom_device_ops *cdo = cdi->ops;
 
 	memset(buf, 0, sizeof (buf));
-	memset(&cgc, 0, sizeof (cgc));
-	cgc.buffer = buf;
-	cgc.buflen = sizeof(buf);
+	init_cdrom_command(&cgc, buf, sizeof(buf));
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[7] = s->type;
 	cgc.cmd[9] = cgc.buflen = 0xff;
 
-	ret = cdo->generic_packet(cdi, &cgc);
-	if (ret) return ret;
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		return ret;
 
 	s->bca.len = buf[0] << 8 | buf[1];
 	if (s->bca.len < 12 || s->bca.len > 188) {
@@ -1011,16 +1160,15 @@
 	struct cdrom_device_ops *cdo = cdi->ops;
 
 	memset(buf, 0, sizeof(buf));
-	memset(&cgc, 0, sizeof(cgc));
-	cgc.buffer = buf;
+	init_cdrom_command(&cgc, buf, sizeof(buf));
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[7] = s->type;
 	cgc.buflen = sizeof(buf);
 	cgc.cmd[8] = sizeof(buf) >> 8;
 	cgc.cmd[9] = cgc.buflen & 0xff;
 
-	ret = cdo->generic_packet(cdi, &cgc);
-	if (ret) return ret;
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		return ret;
 
 	s->manufact.len = buf[0] << 8 | buf[1];
 	if (s->manufact.len < 0 || s->manufact.len > 2048) {
@@ -1057,9 +1205,9 @@
 	}
 }
 
-static int cdrom_mode_sense(struct cdrom_device_info *cdi,
-			    struct cdrom_generic_command *cgc,
-			    int page_code, int page_control)
+int cdrom_mode_sense(struct cdrom_device_info *cdi,
+		     struct cdrom_generic_command *cgc,
+		     int page_code, int page_control)
 {
 	struct cdrom_device_ops *cdo = cdi->ops;
 
@@ -1072,8 +1220,8 @@
 	return cdo->generic_packet(cdi, cgc);
 }
 
-static int cdrom_mode_select(struct cdrom_device_info *cdi,
-			     struct cdrom_generic_command *cgc)
+int cdrom_mode_select(struct cdrom_device_info *cdi,
+		      struct cdrom_generic_command *cgc)
 {
 	struct cdrom_device_ops *cdo = cdi->ops;
 
@@ -1094,13 +1242,12 @@
 	char buffer[32];
 	int ret;
 
-	memset(&cgc, 0, sizeof(cgc));
+	init_cdrom_command(&cgc, buffer, 16);
 	cgc.cmd[0] = GPCMD_READ_SUBCHANNEL;
 	cgc.cmd[1] = 2;     /* MSF addressing */
 	cgc.cmd[2] = 0x40;  /* request subQ data */
 	cgc.cmd[3] = mcn ? 2 : 1;
-	cgc.cmd[8] = cgc.buflen = 16;
-	cgc.buffer = buffer;
+	cgc.cmd[8] = 16;
 
 	if ((ret = cdo->generic_packet(cdi, &cgc)))
 		return ret;
@@ -1138,12 +1285,16 @@
 	cgc->cmd[4] = (lba >>  8) & 0xff;
 	cgc->cmd[5] = lba & 0xff;
 	/* number of blocks */
-	cgc->cmd[8] = nblocks;
+	cgc->cmd[6] = (nblocks >> 16) & 0xff;
+	cgc->cmd[7] = (nblocks >>  8) & 0xff;
+	cgc->cmd[8] = nblocks & 0xff;
 	cgc->buflen = blksize * nblocks;
+	
+	/* set the header info returned */
 	switch (blksize) {
-	case CD_FRAMESIZE_RAW0	: cgc->cmd[9] = 0x58;
-	case CD_FRAMESIZE_RAW1	: cgc->cmd[9] = 0x78;
-	case CD_FRAMESIZE_RAW	: cgc->cmd[9] = 0xf8;
+	case CD_FRAMESIZE_RAW0	: cgc->cmd[9] = 0x58; break;
+	case CD_FRAMESIZE_RAW1	: cgc->cmd[9] = 0x78; break;
+	case CD_FRAMESIZE_RAW	: cgc->cmd[9] = 0xf8; break;
 	default			: cgc->cmd[9] = 0x10;
 	}
 	
@@ -1168,7 +1319,7 @@
 		unsigned int cmd, unsigned long arg)
 {
 	kdev_t dev = ip->i_rdev;
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_device_ops *cdo;
 	int ret;
 
@@ -1235,15 +1386,22 @@
 		}
 
 	case CDROM_MEDIA_CHANGED: {
+		struct cdrom_changer_info info;
+
 		cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); 
 		if (!CDROM_CAN(CDC_MEDIA_CHANGED))
 			return -ENOSYS;
 		if (!CDROM_CAN(CDC_SELECT_DISC) || arg == CDSL_CURRENT)
 			/* cannot select disc or select current disc */
 			return media_changed(cdi, 1);
-		if ((unsigned int)arg >= cdi->capacity)
+		if ((unsigned int)arg >= cdi->capacity) {
 			return -EINVAL;
-		return cdo->media_changed (cdi, arg);
+		}
+
+		if ((ret = cdrom_read_mech_status(cdi, &info)))
+			return ret;
+
+		return info.slots[arg].change;
 		}
 
 	case CDROM_SET_OPTIONS: {
@@ -1286,11 +1444,22 @@
 		cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); 
 		if (!CDROM_CAN(CDC_SELECT_DISC))
 			return -ENOSYS;
-                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) 
-			return cdo->select_disc(cdi, arg);
-		if ((int)arg >= cdi->capacity)
+
+                if ((arg != CDSL_CURRENT) && (arg != CDSL_NONE)) {
+		    if ((int)arg >= cdi->capacity)
 			return -EINVAL;
-		return cdo->select_disc(cdi, arg);
+		}
+		/* cdo->select_disc is a hook to allow a driver-specific
+		 * way of seleting disc.  However, since there is no
+		 * equiv hook for cdrom_slot_status this may not 
+		 * actually be useful...
+		 */
+		if (cdo->select_disc != NULL)
+			return cdo->select_disc(cdi, arg);
+
+		/* no driver specific select_disc(), call our own */
+		cdinfo(CD_CHANGER, "Using generic cdrom_select_disc()\n"); 
+		return cdrom_select_disc(cdi, arg);
 		}
 
 	case CDROMRESET: {
@@ -1304,12 +1473,10 @@
 
 	case CDROM_LOCKDOOR: {
 		cdinfo(CD_DO_IOCTL, "%socking door.\n", arg ? "L" : "Unl");
-		if (!CDROM_CAN(CDC_LOCK)) {
+		if (!CDROM_CAN(CDC_LOCK))
 			return -EDRIVE_CANT_DO_THIS;
-		} else {
-			keeplocked = arg ? 1 : 0;
-			return cdo->lock_door(cdi, arg);
-		}
+		keeplocked = arg ? 1 : 0;
+		return cdo->lock_door(cdi, arg);
 		}
 
 	case CDROM_DEBUG: {
@@ -1352,7 +1519,7 @@
 			return cdo->drive_status(cdi, CDSL_CURRENT);
 		if (((int)arg >= cdi->capacity))
 			return -EINVAL;
-		return cdo->drive_status(cdi, arg);
+		return cdrom_slot_status(cdi, arg);
 		}
 
 	/* Ok, this is where problems start.  The current interface for the
@@ -1383,7 +1550,8 @@
 		if (tracks.audio > 0) {
 			if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) 
 				return CDS_AUDIO;
-			else return CDS_MIXED;
+			else
+				return CDS_MIXED;
 		}
 		if (tracks.cdi > 0) return CDS_XA_2_2;
 		if (tracks.xa > 0) return CDS_XA_2_1;
@@ -1552,15 +1720,13 @@
 		switch (cmd) {
 		case CDROMREADRAW:
 			blocksize = CD_FRAMESIZE_RAW;
-			format = 0;
 			break;
 		case CDROMREADMODE1:
-			blocksize = CD_FRAMESIZE; break;
+			blocksize = CD_FRAMESIZE;
 			format = 2;
 			break;
 		case CDROMREADMODE2:
-			blocksize = CD_FRAMESIZE_RAW0; break;
-			format = 0;
+			blocksize = CD_FRAMESIZE_RAW0;
 			break;
 		}
 		IOCTL_IN(arg, struct cdrom_msf, msf);
@@ -1574,7 +1740,7 @@
 		ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize);
 		if (!ret)
 			if (copy_to_user((char *)arg, cgc.buffer, blocksize))
-				return -EFAULT;
+				ret = -EFAULT;
 		kfree(cgc.buffer);
 		return ret;
 		}
@@ -1606,8 +1772,10 @@
 						   GFP_KERNEL)) == NULL)
 			return -ENOMEM;
 
-		if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW))
+		if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW)) {
+			kfree(cgc.buffer);
 			return -EFAULT;
+		}
 
 		while (ra.nframes > 0) {
 			ret = cdrom_read_block(cdi, &cgc, lba, ra.nframes, 1,
@@ -1642,13 +1810,29 @@
 		}
 	case CDROMPLAYTRKIND: {
 		struct cdrom_ti ti;
+		struct cdrom_tocentry entry;
+
 		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n");
 		IOCTL_IN(arg, struct cdrom_ti, ti);
-		cgc.cmd[0] = GPCMD_PLAYAUDIO_TI;
-		cgc.cmd[4] = ti.cdti_trk0;
-		cgc.cmd[5] = ti.cdti_ind0;
-		cgc.cmd[7] = ti.cdti_trk1;
-		cgc.cmd[8] = ti.cdti_ind1;
+		entry.cdte_format = CDROM_MSF;
+
+		/* get toc entry for start and end track */
+		entry.cdte_track = ti.cdti_trk0;
+		if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry))
+			return -EINVAL;
+
+		cgc.cmd[3] = entry.cdte_addr.msf.minute;
+		cgc.cmd[4] = entry.cdte_addr.msf.second;
+		cgc.cmd[5] = entry.cdte_addr.msf.frame;
+
+		entry.cdte_track = ti.cdti_trk1;
+		if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry))
+			return -EINVAL;
+
+		cgc.cmd[6] = entry.cdte_addr.msf.minute;
+		cgc.cmd[7] = entry.cdte_addr.msf.second;
+		cgc.cmd[8] = entry.cdte_addr.msf.frame;
+		cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
 		return cdo->generic_packet(cdi, &cgc);
 		}
 	case CDROMPLAYMSF: {
@@ -1669,12 +1853,12 @@
 		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
 		IOCTL_IN(arg, struct cdrom_blk, blk);
 		cgc.cmd[0] = GPCMD_PLAY_AUDIO_10;
-		cgc.cmd[2] = blk.from >> 24;
-		cgc.cmd[3] = blk.from >> 16;
-		cgc.cmd[4] = blk.from >> 8;
-		cgc.cmd[5] = blk.from;
-		cgc.cmd[7] = blk.len >> 8;
-		cgc.cmd[8] = blk.len;
+		cgc.cmd[2] = (blk.from >> 24) & 0xff;
+		cgc.cmd[3] = (blk.from >> 16) & 0xff;
+		cgc.cmd[4] = (blk.from >>  8) & 0xff;
+		cgc.cmd[5] = blk.from & 0xff;
+		cgc.cmd[7] = (blk.len >> 8) & 0xff;
+		cgc.cmd[8] = blk.len & 0xff;
 		return cdo->generic_packet(cdi, &cgc);
 		}
 	case CDROMVOLCTRL:
@@ -1688,8 +1872,7 @@
 
 		cgc.buffer = buffer;
 		cgc.buflen = 24;
-		if ((ret = cdrom_mode_sense(cdi, &cgc, 
-				GPMODE_AUDIO_CTL_PAGE, 0)))
+		if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0)))
 		    return ret;
 		
 		/* some drives have longer pages, adjust and reread. */
@@ -1757,11 +1940,10 @@
 			return -ENOSYS;
 		cdinfo(CD_DO_IOCTL, "entering dvd_read_struct\n"); 
 		IOCTL_IN(arg, dvd_struct, s);
-		if ((ret = dvd_read_struct(cdi, &s)) == 0) {
-			IOCTL_OUT(arg, dvd_struct, s);
-			return 0;
-		}
-		return ret;
+		if ((ret = dvd_read_struct(cdi, &s)))
+			return ret;
+		IOCTL_OUT(arg, dvd_struct, s);
+		return 0;
 		}
 
 	case DVD_AUTH: {
@@ -1783,10 +1965,8 @@
 		IOCTL_IN(arg, struct cdrom_generic_command, cgc);
 		cgc.buffer = kmalloc(cgc.buflen, GFP_KERNEL);
 		ret = cdo->generic_packet(cdi, &cgc);
-		if (copy_to_user((void*)arg, cgc.buffer, cgc.buflen)) {
-			kfree(cgc.buffer);
-			return -EFAULT;
-		}
+		if (copy_to_user((void*)arg, cgc.buffer, cgc.buflen))
+			ret = -EFAULT;
 		kfree(cgc.buffer);
 		return ret;
 		}
@@ -1810,23 +1990,22 @@
 }
 
 static int cdrom_get_track_info(kdev_t dev, __u16 track, __u8 type,
-				 track_information *ti)
+				track_information *ti)
 {
-        struct cdrom_device_info *cdi = cdrom_find_device (dev);
+        struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_device_ops *cdo = cdi->ops;
 	struct cdrom_generic_command cgc;
 	int ret;
 
-	memset(&cgc, 0, sizeof(cgc));
+	init_cdrom_command(&cgc, ti, 8);
 	cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
 	cgc.cmd[1] = type & 3;
 	cgc.cmd[4] = (track & 0xff00) >> 8;
 	cgc.cmd[5] = track & 0xff;
-	cgc.cmd[8] = cgc.buflen = 8;
-	cgc.buffer = (char *)ti;
+	cgc.cmd[8] = 8;
 
-	ret = cdo->generic_packet(cdi, &cgc);
-	if (ret) return ret;
+	if ((ret = cdo->generic_packet(cdi, &cgc)))
+		return ret;
 	
 	cgc.cmd[8] = cgc.buflen = be16_to_cpu(ti->track_information_length) +
 		     sizeof(ti->track_information_length);
@@ -1835,16 +2014,15 @@
 
 static int cdrom_get_disc_info(kdev_t dev, disc_information *di)
 {
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_device_ops *cdo = cdi->ops;
 	struct cdrom_generic_command cgc;
 
 	/* set up command and get the disc info */
-	memset(&cgc, 0, sizeof(cgc));
+	init_cdrom_command(&cgc, di, sizeof(*di));
 	memset(di, 0, sizeof(disc_information));
 	cgc.cmd[0] = GPCMD_READ_DISC_INFO;
-	cgc.cmd[8] = cgc.buflen = sizeof(*di);
-	cgc.buffer = (char *)di;
+	cgc.cmd[8] = cgc.buflen;
 
 	return cdo->generic_packet(cdi, &cgc);
 }
@@ -1854,7 +2032,7 @@
    file system. */
 int cdrom_get_last_written(kdev_t dev, long *last_written)
 {	
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	struct cdrom_tocentry toc;
 	disc_information di;
 	track_information ti;
@@ -1906,7 +2084,7 @@
 /* return the next writable block. also for udf file system. */
 int cdrom_get_next_writable(kdev_t dev, long *next_writable)
 {
-	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	struct cdrom_device_info *cdi = cdrom_find_device(dev);
 	disc_information di;
 	track_information ti;
 	__u16 last_track;
@@ -1953,6 +2131,11 @@
 EXPORT_SYMBOL(register_cdrom);
 EXPORT_SYMBOL(unregister_cdrom);
 EXPORT_SYMBOL(cdrom_fops);
+EXPORT_SYMBOL(cdrom_number_of_slots);
+EXPORT_SYMBOL(cdrom_select_disc);
+EXPORT_SYMBOL(cdrom_mode_select);
+EXPORT_SYMBOL(cdrom_mode_sense);
+EXPORT_SYMBOL(init_cdrom_command);
 
 #ifdef CONFIG_SYSCTL
 
diff -u --recursive --new-file v2.3.18/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.3.18/linux/drivers/char/Config.in	Fri Sep 10 23:57:29 1999
+++ linux/drivers/char/Config.in	Fri Oct  1 08:49:19 1999
@@ -69,7 +69,7 @@
   dep_tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE $CONFIG_BUSMOUSE
   dep_tristate 'Logitech busmouse support' CONFIG_LOGIBUSMOUSE $CONFIG_BUSMOUSE
   dep_tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE $CONFIG_BUSMOUSE
-  if [ "$CONFIG_PPC" = "y" ; then
+  if [ "$CONFIG_PPC" = "y" ] ; then
     dep_tristate 'Apple Desktop Bus mouse support' CONFIG_ADBMOUSE $CONFIG_BUSMOUSE
   fi
 fi
diff -u --recursive --new-file v2.3.18/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.3.18/linux/drivers/char/bttv.c	Tue Aug 31 17:29:13 1999
+++ linux/drivers/char/bttv.c	Wed Sep 29 15:32:15 1999
@@ -29,9 +29,7 @@
 #include <linux/major.h>
 #include <linux/malloc.h>
 #include <linux/mm.h>
-#if LINUX_VERSION_CODE >= 0x020100
 #include <linux/poll.h>
-#endif
 #include <linux/pci.h>
 #include <linux/signal.h>
 #include <asm/io.h>
@@ -44,36 +42,8 @@
 #include <linux/wrapper.h>
 #include <linux/interrupt.h>
 
-#if LINUX_VERSION_CODE >= 0x020100
 #include <asm/uaccess.h>
 #include <linux/vmalloc.h>
-#else
-#include <linux/bios32.h>
-#define mdelay(x) udelay((x)*1000)
-#define signal_pending(current)  (current->signal & ~current->blocked)
-#define sigfillset(set)
-
-static inline int time_before(unsigned long a, unsigned long b)
-{
-	return((long)((a) - (b)) < 0L);
-}
-
-static inline unsigned long
-copy_to_user(void *to, const void *from, unsigned long n)
-{
-        memcpy_tofs(to,from,n);
-	return 0;
-}
-
-static inline unsigned long
-copy_from_user(void *to, const void *from, unsigned long n)
-{
-        memcpy_fromfs(to,from,n);
-	return 0;
-}
-#define ioremap vremap 
-#define iounmap vfree
-#endif
 
 #include <linux/videodev.h>
 #include <linux/i2c.h>
@@ -83,19 +53,10 @@
 #define DEBUG(x)		/* Debug driver */	
 #define IDEBUG(x)		/* Debug interrupt handler */
 
-#if LINUX_VERSION_CODE >= 0x020117
-MODULE_PARM(vidmem,"i");
-MODULE_PARM(triton1,"i");
-MODULE_PARM(remap,"1-4i");
-MODULE_PARM(radio,"1-4i");
-MODULE_PARM(card,"1-4i");
-MODULE_PARM(pll,"1-4i");
-#endif
 
 /* Anybody who uses more than four? */
 #define BTTV_MAX 4
 
-static int find_vga(void);
 static void bt848_set_risc_jmps(struct bttv *btv);
 
 static unsigned int vidmem=0;   /* manually set video mem address */
@@ -243,6 +204,12 @@
 	}
 }
 
+MODULE_PARM(vidmem,"i");
+MODULE_PARM(triton1,"i");
+MODULE_PARM(remap,"1-4i");
+MODULE_PARM(radio,"1-4i");
+MODULE_PARM(card,"1-4i");
+MODULE_PARM(pll,"1-4i");
 
 
 /*
@@ -403,7 +370,7 @@
 	}
 }
 
-void attach_inform(struct i2c_bus *bus, int id)
+static void attach_inform(struct i2c_bus *bus, int id)
 {
         struct bttv *btv = (struct bttv*)bus->data;
         
@@ -422,7 +389,7 @@
 	}
 }
 
-void detach_inform(struct i2c_bus *bus, int id)
+static void detach_inform(struct i2c_bus *bus, int id)
 {
         struct bttv *btv = (struct bttv*)bus->data;
 
@@ -563,7 +530,7 @@
         /* MIRO PCTV pro */
         { 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10}},
 	/* ADS Technologies Channel Surfer TV (and maybe TV+FM) */
-	{ 3, 4, 2, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0},
+	{ 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0},
         /* AVerMedia TVCapture 98 */
 	{ 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0},
         /* Aimslab VHX */
@@ -1591,20 +1558,18 @@
 static int bttv_open(struct video_device *dev, int flags)
 {
 	struct bttv *btv = (struct bttv *)dev;
-	int users, i;
-
+	int i, ret;
+	
+	ret = -EBUSY;
+	down(&btv->lock);
         if (btv->user)
-                return -EBUSY;
-        audio(btv, AUDIO_UNMUTE);
-        for (i=users=0; i<bttv_num; i++)
-                users+=bttvs[i].user;
-        if (users==1)
-                find_vga();
-        btv->fbuffer=NULL;
-        if (!btv->fbuffer)
-                btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF);
+		goto out_unlock;
+
+	btv->fbuffer= (unsigned char *) rvmalloc(2*BTTV_MAX_FBUF);
+	ret = -ENOMEM;
         if (!btv->fbuffer)
-                return -EINVAL;
+		goto out_unlock;
+	audio(btv, AUDIO_UNMUTE);
         btv->grabbing = 0;
         btv->grab = 0;
         btv->lastgrab = 0;
@@ -1612,14 +1577,20 @@
                 btv->frame_stat[i] = GBUFFER_UNUSED;
 
         btv->user++;
+        up(&btv->lock);
         MOD_INC_USE_COUNT;
         return 0;   
+
+ out_unlock:
+	up(&btv->lock);
+	return ret;
 }
 
 static void bttv_close(struct video_device *dev)
 {
 	struct bttv *btv=(struct bttv *)dev;
-	
+
+	down(&btv->lock);	
 	btv->user--;
 	audio(btv, AUDIO_INTERN);
 	btv->cap&=~3;
@@ -1649,6 +1620,7 @@
 	if(btv->fbuffer)
 		rvfree((void *) btv->fbuffer, 2*BTTV_MAX_FBUF);
 	btv->fbuffer=0;
+	up(&btv->lock);
 	MOD_DEC_USE_COUNT;  
 }
 
@@ -1769,14 +1741,16 @@
                         
                         if (v.channel>tvcards[btv->type].video_inputs)
                                 return -EINVAL;
-			bt848_muxsel(btv, v.channel);
 			if(v.norm!=VIDEO_MODE_PAL&&v.norm!=VIDEO_MODE_NTSC
 			   &&v.norm!=VIDEO_MODE_SECAM)
 				return -EOPNOTSUPP;
+			down(&btv->lock);
+			bt848_muxsel(btv, v.channel);
 			btv->win.norm = v.norm;
                         make_vbitab(btv);
 			bt848_set_winsize(btv);
 			btv->channel=v.channel;
+			up(&btv->lock);
 			return 0;
 		}
 		case VIDIOCGTUNER:
@@ -1821,7 +1795,9 @@
 			   &&v.mode!=VIDEO_MODE_SECAM)
 				return -EOPNOTSUPP;
  			btv->win.norm = v.mode;
+ 			down(&btv->lock);
  			bt848_set_winsize(btv);
+ 			up(&btv->lock);
 			return 0;
 		}
 		case VIDIOCGPICT:
@@ -1848,6 +1824,7 @@
 			int format;
 			if(copy_from_user(&p, arg,sizeof(p)))
 				return -EFAULT;
+			down(&btv->lock);
 			/* We want -128 to 127 we get 0-65535 */
 			bt848_bright(btv, (p.brightness>>8)-128);
 			/* 0-511 for the colour */
@@ -1865,6 +1842,7 @@
                                 if (fmtbppx2[format&0x0f]/2 == btv->win.bpp)
                                         btv->win.color_fmt = format;
                         }
+			up(&btv->lock);
                 	return 0;
 		}
 		case VIDIOCSWIN:
@@ -1878,7 +1856,9 @@
 				
  			if(vw.flags || vw.width < 16 || vw.height < 16) 
                         {
+                        	down(&btv->lock);
  			        bt848_cap(btv,0);
+ 			        up(&btv->lock);
 				return -EINVAL;
                         }		
 			if (btv->win.bpp < 4) 
@@ -1888,6 +1868,8 @@
 				i = vw.x - i;
 				vw.width -= i;
 			}
+			
+			down(&btv->lock);
 			btv->win.x=vw.x;
 			btv->win.y=vw.y;
 			btv->win.width=vw.width;
@@ -1902,6 +1884,8 @@
 			
 			bt848_cap(btv,0);
 			bt848_set_winsize(btv);
+			
+			up(&btv->lock);
 
 			/*
 			 *	Do any clips.
@@ -1925,11 +1909,13 @@
 					return -EFAULT;
 				}
 			}
+			down(&btv->lock);
 			make_clip_tab(btv, vcp, vw.clipcount);
 			if (vw.clipcount != 0)
 				vfree(vcp);
 			if(on && btv->win.vidadr!=0)
 				bt848_cap(btv,1);
+			up(&btv->lock);
 			return 0;
 		}
 		case VIDIOCGWIN:
@@ -1953,17 +1939,17 @@
 			int v;
 			if(copy_from_user(&v, arg,sizeof(v)))
 				return -EFAULT;
+		        if(v!=0 && (btv->win.vidadr==0 || btv->win.width==0
+				   || btv->win.height==0))
+				   return -EINVAL;
+
+			down(&btv->lock);				   
 			if(v==0)
-			{
 				bt848_cap(btv,0);
-			}
 			else
-			{
-			        if(btv->win.vidadr==0 || btv->win.width==0
-				   || btv->win.height==0)
-				  return -EINVAL;
 				bt848_cap(btv,1);
-			}
+			up(&btv->lock);
+
 			return 0;
 		}
 		case VIDIOCGFBUF:
@@ -1995,6 +1981,7 @@
 				v.depth!=24 && v.depth!=32 && v.width > 16 &&
 				v.height > 16 && v.bytesperline > 16)
 				return -EINVAL;
+			down(&btv->lock);
                         if (v.base)
                         	btv->win.vidadr=(unsigned long)v.base;
 			btv->win.sheight=v.height;
@@ -2006,6 +1993,7 @@
 			DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
 				v.base, v.width,v.height, btv->win.bpp, btv->win.bpl));
 			bt848_set_winsize(btv);
+			up(&btv->lock);
 			return 0;		
 		}
 		case VIDIOCKEY:
@@ -2072,13 +2060,17 @@
 			struct video_audio v;
 			if(copy_from_user(&v,arg, sizeof(v)))
 				return -EFAULT;
+			down(&btv->lock);
 			if(v.flags&VIDEO_AUDIO_MUTE)
 				audio(btv, AUDIO_MUTE);
  			/* One audio source per tuner */
  			/* if(v.audio!=0) */
 			/* ADSTech TV card has more than one */
 			if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs)
+			{
+				up(&btv->lock);
 				return -EINVAL;
+			}
 			bt848_muxsel(btv,v.audio);
 			if(!(v.flags&VIDEO_AUDIO_MUTE))
 				audio(btv, AUDIO_UNMUTE);
@@ -2142,6 +2134,7 @@
                                 		MSP_SET_STEREO,&(v.mode));
 			}
 			btv->audio_dev=v;
+			up(&btv->lock);
 			return 0;
 		}
 
@@ -2168,26 +2161,21 @@
                         return 0;
 
 		case BTTV_WRITEE:
-#if LINUX_VERSION_CODE >= 0x020100
-			if(!capable(CAP_SYS_ADMIN)
-			|| !capable(CAP_SYS_RAWIO))
-#else
-			if(!suser())
-#endif
+			if(!capable(CAP_SYS_ADMIN))
 				return -EPERM;
 			if(copy_from_user((void *) eedata, (void *) arg, 256))
 				return -EFAULT;
+			down(&btv->lock);
 			writeee(&(btv->i2c), eedata);
+			up(&btv->lock);
 			return 0;
 
 		case BTTV_READEE:
-#if LINUX_VERSION_CODE >= 0x020100
 			if(!capable(CAP_SYS_ADMIN))
-#else
-			if(!suser())
-#endif
 				return -EPERM;
+			down(&btv->lock);
 			readee(&(btv->i2c), eedata);
+			up(&btv->lock);
 			if(copy_to_user((void *) arg, (void *) eedata, 256))
 				return -EFAULT;
 			break;
@@ -2200,28 +2188,29 @@
       
                 case BTTV_PLLSET: {
                         struct bttv_pll_info p;
-#if LINUX_VERSION_CODE >= 0x020100
 			if(!capable(CAP_SYS_ADMIN))
-#else
-			if(!suser())
-#endif
                                 return -EPERM;
                         if(copy_from_user(&p , (void *) arg, sizeof(btv->pll)))
 				return -EFAULT;
+			down(&btv->lock);
                         btv->pll.pll_ifreq = p.pll_ifreq;
                         btv->pll.pll_ofreq = p.pll_ofreq;
                         btv->pll.pll_crystal = p.pll_crystal;
-
+			up(&btv->lock);
 			break;
                 }						
 	        case VIDIOCMCAPTURE:
 		{
                         struct video_mmap vm;
+                        int v;
 			if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm)))
 				return -EFAULT;
                         if (btv->frame_stat[vm.frame] == GBUFFER_GRABBING)
                                 return -EBUSY;
-		        return vgrab(btv, &vm);
+                        down(&btv->lock);
+		        v=vgrab(btv, &vm);
+		        up(&btv->lock);
+				return v;
 		}
 		
 		case VIDIOCGMBUF:
@@ -2303,9 +2292,8 @@
  *    But e.g. pte_alloc() does not work in modules ... :-(
  */
  
-static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size)
+static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size)
 {
-	struct bttv *btv=(struct bttv *)dev;
         unsigned long start=(unsigned long) adr;
 	unsigned long page,pos;
 
@@ -2329,6 +2317,17 @@
 	return 0;
 }
 
+static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+	struct bttv *btv=(struct bttv *)dev;
+	int r;
+	
+	down(&btv->lock);
+	r=do_bttv_mmap(btv, adr, size);
+	up(&btv->lock);
+	return r;
+}
+
 static struct video_device bttv_template=
 {
 	"UNSET",
@@ -2394,7 +2393,6 @@
 	return count;
 }
 
-#if LINUX_VERSION_CODE >= 0x020100
 static unsigned int vbi_poll(struct video_device *dev, struct file *file,
 	poll_table *wait)
 {
@@ -2408,15 +2406,16 @@
 
 	return mask;
 }
-#endif
 
 static int vbi_open(struct video_device *dev, int flags)
 {
 	struct bttv *btv=(struct bttv *)(dev-2);
 
+	down(&btv->lock);
 	btv->vbip=VBIBUF_SIZE;
 	btv->cap|=0x0c;
 	bt848_set_risc_jmps(btv);
+	up(&btv->lock);
 
 	MOD_INC_USE_COUNT;
 	return 0;   
@@ -2425,9 +2424,11 @@
 static void vbi_close(struct video_device *dev)
 {
 	struct bttv *btv=(struct bttv *)(dev-2);
-  
+
+	down(&btv->lock);  
 	btv->cap&=~0x0c;
 	bt848_set_risc_jmps(btv);
+	up(&btv->lock);
 
 	MOD_DEC_USE_COUNT;  
 }
@@ -2463,25 +2464,34 @@
 {
 	struct bttv *btv = (struct bttv *)(dev-1);
 
+	down(&btv->lock);
 	if (btv->user)
-		return -EBUSY;
+		goto busy_unlock;
 	btv->user++;
+	
 	set_freq(btv,400*16);
 	btv->radio = 1;
 	bt848_muxsel(btv,0);
 	audio(btv, AUDIO_UNMUTE);
+	up(&btv->lock);
 	
 	MOD_INC_USE_COUNT;
 	return 0;   
+
+ busy_unlock:
+	up(&btv->lock);
+	return -EBUSY;
 }
 
 static void radio_close(struct video_device *dev)
 {
 	struct bttv *btv=(struct bttv *)(dev-1);
   
+	down(&btv->lock);
 	btv->user--;
 	btv->radio = 0;
 	/*audio(btv, AUDIO_MUTE);*/
+	up(&btv->lock);
 	MOD_DEC_USE_COUNT;  
 }
 
@@ -2571,233 +2581,6 @@
 };
 
 
-struct vidbases 
-{
-	unsigned short vendor, device;
-	char *name;
-	uint badr;
-};
-
-static struct vidbases vbs[] = {
-        { PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D,
-                "Alliance AT3D", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215CT222,
-                "ATI MACH64 CT", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX,
-		"ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0},
- 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215GT,
-                "ATI MACH64 GT", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
-		"DEC DC21030", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
-		"Matrox Millennium", PCI_BASE_ADDRESS_1},
-	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2,
-		"Matrox Millennium II", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP,
-		"Matrox Millennium II AGP", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
-	{ PCI_VENDOR_ID_MATROX, 0x0521, "Matrox G200", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, 
-		"Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128_2, 
-		"Number Nine Imagine 128 Series 2", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_NVIDIA_SGS, PCI_DEVICE_ID_NVIDIA_SGS_RIVA128,
-                "Riva128", PCI_BASE_ADDRESS_1},
-};
-
-
-/* DEC TGA offsets stolen from XFree-3.2 */
-
-static uint dec_offsets[4] = {
-	0x200000,
-	0x804000,
-	0,
-	0x1004000
-};
-
-#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases))
-
-/* Scan for PCI display adapter
-   if more than one card is present the last one is used for now */
-
-#if LINUX_VERSION_CODE >= 0x020100
-
-static int find_vga(void)
-{
-	unsigned short badr;
-	int found = 0, i, tga_type;
-	unsigned int vidadr=0;
-	struct pci_dev *dev;
-
-
-	for (dev = pci_devices; dev != NULL; dev = dev->next) 
-	{
-		if (dev->class != PCI_CLASS_NOT_DEFINED_VGA &&
-			((dev->class) >> 16 != PCI_BASE_CLASS_DISPLAY))
-		{
-			continue;
-		}
-		if (PCI_FUNC(dev->devfn) != 0)
-			continue;
-
-		badr=0;
-		printk(KERN_INFO "bttv: PCI display adapter: ");
-		for (i=0; i<NR_CARDS; i++) 
-		{
-			if (dev->vendor == vbs[i].vendor) 
-			{
-				if (vbs[i].device) 
-					if (vbs[i].device!=dev->device)
-						continue;
-				printk("%s.\n", vbs[i].name);
-				badr=vbs[i].badr;
-				break;
-			}
-		}
-		if (!badr) 
-		{
-			printk(KERN_ERR "bttv: Unknown video memory base address.\n");
-			continue;
-		}
-		pci_read_config_dword(dev, badr, &vidadr);
-		if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) 
-		{
-			printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
-			printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
-			continue;
-		}
-		vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
-		if (!vidadr) 
-		{
-			printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!");
-			continue;
-		}
-     
-		if (dev->vendor == PCI_VENDOR_ID_DEC &&
-			dev->device == PCI_DEVICE_ID_DEC_TGA) 
-		{
-			tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
-			if (tga_type != 0 && tga_type != 1 && tga_type != 3) 
-			{
-				printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
-				found--;
-			}
-			vidadr+=dec_offsets[tga_type];
-		}
-		DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
-		DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", dev->devfn));
-		found++;
-	}
-  
-	if (vidmem)
-	{
-		vidadr=vidmem<<20;
-		printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
-		found=1;
-	}
-	for (i=0; i<BTTV_MAX; i++)
-		bttvs[i].win.vidadr=vidadr;
-
-	return found;
-}
-
-#else
-static int find_vga(void)
-{
-	unsigned int devfn, class, vendev;
-	unsigned short vendor, device, badr;
-	int found=0, bus=0, i, tga_type;
-	unsigned int vidadr=0;
-
-
-	for (devfn = 0; devfn < 0xff; devfn++) 
-	{
-		if (PCI_FUNC(devfn) != 0)
-			continue;
-		pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev);
-		if (vendev == 0xffffffff || vendev == 0x00000000) 
-			continue;
-		pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
-		pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device);
-		pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
-		class = class >> 16;
-/*		if (class == PCI_CLASS_DISPLAY_VGA) {*/
-		if ((class>>8) == PCI_BASE_CLASS_DISPLAY ||
-			/* Number 9 GXE64Pro needs this */
-			class == PCI_CLASS_NOT_DEFINED_VGA) 
-		{
-			badr=0;
-			printk(KERN_INFO "bttv: PCI display adapter: ");
-			for (i=0; i<NR_CARDS; i++) 
-			{
-				if (vendor==vbs[i].vendor) 
-				{
-					if (vbs[i].device) 
-						if (vbs[i].device!=device)
-							continue;
-					printk("%s.\n", vbs[i].name);
-					badr=vbs[i].badr;
-					break;
-				}
-			}
-                        if (NR_CARDS == i)
-                            printk("UNKNOWN.\n");
-			if (!badr) 
-			{
-				printk(KERN_ERR "bttv: Unknown video memory base address.\n");
-				continue;
-			}
-			pcibios_read_config_dword(bus, devfn, badr, &vidadr);
-			if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) 
-			{
-				printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
-				printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
-				continue;
-			}
-			vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
-			if (!vidadr) 
-			{
-				printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!\n");
-				continue;
-			}
-      
-			if (vendor==PCI_VENDOR_ID_DEC)
-				if (device==PCI_DEVICE_ID_DEC_TGA) 
-			{
-				tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
-				if (tga_type != 0 && tga_type != 1 && tga_type != 3) 
-				{
-					printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
-					found--;
-				}
-				vidadr+=dec_offsets[tga_type];
-			}
-
-			DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
-			DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn));
-			found++;
-		}
-	}
-  
-	if (vidmem)
-	{
-                if (vidmem < 0x1000)
-                        vidadr=vidmem<<20;
-                else
-                        vidadr=vidmem;
-		printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
-		found=1;
-	}
-	for (i=0; i<BTTV_MAX; i++)
-		bttvs[i].win.vidadr=vidadr;
-
-	return found;
-}
-#endif
-
 
 #define  TRITON_PCON	           0x50 
 #define  TRITON_BUS_CONCURRENCY   (1<<0)
@@ -2806,8 +2589,6 @@
 #define  TRITON_PEER_CONCURRENCY  (1<<3)
   
 
-#if LINUX_VERSION_CODE >= 0x020100
-
 static void handle_chipset(void)
 {
 	struct pci_dev *dev = NULL;
@@ -2839,110 +2620,8 @@
 
 		printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
 		triton1=BT848_INT_ETBF;
-			
-#if 0			
-		/* The ETBF bit SHOULD make all this unnecessary */
-		/* 430FX (Triton I) freezes with bus concurrency on -> switch it off */
-
-		pci_read_config_byte(dev, TRITON_PCON, &b);
-		bo=b;
-		DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));
-		if(!(b & TRITON_BUS_CONCURRENCY)) 
-		{
-			printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
-			b |= TRITON_BUS_CONCURRENCY;
-		}
-		if(b & TRITON_PEER_CONCURRENCY) 
-		{
-			printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n");
-			b &= ~TRITON_PEER_CONCURRENCY;
-		}
-		if(!(b & TRITON_STREAMING)) 
-		{
-			printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n");
-			b |=  TRITON_STREAMING;
-		}
-
-		if (b!=bo) 
-		{
-			pci_write_config_byte(dev, TRITON_PCON, b); 
-			printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
-		}
-#endif
 	}
 }
-#else
-static void handle_chipset(void)
-{
-	int index;
-  
-	for (index = 0; index < 8; index++)
-	{
-		unsigned char bus, devfn;
-		unsigned char b;
-    
-		/* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */
-		
-		if (!pcibios_find_device(PCI_VENDOR_ID_SI, 
-					 PCI_DEVICE_ID_SI_496, 
-					 index, &bus, &devfn))
-		{
-			printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n");
-		}			
-
-		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, 
-					 PCI_DEVICE_ID_INTEL_82441,
-					 index, &bus, &devfn)) 
-		{
-			pcibios_read_config_byte(bus, devfn, 0x53, &b);
-			DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
-			DEBUG(printk("bufcon=0x%02x\n",b));
-		}
-
-		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437,
-			    index, &bus, &devfn)) 
-		{
-			printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
-			triton1=BT848_INT_ETBF;
-			
-#if 0			
-			/* The ETBF bit SHOULD make all this unnecessary */
-			/* 430FX (Triton I) freezes with bus concurrency on -> switch it off */
-			{   
-			        unsigned char bo;
-
-				pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b);
-				bo=b;
-				DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));
-
-				if(!(b & TRITON_BUS_CONCURRENCY)) 
-				{
-					printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
-					b |= TRITON_BUS_CONCURRENCY;
-				}
-
-				if(b & TRITON_PEER_CONCURRENCY) 
-				{
-					printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n");
-					b &= ~TRITON_PEER_CONCURRENCY;
-				}
-				if(!(b & TRITON_STREAMING)) 
-				{
-					printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n");
-					b |=  TRITON_STREAMING;
-				}
-
-				if (b!=bo) 
-				{
-					pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); 
-					printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
-				}
-			}
-#endif
-		}
-	}
-}
-#endif
 
 static void init_tea6300(struct i2c_bus *bus) 
 {
@@ -2956,14 +2635,14 @@
 
 static void init_tea6320(struct i2c_bus *bus)
 {
-  I2CWrite(bus, I2C_TEA6300, TEA6320_V, 0x28, 1); /* master volume */
-  I2CWrite(bus, I2C_TEA6300, TEA6320_FFL, 0x28, 1); /* volume left 0dB  */
-  I2CWrite(bus, I2C_TEA6300, TEA6320_FFR, 0x28, 1); /* volume right 0dB */
-  I2CWrite(bus, I2C_TEA6300, TEA6320_FRL, 0x28, 1); /* volume rear left 0dB  */
-  I2CWrite(bus, I2C_TEA6300, TEA6320_FRR, 0x28, 1); /* volume rear right 0dB */
-  I2CWrite(bus, I2C_TEA6300, TEA6320_BA, 0x11, 1); /* bass 0dB         */
-  I2CWrite(bus, I2C_TEA6300, TEA6320_TR, 0x11, 1); /* treble 0dB       */
-  I2CWrite(bus, I2C_TEA6300, TEA6320_S, TEA6320_S_GMU, 1); /* mute off input A */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_V, 0x28, 1); /* master volume */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_FFL, 0x28, 1); /* volume left 0dB  */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_FFR, 0x28, 1); /* volume right 0dB */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_FRL, 0x28, 1); /* volume rear left 0dB  */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_FRR, 0x28, 1); /* volume rear right 0dB */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_BA, 0x11, 1); /* bass 0dB         */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_TR, 0x11, 1); /* treble 0dB       */
+	I2CWrite(bus, I2C_TEA6300, TEA6320_S, TEA6320_S_GMU, 1); /* mute off input A */
 }
 
 static void init_tda8425(struct i2c_bus *bus) 
@@ -3093,16 +2772,16 @@
         
         if (I2CRead(&(btv->i2c), I2C_TEA6300) >=0)
         {
-          if(btv->type==BTTV_AVEC_INTERCAP)
-            {
-                printk(KERN_INFO "bttv%d: fader chip: TEA6320\n",btv->nr);
-                btv->audio_chip = TEA6320;
-                init_tea6320(&(btv->i2c));
-            } else {
-		printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr);
-		btv->audio_chip = TEA6300;
-		init_tea6300(&(btv->i2c));
-            }
+		if(btv->type==BTTV_AVEC_INTERCAP)
+        	{
+                	printk(KERN_INFO "bttv%d: fader chip: TEA6320\n",btv->nr);
+                	btv->audio_chip = TEA6320;
+                	init_tea6320(&(btv->i2c));
+		} else {
+			printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr);
+			btv->audio_chip = TEA6300;
+			init_tea6300(&(btv->i2c));
+        	}
         } else
 		printk(KERN_INFO "bttv%d: NO fader chip: TEA6300\n",btv->nr);
 
@@ -3213,6 +2892,8 @@
         struct bttv *btv = &bttvs[i];
 
 	btv->user=0; 
+	
+	init_MUTEX(&btv->lock);
 
 	/* reset the bt848 */
 	btwrite(0, BT848_SRESET);
@@ -3521,7 +3202,6 @@
  *	Scan for a Bt848 card, request the irq and map the io memory 
  */
 
-#if LINUX_VERSION_CODE >= 0x020100
 int configure_bt848(struct pci_dev *dev, int bttv_num)
 {
 	int result;
@@ -3550,22 +3230,6 @@
         else
                 btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);
 
-#if 0
-        if (remap[bttv_num])
-        {
-                unsigned int dw = btv->bt848_adr;
-
-                if (remap[bttv_num] < 0x1000)
-                        remap[bttv_num]<<=20;
-                remap[bttv_num]&=PCI_BASE_ADDRESS_MEM_MASK;
-                printk(KERN_INFO "bttv%d: remapping to : 0x%lx.\n",
-                       bttv_num,remap[bttv_num]);
-                remap[bttv_num]|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK);
-                pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, remap[bttv_num]);
-                pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dw);
-                btv->dev->base_address[0] = btv->bt848_adr;
-        }					
-#endif        
         pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
         printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ",
                bttv_num,btv->id, btv->revision);
@@ -3660,170 +3324,6 @@
 		printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num);
 	return bttv_num;
 }
-#else
-static int find_bt848(void)
-{
-	short pci_index;    
-	unsigned char command, latency;
-	int result;
-	unsigned char bus, devfn;
-	struct bttv *btv;
-
-	bttv_num=0;
-
-	if (!pcibios_present()) 
-	{
-		DEBUG(printk(KERN_DEBUG "bttv%d: PCI-BIOS not present or not accessable!\n",bttv_num));
-		return 0;
-	}
-
-	for (pci_index = 0;
-                !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849,
-                                     pci_index, &bus, &devfn)
-             ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
-                                    pci_index, &bus, &devfn)
-             ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878,
-                                    pci_index, &bus, &devfn)
-             ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879,
-                                    pci_index, &bus, &devfn);
-             ++pci_index) 
-	{
-		btv=&bttvs[bttv_num];
-                btv->nr = bttv_num;
-		btv->bus=bus;
-		btv->devfn=devfn;
-		btv->bt848_mem=NULL;
-		btv->vbibuf=NULL;
-		btv->risc_jmp=NULL;
-		btv->vbi_odd=NULL;
-		btv->vbi_even=NULL;
-		btv->vbiq=NULL;
-		btv->capq=NULL;
-		btv->capqo=NULL;
-		btv->capqe=NULL;
-
-		btv->vbip=VBIBUF_SIZE;
-
-		pcibios_read_config_word(btv->bus, btv->devfn, PCI_DEVICE_ID,
-					 &btv->id);
-		pcibios_read_config_byte(btv->bus, btv->devfn,
-			PCI_INTERRUPT_LINE, &btv->irq);
-		pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
-			&btv->bt848_adr);
-                if (btv->id >= 878)
-                        btv->i2c_command = 0x83;                   
-                else
-                        btv->i2c_command=
-                                (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);
-
-		if (remap[bttv_num])
-		{
-                        if (remap[bttv_num] < 0x1000)
-                                remap[bttv_num]<<=20;
-			remap[bttv_num]&=PCI_BASE_ADDRESS_MEM_MASK;
-			printk(KERN_INFO "bttv%d: remapping to : 0x%08x.\n",
-                               bttv_num,remap[bttv_num]);
-			remap[bttv_num]|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK);
-			pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
-				 remap[bttv_num]);
-			pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
-				&btv->bt848_adr);
-		}					
-                
-		btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK;
-		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION,
-			     &btv->revision);
-		printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ",
-                       bttv_num,btv->id, btv->revision);
-                printk("bus: %d, devfn: %d, ",
-                       btv->bus, btv->devfn);
-		printk("irq: %d, ",btv->irq);
-		printk("memory: 0x%08x.\n", btv->bt848_adr);
-    
-                btv->pll.pll_crystal = 0;
-                btv->pll.pll_ifreq   = 0;
-                btv->pll.pll_ofreq   = 0;
-                btv->pll.pll_current = 0;
-                if (!(btv->id==848 && btv->revision==0x11)) {
-                        switch (pll[btv->nr]) {
-                        case 0:
-                                /* off */
-                                break;
-                        case 1:
-                                /* 28 MHz crystal installed */
-                                btv->pll.pll_ifreq=28636363;
-                                btv->pll.pll_crystal=BT848_IFORM_XT0;
-                                break;
-                        case 2:
-                                /* 35 MHz crystal installed */
-                                btv->pll.pll_ifreq=35468950;
-                                btv->pll.pll_crystal=BT848_IFORM_XT1;
-                                break;
-                        }
-                }
-                
-		btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
-                
-		result = request_irq(btv->irq, bttv_irq,
-			SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
-		if (result==-EINVAL) 
-		{
-			printk(KERN_ERR "bttv%d: Bad irq number or handler\n",
-                               bttv_num);
-			return -EINVAL;
-		}
-		if (result==-EBUSY)
-		{
-			printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq);
-			return result;
-		}
-		if (result < 0) 
-			return result;
-
-		/* Enable bus-mastering */
-		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
-		command|=PCI_COMMAND_MASTER;
-		pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
-		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
-		if (!(command&PCI_COMMAND_MASTER)) 
-		{
-			printk(KERN_ERR "bttv%d: PCI bus-mastering could not be enabled\n",bttv_num);
-			return -1;
-		}
-		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
-			&latency);
-		if (!latency) 
-		{
-			latency=32;
-			pcibios_write_config_byte(btv->bus, btv->devfn,
-						  PCI_LATENCY_TIMER, latency);
-		}
-		DEBUG(printk(KERN_DEBUG "bttv%d: latency: %02x\n",
-                             bttv_num, latency));
-
-                btv->triton1=triton1 ? BT848_INT_ETBF : 0;
-		if (triton1 && btv->id >= 878) 
-		{
-			triton1 = 0;
-			printk("bttv: Enabling 430FX compatibilty for bt878\n");
-			pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command);
-			command|=BT878_EN_TBFX;
-			pcibios_write_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, command);
-			pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command);
-			if (!(command&BT878_EN_TBFX)) 
-			{
-		   		printk("bttv: 430FX compatibility could not be enabled\n");
-	  	    		return -1;
- 	 		}
-		}
-
-		bttv_num++;
-	}
-	if(bttv_num)
-		printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num);
-	return bttv_num;
-}
-#endif
 
 static void release_bttv(void)
 {
@@ -3848,17 +3348,11 @@
 		i2c_unregister_bus((&btv->i2c));
 
 		/* disable PCI bus-mastering */
-#if LINUX_VERSION_CODE >= 0x020100
+
 		pci_read_config_byte(btv->dev, PCI_COMMAND, &command);
 		/* Should this be &=~ ?? */
 		command&=~PCI_COMMAND_MASTER;
 		pci_write_config_byte(btv->dev, PCI_COMMAND, command);
-#else
-		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
-		command&=~PCI_COMMAND_MASTER;
-		pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
-
-#endif
     
 		/* unmap and free memory */
 		if (btv->grisc)
diff -u --recursive --new-file v2.3.18/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.3.18/linux/drivers/char/bttv.h	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/bttv.h	Wed Sep 29 15:32:15 1999
@@ -84,6 +84,7 @@
 	struct video_picture picture;		/* Current picture params */
 	struct video_audio audio_dev;		/* Current audio params */
 
+	struct semaphore lock;
 	int user;
 	int capuser;
 	struct device_open open_data[MAX_OPENS];
diff -u --recursive --new-file v2.3.18/linux/drivers/char/buz.c linux/drivers/char/buz.c
--- v2.3.18/linux/drivers/char/buz.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/char/buz.c	Wed Sep 29 15:32:15 1999
@@ -374,13 +374,13 @@
 	return (btread(ZR36057_I2CBR) >> 1) & 1;
 }
 
-void attach_inform(struct i2c_bus *bus, int id)
+static void attach_inform(struct i2c_bus *bus, int id)
 {
 	DEBUG(struct zoran *zr = (struct zoran *) bus->data);
 	DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id));
 }
 
-void detach_inform(struct i2c_bus *bus, int id)
+static void detach_inform(struct i2c_bus *bus, int id)
 {
 	DEBUG(struct zoran *zr = (struct zoran *) bus->data);
 	DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id));
diff -u --recursive --new-file v2.3.18/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c
--- v2.3.18/linux/drivers/char/bw-qcam.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/bw-qcam.c	Sat Oct  2 07:35:15 1999
@@ -76,18 +76,19 @@
 #include <linux/sched.h>
 #include <linux/version.h>
 #include <linux/videodev.h>
+#include <asm/semaphore.h>
 #include <asm/uaccess.h>
 
 #include "bw-qcam.h"
 
+static unsigned int maxpoll=250;   /* Maximum busy-loop count for qcam I/O */
+static unsigned int yieldlines=4;  /* Yield after this many during capture */
+
 #if LINUX_VERSION_CODE >= 0x020117
 MODULE_PARM(maxpoll,"i");
 MODULE_PARM(yieldlines,"i");   
 #endif
 
-static unsigned int maxpoll=250;   /* Maximum busy-loop count for qcam I/O */
-static unsigned int yieldlines=4;  /* Yield after this many during capture */
-
 extern __inline__ int read_lpstatus(struct qcam_device *q)
 {
 	return parport_read_status(q->pport);
@@ -174,6 +175,8 @@
 	}
 	
 	memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
+	
+	init_MUTEX(&q->lock);
 
 	q->port_mode = (QC_ANY | QC_NOTSET);
 	q->width = 320;
@@ -817,14 +820,12 @@
 			qcam->contrast = p.contrast>>8;
 			qcam->whitebal = p.whiteness>>8;
 			qcam->bpp = p.depth;
-			
+
+			down(&qcam->lock);			
 			qc_setscanmode(qcam);
+			up(&qcam->lock);
 			qcam->status |= QC_PARAM_CHANGE;
 
-/*			parport_claim_or_block(qcam->pdev);
-			qc_set(qcam);
-			parport_release(qcam->pdev);
-*/
 			return 0;
 		}
 		case VIDIOCSWIN:
@@ -855,7 +856,9 @@
 				qcam->height = 240;
 				qcam->transfer_scale = 1;
 			}
+			down(&qcam->lock);
 			qc_setscanmode(qcam);
+			up(&qcam->lock);
 			
 			/* We must update the camera before we grab. We could
 			   just have changed the grab size */
@@ -904,7 +907,9 @@
 	struct qcam_device *qcam=(struct qcam_device *)v;
 	int len;
 	parport_claim_or_block(qcam->pdev);
-	/* Probably should have a semaphore against multiple users */
+	
+	down(&qcam->lock);
+	
 	qc_reset(qcam);
 
 	/* Update the camera parameters if we need to */
@@ -912,6 +917,9 @@
 		qc_set(qcam);
 
 	len=qc_capture(qcam, buf,count);
+	
+	up(&qcam->lock);
+	
 	parport_release(qcam->pdev);
 	return len;
 }
diff -u --recursive --new-file v2.3.18/linux/drivers/char/bw-qcam.h linux/drivers/char/bw-qcam.h
--- v2.3.18/linux/drivers/char/bw-qcam.h	Wed Mar 10 17:03:52 1999
+++ linux/drivers/char/bw-qcam.h	Sat Oct  2 07:35:15 1999
@@ -55,6 +55,7 @@
 	struct video_device vdev;
 	struct pardevice *pdev;
 	struct parport *pport;
+	struct semaphore lock;
 	int width, height;
 	int bpp;
 	int mode;
diff -u --recursive --new-file v2.3.18/linux/drivers/char/c-qcam.c linux/drivers/char/c-qcam.c
--- v2.3.18/linux/drivers/char/c-qcam.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/c-qcam.c	Sat Oct  2 07:35:15 1999
@@ -16,6 +16,7 @@
 #include <linux/sched.h>
 #include <linux/version.h>
 #include <linux/videodev.h>
+#include <asm/semaphore.h>
 #include <asm/uaccess.h>
 
 struct qcam_device {
@@ -28,6 +29,7 @@
 	int contrast, brightness, whitebal;
 	int top, left;
 	unsigned int bidirectional;
+	struct semaphore lock;
 };
 
 /* The three possible QuickCam modes */
@@ -516,10 +518,12 @@
 			qcam->brightness = p.brightness>>8;
 			qcam->contrast = p.contrast>>8;
 			qcam->whitebal = p.whiteness>>8;
-			
+
+			down(&qcam->lock);			
 			parport_claim_or_block(qcam->pdev);
 			qc_setup(qcam); 
 			parport_release(qcam->pdev);
+			up(&qcam->lock);
 			return 0;
 		}
 		case VIDIOCSWIN:
@@ -564,9 +568,11 @@
 #endif
 			/* Ok we figured out what to use from our 
 			   wide choice */
+			down(&qcam->lock);
 			parport_claim_or_block(qcam->pdev);
 			qc_setup(qcam);
 			parport_release(qcam->pdev);
+			up(&qcam->lock);
 			return 0;
 		}
 		case VIDIOCGWIN:
@@ -608,10 +614,13 @@
 {
 	struct qcam_device *qcam=(struct qcam_device *)v;
 	int len;
+
+	down(&qcam->lock);
 	parport_claim_or_block(qcam->pdev);
 	/* Probably should have a semaphore against multiple users */
 	len = qc_capture(qcam, buf,count); 
 	parport_release(qcam->pdev);
+	up(&qcam->lock);
 	return len;
 }
 
@@ -660,6 +669,7 @@
 	
 	memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
 
+	init_MUTEX(&q->lock);
 	q->width = q->ccd_width = 320;
 	q->height = q->ccd_height = 240;
 	q->mode = QC_MILLIONS | QC_DECIMATION_1;
diff -u --recursive --new-file v2.3.18/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.3.18/linux/drivers/char/cyclades.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/char/cyclades.c	Sat Oct  2 07:50:16 1999
@@ -1,7 +1,7 @@
-#define BLOCKMOVE
+#undef	BLOCKMOVE
 #define	Z_WAKE
 static char rcsid[] =
-"$Revision: 2.3.1.1 $$Date: 1999/07/15 16:45:53 $";
+"$Revision: 2.3.2.2 $$Date: 1999/10/01 11:27:43 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -9,9 +9,8 @@
  * This file contains the driver for the Cyclades Cyclom-Y multiport
  * serial boards.
  *
- * Maintained by Ivan Passos (ivan@cyclades.com),
- * Marcio Saito (marcio@cyclades.com) and 
- * Randolph Bentson (bentson@grieg.seaslug.org).
+ * Initially written by Randolph Bentson (bentson@grieg.seaslug.org).
+ * Maintained by Ivan Passos (ivan@cyclades.com).
  *
  * For Technical support and installation problems, please send e-mail
  * to support@cyclades.com.
@@ -31,6 +30,25 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
+ * Revision 2.3.2.2   1999/10/01 11:27:43 ivan
+ * Fixed bug in cyz_poll that would make all ports but port 0
+ * unable to transmit/receive data (Cyclades-Z only);
+ * Implemented logic to prevent the RX buffer from being stuck with
+ * due to a driver / firmware race condition in interrupt op mode
+ * (Cyclades-Z only);
+ * Fixed bug in block_til_ready logic that would lead to a system crash;
+ * Revisited cy_close spinlock usage;
+ *
+ * Revision 2.3.2.1   1999/09/28 11:01:22 ivan
+ * Revisited CONFIG_PCI conditional compilation for PCI board support;
+ * Implemented TIOCGICOUNT and TIOCMIWAIT ioctl support;
+ * _Major_ cleanup on the Cyclades-Z interrupt support code / logic;
+ * Removed CTS handling from the driver -- this is now completely handled
+ * by the firmware (Cyclades-Z only);
+ * Flush RX on-board buffers on a port open (Cyclades-Z only);
+ * Fixed handling of ASYNC_SPD_* TTY flags;
+ * Module unload now unmaps all memory area allocated by ioremap;
+ *
  * Revision 2.3.1.1   1999/07/15 16:45:53 ivan
  * Removed CY_PROC conditional compilation;
  * Implemented SMP-awareness for the driver;
@@ -655,7 +673,8 @@
 
 #define cy_put_user	put_user
 
-static unsigned long cy_get_user(unsigned long *addr)
+static unsigned long 
+cy_get_user(unsigned long *addr)
 {
 	unsigned long result = 0;
 	int error = get_user (result, addr);
@@ -829,6 +848,7 @@
 static unsigned short	cy_pci_nboard = 0;
 static unsigned short	cy_isa_nboard = 0;
 static unsigned short	cy_nboard = 0;
+#ifdef CONFIG_PCI
 static unsigned short	cy_pci_dev_id[] = {
 			    PCI_DEVICE_ID_CYCLOM_Y_Lo,	/* PCI < 1Mb */
 			    PCI_DEVICE_ID_CYCLOM_Y_Hi,	/* PCI > 1Mb */
@@ -840,10 +860,11 @@
 			    PCI_DEVICE_ID_CYCLOM_Z_Hi,	/* Z PCI > 1Mb */
 			    0				/* end of table */
 			};
-
+#endif
 
 static void cy_start(struct tty_struct *);
 static void set_line_char(struct cyclades_port *);
+static int cyz_issue_cmd(struct cyclades_card *, uclong, ucchar, uclong);
 #ifndef CONFIG_COBALT_27
 static unsigned detect_isa_irq (volatile ucchar *);
 #endif /* CONFIG_COBALT_27 */
@@ -864,6 +885,9 @@
 cyz_timerlist = {
     NULL, NULL, 0, 0, cyz_poll
 };
+#else /* CONFIG_CYZ_INTR */
+static void cyz_rx_restart(unsigned long);
+static struct timer_list cyz_rx_full_timer[NR_PORTS];
 #endif /* CONFIG_CYZ_INTR */
 
 /**************************************************
@@ -968,6 +992,17 @@
     if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
         wake_up_interruptible(&info->open_wait);
     }
+#ifdef CONFIG_CYZ_INTR
+    if (test_and_clear_bit(Cy_EVENT_Z_RX_FULL, &info->event)) {
+	cyz_rx_full_timer[info->line].expires = jiffies + 1;
+	cyz_rx_full_timer[info->line].function = cyz_rx_restart;
+	cyz_rx_full_timer[info->line].data = (unsigned long)info;
+	add_timer(&cyz_rx_full_timer[info->line]);
+    }
+#endif
+    if (test_and_clear_bit(Cy_EVENT_DELTA_WAKEUP, &info->event)) {
+	wake_up_interruptible(&info->delta_msr_wait);
+    }
     if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
         if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
         && tty->ldisc.write_wakeup){
@@ -1016,7 +1051,8 @@
 } /* cyy_issue_cmd */
 
 #ifndef CONFIG_COBALT_27	/* ISA interrupt detection code */
-static unsigned detect_isa_irq (volatile ucchar *address)
+static unsigned 
+detect_isa_irq (volatile ucchar *address)
 {
   int irq;
   unsigned long irqs, flags;
@@ -1151,7 +1187,19 @@
                         j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
                             data = cy_readb(base_addr+(CyRDSR<<index));
+
+			    /* For statistics only */
+			    if (data & CyBREAK)
+				info->icount.brk++;
+			    else if(data & CyFRAME)
+				info->icount.frame++;
+			    else if(data & CyPARITY)
+				info->icount.parity++;
+			    else if(data & CyOVERRUN)
+				info->icount.overrun++;
+
                             if(data & info->ignore_status_mask){
+				info->icount.rx++;
                                 continue;
                             }
                             if (tty->flip.count < TTY_FLIPBUF_SIZE){
@@ -1159,9 +1207,10 @@
                                 if (data & info->read_status_mask){
                                     if(data & CyBREAK){
                                         *tty->flip.flag_buf_ptr++ =
-							    TTY_BREAK;
+	    						    TTY_BREAK;
                                         *tty->flip.char_buf_ptr++ =
 					  cy_readb(base_addr+(CyRDSR<<index));
+					info->icount.rx++;
                                         if (info->flags & ASYNC_SAK){
                                             do_SAK(tty);
                                         }
@@ -1170,17 +1219,20 @@
 							    TTY_FRAME;
                                         *tty->flip.char_buf_ptr++ =
 					  cy_readb(base_addr+(CyRDSR<<index));
+					info->icount.rx++;
 					info->idle_stats.frame_errs++;
                                     }else if(data & CyPARITY){
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_PARITY;
                                         *tty->flip.char_buf_ptr++ =
 					  cy_readb(base_addr+(CyRDSR<<index));
+					info->icount.rx++;
 					info->idle_stats.parity_errs++;
                                     }else if(data & CyOVERRUN){
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_OVERRUN;
                                         *tty->flip.char_buf_ptr++ = 0;
+					info->icount.rx++;
                                         /* If the flip buffer itself is
                                            overflowing, we still lose
                                            the next incoming character.
@@ -1192,6 +1244,7 @@
 							     TTY_NORMAL;
                                            *tty->flip.char_buf_ptr++ =
 					    cy_readb(base_addr+(CyRDSR<<index));
+					    info->icount.rx++;
                                         }
 					info->idle_stats.overruns++;
                                     /* These two conditions may imply */
@@ -1201,15 +1254,18 @@
                                     }else{
                                         *tty->flip.flag_buf_ptr++ = 0;
                                         *tty->flip.char_buf_ptr++ = 0;
+					info->icount.rx++;
                                     }
                                 }else{
                                     *tty->flip.flag_buf_ptr++ = 0;
                                     *tty->flip.char_buf_ptr++ = 0;
+				    info->icount.rx++;
                                 }
                             }else{
                                 /* there was a software buffer
 				   overrun and nothing could be
 				   done about it!!! */
+				info->icount.buf_overrun++;
 				info->idle_stats.overruns++;
                             }
                         } else { /* normal character reception */
@@ -1233,6 +1289,7 @@
                                 data = cy_readb(base_addr+(CyRDSR<<index));
                                 *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
                                 *tty->flip.char_buf_ptr++ = data;
+				info->icount.rx++;
 #ifdef CY_16Y_HACK
                                 udelay(10L);
 #endif
@@ -1286,11 +1343,11 @@
                     /* load the on-chip space for outbound data */
                     char_count = info->xmit_fifo_size;
 
-
                     if(info->x_char) { /* send special char */
                         outch = info->x_char;
                         cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         char_count--;
+			info->icount.tx++;
                         info->x_char = 0;
                     }
 
@@ -1351,6 +1408,7 @@
                             info->xmit_tail = (info->xmit_tail + 1)
                                                       & (SERIAL_XMIT_SIZE - 1);
                             cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
+			    info->icount.tx++;
                         }else{
                             if(char_count > 1){
                                 info->xmit_cnt--;
@@ -1359,6 +1417,7 @@
                                 cy_writeb((u_long)base_addr+(CyTDR<<index), 
 					  outch);
                                 cy_writeb((u_long)base_addr+(CyTDR<<index), 0);
+				info->icount.tx++;
                                 char_count--;
                             }else{
                             }
@@ -1397,6 +1456,16 @@
                     if(info->tty == 0){/* no place for data, ignore it*/
                         ;
                     }else{
+			if (mdm_change & CyANY_DELTA) {
+			    /* For statistics only */
+			    if (mdm_change & CyDCD)	info->icount.dcd++;
+			    if (mdm_change & CyCTS)	info->icount.cts++;
+			    if (mdm_change & CyDSR)	info->icount.dsr++;
+			    if (mdm_change & CyRI)	info->icount.rng++;
+
+			    cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP);
+			}
+
                         if((mdm_change & CyDCD)
                         && (info->flags & ASYNC_CHECK_CD)){
                             if(mdm_status & CyDCD){
@@ -1438,9 +1507,9 @@
                                 }
                             }
                         }
-                        if(mdm_status & CyDSR){
+                        if(mdm_change & CyDSR){
                         }
-                        if(mdm_status & CyRI){
+                        if(mdm_change & CyRI){
                         }
                     }
                     /* end of service */
@@ -1497,7 +1566,6 @@
     return 0;
 } /* cyz_fetch_msg */
 
-
 static int
 cyz_issue_cmd( struct cyclades_card *cinfo,
 	    uclong channel, ucchar cmd, uclong param)
@@ -1532,12 +1600,184 @@
     return(0);
 } /* cyz_issue_cmd */
 
+static void
+cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl)
+{
+  struct cyclades_card *cinfo = &cy_card[info->card];
+  struct tty_struct *tty = info->tty;
+  volatile int char_count;
+#ifdef BLOCKMOVE
+  int small_count;
+#else
+  char data;
+#endif
+  volatile uclong rx_put, rx_get, rx_bufsize;
+
+/* Removed due to compilation problems in Alpha systems */
+//    if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+
+    rx_get = cy_readl(&buf_ctrl->rx_get);
+    rx_put = cy_readl(&buf_ctrl->rx_put);
+    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
+    if (rx_put >= rx_get)
+	char_count = rx_put - rx_get;
+    else
+	char_count = rx_put - rx_get + rx_bufsize;
+
+    if ( char_count ) {
+	info->last_active = jiffies;
+	info->jiffies[1] = jiffies;
+
+#ifdef CY_ENABLE_MONITORING
+	info->mon.int_count++;
+	info->mon.char_count += char_count;
+	if (char_count > info->mon.char_max)
+	    info->mon.char_max = char_count;
+	info->mon.char_last = char_count;
+#endif
+	if(tty == 0){
+	    /* flush received characters */
+	    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
+	    info->rflush_count++;
+	}else{
+#ifdef BLOCKMOVE
+	    /* we'd like to use memcpy(t, f, n) and memset(s, c, count)
+	       for performance, but because of buffer boundaries, there
+	       may be several steps to the operation */
+	    while(0 < (small_count = 
+		       cy_min((rx_bufsize - rx_get),
+		       cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), char_count))
+		 )) {
+		memcpy_fromio(tty->flip.char_buf_ptr,
+			      (char *)(cinfo->base_addr
+				       + cy_readl(&buf_ctrl->rx_bufaddr)
+				       + rx_get),
+			      small_count);
+
+		tty->flip.char_buf_ptr += small_count;
+		memset(tty->flip.flag_buf_ptr, TTY_NORMAL, small_count);
+		tty->flip.flag_buf_ptr += small_count;
+		rx_get = (rx_get + small_count) & (rx_bufsize - 1);
+		char_count -= small_count;
+		info->icount.rx += small_count;
+		info->idle_stats.recv_bytes += small_count;
+		tty->flip.count += small_count;
+	    }
+#else
+	    while(char_count--){
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE){
 #ifdef CONFIG_CYZ_INTR
+		    cy_sched_event(info, Cy_EVENT_Z_RX_FULL);
+#endif
+		    break;
+		}
+		data = cy_readb(cinfo->base_addr +
+			cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
+		rx_get = (rx_get + 1) & (rx_bufsize - 1);
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+		*tty->flip.char_buf_ptr++ = data;
+		info->idle_stats.recv_bytes++;
+		info->icount.rx++;
+	    }
+#endif
+	    info->idle_stats.recv_idle = jiffies;
+	    queue_task(&tty->flip.tqueue, &tq_timer);
+	}
+	/* Update rx_get */
+	cy_writel(&buf_ctrl->rx_get, rx_get);
+    }
+}
+
 static void
-cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl)
+{
+  struct cyclades_card *cinfo = &cy_card[info->card];
+  struct tty_struct *tty = info->tty;
+  char data;
+  volatile int char_count;
+#ifdef BLOCKMOVE
+  int small_count;
+#endif
+  volatile uclong tx_put, tx_get, tx_bufsize;
+
+/* Removed due to compilation problems in Alpha systems */
+//    if ((char_count = SPACE_IN_BUF(buf_ctrl))){
+
+    tx_get = cy_readl(&buf_ctrl->tx_get);
+    tx_put = cy_readl(&buf_ctrl->tx_put);
+    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+    if (tx_put >= tx_get)
+	char_count = tx_get - tx_put - 1 + tx_bufsize;
+    else
+	char_count = tx_get - tx_put - 1;
+
+    if ( char_count ) {
+
+	if( tty == 0 ){
+	    goto ztxdone;
+	}
+
+	if(info->x_char) { /* send special char */
+	    data = info->x_char;
+
+	    cy_writeb((cinfo->base_addr + 
+		cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
+	    tx_put = (tx_put + 1) & (tx_bufsize - 1);
+	    info->x_char = 0;
+	    char_count--;
+	    info->icount.tx++;
+	    info->last_active = jiffies;
+	    info->jiffies[2] = jiffies;
+	}
+#ifdef BLOCKMOVE
+	while(0 < (small_count = 
+		   cy_min((tx_bufsize - tx_put),
+		   cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
+			cy_min(info->xmit_cnt, char_count))))){
+
+	    memcpy_toio((char *)(cinfo->base_addr
+				 + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
+			&info->xmit_buf[info->xmit_tail],
+			small_count);
+
+	    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
+	    char_count -= small_count;
+	    info->icount.tx += small_count;
+	    info->xmit_cnt -= small_count;
+	    info->xmit_tail = 
+		(info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
+	    info->last_active = jiffies;
+	    info->jiffies[2] = jiffies;
+	}
+#else
+	while (info->xmit_cnt && char_count){
+	    data = info->xmit_buf[info->xmit_tail];
+	    info->xmit_cnt--;
+	    info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+
+	    cy_writeb(cinfo->base_addr + 
+		cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, data);
+	    tx_put = (tx_put + 1) & (tx_bufsize - 1);
+	    char_count--;
+	    info->icount.tx++;
+	    info->last_active = jiffies;
+	    info->jiffies[2] = jiffies;
+	}
+#endif
+    ztxdone:
+	if (info->xmit_cnt < WAKEUP_CHARS) {
+	    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+	}
+	/* Update tx_put */
+	cy_writel(&buf_ctrl->tx_put, tx_put);
+    }
+}
+
+static void
+cyz_handle_cmd(struct cyclades_card *cinfo)
 {
   struct tty_struct *tty;
-  struct cyclades_card *cinfo;
   struct cyclades_port *info;
   static volatile struct FIRM_ID *firm_id;
   static volatile struct ZFW_CTRL *zfw_ctrl;
@@ -1548,541 +1788,200 @@
   ucchar cmd;
   uclong param;
   uclong hw_ver, fw_ver;
-  char data;
-  volatile int char_count, special_count;
-#ifdef BLOCKMOVE
-  int small_count;
-#endif
-  volatile uclong tx_put, tx_get, tx_bufsize;
-  volatile uclong rx_put, rx_get, rx_bufsize;
-
-    if((cinfo = (struct cyclades_card *)dev_id) == 0){
-#ifdef CY_DEBUG_INTERRUPTS
-	printk("cyz_interrupt: spurious interrupt %d\n\r", irq);
-#endif
-        return; /* spurious interrupt */
-    }
+  int special_count;
+  int delta_count;
 
-	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
-        if (!ISZLOADED(*cinfo)) {
-#ifdef CY_DEBUG_INTERRUPTS
-	    printk("cyz_interrupt: board not yet loaded (INT %d).\n\r", irq);
-#endif
-	    return;
+    firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+    zfw_ctrl = (struct ZFW_CTRL *)
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+    board_ctrl = &(zfw_ctrl->board_ctrl);
+    fw_ver = cy_readl(&board_ctrl->fw_version);
+    hw_ver = cy_readl(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0);
+
+    while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
+	special_count = 0;
+	delta_count = 0;
+	info = &cy_port[channel + cinfo->first_line];
+	if((tty = info->tty) == 0) {
+	    continue;
 	}
+	ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+	buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
 
-	zfw_ctrl = (struct ZFW_CTRL *)
-	           (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
-	board_ctrl = &(zfw_ctrl->board_ctrl);
-	fw_ver = cy_readl(&board_ctrl->fw_version);
-	hw_ver = cy_readl(&((struct RUNTIME_9060 *)
-                            (cinfo->ctl_addr))->mail_box_0);
-
-	while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
-	    special_count = 0;
-	    info = &cy_port[channel + cinfo->first_line];
-            if((tty = info->tty) == 0) continue;
-	    ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
-	    buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
-
-	    switch(cmd){
+	switch(cmd) {
 	    case C_CM_PR_ERROR:
 		tty->flip.count++;
 		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
 		*tty->flip.char_buf_ptr++ = 0;
+		info->icount.rx++;
 		special_count++;
-	        break;
+		break;
 	    case C_CM_FR_ERROR:
 		tty->flip.count++;
 		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
 		*tty->flip.char_buf_ptr++ = 0;
+		info->icount.rx++;
 		special_count++;
-	        break;
+		break;
 	    case C_CM_RXBRK:
 		tty->flip.count++;
 		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
 		*tty->flip.char_buf_ptr++ = 0;
+		info->icount.rx++;
 		special_count++;
-	        break;
+		break;
 	    case C_CM_MDCD:
+		info->icount.dcd++;
+		delta_count++;
 		if (info->flags & ASYNC_CHECK_CD){
 		    if ((fw_ver > 241 ? 
-                          ((u_long)param) : 
-                          cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
-			/* SP("Open Wakeup\n"); */
-			cy_sched_event(info,
-			    Cy_EVENT_OPEN_WAKEUP);
-		    }else if(!((info->flags
-				& ASYNC_CALLOUT_ACTIVE)
-			 &&(info->flags
-			    & ASYNC_CALLOUT_NOHUP))){
-			/* SP("Hangup\n"); */
-			cy_sched_event(info,
-			    Cy_EVENT_HANGUP);
+			  ((u_long)param) : 
+			  cy_readl(&ch_ctrl->rs_status)) & C_RS_DCD) {
+			cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);
+		    }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)
+			     &&(info->flags & ASYNC_CALLOUT_NOHUP))){
+			cy_sched_event(info, Cy_EVENT_HANGUP);
 		    }
 		}
-	        break;
+		break;
 	    case C_CM_MCTS:
-		if (info->flags & ASYNC_CTS_FLOW) {
-		    if(info->tty->hw_stopped){
-			if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
-			    /* cy_start isn't used because... 
-			       HW flow is handled by the board */
-			    /* SP("Write Wakeup\n"); */
-			    cy_sched_event(info,
-				Cy_EVENT_WRITE_WAKEUP);
-			}
-		    }else{
-			if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
-			    /* cy_stop isn't used because 
-			       HW flow is handled by the board */
-			    /* SP("Write stop\n"); */
-			}
-		    }
-		}
-	        break;
+		info->icount.cts++;
+		delta_count++;
+		break;
 	    case C_CM_MRI:
-	        break;
+		info->icount.rng++;
+		delta_count++;
+		break;
 	    case C_CM_MDSR:
-	        break;
+		info->icount.dsr++;
+		delta_count++;
+		break;
 #ifdef Z_WAKE
 	    case C_CM_IOCTLW:
 		cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP);
-	        break;
+		break;
 #endif
+#ifdef CONFIG_CYZ_INTR
 	    case C_CM_RXHIWM:
 	    case C_CM_RXNNDT:
+	    case C_CM_INTBACK2:
 		/* Reception Interrupt */
 #ifdef CY_DEBUG_INTERRUPTS
-	    printk("cyz_interrupt: rcvd intr, card %d, port %ld\n\r", 
-		info->card, channel);
-#endif
-
-	    rx_get = cy_readl(&buf_ctrl->rx_get);
-	    rx_put = cy_readl(&buf_ctrl->rx_put);
-	    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
-	    if (rx_put >= rx_get)
-	    	char_count = rx_put - rx_get;
-	    else
-	    	char_count = rx_put - rx_get + rx_bufsize;
-
-	    if ( char_count ){
-
-#ifdef CY_ENABLE_MONITORING
-		info->mon.int_count++;
-		info->mon.char_count += char_count;
-		if (char_count > info->mon.char_max)
-		   info->mon.char_max = char_count;
-		info->mon.char_last = char_count;
-#endif
-		info->idle_stats.recv_bytes += char_count;
-		info->idle_stats.recv_idle   = jiffies;
-		if( tty == 0){
-		    /* flush received characters */
-		    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
-		    /* SP("-"); */
-		    info->rflush_count++;
-		}else{
-#ifdef BLOCKMOVE
-		/* we'd like to use memcpy(t, f, n) and memset(s, c, count)
-		   for performance, but because of buffer boundaries, there
-		   may be several steps to the operation */
-		    while(0 < (small_count
-			= cy_min((rx_bufsize - rx_get),
-			         cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
-				        char_count)))){
-
-			memcpy_fromio(tty->flip.char_buf_ptr,
-			              (char *)(cinfo->base_addr
-				       + cy_readl(&buf_ctrl->rx_bufaddr)
-				       + rx_get),
-			              small_count);
-
-			tty->flip.char_buf_ptr += small_count;
-			memset(tty->flip.flag_buf_ptr,
-			       TTY_NORMAL,
-			       small_count);
-			tty->flip.flag_buf_ptr += small_count;
-			rx_get = (rx_get + small_count) & (rx_bufsize - 1);
-			char_count -= small_count;
-			tty->flip.count += small_count;
-		    }
-#else
-		    while(char_count--){
-			if (tty->flip.count >= TTY_FLIPBUF_SIZE){
-				break;
-			}
-			data = cy_readb(cinfo->base_addr +
-			           cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
-			rx_get = (rx_get + 1) & (rx_bufsize - 1);
-			tty->flip.count++;
-			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-			*tty->flip.char_buf_ptr++ = data;
-		    }
+		printk("cyz_interrupt: rcvd intr, card %d, port %ld\n\r", 
+			info->card, channel);
 #endif
-		    queue_task(&tty->flip.tqueue, &tq_timer);
-		}
-		/* Update rx_get */
-		cy_writel(&buf_ctrl->rx_get, rx_get);
-	    }
+		cyz_handle_rx(info, buf_ctrl);
 		break;
 	    case C_CM_TXBEMPTY:
 	    case C_CM_TXLOWWM:
 	    case C_CM_INTBACK:
 		/* Transmission Interrupt */
 #ifdef CY_DEBUG_INTERRUPTS
-	    printk("cyz_interrupt: xmit intr, card %d, port %ld\n\r", 
-		info->card, channel);
-#endif
-
-	    tx_get = cy_readl(&buf_ctrl->tx_get);
-	    tx_put = cy_readl(&buf_ctrl->tx_put);
-	    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
-	    if (tx_put >= tx_get)
-	    	char_count = tx_get - tx_put - 1 + tx_bufsize;
-	    else
-	    	char_count = tx_get - tx_put - 1;
-
-	    if ( char_count ){
-
-		if( tty == 0 ){
-		    goto ztxdone;
-		}
-
-		if(info->x_char) { /* send special char */
-		    data = info->x_char;
-
-		    cy_writeb((cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    info->x_char = 0;
-		    char_count--;
-		}
-#ifdef BLOCKMOVE
-		while(0 < (small_count
-		    = cy_min((tx_bufsize - tx_put),
-			     cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
-			             cy_min(info->xmit_cnt, char_count))))){
-
-		    memcpy_toio((char *)(cinfo->base_addr
-			         + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
-			        &info->xmit_buf[info->xmit_tail],
-			        small_count);
-
-		    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
-		    char_count -= small_count;
-		    info->xmit_cnt -= small_count;
-		    info->xmit_tail =
-		       (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
-		}
-#else
-		while (info->xmit_cnt && char_count){
-		    data = info->xmit_buf[info->xmit_tail];
-		    info->xmit_cnt--;
-		    info->xmit_tail =
-			(info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
-
-		    cy_writeb(cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, 
-			      data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    char_count--;
-		}
-
+		printk("cyz_interrupt: xmit intr, card %d, port %ld\n\r", 
+			info->card, channel);
 #endif
-	    ztxdone:
-		if (info->xmit_cnt < WAKEUP_CHARS) {
-		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
-		}
-		/* Update tx_put */
-		cy_writel(&buf_ctrl->tx_put, tx_put);
-	    }
+		cyz_handle_tx(info, buf_ctrl);
 		break;
+#endif /* CONFIG_CYZ_INTR */
 	    case C_CM_FATAL:
 		/* should do something with this !!! */
-	        break;
-	    }
-	    if(special_count){
-		queue_task(&tty->flip.tqueue, &tq_timer);
-	    }
+		break;
+	    default:
+		break;
 	}
+	if(delta_count)
+	    cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP);
+	if(special_count)
+	    queue_task(&tty->flip.tqueue, &tq_timer);
+    }
+}
+
+#ifdef CONFIG_CYZ_INTR
+static void
+cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct cyclades_card *cinfo;
+
+    if((cinfo = (struct cyclades_card *)dev_id) == 0){
+#ifdef CY_DEBUG_INTERRUPTS
+	printk("cyz_interrupt: spurious interrupt %d\n\r", irq);
+#endif
+        return; /* spurious interrupt */
+    }
+
+    if (!ISZLOADED(*cinfo)) {
+#ifdef CY_DEBUG_INTERRUPTS
+	printk("cyz_interrupt: board not yet loaded (IRQ%d).\n\r", irq);
+#endif
+	return;
+    }
+
+    /* Handle the interrupts */
+    cyz_handle_cmd(cinfo);
 
     return;
 } /* cyz_interrupt */
 
+static void
+cyz_rx_restart(unsigned long arg)
+{
+    struct cyclades_port *info = (struct cyclades_port *)arg;
+    int retval;
+    int card = info->card;
+    uclong channel = (info->line) - (cy_card[card].first_line);
+
+    cyz_rx_full_timer[info->card].expires = jiffies + HZ;
+    retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_INTBACK2, 0L);
+    if (retval != 0){
+	printk("cyc:cyz_rx_restart retval was %x\n", retval);
+    }
+}
+
 #else /* CONFIG_CYZ_INTR */
 
 static void
 cyz_poll(unsigned long arg)
 {
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info;
+  struct tty_struct *tty;
   static volatile struct FIRM_ID *firm_id;
   static volatile struct ZFW_CTRL *zfw_ctrl;
   static volatile struct BOARD_CTRL *board_ctrl;
   static volatile struct CH_CTRL *ch_ctrl;
   static volatile struct BUF_CTRL *buf_ctrl;
-  struct cyclades_card *cinfo;
-  struct cyclades_port *info;
-  struct tty_struct *tty;
   int card, port;
-  int char_count;
-#ifdef BLOCKMOVE
-  int small_count;
-#endif
-  char data;
-  uclong channel;
-  ucchar cmd;
-  uclong param;
-  uclong hw_ver, fw_ver;
-  volatile uclong tx_put, tx_get, tx_bufsize;
-  volatile uclong rx_put, rx_get, rx_bufsize;
 
     cyz_timerlist.expires = jiffies + (HZ);
     for (card = 0 ; card < NR_CARDS ; card++){
 	cinfo = &cy_card[card];
-	if (!IS_CYC_Z(*cinfo)) continue;
 
+	if (!IS_CYC_Z(*cinfo)) continue;
+	if (!ISZLOADED(*cinfo)) continue;
 
-	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
-        if (!ISZLOADED(*cinfo)) {
+	/* Skip first polling cycle to avoid racing conditions with the FW */
+	if (!cinfo->intr_enabled) {
+	    cinfo->intr_enabled = 1;
 	    continue;
 	}
 
+	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
 	zfw_ctrl = (struct ZFW_CTRL *)
-	           (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+		   (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
 	board_ctrl = &(zfw_ctrl->board_ctrl);
-	fw_ver = cy_readl(&board_ctrl->fw_version);
-	hw_ver = cy_readl(&((struct RUNTIME_9060 *)
-                            (cinfo->ctl_addr))->mail_box_0);
-
-	while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1){
-	    char_count = 0;
-	    info = &cy_port[ channel + cinfo->first_line ];
-            if((tty = info->tty) == 0) continue;
-	    ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
-	    buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
-	    info->jiffies[0] = jiffies;
 
-	    switch(cmd){
-	    case C_CM_PR_ERROR:
-		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
-		*tty->flip.char_buf_ptr++ = 0;
-		char_count++;
-	        break;
-	    case C_CM_FR_ERROR:
-		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
-		*tty->flip.char_buf_ptr++ = 0;
-		char_count++;
-	        break;
-	    case C_CM_RXBRK:
-		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
-		*tty->flip.char_buf_ptr++ = 0;
-		char_count++;
-	        break;
-	    case C_CM_MDCD:
-		if (info->flags & ASYNC_CHECK_CD){
-		    if ((fw_ver > 241 ? 
-                          ((u_long)param) : 
-                          cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
-			cy_sched_event(info,
-			    Cy_EVENT_OPEN_WAKEUP);
-		    }else if(!((info->flags
-				& ASYNC_CALLOUT_ACTIVE)
-			 &&(info->flags
-			    & ASYNC_CALLOUT_NOHUP))){
-			cy_sched_event(info,
-			    Cy_EVENT_HANGUP);
-		    }
-		}
-	        break;
-	    case C_CM_MCTS:
-		if (info->flags & ASYNC_CTS_FLOW) {
-		    if(info->tty->hw_stopped){
-			if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
-			    /* cy_start isn't used because... 
-			       HW flow is handled by the board */
-			    cy_sched_event(info,
-				Cy_EVENT_WRITE_WAKEUP);
-			}
-		    }else{
-			if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
-			    /* cy_stop isn't used because 
-			       HW flow is handled by the board */
-			}
-		    }
-		}
-	        break;
-	    case C_CM_MRI:
-	        break;
-	    case C_CM_MDSR:
-	        break;
-#ifdef Z_WAKE
-	    case C_CM_IOCTLW:
-		cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP);
-	        break;
-#endif
-	    case C_CM_FATAL:
-		/* should do something with this !!! */
-	        break;
-	    }
-	    if(char_count){
-		queue_task(&tty->flip.tqueue, &tq_timer);
-	    }
-	}
+	cyz_handle_cmd(cinfo);
+
 	for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){
 	    info = &cy_port[ port + cinfo->first_line ];
             tty = info->tty;
 	    ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
 	    buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
 
-/* Removed due to compilation problems in Alpha systems */
-//	    if ((char_count = CHARS_IN_BUF(buf_ctrl))){
-
-	    rx_get = cy_readl(&buf_ctrl->rx_get);
-	    rx_put = cy_readl(&buf_ctrl->rx_put);
-	    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
-	    if (rx_put >= rx_get)
-	    	char_count = rx_put - rx_get;
-	    else
-	    	char_count = rx_put - rx_get + rx_bufsize;
-
-	    if ( char_count ){
-
-		info->last_active = jiffies;
-		info->jiffies[1] = jiffies;
-
-#ifdef CY_ENABLE_MONITORING
-		info->mon.int_count++;
-		info->mon.char_count += char_count;
-		if (char_count > info->mon.char_max)
-		   info->mon.char_max = char_count;
-		info->mon.char_last = char_count;
-#endif
-		info->idle_stats.recv_bytes += char_count;
-		info->idle_stats.recv_idle   = jiffies;
-		if( tty == 0){
-		    /* flush received characters */
-		    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
-		    info->rflush_count++;
-		}else{
-#ifdef BLOCKMOVE
-		/* we'd like to use memcpy(t, f, n) and memset(s, c, count)
-		   for performance, but because of buffer boundaries, there
-		   may be several steps to the operation */
-		    while(0 < (small_count
-			= cy_min((rx_bufsize - rx_get),
-			         cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
-				        char_count)))){
-
-			memcpy_fromio(tty->flip.char_buf_ptr,
-			              (char *)(cinfo->base_addr
-				       + cy_readl(&buf_ctrl->rx_bufaddr)
-				       + rx_get),
-			              small_count);
-
-			tty->flip.char_buf_ptr += small_count;
-			memset(tty->flip.flag_buf_ptr,
-			       TTY_NORMAL,
-			       small_count);
-			tty->flip.flag_buf_ptr += small_count;
-			rx_get = (rx_get + small_count) & (rx_bufsize - 1);
-			char_count -= small_count;
-			tty->flip.count += small_count;
-		    }
-#else
-		    while(char_count--){
-			if (tty->flip.count >= TTY_FLIPBUF_SIZE){
-				break;
-			}
-			data = cy_readb(cinfo->base_addr +
-			           cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
-			rx_get = (rx_get + 1) & (rx_bufsize - 1);
-			tty->flip.count++;
-			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-			*tty->flip.char_buf_ptr++ = data;
-		    }
-#endif
-		    queue_task(&tty->flip.tqueue, &tq_timer);
-		}
-		/* Update rx_get */
-		cy_writel(&buf_ctrl->rx_get, rx_get);
-	    }
-
-/* Removed due to compilation problems in Alpha systems */
-//	    if ((char_count = SPACE_IN_BUF(buf_ctrl))){
-
-	    tx_get = cy_readl(&buf_ctrl->tx_get);
-	    tx_put = cy_readl(&buf_ctrl->tx_put);
-	    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
-	    if (tx_put >= tx_get)
-	    	char_count = tx_get - tx_put - 1 + tx_bufsize;
-	    else
-	    	char_count = tx_get - tx_put - 1;
-
-	    if ( char_count ){
-
-		if( tty == 0 ){
-		    goto ztxdone;
-		}
-
-		if(info->x_char) { /* send special char */
-		    data = info->x_char;
-
-		    cy_writeb((cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    info->x_char = 0;
-		    char_count--;
-		    info->last_active = jiffies;
-		    info->jiffies[2] = jiffies;
-		}
-#ifdef BLOCKMOVE
-		while(0 < (small_count
-		    = cy_min((tx_bufsize - tx_put),
-			     cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
-			             cy_min(info->xmit_cnt, char_count))))){
-
-		    memcpy_toio((char *)(cinfo->base_addr
-			         + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
-			        &info->xmit_buf[info->xmit_tail],
-			        small_count);
-
-		    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
-		    char_count -= small_count;
-		    info->xmit_cnt -= small_count;
-		    info->xmit_tail =
-		       (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
-		    info->last_active = jiffies;
-		    info->jiffies[2] = jiffies;
-		}
-#else
-		while (info->xmit_cnt && char_count){
-		    data = info->xmit_buf[info->xmit_tail];
-		    info->xmit_cnt--;
-		    info->xmit_tail =
-			(info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
-
-		    cy_writeb(cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, 
-			      data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    char_count--;
-		    info->last_active = jiffies;
-		    info->jiffies[2] = jiffies;
-		}
-
-#endif
-	    ztxdone:
-		if (info->xmit_cnt < WAKEUP_CHARS) {
-		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
-		}
-		/* Update tx_put */
-		cy_writel(&buf_ctrl->tx_put, tx_put);
-	    }
+	    cyz_handle_rx(info, buf_ctrl);
+	    cyz_handle_tx(info, buf_ctrl);
 	}
-	/* poll every 40 ms */
+	/* poll every 'cyz_polling_cycle' period */
 	cyz_timerlist.expires = jiffies + cyz_polling_cycle;
     }
     add_timer(&cyz_timerlist);
@@ -2220,29 +2119,34 @@
 	cy_writel(&ch_ctrl[channel].intr_enable, 
 		  C_IN_TXBEMPTY|C_IN_TXLOWWM|C_IN_RXHIWM|C_IN_RXNNDT|
 		  C_IN_IOCTLW|
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #else
 	cy_writel(&ch_ctrl[channel].intr_enable, 
 		  C_IN_IOCTLW|
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #endif /* CONFIG_CYZ_INTR */
 #else
 #ifdef CONFIG_CYZ_INTR
 	cy_writel(&ch_ctrl[channel].intr_enable, 
 		  C_IN_TXBEMPTY|C_IN_TXLOWWM|C_IN_RXHIWM|C_IN_RXNNDT|
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #else
 	cy_writel(&ch_ctrl[channel].intr_enable, 
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #endif /* CONFIG_CYZ_INTR */
 #endif /* Z_WAKE */
 
-	retval = cyz_issue_cmd( &cy_card[card],
-	    channel, C_CM_IOCTL, 0L);	/* was C_CM_RESET */
+	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L);
 	if (retval != 0){
 	    printk("cyc:startup(1) retval was %x\n", retval);
 	}
 
+	/* Flush RX buffers before raising DTR and RTS */
+	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_FLUSH_RX, 0L);
+	if (retval != 0){
+	    printk("cyc:startup(2) retval was %x\n", retval);
+	}
+
 	/* set timeout !!! */
 	/* set RTS and DTR !!! */
 	cy_writel(&ch_ctrl[channel].rs_control,
@@ -2250,7 +2154,7 @@
 	retval = cyz_issue_cmd(&cy_card[info->card],
 	    channel, C_CM_IOCTLM, 0L);
 	if (retval != 0){
-	    printk("cyc:startup(2) retval was %x\n", retval);
+	    printk("cyc:startup(3) retval was %x\n", retval);
 	}
 #ifdef CY_DEBUG_DTR
 	    printk("cyc:startup raising Z DTR\n");
@@ -2353,6 +2257,9 @@
 
 	CY_LOCK(info, flags);
 
+	    /* Clear delta_msr_wait queue to avoid mem leaks. */
+	    wake_up_interruptible(&info->delta_msr_wait);
+
 	    if (info->xmit_buf){
 		unsigned char * temp;
 		temp = info->xmit_buf;
@@ -2419,7 +2326,7 @@
 		retval = cyz_issue_cmd(&cy_card[info->card],
 			channel, C_CM_IOCTLM, 0L);
 		if (retval != 0){
-		    printk("cyc:shutdown retval (2) was %x\n", retval);
+		    printk("cyc:shutdown retval was %x\n", retval);
 		}
 #ifdef CY_DEBUG_DTR
 		printk("cyc:shutdown dropping Z DTR\n");
@@ -2556,7 +2463,7 @@
 	    set_current_state(TASK_INTERRUPTIBLE);
 	    if (tty_hung_up_p(filp)
 	    || !(info->flags & ASYNC_INITIALIZED) ){
-		return ((info->flags & ASYNC_HUP_NOTIFY) ? 
+		retval = ((info->flags & ASYNC_HUP_NOTIFY) ? 
 		    -EAGAIN : -ERESTARTSYS);
 		break;
 	    }
@@ -2617,7 +2524,7 @@
 	    set_current_state(TASK_INTERRUPTIBLE);
 	    if (tty_hung_up_p(filp)
 	    || !(info->flags & ASYNC_INITIALIZED) ){
-		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+		retval = ((info->flags & ASYNC_HUP_NOTIFY) ?
 		    -EAGAIN : -ERESTARTSYS);
 		break;
 	    }
@@ -2707,6 +2614,10 @@
 	       interrupts should be enabled as soon as the first open happens 
 	       to one of its ports. */
 	    if (!cy_card[info->card].intr_enabled) {
+		/* Enable interrupts on the PLX chip */
+		cy_writew(cy_card[info->card].ctl_addr+0x68,
+			cy_readw(cy_card[info->card].ctl_addr+0x68)|0x0900);
+		/* Enable interrupts on the FW */
 		retval = cyz_issue_cmd(&cy_card[info->card], 
 					0, C_CM_IRQ_ENBL, 0L);
 		if (retval != 0){
@@ -2791,7 +2702,8 @@
 /*
  * cy_wait_until_sent() --- wait until the transmitter is empty
  */
-static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
+static void 
+cy_wait_until_sent(struct tty_struct *tty, int timeout)
 {
   struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
   unsigned char *base_addr;
@@ -2873,9 +2785,9 @@
  * This routine is called when a particular tty device is closed.
  */
 static void
-cy_close(struct tty_struct * tty, struct file * filp)
+cy_close(struct tty_struct *tty, struct file *filp)
 {
-  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
   unsigned long flags;
 
 #ifdef CY_DEBUG_OTHER
@@ -2979,7 +2891,7 @@
 	    retval = cyz_issue_cmd(&cy_card[info->card], channel, 
 				   C_CM_IOCTLW, 0L);
 	    if (retval != 0){
-		printk("cyc:shutdown retval (1) was %x\n", retval);
+		printk("cyc:cy_close retval was %x\n", retval);
 	    }
 	    CY_UNLOCK(info, flags);
 	    interruptible_sleep_on(&info->shutdown_wait);
@@ -3000,11 +2912,13 @@
     info->event = 0;
     info->tty = 0;
     if (info->blocked_open) {
+	CY_UNLOCK(info, flags);
         if (info->close_delay) {
             current->state = TASK_INTERRUPTIBLE;
             schedule_timeout(info->close_delay);
         }
         wake_up_interruptible(&info->open_wait);
+	CY_LOCK(info, flags);
     }
     info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
                      ASYNC_CLOSING);
@@ -3273,6 +3187,20 @@
     cflag = info->tty->termios->c_cflag;
     iflag = info->tty->termios->c_iflag;
 
+    /*
+     * Set up the tty->alt_speed kludge
+     */
+    if (info->tty) {
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+	    info->tty->alt_speed = 57600;
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+	    info->tty->alt_speed = 115200;
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+	    info->tty->alt_speed = 230400;
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+	    info->tty->alt_speed = 460800;
+    }
+
     card = info->card;
     channel = (info->line) - (cy_card[card].first_line);
     chip_number = channel / 4;
@@ -3282,7 +3210,11 @@
 	index = cy_card[card].bus_index;
 
 	/* baud rate */
-	baud = tty_get_baud_rate(info->tty);
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+	    baud = info->baud;
+	} else {
+	    baud = tty_get_baud_rate(info->tty);
+	}
 	if (baud > CD1400_MAX_SPEED) {
 	    baud = CD1400_MAX_SPEED;
 	}
@@ -3483,6 +3415,7 @@
       struct BOARD_CTRL *board_ctrl;
       struct CH_CTRL *ch_ctrl;
       struct BUF_CTRL *buf_ctrl;
+      uclong sw_flow;
       int retval;
 
         firm_id = (struct FIRM_ID *)
@@ -3498,7 +3431,11 @@
 	buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
 
 	/* baud rate */
-	baud = tty_get_baud_rate(info->tty);
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+	    baud = info->baud;
+	} else {
+	    baud = tty_get_baud_rate(info->tty);
+	}
 	if (baud > CYZ_MAX_SPEED) {
 	    baud = CYZ_MAX_SPEED;
 	}
@@ -3540,14 +3477,24 @@
 
 	/* CTS flow control flag */
 	if (cflag & CRTSCTS){
-	    info->flags |= ASYNC_CTS_FLOW;
 	    cy_writel(&ch_ctrl->hw_flow,
                cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
 	}else{
-	    info->flags &= ~ASYNC_CTS_FLOW;
 	    cy_writel(&ch_ctrl->hw_flow,
                cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS));
 	}
+	/* As the HW flow control is done in firmware, the driver doesn't
+	   need to care about it */
+	info->flags &= ~ASYNC_CTS_FLOW;
+
+	/* XON/XOFF/XANY flow control flags */
+	sw_flow = 0;
+	if (iflag & IXON){
+	    sw_flow |= C_FL_OXX;
+	    if (iflag & IXANY)
+		sw_flow |= C_FL_OIXANY;
+	}
+	cy_writel(&ch_ctrl->sw_flow, sw_flow);
 
 	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L);
 	if (retval != 0){
@@ -3562,14 +3509,6 @@
 	    info->flags |= ASYNC_CHECK_CD;
 	}
 
-	if (iflag & IXON){
-	    cy_writel(&ch_ctrl->sw_flow, 
-		cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX);
-	} else {
-	    cy_writel(&ch_ctrl->sw_flow, 
-		cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX);
-	}
-
 	if(baud == 0){ /* baud rate is zero, turn off line */
 	    cy_writel(&ch_ctrl->rs_control,
                cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
@@ -3595,20 +3534,6 @@
 	}
     }
 
-    /*
-     * Set up the tty->alt_speed kludge
-     */
-    if (info->tty) {
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-	    info->tty->alt_speed = 57600;
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-	    info->tty->alt_speed = 115200;
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-	    info->tty->alt_speed = 230400;
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-	    info->tty->alt_speed = 460800;
-    }
-
 
 } /* set_line_char */
 
@@ -3749,7 +3674,7 @@
 	}
 
     }
-    return cy_put_user(result,(unsigned int *) value);
+    return cy_put_user(result, value);
 } /* get_modem_info */
 
 
@@ -4208,7 +4133,10 @@
             unsigned int cmd, unsigned long arg)
 {
   struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  struct cyclades_icount cprev, cnow;		/* kernel counter temps */
+  struct serial_icounter_struct *p_cuser;	/* user space */
   int ret_val = 0;
+  unsigned long flags;
 
     if (serial_paranoia_check(info, tty->device, "cy_ioctl"))
 	return -ENODEV;
@@ -4264,6 +4192,7 @@
             if (copy_to_user((void *)arg, (void *)&cy_card[info->card], 
 			sizeof (struct cyclades_card))) {
 		ret_val = -EFAULT;
+		break;
 	    }
 	    ret_val = 0;
             break;
@@ -4300,6 +4229,77 @@
         case TIOCSSERIAL:
             ret_val = set_serial_info(info, (struct serial_struct *) arg);
             break;
+	/*
+	 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change 
+	 * - mask passed in arg for lines of interest
+	 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+	 * Caller should use TIOCGICOUNT to see which one it was
+	 */
+	case TIOCMIWAIT:
+	    CY_LOCK(info, flags);
+	    /* note the counters on entry */
+	    cprev = info->icount;
+	    CY_UNLOCK(info, flags);
+	    while (1) {
+		interruptible_sleep_on(&info->delta_msr_wait);
+		/* see if a signal did it */
+		if (signal_pending(current)) {
+		    return -ERESTARTSYS;
+		}
+
+		CY_LOCK(info, flags);
+		cnow = info->icount; /* atomic copy */
+		CY_UNLOCK(info, flags);
+
+		if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+		    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+		    return -EIO; /* no change => error */
+		}
+		if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || 
+		     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || 
+		     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) || 
+		     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+		    return 0;
+		}
+		cprev = cnow;
+	    }
+	    /* NOTREACHED */
+
+	/*
+	 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+	 * Return: write counters to the user passed counter struct
+	 * NB: both 1->0 and 0->1 transitions are counted except for
+	 *     RI where only 0->1 is counted.
+	 */
+	case TIOCGICOUNT:
+	    CY_LOCK(info, flags);
+	    cnow = info->icount;
+	    CY_UNLOCK(info, flags);
+	    p_cuser = (struct serial_icounter_struct *) arg;
+	    ret_val = put_user(cnow.cts, &p_cuser->cts);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.dsr, &p_cuser->dsr);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.rng, &p_cuser->rng);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.dcd, &p_cuser->dcd);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.rx, &p_cuser->rx);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.tx, &p_cuser->tx);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.frame, &p_cuser->frame);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.overrun, &p_cuser->overrun);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.parity, &p_cuser->parity);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.brk, &p_cuser->brk);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.buf_overrun, &p_cuser->buf_overrun);
+	    if (ret_val) return ret_val;
+	    ret_val = 0;
+	    break;
         default:
             ret_val = -ENOIOCTLCMD;
     }
@@ -4328,7 +4328,8 @@
 #endif
 
     if ((tty->termios->c_cflag == old_termios->c_cflag) &&
-	((tty->termios->c_iflag & IXON) == (old_termios->c_iflag & IXON)))
+	((tty->termios->c_iflag & (IXON|IXANY)) == 
+	 (old_termios->c_iflag & (IXON|IXANY))))
         return;
     set_line_char(info);
 
@@ -4819,7 +4820,8 @@
 } /* cy_detect_isa */
 #endif /* CONFIG_COBALT_27 */
 
-static void plx_init(uclong addr, uclong initctl)
+static void 
+plx_init(uclong addr, uclong initctl)
 {
     /* Reset PLX */
     cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x40000000);
@@ -4851,6 +4853,7 @@
   unsigned short        device_id,dev_index = 0;
   uclong		mailbox;
   uclong		Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
+  unsigned char         Ze_irq[NR_CARDS];
 
         if(pci_present() == 0) {    /* PCI bus not present */
                 return(0);
@@ -4872,8 +4875,8 @@
                 /* read PCI configuration area */
 		cy_pci_irq = pdev->irq;
 		cy_pci_addr0 = pdev->resource[0].start;
-		cy_pci_addr1 = pdev->resource[1].start; 
-		cy_pci_addr2 = pdev->resource[2].start; 
+		cy_pci_addr1 = pdev->resource[1].start;
+		cy_pci_addr2 = pdev->resource[2].start;
                 pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id);
 
 		device_id &= ~PCI_DEVICE_ID_MASK;
@@ -4892,7 +4895,7 @@
 		if (pdev->resource[2].flags & ~PCI_BASE_ADDRESS_IO_MASK) {
 		    printk("  Warning: PCI I/O bit incorrectly set. "
 			   "Ignoring it...\n");
-		    cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK;
+		    pdev->resource[2].flags &= PCI_BASE_ADDRESS_IO_MASK;
 		}
 
 #if defined(__alpha__)
@@ -5020,11 +5023,14 @@
             printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
                 (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
 #endif
-                cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK;
 #if !defined(__alpha__)
                 cy_pci_addr0 = (ulong)ioremap(cy_pci_addr0, CyPCI_Zctl);
 #endif
 
+		/* Disable interrupts on the PLX before resetting it */
+		cy_writew(cy_pci_addr0+0x68,
+			cy_readw(cy_pci_addr0+0x68) & ~0x0900);
+
 		plx_init(cy_pci_addr0, 0x6c);
 		/* For some yet unknown reason, once the PLX9060 reloads
 		   the EEPROM, the IRQ is lost and, thus, we have to
@@ -5034,12 +5040,11 @@
 
 		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
 			   cy_pci_addr0)->mail_box_0);
-                cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK;
 
-		if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) {
+		if (pdev->resource[2].flags & ~PCI_BASE_ADDRESS_IO_MASK) {
 		    printk("  Warning: PCI I/O bit incorrectly set. "
 			   "Ignoring it...\n");
-		    cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK;
+		    pdev->resource[2].flags &= PCI_BASE_ADDRESS_IO_MASK;
 		}
 		if (mailbox == ZE_V1) {
 #if !defined(__alpha__)
@@ -5053,6 +5058,7 @@
 		    } else {
 			Ze_addr0[ZeIndex] = cy_pci_addr0;
 			Ze_addr2[ZeIndex] = cy_pci_addr2;
+			Ze_irq[ZeIndex] = cy_pci_irq;
 			ZeIndex++;
 		    }
 		    i--;
@@ -5154,11 +5160,6 @@
 
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
-#ifdef CONFIG_CYZ_INTR
-		/* Enable interrupts on the PLX chip */
-		cy_writew(cy_pci_addr0+0x68,
-			cy_readw(cy_pci_addr0+0x68)|0x0900);
-#endif /* CONFIG_CYZ_INTR */
                 cy_next_channel += cy_pci_nchan;
     }
         }
@@ -5166,9 +5167,11 @@
         for (; ZeIndex != 0 && i < NR_CARDS; i++) {
 	    cy_pci_addr0 = Ze_addr0[0];
 	    cy_pci_addr2 = Ze_addr2[0];
+	    cy_pci_irq = Ze_irq[0];
 	    for (j = 0 ; j < ZeIndex-1 ; j++) {
 		Ze_addr0[j] = Ze_addr0[j+1];
 		Ze_addr2[j] = Ze_addr2[j+1];
+		Ze_irq[j] = Ze_irq[j+1];
 	    }
 	    ZeIndex--;
 		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
@@ -5241,11 +5244,6 @@
 
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
-#ifdef CONFIG_CYZ_INTR
-		/* Enable interrupts on the PLX chip */
-		cy_writew(cy_pci_addr0+0x68,
-			cy_readw(cy_pci_addr0+0x68)|0x0900);
-#endif /* CONFIG_CYZ_INTR */
                 cy_next_channel += cy_pci_nchan;
         }
 	if (ZeIndex != 0) {
@@ -5501,6 +5499,11 @@
                     info->rco = 0;
                     info->close_delay = 5*HZ/10;
 		    info->closing_wait = CLOSING_WAIT_DELAY;
+		    info->icount.cts = info->icount.dsr = 
+			info->icount.rng = info->icount.dcd = 0;
+		    info->icount.rx = info->icount.tx = 0;
+		    info->icount.frame = info->icount.parity = 0;
+		    info->icount.overrun = info->icount.brk = 0;
                     info->x_char = 0;
                     info->event = 0;
                     info->count = 0;
@@ -5519,6 +5522,7 @@
 		    init_waitqueue_head(&info->open_wait);
 		    init_waitqueue_head(&info->close_wait);
 		    init_waitqueue_head(&info->shutdown_wait);
+		    init_waitqueue_head(&info->delta_msr_wait);
                     /* info->session */
                     /* info->pgrp */
                     info->read_status_mask = 0;
@@ -5552,6 +5556,11 @@
                     info->cor5 = 0;
                     info->close_delay = 5*HZ/10;
 		    info->closing_wait = CLOSING_WAIT_DELAY;
+		    info->icount.cts = info->icount.dsr = 
+			info->icount.rng = info->icount.dcd = 0;
+		    info->icount.rx = info->icount.tx = 0;
+		    info->icount.frame = info->icount.parity = 0;
+		    info->icount.overrun = info->icount.brk = 0;
 		    chip_number = (port - cinfo->first_line) / 4;
                     if ((info->chip_rev = cy_readb(cinfo->base_addr +
 	               		 (cy_chip_offset[chip_number]<<index) +
@@ -5589,6 +5598,7 @@
 		    init_waitqueue_head(&info->open_wait);
 		    init_waitqueue_head(&info->close_wait);
 		    init_waitqueue_head(&info->shutdown_wait);
+		    init_waitqueue_head(&info->delta_msr_wait);
                     /* info->session */
                     /* info->pgrp */
                     info->read_status_mask =
@@ -5650,13 +5660,16 @@
     restore_flags(flags);
 
     for (i = 0; i < NR_CARDS; i++) {
-        if (cy_card[i].base_addr != 0
+        if (cy_card[i].base_addr != 0) {
+	    iounmap((void *)cy_card[i].base_addr);
+	    if (cy_card[i].ctl_addr != 0)
+		iounmap((void *)cy_card[i].ctl_addr);
+	    if (cy_card[i].irq
 #ifndef CONFIG_CYZ_INTR
-	    && cy_card[i].num_chips != -1 /* not a Z card */
+		&& cy_card[i].num_chips != -1 /* not a Z card */
 #endif /* CONFIG_CYZ_INTR */
-	    && cy_card[i].irq)
-        {
-            free_irq(cy_card[i].irq, &cy_card[i]);
+	    )
+		free_irq(cy_card[i].irq, &cy_card[i]);
         }
     }
     if (tmp_buf) {
diff -u --recursive --new-file v2.3.18/linux/drivers/char/drm/fops.c linux/drivers/char/drm/fops.c
--- v2.3.18/linux/drivers/char/drm/fops.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/char/drm/fops.c	Sat Oct  2 07:35:15 1999
@@ -76,7 +76,7 @@
 	drm_device_t  *dev    = priv->dev;
 
 	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d, f_count = %d\n",
-		  current->pid, dev->device, dev->open_count, filp->f_count);
+		  current->pid, dev->device, dev->open_count, atomic_read(&filp->f_count));
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.18/linux/drivers/char/joystick/joystick.c linux/drivers/char/joystick/joystick.c
--- v2.3.18/linux/drivers/char/joystick/joystick.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/char/joystick/joystick.c	Sat Oct  2 07:35:15 1999
@@ -42,6 +42,7 @@
 #include <linux/malloc.h>
 #include <linux/mm.h>
 #include <linux/module.h>
+#include <linux/version.h>
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 #include <linux/spinlock.h>
 #include <linux/poll.h>
diff -u --recursive --new-file v2.3.18/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c
--- v2.3.18/linux/drivers/char/pcwd.c	Thu Aug 26 13:05:35 1999
+++ linux/drivers/char/pcwd.c	Sat Oct  2 07:35:15 1999
@@ -59,6 +59,7 @@
 #include <linux/watchdog.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <linux/spinlock.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -96,6 +97,7 @@
 
 static int current_readport, revision, temp_panic;
 static int is_open, initial_status, supports_temp, mode_debug;
+static spinlock_t io_lock;
 
 /*
  * PCWD_CHECKCARD
@@ -246,10 +248,12 @@
 		return i ? -EFAULT : 0;
 
 	case WDIOC_GETSTATUS:
+		spin_lock(&io_lock);
 		if (revision == PCWD_REVISION_A) 
-		cdat = inb(current_readport);
+			cdat = inb(current_readport);
 		else
 			cdat = inb(current_readport + 1 );
+		spin_unlock(&io_lock);
 		rv = 0;
 
 		if (revision == PCWD_REVISION_A) 
@@ -312,7 +316,9 @@
 		rv = 0;
 		if ((supports_temp) && (mode_debug == 0)) 
 		{
+			spin_lock(&io_lock);
 			rv = inb(current_readport);
+			spin_unlock(&io_lock);
 			if(put_user(rv, (int*) arg))
 				return -EFAULT;
 		} else if(put_user(rv, (int*) arg))
@@ -327,9 +333,11 @@
 
 			if (rv & WDIOS_DISABLECARD) 
 			{
+				spin_lock(&io_lock);
 				outb_p(0xA5, current_readport + 3);
 				outb_p(0xA5, current_readport + 3);
 				cdat = inb_p(current_readport + 2);
+				spin_unlock(&io_lock);
 				if ((cdat & 0x10) == 0) 
 				{
 					printk("pcwd: Could not disable card.\n");
@@ -341,8 +349,10 @@
 
 			if (rv & WDIOS_ENABLECARD) 
 			{
+				spin_lock(&io_lock);
 				outb_p(0x00, current_readport + 3);
 				cdat = inb_p(current_readport + 2);
+				spin_unlock(&io_lock);
 				if (cdat & 0x10) 
 				{
 					printk("pcwd: Could not enable card.\n");
@@ -391,7 +401,11 @@
                     MOD_INC_USE_COUNT;
                     /*  Enable the port  */
                     if (revision == PCWD_REVISION_C)
-                        outb_p(0x00, current_readport + 3);
+                    {
+                    	spin_lock(&io_lock);
+                    	outb_p(0x00, current_readport + 3);
+                    	spin_unlock(&io_lock);
+                    }
                     is_open = 1;
                     return(0);
                 case TEMP_MINOR:
@@ -405,7 +419,7 @@
 static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
 			 loff_t *ppos)
 {
-	unsigned short c = inb(current_readport);
+	unsigned short c;
 	unsigned char cp;
 
 	/*  Can't seek (pread) on this device  */
@@ -418,6 +432,8 @@
 			 * Convert metric to Fahrenheit, since this was
 			 * the decided 'standard' for this return value.
 			 */
+			
+			c = inb(current_readport);
 			cp = (c * 9 / 5) + 32;
 			if(copy_to_user(buf, &cp, 1))
 				return -EFAULT;
@@ -436,8 +452,10 @@
 #ifndef CONFIG_WATCHDOG_NOWAYOUT
 		/*  Disable the board  */
 		if (revision == PCWD_REVISION_C) {
+			spin_lock(&io_lock);
 			outb_p(0xA5, current_readport + 3);
 			outb_p(0xA5, current_readport + 3);
+			spin_unlock(&io_lock);
 		}
 #endif
 	}
@@ -452,11 +470,15 @@
 
 static inline int get_revision(void)
 {
+	int r = PCWD_REVISION_C;
+	
+	spin_lock(&io_lock);
 	if ((inb(current_readport + 2) == 0xFF) ||
 	    (inb(current_readport + 3) == 0xFF))
-		return(PCWD_REVISION_A);
+		r=PCWD_REVISION_A;
+	spin_unlock(&io_lock);
 
-	return(PCWD_REVISION_C);
+	return r;
 }
 
 static int __init send_command(int cmd)
@@ -584,7 +606,8 @@
 #endif
 {
 	int i, found = 0;
-
+	spin_lock_init(&io_lock);
+	
 	revision = PCWD_REVISION_A;
 
 	printk("pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER);
diff -u --recursive --new-file v2.3.18/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v2.3.18/linux/drivers/char/pcxx.c	Tue Aug 31 17:29:13 1999
+++ linux/drivers/char/pcxx.c	Wed Sep 29 15:32:15 1999
@@ -79,6 +79,7 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/bitops.h>
+#include <asm/semaphore.h>
 
 #define VERSION 	"1.6.1"
 
@@ -1588,8 +1589,8 @@
 			ch->blocked_open = 0;
 			ch->callout_termios = pcxe_callout.init_termios;
 			ch->normal_termios = pcxe_driver.init_termios;
-			ch->open_wait = 0;
-			ch->close_wait = 0;
+			init_waitqueue_head(ch->open_wait);
+			init_waitqueue_head(ch->close_wait);
 			ch->asyncflags = 0;
 		}
 
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-aimslab.c linux/drivers/char/radio-aimslab.c
--- v2.3.18/linux/drivers/char/radio-aimslab.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/radio-aimslab.c	Wed Sep 29 14:02:59 1999
@@ -35,6 +35,7 @@
 #include <asm/uaccess.h>	/* copy to/from user		*/
 #include <linux/videodev.h>	/* kernel radio structs		*/
 #include <linux/config.h>	/* CONFIG_RADIO_RTRACK_PORT 	*/
+#include <asm/semaphore.h>	/* Lock for the I/O 		*/
 
 #ifndef CONFIG_RADIO_RTRACK_PORT
 #define CONFIG_RADIO_RTRACK_PORT -1
@@ -42,6 +43,7 @@
 
 static int io = CONFIG_RADIO_RTRACK_PORT; 
 static int users = 0;
+static struct semaphore lock;
 
 struct rt_device
 {
@@ -86,19 +88,23 @@
 static void rt_mute(struct rt_device *dev)
 {
 	dev->muted = 1;
+	down(&lock);
 	outb(0xd0, io);			/* volume steady, off		*/
+	up(&lock);
 }
 
 static int rt_setvol(struct rt_device *dev, int vol)
 {
 	int i;
 
+	down(&lock);
+	
 	if(vol == dev->curvol) {	/* requested volume = current */
 		if (dev->muted) {	/* user is unmuting the card  */
 			dev->muted = 0;
 			outb (0xd8, io);	/* enable card */
 		}	
-	
+		up(&lock);
 		return 0;
 	}
 
@@ -107,6 +113,7 @@
 		sleep_delay(2000000);	/* make sure it's totally down	*/
 		outb(0xd0, io);		/* volume steady, off		*/
 		dev->curvol = 0;	/* track the volume state!	*/
+		up(&lock);
 		return 0;
 	}
 
@@ -119,7 +126,7 @@
 			rt_decvol();
 
 	dev->curvol = vol;
-
+	up(&lock);
 	return 0;
 }
 
@@ -165,6 +172,8 @@
 
 	freq += 171200;			/* Add 10.7 MHz IF 		*/
 	freq /= 800;			/* Convert to 50 kHz units	*/
+	
+	down(&lock);			/* Stop other ops interfering */
 	 
 	send_0_byte (io, dev);		/*  0: LSB of frequency		*/
 
@@ -191,11 +200,13 @@
 		outb (0xd0, io);	/* volume steady + sigstr */
 	else
 		outb (0xd8, io);	/* volume steady + sigstr + on */
+		
+	up(&lock);
 
 	return 0;
 }
 
-int rt_getsigstr(struct rt_device *dev)
+static int rt_getsigstr(struct rt_device *dev)
 {
 	if (inb(io) & 2)	/* bit set = no signal present	*/
 		return 0;
@@ -324,8 +335,14 @@
 	NULL
 };
 
-int __init rtrack_init(struct video_init *v)
+static int __init rtrack_init(void)
 {
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+
 	if (check_region(io, 2)) 
 	{
 		printk(KERN_ERR "rtrack: port 0x%x already in use\n", io);
@@ -340,6 +357,10 @@
 	request_region(io, 2, "rtrack");
 	printk(KERN_INFO "AIMSlab Radiotrack/radioreveal card driver.\n");
 
+	/* Set up the I/O locking */
+	
+	init_MUTEX(&lock);
+	
  	/* mute card - prevents noisy bootups */
 
 	/* this ensures that the volume is all the way down  */
@@ -351,8 +372,6 @@
 	return 0;
 }
 
-#ifdef MODULE
-
 MODULE_AUTHOR("M.Kirkwood");
 MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
 MODULE_PARM(io, "i");
@@ -360,20 +379,12 @@
 
 EXPORT_NO_SYMBOLS;
 
-int init_module(void)
-{
-	if(io==-1)
-	{
-		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
-		return -EINVAL;
-	}
-	return rtrack_init(NULL);
-}
-
-void cleanup_module(void)
+static void __exit cleanup_rtrack_module(void)
 {
 	video_unregister_device(&rtrack_radio);
 	release_region(io,2);
 }
 
-#endif
+module_init(rtrack_init);
+module_exit(cleanup_rtrack_module);
+
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-aztech.c linux/drivers/char/radio-aztech.c
--- v2.3.18/linux/drivers/char/radio-aztech.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/radio-aztech.c	Wed Sep 29 14:02:59 1999
@@ -42,6 +42,7 @@
 static int io = CONFIG_RADIO_AZTECH_PORT; 
 static int radio_wait_time = 1000;
 static int users = 0;
+static struct semaphore lock;
 
 struct az_device
 {
@@ -86,7 +87,9 @@
 
 static int az_setvol(struct az_device *dev, int vol)
 {
+	down(&lock);
 	outb (volconvert(vol), io);
+	up(&lock);
 	return 0;
 }
 
@@ -119,6 +122,8 @@
 	frequency += 171200;		/* Add 10.7 MHz IF		*/
 	frequency /= 800;		/* Convert to 50 kHz units	*/
 					
+	down(&lock);
+	
 	send_0_byte (dev);		/*  0: LSB of frequency       */
 
 	for (i = 0; i < 13; i++)	/*   : frequency bits (1-13)  */
@@ -146,6 +151,8 @@
 
 	udelay (radio_wait_time);
 	outb_p(128+64+volconvert(dev->curvol), io);
+	
+	up(&lock);
 
 	return 0;
 }
@@ -279,14 +286,21 @@
 	NULL
 };
 
-int __init aztech_init(struct video_init *v)
+static int __init aztech_init(void)
 {
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+
 	if (check_region(io, 2)) 
 	{
 		printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
 		return -EBUSY;
 	}
 
+	init_MUTEX(&lock);
 	aztech_radio.priv=&aztech_unit;
 	
 	if(video_register_device(&aztech_radio, VFL_TYPE_RADIO)==-1)
@@ -299,8 +313,6 @@
 	return 0;
 }
 
-#ifdef MODULE
-
 MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
 MODULE_DESCRIPTION("A driver for the Aztech radio card.");
 MODULE_PARM(io, "i");
@@ -308,20 +320,11 @@
 
 EXPORT_NO_SYMBOLS;
 
-int init_module(void)
-{
-	if(io==-1)
-	{
-		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
-		return -EINVAL;
-	}
-	return aztech_init(NULL);
-}
-
-void cleanup_module(void)
+static void __exit aztech_cleanup(void)
 {
 	video_unregister_device(&aztech_radio);
 	release_region(io,2);
 }
 
-#endif
+module_init(aztech_init);
+module_exit(aztech_cleanup);
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-gemtek.c linux/drivers/char/radio-gemtek.c
--- v2.3.18/linux/drivers/char/radio-gemtek.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/radio-gemtek.c	Wed Sep 29 14:02:59 1999
@@ -23,6 +23,7 @@
 #include <asm/uaccess.h>	/* copy to/from user		*/
 #include <linux/videodev.h>	/* kernel radio structs		*/
 #include <linux/config.h>	/* CONFIG_RADIO_GEMTEK_PORT 	*/
+#include <linux/spinlock.h>
 
 #ifndef CONFIG_RADIO_GEMTEK_PORT
 #define CONFIG_RADIO_GEMTEK_PORT -1
@@ -30,6 +31,7 @@
 
 static int io = CONFIG_RADIO_GEMTEK_PORT; 
 static int users = 0;
+static spinlock_t lock;
 
 struct gemtek_device
 {
@@ -48,7 +50,9 @@
 {
         if(dev->muted)
 		return;
+	spin_lock(&lock);
 	outb(0x10, io);
+	spin_unlock(&lock);
 	dev->muted = 1;
 }
 
@@ -56,7 +60,9 @@
 {
 	if(dev->muted == 0)
 		return;
+	spin_lock(&lock);
 	outb(0x20, io);
+	spin_unlock(&lock);
 	dev->muted = 0;
 }
 
@@ -87,6 +93,8 @@
 	freq *= 7825;
 	freq /= 100000;
 
+	spin_lock(&lock);
+	
 	/* 2 start bits */
 	outb_p(0x03, io);
 	udelay(5);
@@ -114,13 +122,17 @@
 	outb_p(0x07, io);
 	udelay(5);
 
+	spin_unlock(&lock);
+	
 	return 0;
 }
 
 int gemtek_getsigstr(struct gemtek_device *dev)
 {
+	spin_lock(&lock);
 	inb(io);
 	udelay(5);
+	spin_unlock(&lock);
 	if (inb(io) & 8)		/* bit set = no signal present */
 		return 0;
 	return 1;		/* signal present */
@@ -250,8 +262,14 @@
 	NULL
 };
 
-int __init gemtek_init(struct video_init *v)
+static int __init gemtek_init(void)
 {
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (or io=0x248 for the combined sound/radiocard)\n");
+		return -EINVAL;
+	}
+
 	if (check_region(io, 4)) 
 	{
 		printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
@@ -266,6 +284,7 @@
 	request_region(io, 4, "gemtek");
 	printk(KERN_INFO "GemTek Radio Card driver.\n");
 
+	spin_lock_init(&lock);
  	/* mute card - prevents noisy bootups */
 	outb(0x10, io);
 	udelay(5);
@@ -277,8 +296,6 @@
 	return 0;
 }
 
-#ifdef MODULE
-
 MODULE_AUTHOR("Jonas Munsin");
 MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
 MODULE_PARM(io, "i");
@@ -286,23 +303,14 @@
 
 EXPORT_NO_SYMBOLS;
 
-int init_module(void)
-{
-	if(io==-1)
-	{
-		printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (or io=0x248 for the combined sound/radiocard)\n");
-		return -EINVAL;
-	}
-	return gemtek_init(NULL);
-}
-
-void cleanup_module(void)
+static void __exit gemtek_cleanup(void)
 {
 	video_unregister_device(&gemtek_radio);
 	release_region(io,4);
 }
 
-#endif
+module_init(gemtek_init);
+module_exit(gemtek_cleanup);
 
 /*
   Local variables:
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-miropcm20.c linux/drivers/char/radio-miropcm20.c
--- v2.3.18/linux/drivers/char/radio-miropcm20.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/radio-miropcm20.c	Wed Sep 29 14:02:59 1999
@@ -205,7 +205,7 @@
 	NULL
 };
 
-int __init pcm20_init(struct video_init *v)
+static int __init pcm20_init(void)
 {
 
 	pcm20_radio.priv=&pcm20_unit;
@@ -224,22 +224,16 @@
 	return 0;
 }
 
-#ifdef MODULE
-
 MODULE_AUTHOR("Ruurd Reitsma");
 MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
 
 EXPORT_NO_SYMBOLS;
 
-int init_module(void)
-{
-
-	return pcm20_init(NULL); 
-}
-
-void cleanup_module(void)
+static void __exit pcm20_cleanup(void)
 {
 	video_unregister_device(&pcm20_radio);
 }
 
-#endif
+module_init(pcm20_init);
+module_exit(pcm20_cleanup);
+
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-rtrack2.c linux/drivers/char/radio-rtrack2.c
--- v2.3.18/linux/drivers/char/radio-rtrack2.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/radio-rtrack2.c	Wed Sep 29 14:02:59 1999
@@ -16,6 +16,7 @@
 #include <asm/uaccess.h>	/* copy to/from user		*/
 #include <linux/videodev.h>	/* kernel radio structs		*/
 #include <linux/config.h>	/* CONFIG_RADIO_RTRACK2_PORT 	*/
+#include <linux/spinlock.h>
 
 #ifndef CONFIG_RADIO_RTRACK2_PORT
 #define CONFIG_RADIO_RTRACK2_PORT -1
@@ -23,6 +24,7 @@
 
 static int io = CONFIG_RADIO_RTRACK2_PORT; 
 static int users = 0;
+static spinlock_t lock;
 
 struct rt_device
 {
@@ -38,7 +40,9 @@
 {
         if(dev->muted)
 		return;
+	spin_lock(&lock);
 	outb(1, io);
+	spin_unlock(&lock);
 	dev->muted = 1;
 }
 
@@ -46,7 +50,9 @@
 {
 	if(dev->muted == 0)
 		return;
+	spin_lock(&lock);
 	outb(0, io);
+	spin_unlock(&lock);
 	dev->muted = 0;
 }
 
@@ -69,6 +75,8 @@
 	int i;
 
 	freq = freq / 200 + 856;
+	
+	spin_lock(&lock);
 
 	outb_p(0xc8, io);
 	outb_p(0xc9, io);
@@ -85,11 +93,13 @@
 
 	outb_p(0xc8, io);
 	if (!dev->muted)
-	  outb_p(0, io);
+		outb_p(0, io);
+		
+	spin_unlock(&lock);
 	return 0;
 }
 
-int rt_getsigstr(struct rt_device *dev)
+static int rt_getsigstr(struct rt_device *dev)
 {
 	if (inb(io) & 2)	/* bit set = no signal present	*/
 		return 0;
@@ -227,7 +237,8 @@
 	}
 
 	rtrack2_radio.priv=&rtrack2_unit;
-	
+
+	spin_lock_init(&lock);	
 	if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO)==-1)
 		return -EINVAL;
 		
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-sf16fmi.c linux/drivers/char/radio-sf16fmi.c
--- v2.3.18/linux/drivers/char/radio-sf16fmi.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/radio-sf16fmi.c	Wed Sep 29 14:02:59 1999
@@ -22,6 +22,7 @@
 #include <asm/uaccess.h>	/* copy to/from user		*/
 #include <linux/videodev.h>	/* kernel radio structs		*/
 #include <linux/config.h>	/* CONFIG_RADIO_SF16MI_PORT 	*/
+#include <asm/semaphore.h>
 
 struct fmi_device
 {
@@ -37,6 +38,7 @@
 
 static int io = CONFIG_RADIO_SF16FMI_PORT; 
 static int users = 0;
+static struct semaphore lock;
 
 /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
 /* It is only usefull to give freq in intervall of 800 (=0.05Mhz),
@@ -67,12 +69,16 @@
 
 static inline void fmi_mute(int port)
 {
+	down(&lock);
 	outb(0x00, port);
+	up(&lock);
 }
 
 static inline void fmi_unmute(int port)
 {
+	down(&lock);
 	outb(0x08, port);
+	up(&lock);
 }
 
 static inline int fmi_setfreq(struct fmi_device *dev)
@@ -81,6 +87,8 @@
 	unsigned long freq = dev->curfreq;
 	int i;
 	
+	down(&lock);
+	
 	outbits(16, RSF16_ENCODE(freq), myport);
 	outbits(8, 0xC0, myport);
 	for(i=0; i< 100; i++)
@@ -93,6 +101,8 @@
 	current->state = TASK_UNINTERRUPTIBLE;
 	schedule_timeout(HZ/7);
 */	
+
+	up(&lock);
 	if (dev->curvol) fmi_unmute(myport);
 	return 0;
 }
@@ -104,6 +114,7 @@
 	int myport = dev->port;
 	int i;
 	
+	down(&lock);
 	val = dev->curvol ? 0x08 : 0x00;	/* unmute/mute */
 	outb(val, myport);
 	outb(val | 0x10, myport);
@@ -119,6 +130,8 @@
 */	
 	res = (int)inb(myport+1);
 	outb(val, myport);
+	
+	up(&lock);
 	return (res & 2) ? 0 : 0xFFFF;
 }
 
@@ -289,6 +302,8 @@
 	fmi_unit.curfreq = 0;
 	fmi_unit.flags = VIDEO_TUNER_LOW;
 	fmi_radio.priv = &fmi_unit;
+	
+	init_MUTEX(&lock);
 	
 	if(video_register_device(&fmi_radio, VFL_TYPE_RADIO)==-1)
 		return -EINVAL;
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-terratec.c linux/drivers/char/radio-terratec.c
--- v2.3.18/linux/drivers/char/radio-terratec.c	Tue Aug 31 17:29:13 1999
+++ linux/drivers/char/radio-terratec.c	Wed Sep 29 14:02:59 1999
@@ -31,6 +31,7 @@
 #include <asm/uaccess.h>	/* copy to/from user		*/
 #include <linux/videodev.h>	/* kernel radio structs		*/
 #include <linux/config.h>	/* CONFIG_RADIO_TERRATEC_PORT 	*/
+#include <linux/spinlock.h>
 
 #ifndef CONFIG_RADIO_TERRATEC_PORT
 #define CONFIG_RADIO_TERRATEC_PORT 0x590
@@ -50,6 +51,7 @@
 
 static int io = CONFIG_RADIO_TERRATEC_PORT; 
 static int users = 0;
+static spinlock_t lock;
 
 struct tt_device
 {
@@ -66,12 +68,14 @@
 {
 	int i;
 	volume = volume+(volume * 32); // change both channels
+	spin_lock(&lock);
 	for (i=0;i<8;i++)
 	{
 		if (volume & (0x80>>i))
 			outb(0x80, VOLPORT);
 		else outb(0x00, VOLPORT);
 	}
+	spin_unlock(&lock);
 }
 
 
@@ -148,6 +152,8 @@
 		temp = temp/2;
        }
 
+	spin_lock(&lock);
+	
 	for (i=24;i>-1;i--)			/* bit shift the values to the radiocard */
 	{
 		if (buffer[i]==1) 
@@ -163,6 +169,8 @@
 		}
 	}
 	outb(0x00, BASEPORT);     
+	
+	spin_unlock(&lock);
   
   	return 0;
 }
@@ -308,6 +316,8 @@
 	}
 
 	terratec_radio.priv=&terratec_unit;
+	
+	spin_lock_init(&lock);
 	
 	if(video_register_device(&terratec_radio, VFL_TYPE_RADIO)==-1)
 		return -EINVAL;
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-trust.c linux/drivers/char/radio-trust.c
--- v2.3.18/linux/drivers/char/radio-trust.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/radio-trust.c	Wed Sep 29 14:02:59 1999
@@ -0,0 +1,354 @@
+/* radio-trust.c - Trust FM Radio card driver for Linux 2.2 
+ * by Eric Lammerts <eric@scintilla.utwente.nl>
+ *
+ * Based on radio-aztech.c. Original notes:
+ *
+ * Adapted to support the Video for Linux API by 
+ * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
+ *
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis      (jlewis@twilight.vtc.vsc.edu) 
+ * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
+ */
+
+#include <stdarg.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <linux/config.h>	/* CONFIG_RADIO_TRUST_PORT 	*/
+
+/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+#ifndef CONFIG_RADIO_TRUST_PORT
+#define CONFIG_RADIO_TRUST_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_TRUST_PORT; 
+static int ioval = 0xf;
+static int users = 0;
+static __u16 curvol;
+static __u16 curbass;
+static __u16 curtreble;
+static unsigned long curfreq;
+static int curstereo;
+static int curmute;
+
+/* i2c addresses */
+#define TDA7318_ADDR 0x88
+#define TSA6060T_ADDR 0xc4
+
+#define TR_DELAY do { inb(io); inb(io); inb(io); } while(0)
+#define TR_SET_SCL outb(ioval |= 2, io)
+#define TR_CLR_SCL outb(ioval &= 0xfd, io)
+#define TR_SET_SDA outb(ioval |= 1, io)
+#define TR_CLR_SDA outb(ioval &= 0xfe, io)
+
+static void write_i2c(int n, ...)
+{
+	unsigned char val, mask;
+	va_list args;
+
+	va_start(args, n);
+
+	/* start condition */
+	TR_SET_SDA;
+	TR_SET_SCL;
+	TR_DELAY;
+	TR_CLR_SDA;
+	TR_CLR_SCL;
+	TR_DELAY;
+
+	for(; n; n--) {
+		val = va_arg(args, unsigned);
+		for(mask = 0x80; mask; mask >>= 1) {
+			if(val & mask)
+				TR_SET_SDA;
+			else
+				TR_CLR_SDA;
+			TR_SET_SCL;
+			TR_DELAY;
+			TR_CLR_SCL;
+			TR_DELAY;
+		}
+		/* acknowledge bit */
+		TR_SET_SDA;
+		TR_SET_SCL;
+		TR_DELAY;
+		TR_CLR_SCL;
+		TR_DELAY;
+	}
+
+	/* stop condition */
+	TR_CLR_SDA;
+	TR_DELAY;
+	TR_SET_SCL;
+	TR_DELAY;
+	TR_SET_SDA;
+	TR_DELAY;
+
+	va_end(args);
+}
+
+static void tr_setvol(__u16 vol)
+{
+	curvol = vol / 2048;
+	write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f);
+}
+
+static int basstreble2chip[15] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
+};
+
+static void tr_setbass(__u16 bass)
+{
+	curbass = bass / 4370;
+	write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]);
+}
+
+static void tr_settreble(__u16 treble)
+{
+	curtreble = treble / 4370;
+	write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]);
+}
+
+static void tr_setstereo(int stereo)
+{
+	curstereo = !!stereo;
+	ioval = (ioval & 0xfb) | (!curstereo << 2);
+	outb(ioval, io);
+}
+
+static void tr_setmute(int mute)
+{
+	curmute = !!mute;
+	ioval = (ioval & 0xf7) | (curmute << 3);
+	outb(ioval, io);
+}
+
+static int tr_getsigstr(void)
+{
+	int i, v;
+	
+	for(i = 0, v = 0; i < 100; i++) v |= inb(io);
+	return (v & 1)? 0 : 0xffff;
+}
+
+static int tr_getstereo(void)
+{
+	/* don't know how to determine it, just return the setting */
+	return curstereo;
+}
+
+static void tr_setfreq(unsigned long f)
+{
+	f /= 160;	/* Convert to 10 kHz units	*/
+	f += 1070;	/* Add 10.7 MHz IF			*/
+
+	write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
+}
+
+static int tr_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability v;
+
+			v.type=VID_TYPE_TUNER;
+			v.channels=1;
+			v.audios=1;
+
+			/* No we don't do pictures */
+			v.maxwidth=0;
+			v.maxheight=0;
+			v.minwidth=0;
+			v.minheight=0;
+
+			strcpy(v.name, "Trust FM Radio");
+
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+
+			if(copy_from_user(&v, arg,sizeof(v))!=0) 
+				return -EFAULT;
+
+			if(v.tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+
+			v.rangelow = 87500 * 16;
+			v.rangehigh = 108000 * 16;
+			v.flags = VIDEO_TUNER_LOW;
+			v.mode = VIDEO_MODE_AUTO;
+
+			v.signal = tr_getsigstr();
+			if(tr_getstereo())
+				v.flags |= VIDEO_TUNER_STEREO_ON;
+
+			strcpy(v.name, "FM");
+
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner v;
+
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner != 0)
+				return -EINVAL;
+
+			return 0;
+		}
+		case VIDIOCGFREQ:
+			if(copy_to_user(arg, &curfreq, sizeof(curfreq)))
+				return -EFAULT;
+			return 0;
+
+		case VIDIOCSFREQ:
+		{
+			unsigned long f;
+
+			if(copy_from_user(&f, arg, sizeof(curfreq)))
+				return -EFAULT;
+			tr_setfreq(f);
+			return 0;
+		}
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio v;
+
+			memset(&v,0, sizeof(v));
+			v.flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME |
+			          VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
+			v.mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+			v.volume = curvol * 2048;
+			v.step = 2048;
+			v.bass = curbass * 4370;
+			v.treble = curtreble * 4370;
+			
+			strcpy(v.name, "Trust FM Radio");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio v;
+
+			if(copy_from_user(&v, arg, sizeof(v))) 
+				return -EFAULT;	
+			if(v.audio) 
+				return -EINVAL;
+
+			tr_setvol(v.volume);					
+			tr_setbass(v.bass);
+			tr_settreble(v.treble);
+			tr_setstereo(v.mode & VIDEO_SOUND_STEREO);
+			tr_setmute(v.flags & VIDEO_AUDIO_MUTE);
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int tr_open(struct video_device *dev, int flags)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void tr_close(struct video_device *dev)
+{
+	users--;
+	MOD_DEC_USE_COUNT;
+}
+
+static struct video_device trust_radio=
+{
+	"Trust FM Radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_TRUST,
+	tr_open,
+	tr_close,
+	NULL,	/* Can't read  (no capture ability) */
+	NULL,	/* Can't write */
+	NULL,	/* No poll */
+	tr_ioctl,
+	NULL,
+	NULL
+};
+
+static int __init trust_init(void)
+{
+	if(io == -1) {
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	if(check_region(io, 2)) {
+		printk(KERN_ERR "trust: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+	if(video_register_device(&trust_radio, VFL_TYPE_RADIO)==-1)
+		return -EINVAL;
+		
+	request_region(io, 2, "Trust FM Radio");
+
+	printk(KERN_INFO "Trust FM Radio card driver v1.0.\n");
+
+	write_i2c(2, TDA7318_ADDR, 0x80);	/* speaker att. LF = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0xa0);	/* speaker att. RF = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0xc0);	/* speaker att. LR = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0xe0);	/* speaker att. RR = 0 dB */
+	write_i2c(2, TDA7318_ADDR, 0x40);	/* stereo 1 input, gain = 18.75 dB */
+
+	tr_setvol(0x8000);					
+	tr_setbass(0x8000);
+	tr_settreble(0x8000);
+	tr_setstereo(1);
+
+	/* mute card - prevents noisy bootups */
+	tr_setmute(1);
+
+	return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
+
+EXPORT_NO_SYMBOLS;
+
+#endif /* MODULE */
+
+static void __exit cleanup_trust_module(void)
+{
+	video_unregister_device(&trust_radio);
+	release_region(io, 2);
+}
+
+module_init(trust_init);
+module_exit(cleanup_trust_module);
diff -u --recursive --new-file v2.3.18/linux/drivers/char/radio-zoltrix.c linux/drivers/char/radio-zoltrix.c
--- v2.3.18/linux/drivers/char/radio-zoltrix.c	Mon Jul  5 20:07:02 1999
+++ linux/drivers/char/radio-zoltrix.c	Wed Sep 29 14:02:59 1999
@@ -47,6 +47,7 @@
 	unsigned long curfreq;
 	int muted;
 	unsigned int stereo;
+	struct semaphore lock;
 };
 
 
@@ -64,26 +65,30 @@
 	if (dev->muted)
 		return 0;
 
+	down(&dev->lock);
 	if (vol == 0) {
 		outb(0, io);
 		outb(0, io);
 		inb(io + 3);    /* Zoltrix needs to be read to confirm */
+		up(&dev->lock);
 		return 0;
 	}
 
 	outb(dev->curvol-1, io);
 	sleep_delay();
 	inb(io + 2);
-
+	up(&dev->lock);
 	return 0;
 }
 
 static void zol_mute(struct zol_device *dev)
 {
 	dev->muted = 1;
+	down(&dev->lock);
 	outb(0, io);
 	outb(0, io);
 	inb(io + 3);            /* Zoltrix needs to be read to confirm */
+	up(&dev->lock);
 }
 
 static void zol_unmute(struct zol_device *dev)
@@ -107,6 +112,8 @@
 	bitmask = 0xc480402c10080000ull;
 	i = 45;
 
+	down(&dev->lock);
+	
 	outb(0, io);
 	outb(0, io);
 	inb(io + 3);            /* Zoltrix needs to be read to confirm */
@@ -141,14 +148,21 @@
 	inb(io+2);
 
         udelay(1000);
+        
 	if (dev->muted)
 	{
 		outb(0, io);
 		outb(0, io);
 		inb(io + 3);
 		udelay(1000);
-	} else
-        zol_setvol(dev, dev->curvol);
+	}
+	
+	up(&dev->lock);
+	
+	if(!dev->muted)
+	{
+	        zol_setvol(dev, dev->curvol);
+	}
 	return 0;
 }
 
@@ -158,6 +172,7 @@
 {
 	int a, b;
 
+	down(&dev->lock);
 	outb(0x00, io);         /* This stuff I found to do nothing */
 	outb(dev->curvol, io);
 	sleep_delay();
@@ -167,6 +182,8 @@
 	sleep_delay();
 	b = inb(io);
 
+	up(&dev->lock);
+	
 	if (a != b)
 		return (0);
 
@@ -180,6 +197,8 @@
 {
 	int x1, x2;
 
+	down(&dev->lock);
+	
 	outb(0x00, io);
 	outb(dev->curvol, io);
 	sleep_delay();
@@ -189,6 +208,8 @@
 	sleep_delay();
 	x2 = inb(io);
 
+	up(&dev->lock);
+	
 	if ((x1 == x2) && (x1 == 0xcf))
 		return 1;
 	return 0;
@@ -351,6 +372,8 @@
 	request_region(io, 2, "zoltrix");
 	printk(KERN_INFO "Zoltrix Radio Plus card driver.\n");
 
+	init_MUTEX(&zoltrix_unit.lock);
+	
 	/* mute card - prevents noisy bootups */
 
 	/* this ensures that the volume is all the way down  */
diff -u --recursive --new-file v2.3.18/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.3.18/linux/drivers/char/random.c	Tue Aug 31 17:29:13 1999
+++ linux/drivers/char/random.c	Wed Sep 29 14:02:59 1999
@@ -1,7 +1,7 @@
 /*
  * random.c -- A strong random number generator
  *
- * Version 1.88, last modified 30-Aug-99
+ * Version 1.89, last modified 19-Sep-99
  * 
  * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999.  All
  * rights reserved.
@@ -1329,8 +1329,14 @@
  */
 void get_random_bytes(void *buf, int nbytes)
 {
-	extract_entropy(sec_random_state, (char *) buf, nbytes, 
-			EXTRACT_ENTROPY_SECONDARY);
+	if (sec_random_state)  
+		extract_entropy(sec_random_state, (char *) buf, nbytes, 
+				EXTRACT_ENTROPY_SECONDARY);
+	else if (random_state)
+		extract_entropy(random_state, (char *) buf, nbytes, 0);
+	else
+		printk(KERN_NOTICE "get_random_bytes called before "
+				   "random driver initialization\n");
 }
 
 /*********************************************************************
@@ -2012,7 +2018,7 @@
 	if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) {
 		rekey_time = tv.tv_sec;
 		/* First three words are overwritten below. */
-		get_random_bytes(&secret+3, sizeof(secret)-12);
+		get_random_bytes(&secret[3], sizeof(secret)-12);
 		count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS;
 	}
 
diff -u --recursive --new-file v2.3.18/linux/drivers/char/saa5249.c linux/drivers/char/saa5249.c
--- v2.3.18/linux/drivers/char/saa5249.c	Thu Jan 14 22:53:02 1999
+++ linux/drivers/char/saa5249.c	Wed Sep 29 15:32:15 1999
@@ -79,9 +79,9 @@
 
 
 
-#define PAGE_WAIT 30				/* Time in jiffies between requesting page and */
+#define PAGE_WAIT (300*HZ/1000)			/* Time between requesting page and */
 						/* checking status bits */
-#define PGBUF_EXPIRE 1500			/* Time in jiffies to wait before retransmitting */
+#define PGBUF_EXPIRE (15*HZ)			/* Time to wait before retransmitting */
 						/* page regardless of infobits */
 typedef struct {
 	u8 pgbuf[VTX_VIRTUALSIZE];		/* Page-buffer */
@@ -107,9 +107,9 @@
 #define CCTWR 34		/* I˛C write/read-address of vtx-chip */
 #define CCTRD 35
 #define NOACK_REPEAT 10		/* Retry access this many times on failure */
-#define CLEAR_DELAY 5		/* Time in jiffies required to clear a page */
-#define I2C_TIMEOUT 300		/* open/close/SDA-check timeout in jiffies */
-#define READY_TIMEOUT 3		/* Time in jiffies to wait for ready signal of I˛C-bus interface */
+#define CLEAR_DELAY (HZ/20)	/* Time required to clear a page */
+#define I2C_TIMEOUT (3*HZ)	/* open/close/SDA-check timeout */
+#define READY_TIMEOUT (30*HZ/1000)	/* Time to wait for ready signal of I˛C-bus interface */
 #define INIT_DELAY 500		/* Time in usec to wait at initialization of CEA interface */
 #define START_DELAY 10		/* Time in usec to wait before starting write-cycle (CEA) */
 
diff -u --recursive --new-file v2.3.18/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.3.18/linux/drivers/char/serial.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/char/serial.c	Sat Oct  2 07:36:17 1999
@@ -2006,7 +2006,7 @@
 	info->xmit_fifo_size = state->xmit_fifo_size =
 		new_serial.xmit_fifo_size;
 
-	if (state->port)
+	if (state->type != PORT_UNKNOWN && state->port)
 		release_region(state->port,8);
 	if (change_port || change_irq) {
 		/*
@@ -4082,6 +4082,7 @@
 	unsigned long flags;
 	int e1, e2;
 	int i;
+	struct async_struct *info;
 
 	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
 	save_flags(flags);
@@ -4101,6 +4102,11 @@
 	for (i = 0; i < NR_PORTS; i++) {
 		if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port)
 			release_region(rs_table[i].port, 8);
+ 		info = rs_table[i].info;
+ 		if (info) {
+ 			rs_table[i].info = NULL;
+ 			kfree_s(info, sizeof(struct async_struct));
+ 		}
 #if defined(ENABLE_SERIAL_PCI) && defined (CONFIG_SERIAL_PCI_MEMMAPPED)
 		if (rs_table[i].iomem_base)
 			iounmap(rs_table[i].iomem_base);
diff -u --recursive --new-file v2.3.18/linux/drivers/char/softdog.c linux/drivers/char/softdog.c
--- v2.3.18/linux/drivers/char/softdog.c	Thu Aug  5 14:34:02 1999
+++ linux/drivers/char/softdog.c	Sat Oct  2 07:36:17 1999
@@ -23,6 +23,9 @@
  *	Added soft_margin; use upon insmod to change the timer delay.
  *	NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
  *	    minors.
+ *
+ *  19980911 Alan Cox
+ *	Made SMP safe for 2.3.x
  */
  
 #include <linux/module.h>
@@ -106,10 +109,8 @@
 	/*
 	 *	Refresh the timer.
 	 */
-	del_timer(&watchdog_ticktock);
-	watchdog_ticktock.expires=jiffies + (soft_margin * HZ);
-	add_timer(&watchdog_ticktock);
-	return;
+
+	mod_timer(&watchdog_ticktock, jiffies + (soft_margin * HZ));
 }
 
 static ssize_t softdog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
diff -u --recursive --new-file v2.3.18/linux/drivers/char/tuner.c linux/drivers/char/tuner.c
--- v2.3.18/linux/drivers/char/tuner.c	Mon Jun  7 16:17:59 1999
+++ linux/drivers/char/tuner.c	Sat Oct  2 07:36:17 1999
@@ -84,9 +84,11 @@
 	      //  16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623},
 	        16*170.00,16*450.00,0x02,0x04,0x01,0x8e,0xc2,623},
 	{"Temic 4036 FY5 NTSC", TEMIC, NTSC,
-	        16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732},
-        {"Alps HSBH1", TEMIC, NTSC,
-                16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732},
+		16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732},
+	{"Alps TSBH1",TEMIC,NTSC,
+		16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732},
+	{"Alps TSBE1",TEMIC,PAL,
+		16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732},
 };
 
 /* ---------------------------------------------------------------------- */
@@ -192,12 +194,13 @@
 
 	/*
 	 *	For now we only try and attach these tuners to the BT848
-	 *	bus. This same module will however work different species
-	 *	of card using these chips. Just change the constraints
+	 *	or ZORAN bus. This same module will however work different
+	 *	species	of card using these chips. Just change the constraints
 	 *	(i2c doesn't have a totally clash free 'address' space)
 	 */
 	 
-	if(device->bus->id!=I2C_BUSID_BT848)
+	if(device->bus->id!=I2C_BUSID_BT848 &&
+	   device->bus->id!=I2C_BUSID_ZORAN)
 		return -EINVAL;
 		
 	device->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL);
diff -u --recursive --new-file v2.3.18/linux/drivers/char/videodev.c linux/drivers/char/videodev.c
--- v2.3.18/linux/drivers/char/videodev.c	Mon Jul  5 20:09:40 1999
+++ linux/drivers/char/videodev.c	Sat Oct  2 07:36:17 1999
@@ -57,24 +57,12 @@
 #ifdef CONFIG_VIDEO_PLANB
 extern int init_planbs(struct video_init *);
 #endif
-#ifdef CONFIG_RADIO_AZTECH
-extern int aztech_init(struct video_init *);
-#endif
-#ifdef CONFIG_RADIO_RTRACK
-extern int rtrack_init(struct video_init *);
-#endif
 #ifdef CONFIG_RADIO_RTRACK2
 extern int rtrack2_init(struct video_init *);
 #endif
 #ifdef CONFIG_RADIO_SF16FMI
 extern int fmi_init(struct video_init *);
 #endif
-#ifdef CONFIG_RADIO_MIROPCM20
-extern int pcm20_init(struct video_init *);
-#endif
-#ifdef CONFIG_RADIO_GEMTEK
-extern int gemtek_init(struct video_init *);
-#endif
 #ifdef CONFIG_RADIO_TYPHOON
 extern int typhoon_init(struct video_init *);
 #endif
@@ -84,9 +72,6 @@
 #ifdef CONFIG_RADIO_TERRATEC
 extern int terratec_init(struct video_init *);
 #endif
-#ifdef CONFIG_VIDEO_PMS
-extern int init_pms_cards(struct video_init *);
-#endif
 #ifdef CONFIG_VIDEO_ZORAN
 extern int init_zoran_cards(struct video_init *);
 #endif
@@ -105,33 +90,18 @@
 #ifdef CONFIG_VIDEO_BWQCAM
 	{"bw-qcam", init_bw_qcams},
 #endif	
-#ifdef CONFIG_VIDEO_PMS
-	{"PMS", init_pms_cards}, 
-#endif	
 #ifdef CONFIG_VIDEO_PLANB
 	{"planb", init_planbs},
 #endif
-#ifdef CONFIG_RADIO_AZTECH
-	{"Aztech", aztech_init}, 
-#endif	
-#ifdef CONFIG_RADIO_RTRACK
-	{"RTrack", rtrack_init}, 
-#endif 
 #ifdef CONFIG_RADIO_RTRACK2
 	{"RTrack2", rtrack2_init}, 
 #endif
 #ifdef CONFIG_RADIO_SF16FMI
 	{"SF16FMI", fmi_init}, 
 #endif	
-#ifdef CONFIG_RADIO_MIROPCM20
-	{"PCM20", pcm20_init}, 
-#endif
 #ifdef CONFIG_RADIO_CADET
 	{"Cadet", cadet_init},
 #endif
-#ifdef CONFIG_RADIO_GEMTEK
-	{"GemTek", gemtek_init},
-#endif
 #ifdef CONFIG_RADIO_TYPHOON
 	{"radio-typhoon", typhoon_init},
 #endif
@@ -144,7 +114,6 @@
 	{"end", NULL}
 };
 
-#if LINUX_VERSION_CODE >= 0x020100
 /*
  *	Read will do some smarts later on. Buffer pin etc.
  */
@@ -160,7 +129,6 @@
 }
 
 
-
 /*
  *	Write for now does nothing. No reason it shouldnt do overlay setting
  *	for some boards I guess..
@@ -190,31 +158,6 @@
 		return 0;
 }
 
-
-#else
-static int video_read(struct inode *ino,struct file *file,
-			  char *buf, int count)
-{
-         int err;
-	 struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
-	 if (vfl->read)
-	   return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK);
-	 else
-	   return -EINVAL;
-}
-
-static int video_write(struct inode *ino,struct file *file, const char *buf, 
-			int count)
-{
-	int err;
-	struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
-	if (vfl->write)
-	  return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK);
-	else
-	  return 0;
-}
-
-#endif
 
 /*
  *	Open a video device.
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/Config.in linux/drivers/i2o/Config.in
--- v2.3.18/linux/drivers/i2o/Config.in	Wed Jun  2 14:47:42 1999
+++ linux/drivers/i2o/Config.in	Sat Oct  2 07:36:17 1999
@@ -3,10 +3,10 @@
 
 tristate 'I2O support' CONFIG_I2O
 
-dep_tristate 'I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O
-dep_tristate 'I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O
-dep_tristate 'I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O
-dep_tristate 'I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O
-dep_tristate 'I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O
+dep_tristate '  I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O
+dep_tristate '  I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O
+dep_tristate '  I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O
+dep_tristate '  I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O $CONFIG_SCSI
+dep_tristate '  I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O
 
 endmenu
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/README linux/drivers/i2o/README
--- v2.3.18/linux/drivers/i2o/README	Mon Jul  5 20:09:40 1999
+++ linux/drivers/i2o/README	Sat Oct  2 07:36:17 1999
@@ -18,18 +18,19 @@
 Deepak Saxena,	Intel Corp.
 	/proc interface, bug fixes
 	Ioctl interfaces for control
+	Debugging LAN OSM
 
 Philip Rumpf
 	Fixed assorted dumb SMP locking bugs
 
 Juha Sievanen,  University of Helsinki Finland
-	LAN OSM
+	LAN OSM code
 	/proc interface to LAN class
 	Bug fixes
 	Core code extensions
 
 Auvo Häkkinen,  University of Helsinki Finland
-	LAN OSM
+	LAN OSM code
 	/Proc interface to LAN class
 	Bug fixes
 	Core code extensions
@@ -51,13 +52,19 @@
 European Comission
 	Funding the work done by the University of Helsinki
 
+SysKonnect
+        Loan of FDDI and Gigabit Ethernet cards
+
+ASUSTeK
+        Loan of I2O motherboard 
+
 STATUS:
 
 o	The core setup works within limits.
 o	The scsi layer seems to almost work. I'm still chasing down the hang
 	bug.
 o	The block OSM is fairly minimal but does seem to work.
-o	LAN OSM works with FDDI cards.
+o	LAN OSM works with FDDI and Ethernet cards.
 
 TO DO:
 
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/README.ioctl linux/drivers/i2o/README.ioctl
--- v2.3.18/linux/drivers/i2o/README.ioctl	Wed Jun  2 14:40:22 1999
+++ linux/drivers/i2o/README.ioctl	Sat Oct  2 07:36:17 1999
@@ -3,13 +3,13 @@
 rev 0.3 - 04/20/99
 
 =============================================================================
-Originally written by Deepak Saxena(deepak.saxena@intel.com)
-Currently maintained by Deepak Saxena(deepak.saxena@intel.com)
+Originally written by Deepak Saxena(deepak@plexity.net)
+Currently maintained by Deepak Saxena(deepak@plexity.net)
 =============================================================================
 
 I. Introduction
 
-The Linux I2O susbsytem provides a set of ioctl() commands than can be
+The Linux I2O subsystem provides a set of ioctl() commands that can be
 utilized by user space applications to communicate with IOPs and devices
 on individual IOPs. This document defines the specific ioctl() commands
 that are available to the user and provides examples of their uses.
@@ -20,7 +20,7 @@
 
 This document and the I2O user space interface are currently maintained
 by Deepak Saxena.  Please send all comments, errata, and bug fixes to
-deepak.saxena@intel.com
+deepak@plexity.net
 
 II. IOP Access
 
@@ -50,9 +50,9 @@
    Returns 0 if no errors occur, and -1 otherwise.  If an error occurs,
    errno is set appropriately:
 
-      EIO      Unkown error
+     EFAULT   Invalid user space pointer was passed
 
-IV. ExecHrtGet Message
+IV. Getting Hardware Resource Table
 
    SYNOPSIS 
  
@@ -67,25 +67,21 @@
 
    DESCRIPTION
 
-   This function posts an ExecHrtHet message to the IOP specified by
-   hrt->iop and returns the data in the buffer pointed to by hrt->buf
-   The size of the data written is placed into the memory pointed to
-   by hrt->len.
+   This function returns the Hardware Resource Table of the IOP specified 
+   by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of 
+   the data is written into *(hrt->reslen).
 
    RETURNS
 
    This function returns 0 if no errors occur. If an error occurs, -1 
    is returned and errno is set appropriately:
 
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(hrt->reslen)
       EFAULT      Invalid user space pointer was passed
       ENXIO       Invalid IOP number
-      EIO         Unkown error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(hrt->reslen)
   
-V. ExecLctNotify Message
+V. Getting Logical Configuration Table
    
    SYNOPSIS 
  
@@ -100,25 +96,21 @@
 
    DESCRIPTION
 
-   This function posts an ExecLctGet message to the IOP specified by 
-   lct->iop and returns the data in the buffer pointed to by lct->buf
-   The size of the data written is placed into the memory pointed to
-   by lct->reslen.
+   This function returns the Logical Configuration Table of the IOP specified
+   by lct->iop in the buffer pointed to by lct->resbuf. The actual size of 
+   the data is written into *(lct->reslen).
 
    RETURNS
 
    This function returns 0 if no errors occur. If an error occurs, -1 
    is returned and errno is set appropriately:
 
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(lct->reslen)
       EFAULT      Invalid user space pointer was passed
       ENXIO       Invalid IOP number
-      EIO         Unkown error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(lct->reslen)
 
-VI. UtilParamsSet Message
+VI. Settting Parameters
    
    SYNOPSIS 
  
@@ -138,8 +130,8 @@
 
    This function posts a UtilParamsSet message to the device identified
    by ops->iop and ops->tid.  The operation list for the message is 
-   sent through the ops->oplen buffer, and the result list is written
-   into the buffer pointed to by ops->oplen.  The number of bytes 
+   sent through the ops->opbuf buffer, and the result list is written
+   into the buffer pointed to by ops->resbuf.  The number of bytes 
    written is placed into *(ops->reslen). 
 
    RETURNS
@@ -148,19 +140,18 @@
    ops->resbuf if no errors occur.  If an error occurs, -1 is returned 
    and errno is set appropriatly:
 
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
       EFAULT      Invalid user space pointer was passed
       ENXIO       Invalid IOP number
-      EIO         Unkown error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
 
    A return value of 0 does not mean that the value was actually
    changed properly on the IOP.  The user should check the result
    list to determine the specific status of the transaction.
 
-VII. UtilParamsGet Message
+VII. Getting Parameters
    
    SYNOPSIS 
  
@@ -180,25 +171,24 @@
 
    This function posts a UtilParamsGet message to the device identified
    by ops->iop and ops->tid.  The operation list for the message is 
-   sent through the ops->oplen buffer, and the result list is written
-   into the buffer pointed to by ops->oplen.  The actual size of data
+   sent through the ops->opbuf buffer, and the result list is written
+   into the buffer pointed to by ops->resbuf.  The actual size of data
    written is placed into *(ops->reslen).
 
    RETURNS
 
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
       EFAULT      Invalid user space pointer was passed
       ENXIO       Invalid IOP number
-      EIO         Unkown error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
 
    A return value of 0 does not mean that the value was actually
    properly retreived.  The user should check the result list 
    to determine the specific status of the transaction.
 
-VIII. ExecSwDownload Message
+VIII. Downloading Software
    
    SYNOPSIS 
  
@@ -207,21 +197,21 @@
       struct i2o_sw_xfer
       {
          u32   iop;       /* IOP unit number */
-         u8    dl_flags;  /* DownLoadFlags field */
+         u8    flags;     /* DownloadFlags field */
          u8    sw_type;   /* Software type */
          u32   sw_id;     /* Software ID */
          void  *buf;      /* Pointer to software buffer */
-         u32   *swlen;    /* Length of software data */        
+         u32   *swlen;    /* Length of software buffer */        
          u32   *maxfrag;  /* Number of fragments */
          u32   *curfrag;  /* Current fragment number */
       };
 
    DESCRIPTION
 
-   This function downloads the software pointed to by  sw->buf to the
-   iop identified by sw->iop. The DownloadFlags, SwID, and SwType fields
-   of the ExecSwDownload message are filed in with the values of 
-   sw->dl_flags, sw->sw_id, and sw->sw_type.
+   This function downloads the software pointed by sw->buf to the
+   iop identified by sw->iop. The DownloadFlags, SwID, SwType and SwSize 
+   fields of the ExecSwDownload message are filled in with the values of 
+   sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen).
 
    Once the ioctl() is called and software transfer begins, the 
    user can read the value *(sw->maxfrag) and *(sw->curfrag) to
@@ -236,15 +226,12 @@
    This function returns 0 no errors occur. If an error occurs, -1 
    is returned and errno is set appropriatly:
 
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
       EFAULT      Invalid user space pointer was passed
       ENXIO       Invalid IOP number
-      EIO         Unkown error
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
 
-IX. ExecSwUpload Message
+IX. Uploading Software
    
    SYNOPSIS 
  
@@ -253,11 +240,11 @@
       struct i2o_sw_xfer
       {
          u32   iop;      /* IOP unit number */
-         u8    flags;    /* Unused */
+         u8    flags; 	 /* UploadFlags */
          u8    sw_type;  /* Software type */
          u32   sw_id;    /* Software ID */
          void  *buf;     /* Pointer to software buffer */
-         u32   *swlen;   /* Length in bytes of software */        
+         u32   *swlen;   /* Length of software buffer */        
          u32   *maxfrag; /* Number of fragments */
          u32   *curfrag; /* Current fragment number */
       };
@@ -265,10 +252,10 @@
    DESCRIPTION
 
    This function uploads software from the IOP identified by sw->iop
-   and places it in the buffer pointed to by sw->buf.  The SwID, SwType
-   and SwSize fields of the ExecSwDownload message are filed in 
-   with the values of sw->sw_id, sw->sw_type, sw->swlen, and.  The 
-   actual size of the module is written into *(sw->buflen).
+   and places it in the buffer pointed to by sw->buf. The length of the
+   buffer is given in *(sw->swlen). The UploadFlags, SwID and SwType
+   fields of the ExecSwUpload message are filled in 
+   with the values of sw->flags, sw->sw_id and sw->sw_type. 
 
    Once the ioctl() is called and software transfer begins, the 
    user can read the value *(sw->maxfrag) and *(sw->curfrag) to
@@ -283,15 +270,12 @@
    This function returns 0 if no errors occur.  If an error occurs, -1
    is returned and errno is set appropriatly:
 
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
       EFAULT      Invalid user space pointer was passed
       ENXIO       Invalid IOP number
-      EIO         Unkown error
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
          
-X. ExecSwRemove Message
+X. Removing Software
    
    SYNOPSIS 
  
@@ -300,42 +284,61 @@
       struct i2o_sw_xfer
       {
          u32   iop;      /* IOP unit number */
-         u8    flags;    /* Unused */
+         u8    flags; 	 /* RemoveFlags */
          u8    sw_type;  /* Software type */
          u32   sw_id;    /* Software ID */
          void  *buf;     /* Unused */
-         u32   *swlen;   /* Length in bytes of software data */        
+         u32   *swlen;   /* Length of the software data */        
          u32   *maxfrag; /* Unused */
          u32   *curfrag; /* Unused */
       };
 
    DESCRIPTION
 
-   This function uploads software from the IOP identified by sw->iop
-   and places it in the buffer pointed to by sw->buf.  The SwID, SwType
-   and SwSize fields of the ExecSwDownload message are filed in 
-   with the values of sw->dl_flags, sw->sw_id, and sw->sw_type.  The 
-   actual size of the module is written into *(sw->buflen).
+   This function removes software from the IOP identified by sw->iop.
+   The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message 
+   are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and 
+   *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses 
+   *(sw->swlen) value to verify correct identication of the module to remove. 
+   The actual size of the module is written into *(sw->swlen).
 
    RETURNS
 
    This function returns 0 if no errors occur.  If an error occurs, -1
    is returned and errno is set appropriatly:
 
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
       ETIMEDOUT   Timeout waiting for reply message
       ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
-      EFAULT      Invalid user space pointer was passed
+
+X. Validating Configuration
+
+   SYNOPSIS
+
+   ioctl(fd, I2OVALIDATE, int *iop);
+	u32 iop;
+
+   DESCRIPTION
+
+   This function posts an ExecConfigValidate message to the controller
+   identified by iop. This message indicates that the the current
+   configuration is accepted. The iop changes the status of suspect drivers 
+   to valid and may delete old drivers from its store.
+
+   RETURNS
+
+   This function returns 0 if no erro occur.  If an error occurs, -1 is
+   returned and errno is set appropriatly:
+
+      ETIMEDOUT   Timeout waiting for reply message
       ENXIO       Invalid IOP number
-      EIO         Unkown error
 
-X. UtilConfigDialog Message
+XI. Configuration Dialog
    
    SYNOPSIS 
  
    ioctl(fd, I2OHTML, struct i2o_html *htquery);
-
       struct i2o_html
       {
          u32   iop;      /* IOP unit number */
@@ -356,22 +359,21 @@
    string that is to be sent to the device, it should be sent in the buffer
    pointed to by htquery->qbuf.  If there is no query string, this field
    should be set to NULL. The actual size of the reply received is written
-   into *(htquery->reslen)
+   into *(htquery->reslen).
   
    RETURNS
 
    This function returns 0 if no error occur. If an error occurs, -1
-   is returned and J errno is set appropriatly:
+   is returned and errno is set appropriatly:
 
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
       EFAULT      Invalid user space pointer was passed
       ENXIO       Invalid IOP number
-      EIO         Unkown error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
 
-XI. Events
+XII. Events
 
     In the process of determining this.  Current idea is to have use
     the select() interface to allow user apps to periodically poll
@@ -394,5 +396,3 @@
 Rev 0.3 - 04/20/99
 - Changed all ioctls() to use pointers to user data instead of actual data
 - Updated error values to match the code
-
-
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/README.lan linux/drivers/i2o/README.lan
--- v2.3.18/linux/drivers/i2o/README.lan	Mon Jul  5 20:09:40 1999
+++ linux/drivers/i2o/README.lan	Wed Dec 31 16:00:00 1969
@@ -1,38 +0,0 @@
-
-	Linux I2O LAN OSM
-
-	(c) University of Helsinki, Department of Computer Science
-
-	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.
-
-AUTHORS
-	Auvo Häkkinen, Auvo.Hakkinen@cs.Helsinki.FI
-	Juha Sievänen, Juha.Sievanen@cs.Helsinki.FI
-
-CREDITS
-
-	This work was made possible by 
-
-European Commission
-	Funding for the project
-
-SysKonnect
-	Loaning of FDDI cards
-
-ASUSTeK
-	I2O motherboard
-
-STATUS:
-o	The FDDI part of LAN OSM is working to some extent.
-o	Only packet per bucket is now supported.
-
-TO DO:
-
-LAN:
-o	Add support for bactches
-o	Find the bug in i2o_set_multicast_list(), which kills interrupt
-	handler in i2o_wait_reply()
-o	Add support for Ethernet, Token Ring, AnyLAN, Fibre Channel
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/i2o_block.c linux/drivers/i2o/i2o_block.c
--- v2.3.18/linux/drivers/i2o/i2o_block.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/i2o/i2o_block.c	Sat Oct  2 07:36:17 1999
@@ -56,7 +56,8 @@
 
 #define MAX_I2OB	16
 
-#define MAX_I2OB_DEPTH	32
+#define MAX_I2OB_DEPTH	32                
+#define MAX_I2OB_RETRIES 4
 
 /*
  *	Some of these can be made smaller later
@@ -70,8 +71,6 @@
 
 static int i2ob_context;
 
-static spinlock_t i2ob_lock = SPIN_LOCK_UNLOCKED;
-
 struct i2ob_device
 {
 	struct i2o_controller *controller;
@@ -110,22 +109,41 @@
 static struct i2ob_request i2ob_queue[MAX_I2OB_DEPTH+1];
 static struct i2ob_request *i2ob_qhead;
 
+static struct timer_list i2ob_timer;
+static int i2ob_timer_started = 0;
+
 #define DEBUG( s )
 /* #define DEBUG( s ) printk( s ) 
  */
 
 static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int);
 static void i2ob_end_request(struct request *);
-static void do_i2ob_request(void);
+static void i2ob_request(void);
+
+/*
+ * Dump messages.
+ */
+static void i2ob_dump_msg(struct i2ob_device *dev,u32 *msg,int size)
+{
+        int cnt;
+
+        printk(KERN_INFO "\n\ni2o message:\n");
+        for (cnt = 0; cnt<size; cnt++)
+        {
+                printk(KERN_INFO "m[%d]=%x\n",cnt,msg[cnt]);
+        }
+        printk(KERN_INFO "\n");
+}
+
 
 /*
  *	Get a message
  */
 
 static u32 i2ob_get(struct i2ob_device *dev)
-{ 
+{
 	struct i2o_controller *c=dev->controller;
-	return I2O_POST_READ32(c);
+   	return I2O_POST_READ32(c);
 }
  
 /*
@@ -141,15 +159,14 @@
 	u64 offset;
 	struct request *req = ireq->req;
 	struct buffer_head *bh = req->bh;
-	static int old_qd = 2;
 	int count = req->nr_sectors<<9;
+
+	/* Map the message to a virtual address */
+	msg = bus_to_virt(c->mem_offset + m);
 	
 	/*
-	 *	Build a message
+         * Build the message based on the request.
 	 */
-	
-	msg = bus_to_virt(c->mem_offset + m);
-	
 	msg[2] = i2ob_context|(unit<<8);
 	msg[3] = ireq->num;
 	msg[5] = req->nr_sectors << 9;
@@ -212,8 +229,7 @@
 
 	i2o_post_message(c,m);
 	atomic_inc(&queue_depth);
-	if(atomic_read(&queue_depth)>old_qd)
-		old_qd=atomic_read(&queue_depth);
+
 	return 0;
 }
 
@@ -263,6 +279,7 @@
 
 static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg)
 {
+	unsigned long flags;
 	struct i2ob_request *ireq;
 	u8 st;
 	u32 *m = (u32 *)msg;
@@ -319,18 +336,61 @@
 		if(st!=0)
 		{
 			printk(KERN_ERR "i2ob: error %08X\n", m[4]);
-			/*
-			 *	Now error out the request block
-			 */
 			ireq->req->errors++;	
+			if (ireq->req->errors < MAX_I2OB_RETRIES)
+			{
+				u32 retry_msg;
+				struct i2ob_device *dev;
+
+  				printk(KERN_ERR "i2ob: attempting retry %d for request %p\n",ireq->req->errors+1,ireq->req);
+     				
+		                /* 
+				 * Get a message for this retry.
+				 */
+				dev = &i2ob_dev[(unit&0xF0)];
+                		retry_msg = i2ob_get(dev);
+
+                		/* 
+				 * If we cannot get a message then
+				 * forget the retry and fail the
+				 * request.   Note that since this is
+				 * being called from the interrupt 
+				 * handler, a request has just been 
+				 * completed and there will most likely 
+				 * be space on the inbound message
+				 * fifo so this won't happen often.
+				 */
+                		if(retry_msg!=0xFFFFFFFF)
+				{
+			                /*
+			                 * Decrement the queue depth since
+			                 * this request has completed and
+			                 * it will be incremented again when
+			                 * i2ob_send is called below.
+			                 */
+			                atomic_dec(&queue_depth);
+
+			                /*
+			                 * Send the request again.
+			                 */
+			                i2ob_send(retry_msg, dev,ireq,i2ob[unit].start_sect, (unit&0xF0));
+					/*
+					 * Don't fall through.
+					 */
+					return;
+				}
+			}		
 		}
+		else
+			ireq->req->errors = 0;
 	}
+		
 	/*
-	 *	Dequeue the request.
+	 *	Dequeue the request. We use irqsave locks as one day we
+	 *	may be running polled controllers from a BH...
 	 */
 	
-	spin_lock(&io_request_lock);
-	spin_lock(&i2ob_lock);
+	spin_lock_irqsave(&io_request_lock, flags);
 	i2ob_unhook_request(ireq);
 	i2ob_end_request(ireq->req);
 	
@@ -339,9 +399,8 @@
 	 */
 	 
 	atomic_dec(&queue_depth);
-	do_i2ob_request();
-	spin_unlock(&i2ob_lock);
-	spin_unlock(&io_request_lock);
+	i2ob_request();
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 static struct i2o_handler i2o_block_handler =
@@ -352,6 +411,39 @@
 	I2O_CLASS_RANDOM_BLOCK_STORAGE
 };
 
+/*
+ * The timer handler will attempt to restart requests 
+ * that are queued to the driver.  This handler
+ * currently only gets called if the controller
+ * had no more room in its inbound fifo.  
+ */
+
+static void i2ob_timer_handler(unsigned long dummy)
+{
+	unsigned long flags;
+
+	/*
+	 * We cannot touch the request queue or the timer
+         * flag without holding the io_request_lock.
+	 */
+	spin_lock_irqsave(&io_request_lock,flags);
+
+	/* 
+	 * Clear the timer started flag so that 
+	 * the timer can be queued again.
+	 */
+	i2ob_timer_started = 0;
+
+	/* 
+	 * Restart any requests.
+	 */
+	i2ob_request();
+
+	/* 
+	 * Free the lock.
+	 */
+	spin_unlock_irqrestore(&io_request_lock,flags);
+}
 
 /*
  *	The I2O block driver is listed as one of those that pulls the
@@ -361,7 +453,7 @@
  *	we use it.
  */
 
-static void do_i2ob_request(void)
+static void i2ob_request(void)
 {
 	struct request *req;
 	struct i2ob_request *ireq;
@@ -391,13 +483,36 @@
 		dev = &i2ob_dev[(unit&0xF0)];
 		/* Get a message */
 		m = i2ob_get(dev);
-		/* No messages -> punt 
-		   FIXME: if we have no messages, and there are no messages 
-		   we deadlock now. Need a timer/callback ?? */
+
 		if(m==0xFFFFFFFF)
 		{
-			printk("i2ob: no messages!\n");
-			break;
+			/* 
+			 * See if the timer has already been queued.
+			 */
+			if (!i2ob_timer_started)
+			{
+				printk(KERN_ERR "i2ob: starting timer\n");
+
+				/*
+				 * Set the timer_started flag to insure
+				 * that the timer is only queued once.
+				 * Queing it more than once will corrupt
+				 * the timer queue.
+				 */
+				i2ob_timer_started = 1;
+
+				/* 
+				 * Set up the timer to expire in
+				 * 500ms.
+				 */
+				i2ob_timer.expires = jiffies + (HZ >> 1);
+
+				/*
+				 * Start it.
+				 */
+				 
+				add_timer(&i2ob_timer);
+			}
 		}
 		req->errors = 0;
 		CURRENT = CURRENT->next;
@@ -412,13 +527,6 @@
 	}
 }
 
-static void i2ob_request(void)
-{
-	unsigned long flags;
-	spin_lock_irqsave(&i2ob_lock, flags);
-	do_i2ob_request();
-	spin_unlock_irqrestore(&i2ob_lock, flags);
-}	
 
 /*
  *	SCSI-CAM for ioctl geometry mapping
@@ -1010,6 +1118,14 @@
 	/* Queue is MAX_I2OB + 1... */
 	i2ob_queue[i].next = NULL;
 	i2ob_qhead = &i2ob_queue[0];
+	
+	/*
+	 *	Timers
+	 */
+	 
+	init_timer(&i2ob_timer);
+	i2ob_timer.function = i2ob_timer_handler;
+	i2ob_timer.data = 0;
 	
 	/*
 	 *	Register the OSM handler as we will need this to probe for
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/i2o_config.c linux/drivers/i2o/i2o_config.c
--- v2.3.18/linux/drivers/i2o/i2o_config.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/i2o/i2o_config.c	Sat Oct  2 07:36:17 1999
@@ -9,6 +9,9 @@
  *         - Added basic ioctl() support
  *      Modified 06/07/1999 by Deepak Saxena
  *         - Added software download ioctl (still testing)
+ *	Modified 09/10/1999 by Auvo Häkkinen
+ *	   - Changes to i2o_cfg_reply(), ioctl_parms()
+ *	   - Added ioct_validate() (not yet tested)
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -36,11 +39,6 @@
 static int i2o_cfg_context = -1;
 static void *page_buf;
 static void *i2o_buffer;
-static int i2o_ready;
-static int i2o_pagelen;
-static int i2o_error;
-static int cfg_inuse;
-static int i2o_eof;
 static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED;
 struct wait_queue *i2o_wait_queue;
 
@@ -52,6 +50,7 @@
 static int ioctl_swdl(unsigned long);
 static int ioctl_swul(unsigned long);
 static int ioctl_swdel(unsigned long);
+static int ioctl_validate(unsigned long); 
 
 /*
  *	This is the callback for any message we have posted. The message itself
@@ -61,8 +60,13 @@
  */
 static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m)
 {
-	i2o_cfg_token = I2O_POST_WAIT_OK;
-
+        u32 *msg = (u32 *)m;
+        
+	if (msg[4] >> 24) // RegStatus != SUCCESS
+        	i2o_cfg_token = -(msg[4] & 0xFFFF); // DetailedStatus
+        else
+		i2o_cfg_token = I2O_POST_WAIT_OK;
+               
 	return;
 }
 
@@ -84,7 +88,6 @@
 	return -ESPIPE;
 }
 
-/* i2ocontroller/i2odevice/page/?data */
 
 static ssize_t cfg_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
@@ -93,12 +96,15 @@
 	return 0;
 }
 
-/* To be written for event management support */
+
 static ssize_t cfg_read(struct file *file, char *buf, size_t count, loff_t *ptr)
 {
 	return 0;
 }
 
+/*
+ * IOCTL Handler
+ */
 static int cfg_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	unsigned long arg)
 {
@@ -141,6 +147,10 @@
 			ret = ioctl_swdel(arg);
 			break;
 
+		case I2OVALIDATE:
+			ret = ioctl_validate(arg);
+			break;
+			
 		case I2OHTML:
 			ret = ioctl_html(arg);
 			break;
@@ -168,13 +178,11 @@
 		c = i2o_find_controller(i);
 		if(c)
 		{
-			printk(KERN_INFO "ioctl: iop%d found\n", i);
 			foo[i] = 1;
 			i2o_unlock_controller(c);
 		}
 		else
 		{
-			printk(KERN_INFO "ioctl: iop%d not found\n", i);
 			foo[i] = 0;
 		}
 	}
@@ -189,10 +197,7 @@
 	struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg;
 	struct i2o_cmd_hrtlct kcmd;
 	pi2o_hrt hrt;
-	u32 msg[6];
-	u32 *workspace;
 	int len;
-	int token;
 	u32 reslen;
 	int ret = 0;
 
@@ -208,42 +213,20 @@
 	c = i2o_find_controller(kcmd.iop);
 	if(!c)
 		return -ENXIO;
+		
+	hrt = (pi2o_hrt)c->hrt;
 
-	workspace = kmalloc(8192, GFP_KERNEL);
-	hrt = (pi2o_hrt)workspace;
-	if(workspace==NULL)
-	{
-		i2o_unlock_controller(c);
-		return -ENOMEM;
-	}
-
-	memset(workspace, 0, 8192);
-
-	msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
-	msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
-	msg[2]= (u32)cfg_handler.context;
-	msg[3]= 0;
-	msg[4]= (0xD0000000 | 8192);
-	msg[5]= virt_to_phys(workspace);
-
-	token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_cfg_token,2);
-	if(token == I2O_POST_WAIT_TIMEOUT)
-	{
-		kfree(workspace);
-		i2o_unlock_controller(c);
-		return -ETIMEDOUT;
-	}
 	i2o_unlock_controller(c);
 
 	len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
+	
 	/* We did a get user...so assuming mem is ok...is this bad? */
 	put_user(len, kcmd.reslen);
 	if(len > reslen)
 		ret = -ENOBUFS;	
 	if(copy_to_user(kcmd.resbuf, (void*)hrt, len))
-		ret = -EINVAL;
+		ret = -EFAULT;
 
-	kfree(workspace);
 	return ret;
 }
 
@@ -253,10 +236,7 @@
 	struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg;
 	struct i2o_cmd_hrtlct kcmd;
 	pi2o_lct lct;
-	u32 msg[9];
-	u32 *workspace;
 	int len;
-	int token;
 	int ret = 0;
 	u32 reslen;
 
@@ -273,32 +253,7 @@
 	if(!c)
 		return -ENXIO;
 
-	workspace = kmalloc(8192, GFP_KERNEL);
-	lct = (pi2o_lct)workspace;
-	if(workspace==NULL)
-	{
-		i2o_unlock_controller(c);
-		return -ENOMEM;
-	}
-
-	memset(workspace, 0, 8192);
-
-	msg[0]= EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6;
-	msg[1]= I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
-	msg[2]= (u32)cfg_handler.context;
-	msg[3]= 0;
-	msg[4]= 0xFFFFFFFF;
-	msg[5]= 0;
-	msg[6]= (0xD0000000 | 8192);
-	msg[7]= virt_to_phys(workspace);
-
-	token = i2o_post_wait(c, ADAPTER_TID, msg, 8*4, &i2o_cfg_token,2);
-	if(token == I2O_POST_WAIT_TIMEOUT)
-	{
-		kfree(workspace);
-		i2o_unlock_controller(c);
-		return -ETIMEDOUT;
-	}
+	lct = (pi2o_lct)c->lct;
 	i2o_unlock_controller(c);
 
 	len = (unsigned int)lct->table_size << 2;
@@ -306,9 +261,8 @@
 	if(len > reslen)
 		ret = -ENOBUFS;	
 	else if(copy_to_user(kcmd.resbuf, (void*)lct, len))
-		ret = -EINVAL;
+		ret = -EFAULT;
 
-	kfree(workspace);
 	return ret;
 }
 
@@ -318,16 +272,10 @@
 	struct i2o_controller *c;
 	struct i2o_cmd_psetget *cmd = (struct i2o_cmd_psetget*)arg;
 	struct i2o_cmd_psetget kcmd;
-	u32 msg[9];
 	u32 reslen;
-	int token;
 	u8 *ops;
 	u8 *res;
-	u16 *res16;	
-	u32 *res32;
-	u16 count;
 	int len;
-	int i,j;
 
 	u32 i2o_cmd = (type == I2OPARMGET ? 
 				I2O_CMD_UTIL_PARAMS_GET :
@@ -369,47 +317,14 @@
 		return -ENOMEM;
 	}
 
-	res16 = (u16*)res;
-
-	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
-	msg[1]=i2o_cmd<<24|HOST_TID<<12|cmd->tid;
-	msg[2]=(u32)cfg_handler.context;
-	msg[3]=0;
-	msg[4]=0;
-	msg[5]=0x54000000|kcmd.oplen;
-	msg[6]=virt_to_bus(ops);
-	msg[7]=0xD0000000|(65536);
-	msg[8]=virt_to_bus(res);
-
-	/*
-	 * Parm set sometimes takes a little while for some reason
-	 */
-	token = i2o_post_wait(c, kcmd.tid, msg, 9*4, &i2o_cfg_token,10);
-	if(token == I2O_POST_WAIT_TIMEOUT)
-	{
-		i2o_unlock_controller(c);
-		kfree(ops);
-		kfree(res);
-		return -ETIMEDOUT;
-	}
-	i2o_unlock_controller(c);
+        len = i2o_issue_params(i2o_cmd, c, kcmd.tid, cfg_handler.context,
+        			ops, kcmd.oplen, res, 65536, &i2o_cfg_token);
+        i2o_unlock_controller(c);
 	kfree(ops);
-
-	/* 
-  	 * Determine required size...there's got to be a quicker way? 
-	 * Dump data to syslog for debugging failures
-	 */
-	count = res16[0];
-	printk(KERN_INFO "%0#6x\n%0#6x\n", res16[0], res16[1]);
-	len = 4;
-	res16 += 2;
-	for(i = 0; i < count; i++ )
-	{
-		len += res16[0] << 2;	/* BlockSize field in ResultBlock */
-		res32 = (u32*)res16;
-		for(j = 0; j < res16[0]; j++)
-			printk(KERN_INFO "%0#10x\n", res32[j]);
-		res16 += res16[0] << 1;	/* Shift to next block */
+        
+	if (len < 0) {
+		kfree(res);
+		return len; /* -DetailedStatus */
 	}
 
 	put_user(len, kcmd.reslen);
@@ -499,7 +414,7 @@
 	}
 
 	token = i2o_post_wait(c, cmd->tid, msg, 9*4, &i2o_cfg_token, 10);
-	if(token == I2O_POST_WAIT_TIMEOUT)
+	if(token != I2O_POST_WAIT_OK)
 	{
 		i2o_unlock_controller(c);
 		kfree(res);
@@ -529,7 +444,7 @@
 	struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg;
 	unsigned char maxfrag = 0, curfrag = 0;
 	unsigned char buffer[8192];
-	u32 msg[MSG_FRAME_SIZE/4];
+	u32 msg[9];
 	unsigned int token = 0, diff = 0, swlen = 0, swxfer = 0;
 	struct i2o_controller *c;
 	int foo = 0;
@@ -588,11 +503,11 @@
 		return -ENXIO;
 	printk("*** foo%d ***\n", foo++);
 
-	msg[0]= EIGHT_WORD_MSG_SIZE| SGL_OFFSET_7;
+	msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7;
 	msg[1]= I2O_CMD_SW_DOWNLOAD<<24 | HOST_TID<<12 | ADAPTER_TID;
 	msg[2]= (u32)cfg_handler.context;
 	msg[3]= 0;
-	msg[4]= ((u32)kxfer.dl_flags)<<24|((u32)kxfer.sw_type)<<16|((u32)maxfrag)<<8|((u32)curfrag);
+	msg[4]= ((u32)kxfer.flags)<<24|((u32)kxfer.sw_type)<<16|((u32)maxfrag)<<8|((u32)curfrag);
 	msg[5]= swlen;
 	msg[6]= kxfer.sw_id;
 	msg[7]= (0xD0000000 | 8192);
@@ -611,14 +526,15 @@
 		msg[4] |= (u32)curfrag;
 
 		__copy_from_user(buffer, kxfer.buf, 8192);
-		swxfer += 8129;
+		swxfer += 8192;
 
 		// Yes...that's one minute, but the spec states that
 		// transfers take a long time, and I've seen just how
 		// long they can take.
-		token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_cfg_token,60);
-		if( token == I2O_POST_WAIT_TIMEOUT )	// Something very wrong
+		token = i2o_post_wait(c, ADAPTER_TID, msg, sizeof(msg), &i2o_cfg_token,60);
+		if (token != I2O_POST_WAIT_OK )	// Something very wrong
 		{
+			i2o_unlock_controller(c);
 			printk("Timeout downloading software");
 			return -ETIMEDOUT;
 		}
@@ -631,13 +547,15 @@
 	msg[4] |= (u32)maxfrag;
 	msg[7] = (0xD0000000 | diff);
 	__copy_from_user(buffer, kxfer.buf, 8192);
-	token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_cfg_token,60);
-	if( token == I2O_POST_WAIT_TIMEOUT )	// Something very wrong
+	token = i2o_post_wait(c, ADAPTER_TID, msg, sizeof(msg), &i2o_cfg_token,60);
+	if( token != I2O_POST_WAIT_OK )	// Something very wrong
 	{
+		i2o_unlock_controller(c);
 		printk("Timeout downloading software");
 		return -ETIMEDOUT;
 	}
 	__put_user(curfrag, kxfer.curfrag);
+	i2o_unlock_controller(c);
 
 	return 0;
 }
@@ -651,9 +569,39 @@
 /* To be written */
 int ioctl_swdel(unsigned long arg)
 {
-	return 0;
+	return -EINVAL;
 }
 
+int ioctl_validate(unsigned long arg)
+{
+        int token;
+        int iop = (int)arg;
+        u32 msg[4];
+        struct i2o_controller *c;
+
+        c=i2o_find_controller(iop);
+        if (!c)
+                return -ENXIO;
+
+        msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+        msg[1] = I2O_CMD_CONFIG_VALIDATE<<24 | HOST_TID<<12 | iop;
+        msg[2] = (u32)i2o_cfg_context;
+        msg[3] = 0;
+
+        token = i2o_post_wait(c, ADAPTER_TID, msg, sizeof(msg),&i2o_cfg_token, 10);
+        i2o_unlock_controller(c);
+
+        if (token != I2O_POST_WAIT_OK)
+        {
+                printk("Can't validate configuration, ErrorStatus = %d\n",
+                	token);
+                return -ETIMEDOUT;
+        }
+
+        return 0;
+}   
+
+
 static int cfg_open(struct inode *inode, struct file *file)
 {
 	/* 
@@ -693,7 +641,7 @@
 #ifdef MODULE
 int init_module(void)
 #else
-__init int i2o_config_init(void)
+int __init i2o_config_init(void)
 #endif
 {
 	printk(KERN_INFO "i2o configuration manager v 0.02\n");
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/i2o_core.c linux/drivers/i2o/i2o_core.c
--- v2.3.18/linux/drivers/i2o/i2o_core.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/i2o/i2o_core.c	Sat Oct  2 07:36:17 1999
@@ -24,9 +24,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 
-#if defined(CONFIG_I2O_PCI) || defined (CONFIG_I2O_PCI_MODULE)
 #include <linux/i2o.h>
-#endif
 
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -261,11 +259,12 @@
 int i2o_delete_controller(struct i2o_controller *c)
 {
 	struct i2o_controller **p;
+	int users;
 
 	spin_lock(&i2o_configuration_lock);
-	if(atomic_read(&c->users))
+	if((users=atomic_read(&c->users)))
 	{
-		printk("Someone is using controller iop%d\n", c->unit);
+		printk("I2O: %d users for controller iop%d\n", users, c->unit);
 		spin_unlock(&i2o_configuration_lock);
 		return -EBUSY;
 	}
@@ -278,7 +277,6 @@
 			return -EBUSY;
 		}
 	}
-//	c->destructor(c); /* We dont want to free the IRQ yet */
 
 	p=&i2o_controller_chain;
 
@@ -397,7 +395,8 @@
 			err = -ENOENT;
 		else
 		{
-			if(i2o_issue_claim(d->controller,d->id, h->context, 0, &reply_flag, type) < 0)
+			if(i2o_issue_claim(d->controller, d->id, h->context, 0,
+					   &reply_flag, type) < 0)
 			{
 				err = -ENXIO;
 			}
@@ -420,7 +419,7 @@
 		atomic_dec(&d->controller->users);
 
 		if(i2o_issue_claim(d->controller,d->id, h->context, 0, 
-									&reply_flag, type) < 0)
+				   &reply_flag, type) < 0)
 			err = -ENXIO;
 	}
 
@@ -2172,6 +2171,7 @@
 EXPORT_SYMBOL(i2o_post_this);
 EXPORT_SYMBOL(i2o_post_wait);
 EXPORT_SYMBOL(i2o_issue_claim);
+EXPORT_SYMBOL(i2o_issue_params);
 
 EXPORT_SYMBOL(i2o_report_status);
 
@@ -2218,7 +2218,7 @@
 extern int i2o_proc_init(void);
 extern int i2o_scsi_init(void);
 
-__init int i2o_init(void)
+int __init i2o_init(void)
 {
         if (i2o_install_handler(&i2o_core_handler) < 0)
         {
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/i2o_lan.c linux/drivers/i2o/i2o_lan.c
--- v2.3.18/linux/drivers/i2o/i2o_lan.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/i2o/i2o_lan.c	Sat Oct  2 07:36:17 1999
@@ -1,19 +1,19 @@
 /*
- * 	linux/drivers/i2o/i2o_lan.c
+ *	linux/drivers/i2o/i2o_lan.c
  *
- *    	I2O LAN CLASS OSM 	Prototyping, July 16th 1999
+ * 	I2O LAN CLASS OSM 	Prototyping, September 17th 1999
  *
  *	(C) Copyright 1999 	University of Helsinki,
  *				Department of Computer Science
  *
- *   	This code is still under development / test.
+ * 	This code is still under development / test.
  *
- *      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 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.    
  *
- * 	Authors: 	Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ *	Authors: 	Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
  *			Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
  *			Deepak Saxena <deepak@plexity.net>
  *
@@ -61,7 +61,6 @@
 	u8 unit;
 	struct i2o_device *i2o_dev;
 	int reply_flag; 		/* needed by scalar/table queries */
-	u32 packet_tresh;		/* treshold for incoming skb's */	
 	struct fddi_statistics stats;   /* see also struct net_device_stats */ 
 	unsigned short (*type_trans)(struct sk_buff *, struct net_device *);
 	/* 
@@ -70,12 +69,22 @@
 	 * the DDM with buckets.
 	 */
 	u32	bucket_count;
+
+   /*
+    * Keep track of no. of outstanding TXes
+    */
+   u32   tx_count;
+   u32   max_tx;
+   u32   tx_full;
+
+   spinlock_t lock;
+
 };
 
 /* function prototypes */
-static int i2o_lan_receive_post(struct net_device *dev);
-static int i2o_lan_receive_post_reply(struct net_device *dev, struct i2o_message *m);
-static void i2o_lan_release_buckets(u32 *msg, struct i2o_lan_local *priv);
+static int i2o_lan_receive_post(struct net_device *dev, u32 count);
+static int i2o_lan_receive_post_reply(struct net_device *dev, u32 *msg);
+static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg);
 
 /*
  * Module params
@@ -90,15 +99,6 @@
 	u32 *msg = (u32 *)m;
 	u8 unit  = (u8)(msg[2]>>16); // InitiatorContext
 	struct net_device *dev = i2o_landevs[unit];
-	struct i2o_lan_local *priv; 
-
-	if(dev)
-		priv = (struct i2o_lan_local *)dev->priv;
-	else
-		priv = NULL;
-	
-	dprintk("Unit: %d Function: %#x\n",
-				unit, msg[1]>>24);
 
     	if (msg[0] & (1<<13)) // Fail bit is set
  	{
@@ -126,20 +126,16 @@
 		{
 			if(!(msg[4]>>24))
 			{
-				i2o_lan_receive_post_reply(dev,m);
+				i2o_lan_receive_post_reply(dev,msg);
 				break;
 			}
-			else
-			{
-				// Something VERY wrong if this is happening
-				 printk( KERN_WARNING "i2olan: Device %s rejected bucket post\n", dev->name);
-				i2o_lan_release_buckets(msg,priv);
-			}
-		}
-		else
-		{
-			i2o_lan_release_buckets(msg,priv);	
+
+			// Something VERY wrong if this is happening
+			printk( KERN_WARNING "i2olan: Device %s rejected bucket post\n", dev->name);
 		}
+
+		// Getting unused buckets back
+		i2o_lan_release_buckets(dev,msg);	
 	
 		break;
 	}
@@ -147,16 +143,21 @@
 	case LAN_PACKET_SEND:
 	case LAN_SDU_SEND: 
 	{
+		struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
 		u8 trl_count  = msg[3] & 0x000000FF; 	
 		
 		do { 	// The HDM has handled the outgoing packet
 			dev_kfree_skb((struct sk_buff *)msg[4 + trl_count]);
 			dprintk(KERN_INFO "%s: Request skb freed (trl_count=%d).\n",
 				dev->name,trl_count);
+			priv->tx_count--;
 		} while (--trl_count);
 		
-		dev->tbusy = 0;
-		mark_bh(NET_BH); /* inform upper layers */
+		if(dev->tbusy)
+		{
+			clear_bit(0,(void*)&dev->tbusy);
+			mark_bh(NET_BH); /* inform upper layers */
+		}
 	
 		break;	
 	}
@@ -164,21 +165,20 @@
 	default: 
 		if (msg[2] & 0x80000000)  // reply to a UtilParamsGet/Set
 		{	
-	   	int *flag = (int *)msg[3]; // flag for i2o_post_wait
+	   		int *flag = (int *)msg[3]; // flag for i2o_post_wait
 			if (msg[4] >> 24) // ReqStatus != SUCCESS
-			{
-	     	 	*flag = -(msg[4] & 0xFFFF); // DetailedStatus
+	     	 		*flag = -(msg[4] & 0xFFFF); // DetailedStatus
+	     		else
+	     			*flag = I2O_POST_WAIT_OK;
 	     	}
-	     	else
-	     		*flag = I2O_POST_WAIT_OK;
-		}
 	}
 }
 
-void i2o_lan_release_buckets(u32 *msg, struct i2o_lan_local *priv)
+void i2o_lan_release_buckets(struct net_device *dev, u32 *msg)
 {
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
 	u8 trl_count  = (u8)(msg[3] & 0x000000FF); 	
-   u32 *pskb = &msg[6];
+	u32 *pskb = &msg[6];
 	
 	while (trl_count) 
 	{	
@@ -186,8 +186,7 @@
 			(struct sk_buff*)(*pskb));
 		dev_kfree_skb((struct sk_buff*)(*pskb)); 
 		pskb++;
-		if(priv)
-			priv->bucket_count--;
+		priv->bucket_count--;
 		trl_count--;
 	}
 }
@@ -202,9 +201,8 @@
 static int lan_context;
 
 
-static int i2o_lan_receive_post_reply(struct net_device *dev, struct i2o_message *m)
+static int i2o_lan_receive_post_reply(struct net_device *dev, u32 *msg)
 {
-	u32 *msg = (u32 *)m;
 	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
 	struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6];
 	struct i2o_packet_info *packet;
@@ -212,19 +210,23 @@
 	u8 trl_count  = msg[3] & 0x000000FF; 	
 	struct sk_buff *skb, *newskb;
 
+static int n_calls = 0;
+n_calls++;
+
 #if 0
 	dprintk(KERN_INFO "TrlFlags = 0x%02X, TrlElementSize = %d, TrlCount = %d\n"
 		"msgsize = %d, buckets_remaining = %d\n", 
 		msg[3]>>24, msg[3]&0x0000FF00, trl_count, msg[0]>>16, msg[5]);	
 #endif
 
-	dprintk(KERN_INFO "Buckets_remaining = %d, bucket_count = %d\n",
-		msg[5], priv->bucket_count);
-
-	do {
+	while (trl_count--)
+	{
 		skb = (struct sk_buff *)(bucket->context);		
 		packet = (struct i2o_packet_info *)bucket->packet_info;	
 		priv->bucket_count--;
+
+		dprintk(KERN_INFO "Buckets_remaining = %d, bucket_count = %d, trl_count = %d\n",
+			msg[5], priv->bucket_count, trl_count);
 #if 0
 		dprintk(KERN)INFO "flags = 0x%02X, offset = 0x%06X, status = 0x%02X, length = %d\n",
 			packet->flags, packet->offset, packet->status, packet->len);
@@ -234,8 +236,7 @@
 					dev_alloc_skb(packet->len+2);	
 			if (newskb) {
 				skb_reserve(newskb,2);
-				memcpy(skb_put(newskb,packet->len), 
-			       skb->data, packet->len);
+				memcpy(skb_put(newskb,packet->len), skb->data, packet->len);
 				newskb->dev = dev;
 				newskb->protocol = priv->type_trans(newskb, dev);
 
@@ -247,7 +248,6 @@
 				return -ENOMEM;
 			}
 		} else {
-
 			skb_put(skb,packet->len);		
 			skb->dev  = dev;		
 			skb->protocol = priv->type_trans(skb, dev);
@@ -258,33 +258,25 @@
 			"to upper level.\n",dev->name,packet->len);
 
 		bucket++; // to next Packet Descriptor Block
-	} while (--trl_count);
-
-	if (priv->bucket_count <= bucketthresh) // BucketsRemaining
-	{
-		dprintk("Bucket_count = %d, ",priv->bucket_count);
-		i2o_lan_receive_post(dev);
 	}
 
+	if (priv->bucket_count < bucketpost - bucketthresh)
+		i2o_lan_receive_post(dev, bucketpost - priv->bucket_count);
 
-	if((msg[4]& 0x0000ffff) == 0x05) // I2O_LAN_RECEIVE_OVERRUN
+	if ((msg[4] & 0x000000FF) == I2O_LAN_DSC_BUCKET_OVERRUN)
 	{
-		printk("Bucket overrun! priv->bucketcount = %d\n",
-					priv->bucket_count);
+		printk(KERN_INFO "%s: DDM out of buckets (count = %d)! "
+			 "Number of posts = %d\n", dev->name, msg[5], n_calls);
+		n_calls = 0;
 	}
 
 	return 0;
 }
 
-/*  ====================================================
- *  Interface to i2o:  functions to send lan class request 
- */
-
-
 /* 
  * i2o_lan_receive_post(): Post buckets to receive packets.
  */
-static int i2o_lan_receive_post(struct net_device *dev)
+static int i2o_lan_receive_post(struct net_device *dev, u32 count)
 {	
 	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
 	struct i2o_device *i2o_dev = priv->i2o_dev;
@@ -298,16 +290,16 @@
 	u32 total = 0;
 	int i;
 
-	while (total < bucketpost)
+	while (total < count)
 	{
 		m = I2O_POST_READ32(iop);
 		if (m == 0xFFFFFFFF)
 			return -ETIMEDOUT;		
 		msg = bus_to_virt(iop->mem_offset + m);
-	
-		bucket_count = (total + n_elems < bucketpost)
+
+		bucket_count = (total + n_elems < count)
 			     ? n_elems
-			     : bucketpost - total;
+			     : count - total;
 
 		msg[0] = I2O_MESSAGE_SIZE(4 + 3 *  bucket_count) | SGL_OFFSET_4;
 		msg[1] = LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->id;
@@ -440,11 +432,7 @@
 {
 	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
 	struct i2o_device *i2o_dev = priv->i2o_dev;	
-	struct i2o_controller *iop = i2o_dev->controller;
 
-/*	if (i2o_issue_claim(iop, i2o_dev->id, priv->unit << 16 | lan_context, 1, 
-			    &priv->reply_flag) < 0)
-*/
 	if(i2o_claim_device(i2o_dev, &i2o_lan_handler, I2O_CLAIM_PRIMARY))
 	{
 		printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name);
@@ -457,10 +445,8 @@
 	dev->tbusy = 0;
 	dev->start = 1;
 
-	priv->packet_tresh = dev->mtu - (dev->mtu >> 3);
-
 	i2o_set_batch_mode(dev);
-	i2o_lan_receive_post(dev);
+	i2o_lan_receive_post(dev, bucketpost);
 
 	MOD_INC_USE_COUNT;
 
@@ -474,21 +460,11 @@
 {
 	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
 	struct i2o_device *i2o_dev = priv->i2o_dev;	
-	struct i2o_controller *iop = i2o_dev->controller;	
 
 	dev->tbusy = 1;
 	dev->start = 0;
-
-// This is the right place for LanSuspend, but it seems to cause 
-// a kernel crash when we are using 82558 HDM proto
-
 	i2o_lan_suspend(dev);
 
-/*
-	if (i2o_issue_claim(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0, 
-			    &priv->reply_flag) < 0)
-*/
-
 	if(i2o_release_device(i2o_dev, &i2o_lan_handler, I2O_CLAIM_PRIMARY))
 		printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device "
 		       "(tid=%d)\n", dev->name, i2o_dev->id);
@@ -498,6 +474,7 @@
 	return 0;
 }
 
+#if 0
 /* 
  * i2o_lan_sdu_send(): Send a packet, MAC header added by the HDM.
  * Must be supported by Fibre Channel, optional for Ethernet/802.3, 
@@ -505,44 +482,9 @@
  */
 static int i2o_lan_sdu_send(struct sk_buff *skb, struct net_device *dev)
 {	
-#if 0
-/* not yet tested */
-        struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; 
-        struct i2o_device *i2o_dev = priv->i2o_dev;     
-        struct i2o_controller *iop = i2o_dev->controller;
-        u32 m; u32 *msg;
-
-	dprintk(KERN_INFO "LanSDUSend called, skb->len = %d\n", skb->len);
-
-        m = I2O_POST_READ32(iop);
-	if (m == 0xFFFFFFFF) 
-	{
-		dev_kfree_skb(skb);
-		return -ETIMEDOUT;
-	}    
-	msg = bus_to_virt(iop->mem_offset + m);
-
-        msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_4;
-        msg[1] = LAN_SDU_SEND<<24 | HOST_TID<<12 | i2o_dev->id; 
-        msg[2] = priv->unit << 16 | lan_context; // IntiatorContext
-        msg[3] = 1<<4;		// TransmitControlWord: suppress CRC generation
-
-        // create a simple SGL, see fig. 3-26
-        // D7 = 1101 0111 = LE eob 0 1 LA dir bc1 bc0
-
-        msg[4] = 0xD7000000 | (skb->len);  // no MAC hdr included     
-        msg[5] = (u32)skb;		   // TransactionContext
-        memcpy(&msg[6], skb->data, 8);	   // Destination MAC Addr ??
-        msg[7] &= 0x0000FFFF;		   // followed by two bytes zeros
-        msg[8] = virt_to_bus(skb->data);
-        dev->trans_start = jiffies;
-	i2o_post_message(iop,m);
-
-	dprintk(KERN_INFO "%s: Packet (%d bytes) sent to network.\n",
-		dev->name,skb->len);
-#endif
-        return 0;
+        return -EINVAL;
 }
+#endif
 
 /* 
  * i2o_lan_packet_send(): Send a packet as is, including the MAC header.
@@ -555,10 +497,24 @@
 	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
 	struct i2o_device *i2o_dev = priv->i2o_dev;	
 	struct i2o_controller *iop = i2o_dev->controller;
-        u32 m; u32 *msg;
+	u32 m, *msg;
+	u32 flags = 0;
+
+	/* 
+	 * Keep interrupt from changing dev->tbusy from underneath us
+	 * (Do we really need to do this?)
+	 */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if(test_and_set_bit(0,(void*)&dev->tbusy) != 0)
+	{
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return 1;
+	}
 
-        m = I2O_POST_READ32(iop);
+	m = I2O_POST_READ32(iop);
 	if (m == 0xFFFFFFFF) {
+		spin_unlock_irqrestore(&priv->lock, flags);
 		dev_kfree_skb(skb);
 		return -ETIMEDOUT;
 	}    
@@ -576,7 +532,14 @@
 	msg[5] = (u32)skb; 			// TransactionContext
 	msg[6] = virt_to_bus(skb->data);
 
-        i2o_post_message(iop,m);
+	i2o_post_message(iop,m);
+
+	// Check to see if HDM queue is full..if so...stay busy
+	if(++priv->tx_count < priv->max_tx)
+		clear_bit(0, (void *)&dev->tbusy);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+		
 	dprintk(KERN_INFO "%s: Packet (%d bytes) sent to network.\n",
 		dev->name, skb->len);
 
@@ -593,7 +556,7 @@
 
         if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0100, -1, 
         		 val64, sizeof(val64), &priv->reply_flag) < 0)
-        	dprintk("%s: Unable to query LAN_HISTORICAL_STATS.\n",dev->name);
+        	printk("%s: Unable to query LAN_HISTORICAL_STATS.\n",dev->name);
 	else {
         	dprintk("%s: LAN_HISTORICAL_STATS queried.\n",dev->name);
         	priv->stats.tx_packets = val64[0];
@@ -605,13 +568,14 @@
 		priv->stats.rx_dropped = val64[6];
 	}
 
-        i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0180, -1, 
-        		&supported_group, sizeof(supported_group), &priv->reply_flag);
+        if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0180, -1, 
+        		&supported_group, sizeof(supported_group), &priv->reply_flag) < 0)
+        	printk("%s: Unable to query LAN_SUPPORTED_OPTIONAL_HISTORICAL_STATS.\n",dev->name);
 
 	if (supported_group[2]) {
         	if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0183, -1, 
         	 	val64, sizeof(val64), &priv->reply_flag) < 0)
-        		dprintk("%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n",dev->name);
+        		printk("%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n",dev->name);
 		else {
         		dprintk("%s: LAN_OPTIONAL_RX_HISTORICAL_STATS queried.\n",dev->name);
 			priv->stats.multicast        = val64[4];
@@ -626,9 +590,9 @@
 
         	if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0200, -1, 
         			 val64, sizeof(val64), &priv->reply_flag) < 0)
-        		dprintk("%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n",dev->name);
+        		printk("%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n",dev->name);
 		else {
-//        		dprintk("%s: LAN_802_3_HISTORICAL_STATS queried.\n",dev->name);
+        		dprintk("%s: LAN_802_3_HISTORICAL_STATS queried.\n",dev->name);
 	 		priv->stats.transmit_collision = val64[1] + val64[2];
 			priv->stats.rx_frame_errors    = val64[0];		
 			priv->stats.tx_carrier_errors  = val64[6];
@@ -636,12 +600,12 @@
 
         	if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0280, -1, 
         			 &supported_stats, 8, &priv->reply_flag) < 0)
-        		dprintk("%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS\n", dev->name);
-        
+        		printk("%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS\n", dev->name);
+
         	if (supported_stats != 0) {
         		if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0281, -1, 
         				 val64, sizeof(val64), &priv->reply_flag) < 0)
-        			dprintk("%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n",dev->name);
+        			printk("%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n",dev->name);
 			else {
         			dprintk("%s: LAN_OPTIONAL_802_3_HISTORICAL_STATS queried.\n",dev->name);
 				if (supported_stats & 0x1)
@@ -658,7 +622,7 @@
 	{
         	if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0300, -1, 
         			 val64, sizeof(val64), &priv->reply_flag) < 0)
-        		dprintk("%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n",dev->name);
+        		printk("%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n",dev->name);
 		else {
 			struct tr_statistics *stats = 
 					(struct tr_statistics *)&priv->stats;
@@ -684,7 +648,7 @@
 	{
         	if (i2o_query_scalar(iop, i2o_dev->id, priv->unit << 16 | lan_context, 0x0400, -1, 
         			 val64, sizeof(val64), &priv->reply_flag) < 0)
-        		dprintk("%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n",dev->name);
+        		printk("%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n",dev->name);
 		else {
 //        		dprintk("%s: LAN_FDDI_HISTORICAL_STATS queried.\n",dev->name);
 			priv->stats.smt_cf_state = val64[0];
@@ -719,20 +683,6 @@
 
 	dprintk(KERN_INFO "%s: Entered i2o_lan_set_multicast_list().\n", dev->name);
 
-if (dev==NULL)
-        printk("dev is NULL\n");
-else if (dev->priv==NULL)
-        printk("dev->priv is NULL\n");
-else if (priv->i2o_dev==NULL)
-        printk("i2o_dev is NULL\n");
-else if (i2o_dev->controller==NULL)
-	printk("iop is NULL\n");
-else {
-	printk("Everything seems to be OK in i2o_lan_set_multicast_list().\n");
-        printk("id = %d, unit = %d, lan_context = %d\n",
-        i2o_dev->id, priv->unit, lan_context);
-}  
-
 return;
 
 /* FIXME: Why does the next call kill the interrupt handler?
@@ -810,6 +760,7 @@
 	struct net_device *dev = NULL;
 	struct i2o_lan_local *priv = NULL;
 	u8 hw_addr[8];
+	u32 max_tx = 0;
 	unsigned short (*type_trans)(struct sk_buff *, struct net_device *);
 	void (*unregister_dev)(struct net_device *dev);
 
@@ -893,12 +844,12 @@
 			priv->unit << 16 | lan_context,
 			0x0001, 0, &hw_addr, 8, &priv->reply_flag) < 0)
 	{
-        	printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name);
+     	printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name);
 		unit--;
 		unregister_dev(dev);
 		kfree(dev);
       		return NULL;  
-        }
+	}
 
 	dprintk("%s: hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
       		dev->name,hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3],
@@ -907,6 +858,22 @@
 	dev->addr_len = 6;
 	memcpy(dev->dev_addr, hw_addr, 6);
 
+   if (i2o_query_scalar(i2o_dev->controller, i2o_dev->id,
+         priv->unit << 16 | lan_context,
+         0x0007, 2, &max_tx, 4, &priv->reply_flag) < 0)
+   {
+      printk(KERN_ERR "%s: Unable to query max TX queue.\n", dev->name);
+      unit--;
+      unregister_dev(dev);
+      kfree(dev);
+         return NULL;
+   }
+   printk(KERN_INFO "%s: Max TX Outstanding = %d\n", dev->name, max_tx);
+   priv->max_tx = max_tx;
+   priv->tx_count = 0;
+
+	priv->lock = SPIN_LOCK_UNLOCKED;
+
 	dev->open               = i2o_lan_open;
 	dev->stop               = i2o_lan_close;
 	dev->hard_start_xmit    = i2o_lan_packet_send;
@@ -920,24 +887,21 @@
 #define i2o_lan_init	init_module
 #endif
 
-__init int i2o_lan_init(void)
+int __init i2o_lan_init(void)
 {
 	struct net_device *dev;
 	int i;
 
-	bucketpost = bucketpost - bucketthresh;
-
 	if (i2o_install_handler(&i2o_lan_handler) < 0)
 	{
  		printk(KERN_ERR "Unable to register I2O LAN OSM.\n");
 		return -EINVAL;
 	}   
-
+	lan_context = i2o_lan_handler.context;
+	
 	for(i=0; i <= MAX_LAN_CARDS; i++)
 		i2o_landevs[i] = NULL;
 
-	lan_context = i2o_lan_handler.context;
-
 	for (i=0; i < MAX_I2O_CONTROLLERS; i++)
 	{
 		struct i2o_controller *iop = i2o_find_controller(i);
@@ -1026,7 +990,7 @@
 MODULE_AUTHOR("Univ of Helsinki, CS Department");
 MODULE_DESCRIPTION("I2O Lan OSM");
 
-MODULE_PARM(bucketpost, "i");           // Number of buckets to post
+MODULE_PARM(bucketpost, "i");   // Number of buckets to post
 MODULE_PARM(bucketthresh, "i"); // Bucket post threshold
 MODULE_PARM(rx_copybreak, "i");
 
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/i2o_lan.h linux/drivers/i2o/i2o_lan.h
--- v2.3.18/linux/drivers/i2o/i2o_lan.h	Thu Aug  5 15:04:52 1999
+++ linux/drivers/i2o/i2o_lan.h	Sat Oct  2 07:36:17 1999
@@ -17,7 +17,7 @@
 
 /* Tunable parameters first */
 
-#define I2O_BUCKET_COUNT 	64
+#define I2O_BUCKET_COUNT 	256
 #define I2O_BUCKET_THRESH	16
 
 /* LAN types */
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/i2o_pci.c linux/drivers/i2o/i2o_pci.c
--- v2.3.18/linux/drivers/i2o/i2o_pci.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/i2o/i2o_pci.c	Sat Oct  2 07:36:17 1999
@@ -340,7 +340,7 @@
 MODULE_DESCRIPTION("I2O PCI Interface");
 
 #else
-__init void i2o_pci_init(void)
+void __init i2o_pci_init(void)
 {
 	printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n");
 	if(i2o_pci_scan()>=0)
diff -u --recursive --new-file v2.3.18/linux/drivers/i2o/i2o_proc.c linux/drivers/i2o/i2o_proc.c
--- v2.3.18/linux/drivers/i2o/i2o_proc.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/i2o/i2o_proc.c	Sat Oct  2 07:36:17 1999
@@ -1,7 +1,7 @@
 /*
  *   procfs handler for Linux I2O subsystem
  *
- *   Copyright (c) 1999 Intel Corporation
+ *   Copyright (c) 1999 Deepak Saxena
  *   
  *   Originally written by Deepak Saxena(deepak@plexity.net)
  *
@@ -314,48 +314,17 @@
 		      int *eof, void *data)
 {
 	struct i2o_controller *c = (struct i2o_controller *)data;
-	pi2o_hrt hrt;
-	u32 msg[6];
-	u32 *workspace;
+	pi2o_hrt hrt = (pi2o_hrt)c->hrt;
 	u32 bus;
 	int count;
 	int i;
-	int token;
 
 	spin_lock(&i2o_proc_lock);
 
 	len = 0;
 
-	workspace = kmalloc(2048, GFP_KERNEL);
-	hrt = (pi2o_hrt)workspace;
-	if(workspace==NULL)
-	{
-		len += sprintf(buf, "No free memory for HRT buffer\n");
-		spin_unlock(&i2o_proc_lock);
-		return len;
-	}
-
-	memset(workspace, 0, 2048);
-
-	msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
-	msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
-	msg[2]= (u32)proc_context;
-	msg[3]= 0;
-	msg[4]= (0xD0000000 | 2048); 
-	msg[5]= virt_to_phys(workspace); 
-
-	token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_proc_token,2);
-	if(token == I2O_POST_WAIT_TIMEOUT)
-	{
-		kfree(workspace);
-		len += sprintf(buf, "Timeout waiting for HRT\n");
-		spin_unlock(&i2o_proc_lock);
-		return len;
-	}
-
 	if(hrt->hrt_version)
 	{
-		kfree(workspace);
 		len += sprintf(buf+len, 
 			       "HRT table for controller is too new a version.\n");
 		spin_unlock(&i2o_proc_lock);
@@ -366,7 +335,6 @@
 
 	if((count * hrt->entry_len + 8) > 2048) {
 		printk(KERN_WARNING "i2o_proc: HRT does not fit into buffer\n");
-		kfree(workspace);
 		len += sprintf(buf+len,
 			       "HRT table too big to fit in buffer.\n");
 		spin_unlock(&i2o_proc_lock);
@@ -448,8 +416,6 @@
 			len += sprintf(buf+len, "   Unknown Bus Type\n");
 	}
 
-	kfree(workspace);
-
 	spin_unlock(&i2o_proc_lock);
 	
 	return len;
@@ -459,11 +425,8 @@
 	int *eof, void *data)
 {
 	struct i2o_controller *c = (struct i2o_controller*)data;
-	u32 msg[8];
-	u32 *workspace;
-	pi2o_lct lct; /* = (pi2o_lct)c->lct; */
+	pi2o_lct lct = (pi2o_lct)c->lct;
 	int entries;
-	int token;
 	int i;
 
 #define BUS_TABLE_SIZE 3
@@ -478,35 +441,6 @@
 
 	len = 0;
 
-	workspace = kmalloc(8192, GFP_KERNEL);
-	lct = (pi2o_lct)workspace;
-	if(workspace==NULL)
-	{
-		len += sprintf(buf, "No free memory for LCT buffer\n");
-		spin_unlock(&i2o_proc_lock);
-		return len;
-	}
-
-	memset(workspace, 0, 8192);
-
-	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_6;
-	msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
-	msg[2] = (u32)proc_context;
-	msg[3] = 0;
-	msg[4] = 0xFFFFFFFF; /* All devices */
-	msg[5] = 0x00000000; /* Report now */
-	msg[6] = 0xD0000000|8192;
-	msg[7] = virt_to_bus(workspace);
-
-	token = i2o_post_wait(c, ADAPTER_TID, msg, 8*4, &i2o_proc_token,2);
-	if(token == I2O_POST_WAIT_TIMEOUT)
-	{
-		kfree(workspace);
-		len += sprintf(buf, "Timeout waiting for LCT\n");
-		spin_unlock(&i2o_proc_lock);
-		return len;
-	}
-
 	entries = (lct->table_size - 3)/9;
 
 	len += sprintf(buf, "LCT contains %d %s\n", entries,
@@ -618,7 +552,6 @@
 				lct->lct_entry[i].device_flags);
 	}
 
-	kfree(workspace);
 	spin_unlock(&i2o_proc_lock);
 	
 	return len;
@@ -986,8 +919,8 @@
 			len += sprintf(buf+len, "                ");
 		}
 
-		len += sprintf(buf+len, "%-0#7x", ddm_table.i2o_vendor_id);
-		len += sprintf(buf+len, "%-0#7x", ddm_table.module_id);
+		len += sprintf(buf+len, "%0#7x", ddm_table.i2o_vendor_id);
+		len += sprintf(buf+len, "%0#7x", ddm_table.module_id);
 		len += sprintf(buf+len, "%-25s", chtostr(ddm_table.module_name, 24));
 		len += sprintf(buf+len, "%-6s", chtostr(ddm_table.module_version,4));
 		len += sprintf(buf+len, "%8d  ", ddm_table.data_size);
@@ -1123,8 +1056,8 @@
 			len += sprintf(buf+len, "%-d", dst.module_state);
 #endif
 
-		len += sprintf(buf+len, "%-0#7x", dst.i2o_vendor_id);
-		len += sprintf(buf+len, "%-0#8x", dst.module_id);
+		len += sprintf(buf+len, "%#7x", dst.i2o_vendor_id);
+		len += sprintf(buf+len, "%#8x", dst.module_id);
 		len += sprintf(buf+len, "%-29s", chtostr(dst.module_name_version,28));
 		len += sprintf(buf+len, "%-9s", chtostr(dst.date,8));
 		len += sprintf(buf+len, "%8d ", dst.module_size);
@@ -1198,7 +1131,7 @@
 	{
 		len += sprintf(buf+len, "%-3d", i);
 
-		len += sprintf(buf+len, "%-0#6x ", group[0]);
+		len += sprintf(buf+len, "%#6x ", group[0]);
 		len += sprintf(buf+len, "%10d ", group[1]);
 		len += sprintf(buf+len, "%8d ", group[2]);
 
@@ -1928,8 +1861,8 @@
 		       work8[16],work8[17],work8[18],work8[19],
 		       work8[20],work8[21],work8[22],work8[23]);
 
-	len += sprintf(buf+len, "Max Tx wire speed   : %d bps\n", work64[3]);
-	len += sprintf(buf+len, "Max Rx wire speed   : %d bps\n", work64[4]);
+	len += sprintf(buf+len, "Max Tx wire speed   : %d bps\n", (int)work64[3]);
+	len += sprintf(buf+len, "Max Rx wire speed   : %d bps\n", (int)work64[4]);
 
 	len += sprintf(buf+len, "Min SDU packet size : 0x%08x\n", work32[10]);
 	len += sprintf(buf+len, "Max SDU packet size : 0x%08x\n", work32[11]);
@@ -2232,8 +2165,8 @@
 	len += sprintf(buf+len, "Connection type        : %s\n",
 		       i2o_get_connection_type(work32[1]));
 
-	len += sprintf(buf+len, "Current Tx wire speed  : %d bps\n", work64[1]);
-	len += sprintf(buf+len, "Current Rx wire speed  : %d bps\n", work64[2]);
+	len += sprintf(buf+len, "Current Tx wire speed  : %d bps\n", (int)work64[1]);
+	len += sprintf(buf+len, "Current Rx wire speed  : %d bps\n", (int)work64[2]);
 
 	len += sprintf(buf+len, "Duplex mode            : %s duplex\n", 
 			(work8[24]&1)?"Full":"Half");
@@ -3046,9 +2979,6 @@
 	struct i2o_controller *pctrl = NULL;
 	int i;
 
-	if(!i2o_find_controller(0))
-		return -1;
-	
 	for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
 	{
 		pctrl = i2o_find_controller(i);
@@ -3057,7 +2987,7 @@
 			i2o_proc_remove_controller(pctrl, i2o_proc_dir_root);
 			i2o_unlock_controller(pctrl);
 		}
-	};
+	}
 
 	if(!i2o_proc_dir_root->count)
 		remove_proc_entry("i2o", 0);
@@ -3071,7 +3001,7 @@
 #define i2o_proc_init init_module
 #endif
 
-__init int i2o_proc_init(void)
+int __init i2o_proc_init(void)
 {
 	if(create_i2o_procfs())
 		return -EBUSY;
diff -u --recursive --new-file v2.3.18/linux/drivers/misc/Config.in linux/drivers/misc/Config.in
--- v2.3.18/linux/drivers/misc/Config.in	Mon Aug  9 15:06:52 1999
+++ linux/drivers/misc/Config.in	Fri Oct  1 10:14:00 1999
@@ -4,6 +4,10 @@
 mainmenu_option next_comment
 comment 'Misc devices'
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Generic ACPI support' CONFIG_ACPI
+fi
+
 # PIIX4 ACPI requires PCI for setup and a hardcoded TSC for timing
 if [ "$CONFIG_PCI" = "y" -a "$CONFIG_X86_TSC" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
   bool 'PIIX4 ACPI support' CONFIG_PIIX4_ACPI
diff -u --recursive --new-file v2.3.18/linux/drivers/misc/Makefile linux/drivers/misc/Makefile
--- v2.3.18/linux/drivers/misc/Makefile	Mon Aug  9 14:12:48 1999
+++ linux/drivers/misc/Makefile	Fri Oct  1 10:14:00 1999
@@ -18,6 +18,14 @@
 O_OBJS   :=
 OX_OBJS  :=
 
+ifeq ($(CONFIG_ACPI),y)
+  O_OBJS += acpi.o
+else
+  ifeq ($(CONFIG_ACPI),m)
+    M_OBJS += acpi.o
+  endif
+endif
+
 ifdef CONFIG_PIIX4_ACPI
 O_OBJS += piix4_acpi.o
 endif
diff -u --recursive --new-file v2.3.18/linux/drivers/misc/acpi.c linux/drivers/misc/acpi.c
--- v2.3.18/linux/drivers/misc/acpi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/misc/acpi.c	Fri Oct  1 10:20:24 1999
@@ -0,0 +1,412 @@
+/*
+ *  acpi.c - Linux ACPI driver
+ *
+ *  Copyright (C) 1999 Andrew Henroid
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/acpi.h>
+
+/*
+ * Defines for 2.2.x
+ */
+#ifndef __exit
+#define __exit
+#endif
+#ifndef module_init
+#define module_init(x) int init_module(void) {return x();}
+#endif
+#ifndef module_exit
+#define module_exit(x) void cleanup_module(void) {x();}
+#endif
+#ifndef DECLARE_WAIT_QUEUE_HEAD
+#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue * x = NULL
+#endif
+
+static struct acpi_facp *acpi_facp = NULL;
+static unsigned long acpi_facp_addr = 0;
+static unsigned long acpi_dsdt_addr = 0;
+static DECLARE_WAIT_QUEUE_HEAD(acpi_wait_event);
+
+/*
+ * Get the value of the fixed event status register
+ */
+static u32 acpi_read_pm1_status(struct acpi_facp *facp)
+{
+	u32 value = inw(facp->pm1a_evt);
+
+	if (facp->pm1b_evt) {
+		value |= inw(facp->pm1b_evt);
+	}
+	return value;
+}
+
+/*
+ * Set the value of the fixed event status register (clear events)
+ */
+static void acpi_write_pm1_status(struct acpi_facp *facp, u32 value)
+{
+	outw(value, facp->pm1a_evt);
+	if (facp->pm1b_evt) {
+		outw(value, facp->pm1b_evt);
+	}
+}
+
+/*
+ * Set the value of the fixed event enable register (enable events)
+ */
+static void acpi_write_pm1_enable(struct acpi_facp *facp, u32 value)
+{
+	int offset = facp->pm1_evt_len >> 1;
+
+	outw(value, facp->pm1a_evt + offset);
+	if (facp->pm1b_evt) {
+		outw(value, facp->pm1b_evt + offset);
+	}
+}
+
+/*
+ * Get the value of the general-purpose event status register
+ */
+static u32 acpi_read_gpe_status(struct acpi_facp *facp)
+{
+	u32 value = 0;
+	int i, size;
+
+	if (facp->gpe1) {
+		size = facp->gpe1_len >> 1;
+		for (i = size - 1; i >= 0; i--) {
+			value = (value << 8) | inb(facp->gpe1 + i);
+		}
+	}
+	size = facp->gpe0_len >> 1;
+	for (i = size - 1; i >= 0; i--) {
+		value = (value << 8) | inb(facp->gpe0 + i);
+	}
+	return value;
+}
+
+/*
+ * Set the value of the general-purpose event status register (clear events)
+ */
+static void acpi_write_gpe_status(struct acpi_facp *facp, u32 value)
+{
+	int i, size;
+
+	size = facp->gpe0_len >> 1;
+	for (i = 0; i < size; i++) {
+		outb(value & 0xff, facp->gpe0 + i);
+		value >>= 8;
+	}
+	if (facp->gpe1) {
+		size = facp->gpe1_len >> 1;
+		for (i = 0; i < size; i++) {
+			outb(value & 0xff, facp->gpe1 + i);
+			value >>= 8;
+		}
+	}
+}
+
+/*
+ * Set the value of the general-purpose event enable register (enable events)
+ */
+static void acpi_write_gpe_enable(struct acpi_facp *facp, u32 value)
+{
+	int i, offset;
+
+	offset = facp->gpe0_len >> 1;
+	for (i = 0; i < offset; i++) {
+		outb(value & 0xff, facp->gpe0 + offset + i);
+		value >>= 8;
+	}
+	if (facp->gpe1) {
+		offset = facp->gpe1_len >> 1;
+		for (i = 0; i < offset; i++) {
+			outb(value & 0xff, facp->gpe1 + offset + i);
+			value >>= 8;
+		}
+	}
+}
+
+/*
+ * Map an ACPI table into virtual memory
+ */
+static struct acpi_table *__init acpi_map_table(u32 addr)
+{
+	struct acpi_table *table = NULL;
+	if (addr) {
+		// map table header to determine size
+		table = (struct acpi_table *)
+		    ioremap_nocache((unsigned long) addr, sizeof(struct acpi_table));
+		if (table) {
+			unsigned long table_size = table->length;
+			iounmap(table);
+			// remap entire table
+			table = (struct acpi_table *)
+			    ioremap_nocache((unsigned long) addr, table_size);
+		}
+	}
+	return table;
+}
+
+/*
+ * Unmap an ACPI table from virtual memory
+ */
+static void acpi_unmap_table(struct acpi_table *table)
+{
+	if (table) {
+		iounmap(table);
+	}
+}
+
+/*
+ * Locate and map ACPI tables (FACP, DSDT, ...)
+ */
+static int __init acpi_map_tables(void)
+{
+	struct acpi_rsdp *rsdp;
+	struct acpi_table *rsdt;
+	u32 *rsdt_entry;
+	int rsdt_entry_count;
+	u8 *i;
+
+	// search BIOS memory for RSDP
+	for (i = ACPI_BIOS_ROM_BASE; i < ACPI_BIOS_ROM_END; i += 16) {
+		rsdp = (struct acpi_rsdp *) i;
+		if (readl(rsdp->signature) == ACPI_RSDP1_SIG
+		    && readl(rsdp->signature + 1) == ACPI_RSDP2_SIG) {
+			char oem[7];
+			int j;
+
+			// strip trailing space and print OEM identifier
+			memcpy_fromio(oem, rsdp->oem, 6);
+			oem[6] = '\0';
+			for (j = 5; j > 0 && (oem[j] == '\0' || oem[j] == ' '); j--) {
+				oem[j] = '\0';
+			}
+			printk(KERN_INFO "ACPI: \"%s\" found at 0x%p\n", oem, (void *) i);
+
+			break;
+		}
+	}
+	if (i >= ACPI_BIOS_ROM_END) {
+		printk(KERN_ERR "ACPI: no RSDP found\n");
+		return -ENODEV;
+	}
+	// fetch RSDT from RSDP
+	rsdt = acpi_map_table(readl(&rsdp->rsdt));
+	if (!rsdt || rsdt->signature != ACPI_RSDT_SIG) {
+		printk(KERN_ERR "ACPI: no RSDT found\n");
+		acpi_unmap_table(rsdt);
+		return -ENODEV;
+	}
+	// search RSDT for FACP
+	acpi_facp = NULL;
+	rsdt_entry = (u32 *) (rsdt + 1);
+	rsdt_entry_count = (int) ((rsdt->length - sizeof(*rsdt)) >> 2);
+	while (rsdt_entry_count) {
+		struct acpi_table *dt = acpi_map_table(*rsdt_entry);
+		if (dt && dt->signature == ACPI_FACP_SIG) {
+			acpi_facp = (struct acpi_facp *) dt;
+			acpi_facp_addr = *rsdt_entry;
+			acpi_dsdt_addr = acpi_facp->dsdt;
+			break;
+		} else {
+			acpi_unmap_table(dt);
+		}
+		rsdt_entry++;
+		rsdt_entry_count--;
+	}
+
+	acpi_unmap_table(rsdt);
+
+	if (!acpi_facp) {
+		printk(KERN_ERR "ACPI: no FACP found\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/*
+ * Unmap ACPI tables (FACP, DSDT, ...)
+ */
+static void acpi_unmap_tables(void)
+{
+	acpi_dsdt_addr = 0;
+	acpi_facp_addr = 0;
+	acpi_unmap_table((struct acpi_table *) acpi_facp);
+	acpi_facp = NULL;
+}
+
+/*
+ * Handle an ACPI SCI (fixed or general purpose event)
+ */
+static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u32 status;
+
+	// detect and disable any fixed events
+	status = acpi_read_pm1_status(acpi_facp);
+	acpi_write_pm1_enable(acpi_facp, ~status);
+
+	// detect and disable any general-purpose events
+	status = acpi_read_gpe_status(acpi_facp);
+	acpi_write_gpe_enable(acpi_facp, ~status);
+
+	// notify process reading /dev/acpi
+	wake_up_interruptible(&acpi_wait_event);
+}
+
+/*
+ * Handle open of /dev/acpi
+ */
+static int acpi_open(struct inode *inode, struct file *file)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Handle close of /dev/acpi
+ */
+static int acpi_release(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Handle command to /dev/acpi
+ */
+static int acpi_ioctl(struct inode *inode,
+		      struct file *file,
+		      unsigned cmd,
+		      unsigned long arg)
+{
+	int status = -EINVAL;
+
+	switch (cmd) {
+	case ACPI_FIND_TABLES:
+		status = verify_area(VERIFY_WRITE,
+				     (void *) arg,
+				     sizeof(struct acpi_find_tables));
+		if (!status) {
+			struct acpi_find_tables *rqst = (struct acpi_find_tables *) arg;
+			put_user(acpi_facp_addr, &rqst->facp);
+			put_user(acpi_dsdt_addr, &rqst->dsdt);
+			status = 0;
+		}
+		break;
+	case ACPI_WAIT_EVENT:
+		interruptible_sleep_on(&acpi_wait_event);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		status = 0;
+		break;
+	}
+	return status;
+}
+
+static struct file_operations acpi_fops =
+{
+	NULL,			/* llseek */
+	NULL,			/* read */
+	NULL,			/* write */
+	NULL,			/* readdir */
+	NULL,			/* poll */
+	acpi_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	acpi_open,		/* open */
+	NULL,			/* flush */
+	acpi_release,		/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL,			/* revalidate */
+	NULL,			/* lock */
+};
+
+static struct miscdevice acpi_device =
+{
+	ACPI_MINOR_DEV,
+	"acpi",
+	&acpi_fops,
+	NULL,
+	NULL
+};
+
+/*
+ * Initialize and enable ACPI
+ */
+static int __init acpi_init(void)
+{
+	if (acpi_map_tables()) {
+		return -ENODEV;
+	}
+	if (request_irq(acpi_facp->sci_int,
+			acpi_irq,
+			SA_INTERRUPT | SA_SHIRQ,
+			"acpi",
+			NULL)) {
+		printk(KERN_ERR "ACPI: SCI (IRQ%d) allocation failed\n",
+		       acpi_facp->sci_int);
+		acpi_unmap_tables();
+		return -ENODEV;
+	}
+	if (misc_register(&acpi_device)) {
+		printk(KERN_ERR "ACPI: misc. register failed\n");
+	}
+	return 0;
+}
+
+/*
+ * Disable and deinitialize ACPI
+ */
+static void __exit acpi_exit(void)
+{
+	misc_deregister(&acpi_device);
+
+	// disable and clear any pending events
+	acpi_write_gpe_enable(acpi_facp, 0);
+	while (acpi_read_gpe_status(acpi_facp)) {
+		acpi_write_gpe_status(acpi_facp, acpi_read_gpe_status(acpi_facp));
+	}
+	acpi_write_pm1_enable(acpi_facp, 0);
+	acpi_write_pm1_status(acpi_facp, acpi_read_pm1_status(acpi_facp));
+
+	// disable SCI and free interrupt
+	outb(acpi_facp->acpi_disable, acpi_facp->smi_cmd);
+	free_irq(acpi_facp->sci_int, NULL);
+
+	acpi_unmap_tables();
+}
+
+#ifdef MODULE
+
+module_init(acpi_init)
+module_exit(acpi_exit)
+#else
+
+__initcall(acpi_init);
+
+#endif
diff -u --recursive --new-file v2.3.18/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.3.18/linux/drivers/net/3c509.c	Fri Sep 10 23:57:29 1999
+++ linux/drivers/net/3c509.c	Sat Oct  2 07:36:17 1999
@@ -226,9 +226,6 @@
 				 * detected and is enabled
 				 */
 
-				printk("3c509: found %s at slot %d\n",
-					el3_mca_adapters[j].name, slot + 1 );
-
 				pos4 = mca_read_stored_pos( slot, 4 );
 				pos5 = mca_read_stored_pos( slot, 5 );
 
@@ -241,6 +238,9 @@
 					slot++;         /* probing next slot */
 					continue;
 				}
+
+				printk("3c509: found %s at slot %d\n",
+					el3_mca_adapters[j].name, slot + 1 );
 
 				/* claim the slot */
 				mca_set_adapter_name(slot, el3_mca_adapters[j].name);
diff -u --recursive --new-file v2.3.18/linux/drivers/net/3c527.c linux/drivers/net/3c527.c
--- v2.3.18/linux/drivers/net/3c527.c	Wed Aug 18 11:36:41 1999
+++ linux/drivers/net/3c527.c	Wed Sep 29 14:02:59 1999
@@ -1,3 +1,4 @@
+
 /* 3c527.c: 3Com Etherlink/MC32 driver for Linux
  *
  *	(c) Copyright 1998 Red Hat Software Inc
@@ -15,7 +16,7 @@
  */
 
 static const char *version =
-	"3c527.c:v0.04 1999/03/16 Alan Cox (alan@redhat.com)\n";
+	"3c527.c:v0.05 1999/09/06 Alan Cox (alan@redhat.com)\n";
 
 /*
  *	Things you need
@@ -108,6 +109,7 @@
 	u16 tx_skb_end;
 	struct sk_buff *rx_skb[RX_RING_MAX];	/* Receive ring */
 	void *rx_ptr[RX_RING_MAX];		/* Data pointers */
+	u32 mc_list_valid;			/* True when the mclist is set */
 };
 
 /* The station (ethernet) address prefix, used for a sanity check. */
@@ -138,6 +140,8 @@
 static int	mc32_close(struct net_device *dev);
 static struct	net_device_stats *mc32_get_stats(struct net_device *dev);
 static void	mc32_set_multicast_list(struct net_device *dev);
+static void	mc32_reset_multicast_list(struct net_device *dev);
+
 
 /*
  * Check for a network adaptor of this type, and return '0' iff one exists.
@@ -445,16 +449,56 @@
 /*
  *	Send exec commands
  */
- 
+
+
+/*
+ *	Send command from interrupt state
+ */
+
+static int mc32_command_nowait(struct net_device *dev, u16 cmd, void *data, int len)
+{
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	
+	if(lp->exec_pending)
+		return -1;
+		
+	lp->exec_pending=1;
+	lp->exec_box->mbox=0;
+	lp->exec_box->mbox=cmd;
+	memcpy((void *)lp->exec_box->data, data, len);
+	barrier();	/* the memcpy forgot the volatile so be sure */
+
+	/* Send the command */
+	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+	outb(1<<6, ioaddr+HOST_CMD);	
+	return 0;
+}
+
+
+/*
+ *	Send command and block for results. On completion spot and reissue
+ *	multicasts
+ */
+  
 static int mc32_command(struct net_device *dev, u16 cmd, void *data, int len)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 	unsigned long flags;
+	int ret = 0;
 	
+	/*
+	 *	Wait for a command
+	 */
+	 
 	while(lp->exec_pending)
 		sleep_on(&lp->event);
 		
+	/*
+	 *	Issue mine
+	 */
+
 	lp->exec_pending=1;
 	lp->exec_box->mbox=0;
 	lp->exec_box->mbox=cmd;
@@ -472,18 +516,20 @@
 	lp->exec_pending=0;
 	restore_flags(flags);
 	
+	 
+	if(lp->exec_box->data[0]&(1<<13))
+		ret = -1;
 	/*
 	 *	A multicast set got blocked - do it now
 	 */
-	 
+		
 	if(lp->mc_reload_wait)
-		mc32_set_multicast_list(dev);
+		mc32_reset_multicast_list(dev);
 
-	if(lp->exec_box->data[0]&(1<<13))
-		return -1;
-	return 0;
+	return ret;
 }
 
+
 /*
  *	RX abort
  */
@@ -1063,20 +1109,18 @@
  * num_addrs > 0	Multicast mode, receive normal and MC packets,
  *			and do best-effort filtering.
  */
-static void mc32_set_multicast_list(struct net_device *dev)
+static void do_mc32_set_multicast_list(struct net_device *dev, int retry)
 {
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	u16 filt;
+
 	if (dev->flags&IFF_PROMISC)
-	{
 		/* Enable promiscuous mode */
 		filt = 1;
-		mc32_command(dev, 0, &filt, 2);
-	}
 	else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > 10)
 	{
 		dev->flags|=IFF_PROMISC;
 		filt = 1;
-		mc32_command(dev, 0, &filt, 2);
 	}
 	else if(dev->mc_count)
 	{
@@ -1087,24 +1131,47 @@
 		int i;
 		
 		filt = 0;
-		block[1]=0;
-		block[0]=dev->mc_count;
-		bp=block+2;
 		
-		for(i=0;i<dev->mc_count;i++)
+		if(retry==0)
+			lp->mc_list_valid = 0;
+		if(!lp->mc_list_valid)
 		{
-			memcpy(bp, dmc->dmi_addr, 6);
-			bp+=6;
-			dmc=dmc->next;
+			block[1]=0;
+			block[0]=dev->mc_count;
+			bp=block+2;
+		
+			for(i=0;i<dev->mc_count;i++)
+			{
+				memcpy(bp, dmc->dmi_addr, 6);
+				bp+=6;
+				dmc=dmc->next;
+			}
+			if(mc32_command_nowait(dev, 2, block, 2+6*dev->mc_count)==-1)
+			{
+				lp->mc_reload_wait = 1;
+				return;
+			}
+			lp->mc_list_valid=1;
 		}
-		mc32_command(dev, 2, block, 2+6*dev->mc_count);
-		mc32_command(dev, 0, &filt, 2);
 	}
 	else 
 	{
 		filt = 0;
-		mc32_command(dev, 0, &filt, 2);
 	}
+	if(mc32_command_nowait(dev, 0, &filt, 2)==-1)
+	{
+		lp->mc_reload_wait = 1;
+	}
+}
+
+static void mc32_set_multicast_list(struct net_device *dev)
+{
+	do_mc32_set_multicast_list(dev,0);
+}
+
+static void mc32_reset_multicast_list(struct net_device *dev)
+{
+	do_mc32_set_multicast_list(dev,1);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.3.18/linux/drivers/net/82596.c linux/drivers/net/82596.c
--- v2.3.18/linux/drivers/net/82596.c	Wed Aug 18 11:36:41 1999
+++ linux/drivers/net/82596.c	Sat Oct  2 07:36:17 1999
@@ -103,11 +103,6 @@
 #define PORT_ALTSCP		0x02	/* alternate SCB address */
 #define PORT_ALTDUMP		0x03	/* Alternate DUMP address */
 
-#ifndef HAVE_PORTRESERVE
-#define check_region(addr, size)	0
-#define request_region(addr, size,name)	do ; while(0)
-#endif
-
 #ifndef HAVE_ALLOC_SKB
 #define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
 #define kfree_skbmem(buff, size) kfree_s(buff,size)
diff -u --recursive --new-file v2.3.18/linux/drivers/net/acenic.c linux/drivers/net/acenic.c
--- v2.3.18/linux/drivers/net/acenic.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/net/acenic.c	Sat Oct  2 07:36:39 1999
@@ -24,6 +24,7 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
@@ -572,7 +573,7 @@
 }
 
 
-static __init int ace_init(struct net_device *dev, int board_idx)
+static int __init ace_init(struct net_device *dev, int board_idx)
 {
 	struct ace_private *ap;
 	struct ace_regs *regs;
diff -u --recursive --new-file v2.3.18/linux/drivers/net/arlan-proc.c linux/drivers/net/arlan-proc.c
--- v2.3.18/linux/drivers/net/arlan-proc.c	Wed Aug 18 11:36:41 1999
+++ linux/drivers/net/arlan-proc.c	Sat Oct  2 07:38:27 1999
@@ -1,10 +1,11 @@
 #include <linux/config.h>
 #include "arlan.h"
 
+#include <linux/sysctl.h>
+
 #ifdef CONFIG_PROC_FS
 
 
-#include <linux/sysctl.h>
 #include <linux/version.h>
 
 /* void enableReceive(struct net_device* dev);
@@ -1001,6 +1002,12 @@
 	{0}
 };
 #endif
+#else
+
+static ctl_table arlan_table[MAX_ARLANS + 1] =
+{
+	{0}
+};
 #endif
 
 static int mmtu = 1234;
diff -u --recursive --new-file v2.3.18/linux/drivers/net/arlan.c linux/drivers/net/arlan.c
--- v2.3.18/linux/drivers/net/arlan.c	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/arlan.c	Sat Oct  2 07:38:27 1999
@@ -98,7 +98,7 @@
 static  void 	arlan_tx_done_interrupt		(struct net_device * dev, int status);
 static  void	arlan_rx_interrupt		(struct net_device * dev, u_char rxStatus, u_short, u_short);
 static  void	arlan_process_interrupt		(struct net_device * dev);
-static  int	arlan_command(struct net_device * dev, int command);
+int	arlan_command(struct net_device * dev, int command);
 
 EXPORT_SYMBOL(arlan_command);
 
@@ -187,7 +187,7 @@
 };
 
 
-static int arlan_command(struct net_device *dev, int command_p)
+int arlan_command(struct net_device *dev, int command_p)
 {
 
 	volatile struct arlan_shmem *arlan = ((struct arlan_private *) dev->priv)->card;
diff -u --recursive --new-file v2.3.18/linux/drivers/net/atp.c linux/drivers/net/atp.c
--- v2.3.18/linux/drivers/net/atp.c	Wed Aug 18 11:36:41 1999
+++ linux/drivers/net/atp.c	Sat Oct  2 07:38:27 1999
@@ -184,7 +184,7 @@
 	/* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
 	outb(0x04, ioaddr + PAR_CONTROL);
 	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
-	eeprom_delay(2048);
+	udelay(100);
 	status = read_nibble(ioaddr, CMR1);
 
 	if ((status & 0x78) != 0x08) {
@@ -299,12 +299,12 @@
 	while (--num_bits >= 0) {
 		char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0;
 		write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW);
-		eeprom_delay(5);
+		udelay(5);
 		write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH);
 		eedata_out <<= 1;
 		if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ)
 			eedata_out++;
-		eeprom_delay(5);
+		udelay(5);
 	}
 	write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS);
 	return eedata_out;
diff -u --recursive --new-file v2.3.18/linux/drivers/net/atp.h linux/drivers/net/atp.h
--- v2.3.18/linux/drivers/net/atp.h	Sun Feb  2 05:18:36 1997
+++ linux/drivers/net/atp.h	Sat Oct  2 07:38:27 1999
@@ -261,10 +261,6 @@
 #define EE_DATA_WRITE	0x01	/* EEPROM chip data in. */
 #define EE_DATA_READ	0x08	/* EEPROM chip data out. */
 
-/* Delay between EEPROM clock transitions. */
-#define eeprom_delay(ticks) \
-do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
-
 /* The EEPROM commands include the alway-set leading bit. */
 #define EE_WRITE_CMD(offset)	(((5 << 6) + (offset)) << 17)
 #define EE_READ(offset) 	(((6 << 6) + (offset)) << 17)
diff -u --recursive --new-file v2.3.18/linux/drivers/net/bmac.c linux/drivers/net/bmac.c
--- v2.3.18/linux/drivers/net/bmac.c	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/bmac.c	Sat Oct  2 07:38:27 1999
@@ -19,6 +19,11 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/feature.h>
+#ifdef CONFIG_PMAC_PBOOK
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <asm/irq.h>
+#endif
 #include "bmac.h"
 
 #define trunc_page(x)	((void *)(((unsigned long)(x)) & ~((unsigned long)(PAGE_SIZE - 1))))
@@ -117,6 +122,13 @@
 struct net_device *bmac_devs = NULL;
 static int is_bmac_plus;
 
+#ifdef CONFIG_PMAC_PBOOK
+int bmac_sleep_notify(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier bmac_sleep_notifier = {
+	bmac_sleep_notify, SLEEP_LEVEL_NET,
+};
+#endif
+
 #if 0
 /*
  * If we can't get a skbuff when we need it, we use this area for DMA.
@@ -244,7 +256,7 @@
 	udelay(10000);
 }
 
-#define MIFDELAY	udelay(500)
+#define MIFDELAY	udelay(10)
 
 static unsigned int
 bmac_mif_readbits(struct net_device *dev, int nb)
@@ -427,8 +439,8 @@
 	udelay(20000);
 }
 
-static int
-bmac_init_chip(struct net_device *dev)
+static void
+bmac_init_phy(struct net_device *dev)
 {
 	unsigned int addr;
 
@@ -451,10 +463,54 @@
 		} else
 			bmac_mif_write(dev, 0, 0x1000);
 	}
+}
+
+static int
+bmac_init_chip(struct net_device *dev)
+{
+	bmac_init_phy(dev);
 	bmac_init_registers(dev);
 	return 1;
 }
 
+#ifdef CONFIG_PMAC_PBOOK
+int
+bmac_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+	struct bmac_data *bp;
+
+	if (bmac_devs == 0)
+		return PBOOK_SLEEP_OK;
+		
+	bp = (struct bmac_data *) bmac_devs->priv;
+	
+	switch (when) {
+	case PBOOK_SLEEP_REQUEST:
+		break;
+	case PBOOK_SLEEP_REJECT:
+		break;
+	case PBOOK_SLEEP_NOW:
+		/* prolly should wait for dma to finish & turn off the chip */
+		disable_irq(bmac_devs->irq);
+		disable_irq(bp->tx_dma_intr);
+		disable_irq(bp->rx_dma_intr);
+		feature_set(bp->node, FEATURE_BMac_reset);
+		udelay(10000);
+		feature_clear(bp->node, FEATURE_BMac_IO_enable);
+		udelay(10000);
+		break;
+	case PBOOK_WAKE:
+		/* see if this is enough */
+		bmac_reset_and_enable(bmac_devs, 1);
+		enable_irq(bmac_devs->irq);
+		enable_irq(bp->tx_dma_intr);
+		enable_irq(bp->rx_dma_intr);
+		break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+#endif
+
 static int bmac_set_address(struct net_device *dev, void *addr)
 {
 	unsigned char *p = addr;
@@ -1220,7 +1276,12 @@
 	if (bmacs == NULL) return -ENODEV;
 	next_bmac = bmacs->next;
 
-	bmac_devs = dev; /* KLUDGE!! */
+	if (bmac_devs == 0) {
+		bmac_devs = dev; /* KLUDGE!! */
+#ifdef CONFIG_PMAC_PBOOK
+		pmu_register_sleep_notifier(&bmac_sleep_notifier);
+#endif
+	}
 
 	if (bmacs->n_addrs != 3 || bmacs->n_intrs != 3) {
 		printk(KERN_ERR "can't use BMAC %s: expect 3 addrs and 3 intrs\n",
@@ -1556,15 +1617,24 @@
     res = bmac_probe(NULL);
     return res;
 }
+
 void cleanup_module(void)
 {
-    struct bmac_data *bp = (struct bmac_data *) bmac_devs->priv;
+    struct bmac_data *bp;
+
+    if (bmac_devs == 0)
+	return;
+
+    bp = (struct bmac_data *) bmac_devs->priv;
     unregister_netdev(bmac_devs);
 
     free_irq(bmac_devs->irq, bmac_misc_intr);
     free_irq(bp->tx_dma_intr, bmac_txdma_intr);
     free_irq(bp->rx_dma_intr, bmac_rxdma_intr);
 
+#ifdef CONFIG_PMAC_PBOOK
+    pmu_unregister_sleep_notifier(&bmac_sleep_notifier);
+#endif
     kfree(bmac_devs);
     bmac_devs = NULL;
 }
diff -u --recursive --new-file v2.3.18/linux/drivers/net/defxx.c linux/drivers/net/defxx.c
--- v2.3.18/linux/drivers/net/defxx.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/net/defxx.c	Wed Sep 29 15:32:15 1999
@@ -3295,7 +3295,7 @@
 	 *			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_0	= (u32) (PI_XMT_DESCR_M_SOP | PI_XMT_DESCR_M_EOP | ((skb->len) << PI_XMT_DESCR_V_SEG_LEN));
 	p_xmt_descr->long_1 = (u32) virt_to_bus(skb->data);
 
 	/*
diff -u --recursive --new-file v2.3.18/linux/drivers/net/dmfe.c linux/drivers/net/dmfe.c
--- v2.3.18/linux/drivers/net/dmfe.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/dmfe.c	Sat Oct  2 07:38:27 1999
@@ -0,0 +1,1517 @@
+/*
+   dmfe.c: Version 1.26
+
+   A Davicom DM9102 fast ethernet driver for Linux. 
+
+   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, version 1.
+
+   Compiler command:
+   "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall 
+		-Wstrict-prototypes -O6 -c dmfe.c"
+
+   The following steps teach you how to active DM9102 board:
+   1. Used the upper compiler command to compile dmfe.c
+   2. insert dmfe module into kernel
+   "insmod dmfe"        ;;Auto Detection Mode
+   "insmod dmfe mode=0" ;;Force 10M Half Duplex
+   "insmod dmfe mode=1" ;;Force 100M Half Duplex
+   "insmod dmfe mode=4" ;;Force 10M Full Duplex
+   "insmod dmfe mode=5" ;;Force 100M Full Duplex
+   3. config a dm9102 network interface
+   "ifconfig eth0 172.22.3.18"
+   4. active the IP routing table
+   "route add -net 172.22.3.0 eth0"
+   5. Well done. Your DM9102 adapter actived now.
+
+   Author: Sten Wang, E-mail: sten_wang@davicom.com.tw
+
+   Date:   10/28,1998
+
+   (C)Copyright 1997-1998 DAVICOM Semiconductor, Inc. All Rights Reserved.
+
+   Marcelo Tosatti <marcelo@conectiva.com.br> : 
+   Made it compile in 2.3 (device to net_device)
+   
+   Alan Cox <alan@redhat.com> :
+   Removed the back compatibility support
+   Reformatted, fixing spelling etc as I went
+   Removed IRQ 0-15 assumption
+   
+   TODO
+   
+   Check and fix on 64bit and big endian boxes.
+   Sort out the PCI latency.
+   
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+
+#include <linux/delay.h>
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+
+/* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9102_ID   0x91021282	/* Davicom DM9102 ID */
+#define PCI_DM9100_ID   0x91001282	/* Davicom DM9100 ID */
+
+#define DMFE_SUCC       0
+#define DM9102_IO_SIZE  0x80
+#define TX_FREE_DESC_CNT 0x1	/* Tx packet count */
+#define TX_DESC_CNT     0x10	/* Allocated Tx descriptors */
+#define RX_DESC_CNT     0x10	/* Allocated Rx descriptors */
+#define DESC_ALL_CNT    TX_DESC_CNT+RX_DESC_CNT
+#define TX_BUF_ALLOC    0x600
+#define RX_ALLOC_SIZE   0x620
+#define DM910X_RESET    1
+#define CR6_DEFAULT     0x002c0000	/* SF, MII, HD */
+#define CR7_DEFAULT     0x1a2cd
+#define CR15_DEFAULT    0x06	/* TxJabber RxWatchdog */
+#define TDES0_ERR_MASK  0x4302	/* TXJT, LC, EC, FUE */
+#define MAX_PACKET_SIZE 1514
+#define DMFE_MAX_MULTICAST 14
+#define RX_MAX_TRAFFIC 0x5000
+#define MAX_CHECK_PACKET 0x8000
+
+#define DMFE_10MHF      0
+#define DMFE_100MHF     1
+#define DMFE_10MFD      4
+#define DMFE_100MFD     5
+#define DMFE_AUTO       8
+
+#define DMFE_TIMER_WUT  jiffies+HZ*1	/* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT HZ*2	/* tx packet time-out time */
+
+#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
+
+#define DELAY_5US udelay(5)	/* udelay scale 1 usec */
+
+#define DELAY_1US udelay(1)	/* udelay scale 1 usec */
+
+#define SHOW_MEDIA_TYPE(mode) printk("\n<WARN> Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+
+
+/* CR9 definition: SROM/MII */
+#define CR9_SROM_READ   0x4800
+#define CR9_SRCS        0x1
+#define CR9_SRCLK       0x2
+#define CR9_CRDOUT      0x8
+#define SROM_DATA_0     0x0
+#define SROM_DATA_1     0x4
+#define PHY_DATA_1      0x20000
+#define PHY_DATA_0      0x00000
+#define MDCLKH          0x10000
+
+#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;
+
+/* Structure/enum declaration ------------------------------- */
+struct tx_desc {
+	u32 tdes0, tdes1, tdes2, tdes3;
+	u32 tx_skb_ptr;
+	u32 tx_buf_ptr;
+	u32 next_tx_desc;
+	u32 reserved;
+};
+
+struct rx_desc {
+	u32 rdes0, rdes1, rdes2, rdes3;
+	u32 rx_skb_ptr;
+	u32 rx_buf_ptr;
+	u32 next_rx_desc;
+	u32 reserved;
+};
+
+struct dmfe_board_info {
+	u32 chip_id;		/* Chip vendor/Device ID */
+	u32 chip_revesion;	/* Chip revesion */
+	struct net_device *next_dev;	/* next device */
+
+	struct pci_dev *net_dev;	/* PCI device */
+
+	u32 ioaddr;		/* I/O base address */
+	u32 cr5_data;
+	u32 cr6_data;
+	u32 cr7_data;
+	u32 cr15_data;
+
+/* descriptor pointer */
+	unsigned char *buf_pool_ptr;	/* Tx buffer pool memory */
+	unsigned char *buf_pool_start;	/* Tx buffer pool align dword */
+	unsigned char *desc_pool_ptr;	/* descriptor pool memory */
+	struct tx_desc *first_tx_desc;
+	struct tx_desc *tx_insert_ptr;
+	struct tx_desc *tx_remove_ptr;
+	struct rx_desc *first_rx_desc;
+	struct rx_desc *rx_insert_ptr;
+	struct rx_desc *rx_ready_ptr;	/* packet come pointer */
+	u32 tx_packet_cnt;	/* transmitted packet count */
+	u32 rx_avail_cnt;	/* available rx descriptor count */
+	u32 interval_rx_cnt;	/* rx packet count a callback time */
+
+	u8 media_mode;		/* user specify media mode */
+	u8 op_mode;		/* real work media mode */
+	u8 phy_addr;
+	u8 link_failed;		/* Ever link failed */
+	u8 wait_reset;		/* Hardware failed, need to reset */
+	u8 in_reset_state;	/* Now driver in reset routine */
+	u8 rx_error_cnt;	/* recievd abnormal case count */
+	u8 dm910x_chk_mode;	/* Operating mode check */
+	struct timer_list timer;
+	struct enet_statistics stats;	/* statistic counter */
+	unsigned char srom[128];
+};
+
+enum dmfe_offsets {
+	DCR0 = 0, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, DCR5 = 0x28,
+	DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, DCR10 = 0x50, DCR11 = 0x58,
+	DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, DCR15 = 0x78
+};
+
+enum dmfe_CR6_bits {
+	CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200,
+	CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000
+};
+
+/* Global variable declaration ----------------------------- */
+
+static int dmfe_debug = 0;
+static unsigned char dmfe_media_mode = 8;
+static struct net_device *dmfe_root_dev = NULL;		/* First device */
+static u32 dmfe_cr6_user_set = 0;
+
+/* For module input parameter */
+static int debug = 0;
+static u32 cr6set = 0;
+static unsigned char mode = 8;
+static u8 chkmode = 1;
+
+unsigned long CrcTable[256] =
+{
+	0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
+	0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
+	0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
+	0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
+	0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
+	0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
+	0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
+	0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
+	0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
+	0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
+	0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
+	0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
+	0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
+	0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
+	0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
+	0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
+	0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
+	0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
+	0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
+	0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
+	0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
+	0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
+	0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
+	0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
+	0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
+	0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
+	0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
+	0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
+	0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
+	0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
+	0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
+	0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
+	0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
+	0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
+	0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
+	0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
+	0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
+	0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
+	0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
+	0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
+	0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
+	0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
+	0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
+	0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
+	0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
+	0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
+	0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
+	0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
+	0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
+	0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
+	0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
+	0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
+	0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
+	0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
+	0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
+	0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
+	0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
+	0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
+	0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
+	0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
+	0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
+	0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
+	0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
+	0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
+};
+
+/* function declaration ------------------------------------- */
+int dmfe_reg_board(struct net_device *);
+static int dmfe_open(struct net_device *);
+static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
+static int dmfe_stop(struct net_device *);
+static struct enet_statistics *dmfe_get_stats(struct net_device *);
+static void dmfe_set_filter_mode(struct net_device *);
+static int dmfe_do_ioctl(struct net_device *, struct ifreq *, int);
+static u16 read_srom_word(long, int);
+static void dmfe_interrupt(int, void *, struct pt_regs *);
+static void dmfe_descriptor_init(struct dmfe_board_info *, u32);
+static void allocated_rx_buffer(struct dmfe_board_info *);
+static void update_cr6(u32, u32);
+static void send_filter_frame(struct net_device *, int);
+static u16 phy_read(u32, u8, u8);
+static void phy_write(u32, u8, u8, u16);
+static void phy_write_1bit(u32, u32);
+static u16 phy_read_1bit(u32);
+static void parser_ctrl_info(struct dmfe_board_info *);
+static void dmfe_sense_speed(struct dmfe_board_info *);
+static void dmfe_process_mode(struct dmfe_board_info *);
+static void dmfe_timer(unsigned long);
+static void dmfe_rx_packet(struct net_device *, struct dmfe_board_info *);
+static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *);
+static void dmfe_dynamic_reset(struct net_device *);
+static void dmfe_free_rxbuffer(struct dmfe_board_info *);
+static void dmfe_init_dm910x(struct net_device *);
+static unsigned long cal_CRC(unsigned char *, unsigned int);
+
+/* DM910X network board routine ---------------------------- */
+
+/*
+ *	Search DM910X board, allocate space and register it
+ */
+ 
+int dmfe_reg_board(struct net_device *dev)
+{
+	u32 pci_iobase;
+	u16 dm9102_count = 0;
+	u8 pci_irqline;
+	static int index = 0;	/* For multiple call */
+	struct dmfe_board_info *db;	/* Point a board information structure */
+	int i;
+	struct pci_dev *net_dev = NULL;
+
+	DMFE_DBUG(0, "dmfe_reg_board()", 0);
+
+	if (!pci_present())
+		return -ENODEV;
+
+	index = 0;
+	while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev)))
+	{
+		u32 pci_id;
+		u8 pci_cmd;
+
+		index++;
+		if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
+			continue;
+
+		if (pci_id != PCI_DM9102_ID)
+			continue;
+
+		pci_iobase = net_dev->resource[0].start;
+		pci_irqline = net_dev->irq;
+				
+		/* Enable Master/IO access, Disable memory access */
+		
+		pci_set_master(net_dev);
+		
+		pci_read_config_byte(net_dev, PCI_COMMAND, &pci_cmd);
+		pci_cmd |= PCI_COMMAND_IO;
+		pci_cmd &= ~PCI_COMMAND_MEMORY;
+		pci_write_config_byte(net_dev, PCI_COMMAND, pci_cmd);
+
+		/* Set Latency Timer 80h */
+		
+		/* FIXME: setting values > 32 breaks some SiS 559x stuff.
+		   Need a PCI quirk.. */
+		   
+		pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80);
+
+		/* IO range and interrupt check */
+
+		if (check_region(pci_iobase, DM9102_IO_SIZE))	/* IO range check */
+			continue;
+
+		/* Found DM9102 card and PCI resource allocated OK */
+		dm9102_count++;	/* Found a DM9102 card */
+
+		/* Init network device */
+		dev = init_etherdev(dev, 0);
+
+		/* Allocated board information structure */
+		db = (void *) (kmalloc(sizeof(*db), GFP_KERNEL | GFP_DMA));
+		if(db==NULL)
+			continue;	/* Out of memory */
+			
+		memset(db, 0, sizeof(*db));
+		dev->priv = db;	/* link device and board info */
+		db->next_dev = dmfe_root_dev;
+		dmfe_root_dev = dev;
+
+		db->chip_id = pci_id;	/* keep Chip vandor/Device ID */
+		db->ioaddr = pci_iobase;
+		pci_read_config_dword(net_dev, 8, &db->chip_revesion);
+
+		db->net_dev = net_dev;
+
+		dev->base_addr = pci_iobase;
+		dev->irq = pci_irqline;
+		dev->open = &dmfe_open;
+		dev->hard_start_xmit = &dmfe_start_xmit;
+		dev->stop = &dmfe_stop;
+		dev->get_stats = &dmfe_get_stats;
+		dev->set_multicast_list = &dmfe_set_filter_mode;
+		dev->do_ioctl = &dmfe_do_ioctl;
+
+		request_region(pci_iobase, DM9102_IO_SIZE, dev->name);
+
+		/* read 64 word srom data */
+		for (i = 0; i < 64; i++)
+			((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i);
+
+		/* Set Node address */
+		for (i = 0; i < 6; i++)
+			dev->dev_addr[i] = db->srom[20 + i];
+
+		dev = 0;	/* NULL device */
+	}
+
+#ifdef MODULE
+	if (!dm9102_count)
+		printk(KERN_WARNING "dmfe: Can't find DM910X board\n");
+#endif		
+	return dm9102_count ? 0 : -ENODEV;
+}
+
+/*
+ *	Open the interface.
+ *	The interface is opened whenever "ifconfig" actives it.
+ */
+ 
+static int dmfe_open(struct net_device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+
+	DMFE_DBUG(0, "dmfe_open", 0);
+
+	if (request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+
+	/* Allocated Tx/Rx descriptor memory */
+	db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA);
+	if (db->desc_pool_ptr == NULL)
+		return -ENOMEM;
+
+	if ((u32) db->desc_pool_ptr & 0x1f)
+		db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20);
+	else
+		db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
+
+	/* Allocated Tx buffer memory */
+	
+	db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA);
+	if (db->buf_pool_ptr == NULL) {
+		kfree(db->desc_pool_ptr);
+		return -ENOMEM;
+	}
+	
+	if ((u32) db->buf_pool_ptr & 0x3)
+		db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4);
+	else
+		db->buf_pool_start = db->buf_pool_ptr;
+
+	/* system variable init */
+	db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
+	db->tx_packet_cnt = 0;
+	db->rx_avail_cnt = 0;
+	db->link_failed = 0;
+	db->wait_reset = 0;
+	db->in_reset_state = 0;
+	db->rx_error_cnt = 0;
+
+	if (chkmode && (db->chip_revesion < 0x02000030)) {
+		db->dm910x_chk_mode = 1;	/* Enter the check mode */
+	} else {
+		db->dm910x_chk_mode = 4;	/* Enter the normal mode */
+	}
+
+	/* Initilize DM910X board */
+	dmfe_init_dm910x(dev);
+
+	/* Active System Interface */
+	dev->tbusy = 0;		/* Can transmit packet */
+	dev->start = 1;		/* interface ready */
+	MOD_INC_USE_COUNT;
+
+	/* set and active a timer process */
+	init_timer(&db->timer);
+	db->timer.expires = DMFE_TIMER_WUT;
+	db->timer.data = (unsigned long) dev;
+	db->timer.function = &dmfe_timer;
+	add_timer(&db->timer);
+
+	return 0;
+}
+
+/*
+ *	Initialize DM910X board
+ *	Reset DM910X board
+ *	Initialize TX/Rx descriptor chain structure
+ *	Send the set-up frame
+ *	Enable Tx/Rx machine
+ */
+ 
+static void dmfe_init_dm910x(struct net_device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+	u32 ioaddr = db->ioaddr;
+
+	DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
+
+	/* Reset DM910x board : need 32 PCI clock to complete */
+	outl(DM910X_RESET, ioaddr + DCR0);
+	DELAY_5US;
+	outl(0, ioaddr + DCR0);
+
+	outl(0x180, ioaddr + DCR12);	/* Let bit 7 output port */
+	outl(0x80, ioaddr + DCR12);	/* Reset DM9102 phyxcer */
+	outl(0x0, ioaddr + DCR12);	/* Clear RESET signal */
+
+	/* Parser control information: Phy addr */
+	parser_ctrl_info(db);
+	db->media_mode = dmfe_media_mode;
+	if (db->media_mode & DMFE_AUTO)
+		dmfe_sense_speed(db);
+	else
+		db->op_mode = db->media_mode;
+	dmfe_process_mode(db);
+
+	/* Initiliaze Transmit/Receive decriptor and CR3/4 */
+	dmfe_descriptor_init(db, ioaddr);
+
+	/* Init CR6 to program DM910x operation */
+	update_cr6(db->cr6_data, ioaddr);
+
+	/* Send setup frame */
+	send_filter_frame(dev, 0);
+
+	/* Init CR5/CR7, interrupt active bit */
+	outl(0xffffffff, ioaddr + DCR5);	/* clear all CR5 status */
+	db->cr7_data = CR7_DEFAULT;
+	outl(db->cr7_data, ioaddr + DCR7);
+
+	/* Init CR15, Tx jabber and Rx watchdog timer */
+	db->cr15_data = CR15_DEFAULT;
+	outl(db->cr15_data, ioaddr + DCR15);
+
+	/* Enable DM910X Tx/Rx function */
+	db->cr6_data |= CR6_RXSC | CR6_TXSC;
+	update_cr6(db->cr6_data, ioaddr);
+
+}
+
+
+/*
+ *	Hardware start transmission.
+ *	Send a packet to media from the upper layer.
+ */
+ 
+static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+	struct tx_desc *txptr;
+
+	DMFE_DBUG(0, "dmfe_start_xmit", 0);
+
+	if ((dev->tbusy == 1) && (db->tx_packet_cnt != 0))
+		return 1;
+	else
+		dev->tbusy = 0;
+
+	/* Too large packet check */
+	if (skb->len > MAX_PACKET_SIZE) {
+		printk(KERN_ERR "%s: oversized frame (%d bytes) received.\n", dev->name, (u16) skb->len);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	/* No Tx resource check, it never happen nromally */
+	if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
+		printk(KERN_WARNING "%s: No Tx resource, enter xmit() again \n", dev->name);
+		dev_kfree_skb(skb);
+		dev->tbusy = 1;
+		return -EBUSY;
+	}
+
+	/* transmit this packet */
+	txptr = db->tx_insert_ptr;
+	memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
+	txptr->tdes1 = 0xe1000000 | skb->len;
+	txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
+
+	/* Point to next transmit free descriptor */
+	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+
+	/* transmit counter increase 1 */
+	db->tx_packet_cnt++;
+	db->stats.tx_packets++;
+
+	/* issue Tx polling command */
+	outl(0x1, dev->base_addr + DCR1);
+
+	/* Tx resource check */
+	if (db->tx_packet_cnt >= TX_FREE_DESC_CNT)
+		dev->tbusy = 1;
+
+	/* Set transmit time stamp */
+	dev->trans_start = jiffies;	/* saved the time stamp */
+
+	/* free this SKB */
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+/*
+ *	Stop the interface.
+ *	The interface is stopped when it is brought.
+ */
+
+static int dmfe_stop(struct net_device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+	u32 ioaddr = dev->base_addr;
+
+	DMFE_DBUG(0, "dmfe_stop", 0);
+
+	/* disable system */
+	dev->start = 0;		/* interface disable */
+	dev->tbusy = 1;		/* can't transmit */
+
+	/* Reset & stop DM910X board */
+	outl(DM910X_RESET, ioaddr + DCR0);
+	DELAY_5US;
+
+	/* deleted timer */
+	del_timer(&db->timer);
+
+	/* free interrupt */
+	free_irq(dev->irq, dev);
+
+	/* free allocated rx buffer */
+	dmfe_free_rxbuffer(db);
+
+	/* free all descriptor memory and buffer memory */
+	kfree(db->desc_pool_ptr);
+	kfree(db->buf_pool_ptr);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ *	DM9102 insterrupt handler
+ *	receive the packet to upper layer, free the transmitted packet
+ */
+
+static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct tx_desc *txptr;
+	struct dmfe_board_info *db;
+	u32 ioaddr;
+
+	if (!dev) {
+		DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
+		return;
+	}
+	if (dev->interrupt) {
+		DMFE_DBUG(1, "dmfe_interrupt() re-entry ", 0);
+		return;
+	}
+
+	/* A real interrupt coming */
+	dev->interrupt = 1;	/* Lock interrupt */
+	db = (struct dmfe_board_info *) dev->priv;
+	ioaddr = dev->base_addr;
+
+	DMFE_DBUG(0, "dmfe_interrupt()", 0);
+
+	/* Disable all interrupt in CR7 to solve the interrupt edge problem */
+	outl(0, ioaddr + DCR7);
+
+	/* Got DM910X status */
+	db->cr5_data = inl(ioaddr + DCR5);
+	outl(db->cr5_data, ioaddr + DCR5);
+	/* printk("CR5=%x\n", db->cr5_data); */
+
+	/* Check system status */
+	if (db->cr5_data & 0x2000) {
+		/* A system bus error occurred */
+		DMFE_DBUG(1, "A system bus error occurred. CR5=", db->cr5_data);
+		dev->tbusy = 1;
+		db->wait_reset = 1;		/* Need to RESET */
+		outl(0, ioaddr + DCR7);		/* disable all interrupt */
+		dev->interrupt = 0;		/* unlock interrupt */
+		return;
+	}
+	/* Free the transmitted descriptor */
+	txptr = db->tx_remove_ptr;
+	while (db->tx_packet_cnt) {
+		/* printk("tdes0=%x\n", txptr->tdes0); */
+		if (txptr->tdes0 & 0x80000000)
+			break;
+		if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
+			/* printk("tdes0=%x\n", txptr->tdes0); */
+			db->stats.tx_errors++;
+		}
+		txptr = (struct tx_desc *) txptr->next_tx_desc;
+		db->tx_packet_cnt--;
+	}
+	db->tx_remove_ptr = (struct tx_desc *) txptr;
+
+	if (dev->tbusy && (db->tx_packet_cnt < TX_FREE_DESC_CNT)) {
+		dev->tbusy = 0;		/* free a resource */
+		mark_bh(NET_BH);	/* active bottom half */
+	}
+	/* Received the coming packet */
+	if (db->rx_avail_cnt)
+		dmfe_rx_packet(dev, db);
+
+	/* reallocated rx descriptor buffer */
+	if (db->rx_avail_cnt < RX_DESC_CNT)
+		allocated_rx_buffer(db);
+
+	/* Mode Check */
+	if (db->dm910x_chk_mode & 0x2) {
+		db->dm910x_chk_mode = 0x4;
+		db->cr6_data |= 0x100;
+		update_cr6(db->cr6_data, db->ioaddr);
+	}
+	dev->interrupt = 0;	/* release interrupt lock */
+
+	/* Restore CR7 to enable interrupt mask */
+	
+	if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
+		db->cr7_data = 0x1a28d;
+	else
+		db->cr7_data = 0x1a2cd;
+	outl(db->cr7_data, ioaddr + DCR7);
+}
+
+/*
+ *	Receive the come packet and pass to upper layer
+ */
+ 
+static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
+{
+	struct rx_desc *rxptr;
+	struct sk_buff *skb;
+	int rxlen;
+
+	rxptr = db->rx_ready_ptr;
+
+	while (db->rx_avail_cnt) {
+		if (rxptr->rdes0 & 0x80000000)	/* packet owner check */
+			break;
+
+		db->rx_avail_cnt--;
+		db->interval_rx_cnt++;
+
+		if ((rxptr->rdes0 & 0x300) != 0x300) {
+			/* A packet without First/Last flag */
+			/* reused this SKB */
+			DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
+			dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
+			db->rx_error_cnt++;
+		} else {
+			rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4;	/* skip CRC */
+
+			/* A packet with First/Last flag */
+			if (rxptr->rdes0 & 0x8000) {	/* error summary bit check */
+				/* This is a error packet */
+				/* printk("rdes0 error : %x \n", rxptr->rdes0); */
+				db->stats.rx_errors++;
+				if (rxptr->rdes0 & 1)
+					db->stats.rx_fifo_errors++;
+				if (rxptr->rdes0 & 2)
+					db->stats.rx_crc_errors++;
+				if (rxptr->rdes0 & 0x80)
+					db->stats.rx_length_errors++;
+			}
+			if (!(rxptr->rdes0 & 0x8000) ||
+			    ((db->cr6_data & CR6_PM) && (rxlen > 6))) {
+				skb = (struct sk_buff *) rxptr->rx_skb_ptr;
+
+				/* Received Packet CRC check need or not */
+				if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen) != (*(unsigned long *) (skb->tail + rxlen)))) {
+					/* Found a error received packet */
+					dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
+					db->dm910x_chk_mode = 3;
+				} else {
+					/* A good packet coming, send to upper layer */
+					skb->dev = dev;
+					skb_put(skb, rxlen);
+					skb->protocol = eth_type_trans(skb, dev);
+					netif_rx(skb);	/* Send to upper layer */
+					/* skb->ip_summed = CHECKSUM_UNNECESSARY; */
+					dev->last_rx = jiffies;
+					db->stats.rx_packets++;
+				}
+			} else {
+				DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
+				dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
+			}
+		}
+
+		rxptr = (struct rx_desc *) rxptr->next_rx_desc;
+	}
+
+	db->rx_ready_ptr = rxptr;
+}
+
+/*
+ *	Get statistics from driver.
+ */
+ 
+static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
+{
+	struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
+
+	DMFE_DBUG(0, "dmfe_get_stats", 0);
+	return &db->stats;
+}
+
+/*
+ *	Set DM910X multicast address
+ */
+ 
+static void dmfe_set_filter_mode(struct net_device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+
+	DMFE_DBUG(0, "dmfe_set_filter_mode()", 0);
+
+	if (dev->flags & IFF_PROMISC) {
+		DMFE_DBUG(0, "Enable PROM Mode", 0);
+		db->cr6_data |= CR6_PM | CR6_PBF;
+		update_cr6(db->cr6_data, db->ioaddr);
+		return;
+	}
+	if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) {
+		DMFE_DBUG(0, "Pass all multicast address", dev->mc_count);
+		db->cr6_data &= ~(CR6_PM | CR6_PBF);
+		db->cr6_data |= CR6_PAM;
+		return;
+	}
+	DMFE_DBUG(0, "Set multicast address", dev->mc_count);
+	send_filter_frame(dev, dev->mc_count);
+}
+
+/*
+ *	Process the upper socket ioctl command
+ */
+ 
+static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
+	return 0;
+}
+
+/*
+ *	A periodic timer routine
+ *	Dynamic media sense, allocated Rx buffer...
+ */
+ 
+static void dmfe_timer(unsigned long data)
+{
+	u32 tmp_cr8;
+	unsigned char tmp_cr12;
+	struct net_device *dev = (struct net_device *) data;
+	struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
+
+	DMFE_DBUG(0, "dmfe_timer()", 0);
+
+	/* Do reset now */
+	if (db->in_reset_state)
+		return;
+
+	/* Operating Mode Check */
+	if ((db->dm910x_chk_mode & 0x1) && (db->stats.rx_packets > MAX_CHECK_PACKET)) {
+		db->dm910x_chk_mode = 0x4;
+	}
+	/* Dynamic reset DM910X : system error or transmit time-out */
+	tmp_cr8 = inl(db->ioaddr + DCR8);
+	if ((db->interval_rx_cnt == 0) && (tmp_cr8)) {
+		db->wait_reset = 1;
+		/* printk("CR8 %x, Interval Rx %x\n", tmp_cr8, db->interval_rx_cnt); */
+	}
+	/* Receiving Traffic check */
+	if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
+		db->cr7_data = 0x1a28d;
+	else
+		db->cr7_data = 0x1a2cd;
+	outl(db->cr7_data, db->ioaddr + DCR7);
+
+	db->interval_rx_cnt = 0;
+
+	if (db->wait_reset | (db->tx_packet_cnt &&
+			      ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
+		/* printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); */
+		DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
+		dmfe_dynamic_reset(dev);
+		db->timer.expires = DMFE_TIMER_WUT;
+		add_timer(&db->timer);
+		return;
+	}
+	db->rx_error_cnt = 0;	/* Clear previous counter */
+
+	/* Link status check, Dynamic media type change */
+	tmp_cr12 = inb(db->ioaddr + DCR12);
+	if (db->chip_revesion == 0x02000030) {
+		if (tmp_cr12 & 2)
+			tmp_cr12 = 0x0;		/* Link failed */
+		else
+			tmp_cr12 = 0x3;		/* Link OK */
+	}
+	if (!(tmp_cr12 & 0x3) && !db->link_failed) {
+		/* Link Failed */
+		DMFE_DBUG(0, "Link Failed", tmp_cr12);
+		db->link_failed = 1;
+		phy_write(db->ioaddr, db->phy_addr, 0, 0x8000);		/* reset Phy controller */
+	} else if ((tmp_cr12 & 0x3) && db->link_failed) {
+		DMFE_DBUG(0, "Link link OK", tmp_cr12);
+		db->link_failed = 0;
+		if (db->media_mode & DMFE_AUTO)
+			dmfe_sense_speed(db);
+		dmfe_process_mode(db);
+		update_cr6(db->cr6_data, db->ioaddr);
+		/* SHOW_MEDIA_TYPE(db->op_mode); */
+	}
+	/* reallocated rx descriptor buffer */
+	if (db->rx_avail_cnt < RX_DESC_CNT)
+		allocated_rx_buffer(db);
+
+	/* Timer active again */
+	db->timer.expires = DMFE_TIMER_WUT;
+	add_timer(&db->timer);
+}
+
+/*
+ *	Dynamic reset the DM910X board
+ *	Stop DM910X board
+ *	Free Tx/Rx allocated memory
+ *	Reset DM910X board
+ *	Re-initilize DM910X board
+ */
+ 
+static void dmfe_dynamic_reset(struct net_device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+
+	DMFE_DBUG(0, "dmfe_dynamic_reset()", 0);
+
+	/* Enter dynamic reset route */
+	db->in_reset_state = 1;
+
+	/* Disable upper layer interface */
+	dev->tbusy = 1;		/* transmit packet disable */
+	dev->start = 0;		/* interface not ready */
+
+	db->cr6_data &= ~(CR6_RXSC | CR6_TXSC);		/* Disable Tx/Rx */
+	update_cr6(db->cr6_data, dev->base_addr);
+
+	/* Free Rx Allocate buffer */
+	dmfe_free_rxbuffer(db);
+
+	/* system variable init */
+	db->tx_packet_cnt = 0;
+	db->rx_avail_cnt = 0;
+	db->link_failed = 0;
+	db->wait_reset = 0;
+	db->rx_error_cnt = 0;
+
+	/* Re-initilize DM910X board */
+	dmfe_init_dm910x(dev);
+
+	/* Restart upper layer interface */
+	dev->tbusy = 0;		/* Can transmit packet */
+	dev->start = 1;		/* interface ready */
+
+	/* Leave dynamic reser route */
+	db->in_reset_state = 0;
+}
+
+/*
+ *	Free all allocated rx buffer 
+ */
+
+static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
+{
+	DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0);
+
+	/* free allocated rx buffer */
+	while (db->rx_avail_cnt) {
+		dev_kfree_skb((void *) (db->rx_ready_ptr->rx_skb_ptr));
+		db->rx_ready_ptr = (struct rx_desc *) db->rx_ready_ptr->next_rx_desc;
+		db->rx_avail_cnt--;
+	}
+}
+
+/*
+ *	Reused the SK buffer
+ */
+ 
+static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
+{
+	struct rx_desc *rxptr = db->rx_insert_ptr;
+
+	if (!(rxptr->rdes0 & 0x80000000)) {
+		rxptr->rx_skb_ptr = (u32) skb;
+		rxptr->rdes2 = virt_to_bus(skb->tail);
+		rxptr->rdes0 = 0x80000000;
+		db->rx_avail_cnt++;
+		db->rx_insert_ptr = (struct rx_desc *) rxptr->next_rx_desc;
+	} else
+		DMFE_DBUG(0, "SK Buffer reused method error", db->rx_avail_cnt);
+}
+
+/*
+ *	Initialize transmit/Receive descriptor 
+ *	Using Chain structure, and allocated Tx/Rx buffer
+ */
+ 
+static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
+{
+	struct tx_desc *tmp_tx;
+	struct rx_desc *tmp_rx;
+	unsigned char *tmp_buf;
+	int i;
+
+	DMFE_DBUG(0, "dmfe_descriptor_init()", 0);
+
+	/* tx descriptor start pointer */
+	db->tx_insert_ptr = db->first_tx_desc;
+	db->tx_remove_ptr = db->first_tx_desc;
+	outl(virt_to_bus(db->first_tx_desc), ioaddr + DCR4);	/* Init CR4 */
+
+	/* rx descriptor start pointer */
+	db->first_rx_desc = (struct rx_desc *)
+	    ((u32) db->first_tx_desc + sizeof(struct rx_desc) * TX_DESC_CNT);
+	db->rx_insert_ptr = db->first_rx_desc;
+	db->rx_ready_ptr = db->first_rx_desc;
+	outl(virt_to_bus(db->first_rx_desc), ioaddr + DCR3);	/* Init CR3 */
+
+	/* Init Transmit chain */
+	tmp_buf = db->buf_pool_start;
+	for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) {
+		tmp_tx->tx_buf_ptr = (u32) tmp_buf;
+		tmp_tx->tdes0 = 0;
+		tmp_tx->tdes1 = 0x81000000;	/* IC, chain */
+		tmp_tx->tdes2 = (u32) virt_to_bus(tmp_buf);
+		tmp_tx->tdes3 = (u32) virt_to_bus(tmp_tx) + sizeof(struct tx_desc);
+		tmp_tx->next_tx_desc = (u32) ((u32) tmp_tx + sizeof(struct tx_desc));
+		tmp_buf = (unsigned char *) ((u32) tmp_buf + TX_BUF_ALLOC);
+	}
+	(--tmp_tx)->tdes3 = (u32) virt_to_bus(db->first_tx_desc);
+	tmp_tx->next_tx_desc = (u32) db->first_tx_desc;
+
+	/* Init Receive descriptor chain */
+	for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) {
+		tmp_rx->rdes0 = 0;
+		tmp_rx->rdes1 = 0x01000600;
+		tmp_rx->rdes3 = (u32) virt_to_bus(tmp_rx) + sizeof(struct rx_desc);
+		tmp_rx->next_rx_desc = (u32) ((u32) tmp_rx + sizeof(struct rx_desc));
+	}
+	(--tmp_rx)->rdes3 = (u32) virt_to_bus(db->first_rx_desc);
+	tmp_rx->next_rx_desc = (u32) db->first_rx_desc;
+
+	/* pre-allocated Rx buffer */
+	allocated_rx_buffer(db);
+}
+
+/*
+ *	Update CR6 vaule
+ *	Firstly stop DM910X , then written value and start
+ */
+ 
+static void update_cr6(u32 cr6_data, u32 ioaddr)
+{
+	u32 cr6_tmp;
+
+	cr6_tmp = cr6_data & ~0x2002;	/* stop Tx/Rx */
+	outl(cr6_tmp, ioaddr + DCR6);
+	DELAY_5US;
+	outl(cr6_data, ioaddr + DCR6);
+	cr6_tmp = inl(ioaddr + DCR6);
+	/* printk("CR6 update %x ", cr6_tmp); */
+}
+
+/*
+ *	Send a setup frame
+ *	This setup frame initilize DM910X addres filter mode
+ */
+ 
+static void send_filter_frame(struct net_device *dev, int mc_cnt)
+{
+	struct dmfe_board_info *db = dev->priv;
+	struct dev_mc_list *mcptr;
+	struct tx_desc *txptr;
+	u16 *addrptr;
+	u32 *suptr;
+	int i;
+
+	DMFE_DBUG(0, "send_filetr_frame()", 0);
+
+	txptr = db->tx_insert_ptr;
+	suptr = (u32 *) txptr->tx_buf_ptr;
+
+	/* broadcast address */
+	*suptr++ = 0xffff;
+	*suptr++ = 0xffff;
+	*suptr++ = 0xffff;
+
+	/* Node address */
+	addrptr = (u16 *) dev->dev_addr;
+	*suptr++ = addrptr[0];
+	*suptr++ = addrptr[1];
+	*suptr++ = addrptr[2];
+
+	/* fit the multicast address */
+	for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+		addrptr = (u16 *) mcptr->dmi_addr;
+		*suptr++ = addrptr[0];
+		*suptr++ = addrptr[1];
+		*suptr++ = addrptr[2];
+	}
+
+	for (; i < 14; i++) {
+		*suptr++ = 0xffff;
+		*suptr++ = 0xffff;
+		*suptr++ = 0xffff;
+	}
+
+	/* prepare the setup frame */
+	db->tx_packet_cnt++;
+	dev->tbusy = 1;
+	txptr->tdes1 = 0x890000c0;
+	txptr->tdes0 = 0x80000000;
+	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+
+	update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+	outl(0x1, dev->base_addr + DCR1);
+	update_cr6(db->cr6_data, dev->base_addr);
+	dev->trans_start = jiffies;
+
+}
+
+/*
+ *	Allocate rx buffer,
+ *	Allocate as many Rx buffers as possible.
+ */
+static void allocated_rx_buffer(struct dmfe_board_info *db)
+{
+	struct rx_desc *rxptr;
+	struct sk_buff *skb;
+
+	rxptr = db->rx_insert_ptr;
+
+	while (db->rx_avail_cnt < RX_DESC_CNT) {
+		if ((skb = alloc_skb(RX_ALLOC_SIZE, GFP_ATOMIC)) == NULL)
+			break;
+		rxptr->rx_skb_ptr = (u32) skb;
+		rxptr->rdes2 = virt_to_bus(skb->tail);
+		rxptr->rdes0 = 0x80000000;
+		rxptr = (struct rx_desc *) rxptr->next_rx_desc;
+		db->rx_avail_cnt++;
+	}
+
+	db->rx_insert_ptr = rxptr;
+}
+
+/*
+ *	Read one word data from the serial ROM
+ */
+ 
+static u16 read_srom_word(long ioaddr, int offset)
+{
+	int i;
+	u16 srom_data = 0;
+	long cr9_ioaddr = ioaddr + DCR9;
+
+	outl(CR9_SROM_READ, cr9_ioaddr);
+	outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+	/* Send the Read Command 110b */
+	SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+	SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+	SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr);
+
+	/* Send the offset */
+	for (i = 5; i >= 0; i--) {
+		srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
+		SROM_CLK_WRITE(srom_data, cr9_ioaddr);
+	}
+
+	outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+	for (i = 16; i > 0; i--) {
+		outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr);
+		DELAY_5US;
+		srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0);
+		outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+		DELAY_5US;
+	}
+
+	outl(CR9_SROM_READ, cr9_ioaddr);
+	return srom_data;
+}
+
+/*
+ *	Parser Control media block to get Phy address
+ */
+ 
+static void parser_ctrl_info(struct dmfe_board_info *db)
+{
+	int i;
+	char *sdata = db->srom;
+	unsigned char count;
+
+	/* point to info leaf0 */
+	count = *(sdata + 33);
+
+	/* Point to First media block */
+	sdata += 34;
+	for (i = 0; i < count; i++) {
+		if (*(sdata + 1) == 1) {
+			db->phy_addr = *(sdata + 2);
+			break;
+		}
+		sdata += ((unsigned char) *(sdata) & 0x7f) + 1;
+	}
+
+	if (i >= count) {
+		printk("Can't found Control Block\n");
+		db->phy_addr = 1;
+	}
+}
+
+/*
+ *	Auto sense the media mode
+ */
+ 
+static void dmfe_sense_speed(struct dmfe_board_info *db)
+{
+	int i;
+	u16 phy_mode;
+
+	for (i = 1000; i; i--) {
+		DELAY_5US;
+		phy_mode = phy_read(db->ioaddr, db->phy_addr, 1);
+		if ((phy_mode & 0x24) == 0x24)
+			break;
+	}
+
+	if (i) {
+		phy_mode = phy_read(db->ioaddr, db->phy_addr, 17) & 0xf000;
+		/* printk("Phy_mode %x ",phy_mode); */
+		switch (phy_mode) {
+		case 0x1000:
+			db->op_mode = DMFE_10MHF;
+			break;
+		case 0x2000:
+			db->op_mode = DMFE_10MFD;
+			break;
+		case 0x4000:
+			db->op_mode = DMFE_100MHF;
+			break;
+		case 0x8000:
+			db->op_mode = DMFE_100MFD;
+			break;
+		default:
+			db->op_mode = DMFE_100MHF;
+			DMFE_DBUG(1, "Media Type error, phy reg17", phy_mode);
+			break;
+		}
+	} else {
+		db->op_mode = DMFE_100MHF;
+		DMFE_DBUG(0, "Link Failed :", phy_mode);
+	}
+}
+
+/*
+ *	Process op-mode
+ *	AUTO mode : PHY controller in Auto-negotiation Mode
+ *	Force mode: PHY controller in force mode with HUB
+ *	N-way force capability with SWITCH
+ */
+ 
+static void dmfe_process_mode(struct dmfe_board_info *db)
+{
+	u16 phy_reg;
+
+	/* Full Duplex Mode Check */
+	db->cr6_data &= ~CR6_FDM;	/* Clear Full Duplex Bit */
+	if (db->op_mode & 0x4)
+		db->cr6_data |= CR6_FDM;
+
+	if (!(db->media_mode & DMFE_AUTO)) {	/* Force Mode Check */
+		/* User force the media type */
+		phy_reg = phy_read(db->ioaddr, db->phy_addr, 5);
+		/* printk("Nway phy_reg5 %x ",phy_reg); */
+		if (phy_reg & 0x1) {
+			/* parter own the N-Way capability */
+			phy_reg = phy_read(db->ioaddr, db->phy_addr, 4) & ~0x1e0;
+			switch (db->op_mode) {
+			case DMFE_10MHF:
+				phy_reg |= 0x20;
+				break;
+			case DMFE_10MFD:
+				phy_reg |= 0x40;
+				break;
+			case DMFE_100MHF:
+				phy_reg |= 0x80;
+				break;
+			case DMFE_100MFD:
+				phy_reg |= 0x100;
+				break;
+			}
+			phy_write(db->ioaddr, db->phy_addr, 4, phy_reg);
+		} else {
+			/* parter without the N-Way capability */
+			switch (db->op_mode) {
+			case DMFE_10MHF:
+				phy_reg = 0x0;
+				break;
+			case DMFE_10MFD:
+				phy_reg = 0x100;
+				break;
+			case DMFE_100MHF:
+				phy_reg = 0x2000;
+				break;
+			case DMFE_100MFD:
+				phy_reg = 0x2100;
+				break;
+			}
+			phy_write(db->ioaddr, db->phy_addr, 0, phy_reg);
+		}
+	}
+}
+
+/*
+ *	Write a word to Phy register
+ */
+ 
+static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data)
+{
+	u16 i;
+	u32 ioaddr = iobase + DCR9;
+
+	/* Send 33 synchronization clock to Phy controller */
+	for (i = 0; i < 35; i++)
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+
+	/* Send start command(01) to Phy */
+	phy_write_1bit(ioaddr, PHY_DATA_0);
+	phy_write_1bit(ioaddr, PHY_DATA_1);
+
+	/* Send write command(01) to Phy */
+	phy_write_1bit(ioaddr, PHY_DATA_0);
+	phy_write_1bit(ioaddr, PHY_DATA_1);
+
+	/* Send Phy addres */
+	for (i = 0x10; i > 0; i = i >> 1)
+		phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+
+	/* Send register addres */
+	for (i = 0x10; i > 0; i = i >> 1)
+		phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+
+	/* written trasnition */
+	phy_write_1bit(ioaddr, PHY_DATA_1);
+	phy_write_1bit(ioaddr, PHY_DATA_0);
+
+	/* Write a word data to PHY controller */
+	for (i = 0x8000; i > 0; i >>= 1)
+		phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+}
+
+/*
+ *	Read a word data from phy register
+ */
+ 
+static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset)
+{
+	int i;
+	u16 phy_data;
+	u32 ioaddr = iobase + DCR9;
+
+	/* Send 33 synchronization clock to Phy controller */
+	for (i = 0; i < 35; i++)
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+
+	/* Send start command(01) to Phy */
+	phy_write_1bit(ioaddr, PHY_DATA_0);
+	phy_write_1bit(ioaddr, PHY_DATA_1);
+
+	/* Send read command(10) to Phy */
+	phy_write_1bit(ioaddr, PHY_DATA_1);
+	phy_write_1bit(ioaddr, PHY_DATA_0);
+
+	/* Send Phy addres */
+	for (i = 0x10; i > 0; i = i >> 1)
+		phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+
+	/* Send register addres */
+	for (i = 0x10; i > 0; i = i >> 1)
+		phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+
+	/* Skip transition state */
+	phy_read_1bit(ioaddr);
+
+	/* read 16bit data */
+	for (phy_data = 0, i = 0; i < 16; i++) {
+		phy_data <<= 1;
+		phy_data |= phy_read_1bit(ioaddr);
+	}
+
+	return phy_data;
+}
+
+/*
+ *	Write one bit data to Phy Controller
+ */
+ 
+static void phy_write_1bit(u32 ioaddr, u32 phy_data)
+{
+	outl(phy_data, ioaddr);	/* MII Clock Low */
+	DELAY_1US;
+	outl(phy_data | MDCLKH, ioaddr);	/* MII Clock High */
+	DELAY_1US;
+	outl(phy_data, ioaddr);	/* MII Clock Low */
+	DELAY_1US;
+}
+
+/*
+ *	Read one bit phy data from PHY controller
+ */
+ 
+static u16 phy_read_1bit(u32 ioaddr)
+{
+	u16 phy_data;
+
+	outl(0x50000, ioaddr);
+	DELAY_1US;
+	phy_data = (inl(ioaddr) >> 19) & 0x1;
+	outl(0x40000, ioaddr);
+	DELAY_1US;
+
+	return phy_data;
+}
+
+/*
+ *	Calculate the CRC valude of the Rx packet
+ */
+ 
+static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
+{
+	unsigned long Crc = 0xffffffff;
+
+	while (Len--) {
+		Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
+	}
+
+	return ~Crc;
+
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw");
+MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(mode, "i");
+MODULE_PARM(cr6set, "i");
+MODULE_PARM(chkmode, "i");
+
+/*	Description: 
+ *	when user used insmod to add module, system invoked init_module()
+ *	to initilize and register.
+ */
+ 
+int init_module(void)
+{
+	DMFE_DBUG(0, "init_module() ", debug);
+
+	if (debug)
+		dmfe_debug = debug;	/* set debug flag */
+	if (cr6set)
+		dmfe_cr6_user_set = cr6set;
+
+	switch (mode) {
+	case 0:
+	case 1:
+	case 4:
+	case 5:
+		dmfe_media_mode = mode;
+		break;
+	default:
+		dmfe_media_mode = 8;
+		break;
+	}
+
+	return dmfe_reg_board(0);	/* search board and register */
+}
+
+/*
+ *	Description: 
+ *	when user used rmmod to delete module, system invoked clean_module()
+ *	to un-register device.
+ */
+ 
+void cleanup_module(void)
+{
+	struct net_device *next_dev;
+
+	DMFE_DBUG(0, "clean_module()", 0);
+
+	while (dmfe_root_dev) {
+		next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev;
+		unregister_netdev(dmfe_root_dev);
+		release_region(dmfe_root_dev->base_addr, DM9102_IO_SIZE);
+		kfree(dmfe_root_dev->priv);	/* free board information */
+		kfree(dmfe_root_dev);	/* free device structure */
+		dmfe_root_dev = next_dev;
+	}
+	DMFE_DBUG(0, "clean_module() exit", 0);
+}
+
+#endif				/* MODULE */
diff -u --recursive --new-file v2.3.18/linux/drivers/net/e2100.c linux/drivers/net/e2100.c
--- v2.3.18/linux/drivers/net/e2100.c	Wed Aug 18 11:36:42 1999
+++ linux/drivers/net/e2100.c	Wed Sep 29 15:32:15 1999
@@ -75,7 +75,7 @@
 {
 	/* This is a little weird: set the shared memory window by doing a
 	   read.  The low address bits specify the starting page. */
-	mem_base[start_page];
+	readb(mem_base+start_page);
 	inb(port + E21_MEM_ENABLE);
 	outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
 }
diff -u --recursive --new-file v2.3.18/linux/drivers/net/hamradio/Config.in linux/drivers/net/hamradio/Config.in
--- v2.3.18/linux/drivers/net/hamradio/Config.in	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/hamradio/Config.in	Sat Oct  2 07:38:27 1999
@@ -7,8 +7,8 @@
 dep_tristate 'High-speed (DMA) SCC driver for AX.25' CONFIG_DMASCC $CONFIG_AX25
 dep_tristate 'Z8530 SCC driver' CONFIG_SCC $CONFIG_AX25
 if [ "$CONFIG_SCC" != "n" ]; then
-  bool '   additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY
-  bool '   support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO
+   bool '  additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY
+   bool '  support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO
 fi
     
 dep_tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX $CONFIG_AX25
@@ -18,15 +18,16 @@
 
 dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_PARPORT $CONFIG_AX25
 if [ "$CONFIG_SOUNDMODEM" != "n" ]; then
-  bool '   soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
-  bool '   soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
-  bool '   soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200
-  bool '   soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7
-  bool '   soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
-  bool '   soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
-  bool '   soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
-  bool '   soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
-  bool '   soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
+   bool '  soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
+   bool '  soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
+   bool '  soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200
+   bool '  soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7
+   bool '  soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
+   bool '  soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
+   bool '  soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
+   bool '  soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
+   bool '  soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
 fi
 
-tristate 'YAM driver for AX.25' CONFIG_YAM
+dep_tristate 'YAM driver for AX.25' CONFIG_YAM $CONFIG_AX25
+
diff -u --recursive --new-file v2.3.18/linux/drivers/net/hamradio/baycom_ser_fdx.c linux/drivers/net/hamradio/baycom_ser_fdx.c
--- v2.3.18/linux/drivers/net/hamradio/baycom_ser_fdx.c	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/hamradio/baycom_ser_fdx.c	Sat Oct  2 07:38:27 1999
@@ -688,7 +688,7 @@
 
 static int __init baycom_ser_fdx_setup(char *str)
 {
-        static unsigned __initdata nr_dev = 0;
+        static unsigned nr_dev = 0;
         int ints[4];
 
         if (nr_dev >= NR_PORTS)
diff -u --recursive --new-file v2.3.18/linux/drivers/net/hamradio/baycom_ser_hdx.c linux/drivers/net/hamradio/baycom_ser_hdx.c
--- v2.3.18/linux/drivers/net/hamradio/baycom_ser_hdx.c	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/hamradio/baycom_ser_hdx.c	Sat Oct  2 07:38:27 1999
@@ -728,7 +728,7 @@
 
 static int __init baycom_ser_hdx_setup(char *str)
 {
-        static unsigned __initdata nr_dev = 0;
+        static unsigned nr_dev = 0;
 	int ints[3];
 
         if (nr_dev >= NR_PORTS)
diff -u --recursive --new-file v2.3.18/linux/drivers/net/hamradio/yam.c linux/drivers/net/hamradio/yam.c
--- v2.3.18/linux/drivers/net/hamradio/yam.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/net/hamradio/yam.c	Sat Oct  2 07:38:27 1999
@@ -893,10 +893,12 @@
 }
 
 #ifdef CONFIG_INET
+
 #ifndef PROC_NET_YAM
 #define PROC_NET_YAM (PROC_NET_LAST+10)		/* Sorry again... */
 #endif
 
+#ifdef CONFIG_PROC_FS
 struct proc_dir_entry yam_proc_dir_entry =
 {
 	PROC_NET_YAM, 3, "yam", S_IFREG | S_IRUGO, 1, 0, 0, 0,
@@ -905,6 +907,10 @@
 
 #define yam_net_procfs_init()   proc_net_register(&yam_proc_dir_entry);
 #define yam_net_procfs_remove() proc_net_unregister(PROC_NET_YAM);
+#else
+#define yam_net_procfs_init()
+#define yam_net_procfs_remove()
+#endif
 #else
 #define yam_net_procfs_init()
 #define yam_net_procfs_remove()
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/Config.in linux/drivers/net/irda/Config.in
--- v2.3.18/linux/drivers/net/irda/Config.in	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/irda/Config.in	Wed Sep 29 15:32:14 1999
@@ -15,12 +15,12 @@
 comment 'Dongle support' 
 bool 'Serial dongle support' CONFIG_DONGLE
 if [ "$CONFIG_DONGLE" != "n" ]; then
-  dep_tristate '   ESI JetEye PC dongle' CONFIG_ESI_DONGLE $CONFIG_IRDA
-  dep_tristate '   ACTiSYS IR-220L and IR220L+ dongle' CONFIG_ACTISYS_DONGLE $CONFIG_IRDA
-  dep_tristate '   Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRDA
-  dep_tristate '   Greenwich GIrBIL dongle' CONFIG_GIRBIL_DONGLE $CONFIG_IRDA
-  dep_tristate '   Parallax LiteLink dongle' CONFIG_LITELINK_DONGLE $CONFIG_IRDA
-  dep_tristate '   Adaptec Airport 1000/2000 dongle' CONFIG_AIRPORT_DONGLE $CONFIG_IRDA
+   dep_tristate '  ESI JetEye PC dongle' CONFIG_ESI_DONGLE $CONFIG_IRDA
+   dep_tristate '  ACTiSYS IR-220L and IR220L+ dongle' CONFIG_ACTISYS_DONGLE $CONFIG_IRDA
+   dep_tristate '  Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRDA
+   dep_tristate '  Greenwich GIrBIL dongle' CONFIG_GIRBIL_DONGLE $CONFIG_IRDA
+   dep_tristate '  Parallax LiteLink dongle' CONFIG_LITELINK_DONGLE $CONFIG_IRDA
+   dep_tristate '  Adaptec Airport 1000/2000 dongle' CONFIG_AIRPORT_DONGLE $CONFIG_IRDA
 fi
 
 endmenu
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/actisys.c linux/drivers/net/irda/actisys.c
--- v2.3.18/linux/drivers/net/irda/actisys.c	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/irda/actisys.c	Wed Sep 29 15:32:14 1999
@@ -135,14 +135,14 @@
 		
 		/* Wait at a few ms */
 		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(2);
+		schedule_timeout(MSECS_TO_JIFFIES(20));
 
 		/* Set DTR, Set RTS */
 		irda_device_set_dtr_rts(idev, TRUE, TRUE);
 		
 		/* Wait at a few ms again */
 		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(2);
+		schedule_timeout(MSECS_TO_JIFFIES(20));
 
                 /* Go to next baudrate */
 		if (idev->io.dongle_id == ACTISYS_DONGLE)
@@ -174,7 +174,7 @@
 
 	/* Sleep 10-20 ms*/
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(2);
+	schedule_timeout(MSECS_TO_JIFFIES(20));
 	
 	/* Go back to normal mode */
 	irda_device_set_dtr_rts(idev, TRUE, TRUE);
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
--- v2.3.18/linux/drivers/net/irda/irport.c	Fri Sep 10 23:57:30 1999
+++ linux/drivers/net/irda/irport.c	Wed Sep 29 15:32:14 1999
@@ -6,7 +6,7 @@
  * Status:	  Experimental.
  * Author:	  Dag Brattli <dagb@cs.uit.no>
  * Created at:	  Sun Aug  3 13:49:59 1997
- * Modified at:   Wed Aug 11 09:24:46 1999
+ * Modified at:   Tue Aug 31 13:54:27 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:	  serial.c by Linus Torvalds 
  * 
@@ -280,7 +280,7 @@
 	int lcr;    /* Line control reg */
 	int divisor;
 
-	DEBUG(0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
+	DEBUG(2, __FUNCTION__ "(), Setting speed to: %d\n", speed);
 
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
@@ -472,8 +472,8 @@
  */
 static void irport_receive(struct irda_device *idev) 
 {
-	int iobase;
 	int boguscount = 0;
+	int iobase;
 
 	ASSERT(idev != NULL, return;);
 
@@ -488,7 +488,7 @@
 
 		/* Make sure we don't stay here to long */
 		if (boguscount++ > 32) {
-			DEBUG(0,__FUNCTION__ "(), breaking!\n");
+			DEBUG(2,__FUNCTION__ "(), breaking!\n");
 			break;
 		}
 	} while (inb(iobase+UART_LSR) & UART_LSR_DR);	
@@ -530,9 +530,8 @@
 			DEBUG(2, __FUNCTION__ "(), RLSI\n");
 			break;
 		case UART_IIR_RDI:
-			//if (lsr & UART_LSR_DR)
-				/* Receive interrupt */
-				irport_receive(idev);
+			/* Receive interrupt */
+			irport_receive(idev);
 			break;
 		case UART_IIR_THRI:
 			if (lsr & UART_LSR_THRE)
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/irtty.c linux/drivers/net/irda/irtty.c
--- v2.3.18/linux/drivers/net/irda/irtty.c	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/irda/irtty.c	Wed Sep 29 15:32:14 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Dec  9 21:18:38 1997
- * Modified at:   Tue Aug 24 13:32:24 1999
+ * Modified at:   Tue Sep 28 08:39:29 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
  *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
@@ -25,11 +25,13 @@
  ********************************************************************/    
 
 #include <linux/module.h>
-#include <asm/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/tty.h>
-#include <asm/segment.h>
 #include <linux/init.h>
+#include <linux/skbuff.h>
+
+#include <asm/segment.h>
+#include <asm/uaccess.h>
 
 #include <net/irda/irda.h>
 #include <net/irda/irtty.h>
@@ -411,7 +413,7 @@
 		 */
  		if (fp && *fp++) { 
 			DEBUG( 0, "Framing or parity error!\n");
-			irda_device_set_media_busy(&self->idev, TRUE);
+			irda_device_set_media_busy(&self->idev.netdev, TRUE);
 
  			cp++;
  			continue;
@@ -726,8 +728,13 @@
 
 static int irtty_net_open(struct net_device *dev)
 {
+	struct irda_device *idev = dev->priv;
+
 	irda_device_net_open(dev);
 
+	/* Make sure we can receive more data */
+	irtty_stop_receiver(idev, FALSE);
+
 	MOD_INC_USE_COUNT;
 
 	return 0;
@@ -735,6 +742,11 @@
 
 static int irtty_net_close(struct net_device *dev)
 {
+	struct irda_device *idev = dev->priv;
+
+	/* Make sure we don't receive more data */
+	irtty_stop_receiver(idev, TRUE);
+
 	irda_device_net_close(dev);
 
 	MOD_DEC_USE_COUNT;
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/pc87108.c linux/drivers/net/irda/pc87108.c
--- v2.3.18/linux/drivers/net/irda/pc87108.c	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/irda/pc87108.c	Wed Sep 29 15:32:14 1999
@@ -1303,7 +1303,7 @@
 {
 	/* Just delay 60 ms */
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(6);
+	schedule_timeout(MSECS_TO_JIFFIES(60));
 }
 
 /*
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/smc-ircc.c linux/drivers/net/irda/smc-ircc.c
--- v2.3.18/linux/drivers/net/irda/smc-ircc.c	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/irda/smc-ircc.c	Wed Sep 29 15:32:14 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Thomas Davis (tadavis@jps.net)
  * Created at:    
- * Modified at:   Tue Aug 24 13:33:22 1999
+ * Modified at:   Wed Sep 22 07:47:19 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Thomas Davis, All Rights Reserved.
@@ -52,14 +52,16 @@
 
 #define CHIP_IO_EXTENT 8
 
-static unsigned int io[]  = { 0x2e8, 0x140, ~0, ~0 };
-static unsigned int io2[] = { 0x2f8, 0x3e8, 0, 0};
+static unsigned int io[]  = { 0x2e8, 0x140, 0x118, ~0 }; 
+static unsigned int io2[] = { 0x2f8, 0x3e8, 0x2e8, 0};
 
 static struct ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL};
 
 /* Some prototypes */
 static int  ircc_open( int i, unsigned int iobase, unsigned int board_addr);
+#ifdef MODULE
 static int  ircc_close( struct irda_device *idev);
+#endif /* MODULE */
 static int  ircc_probe( int iobase, int board_addr);
 static int  ircc_dma_receive( struct irda_device *idev); 
 static int  ircc_dma_receive_complete(struct irda_device *idev, int iobase);
@@ -264,6 +266,7 @@
  *    Close driver instance
  *
  */
+#ifdef MODULE
 static int ircc_close( struct irda_device *idev)
 {
 	int iobase;
@@ -304,6 +307,7 @@
 	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
 	return 0;
 }
+#endif /* MODULE */
 
 /*
  * Function ircc_probe (iobase, board_addr, irq, dma)
@@ -311,7 +315,7 @@
  *    Returns non-negative on success.
  *
  */
-static int ircc_probe( int iobase, int iobase2) 
+static int ircc_probe(int iobase, int iobase2) 
 {
 	int version = 1;
 	int low, high, chip, config, dma, irq;
@@ -327,17 +331,17 @@
 	irq = config >> 4 & 0x0f;
 	dma = config & 0x0f;
 
-	if (high == 0x10 && low == 0xb8 && chip == 0xf1) {
-		DEBUG(0, "SMC IrDA Controller found; version = %d, "
+        if (high == 0x10 && low == 0xb8 && (chip == 0xf1 || chip == 0xf2)) { 
+                DEBUG(0, "SMC IrDA Controller found; IrCC version %d.%d, "
 		      "port 0x%04x, dma %d, interrupt %d\n",
-		      version, iobase, dma, irq);
+                      chip & 0x0f, version, iobase, dma, irq);
 	} else {
 		return -1;
 	}
 
 	serial_out(iobase, UART_MASTER, 0);
 
-	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	DEBUG(ircc_debug, "--> " __FUNCTION__ "\n");
 
 	return config;
 }
@@ -798,7 +802,7 @@
 
 	/* Just delay 60 ms */
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(6);
+	schedule_timeout(MSECS_TO_JIFFIES(60));
 
 	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
 }
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/toshoboe.c linux/drivers/net/irda/toshoboe.c
--- v2.3.18/linux/drivers/net/irda/toshoboe.c	Tue Sep  7 12:14:06 1999
+++ linux/drivers/net/irda/toshoboe.c	Wed Sep 29 15:32:14 1999
@@ -542,7 +542,7 @@
       while (self->taskfile->xmit[i].control)
         {
           current->state = TASK_INTERRUPTIBLE;
-          schedule_timeout (6);
+          schedule_timeout (MSECS_TO_JIFFIES(60));
         }
     }
 
diff -u --recursive --new-file v2.3.18/linux/drivers/net/irda/uircc.c linux/drivers/net/irda/uircc.c
--- v2.3.18/linux/drivers/net/irda/uircc.c	Tue Aug 31 17:29:14 1999
+++ linux/drivers/net/irda/uircc.c	Wed Sep 29 15:32:14 1999
@@ -767,7 +767,7 @@
 {
 	/* Just delay 60 ms */
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(6);
+	schedule_timeout(MSECS_TO_JIFFIES(60));
 }
 
 /*
diff -u --recursive --new-file v2.3.18/linux/drivers/net/pcmcia/3c589_cs.c linux/drivers/net/pcmcia/3c589_cs.c
--- v2.3.18/linux/drivers/net/pcmcia/3c589_cs.c	Fri Sep 10 23:57:30 1999
+++ linux/drivers/net/pcmcia/3c589_cs.c	Sat Oct  2 07:38:27 1999
@@ -2,9 +2,9 @@
 
     A PCMCIA ethernet driver for the 3com 3c589 card.
     
-    Copyright (C) 1998 David A. Hinds -- dhinds@hyper.stanford.edu
+    Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
 
-    3c589_cs.c 1.126 1999/06/14 17:35:34
+    3c589_cs.c 1.134 1999/09/15 15:33:09
 
     The network driver code is based on Donald Becker's 3c589 code:
     
@@ -18,6 +18,7 @@
 ======================================================================*/
 
 #include <linux/module.h>
+#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/ptrace.h>
@@ -53,8 +54,9 @@
 #define EL3_TIMER	0x0a
 #define EL3_CMD		0x0e
 #define EL3_STATUS	0x0e
-#define ID_PORT		0x100
-#define	EEPROM_READ	0x80
+
+#define EEPROM_READ	0x0080
+#define EEPROM_BUSY	0x8000
 
 #define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
 
@@ -113,7 +115,7 @@
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"3c589_cs.c 1.126 1999/06/14 17:35:34 (David Hinds)";
+"3c589_cs.c 1.134 1999/09/15 15:33:09 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -329,10 +331,10 @@
 	if (link->dev != NULL)
 	    unregister_netdev(dev);
 	if (dev->priv)
-	    kfree_s(dev->priv, sizeof(struct el3_private));
-	kfree_s(link->priv, sizeof(struct net_device));
+	    kfree(dev->priv);
+	kfree(link->priv);
     }
-    kfree_s(link, sizeof(struct dev_link_t));
+    kfree(link);
     
 } /* tc589_detach */
 
@@ -444,14 +446,15 @@
     else
 	printk(KERN_NOTICE "3c589_cs: invalid if_port requested\n");
     
-    printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, %s xcvr, "
-	   "hw_addr ", dev->name, (multi ? "562" : "589"),
-	   dev->base_addr, dev->irq, if_names[dev->if_port]);
+    printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ",
+	   dev->name, (multi ? "562" : "589"), dev->base_addr,
+	   dev->irq);
     for (i = 0; i < 6; i++)
 	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
     i = inl(ioaddr);
-    printk(KERN_INFO "  %dK FIFO split %s Rx:Tx\n",
-	   (i & 7) ? 32 : 8, ram_split[(i >> 16) & 3]);
+    printk(KERN_INFO "  %dK FIFO split %s Rx:Tx, %s xcvr\n",
+	   (i & 7) ? 32 : 8, ram_split[(i >> 16) & 3],
+	   if_names[dev->if_port]);
     return;
 
 cs_failed:
@@ -513,7 +516,7 @@
 	link->state &= ~DEV_PRESENT;
 	if (link->state & DEV_CONFIG) {
 	    dev->tbusy = 1; dev->start = 0;
-	    link->release.expires = jiffies + (HZ/20);
+	    link->release.expires = jiffies + HZ/20;
 	    add_timer(&link->release);
 	}
 	break;
@@ -574,7 +577,7 @@
     outw(EEPROM_READ + index, ioaddr + 10);
     /* Reading the eeprom takes 162 us */
     for (i = 1620; i >= 0; i--)
-	if ((inw(ioaddr + 10) & 0x8000) == 0)
+	if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
 	    break;
     return inw(ioaddr + 12);
 }
@@ -600,8 +603,10 @@
     EL3WINDOW(4);
     outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
     EL3WINDOW(1);
-    lp->media_status = (if_port < 2) ? 0x8800 : 0x4800;
-    lp->last_irq = jiffies;
+    if (if_port == 2)
+	lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
+    else
+	lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
 }
 
 static void dump_status(struct net_device *dev)
@@ -898,7 +903,7 @@
     }
     if (lp->fast_poll) {
 	lp->fast_poll--;
-	lp->media.expires = jiffies + 2;
+	lp->media.expires = jiffies + 1;
 	add_timer(&lp->media);
 	return;
     }
@@ -918,8 +923,9 @@
 	errs = inb(ioaddr + 0);
 	outw(StatsEnable, ioaddr + EL3_CMD);
 	lp->stats.tx_carrier_errors += errs;
-	if (errs) media |= 0x0010;
+	if (errs || (lp->media_status & 0x0010)) media |= 0x0010;
     }
+
     if (media != lp->media_status) {
 	if ((media & lp->media_status & 0x8000) &&
 	    ((lp->media_status ^ media) & 0x0800))
@@ -927,8 +933,8 @@
 		   (lp->media_status & 0x0800 ? "lost" : "found"));
 	else if ((media & lp->media_status & 0x4000) &&
 		 ((lp->media_status ^ media) & 0x0010))
-	    printk(KERN_INFO "%s: cable %s\n", dev->name,
-		   (lp->media_status & 0x0010 ? "fixed" : "problem"));
+	    printk(KERN_INFO "%s: coax cable %s\n", dev->name,
+		   (lp->media_status & 0x0010 ? "ok" : "problem"));
 	if (dev->if_port == 0) {
 	    if (media & 0x8000) {
 		if (media & 0x0800)
@@ -936,8 +942,7 @@
 			   dev->name);
 		else
 		    tc589_set_xcvr(dev, 2);
-	    }
-	    if (media & 0x4000) {
+	    } else if (media & 0x4000) {
 		if (media & 0x0010)
 		    tc589_set_xcvr(dev, 1);
 		else
@@ -1147,7 +1152,7 @@
 
 /*====================================================================*/
 
-int init_3c589_cs(void)
+static int __init init_3c589_cs(void)
 {
     servinfo_t serv;
     DEBUG(0, "%s\n", version);
@@ -1157,21 +1162,17 @@
 	       "does not match!\n");
 	return -1;
     }
-    register_pcmcia_driver(&dev_info, &tc589_attach, &tc589_detach);
+    register_pccard_driver(&dev_info, &tc589_attach, &tc589_detach);
     return 0;
 }
 
-#ifdef MODULE
-int init_module(void)
-{
-	return init_3c589_cs();
-}
-
-void cleanup_module(void)
+static void __exit exit_3c589_cs(void)
 {
     DEBUG(0, "3c589_cs: unloading\n");
-    unregister_pcmcia_driver(&dev_info);
+    unregister_pccard_driver(&dev_info);
     while (dev_list != NULL)
 	tc589_detach(dev_list);
 }
-#endif
+
+module_init(init_3c589_cs);
+module_exit(exit_3c589_cs);
diff -u --recursive --new-file v2.3.18/linux/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in
--- v2.3.18/linux/drivers/net/pcmcia/Config.in	Fri Sep 10 23:57:30 1999
+++ linux/drivers/net/pcmcia/Config.in	Sat Oct  2 07:38:27 1999
@@ -5,8 +5,12 @@
 mainmenu_option next_comment
 comment 'PCMCIA network devices'
 
-tristate 'PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)' CONFIG_PCMCIA_PCNET
-tristate '3Com 3c589 PCMCIA card' CONFIG_PCMCIA_3C589
-tristate 'Aviator/Raytheon 2.4MHz wireless' CONFIG_PCMCIA_RAYCS
+dep_tristate 'PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA
+dep_tristate '3Com 3c589 PCMCIA card' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA
+dep_tristate 'Aviator/Raytheon 2.4MHz wireless' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
+
+if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" ]; then
+   define_bool CONFIG_PCMCIA_NETCARD y
+fi
 
 endmenu
diff -u --recursive --new-file v2.3.18/linux/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile
--- v2.3.18/linux/drivers/net/pcmcia/Makefile	Fri Sep 10 23:57:30 1999
+++ linux/drivers/net/pcmcia/Makefile	Sat Oct  2 07:38:27 1999
@@ -11,7 +11,7 @@
 O_TARGET := pcmcia_net.o
 O_OBJS   :=
 M_OBJS   :=
-MOD_LIST_NAME := NET_MODULES
+MOD_LIST_NAME := PCMCIA_MODULES
 
 ifeq ($(CONFIG_PCMCIA_PCNET),y)
   O_OBJS += pcnet_cs.o
diff -u --recursive --new-file v2.3.18/linux/drivers/net/pcmcia/pcnet_cs.c linux/drivers/net/pcmcia/pcnet_cs.c
--- v2.3.18/linux/drivers/net/pcmcia/pcnet_cs.c	Fri Sep 10 23:57:30 1999
+++ linux/drivers/net/pcmcia/pcnet_cs.c	Sat Oct  2 07:38:27 1999
@@ -9,9 +9,9 @@
     Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory
     mode.  It will also handle the Socket EA card in either mode.
 
-    Copyright (C) 1998 David A. Hinds -- dhinds@hyper.stanford.edu
+    Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
 
-    pcnet_cs.c 1.94 1999/07/29 06:04:49
+    pcnet_cs.c 1.99 1999/09/15 15:33:09
     
     The network driver code is based on Donald Becker's NE2000 code:
 
@@ -29,6 +29,7 @@
 ======================================================================*/
 
 #include <linux/module.h>
+#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/ptrace.h>
@@ -40,7 +41,7 @@
 #include <asm/system.h>
 
 #include <linux/netdevice.h>
-#include "../../drivers/net/8390.h"
+#include <../drivers/net/8390.h>
 
 #include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
@@ -66,13 +67,12 @@
 
 static char *if_names[] = { "auto", "10baseT", "10base2"};
 
-#define PCMCIA_DEBUG 6
 #ifdef PCMCIA_DEBUG
 static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"pcnet_cs.c 1.94 1999/07/29 06:04:49 (David Hinds)";
+"pcnet_cs.c 1.99 1999/09/15 15:33:09 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -125,6 +125,8 @@
 
 static int pcnet_open(struct net_device *dev);
 static int pcnet_close(struct net_device *dev);
+static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs);
+static void ei_watchdog(u_long arg);
 
 static void pcnet_reset_8390(struct net_device *dev);
 
@@ -226,6 +228,9 @@
     dev_node_t		node;
     u_long		flags;
     caddr_t		base;
+    struct timer_list	watchdog;
+    int			stale;
+    u_short		fast_poll;
 } pcnet_dev_t;
 
 /*======================================================================
@@ -890,7 +895,13 @@
     }
     
     set_misc_reg(dev);
-    request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev_info, dev);
+    request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev);
+
+    info->watchdog.function = &ei_watchdog;
+    info->watchdog.data = (u_long)info;
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+
     return ei_open(dev);
 } /* pcnet_open */
 
@@ -909,8 +920,9 @@
     free_irq(dev->irq, dev);
     
     link->open--; dev->start = 0;
+    del_timer(&((pcnet_dev_t *)dev)->watchdog);
     if (link->state & DEV_STALE_CONFIG) {
-	link->release.expires = jiffies+HZ/20;
+	link->release.expires = jiffies + HZ/20;
 	link->state |= DEV_RELEASE_PENDING;
 	add_timer(&link->release);
     }
@@ -973,6 +985,44 @@
 
 /* ======================================================================= */
 
+static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs)
+{
+    pcnet_dev_t *info = dev_id;
+    info->stale = 0;
+    ei_interrupt(irq, dev_id, regs);
+}
+
+static void ei_watchdog(u_long arg)
+{
+    pcnet_dev_t *info = (pcnet_dev_t *)(arg);
+    struct net_device *dev = &info->dev;
+    int nic_base = dev->base_addr;
+
+    if (dev->start == 0) goto reschedule;
+
+    /* Check for pending interrupt with expired latency timer: with
+       this, we can limp along even if the interrupt is blocked */
+    outb_p(E8390_NODMA+E8390_PAGE0, nic_base + E8390_CMD);
+    if (info->stale++ && inb_p(nic_base + EN0_ISR)) {
+	if (!info->fast_poll)
+	    printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+	ei_irq_wrapper(dev->irq, dev, NULL);
+	info->fast_poll = HZ;
+    }
+    if (info->fast_poll) {
+	info->fast_poll--;
+	info->watchdog.expires = jiffies + 1;
+	add_timer(&info->watchdog);
+	return;
+    }
+
+reschedule:
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+}
+
+/* ======================================================================= */
+
 static void dma_get_8390_hdr(struct net_device *dev,
 			     struct e8390_pkt_hdr *hdr,
 			     int ring_page)
@@ -1180,7 +1230,7 @@
     odd = (c & 01); c >>= 1;
 
     if (c) {
-	do { *d++ = readw(s++); } while (--c);
+	do { *d++ = __raw_readw(s++); } while (--c);
     }
     /* get last byte by fetching a word and masking */
     if (odd)
@@ -1198,7 +1248,7 @@
     odd = (c & 01); c >>= 1;
 
     if (c) {
-	do { writew(*s++, d++); } while (--c);
+	do { __raw_writew(*s++, d++); } while (--c);
     }
     /* copy last byte doing a read-modify-write */
     if (odd)
@@ -1291,10 +1341,10 @@
     /* Try scribbling on the buffer */
     info->base = ioremap(req.Base, window_size);
     for (i = 0; i < (TX_PAGES<<8); i += 2)
-	writew((i>>1), info->base+offset+i);
+	__raw_writew((i>>1), info->base+offset+i);
     udelay(100);
     for (i = 0; i < (TX_PAGES<<8); i += 2)
-	if (readw(info->base+offset+i) != (i>>1)) break;
+	if (__raw_readw(info->base+offset+i) != (i>>1)) break;
     pcnet_reset_8390(dev);
     if (i != (TX_PAGES<<8)) {
 	iounmap(info->base);
@@ -1327,11 +1377,7 @@
 
 /*====================================================================*/
 
-#ifdef MODULE
-int init_module(void)
-#else
-int init_pcnet_cs(void)
-#endif
+static int __init init_pcnet_cs(void)
 {
     servinfo_t serv;
     DEBUG(0, "%s\n", version);
@@ -1341,19 +1387,17 @@
 	       "does not match!\n");
 	return -1;
     }
-    register_pcmcia_driver(&dev_info, &pcnet_attach, &pcnet_detach);
-    DEBUG(0, "pcnet driver registered\n" );
+    register_pccard_driver(&dev_info, &pcnet_attach, &pcnet_detach);
     return 0;
 }
 
-//__initcall(init_pcnet_cs);
-
-#ifdef MODULE
-void cleanup_module(void)
+static void __exit exit_pcnet_cs(void)
 {
     DEBUG(0, "pcnet_cs: unloading\n");
-    unregister_pcmcia_driver(&dev_info);
+    unregister_pccard_driver(&dev_info);
     while (dev_list != NULL)
 	pcnet_detach(dev_list);
 }
-#endif
+
+module_init(init_pcnet_cs);
+module_exit(exit_pcnet_cs);
diff -u --recursive --new-file v2.3.18/linux/drivers/net/pcmcia/ray_cs.c linux/drivers/net/pcmcia/ray_cs.c
--- v2.3.18/linux/drivers/net/pcmcia/ray_cs.c	Fri Sep 10 23:57:30 1999
+++ linux/drivers/net/pcmcia/ray_cs.c	Sat Oct  2 07:38:27 1999
@@ -2297,7 +2297,7 @@
     return 0;
 } /* End build_auth_frame */
 /*===========================================================================*/
-int init_ray_cs(void)
+static int __init init_ray_cs(void)
 {
     int rc;
     servinfo_t serv;
@@ -2310,30 +2310,29 @@
     }
     rc = register_pcmcia_driver(&dev_info, &ray_attach, &ray_detach);
     DEBUG(1, "raylink init_module register_pcmcia_driver returns 0x%x\n",rc);
+#ifdef CONFIG_PROC_FS    
     proc_register(&proc_root, &ray_cs_proc_entry);
+#endif    
     if (translate != 0) translate = 1;
     return 0;
-} /* init_module */
+} /* init_ray_cs */
 
-static char init_ess_id[ESSID_SIZE];
+#ifndef MODULE
 
+static char init_ess_id[ESSID_SIZE];
 static int __init essid_setup(char *str)
 {
 	strncpy(init_ess_id, str, ESSID_SIZE);
 	essid = init_ess_id;
 	return 1;
 }
-
 __setup("essid=", essid_setup);
 
+#endif
+
 /*===========================================================================*/
-#ifdef MODULE
-int init_module(void)
-{
-	init_ray_cs();
-}
 
-void cleanup_module(void)
+static void __exit exit_ray_cs(void)
 {
     DEBUG(0, "ray_cs: cleanup_module\n");
 
@@ -2342,7 +2341,12 @@
         if (dev_list->state & DEV_CONFIG) ray_release((u_long)dev_list);
         ray_detach(dev_list);
     }
+#ifdef CONFIG_PROC_FS    
     proc_unregister(&proc_root, ray_cs_proc_entry.low_ino);
-} /* cleanup_module */
-#endif
+#endif   
+} /* exit_ray_cs */
+
+module_init(init_ray_cs);
+module_exit(exit_ray_cs);
+
 /*===========================================================================*/
diff -u --recursive --new-file v2.3.18/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- v2.3.18/linux/drivers/net/pcnet32.c	Wed Aug 18 11:36:42 1999
+++ linux/drivers/net/pcnet32.c	Wed Sep 29 14:02:59 1999
@@ -13,13 +13,10 @@
  * 	This driver is for PCnet32 and PCnetPCI based ethercards
  */
 
-static const char *version = "pcnet32.c:v1.23 6.7.1999 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.23ac 21.9.1999 tsbogend@alpha.franken.de\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -39,6 +36,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/spinlock.h>
 
 static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0};
 
@@ -156,6 +154,7 @@
  *	   Michael Richard <mcr@solidum.com>)
  *         added chip id for 79c973/975 (thanks to Zach Brown <zab@zabbo.net>)
  * v1.23   fixed small bug, when manual selecting MII speed/duplex
+ * v1.23ac Added SMP spinlocking - Alan Cox <alan@redhat.com>
  */
 
 
@@ -255,14 +254,15 @@
     struct sk_buff *rx_skbuff[RX_RING_SIZE];
     struct pcnet32_access a;
     void *origmem;
-    int cur_rx, cur_tx;			/* The next free ring entry */
-    int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */
+    spinlock_t lock;				/* Guard lock */
+    int cur_rx, cur_tx;				/* The next free ring entry */
+    int dirty_rx, dirty_tx;			/* The ring entries to be free()ed. */
     struct net_device_stats stats;
     char tx_full;
     int  options;
-    int  shared_irq:1,                      /* shared irq possible */
-         full_duplex:1,                     /* full duplex possible */
-         mii:1;                             /* mii port available */
+    int  shared_irq:1,                      	/* shared irq possible */
+         full_duplex:1,                     	/* full duplex possible */
+         mii:1;                             	/* mii port available */
 #ifdef MODULE
     struct net_device *next;
 #endif    
@@ -510,7 +510,7 @@
 pcnet32_probe1(struct net_device *dev, unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx)
 {
     struct pcnet32_private *lp;
-    int i,media,fdx = 0, mii = 0;
+    int i,media,fdx = 0, mii = 0, fset = 0;
     int chip_version;
     char *chipname;
     char *priv;
@@ -551,11 +551,11 @@
 	break;
      case 0x2623:
 	chipname = "PCnet/FAST 79C971";
-	fdx = 1; mii = 1;
+	fdx = 1; mii = 1; fset = 1;
 	break;
      case 0x2624:
 	chipname = "PCnet/FAST+ 79C972";
-	fdx = 1; mii = 1;
+	fdx = 1; mii = 1; fset = 1;
 	break;
      case 0x2625:
 	chipname = "PCnet/FAST III 79C973";
@@ -588,6 +588,19 @@
 	printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
 	return ENODEV;
     }
+
+    /*
+     *	On selected chips turn on the BCR18:NOUFLO bit. This stops transmit
+     *  starting until the packet is loaded. Strike one for reliability, lose
+     *  one for latency - although on PCI this isnt a big loss. Older chips 
+     *  have FIFO's smaller than a packet, so you can't do this.
+     */
+         
+    if(fset)
+    {
+    	a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800));
+	a->write_csr(ioaddr, 80, a->read_csr(ioaddr, 80) | 0x0c00);
+    }
     
     dev = init_etherdev(dev, 0);
 
@@ -611,6 +624,9 @@
     lp = (struct pcnet32_private *)(((unsigned long)priv+15) & ~15);
       
     memset(lp, 0, sizeof(*lp));
+    
+    spin_lock_init(&lp->lock);
+    
     dev->priv = lp;
     lp->name = chipname;
     lp->shared_irq = shared;
@@ -933,9 +949,7 @@
 	return 1;
     }
 
-    save_flags (flags);
-    cli ();
-
+    spin_lock_irqsave(&lp->lock, flags);
     /* Fill in a Tx ring entry */
 
     /* Mask to ring buffer boundary. */
@@ -964,7 +978,7 @@
 	clear_bit (0, (void *)&dev->tbusy);
     else
 	lp->tx_full = 1;
-    restore_flags(flags);
+    spin_unlock_irqrestore(&lp->lock, flags);
     return 0;
 }
 
@@ -986,6 +1000,9 @@
 
     ioaddr = dev->base_addr;
     lp = (struct pcnet32_private *)dev->priv;
+    
+    spin_lock(&lp->lock);
+    
     if (dev->interrupt)
 	printk("%s: Re-entering the interrupt handler.\n", dev->name);
 
@@ -1094,12 +1111,14 @@
     /* Clear any other interrupt, and set interrupt enable. */
     lp->a.write_csr (ioaddr, 0, 0x7940);
     lp->a.write_rap(ioaddr,rap);
-
+    
     if (pcnet32_debug > 4)
 	printk("%s: exiting interrupt, csr0=%#4.4x.\n",
 	       dev->name, lp->a.read_csr (ioaddr, 0));
 
     dev->interrupt = 0;
+    
+    spin_unlock(&lp->lock);
     return;
 }
 
@@ -1248,12 +1267,11 @@
     u16 saved_addr;
     unsigned long flags;
 
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&lp->lock, flags);
     saved_addr = lp->a.read_rap(ioaddr);
     lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112);
     lp->a.write_rap(ioaddr, saved_addr);
-    restore_flags(flags);
+    spin_unlock_irqrestore(&lp->lock, flags);
 
     return &lp->stats;
 }
diff -u --recursive --new-file v2.3.18/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.3.18/linux/drivers/net/ppp.c	Wed Aug 18 11:36:42 1999
+++ linux/drivers/net/ppp.c	Wed Dec 31 16:00:00 1969
@@ -1,3153 +0,0 @@
-/*  PPP for Linux
- *
- *  Michael Callahan <callahan@maths.ox.ac.uk>
- *  Al Longyear <longyear@netcom.com>
- *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
- *
- *  ==FILEVERSION 990510==
- *
- *  NOTE TO MAINTAINERS:
- *     If you modify this file at all, please set the number above to the
- *     date of the modification as YYMMDD (year month day).
- *     ppp.c is shipped with a PPP distribution as well as with the kernel;
- *     if everyone increases the FILEVERSION number above, then scripts
- *     can do the right thing when deciding whether to install a new ppp.c
- *     file.  Don't change the format of that line otherwise, so the
- *     installation script can recognize it.
- */
-
-/*
-   Sources:
-
-   slip.c
-
-   RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of
-   Multi-protocol Datagrams over Point-to-Point Links
-
-   RFC1332: IPCP
-
-   ppp-2.0
-
-   Flags for this module (any combination is acceptable for testing.):
-
-   OPTIMIZE_FLAG_TIME - Number of jiffies to force sending of leading flag
-			character. This is normally set to ((HZ * 3) / 2).
-			This is 1.5 seconds. If zero then the leading
-			flag is always sent.
-
-   CHECK_CHARACTERS   - Enable the checking on all received characters for
-			8 data bits, no parity. This adds a small amount of
-			processing for each received character.
-*/
-
-#define OPTIMIZE_FLAG_TIME	((HZ * 3)/2)
-#define CHECK_CHARACTERS	1
-
-#define PPP_MAX_RCV_QLEN	32	/* max # frames we queue up for pppd */
-
-/* $Id: ppp.c,v 1.24 1999/03/31 06:07:57 paulus Exp $ */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/poll.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h>	/* used in new tty drivers */
-#include <linux/signal.h>	/* used in new tty drivers */
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/uaccess.h>
-
-#include <linux/if.h>
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/if_arp.h>
-#include <net/slhc_vj.h>
-
-#define fcstab	ppp_crc16_table		/* Name of the table in the kernel */
-#include <linux/ppp_defs.h>
-
-#include <linux/socket.h>
-#include <linux/if_ppp.h>
-#include <linux/if_pppvar.h>
-#include <linux/ppp-comp.h>
-
-#ifdef CONFIG_KMOD
-#include <linux/kmod.h>
-#endif
-
-/*
- * Local functions
- */
-
-#ifdef CONFIG_MODULES
-static int ppp_register_compressor (struct compressor *cp);
-static void ppp_unregister_compressor (struct compressor *cp);
-#endif
-
-static void ppp_async_init(struct ppp *ppp);
-static void ppp_async_release(struct ppp *ppp);
-static int ppp_tty_sync_push(struct ppp *ppp);
-static int ppp_tty_push(struct ppp *ppp);
-static int ppp_async_encode(struct ppp *ppp);
-static int ppp_async_send(struct ppp *, struct sk_buff *);
-static int ppp_sync_send(struct ppp *, struct sk_buff *);
-static void ppp_tty_flush_output(struct ppp *);
-
-static int ppp_ioctl(struct ppp *, unsigned int, unsigned long);
-static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp);
-static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd);
-static void ppp_ccp_closed(struct ppp *ppp);
-static int ppp_receive_frame(struct ppp *, struct sk_buff *);
-static void ppp_receive_error(struct ppp *ppp);
-static void ppp_output_wakeup(struct ppp *ppp);
-static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb);
-static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
-static void ppp_send_frames(struct ppp *ppp);
-static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb);
-
-static struct ppp *ppp_find (int pid_value);
-static struct ppp *ppp_alloc (void);
-static void ppp_generic_init(struct ppp *ppp);
-static void ppp_release(struct ppp *ppp);
-static void ppp_print_buffer (const char *, const __u8 *, int);
-static struct compressor *find_compressor (int type);
-
-#ifndef OPTIMIZE_FLAG_TIME
-#define OPTIMIZE_FLAG_TIME	0
-#endif
-
-/*
- * Parameters which may be changed via insmod.
- */
-
-static int  flag_time = OPTIMIZE_FLAG_TIME;
-MODULE_PARM(flag_time, "i");
-
-#define CHECK_PPP_MAGIC(ppp)	do { \
-	if (ppp->magic != PPP_MAGIC) { \
-		printk(ppp_magic_warn, ppp, __FILE__, __LINE__); \
-	} \
-} while (0)
-#define CHECK_PPP(a)	do { \
-	CHECK_PPP_MAGIC(ppp); \
-	if (!ppp->inuse) { \
-		printk(ppp_warning, __LINE__); \
-		return a; \
-	} \
-} while (0)
-#define CHECK_PPP_VOID() do { \
-	CHECK_PPP_MAGIC(ppp); \
-	if (!ppp->inuse) { \
-		printk(ppp_warning, __LINE__); \
-		return; \
-	} \
-} while (0)
-
-#define tty2ppp(tty)	((struct ppp *) ((tty)->disc_data))
-#define dev2ppp(dev)	((struct ppp *) ((dev)->priv))
-#define ppp2tty(ppp)	((ppp)->tty)
-#define ppp2dev(ppp)	(&(ppp)->dev)
-
-static struct ppp *ppp_list = NULL;
-static struct ppp *ppp_last = NULL;
-
-/* Define these strings only once for all macro invocations */
-static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
-static char ppp_magic_warn[] = KERN_WARNING "bad magic for ppp %p at %s:%d\n";
-
-static char szVersion[]		= PPP_VERSION;
-
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
-
-/*************************************************************
- * LINE DISCIPLINE SUPPORT
- *    The following code implements the PPP line discipline
- *    and supports using PPP on an async serial line.
- *************************************************************/
-
-#define in_xmap(ppp,c)	(ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
-#define in_rmap(ppp,c)	((((unsigned int) (__u8) (c)) < 0x20) && \
-			ppp->recv_async_map & (1 << (c)))
-
-/*
- * TTY callbacks
- */
-
-static ssize_t ppp_tty_read(struct tty_struct *, struct file *, __u8 *,
-			    size_t);
-static ssize_t ppp_tty_write(struct tty_struct *, struct file *, const __u8 *,
-			     size_t);
-static int ppp_tty_ioctl(struct tty_struct *, struct file *, unsigned int,
-			 unsigned long);
-static unsigned int ppp_tty_poll(struct tty_struct *tty, struct file *filp,
-				 poll_table * wait);
-static int ppp_tty_open (struct tty_struct *);
-static void ppp_tty_close (struct tty_struct *);
-static int ppp_tty_room (struct tty_struct *tty);
-static void ppp_tty_receive (struct tty_struct *tty, const __u8 * cp,
-			     char *fp, int count);
-static void ppp_tty_wakeup (struct tty_struct *tty);
-
-__u16 ppp_crc16_table[256] =
-{
-	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
-	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
-	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
-	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
-	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
-	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
-	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
-	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
-	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
-	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
-	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
-	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
-	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
-	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
-	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
-	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
-	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
-	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
-	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
-	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
-	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
-	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
-	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
-	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
-	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
-	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
-	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
-	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
-	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
-	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
-	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
-	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
-EXPORT_SYMBOL(ppp_crc16_table);
-
-#ifdef CHECK_CHARACTERS
-static __u32 paritytab[8] =
-{
-	0x96696996, 0x69969669, 0x69969669, 0x96696996,
-	0x69969669, 0x96696996, 0x96696996, 0x69969669
-};
-#endif
-
-/*
- * This procedure is called at initialization time to register
- * the PPP line discipline.
- */
-static int
-ppp_first_time(void)
-{
-	static struct tty_ldisc	ppp_ldisc;
-	int    status;
-
-	printk(KERN_INFO
-	       "PPP: version %s (demand dialling)"
-	       "\n", szVersion);
-
-#ifndef MODULE /* slhc module logic has its own copyright announcement */
-	printk(KERN_INFO
-	       "TCP compression code copyright 1989 Regents of the "
-	       "University of California\n");
-#endif
-
-	/*
-	 * Register the tty discipline
-	 */
-	(void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
-	ppp_ldisc.magic		= TTY_LDISC_MAGIC;
-	ppp_ldisc.name          = "ppp";
-	ppp_ldisc.open		= ppp_tty_open;
-	ppp_ldisc.close		= ppp_tty_close;
-	ppp_ldisc.read		= ppp_tty_read;
-	ppp_ldisc.write		= ppp_tty_write;
-	ppp_ldisc.ioctl		= ppp_tty_ioctl;
-	ppp_ldisc.poll		= ppp_tty_poll;
-	ppp_ldisc.receive_room	= ppp_tty_room;
-	ppp_ldisc.receive_buf	= ppp_tty_receive;
-	ppp_ldisc.write_wakeup	= ppp_tty_wakeup;
-
-	status = tty_register_ldisc (N_PPP, &ppp_ldisc);
-	if (status == 0)
-		printk(KERN_INFO "PPP line discipline registered.\n");
-	else
-		printk(KERN_ERR "error registering line discipline: %d\n",
-		       status);
-	return status;
-}
-
-
-#ifndef MODULE
-/*
- * Called at boot time if the PPP driver is compiled into the kernel.
- */
-int
-ppp_init(struct net_device *dev)
-{
-	static int first_time = 1;
-	int    answer = 0;
-
-	if (first_time) {
-		first_time = 0;
-		answer	   = ppp_first_time();
-	}
-	if (answer == 0)
-		answer = -ENODEV;
-	return answer;
-}
-#endif
-
-/*
- * Initialize the async-specific parts of the ppp structure.
- */
-static void
-ppp_async_init(struct ppp *ppp)
-{
-	ppp->escape = 0;
-	ppp->toss   = 0xE0;
-	ppp->tty_pushing = 0;
-
-	memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
-	ppp->xmit_async_map[0] = 0xffffffff;
-	ppp->xmit_async_map[3] = 0x60000000;
-	ppp->recv_async_map    = 0xffffffff;
-
-	ppp->tpkt = NULL;
-	ppp->tfcs = PPP_INITFCS;
-	ppp->optr = ppp->obuf;
-	ppp->olim = ppp->obuf;
-
-	ppp->rpkt = NULL;
-	ppp->rfcs = PPP_INITFCS;
-
-	ppp->tty  = NULL;
-	ppp->backup_tty = NULL;
-
-	ppp->bytes_sent = 0;
-	ppp->bytes_rcvd = 0;
-}
-
-/*
- * Clean up the async-specific parts of the ppp structure.
- */
-static void
-ppp_async_release(struct ppp *ppp)
-{
-	struct sk_buff *skb;
-
-	if ((skb = ppp->rpkt) != NULL)
-		kfree_skb(skb);
-	ppp->rpkt = NULL;
-	if ((skb = ppp->tpkt) != NULL)
-		kfree_skb(skb);
-	ppp->tpkt = NULL;
-}
-
-/*
- * TTY callback.
- *
- * Called when the tty discipline is switched to PPP.
- */
-
-static int
-ppp_tty_open (struct tty_struct *tty)
-{
-	struct ppp *ppp;
-
-	/*
-	 * Allocate a ppp structure to use.
-	 */
-	tty->disc_data = NULL;
-	ppp = ppp_find(current->pid);
-	if (ppp != NULL) {
-		/*
-		 * If we are taking over a ppp unit which is currently
-		 * connected to a loopback pty, there's not much to do.
-		 */
-		CHECK_PPP(-EINVAL);
-
-	} else {
-		ppp = ppp_alloc();
-		if (ppp == NULL) {
-			printk(KERN_ERR "ppp_alloc failed\n");
-			return -ENFILE;
-		}
-
-		/*
-		 * Initialize the control block
-		 */
-		ppp_generic_init(ppp);
-		ppp_async_init(ppp);
-
-		MOD_INC_USE_COUNT;
-	}
-
-	tty->disc_data = ppp;
-	ppp->tty       = tty;
-
-	/*
-	 * Flush any pending characters in the driver
-	 */
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer (tty);
-
-	return ppp->line;
-}
-
-/*
- * TTY callback.
- *
- * Called when the line discipline is changed to something
- * else, the tty is closed, or the tty detects a hangup.
- */
-
-static void
-ppp_tty_close (struct tty_struct *tty)
-{
-	struct ppp *ppp = tty2ppp(tty);
-
-	if (ppp == NULL)
-		return;
-	tty->disc_data = NULL;
-	if (ppp->magic != PPP_MAGIC) {
-		printk(KERN_WARNING "ppp_tty_close: bogus\n");
-		return;
-	}
-	if (!ppp->inuse) {
-		printk(KERN_WARNING "ppp_tty_close: not inuse\n");
-		ppp->tty = ppp->backup_tty = 0;
-		return;
-	}
-	if (tty == ppp->backup_tty)
-		ppp->backup_tty = 0;
-	if (tty != ppp->tty)
-		return;
-	if (ppp->backup_tty) {
-		ppp->tty = ppp->backup_tty;
-		if (ppp_tty_push(ppp))
-			ppp_output_wakeup(ppp);
-		wake_up_interruptible(&ppp->read_wait);
-	} else {
-		ppp->tty = 0;
-		ppp->sc_xfer = 0;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_INFO "ppp: channel %s closing.\n",
-			       ppp2dev(ppp)->name);
-
-		ppp_async_release(ppp);
-		ppp_release(ppp);
-		MOD_DEC_USE_COUNT;
-	}
-}
-
-/*
- * Read a PPP frame from the rcv_q list,
- * waiting if necessary
- */
-static ssize_t
-ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf,
-	     size_t nr)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	struct sk_buff *skb;
-	ssize_t len, err;
-
-	/*
-	 * Validate the pointers
-	 */
-	if (!ppp)
-		return -EIO;
-	CHECK_PPP(-ENXIO);
-
-	/*
-	 * Before we attempt to write the frame to the user, ensure that the
-	 * user has access to the pages for the total buffer length.
-	 */
-	err = verify_area(VERIFY_WRITE, buf, nr);
-	if (err != 0)
-		return (err);
-
-	/*
-	 * Wait for a frame to arrive if necessary.
-	 * We increment the module use count so that the module
-	 * can't go away while we're sleeping.
-	 */
-	MOD_INC_USE_COUNT;
-	skb = NULL;
-	for (;;) {
-		ppp = tty2ppp(tty);
-		err = 0;
-		if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
-		    || tty != ppp->tty)
-			break;
-
-		skb = skb_dequeue(&ppp->rcv_q);
-		if (skb != 0)
-			break;
-
-		/*
-		 * If no frame is available, return -EAGAIN or wait.
-		 */
-		err = -EAGAIN;
-		if (file->f_flags & O_NONBLOCK)
-			break;
-
-		interruptible_sleep_on(&ppp->read_wait);
-		err = -EINTR;
-		if (signal_pending(current))
-			break;
-	}
-	MOD_DEC_USE_COUNT;
-	if (skb == 0)
-		return err;
-
-	/*
-	 * Ensure that the frame will fit within the caller's buffer.
-	 * If not, just discard the frame.
-	 */
-	len = skb->len;
-	if (len > nr) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG
-			       "ppp: read of %lu bytes too small for %ld "
-			       "frame\n", (unsigned long) nr, (long) len);
-		ppp->stats.ppp_ierrors++;
-		err = -EOVERFLOW;
-		goto out;
-	}
-
-	/*
-	 * Copy the received data from the buffer to the caller's area.
-	 */
-	err = len;
-	if (copy_to_user(buf, skb->data, len))
-		err = -EFAULT;
-
-out:
-	kfree_skb(skb);
-	return err;
-}
-
-/*
- * Writing to a tty in ppp line discipline sends a PPP frame.
- * Used by pppd to send control packets (LCP, etc.).
- */
-static ssize_t
-ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data,
-	      size_t count)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	__u8 *new_data;
-	struct sk_buff *skb;
-
-	/*
-	 * Verify the pointers.
-	 */
-	if (!ppp)
-		return -EIO;
-
-	if (ppp->magic != PPP_MAGIC)
-		return -EIO;
-
-	CHECK_PPP(-ENXIO);
-
-	/*
-	 * Ensure that the caller does not wish to send too much.
-	 */
-	if (count > PPP_MTU + PPP_HDRLEN) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_WARNING
-			       "ppp_tty_write: truncating user packet "
-			       "from %lu to mtu %d\n", (unsigned long) count,
-			       PPP_MTU + PPP_HDRLEN);
-		count = PPP_MTU + PPP_HDRLEN;
-	}
-
-	/*
-	 * Allocate a buffer for the data and fetch it from the user space.
-	 */
-	skb = alloc_skb(count, GFP_KERNEL);
-	if (skb == NULL) {
-		printk(KERN_ERR "ppp_tty_write: no memory\n");
-		return 0;
-	}
-	new_data = skb_put(skb, count);
-
-	/*
-	 * Retrieve the user's buffer
-	 */
-	if (copy_from_user(new_data, data, count)) {
-		kfree_skb(skb);
-		return -EFAULT;
-	}
-
-	/*
-	 * Send the frame
-	 */
-	ppp_send_ctrl(ppp, skb);
-
-	return (ssize_t) count;
-}
-
-/*
- * Process the IOCTL call for the tty device.
- * Only the ioctls that relate to using ppp on async serial lines
- * are processed here; the rest are handled by ppp_ioctl.
- */
-static int
-ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
-               unsigned int param2, unsigned long param3)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	register int temp_i = 0;
-	int error = -EFAULT;
-
-	/*
-	 * Verify the status of the PPP device.
-	 */
-	if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
-		return -ENXIO;
-
-	/*
-	 * The user must have an euid of root to do these requests.
-	 */
-	if (!capable(CAP_NET_ADMIN))
-		return -EPERM;
-
-	switch (param2) {
-	case PPPIOCGASYNCMAP:
-		/*
-		 * Retrieve the transmit async map
-		 */
-		if (put_user(ppp->xmit_async_map[0], (int *) param3))
-			break;
-		error = 0;
-		break;
-
-	case PPPIOCSASYNCMAP:
-		/*
-		 * Set the transmit async map
-		 */
-		if (get_user(temp_i, (int *) param3))
-			break;
-		ppp->xmit_async_map[0] = temp_i;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_INFO
-			       "ppp_tty_ioctl: set xmit asyncmap %x\n",
-			       ppp->xmit_async_map[0]);
-		error = 0;
-		break;
-
-	case PPPIOCSRASYNCMAP:
-		/*
-		 * Set the receive async map
-		 */
-		if (get_user(temp_i, (int *) param3))
-			break;
-		ppp->recv_async_map = temp_i;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_INFO
-			       "ppp_tty_ioctl: set rcv asyncmap %x\n",
-			       ppp->recv_async_map);
-		error = 0;
-		break;
-
-	case PPPIOCGXASYNCMAP:
-		/*
-		 * Get the map of characters to be escaped on transmission.
-		 */
-		if (copy_to_user((void *) param3, ppp->xmit_async_map,
-				 sizeof (ppp->xmit_async_map)))
-			break;
-		error = 0;
-		break;
-
-	case PPPIOCSXASYNCMAP:
-		/*
-		 * Set the map of characters to be escaped on transmission.
-		 */
-		{
-			__u32 temp_tbl[8];
-
-			if (copy_from_user(temp_tbl, (void *) param3,
-					   sizeof (temp_tbl)))
-				break;
-
-			temp_tbl[1]  =	0x00000000;
-			temp_tbl[2] &= ~0x40000000;
-			temp_tbl[3] |=	0x60000000;
-
-			memcpy(ppp->xmit_async_map, temp_tbl,
-			       sizeof (ppp->xmit_async_map));
-
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_INFO
-				       "ppp_tty_ioctl: set xasyncmap\n");
-			error = 0;
-		}
-		break;
-
-	case PPPIOCXFERUNIT:
-		/*
-		 * Set up this PPP unit to be used next time this
-		 * process sets a tty to PPP line discipline.
-		 */
-		ppp->backup_tty = tty;
-		ppp->sc_xfer = current->pid;
-		error = 0;
-		break;
-
-	case TCGETS:
-	case TCGETA:
-		/*
-		 * Allow users to read, but not set, the serial port parameters
-		 */
-		error = n_tty_ioctl (tty, file, param2, param3);
-		break;
-
-	case TCFLSH:
-		/*
-		 * Flush our buffers, then call the generic code to
-		 * flush the serial port's buffer.
-		 */
-		if (param3 == TCIFLUSH || param3 == TCIOFLUSH) {
-			struct sk_buff *skb;
-			while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL)
-				kfree_skb(skb);
-		}
-		if (param3 == TCIOFLUSH || param3 == TCOFLUSH)
-			ppp_tty_flush_output(ppp);
-		error = n_tty_ioctl (tty, file, param2, param3);
-		break;
-
-	case FIONREAD:
-		/*
-		 * Returns how many bytes are available for a read().
-		 */
-		{
-			unsigned long flags;
-			struct sk_buff *skb;
-			int count = 0;
-
-			save_flags(flags);
-			cli();
-			skb = skb_peek(&ppp->rcv_q);
-			if (skb != 0)
-				count = skb->len;
-			restore_flags(flags);
-			if (put_user(count, (int *) param3))
-				break;
-			error = 0;
-		}
-		break;
-
-	default:
-		/*
-		 *  All other ioctl() events will come here.
-		 */
-		error = ppp_ioctl(ppp, param2, param3);
-		break;
-	}
-	return error;
-}
-
-/*
- * TTY callback.
- *
- * Process the poll() statement for the PPP device.
- */
-
-static unsigned int
-ppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait)
-{
-	struct ppp *ppp = tty2ppp(tty);
-	unsigned int mask = 0;
-
-	if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
-		CHECK_PPP(0);
-
-		poll_wait(filp, &ppp->read_wait, wait);
-
-		if (skb_peek(&ppp->rcv_q) != NULL)
-			mask |= POLLIN | POLLRDNORM;
-		if (tty->flags & (1 << TTY_OTHER_CLOSED)
-		    || tty_hung_up_p(filp))
-			mask |= POLLHUP;
-		mask |= POLLOUT | POLLWRNORM;
-	}
-	return mask;
-}
-
-/*
- * This function is called by the tty driver when the transmit buffer has
- * additional space. It is used by the ppp code to continue to transmit
- * the current buffer should the buffer have been partially sent.
- */
-static void
-ppp_tty_wakeup (struct tty_struct *tty)
-{
-	struct ppp *ppp = tty2ppp (tty);
-
-	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-	if (!ppp)
-		return;
-	CHECK_PPP_VOID();
-	if (tty != ppp->tty)
-		return;
-
-	if (ppp_tty_push(ppp))
-		ppp_output_wakeup(ppp);
-}
-
-/*
- * Send a packet to the peer over a synchronous tty line.
- * All encoding and FCS are handled by hardware.
- * Addr/Ctrl and Protocol field compression implemented.
- * Returns -1 iff the packet could not be accepted at present,
- * 0 if the packet was accepted but we can't accept another yet, or
- * 1 if we can accept another packet immediately.
- * If this procedure returns 0, ppp_output_wakeup will be called
- * exactly once.
- */
-static int
-ppp_sync_send(struct ppp *ppp, struct sk_buff *skb)
-{
-	unsigned char *data;
-	int islcp;
-	
-	CHECK_PPP(0);
-
-	if (ppp->tpkt != NULL)
-		return -1;
-	ppp->tpkt = skb;
-
-	data = ppp->tpkt->data;
-	
-	/*
-	 * LCP packets with code values between 1 (configure-reqest)
-	 * and 7 (code-reject) must be sent as though no options
-	 * had been negotiated.
-	 */
-	islcp = PPP_PROTOCOL(data) == PPP_LCP
-		&& 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7;
-
-	/* only reset idle time for data packets */
-	if (PPP_PROTOCOL(data) < 0x8000)
-		ppp->last_xmit = jiffies;
-	++ppp->stats.ppp_opackets;
-	ppp->stats.ppp_ooctects += ppp->tpkt->len;
-
-	if ( !(data[2]) && (ppp->flags & SC_COMP_PROT) ) {
-		/* compress protocol field */
-		data[2] = data[1];
-		data[1] = data[0];
-		skb_pull(ppp->tpkt,1);
-		data = ppp->tpkt->data;
-	}
-	
-	/*
-	 * Do address/control compression
-	 */
-	if ((ppp->flags & SC_COMP_AC) && !islcp
-	    && PPP_ADDRESS(data) == PPP_ALLSTATIONS
-	    && PPP_CONTROL(data) == PPP_UI) {
-		/* strip addr and control field */
-		skb_pull(ppp->tpkt,2);
-	}
-
-	return ppp_tty_sync_push(ppp);
-}
-
-/*
- * Push a synchronous frame out to the tty.
- * Returns 1 if frame accepted (or discarded), 0 otherwise.
- */
-static int
-ppp_tty_sync_push(struct ppp *ppp)
-{
-	int sent;
-	struct tty_struct *tty = ppp2tty(ppp);
-	unsigned long flags;
-		
-	CHECK_PPP(0);
-
-	if (ppp->tpkt == NULL)
-		return 0;
-		
-	/* prevent reentrancy with tty_pushing flag */		
-	save_flags(flags);
-	cli();
-	if (ppp->tty_pushing) {
-		/* record wakeup attempt so we don't lose */
-		/* a wakeup call while doing push processing */
-		ppp->woke_up=1;
-		restore_flags(flags);
-		return 0;
-	}
-	ppp->tty_pushing = 1;
-	restore_flags(flags);
-	
-	if (tty == NULL || tty->disc_data != (void *) ppp)
-		goto flush;
-		
-	for(;;){
-		ppp->woke_up=0;
-		
-		/* Note: Sync driver accepts complete frame or nothing */
-		tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-		sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len);
-		if (sent < 0) {
-			/* write error (possible loss of CD) */
-			/* record error and discard current packet */
-			ppp->stats.ppp_oerrors++;
-			break;
-		}
-		ppp->stats.ppp_obytes += sent;
-		if (sent < ppp->tpkt->len) {
-			/* driver unable to accept frame just yet */
-			save_flags(flags);
-			cli();
-			if (ppp->woke_up) {
-				/* wake up called while processing */
-				/* try to send the frame again */
-				restore_flags(flags);
-				continue;
-			}
-			/* wait for wakeup callback to try send again */
-			ppp->tty_pushing = 0;
-			restore_flags(flags);
-			return 0;
-		}
-		break;
-	}
-flush:	
-	/* done with current packet (sent or discarded) */
-	kfree_skb(ppp->tpkt);
-	ppp->tpkt = 0;
-	ppp->tty_pushing = 0;
-	return 1;
-}
-
-/*
- * Send a packet to the peer over an async tty line.
- * Returns -1 iff the packet could not be accepted at present,
- * 0 if the packet was accepted but we can't accept another yet, or
- * 1 if we can accept another packet immediately.
- * If this procedure returns 0, ppp_output_wakeup will be called
- * exactly once.
- */
-static int
-ppp_async_send(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-
-	ppp_tty_push(ppp);
-
-	if (ppp->tpkt != NULL)
-		return -1;
-	ppp->tpkt = skb;
-	ppp->tpkt_pos = 0;
-
-	return ppp_tty_push(ppp);
-}
-
-/*
- * Push as much data as possible out to the tty.
- * Returns 1 if we finished encoding the current frame, 0 otherwise.
- */
-static int
-ppp_tty_push(struct ppp *ppp)
-{
-	int avail, sent, done = 0;
-	struct tty_struct *tty = ppp2tty(ppp);
-	
-	if (ppp->flags & SC_SYNC) 
-		return ppp_tty_sync_push(ppp);
-
-	CHECK_PPP(0);
-	if (ppp->tty_pushing) {
-		ppp->woke_up = 1;
-		return 0;
-	}
-	if (tty == NULL || tty->disc_data != (void *) ppp)
-		goto flush;
-	while (ppp->optr < ppp->olim || ppp->tpkt != 0) {
-		ppp->tty_pushing = 1;
-		mb();
-		ppp->woke_up = 0;
-		avail = ppp->olim - ppp->optr;
-		if (avail > 0) {
-			tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-			sent = tty->driver.write(tty, 0, ppp->optr, avail);
-			if (sent < 0)
-				goto flush;	/* error, e.g. loss of CD */
-			ppp->stats.ppp_obytes += sent;
-			ppp->optr += sent;
-			if (sent < avail) {
-				wmb();
-				ppp->tty_pushing = 0;
-				mb();
-				if (ppp->woke_up)
-					continue;
-				return done;
-			}
-		}
-		if (ppp->tpkt != 0)
-			done = ppp_async_encode(ppp);
-		wmb();
-		ppp->tty_pushing = 0;
-	}
-	return done;
-
-flush:
-	ppp->tty_pushing = 1;
-	mb();
-	ppp->stats.ppp_oerrors++;
-	if (ppp->tpkt != 0) {
-		kfree_skb(ppp->tpkt);
-		ppp->tpkt = 0;
-		done = 1;
-	}
-	ppp->optr = ppp->olim;
-	wmb();
-	ppp->tty_pushing = 0;
-	return done;
-}
-
-/*
- * Procedure to encode the data for async serial transmission.
- * Does octet stuffing (escaping) and address/control
- * and protocol compression.
- * Assumes ppp->opkt != 0 on entry.
- * Returns 1 if we finished the current frame, 0 otherwise.
- */
-static int
-ppp_async_encode(struct ppp *ppp)
-{
-	int fcs, i, count, c;
-	unsigned char *buf, *buflim;
-	unsigned char *data;
-	int islcp;
-
-	CHECK_PPP(0);
-
-	buf = ppp->obuf;
-	ppp->olim = buf;
-	ppp->optr = buf;
-	i = ppp->tpkt_pos;
-	data = ppp->tpkt->data;
-	count = ppp->tpkt->len;
-	fcs = ppp->tfcs;
-
-	/*
-	 * LCP packets with code values between 1 (configure-reqest)
-	 * and 7 (code-reject) must be sent as though no options
-	 * had been negotiated.
-	 */
-	islcp = PPP_PROTOCOL(data) == PPP_LCP
-		&& 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7;
-
-	if (i == 0) {
-		/*
-		 * Start of a new packet - insert the leading FLAG
-		 * character if necessary.
-		 */
-		if (islcp || flag_time == 0
-		    || jiffies - ppp->last_xmit >= flag_time)
-			*buf++ = PPP_FLAG;
-		/* only reset idle time for data packets */
-		if (PPP_PROTOCOL(data) < 0x8000)
-			ppp->last_xmit = jiffies;
-		fcs = PPP_INITFCS;
-		++ppp->stats.ppp_opackets;
-		ppp->stats.ppp_ooctects += count;
-
-		/*
-		 * Do address/control compression
-		 */
-		if ((ppp->flags & SC_COMP_AC) != 0 && !islcp
-		    && PPP_ADDRESS(data) == PPP_ALLSTATIONS
-		    && PPP_CONTROL(data) == PPP_UI)
-			i += 2;
-	}
-
-	/*
-	 * Once we put in the last byte, we need to put in the FCS
-	 * and closing flag, so make sure there is at least 7 bytes
-	 * of free space in the output buffer.
-	 */
-	buflim = buf + OBUFSIZE - 6;
-	while (i < count && buf < buflim) {
-		c = data[i++];
-		if (i == 3 && c == 0 && (ppp->flags & SC_COMP_PROT))
-			continue;	/* compress protocol field */
-		fcs = PPP_FCS(fcs, c);
-		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
-			*buf++ = PPP_ESCAPE;
-			c ^= 0x20;
-		}
-		*buf++ = c;
-	}
-
-	if (i == count) {
-		/*
-		 * We have finished the packet.  Add the FCS and flag.
-		 */
-		fcs = ~fcs;
-		c = fcs & 0xff;
-		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
-			*buf++ = PPP_ESCAPE;
-			c ^= 0x20;
-		}
-		*buf++ = c;
-		c = (fcs >> 8) & 0xff;
-		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
-			*buf++ = PPP_ESCAPE;
-			c ^= 0x20;
-		}
-		*buf++ = c;
-		*buf++ = PPP_FLAG;
-		ppp->olim = buf;
-
-		kfree_skb(ppp->tpkt);
-		ppp->tpkt = 0;
-		return 1;
-	}
-
-	/*
-	 * Remember where we are up to in this packet.
-	 */
-	ppp->olim = buf;
-	ppp->tpkt_pos = i;
-	ppp->tfcs = fcs;
-	return 0;
-}
-
-/*
- * Flush output from our internal buffers.
- * Called for the TCFLSH ioctl.
- */
-static void
-ppp_tty_flush_output(struct ppp *ppp)
-{
-	struct sk_buff *skb;
-	int done = 0;
-
-	while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
-		kfree_skb(skb);
-	ppp->tty_pushing = 1;
-	mb();
-	ppp->optr = ppp->olim;
-	if (ppp->tpkt != NULL) {
-		kfree_skb(ppp->tpkt);
-		ppp->tpkt = 0;
-		done = 1;
-	}
-	wmb();
-	ppp->tty_pushing = 0;
-	if (done)
-		ppp_output_wakeup(ppp);
-}
-
-/*
- * Callback function from tty driver. Return the amount of space left
- * in the receiver's buffer to decide if remote transmitter is to be
- * throttled.
- */
-static int
-ppp_tty_room (struct tty_struct *tty)
-{
-	return 65536;	    /* We can handle an infinite amount of data. :-) */
-}
-
-/*
- * Callback function when data is available at the tty driver.
- */
-static void
-ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
-		 char *flags, int count)
-{
-	register struct ppp *ppp = tty2ppp (tty);
-	struct sk_buff *skb;
-	int chr, flg;
-	unsigned char *p;
-
-	if (ppp != 0)
-		CHECK_PPP_VOID();
-	/*
-	 * This can happen if stuff comes in on the backup tty.
-	 */
-	if (ppp == 0 || tty != ppp->tty)
-		return;
-	/*
-	 * Verify the table pointer and ensure that the line is
-	 * still in PPP discipline.
-	 */
-	if (ppp->magic != PPP_MAGIC) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG
-			       "PPP: tty_receive called but couldn't find "
-			       "PPP struct.\n");
-		return;
-	}
-	/*
-	 * Print the buffer if desired
-	 */
-	if (ppp->flags & SC_LOG_RAWIN)
-		ppp_print_buffer ("receive buffer", data, count);
-
-	ppp->stats.ppp_ibytes += count;
-	skb = ppp->rpkt;
-	
-	if ( ppp->flags & SC_SYNC ) {
-		/* synchronous mode */
-		
-		if (ppp->toss==0xE0) {
-			/* this is the 1st frame, reset vj comp */
-			ppp_receive_error(ppp);
-			ppp->toss = 0;
-		}
-		
-		/*
-		 * Allocate an skbuff for frame.
-		 * The 128 is room for VJ header expansion.
-		 */
-		
-		if (skb == NULL)
-			skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
-			
-		if (skb == NULL) {
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "couldn't "
-				       "alloc skb for recv\n");
-		} else {
-			/*
-			 * Decompress A/C and protocol compression here.
-			 */
-			p = skb_put(skb, 2);
-			p[0] = PPP_ALLSTATIONS;
-			p[1] = PPP_UI;
-			if (*data == PPP_ALLSTATIONS) {
-				data += 2;
-				count -= 2;
-			}
-			if ((*data & 1) != 0) {
-				p = skb_put(skb, 1);
-				p[0] = 0;
-			}
-
-			/* copy frame to socket buffer */
-			p = skb_put(skb, count);
-			memcpy(p,data,count);
-			
-			/*
-			 * Check if we've overflowed the MRU
-			 */
-			if (skb->len >= ppp->mru + PPP_HDRLEN + 2
-			    || skb_tailroom(skb) <= 0) {
-				++ppp->estats.rx_length_errors;
-				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "rcv frame too long: "
-					       "len=%d mru=%d hroom=%d troom=%d\n",
-					       skb->len, ppp->mru, skb_headroom(skb),
-					       skb_tailroom(skb));
-			} else {
-				if (!ppp_receive_frame(ppp, skb)) {
-					kfree_skb(skb);
-					ppp_receive_error(ppp);
-				}
-			}
-		
-			/* Reset for the next frame */
-			skb = NULL;
-		}
-		ppp->rpkt = skb;
-		return;
-	}
-	
-	while (count-- > 0) {
-		/*
-		 * Collect the character and error condition for the character.
-		 * Set the toss flag for the first character error.
-		 */
-		chr = *data++;
-		if (flags) {
-			flg = *flags++;
-			if (flg) {
-				if (ppp->toss == 0)
-					ppp->toss = flg;
-				switch (flg) {
-				case TTY_OVERRUN:
-					++ppp->estats.rx_fifo_errors;
-					break;
-				case TTY_FRAME:
-				case TTY_BREAK:
-					++ppp->estats.rx_frame_errors;
-					break;
-				}
-				continue;
-			}
-		}
-
-		/*
-		 * Set the flags for d7 being 0/1 and parity being
-		 * even/odd so that the normal processing would have
-		 * all flags set at the end of the session.  A
-		 * missing flag bit indicates an error condition.
-		 */
-
-#ifdef CHECK_CHARACTERS
-		if (chr & 0x80)
-			ppp->flags |= SC_RCV_B7_1;
-		else
-			ppp->flags |= SC_RCV_B7_0;
-
-		if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
-			ppp->flags |= SC_RCV_ODDP;
-		else
-			ppp->flags |= SC_RCV_EVNP;
-#endif
-
-		if (chr == PPP_FLAG) {
-			/*
-			 * FLAG. This is the end of the block. If the block
-			 * ends with ESC FLAG, then the block is to be ignored.
-			 */
-			if (ppp->escape)
-				ppp->toss |= 0x80;
-			/*
-			 * Process the frame if it was received correctly.
-			 * If there was an error, let the VJ decompressor know.
-			 * There are 4 cases here:
-			 * skb != NULL, toss != 0: error in frame
-			 * skb != NULL, toss == 0: frame ok
-			 * skb == NULL, toss != 0: very first frame,
-			 *	error on 1st char, or alloc_skb failed
-			 * skb == NULL, toss == 0: empty frame (~~)
-			 */
-			if (ppp->toss || !ppp_receive_frame(ppp, skb)) {
-				if (ppp->toss && (ppp->flags & SC_DEBUG))
-					printk(KERN_DEBUG
-					       "ppp: tossing frame (%x)\n",
-					       ppp->toss);
-				if (skb != NULL)
-					kfree_skb(skb);
-				if (!(ppp->toss == 0xE0 || ppp->toss == 0x80))
-					++ppp->stats.ppp_ierrors;
-				ppp_receive_error(ppp);
-			}
-			/*
-			 * Reset for the next frame.
-			 */
-			skb = NULL;
-			ppp->rfcs = PPP_INITFCS;
-			ppp->escape = 0;
-			ppp->toss = 0;
-			continue;
-		}
-
-		/* If we're tossing, look no further. */
-		if (ppp->toss != 0)
-			continue;
-
-		/* If this is a control char to be ignored, do so */
-		if (in_rmap(ppp, chr))
-			continue;
-
-		/*
-		 * Modify the next character if preceded by escape.
-		 * The escape character (0x7d) could be an escaped
-		 * 0x5d, if it follows an escape :-)
-		 */
-		if (ppp->escape) {
-			chr ^= PPP_TRANS;
-			ppp->escape = 0;
-		} else if (chr == PPP_ESCAPE) {
-			ppp->escape = PPP_TRANS;
-			continue;
-		}
-
-		/*
-		 * Allocate an skbuff on the first character received.
-		 * The 128 is room for VJ header expansion and FCS.
-		 */
-		if (skb == NULL) {
-			skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
-			if (skb == NULL) {
-				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "couldn't "
-					       "alloc skb for recv\n");
-				ppp->toss = 1;
-				continue;
-			}
-		}
-
-		/*
-		 * Decompress A/C and protocol compression here.
-		 */
-		if (skb->len == 0 && chr != PPP_ALLSTATIONS) {
-			p = skb_put(skb, 2);
-			p[0] = PPP_ALLSTATIONS;
-			p[1] = PPP_UI;
-		}
-		if (skb->len == 2 && (chr & 1) != 0) {
-			p = skb_put(skb, 1);
-			p[0] = 0;
-		}
-
-		/*
-		 * Check if we've overflowed the MRU
-		 */
-		if (skb->len >= ppp->mru + PPP_HDRLEN + 2
-		    || skb_tailroom(skb) <= 0) {
-			++ppp->estats.rx_length_errors;
-			ppp->toss = 0xC0;
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "rcv frame too long: "
-				       "len=%d mru=%d hroom=%d troom=%d\n",
-				       skb->len, ppp->mru, skb_headroom(skb),
-				       skb_tailroom(skb));
-			continue;
-		}
-
-		/*
-		 * Store the character and update the FCS.
-		 */
-		p = skb_put(skb, 1);
-		*p = chr;
-		ppp->rfcs = PPP_FCS(ppp->rfcs, chr);
-	}
-	ppp->rpkt = skb;
-}
-
-/*************************************************************
- * PPP NETWORK INTERFACE SUPPORT
- *	The following code implements the PPP network
- *	interface device and handles those parts of
- *	the PPP processing which are independent of the
- *	type of hardware link being used, including
- *	VJ and packet compression.
- *************************************************************/
-
-/*
- * Network device driver callback routines
- */
-
-static int ppp_init_dev(struct net_device *dev);
-static int ppp_dev_open(struct net_device *);
-static int ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-static int ppp_dev_close(struct net_device *);
-static int ppp_dev_xmit(struct sk_buff *, struct net_device *);
-static struct net_device_stats *ppp_dev_stats (struct net_device *);
-
-/*
- * Information for the protocol decoder
- */
-
-typedef int (*pfn_proto)  (struct ppp *, struct sk_buff *);
-
-typedef struct ppp_proto_struct {
-	int		proto;
-	pfn_proto	func;
-} ppp_proto_type;
-
-static int rcv_proto_ip		(struct ppp *, struct sk_buff *);
-static int rcv_proto_ipv6	(struct ppp *, struct sk_buff *);
-static int rcv_proto_ipx	(struct ppp *, struct sk_buff *);
-static int rcv_proto_at		(struct ppp *, struct sk_buff *);
-static int rcv_proto_vjc_comp	(struct ppp *, struct sk_buff *);
-static int rcv_proto_vjc_uncomp (struct ppp *, struct sk_buff *);
-static int rcv_proto_ccp	(struct ppp *, struct sk_buff *);
-static int rcv_proto_unknown	(struct ppp *, struct sk_buff *);
-
-static
-ppp_proto_type proto_list[] = {
-	{ PPP_IP,	  rcv_proto_ip	       },
-	{ PPP_IPV6,	  rcv_proto_ipv6       },
-	{ PPP_IPX,	  rcv_proto_ipx	       },
-	{ PPP_AT,	  rcv_proto_at	       },
-	{ PPP_VJC_COMP,	  rcv_proto_vjc_comp   },
-	{ PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp },
-	{ PPP_CCP,	  rcv_proto_ccp	       },
-	{ 0,		  rcv_proto_unknown    }  /* !!! MUST BE LAST !!! */
-};
-
-/*
- * Called when the PPP network interface device is actually created.
- */
-static int
-ppp_init_dev (struct net_device *dev)
-{
-	dev->hard_header_len  = PPP_HDRLEN;
-
-	/* device INFO */
-	dev->mtu	      = PPP_MTU;
-	dev->hard_start_xmit  = ppp_dev_xmit;
-	dev->open	      = ppp_dev_open;
-	dev->stop	      = ppp_dev_close;
-	dev->get_stats	      = ppp_dev_stats;
-	dev->do_ioctl	      = ppp_dev_ioctl;
-	dev->addr_len	      = 0;
-	dev->tx_queue_len     = 10;
-	dev->type	      = ARPHRD_PPP;
-
-	dev_init_buffers(dev);
-
-	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
-
-	return 0;
-}
-
-/*
- * Callback from the network layer when the device goes up.
- */
-
-static int
-ppp_dev_open (struct net_device *dev)
-{
-	struct ppp *ppp = dev2ppp(dev);
-
-	if (!ppp->inuse || ppp2tty(ppp) == NULL) {
-		printk(KERN_ERR "ppp: %s not active\n", dev->name);
-		return -ENXIO;
-	}
-
-	MOD_INC_USE_COUNT;
-
-	return 0;
-}
-
-/*
- * Callback from the network layer when the ppp device goes down.
- */
-
-static int
-ppp_dev_close (struct net_device *dev)
-{
-	struct ppp *ppp = dev2ppp (dev);
-
-	CHECK_PPP_MAGIC(ppp);
-
-	MOD_DEC_USE_COUNT;
-
-	return 0;
-}
-
-static inline void
-get_vj_stats(struct vjstat *vj, struct slcompress *slc)
-{
-	vj->vjs_packets    = slc->sls_o_compressed + slc->sls_o_uncompressed;
-	vj->vjs_compressed = slc->sls_o_compressed;
-	vj->vjs_searches   = slc->sls_o_searches;
-	vj->vjs_misses     = slc->sls_o_misses;
-	vj->vjs_errorin    = slc->sls_i_error;
-	vj->vjs_tossed     = slc->sls_i_tossed;
-	vj->vjs_uncompressedin = slc->sls_i_uncompressed;
-	vj->vjs_compressedin   = slc->sls_i_compressed;
-}
-
-/*
- * Callback from the network layer to process the sockioctl functions.
- */
-static int
-ppp_dev_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-	struct ppp *ppp = dev2ppp(dev);
-	int nb;
-	union {
-		struct ppp_stats stats;
-		struct ppp_comp_stats cstats;
-		char vers[32];
-	} u;
-
-	CHECK_PPP_MAGIC(ppp);
-
-	memset(&u, 0, sizeof(u));
-	switch (cmd) {
-	case SIOCGPPPSTATS:
-		u.stats.p = ppp->stats;
-		if (ppp->slcomp != NULL)
-			get_vj_stats(&u.stats.vj, ppp->slcomp);
-		nb = sizeof(u.stats);
-		break;
-
-	case SIOCGPPPCSTATS:
-		if (ppp->sc_xc_state != NULL)
-			(*ppp->sc_xcomp->comp_stat)
-				(ppp->sc_xc_state, &u.cstats.c);
-		if (ppp->sc_rc_state != NULL)
-			(*ppp->sc_rcomp->decomp_stat)
-				(ppp->sc_rc_state, &u.cstats.d);
-		nb = sizeof(u.cstats);
-		break;
-
-	case SIOCGPPPVER:
-		strcpy(u.vers, szVersion);
-		nb = strlen(u.vers) + 1;
-		break;
-
-	default:
-		return -EINVAL;
-	}
-
-	if (copy_to_user((void *) ifr->ifr_ifru.ifru_data, &u, nb))
-		return -EFAULT;
-	return 0;
-}
-
-/*
- * Process the generic PPP ioctls, i.e. those which are not specific
- * to any particular type of hardware link.
- */
-static int
-ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
-{
-	register int temp_i = 0, oldflags;
-	int error = -EFAULT;
-	unsigned long flags;
-	struct ppp_idle cur_ddinfo;
-	struct npioctl npi;
-
-	CHECK_PPP(-ENXIO);
-
-	/*
-	 * The user must have an euid of root to do these requests.
-	 */
-	if (!capable(CAP_NET_ADMIN))
-		return -EPERM;
-
-	switch (param2) {
-	case PPPIOCSMRU:
-		/*
-		 * Set the MRU value
-		 */
-		if (get_user(temp_i, (int *) param3))
-			break;
-		if (temp_i < PPP_MRU)
-			temp_i = PPP_MRU;
-		ppp->mru = temp_i;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_INFO
-			       "ppp_ioctl: set mru to %x\n", temp_i);
-		error = 0;
-		break;
-
-	case PPPIOCGFLAGS:
-		/*
-		 * Fetch the current flags
-		 */
-		temp_i = ppp->flags & SC_MASK;
-#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
-		temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 |
-			  SC_RCV_ODDP | SC_RCV_EVNP;
-#endif
-		if (put_user(temp_i, (int *) param3))
-			break;
-		error = 0;
-		break;
-
-	case PPPIOCSFLAGS:
-		/*
-		 * Set the flags for the various options
-		 */
-		if (get_user(temp_i, (int *) param3))
-			break;
-
-		if (ppp->flags & ~temp_i & SC_CCP_OPEN)
-			ppp_ccp_closed(ppp);
-
-		save_flags(flags);
-		cli();
-		oldflags = ppp->flags;
-		temp_i = (temp_i & SC_MASK) | (oldflags & ~SC_MASK);
-		ppp->flags = temp_i;
-		restore_flags(flags);
-
-		if ((oldflags | temp_i) & SC_DEBUG)
-			printk(KERN_INFO
-			       "ppp_ioctl: set flags to %x\n", temp_i);
-		error = 0;
-		break;
-
-	case PPPIOCSCOMPRESS:
-		/*
-		 * Set the compression mode
-		 */
-		error = ppp_set_compression
-			(ppp, (struct ppp_option_data *) param3);
-		break;
-
-	case PPPIOCGUNIT:
-		/*
-		 * Obtain the unit number for this device.
-		 */
-		if (put_user(ppp->line, (int *) param3))
-			break;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_INFO
-			       "ppp_ioctl: get unit: %d\n", ppp->line);
-		error = 0;
-		break;
-
-	case PPPIOCSDEBUG:
-		/*
-		 * Set the debug level
-		 */
-		if (get_user(temp_i, (int *) param3))
-			break;
-		temp_i = (temp_i & 0x1F) << 16;
-
-		if ((ppp->flags | temp_i) & SC_DEBUG)
-			printk(KERN_INFO
-			       "ppp_ioctl: set dbg flags to %x\n", temp_i);
-
-		save_flags(flags);
-		cli();
-		ppp->flags = (ppp->flags & ~0x1F0000) | temp_i;
-		restore_flags(flags);
-		error = 0;
-		break;
-
-	case PPPIOCGDEBUG:
-		/*
-		 * Get the debug level
-		 */
-		temp_i = (ppp->flags >> 16) & 0x1F;
-		if (put_user(temp_i, (int *) param3))
-			break;
-		error = 0;
-		break;
-
-	case PPPIOCGIDLE:
-		/*
-		 * Get the times since the last send/receive frame operation
-		 */
-		/* change absolute times to relative times. */
-		cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
-		cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ;
-		if (copy_to_user((void *) param3, &cur_ddinfo,
-				 sizeof (cur_ddinfo)))
-			break;
-		error = 0;
-		break;
-
-	case PPPIOCSMAXCID:
-		/*
-		 * Set the maximum VJ header compression slot number.
-		 */
-		if (get_user(temp_i, (int *) param3))
-			break;
-		error = -EINVAL;
-		if (temp_i < 2 || temp_i > 255)
-			break;
-		++temp_i;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_INFO "ppp_ioctl: set maxcid to %d\n",
-			       temp_i);
-		if (ppp->slcomp != NULL)
-			slhc_free(ppp->slcomp);
-		ppp->slcomp = slhc_init(16, temp_i);
-
-		error = -ENOMEM;
-		if (ppp->slcomp == NULL) {
-			printk(KERN_ERR "ppp: no memory for VJ compression\n");
-			break;
-		}
-		error = 0;
-		break;
-
-	case PPPIOCGNPMODE:
-	case PPPIOCSNPMODE:
-		if (copy_from_user(&npi, (void *) param3, sizeof(npi)))
-			break;
-
-		switch (npi.protocol) {
-		case PPP_IP:
-			npi.protocol = NP_IP;
-			break;
-		case PPP_IPX:
-			npi.protocol = NP_IPX;
-			break;
-		case PPP_AT:
-			npi.protocol = NP_AT;
-			break;
-		default:
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "pppioc[gs]npmode: "
-				       "invalid proto %d\n", npi.protocol);
-			error = -EINVAL;
-			goto out;
-		}
-
-		if (param2 == PPPIOCGNPMODE) {
-			npi.mode = ppp->sc_npmode[npi.protocol];
-			if (copy_to_user((void *) param3, &npi, sizeof(npi)))
-				break;
-		} else {
-			ppp->sc_npmode[npi.protocol] = npi.mode;
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "ppp: set np %d to %d\n",
-				       npi.protocol, npi.mode);
-			mark_bh(NET_BH);
-		}
-		error = 0;
-		break;
-
-	default:
-		/*
-		 *  All other ioctl() events will come here.
-		 */
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_ERR
-			       "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
-			       param2, param3);
-
-		error = -ENOIOCTLCMD;
-		break;
-	}
-out:
-	return error;
-}
-
-/*
- * Process the set-compression ioctl.
- */
-static int
-ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
-{
-	struct compressor *cp;
-	int error, nb;
-	unsigned long flags;
-	__u8 *ptr;
-	__u8 ccp_option[CCP_MAX_OPTION_LENGTH];
-	struct ppp_option_data data;
-
-	/*
-	 * Fetch the compression parameters
-	 */
-	error = -EFAULT;
-	if (copy_from_user(&data, odp, sizeof (data)))
-		goto out;
-
-	nb  = data.length;
-	ptr = data.ptr;
-	if ((unsigned) nb >= CCP_MAX_OPTION_LENGTH)
-		nb = CCP_MAX_OPTION_LENGTH;
-
-	if (copy_from_user(ccp_option, ptr, nb))
-		goto out;
-
-	error = -EINVAL;
-	if (ccp_option[1] < 2)	/* preliminary check on the length byte */
-		goto out;
-
-	save_flags(flags);
-	cli();
-	ppp->flags &= ~(data.transmit? SC_COMP_RUN: SC_DECOMP_RUN);
-	restore_flags(flags);
-
-	cp = find_compressor (ccp_option[0]);
-#ifdef CONFIG_KMOD
-	if (cp == NULL) {
-		char modname[32];
-		sprintf(modname, "ppp-compress-%d", ccp_option[0]);
-		request_module(modname);
-		cp = find_compressor(ccp_option[0]);
-	}
-#endif /* CONFIG_KMOD */
-
-	if (cp == NULL) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG
-			       "%s: no compressor for [%x %x %x], %x\n",
-			       ppp->name, ccp_option[0], ccp_option[1],
-			       ccp_option[2], nb);
-		goto out;		/* compressor not loaded */
-	}
-
-	/*
-	 * Found a handler for the protocol - try to allocate
-	 * a compressor or decompressor.
-	 */
-	error = 0;
-	if (data.transmit) {
-		if (ppp->sc_xc_state != NULL)
-			(*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state);
-		ppp->sc_xc_state = NULL;
-
-		ppp->sc_xcomp	 = cp;
-		ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
-		if (ppp->sc_xc_state == NULL) {
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "%s: comp_alloc failed\n",
-				       ppp->name);
-			error = -ENOBUFS;
-		}
-	} else {
-		if (ppp->sc_rc_state != NULL)
-			(*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
-		ppp->sc_rc_state = NULL;
-
-		ppp->sc_rcomp	 = cp;
-		ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
-		if (ppp->sc_rc_state == NULL) {
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "%s: decomp_alloc failed\n",
-				       ppp->name);
-			error = -ENOBUFS;
-		}
-	}
-out:
-	return error;
-}
-
-/*
- * Handle a CCP packet.
- *
- * The CCP packet is passed along to the pppd process just like any
- * other PPP frame. The difference is that some processing needs to be
- * immediate or the compressors will become confused on the peer.
- */
-
-static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd)
-{
-	int slen    = CCP_LENGTH(dp);
-	__u8 *opt = dp	 + CCP_HDRLEN;
-	int opt_len = slen - CCP_HDRLEN;
-	unsigned long flags;
-
-	if (slen > len)
-		return;
-
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "ppp_proto_ccp rcvd=%d code=%x flags=%x\n",
-		       rcvd, CCP_CODE(dp), ppp->flags);
-	save_flags(flags);
-	switch (CCP_CODE(dp)) {
-	case CCP_CONFREQ:
-	case CCP_TERMREQ:
-	case CCP_TERMACK:
-		/*
-		 * CCP must be going down - disable compression
-		 */
-		if (ppp->flags & SC_CCP_UP) {
-			cli();
-			ppp->flags &= ~(SC_CCP_UP   |
-					SC_COMP_RUN |
-					SC_DECOMP_RUN);
-		}
-		break;
-
-	case CCP_CONFACK:
-		if ((ppp->flags & SC_CCP_OPEN) == 0)
-			break;
-		if (ppp->flags & SC_CCP_UP)
-			break;
-		if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
-			break;
-		if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
-			break;
-		if (!rcvd) {
-			/*
-			 * we're agreeing to send compressed packets.
-			 */
-			if (ppp->sc_xc_state == NULL)
-				break;
-
-			if ((*ppp->sc_xcomp->comp_init)
-			    (ppp->sc_xc_state,
-			     opt, opt_len,
-			     ppp->line, 0, ppp->flags & SC_DEBUG)) {
-				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: comp running\n",
-					       ppp->name);
-				cli();
-				ppp->flags |= SC_COMP_RUN;
-			}
-			break;
-		}
-
-		/*
-		 * peer is agreeing to send compressed packets.
-		 */
-		if (ppp->sc_rc_state == NULL)
-			break;
-
-		if ((*ppp->sc_rcomp->decomp_init)
-		    (ppp->sc_rc_state,
-		     opt, opt_len,
-		     ppp->line, 0, ppp->mru, ppp->flags & SC_DEBUG)) {
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "%s: decomp running\n",
-				       ppp->name);
-			cli();
-			ppp->flags |= SC_DECOMP_RUN;
-			ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
-		}
-		break;
-
-	case CCP_RESETACK:
-		/*
-		 * CCP Reset-ack resets compressors and decompressors
-		 * as it passes through.
-		 */
-		if ((ppp->flags & SC_CCP_UP) == 0)
-			break;
-
-		if (!rcvd) {
-			if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) {
-				(*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
-				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: comp reset\n",
-					       ppp->name);
-			}
-		} else {
-			if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
-			      (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
-			      if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: decomp reset\n",
-					       ppp->name);
-			      cli();
-			      ppp->flags &= ~SC_DC_ERROR;
-			}
-		}
-		break;
-	}
-	restore_flags(flags);
-}
-
-/*
- * CCP is down; free (de)compressor state if necessary.
- */
-
-static void
-ppp_ccp_closed(struct ppp *ppp)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
-	restore_flags(flags);
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s: ccp closed\n", ppp->name);
-	if (ppp->sc_xc_state) {
-		(*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
-		ppp->sc_xc_state = NULL;
-	}
-
-	if (ppp->sc_rc_state) {
-		(*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
-		ppp->sc_rc_state = NULL;
-	}
-}
-
-/*************************************************************
- * RECEIVE-SIDE ROUTINES
- *************************************************************/
-
-/*
- * On entry, a received frame is in skb.
- * Check it and dispose as appropriate.
- */
-static int
-ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
-{
-	__u8	*data;
-	int	count;
-	int	proto;
-	int	new_count;
-	struct sk_buff *new_skb;
-	ppp_proto_type	*proto_ptr;
-
-	/*
-	 * An empty frame is ignored. This occurs if the FLAG sequence
-	 * precedes and follows each frame.
-	 */
-	if (skb == NULL)
-		return 1;
-	if (skb->len == 0) {
-		kfree_skb(skb);
-		return 1;
-	}
-	data = skb->data;
-	count = skb->len;
-
-	/*
-	 * Generate an error if the frame is too small.
-	 */
-	if (count < PPP_HDRLEN + 2) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG
-			       "ppp: got runt ppp frame, %d chars\n", count);
-		++ppp->estats.rx_length_errors;
-		return 0;
-	}
-
-	if ( !(ppp->flags & SC_SYNC) ) { 
-		/*
-		 * Verify the FCS of the frame and discard the FCS characters
-		 * from the end of the buffer.
-		 */
-		if (ppp->rfcs != PPP_GOODFCS) {
-			if (ppp->flags & SC_DEBUG) {
-				printk(KERN_DEBUG
-				       "ppp: frame with bad fcs, length = %d\n",
-				       count);
-				ppp_print_buffer("bad frame", data, count);
-			}
-			++ppp->estats.rx_crc_errors;
-			return 0;
-		}
-		count -= 2;		/* ignore the fcs characters */
-		skb_trim(skb, count);
-	}
-	
-	/*
-	 * Process the active decompressor.
-	 */
-	if (ppp->sc_rc_state != NULL &&
-	    (ppp->flags & SC_DECOMP_RUN) &&
-	    ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
-		if (PPP_PROTOCOL(data) == PPP_COMP) {
-			/*
-			 * If the frame is compressed then decompress it.
-			 */
-			new_skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
-			if (new_skb == NULL) {
-				printk(KERN_ERR "ppp_recv_frame: no memory\n");
-				new_count = DECOMP_ERROR;
-			} else {
-				new_count = (*ppp->sc_rcomp->decompress)
-					(ppp->sc_rc_state, data, count,
-					 new_skb->data, ppp->mru + PPP_HDRLEN);
-			}
-			if (new_count > 0) {
-				/* Frame was decompressed OK */
-				kfree_skb(skb);
-				skb = new_skb;
-				count = new_count;
-				data = skb_put(skb, count);
-
-			} else {
-				/*
-				 * On a decompression error, we pass the
-				 * compressed frame up to pppd as an
-				 * error indication.
-				 */
-				if (ppp->flags & SC_DEBUG)
-					printk(KERN_INFO "%s: decomp err %d\n",
-					       ppp->name, new_count);
-				if (new_skb != 0)
-					kfree_skb(new_skb);
-				if (ppp->slcomp != 0)
-					slhc_toss(ppp->slcomp);
-				++ppp->stats.ppp_ierrors;
-				if (new_count == DECOMP_FATALERROR) {
-					ppp->flags |= SC_DC_FERROR;
-				} else {
-					ppp->flags |= SC_DC_ERROR;
-				}
-			}
-
-
-		} else {
-			/*
-			 * The frame is not compressed. Pass it to the
-			 * decompression code so it can update its
-			 * dictionary if necessary.
-			 */
-			(*ppp->sc_rcomp->incomp)(ppp->sc_rc_state,
-						 data, count);
-		}
-	}
-	else if (PPP_PROTOCOL(data) == PPP_COMP && (ppp->flags & SC_DEBUG))
-		printk(KERN_INFO "%s: not decomp, rc_state=%p flags=%x\n",
-		       ppp->name, ppp->sc_rc_state, ppp->flags);
-
-	/*
-	 * Count the frame and print it
-	 */
-	++ppp->stats.ppp_ipackets;
-	ppp->stats.ppp_ioctects += count;
-	if (ppp->flags & SC_LOG_INPKT)
-		ppp_print_buffer ("receive frame", data, count);
-
-	/*
-	 * Find the procedure to handle this protocol.
-	 * The last one is marked as protocol 0 which is the 'catch-all'
-	 * to feed it to the pppd daemon.
-	 */
-	proto = PPP_PROTOCOL(data);
-	proto_ptr = proto_list;
-	while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
-		++proto_ptr;
-
-	/*
-	 * Update the appropriate statistic counter.
-	 */
-	if (!(*proto_ptr->func)(ppp, skb)) {
-		kfree_skb(skb);
-		++ppp->stats.ppp_discards;
-	}
-
-	return 1;
-}
-
-/*
- * An input error has been detected, so we need to inform
- * the VJ decompressor.
- */
-static void
-ppp_receive_error(struct ppp *ppp)
-{
-	CHECK_PPP_VOID();
-
-	if (ppp->slcomp != 0)
-		slhc_toss(ppp->slcomp);
-}
-
-/*
- * Put the input frame into the networking system for the indicated protocol
- */
-static int
-ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb)
-{
-
-	/*
-	 * Fill in a few fields of the skb and give it to netif_rx().
-	 */
-	skb->dev      = ppp2dev(ppp);	/* We are the device */
-	skb->protocol = htons(proto);
-	skb_pull(skb, PPP_HDRLEN);	/* pull off ppp header */
-	skb->mac.raw   = skb->data;
-	ppp->last_recv = jiffies;
-	netif_rx (skb);
-	return 1;
-}
-
-/*
- * Process the receipt of an IP frame
- */
-static int
-rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
-	    && ppp->sc_npmode[NP_IP] == NPMODE_PASS)
-		return ppp_rcv_rx(ppp, ETH_P_IP, skb);
-	return 0;
-}
-
-/*
- * Process the receipt of an IPv6 frame
- */
-static int
-rcv_proto_ipv6(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
-	    && ppp->sc_npmode[NP_IPV6] == NPMODE_PASS)
-		return ppp_rcv_rx(ppp, ETH_P_IPV6, skb);
-	return 0;
-}
-
-/*
- * Process the receipt of an IPX frame
- */
-static int
-rcv_proto_ipx(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-	if (((ppp2dev(ppp)->flags & IFF_UP) != 0) && (skb->len > 0)
-	    && ppp->sc_npmode[NP_IPX] == NPMODE_PASS)
-		return ppp_rcv_rx(ppp, ETH_P_IPX, skb);
-	return 0;
-}
-
-/*
- * Process the receipt of an Appletalk frame
- */
-static int
-rcv_proto_at(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
-	    && ppp->sc_npmode[NP_AT] == NPMODE_PASS)
-		return ppp_rcv_rx(ppp, ETH_P_PPPTALK, skb);
-	return 0;
-}
-
-/*
- * Process the receipt of an VJ Compressed frame
- */
-static int
-rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb)
-{
-	int new_count;
-
-	CHECK_PPP(0);
-	if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL)
-		return 0;
-	new_count = slhc_uncompress(ppp->slcomp, skb->data + PPP_HDRLEN,
-				    skb->len - PPP_HDRLEN);
-	if (new_count <= 0) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_NOTICE
-			       "ppp: error in VJ decompression\n");
-		return 0;
-	}
-	new_count += PPP_HDRLEN;
-	if (new_count > skb->len)
-		skb_put(skb, new_count - skb->len);
-	else
-		skb_trim(skb, new_count);
-	return rcv_proto_ip(ppp, skb);
-}
-
-/*
- * Process the receipt of an VJ Un-compressed frame
- */
-static int
-rcv_proto_vjc_uncomp(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-	if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL)
-		return 0;
-	if (slhc_remember(ppp->slcomp, skb->data + PPP_HDRLEN,
-			  skb->len - PPP_HDRLEN) <= 0) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_NOTICE "ppp: error in VJ memorizing\n");
-		return 0;
-	}
-	return rcv_proto_ip(ppp, skb);
-}
-
-static int
-rcv_proto_ccp(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-	ppp_proto_ccp (ppp, skb->data + PPP_HDRLEN, skb->len - PPP_HDRLEN, 1);
-	return rcv_proto_unknown(ppp, skb);
-}
-
-/*
- * Receive all unclassified protocols.
- */
-static int
-rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP(0);
-
-	/*
-	 * Limit queue length by dropping old frames.
-	 */
-	skb_queue_tail(&ppp->rcv_q, skb);
-	while (ppp->rcv_q.qlen > PPP_MAX_RCV_QLEN) {
-		struct sk_buff *skb = skb_dequeue(&ppp->rcv_q);
-		if (skb)
-			kfree_skb(skb);
-	}
-
-	wake_up_interruptible (&ppp->read_wait);
-	if (ppp->tty->fasync != NULL)
-		kill_fasync (ppp->tty->fasync, SIGIO);
-
-	return 1;
-}
-
-/*************************************************************
- * TRANSMIT-SIDE ROUTINES
- *************************************************************/
-
-/* local function to store a value into the LQR frame */
-extern inline __u8 * store_long (register __u8 *p, register int value) {
-	*p++ = (__u8) (value >> 24);
-	*p++ = (__u8) (value >> 16);
-	*p++ = (__u8) (value >>	 8);
-	*p++ = (__u8) value;
-	return p;
-}
-
-/*
- * Compress and send an frame to the peer.
- * Should be called with xmit_busy == 1, having been set by the caller.
- * That is, we use xmit_busy as a lock to prevent reentry of this
- * procedure.
- */
-static void
-ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
-{
-	int	proto;
-	__u8	*data;
-	int	count;
-	__u8	*p;
-	int	ret;
-
-	CHECK_PPP_VOID();
-	data = skb->data;
-	count = skb->len;
-
-	/* dump the buffer */
-	if (ppp->flags & SC_LOG_OUTPKT)
-		ppp_print_buffer ("write frame", data, count);
-
-	/*
-	 * Handle various types of protocol-specific compression
-	 * and other processing, including:
-	 * - VJ TCP header compression
-	 * - updating LQR packets
-	 * - updating CCP state on CCP packets
-	 */
-	proto = PPP_PROTOCOL(data);
-	switch (proto) {
-	case PPP_IP:
-		if ((ppp->flags & SC_COMP_TCP) && ppp->slcomp != NULL)
-			skb = ppp_vj_compress(ppp, skb);
-		break;
-
-	case PPP_LQR:
-		/*
-		 * Update the LQR frame with the current MIB information.
-		 * This way the information is accurate and up-to-date.
-		 */
-		if (count < 48)
-			break;
-		p = data + 40;	/* Point to last two items. */
-		p = store_long(p, ppp->stats.ppp_opackets + 1);
-		p = store_long(p, ppp->stats.ppp_ooctects + count);
-		++ppp->stats.ppp_olqrs;
-		break;
-
-	case PPP_CCP:
-		/*
-		 * Outbound compression control frames
-		 */
-		ppp_proto_ccp(ppp, data + PPP_HDRLEN, count - PPP_HDRLEN, 0);
-		break;
-	}
-	data = skb->data;
-	count = skb->len;
-
-	/*
-	 * Compress the whole frame if possible.
-	 */
-	if (((ppp->flags & SC_COMP_RUN) != 0)	&&
-	    (ppp->sc_xc_state != (void *) 0)	&&
-	    (proto != PPP_LCP)			&&
-	    (proto != PPP_CCP)) {
-		struct sk_buff *new_skb;
-		int new_count;
-
-		/* Allocate an skb for the compressed frame. */
-		new_skb = alloc_skb(ppp->mtu + PPP_HDRLEN, GFP_ATOMIC);
-		if (new_skb == NULL) {
-			printk(KERN_ERR "ppp_send_frame: no memory\n");
-			kfree_skb(skb);
-			ppp->xmit_busy = 0;
-			return;
-		}
-
-		/* Compress the frame. */
-		new_count = (*ppp->sc_xcomp->compress)
-			(ppp->sc_xc_state, data, new_skb->data,
-			 count, ppp->mtu + PPP_HDRLEN);
-
-		/* Did it compress? */
-		if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
-			skb_put(new_skb, new_count);
-			kfree_skb(skb);
-			skb = new_skb;
-		} else {
-			/*
-			 * The frame could not be compressed, or it could not
-			 * be sent in compressed form because CCP is down.
-			 */
-			kfree_skb(new_skb);
-		}
-	}
-
-	/*
-	 * Send the frame
-	 */
-	if ( ppp->flags & SC_SYNC ) 
-		ret = ppp_sync_send(ppp, skb);
-	else
-		ret = ppp_async_send(ppp, skb);
-	if (ret > 0) {
-		/* we can release the lock */
-		ppp->xmit_busy = 0;
-	} else if (ret < 0) {
-		/* can't happen, since the caller got the xmit_busy lock */
-		printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n");
-	}
-}
-
-/*
- * Apply VJ TCP header compression to a packet.
- */
-static struct sk_buff *
-ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb)
-{
-	__u8 *orig_data, *data;
-	struct sk_buff *new_skb;
-	int len, proto;
-
-	new_skb = alloc_skb(skb->len, GFP_ATOMIC);
-	if (new_skb == NULL) {
-		printk(KERN_ERR "ppp: no memory for vj compression\n");
-		return skb;
-	}
-
-	orig_data = data = skb->data + PPP_HDRLEN;
-	len = slhc_compress(ppp->slcomp, data, skb->len - PPP_HDRLEN,
-			    new_skb->data + PPP_HDRLEN, &data,
-			    (ppp->flags & SC_NO_TCP_CCID) == 0);
-
-	if (data == orig_data) {
-		/* Couldn't compress the data */
-		kfree_skb(new_skb);
-		return skb;
-	}
-
-	/* The data has been changed */
-	if (data[0] & SL_TYPE_COMPRESSED_TCP) {
-		proto = PPP_VJC_COMP;
-		data[0] ^= SL_TYPE_COMPRESSED_TCP;
-	} else {
-		if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
-			proto = PPP_VJC_UNCOMP;
-		else
-			proto = PPP_IP;
-		data[0] = orig_data[0];
-	}
-
-	data = skb_put(new_skb, len + PPP_HDRLEN);
-	data[0] = PPP_ALLSTATIONS;
-	data[1] = PPP_UI;
-	data[2] = 0;
-	data[3] = proto;
-
-	kfree_skb(skb);
-	return new_skb;
-}
-
-static inline void
-ppp_send_frames(struct ppp *ppp)
-{
-	struct sk_buff *skb;
-
-	while (!test_and_set_bit(0, &ppp->xmit_busy)) {
-		skb = skb_dequeue(&ppp->xmt_q);
-		if (skb == NULL) {
-			ppp->xmit_busy = 0;
-			break;
-		}
-		ppp_send_frame(ppp, skb);
-	}
-	if (!ppp->xmit_busy && ppp->dev.tbusy) {
-		ppp->dev.tbusy = 0;
-		mark_bh(NET_BH);
-	}
-}
-
-/*
- * Called from the hardware (tty) layer when it can accept
- * another packet.
- */
-static void
-ppp_output_wakeup(struct ppp *ppp)
-{
-	CHECK_PPP_VOID();
-
-	if (!ppp->xmit_busy) {
-		printk(KERN_ERR "ppp_output_wakeup called but xmit_busy==0\n");
-		return;
-	}
-	ppp->xmit_busy = 0;
-	ppp_send_frames(ppp);
-}
-
-/*
- * Send a control frame (from pppd).
- */
-static void
-ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb)
-{
-	CHECK_PPP_VOID();
-
-	/*
-	 * Put the packet on the queue, then send as many as we can.
-	 */
-	skb_queue_tail(&ppp->xmt_q, skb);
-	ppp_send_frames(ppp);
-}
-
-
-/*************************************************************
- * NETWORK OUTPUT
- *    This routine accepts requests from the network layer
- *    and attempts to deliver the packets.
- *************************************************************/
-/*
- * Send a frame to the peer.
- * Returns 1 iff the frame was not accepted.
- */
-static int
-ppp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	struct ppp *ppp = dev2ppp(dev);
-	struct tty_struct *tty = ppp2tty(ppp);
-	enum NPmode npmode;
-	int proto;
-	unsigned char *hdr;
-
-	/* just a little sanity check. */
-	if (skb == NULL)
-		return 0;
-	if (skb->data == NULL) {
-		kfree_skb(skb);
-		return 0;
-	}
-
-	/*
-	 * Avoid timing problem should tty hangup while data is
-	 * queued to be sent.
-	 */
-	if (!ppp->inuse) {
-		dev_kfree_skb(skb);
-		return 0;
-	}
-
-	/*
-	 * Validate the tty interface
-	 */
-	if (tty == NULL) {
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_ERR
-			       "ppp_dev_xmit: %s not connected to a TTY!\n",
-			       dev->name);
-		dev_kfree_skb(skb);
-		return 0;
-	}
-
-	/*
-	 * Work out the appropriate network-protocol mode for this packet.
-	 */
-	npmode = NPMODE_PASS;	/* default */
-	switch (ntohs(skb->protocol)) {
-	case ETH_P_IP:
-		proto = PPP_IP;
-		npmode = ppp->sc_npmode[NP_IP];
-		break;
-	case ETH_P_IPV6:
-		proto = PPP_IPV6;
-		npmode = ppp->sc_npmode[NP_IPV6];
-		break;
-	case ETH_P_IPX:
-		proto = PPP_IPX;
-		npmode = ppp->sc_npmode[NP_IPX];
-		break;
-	case ETH_P_PPPTALK:
-	case ETH_P_ATALK:
-		proto = PPP_AT;
-		npmode = ppp->sc_npmode[NP_AT];
-		break;
-	default:
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_INFO "%s: packet for unknown proto %x\n",
-			       ppp->name, ntohs(skb->protocol));
-		dev_kfree_skb(skb);
-		return 0;
-	}
-
-	/*
-	 * Drop, accept or reject the packet depending on the mode.
-	 */
-	switch (npmode) {
-	case NPMODE_PASS:
-		break;
-
-	case NPMODE_QUEUE:
-		/*
-		 * We may not send the packet now, so drop it.
-		 * XXX It would be nice to be able to return it to the
-		 * network system to be queued and retransmitted later.
-		 */
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "%s: returning frame\n", ppp->name);
-		dev_kfree_skb(skb);
-		return 0;
-
-	case NPMODE_ERROR:
-	case NPMODE_DROP:
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG
-			       "ppp_dev_xmit: dropping (npmode = %d) on %s\n",
-			       npmode, ppp->name);
-		dev_kfree_skb(skb);
-		return 0;
-	}
-
-	/*
-	 * The dev->tbusy field acts as a lock to allow only
-	 * one packet to be processed at a time.  If we can't
-	 * get the lock, try again later.
-	 * We deliberately queue as little as possible inside
-	 * the ppp driver in order to minimize the latency
-	 * for high-priority packets.
-	 */
-	if (test_and_set_bit(0, &ppp->xmit_busy)) {
-		dev->tbusy = 1;	/* can't take it now */
-		return 1;
-	}
-	dev->tbusy = 0;
-
-	/*
-	 * Put the 4-byte PPP header on the packet.
-	 * If there isn't room for it, we have to copy the packet.
-	 */
-	if (skb_headroom(skb) < PPP_HDRLEN) {
-		struct sk_buff *new_skb;
-
-		new_skb = alloc_skb(skb->len + PPP_HDRLEN, GFP_ATOMIC);
-		if (new_skb == NULL) {
-			printk(KERN_ERR "%s: skb hdr alloc failed\n",
-			       ppp->name);
-			dev_kfree_skb(skb);
-			ppp->xmit_busy = 0;
-			ppp_send_frames(ppp);
-			return 0;
-		}
-		skb_reserve(new_skb, PPP_HDRLEN);
-		memcpy(skb_put(new_skb, skb->len), skb->data, skb->len);
-		dev_kfree_skb(skb);
-		skb = new_skb;
-	}
-
-	hdr = skb_push(skb, PPP_HDRLEN);
-	hdr[0] = PPP_ALLSTATIONS;
-	hdr[1] = PPP_UI;
-	hdr[2] = proto >> 8;
-	hdr[3] = proto;
-
-	ppp_send_frame(ppp, skb);
-	if (!ppp->xmit_busy)
-		ppp_send_frames(ppp);
-	return 0;
-}
-
-/*
- * Generate the statistic information for the /proc/net/dev listing.
- */
-static struct net_device_stats *
-ppp_dev_stats (struct net_device *dev)
-{
-	struct ppp *ppp = dev2ppp (dev);
-
-	ppp->estats.rx_packets = ppp->stats.ppp_ipackets;
-	ppp->estats.rx_errors  = ppp->stats.ppp_ierrors;
-	ppp->estats.tx_packets = ppp->stats.ppp_opackets;
-	ppp->estats.tx_errors  = ppp->stats.ppp_oerrors;
-	ppp->estats.rx_bytes   = ppp->stats.ppp_ibytes;
-	ppp->estats.tx_bytes   = ppp->stats.ppp_obytes;
-
-	return &ppp->estats;
-}
-
-/*************************************************************
- * UTILITIES
- *    Miscellany called by various functions above.
- *************************************************************/
-
-/* Locate the previous instance of the PPP channel */
-static struct ppp *
-ppp_find(int pid_value)
-{
-	struct ppp	*ppp;
-
-	/* try to find the device which this pid is already using */
-	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		if (ppp->inuse && ppp->sc_xfer == pid_value) {
-			ppp->sc_xfer = 0;
-			break;
-		}
-	}
-	return ppp;
-}
-
-/* allocate or create a PPP channel */
-static struct ppp *
-ppp_alloc(void)
-{
-	int		if_num;
-	int		status;
-	struct net_device	*dev;
-	struct ppp	*ppp;
-
-	/* try to find an free device */
-	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		if (!test_and_set_bit(0, &ppp->inuse)) {
-			dev = ppp2dev(ppp);
-			if (dev->flags & IFF_UP) {
-				clear_bit(0, &ppp->inuse);
-				continue;
-			}
-			/* Reregister device */
-			unregister_netdev(dev);
-			if (register_netdev(dev) == 0)
-				return ppp;
-			printk(KERN_DEBUG "could not reregister ppp device\n");
-			/* leave inuse set in this case */
-		}
-	}
-
-	/*
-	 * There are no available units, so make a new one.
-	 */
-	ppp = (struct ppp *) kmalloc(sizeof(struct ppp), GFP_KERNEL);
-	if (ppp == 0) {
-		printk(KERN_ERR "ppp: struct ppp allocation failed\n");
-		return 0;
-	}
-	memset(ppp, 0, sizeof(*ppp));
-
-	/* initialize channel control data */
-	ppp->magic = PPP_MAGIC;
-	ppp->next = NULL;
-	ppp->inuse = 1;
-	init_waitqueue_head(&ppp->read_wait);
-
-	/*
-	 * Make up a suitable name for this device
-	 */
-	dev = ppp2dev(ppp);
-	dev->name = ppp->name;
-	if_num = dev_alloc_name(dev, "ppp%d");
-	if (if_num < 0) {
-		printk(KERN_ERR "ppp: dev_alloc_name failed (%d)\n", if_num);
-		kfree(ppp);
-		return 0;
-	}
-	ppp->line = if_num;
-	ppp->slcomp = NULL;
-
-	dev->next = NULL;
-	dev->init = ppp_init_dev;
-	dev->name = ppp->name;
-	dev->priv = (void *) ppp;
-
-	/* register device so that we can be ifconfig'd */
-	/* ppp_init_dev() will be called as a side-effect */
-	status = register_netdev (dev);
-	if (status == 0) {
-		printk(KERN_INFO "registered device %s\n", dev->name);
-	} else {
-		printk(KERN_ERR
-		       "ppp_alloc - register_netdev(%s) = %d failure.\n",
-		       dev->name, status);
-		kfree(ppp);
-		ppp = NULL;
-	}
-
-	/* link this unit into our list */
-	if (ppp_list == 0)
-		ppp_list = ppp;
-	else
-		ppp_last->next = ppp;
-	ppp_last = ppp;
-
-	return ppp;
-}
-
-/*
- * Initialize the generic parts of the ppp structure.
- */
-static void
-ppp_generic_init(struct ppp *ppp)
-{
-	int indx;
-
-	ppp->flags  = 0;
-	ppp->mtu    = PPP_MTU;
-	ppp->mru    = PPP_MRU;
-
-	skb_queue_head_init(&ppp->xmt_q);
-	skb_queue_head_init(&ppp->rcv_q);
-
-	ppp->last_xmit	= jiffies;
-	ppp->last_recv  = jiffies;
-	ppp->xmit_busy  = 0;
-
-	/* clear statistics */
-	memset(&ppp->stats, 0, sizeof (struct pppstat));
-	memset(&ppp->estats, 0, sizeof(struct net_device_stats));
-
-	/* PPP compression data */
-	ppp->sc_xc_state = NULL;
-	ppp->sc_rc_state = NULL;
-
-	for (indx = 0; indx < NUM_NP; ++indx)
-		ppp->sc_npmode[indx] = NPMODE_PASS;
-}
-
-/*
- * Called to clean up the generic parts of the ppp structure.
- */
-static void
-ppp_release(struct ppp *ppp)
-{
-	struct sk_buff *skb;
-
-	CHECK_PPP_MAGIC(ppp);
-
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s released\n", ppp->name);
-
-	ppp_ccp_closed(ppp);
-
-        /* Ensure that the pppd process is not hanging on select()/poll() */
-        wake_up_interruptible(&ppp->read_wait);
-
-	if (ppp->slcomp) {
-		slhc_free(ppp->slcomp);
-		ppp->slcomp = NULL;
-	}
-
-	while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL)
-		kfree_skb(skb);
-	while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
-		kfree_skb(skb);
-
-	ppp->inuse = 0;
-	if (ppp->dev.tbusy) {
-		ppp->dev.tbusy = 0;
-		mark_bh(NET_BH);
-	}
-}
-
-/*
- * Utility procedures to print a buffer in hex/ascii
- */
-static void
-ppp_print_hex (register __u8 * out, const __u8 * in, int count)
-{
-	register __u8 next_ch;
-	static char hex[] = "0123456789ABCDEF";
-
-	while (count-- > 0) {
-		next_ch = *in++;
-		*out++ = hex[(next_ch >> 4) & 0x0F];
-		*out++ = hex[next_ch & 0x0F];
-		++out;
-	}
-}
-
-static void
-ppp_print_char (register __u8 * out, const __u8 * in, int count)
-{
-	register __u8 next_ch;
-
-	while (count-- > 0) {
-		next_ch = *in++;
-
-		if (next_ch < 0x20 || next_ch > 0x7e)
-			*out++ = '.';
-		else {
-			*out++ = next_ch;
-			if (next_ch == '%')   /* printk/syslogd has a bug !! */
-				*out++ = '%';
-		}
-	}
-	*out = '\0';
-}
-
-static void
-ppp_print_buffer (const char *name, const __u8 *buf, int count)
-{
-	__u8 line[44];
-
-	if (name != NULL)
-		printk(KERN_DEBUG "ppp: %s, count = %d\n", name, count);
-
-	while (count > 8) {
-		memset (line, 32, 44);
-		ppp_print_hex (line, buf, 8);
-		ppp_print_char (&line[8 * 3], buf, 8);
-		printk(KERN_DEBUG "%s\n", line);
-		count -= 8;
-		buf += 8;
-	}
-
-	if (count > 0) {
-		memset (line, 32, 44);
-		ppp_print_hex (line, buf, count);
-		ppp_print_char (&line[8 * 3], buf, count);
-		printk(KERN_DEBUG "%s\n", line);
-	}
-}
-
-/*************************************************************
- * Compressor module interface
- *************************************************************/
-
-struct compressor_link {
-	struct compressor_link	*next;
-	struct compressor	*comp;
-};
-
-static struct compressor_link *ppp_compressors = (struct compressor_link *) 0;
-
-static struct compressor *find_compressor (int type)
-{
-	struct compressor_link *lnk;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	lnk = ppp_compressors;
-	while (lnk != (struct compressor_link *) 0) {
-		if ((int) (__u8) lnk->comp->compress_proto == type) {
-			restore_flags(flags);
-			return lnk->comp;
-		}
-		lnk = lnk->next;
-	}
-
-	restore_flags(flags);
-	return (struct compressor *) 0;
-}
-
-#ifdef CONFIG_MODULES
-static int ppp_register_compressor (struct compressor *cp)
-{
-	struct compressor_link *new;
-	unsigned long flags;
-
-	new = (struct compressor_link *)
-		kmalloc (sizeof (struct compressor_link), GFP_KERNEL);
-
-	if (new == (struct compressor_link *) 0)
-		return 1;
-
-	save_flags(flags);
-	cli();
-
-	if (find_compressor (cp->compress_proto)) {
-		restore_flags(flags);
-		kfree (new);
-		return 0;
-	}
-
-	new->next	= ppp_compressors;
-	new->comp	= cp;
-	ppp_compressors = new;
-
-	restore_flags(flags);
-	return 0;
-}
-
-static void ppp_unregister_compressor (struct compressor *cp)
-{
-	struct compressor_link *prev = (struct compressor_link *) 0;
-	struct compressor_link *lnk;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	lnk  = ppp_compressors;
-	while (lnk != (struct compressor_link *) 0) {
-		if (lnk->comp == cp) {
-			if (prev)
-				prev->next = lnk->next;
-			else
-				ppp_compressors = lnk->next;
-			kfree (lnk);
-			break;
-		}
-		prev = lnk;
-		lnk  = lnk->next;
-	}
-	restore_flags(flags);
-}
-#endif
-
-/*************************************************************
- * Module support routines
- *************************************************************/
-
-#ifdef MODULE
-int
-init_module(void)
-{
-	int status;
-
-	/* register our line disciplines */
-	status = ppp_first_time();
-	if (status != 0)
-		printk(KERN_INFO "PPP: ppp_init() failure %d\n", status);
-
-	return status;
-}
-
-void
-cleanup_module(void)
-{
-	int status;
-	struct ppp *ppp, *next_ppp;
-	int busy = 0;
-
-	/*
-	 * Ensure that the devices are not in operation.
-	 */
-	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		CHECK_PPP_MAGIC(ppp);
-		if (ppp->inuse || (ppp->dev.flags & IFF_UP))
-			++busy;
-	}
-	if (busy)
-		printk(KERN_CRIT "PPP: removing despite %d units in use!\n",
-		       busy);
-
-	/*
-	 * Release the tty registration of the line discipline so that
-	 * ttys can no longer be put into PPP line discipline.
-	 */
-	status = tty_register_ldisc (N_PPP, NULL);
-	if (status != 0)
-		printk(KERN_ERR
-		       "PPP: Unable to unregister ppp line discipline "
-		       "(err = %d)\n", status);
-	else
-		printk(KERN_INFO
-		       "PPP: ppp line discipline successfully unregistered\n");
-
-	/*
-	 * De-register the devices so that there is no problem with them
-	 */
-	for (ppp = ppp_list; ppp != 0; ppp = next_ppp) {
-		next_ppp = ppp->next;
-		unregister_netdev(&ppp->dev);
-		kfree (ppp);
-	}
-}
-#endif
diff -u --recursive --new-file v2.3.18/linux/drivers/net/sb1000.c linux/drivers/net/sb1000.c
--- v2.3.18/linux/drivers/net/sb1000.c	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/sb1000.c	Wed Sep 29 15:32:15 1999
@@ -912,6 +912,7 @@
 	/* datagram completed: send to upper level */
 	skb_trim(skb, dlen);
 	netif_rx(skb);
+	stats->rx_bytes+=dlen;
 	stats->rx_packets++;
 	lp->rx_bytes += dlen;
 	lp->rx_skb[ns] = 0;
@@ -1257,7 +1258,7 @@
 	int i;
 	for (i = 0; i < 100; i++) {
 		sprintf(devname, "cm%d", i);
-		if (dev_get(devname) == NULL) break;
+		if (dev_get(devname) == 0) break;
 	}
 	if (i == 100) {
 		printk(KERN_ERR "sb1000: can't register any device cm<n>\n");
diff -u --recursive --new-file v2.3.18/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.3.18/linux/drivers/net/slip.c	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/slip.c	Wed Sep 29 14:02:59 1999
@@ -831,6 +831,9 @@
 	struct slip *sl;
 	int err;
 
+	if(!capable(CAP_NET_ADMIN))
+		return -EPERM;
+		
 	MOD_INC_USE_COUNT;
 
 	/* RTnetlink lock is misused here to serialize concurrent
diff -u --recursive --new-file v2.3.18/linux/drivers/net/smc-mca.c linux/drivers/net/smc-mca.c
--- v2.3.18/linux/drivers/net/smc-mca.c	Wed Aug 18 11:36:42 1999
+++ linux/drivers/net/smc-mca.c	Sat Oct  2 07:38:27 1999
@@ -1,4 +1,4 @@
-/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/* smc-mca.c: A SMC Ultra ethernet driver for linux. */
 /*
     Most of this driver, except for ultramca_probe is nearly
     verbatim from smc-ultra.c by Donald Becker. The rest is
@@ -8,7 +8,7 @@
 
     This driver uses the cards in the 8390-compatible, shared memory mode.
     Most of the run-time complexity is handled by the generic code in
-    8390.c.  The code in this file is responsible for
+    8390.c.
 
     This driver enables the shared memory only when doing the actual data
     transfers to avoid a bug in early version of the card that corrupted
@@ -20,9 +20,20 @@
 
     Changelog:
 
-    Paul Gortmaker  : multiple card support for module users.
-    David Weis      : Micro Channel-ized it.
-
+    Paul Gortmaker	 : multiple card support for module users.
+    David Weis		 : Micro Channel-ized it.
+    Tom Sightler	 : Added support for IBM PS/2 Ethernet Adapter/A
+    Christopher Turcksin : Changed MCA-probe so that multiple adapters are
+			   found correctly (Jul 16, 1997)
+    Chris Beauregard	 : Tried to merge the two changes above (Dec 15, 1997)
+    Tom Sightler	 : Fixed minor detection bug caused by above merge
+    Tom Sightler	 : Added support for three more Western Digital
+			   MCA-adapters
+    Tom Sightler	 : Added support for 2.2.x mca_find_unused_adapter
+    Hartmut Schmidt	 : - Modified parameter detection to handle each
+			     card differently depending on a switch-list
+			   - 'card_ver' removed from the adapter list
+			   - Some minor bug fixes
 */
 
 
@@ -66,52 +77,176 @@
 #define ULTRA_IO_EXTENT 32
 #define EN0_ERWCNT      0x08  /* Early receive warning count. */
 
+#define _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A            0
+#define _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A            1
+#define _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A              2
+#define _6fc1_WD_Starcard_PLUS_A_WD8003ST_A                            3
+#define _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A                        4
+#define _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A        5
+#define _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A        6
+#define _efe5_IBM_PS2_Adapter_A_for_Ethernet                           7
+
+struct smc_mca_adapters_t {
+	unsigned int id;
+	char *name;
+};
+
+const struct smc_mca_adapters_t smc_mca_adapters[] = {
+    { 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" },
+    { 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" },
+    { 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" },
+    { 0x6fc1, "WD Starcard PLUS/A (WD8003ST/A)" },
+    { 0x6fc2, "WD Ethercard PLUS 10T/A (WD8003W/A)" },
+    { 0xefd4, "IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)" },
+    { 0xefd5, "IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)" },
+    { 0xefe5, "IBM PS/2 Adapter/A for Ethernet" },
+    { 0x0000, NULL }
+};
+
 int __init ultramca_probe(struct net_device *dev)
 {
 	unsigned short ioaddr;
 	unsigned char reg4, num_pages;
-	char slot;
-	unsigned char pos2, pos3, pos4, pos5;
-	int i;
-
-	/* Look for two flavors of SMC Elite/A (3013EP/A) -jeh- */
-	if(( (slot=mca_find_adapter(0x61c8,0)) != MCA_NOTFOUND) ||
-            ((slot=mca_find_adapter(0xefd5,0)) != MCA_NOTFOUND) )
+	char slot = -1;
+	unsigned char pos2 = 0xff, pos3 = 0xff, pos4 = 0xff, pos5 = 0xff;
+	int i, j;
+	int adapter_found = 0;
+	int adapter = 0;
+	int tbase = 0;
+	int tirq = 0;
+	int base_addr = dev ? dev->base_addr : 0;
+	int irq = dev ? dev->irq : 0;
 
-	{
-#ifndef MODULE
-		mca_set_adapter_name( slot, "SMC Elite/A (8013EP/A)" );
-#endif
+	if (!MCA_bus) {
+		return ENODEV;
 	}
-	else if( (slot=mca_find_adapter(0x61c9,0)) != MCA_NOTFOUND)
-	{
-#ifndef MODULE
-		mca_set_adapter_name( slot, "SMC Elite10T/A (8013WP/A)" );
-#endif
+
+	if (base_addr || irq) {
+		printk(KERN_INFO "Probing for SMC MCA adapter");
+		if (base_addr) {
+			printk(KERN_INFO " at I/O address 0x%04x%c",
+			       base_addr, irq ? ' ' : '\n');
+		}
+		if (irq) {
+			printk(KERN_INFO "using irq %d\n", irq);
+		}
 	}
-	else
-		return -ENODEV;
 
-	pos2 = mca_read_stored_pos(slot, 2);     /* IO range */
-	pos3 = mca_read_stored_pos(slot, 3);     /* shared mem */
-	pos4 = mca_read_stored_pos(slot, 4);     /* bios base */
-	pos5 = mca_read_stored_pos(slot, 5);     /* irq and media */
+        /* proper multicard detection by ZP Gu (zpg@castle.net) */
+
+	for (j = 0; (smc_mca_adapters[j].name != NULL) && !adapter_found; j++) {
+		slot = mca_find_unused_adapter(smc_mca_adapters[j].id, 0);
+
+		while((slot != MCA_NOTFOUND) && !adapter_found) {
+			tirq = 0;
+			tbase = 0;
+
+                        /* If we're trying to match a specificied irq or
+			 * io address, we'll reject the adapter
+			 * found unless it's the one we're looking for
+			 */
+
+			pos2 = mca_read_stored_pos(slot, 2); /* io_addr */
+			pos3 = mca_read_stored_pos(slot, 3); /* shared mem */
+			pos4 = mca_read_stored_pos(slot, 4); /* ROM bios addr
+							      * range */
+			pos5 = mca_read_stored_pos(slot, 5); /* irq, media
+							      * and RIPL */
+
+			/* Test the following conditions:
+			 * - If an irq parameter is supplied, compare it
+			 *   with the irq of the adapter we found
+			 * - If a base_addr paramater is given, compare it
+			 *   with the base_addr of the adapter we found
+			 * - Check that the irq and the base_addr of the
+			 *   adapter we found is not already in use by
+			 *   this driver
+			 */
+
+			switch (j) { /* j = card-idx (card array above) [hs] */
+				case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+		                case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+				case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+				case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
+				{
+					tbase = addr_table[(pos2 & 0xf0) >> 4].base_addr;
+					tirq  = irq_table[(pos5 & 0xc) >> 2].new_irq;
+					break;
+				}
+				case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+				case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+				case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+				case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+				{
+					tbase = ((pos2 & 0x0fe) * 0x10);
+					tirq  = irq_table[(pos5 & 3)].old_irq;
+					break;
+				}
+			}
+
+			if(!tirq || !tbase || (irq && irq != tirq) || (base_addr && tbase != base_addr)) {
+				slot = mca_find_unused_adapter(smc_mca_adapters[j].id, slot + 1);
+			} else {
+				adapter_found = 1;
+				adapter = j;
+			}
+		}
+	}
+
+	if(!adapter_found) {
+		return ((base_addr || irq) ? ENXIO : ENODEV);
+	}
+
+        /* Adapter found. */
+
+	printk(KERN_INFO "%s: %s found in slot %d\n",
+	       dev->name, smc_mca_adapters[adapter].name, slot + 1);
+
+	mca_set_adapter_name(slot, smc_mca_adapters[adapter].name);
+	mca_mark_as_used(slot);
 
-	dev->base_addr = ioaddr = addr_table[pos2 >> 4].base_addr;
-	dev->irq = irq_table[(pos5 & ~IRQ_MASK) >> 2].irq;
 
+	dev->base_addr = ioaddr = tbase;
+	dev->irq       = tirq;
 	dev->mem_start = 0;
-	num_pages = 40;
-	for (i = 0; i < 15; i++)
-	{
-		if (mem_table[i].mem_index == (pos3 & ~MEM_MASK))
+	num_pages      = 40;
+
+	switch (j) {	/* 'j' = card-# in const array above [hs] */
+		case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+		case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+		{
+			for (i = 0; i < 16; i++) { /* taking 16 counts
+						    * up to 15 [hs] */
+				if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) {
+					dev->mem_start = mem_table[i].mem_start;
+					num_pages = mem_table[i].num_pages;
+				}
+			}
+			break;
+		}
+		case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+		case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+		case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+		case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+		{
+			dev->mem_start = ((pos3 & 0xfc) * 0x1000);
+			num_pages = 0x40;
+			break;
+		}
+		case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+		case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
 		{
-			dev->mem_start = mem_table[i].mem_start;
-			num_pages = mem_table[i].num_pages;
+			/* courtesy of gamera@quartz.ocn.ne.jp, pos3 indicates
+			 * the index of the 0x2000 step.
+			 * beware different number of pages [hs]
+			 */
+			dev->mem_start = 0xc0000 + (0x2000 * (pos3 & 0xf));
+			num_pages = 0x20 + (2 * (pos3 & 0x10));
+			break;
 		}
 	}
 
-	if (dev->mem_start == 0)      /* sanity check, shouldn't happen */
+	if (dev->mem_start == 0) /* sanity check, shouldn't happen */
 		return -ENODEV;
 
 	reg4 = inb(ioaddr + 4) & 0x7f;
@@ -120,52 +255,45 @@
 	if (load_8390_module("wd.c"))
 		return -ENOSYS;
 
-	printk("%s: SMC Ultra MCA at %#3x,", dev->name, ioaddr);
+	printk(KERN_INFO "%s: Parameters: %#3x,", dev->name, ioaddr);
 
 	for (i = 0; i < 6; i++)
-		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+		printk(KERN_INFO " %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
 
-	/*
-	 *	Switch from the station address to the alternate register set and
-	 *	read the useful registers there.
+	/* Switch from the station address to the alternate register set
+	 * and read the useful registers there.
 	 */
 
 	outb(0x80 | reg4, ioaddr + 4);
 
-	/*
-	 *	Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot.
+	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot.
 	 */
 
 	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
 
-	/*
-	 *	Switch back to the station address register set so that the MS-DOS driver
-	 *	can find the card after a warm boot.
+	/* Switch back to the station address register set so that
+	 * the MS-DOS driver can find the card after a warm boot.
 	 */
 
 	outb(reg4, ioaddr + 4);
 
-	/*
-	 *	Allocate dev->priv and fill in 8390 specific dev fields.
+	/* Allocate dev->priv and fill in 8390 specific dev fields.
 	 */
 
-	if (ethdev_init(dev))
-	{
-		printk (", no memory for dev->priv.\n");
+	if (ethdev_init(dev)) {
+		printk (KERN_INFO ", no memory for dev->priv.\n");
 		return -ENOMEM;
 	}
 
-	/*
-	 *	OK, we are certain this is going to work.  Setup the device.
+	/* OK, we are certain this is going to work.  Setup the device.
 	 */
 
 	request_region(ioaddr, ULTRA_IO_EXTENT, "smc-mca");
 
-	/*
-	 *	The 8390 isn't at the base address, so fake the offset
+	/* The 8390 isn't at the base address, so fake the offset
 	 */
 
-	dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+	dev->base_addr = ioaddr + ULTRA_NIC_OFFSET;
 
 	ei_status.name = "SMC Ultra MCA";
 	ei_status.word16 = 1;
@@ -173,16 +301,20 @@
 	ei_status.rx_start_page = START_PG + TX_PAGES;
 	ei_status.stop_page = num_pages;
 
-	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->rmem_start = dev->mem_start + TX_PAGES * 256;
 	dev->mem_end = dev->rmem_end =
-	dev->mem_start + (ei_status.stop_page - START_PG)*256;
+	dev->mem_start + (ei_status.stop_page - START_PG) * 256;
 
-	printk(", IRQ %d memory %#lx-%#lx.\n", dev->irq, dev->mem_start, dev->mem_end-1);
+	printk(KERN_INFO ", IRQ %d memory %#lx-%#lx.\n",
+	dev->irq, dev->mem_start, dev->mem_end - 1);
 
 	ei_status.reset_8390 = &ultramca_reset_8390;
 	ei_status.block_input = &ultramca_block_input;
 	ei_status.block_output = &ultramca_block_output;
 	ei_status.get_8390_hdr = &ultramca_get_8390_hdr;
+
+	ei_status.priv = slot;
+
 	dev->open = &ultramca_open;
 	dev->stop = &ultramca_close_card;
 	NS8390_init(dev, 0);
@@ -195,21 +327,19 @@
 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
 
 	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev))
-	return -EAGAIN;
+		return -EAGAIN;
 
 	outb(ULTRA_MEMENB, ioaddr); /* Enable memory */
 	outb(0x80, ioaddr + 5);     /* ??? */
 	outb(0x01, ioaddr + 6);     /* Enable interrupts and memory. */
 	outb(0x04, ioaddr + 5);     /* ??? */
 
-	/*
-	 *	Set the early receive warning level in window 0 high enough not
-	 *	to receive ERW interrupts.
+	/* Set the early receive warning level in window 0 high enough not
+	 * to receive ERW interrupts.
 	 */
 
-	/*
-	 *	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
-	 *	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	/* outb_p(E8390_NODMA + E8390_PAGE0, dev->base_addr);
+	 * outb(0xff, dev->base_addr + EN0_ERWCNT);
 	 */
 
 	ei_open(dev);
@@ -222,7 +352,8 @@
 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
 
 	outb(ULTRA_RESET, ioaddr);
-	if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
+	if (ei_debug > 1)
+		printk("resetting Ultra, t=%ld...", jiffies);
 	ei_status.txing = 0;
 
 	outb(0x80, ioaddr + 5);     /* ??? */
@@ -234,12 +365,13 @@
 }
 
 /* Grab the 8390 specific header. Similar to the block_input routine, but
-   we don't need to be concerned with ring wrap as the header will be at
-   the start of a page, so we optimize accordingly. */
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
 
 static void ultramca_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 {
-	unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8);
+	unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG) << 8);
 
 #ifdef notdef
 	/* Officially this is what we are doing, but the readl() is faster */
@@ -250,22 +382,20 @@
 }
 
 /* Block input and output are easy on shared memory ethercards, the only
-   complication is when the ring buffer wraps. */
+ * complication is when the ring buffer wraps.
+ */
 
 static void ultramca_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
 {
-	unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG<<8);
+	unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG << 8);
 
-	if (xfer_start + count > dev->rmem_end)
-	{
-        	/* We must wrap the input move. */
+	if (xfer_start + count > dev->rmem_end) {
+		/* We must wrap the input move. */
 		int semi_count = dev->rmem_end - xfer_start;
 		memcpy_fromio(skb->data, xfer_start, semi_count);
 		count -= semi_count;
 		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
-	}
-	else
-	{
+	} else {
 		/* Packet is in one chunk -- we can copy + cksum. */
 		eth_io_copy_and_sum(skb, xfer_start, count, 0);
 	}
@@ -275,7 +405,7 @@
 static void ultramca_block_output(struct net_device *dev, int count, const unsigned char *buf,
                 int start_page)
 {
-	unsigned long shmem = dev->mem_start + ((start_page - START_PG)<<8);
+	unsigned long shmem = dev->mem_start + ((start_page - START_PG) << 8);
 
 	memcpy_toio(shmem, buf, count);
 }
@@ -295,7 +425,8 @@
 
 	NS8390_init(dev, 0);
 	/* We should someday disable shared memory and change to 8-bit mode
-       "just in case"... */
+         * "just in case"...
+	 */
 
 	MOD_DEC_USE_COUNT;
 
@@ -306,8 +437,9 @@
 #ifdef MODULE
 #undef MODULE        /* don't want to bother now! */
 
-#define MAX_ULTRAMCA_CARDS  4   /* Max number of Ultra cards per module */
-#define NAMELEN     8   /* # of chars for storing dev->name */
+#define MAX_ULTRAMCA_CARDS 4	/* Max number of Ultra cards per module */
+#define NAMELEN 8		/* # of chars for storing dev->name */
+
 static char namelist[NAMELEN * MAX_ULTRAMCA_CARDS] = { 0, };
 
 static struct net_device dev_ultra[MAX_ULTRAMCA_CARDS] =
@@ -326,33 +458,23 @@
 MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
 MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
 
-/* This is set up so that only a single autoprobe takes place per call.
-ISA device autoprobes on a running machine are not recommended. */
-
 int init_module(void)
 {
 	int this_dev, found = 0;
 
-	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++)
-	{
+	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) {
 		struct net_device *dev = &dev_ultra[this_dev];
-		dev->name = namelist+(NAMELEN*this_dev);
+		dev->name = namelist + (NAMELEN * this_dev);
 		dev->irq = irq[this_dev];
 		dev->base_addr = io[this_dev];
 		dev->init = ultramca_probe;
-		if (io[this_dev] == 0)
-		{
-			if (this_dev != 0)
-				break; /* only autoprobe 1st one */
-			printk(KERN_NOTICE "smc-mca.c: Presently autoprobing (not recommended) for a single card.\n");
-		}
-		if (register_netdev(dev) != 0)
-		{
-			printk(KERN_WARNING "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+
+		if (register_netdev(dev) != 0) {
 			if (found != 0) {	/* Got at least one. */
 				lock_8390_module();
 				return 0;
 			}
+			printk(KERN_NOTICE "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
 			return -ENXIO;
 		}
 		found++;
@@ -365,14 +487,13 @@
 {
 	int this_dev;
 
-	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++)
-	{
+	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) {
 		struct net_device *dev = &dev_ultra[this_dev];
-        	if (dev->priv != NULL)
-        	{
+		if (dev->priv != NULL) {
 			void *priv = dev->priv;
 			/* NB: ultra_close_card() does free_irq */
 			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+			mca_mark_as_unused(ei_status.priv);
 			release_region(ioaddr, ULTRA_IO_EXTENT);
 			unregister_netdev(dev);
 			kfree(priv);
@@ -381,7 +502,6 @@
 	unlock_8390_module();
 }
 #endif /* MODULE */
-
 
 /*
  * Local variables:
diff -u --recursive --new-file v2.3.18/linux/drivers/net/smc-mca.h linux/drivers/net/smc-mca.h
--- v2.3.18/linux/drivers/net/smc-mca.h	Thu Dec 12 06:51:10 1996
+++ linux/drivers/net/smc-mca.h	Sat Oct  2 07:38:27 1999
@@ -1,7 +1,7 @@
-/* 
-   djweis weisd3458@uni.edu 
-   most of this file was taken from ps2esdi.h
-*/
+/*
+ * djweis weisd3458@uni.edu
+ * most of this file was taken from ps2esdi.h
+ */
 
 struct {
   unsigned int base_addr;
@@ -25,6 +25,7 @@
 };
 
 #define MEM_MASK 64
+
 struct {
   unsigned char mem_index;
   unsigned long mem_start;
@@ -50,13 +51,11 @@
 
 #define IRQ_MASK 243
 struct {
-   unsigned char irq;
+   unsigned char new_irq;
+   unsigned char old_irq;
 } irq_table[] = {
-   {  3 }, 
-   {  4 },
-   { 10 },
-   { 14 }
+   {  3,  3 },
+   {  4,  4 },
+   { 10, 10 },
+   { 14, 15 }
 };
-
- 
-   
diff -u --recursive --new-file v2.3.18/linux/drivers/net/tlan.c linux/drivers/net/tlan.c
--- v2.3.18/linux/drivers/net/tlan.c	Wed Aug 18 11:36:43 1999
+++ linux/drivers/net/tlan.c	Wed Sep 29 15:32:15 1999
@@ -59,6 +59,13 @@
 static	int		duplex = 0;
 static	int		speed = 0;
 
+MODULE_PARM(aui, "i");
+MODULE_PARM(sa_int, "i");
+MODULE_PARM(duplex, "i");
+MODULE_PARM(speed, "i");
+MODULE_PARM(debug, "i");
+EXPORT_NO_SYMBOLS;
+
 #endif
 
 
@@ -352,7 +359,7 @@
 	
 	/* printk( "TLAN:  Found %d device(s).\n", TLanDevicesInstalled ); */
 
-    return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
+	return ( ( TLanDevicesInstalled > 0 ) ? 0 : -ENODEV );
 
 } /* init_module */
 
@@ -2528,13 +2535,13 @@
 
 	for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) {
 		TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
-		TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+		(void) TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
 		if ( data & i )
 			TLan_SetBit( TLAN_NET_SIO_MDATA, sio );
 		else
 			TLan_ClearBit( TLAN_NET_SIO_MDATA, sio );
 		TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
-		TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+		(void) TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
 	}
 
 } /* TLan_MiiSendData */
diff -u --recursive --new-file v2.3.18/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- v2.3.18/linux/drivers/net/via-rhine.c	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/via-rhine.c	Sat Oct  2 07:38:27 1999
@@ -109,7 +109,7 @@
 
 #define RUN_AT(x) (jiffies + (x))
 
-#if (LINUX_VERSION_CODE >= 0x20100)
+#ifdef MODULE
 char kernel_version[] = UTS_RELEASE;
 #else
 #ifndef __alpha__
@@ -1056,6 +1056,7 @@
 				np->rx_skbuff[entry] = NULL;
 			}
 			skb->protocol = eth_type_trans(skb, dev);
+			np->stats.rx_bytes+=skb->len;
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 			np->stats.rx_packets++;
diff -u --recursive --new-file v2.3.18/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.3.18/linux/drivers/net/wavelan.c	Thu Aug 26 13:05:38 1999
+++ linux/drivers/net/wavelan.c	Sat Oct  2 07:41:13 1999
@@ -2094,7 +2094,7 @@
 	  wrq->u.data.length = sizeof(struct iw_range);
 
 	  /* Set information in the range struct.  */
-	  range.throughput = 1.6 * 1024 * 1024;	/* don't argue on this ! */
+	  range.throughput = 1.6 * 1000 * 1000;	/* don't argue on this ! */
 	  range.min_nwid = 0x0000;
 	  range.max_nwid = 0xFFFF;
 
@@ -2240,7 +2240,10 @@
 
     case SIOCSIPQTHR:
       if(!suser())
-	return -EPERM;
+        {
+	  ret = -EPERM;
+	  break;
+	}
       psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
 	       (unsigned char *)&psa.psa_quality_thr, 1);
@@ -2259,7 +2262,10 @@
     case SIOCSIPHISTO:
       /* Verify that the user is root. */
       if(!suser())
-	return -EPERM;
+        {
+	  ret = -EPERM;
+	  break;
+	}
 
       /* Check the number of i v2.3.10cker. The rest is
@@  the user is.k. I'm stilAL;
diff &	ware ak) {
-		ma);
+		 rent_read{
-		ma);	 "(ync_map));
-
 {
+	store ak) {
ma);
+		 rent_read{
-.10ckeŢ*.10ckeŢ*fies;************->len);
-		iE_COUNODULE
-inCOUN0ckeŢ
-inCOUN0cke-{
-	int stae0,7 @@
  )    ps***sa_quaca.c	Sat tnt_read{
-		ma);	 "(ync_map));
-sa_write(ioaddr,1Ř@_window_se,ÓR the backlog
-   queu
       psa@_winh_typebactow_se,ÓR the backlog
EN, 01Ř@_window_te,ÓR,t