diff -u --recursive --new-file v2.0.34/linux/COPYING linux/COPYING
--- v2.0.34/linux/COPYING	Wed Dec  1 04:44:15 1993
+++ linux/COPYING	Mon Jul 13 13:47:25 1998
@@ -3,7 +3,7 @@
  services by normal system calls - this is merely considered normal use
  of the kernel, and does *not* fall under the heading of "derived work".
  Also note that the GPL below is copyrighted by the Free Software
- Foundation, but the instance of code that it refers to (the linux
+ Foundation, but the instance of code that it refers to (the Linux
  kernel) is copyrighted by me and others who actually wrote it.
 
 			Linus Torvalds
diff -u --recursive --new-file v2.0.34/linux/CREDITS linux/CREDITS
--- v2.0.34/linux/CREDITS	Mon Jul 13 13:46:24 1998
+++ linux/CREDITS	Mon Jul 13 13:47:25 1998
@@ -1594,11 +1594,11 @@
 S: Finland
 
 N: Roger E. Wolff
-E: wolff@dutecai.et.tudelft.nl
+E: R.E.Wolff@BitWizard.nl
 D: Written kmalloc/kfree
 D: Written Specialix IO8+ driver
-S: Oosterstraat 23
-S: 2611 TT Delft
+S: Van Bronckhorststraat 12 
+S: 2612 XV  Delft
 S: The Netherlands
 
 N: Frank Xia
diff -u --recursive --new-file v2.0.34/linux/Documentation/00-INDEX linux/Documentation/00-INDEX
--- v2.0.34/linux/Documentation/00-INDEX	Thu Jun  6 04:57:43 1996
+++ linux/Documentation/00-INDEX	Mon Jul 13 13:47:25 1998
@@ -14,6 +14,8 @@
 	- how the boss likes the C code in the kernel to look.
 Configure.help
 	- text file that is used for help when you run "make config"
+IO-mapping.txt
+	- how to access I/O mapped memory from within device drivers.
 SMP.txt
 	- notes, and "To Fix" list for multi-processor Linux. (see smp.tex)
 cdrom/
@@ -38,10 +40,16 @@
 	- info on the in-kernel binary support for Java(tm)
 locks.txt
 	- info on file locking implementations, flock() vs. fcntl(), etc.
+logo.gif
+	- GIF image of penguin as a linux logo (see logo.txt)
+logo.txt
+	- the who where and how of the penguin linux logo.
 magic-number.txt
 	- list of magic numbers used to mark/protect kernel data structures.
 mandatory.txt
 	- info on the linux implementation of Sys V mandatory file locking.
+memory-tuning.txt
+	- info on setting the /proc/sys/vm parameters to match your system.
 modules.txt
 	- short guide on how to make kernel parts into loadable modules
 networking/
@@ -50,6 +58,8 @@
 	- short guide on setting up a diskless box with NFS root filesystem
 oops-tracing.txt
 	- how to decode those nasty internal kernel error dump messages.
+paride.txt
+	- info on parallel port IDE devices (tapes, floppies, CD-ROMS, etc)
 ramdisk.txt
 	- short guide on how to set up and use the RAM disk.
 riscom8.txt
@@ -60,10 +70,14 @@
 	- short blurb on using SCSI support as a module.
 smp.tex
 	- TeX document describing implementation of Multiprocessor Linux
+specialix.txt
+	- info on hardware/driver for specialix IO8+ multiport serial card.
 svga.txt
 	- short guide on selecting video modes at boot via VGA BIOS.
 unicode.txt
 	- info on the Unicode character/font mapping used in Linux.
 watchdog.txt
 	- how to auto-reboot Linux if it has "fallen and can't get up". ;-)
+xterm-linux.xpm
+	- XPM format image/icon of penguin on an xterm (see logo.txt)
 
diff -u --recursive --new-file v2.0.34/linux/Documentation/CodingStyle linux/Documentation/CodingStyle
--- v2.0.34/linux/Documentation/CodingStyle	Wed Jan  3 21:43:40 1996
+++ linux/Documentation/CodingStyle	Mon Jul 13 13:47:25 1998
@@ -132,7 +132,7 @@
 complexity and indentation level of that function.  So, if you have a
 conceptually simple function that is just one long (but simple)
 case-statement, where you have to do lots of small things for a lot of
-different cases, it's ok to have a longer function. 
+different cases, it's OK to have a longer function. 
 
 However, if you have a complex function, and you suspect that a
 less-than-gifted first-year high-school student might not even
@@ -169,7 +169,7 @@
 
 		Chapter 6: You've made a mess of it
 
-That's ok, we all do.  You've probably been told by your long-time unix
+That's OK, we all do.  You've probably been told by your long-time Unix
 user helper that "GNU emacs" automatically formats the C sources for
 you, and you've noticed that yes, it does do that, but the defaults it
 uses are less than desirable (in fact, they are worse than random
diff -u --recursive --new-file v2.0.34/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.0.34/linux/Documentation/Configure.help	Mon Jul 13 13:46:24 1998
+++ linux/Documentation/Configure.help	Mon Jul 13 13:47:25 1998
@@ -320,6 +320,220 @@
   Documentation/modules.txt.
   It's pretty unlikely that you have one of these: say N.
 
+Parallel port IDE device support
+CONFIG_PARIDE
+  There are many external CD-ROM and disk devices that connect through
+  your computer's parallel port. Most of them are actually IDE devices
+  using a parallel port IDE adapter. This option enables the PARIDE
+  subsystem which contains drivers for many of these external drives.
+  Read linux/Documentation/paride.txt for more information.  If you 
+  built PARIDE support into your kernel,  you may still build the 
+  individual protocol modules and high-level drivers as loadable 
+  modules. If you build this support as a module, it will be called 
+  paride.o.  To use the PARIDE support, you must say Y or M here 
+  and also to at least one high-level driver (e.g. "Parallel port 
+  IDE disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI 
+  disks" etc.) and to at least one protocol driver (e.g. "ATEN 
+  EH-100 protocol", "MicroSolutions backpack protocol", "DataStor 
+  Commuter protocol" etc.).
+
+Parallel port IDE disks
+CONFIG_PARIDE_PD 
+  This option enables the high-level driver for IDE-type disk devices 
+  connected through a parallel port.  If you chose to build PARIDE 
+  support into your kernel, you may answer Y here to build in the 
+  parallel port IDE driver, otherwise you should answer M to build 
+  it as a loadable module.  The module will be called pd.o.  You 
+  must also have at least one parallel port protocol driver in your 
+  system.  Among the devices supported by this driver are the SyQuest 
+  EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack
+  hard drives from MicroSolutions.
+
+Parallel port ATAPI CD-ROMs
+CONFIG_PARIDE_PCD 
+  This option enables the high-level driver for ATAPI CD-ROM devices
+  connected through a parallel port. If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI CD-ROM driver, otherwise you should answer M to
+  build it as a loadable module. The module will be called pcd.o. You
+  must also have at least one parallel port protocol driver in your
+  system. Among the devices supported by this driver are the
+  MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If
+  you have such a CD-ROM drive, you should also say Y to "ISO9660
+  cdrom filesystem support" below, because that's the filesystem used
+  on CDROMs.
+
+Parallel port ATAPI disks
+CONFIG_PARIDE_PF 
+  This option enables the high-level driver for ATAPI disk devices
+  connected through a parallel port.  If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI disk driver, otherwise you should answer M
+  to build it as a loadable module.  The module will be called pf.o.
+  You must also have at least one parallel port protocol driver in
+  your system.  Among the devices supported by this driver are the
+  MicroSolutions backpack PD/CD drive and the Imation Superdisk
+  LS-120 drive.
+
+Parallel port ATAPI tapes
+CONFIG_PARIDE_PT
+  This option enables the high-level driver for ATAPI tape devices
+  connected through a parallel port.  If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI tape driver, otherwise you should answer M
+  to build it as a loadable module.  The module will be called pt.o.
+  You must also have at least one parallel port protocol driver in
+  your system.  Among the devices supported by this driver is the
+  parallel port version of the HP 5GB drive.
+
+Parallel port generic ATAPI devices
+CONFIG_PARIDE_PG
+  This option enables a special high-level driver for generic ATAPI
+  devices connected through a parallel port. The driver allows user
+  programs, such as cdrecord, to send ATAPI commands directly to a
+  device. If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the parallel port generic ATAPI driver, 
+  otherwise you should answer M to build it as a loadable module.
+  The module will be called pg.o.  You must also have at least one 
+  parallel port protocol driver in your system.  This driver 
+  implements an API loosely related to the generic SCSI driver.  
+  See /usr/include/linux/pg.h for details, or visit 
+  http://www.torque.net/parport/cdr.html for more information and
+  the required patches to cdrecord.
+
+ATEN EH-100 protocol
+CONFIG_PARIDE_ATEN 
+  This option enables support for the ATEN EH-100 parallel port IDE
+  protocol.  This protocol is used in some inexpensive low performance 
+  parallel port kits made in Hong Kong. If you chose to build PARIDE 
+  support into your kernel, you may answer Y here to build in the 
+  protocol driver, otherwise you should answer M to build it as a 
+  loadable module.  The module will be called aten.o.  You must also 
+  have a high-level driver for the type of device that you want to 
+  support.
+
+MicroSolutions backpack protocol
+CONFIG_PARIDE_BPCK 
+  This option enables support for the MicroSolutions backpack 
+  parallel port IDE protocol.  If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called bpck.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+DataStor Commuter protocol
+CONFIG_PARIDE_COMM 
+  This option enables support for the Commuter parallel port IDE 
+  protocol from DataStor.  If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called comm.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+DataStor EP-2000 protocol
+CONFIG_PARIDE_DSTR 
+  This option enables support for the EP-2000 parallel port IDE 
+  protocol from DataStor.  If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called dstr.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+Shuttle EPAT/EPEZ protocol
+CONFIG_PARIDE_EPAT 
+  This option enables support for the EPAT parallel port IDE 
+  protocol.  EPAT is a parallel port IDE adapter manufactured by
+  Shuttle Technology and widely used in devices from major vendors
+  such as Hewlett-Packard, SyQuest, Imation and Avatar. If you 
+  chose to build PARIDE support into your kernel, you may answer Y 
+  here to build in the protocol driver, otherwise you should answer M 
+  to build it as a loadable module.  The module will be called epat.o.  
+  You must also have a high-level driver for the type of device that 
+  you want to support.
+
+Shuttle EPIA protocol
+CONFIG_PARIDE_EPIA 
+  This option enables support for the (obsolete) EPIA parallel port 
+  IDE protocol from Shuttle Technology.  This adapter can still be found
+  in some no-name kits. If you chose to build PARIDE support into your 
+  kernel, you may answer Y here to build in the protocol driver,
+  otherwise you should answer M to build it as a loadable module.  
+  The module will be called epia.o.  You must also have a high-level 
+  driver for the type of device that you want to support.
+
+FIT TD-2000 protocol
+CONFIG_PARIDE_FIT2
+  This option enables support for the TD-2000 parallel port IDE protocol
+  from Fidelity International Technology.  This is a simple (low speed) 
+  adapter that is used in some portable hard drives.  If you chose to 
+  build PARIDE support into your kernel, you may answer Y here to 
+  build in the protocol driver, otherwise you should answer M to 
+  build it as a loadable module.  The module will be called fit2.o.
+  You must also have a high-level driver for the type of device 
+  that you want to support.
+
+FIT TD-3000 protocol
+CONFIG_PARIDE_FIT3
+  This option enables support for the TD-3000 parallel port IDE protocol
+  from Fidelity International Technology. This protocol is used in newer
+  models of their portable disk, CD-ROM and PD/CD devices.   If you chose
+  to build PARIDE support into your kernel, you may answer Y here to
+  build in the protocol driver, otherwise you should answer M to
+  build it as a loadable module.  The module will be called fit3.o.
+  You must also have a high-level driver for the type of device
+  that you want to support.
+
+FreeCom power protocol
+CONFIG_PARIDE_FRPW 
+  This option enables support for the Freecom power parallel port IDE 
+  protocol.  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will be 
+  called frpw.o.  You must also have a high-level driver for the type 
+  of device that you want to support.
+
+KingByte KBIC-951A/971A protocols
+CONFIG_PARIDE_KBIC 
+  This option enables support for the KBIC-951A and KBIC-971A parallel 
+  port IDE protocols from KingByte Information Corp.  KingByte's adapters
+  appear in many no-name portable disk and CD-ROM products, especially 
+  in Europe. If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you should 
+  answer M to build it as a loadable module.  The module will be called 
+  kbic.o.  You must also have a high-level driver for the type of device 
+  that you want to support.
+
+KT PHd protocol
+CONFIG_PARIDE_KTTI
+  This option enables support for the "PHd" parallel port IDE protocol
+  from KT Technology.  This is a simple (low speed) adapter that is
+  used in some 2.5" portable hard drives.  If you chose to build PARIDE 
+  support into your kernel, you may answer Y here to build in the 
+  protocol driver, otherwise you should answer M to build it as a 
+  loadable module.  The module will be called ktti.o.  You must also 
+  have a high-level driver for the type of device that you want to
+  support.
+
+OnSpec 90c20 protocol
+CONFIG_PARIDE_ON20 
+  This option enables support for the (obsolete) 90c20 parallel port 
+  IDE protocol from OnSpec (often marketed under the ValuStore brand
+  name).  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will 
+  be called on20.o.  You must also have a high-level driver for the 
+  type of device that you want to support.
+
+OnSpec 90c26 protocol
+CONFIG_PARIDE_ON26 
+  This option enables support for the 90c26 parallel port IDE protocol 
+  from OnSpec Electronics (often marketed under the ValuStore brand
+  name).  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will 
+  be called on26.o.  You must also have a high-level driver for the 
+  type of device that you want to support.
+
 Multiple devices driver support
 CONFIG_BLK_DEV_MD
   This driver lets you combine several harddisk partitions into one
@@ -349,6 +563,31 @@
   in and removed from the running kernel whenever you want), say M
   here and read Documentation/modules.txt. If unsure, say Y.
 
+RAID-1 (mirroring) mode
+CONFIG_MD_MIRRORING
+  A RAID-1 set consists of several disk drives which are exact copies
+  of each other. In the event of a mirror failture, the RAID driver
+  will continue to use the operational mirrors in the set, providing
+  an error free MD device to the higher levels of the kernel. In
+  a set with N drives, the available space is the capacity of a single
+  drive, and the set protects against a failture of (N - 1) drives.
+  raidtools, a set of user-space tools which create and maintain
+  RAID1/4/5 sets, is available at:
+  http://luthien.nuclecu.unam.mx/~miguel/raid
+
+RAID-4/RAID-5 mode
+CONFIG_MD_RAID5
+  A RAID-5 set of N drives with a capacity of C MB per drive provides
+  the capacity of C * (N - 1) drives, and protects against a failture
+  of a single drive. For a given sector (row) number, (N - 1) drives
+  contain data sectors, and one drive contains the parity protection.
+  For a RAID-4 set, the parity blocks are present on a single drive,
+  while a RAID-5 set distributes the parity accross the drives in one
+  of the available parity distribution methods.
+  raidtools, a set of user-space tools which create and maintain
+  RAID1/4/5 sets, is available at:
+  http://luthien.nuclecu.unam.mx/~miguel/raid
+
 Support for Deskstation RPC44
 CONFIG_DESKSTATION_RPC44
   This is a machine with a R4400 100 MHz CPU. To compile a Linux
@@ -1013,7 +1252,7 @@
 
 PC/TCP compatibility mode
 CONFIG_INET_PCTCP
-  If you have been having difficulties telneting to your Linux machine
+  If you have been having difficulties telnetting to your Linux machine
   from a DOS system that uses (broken) PC/TCP networking software (all
   versions up to OnNet 2.0) over your local ethernet try enabling this
   option.  Everyone else says N.
@@ -2045,18 +2284,112 @@
   running kernel whenever you want), say M here and read
   Documentation/modules.txt.
 
-BAYCOM ser12 and par96 kiss emulation driver for AX.25
+BAYCOM ser12 and par96 driver for AX.25
 CONFIG_BAYCOM
   This is an experimental driver for Baycom style simple amateur radio
   modems that connect to either a serial interface or a parallel
   interface. The driver supports the ser12 and par96 designs. To
-  configure the driver, use the setbaycom utility available from
-  http://www.ife.ee.ethz.ch/~sailer/ham/ham.html#lnxbay. For
-  information on the modems, see http://www.baycom.de and
-  drivers/char/README.baycom.  If you want to compile this as a module
-  ( = code which can be inserted in and removed from the running
-  kernel whenever you want), say M here and read
-  Documentation/modules.txt. This is recommended.
+  configure the driver, use the sethdlc utility available
+  in the standard ax25 utilities package. For information on the modems,
+  see http://www.baycom.de and drivers/net/README.baycom.
+  If you want to compile this as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. This is recommended.
+
+Sound card modem driver for AX.25
+CONFIG_SOUNDMODEM
+  This experimental driver allows a standard SoundBlaster or
+  WindowsSoundSystem compatible sound card to be used as a packet radio
+  modem (NOT as a telephone modem!), to send digital traffic over
+  amateur radio. 
+
+  To configure the driver, use the sethdlc, smdiag and smmixer
+  utilities available in the standard ax25 utilities package. For
+  information on how to key the transmitter, see
+  http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html (to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape) and
+  Documentation/networking/soundmodem.txt. 
+
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. This is recommended.
+  The module will be called soundmodem.o.
+
+Sound card modem support for SoundBlaster and compatible cards
+CONFIG_SOUNDMODEM_SBC
+  This option enables the soundmodem driver to use SoundBlaster and
+  compatible cards. If you have a dual mode card (i.e. a WSS cards
+  with a SoundBlaster emulation) you should say N here and Y to
+  "Sound card modem support for WSS and Crystal cards", below, because
+  this usually results in better performance. This option also supports
+  SB16/32/64 in full duplex mode.
+
+Sound card modem support for WSS and Crystal cards
+CONFIG_SOUNDMODEM_WSS
+  This option enables the soundmodem driver to use WindowsSoundSystem
+  compatible cards. These cards feature a codec chip from either
+  Analog Devices (such as AD1848, AD1845, AD1812) or Crystal
+  Semiconductors (such as CS4248, CS423x). This option also supports
+  the WSS full duplex operation which currently works with Crystal
+  CS423x chips. If you don't need full duplex operation, do not enable
+  it to save performance.
+
+Sound card modem support for 1200 baud AFSK modulation
+CONFIG_SOUNDMODEM_AFSK1200
+  This option enables the soundmodem driver 1200 baud AFSK modem, 
+  compatible to popular modems using TCM3105 or AM7911. The demodulator
+  requires about 12% of the CPU power of a Pentium 75 CPU per channel.
+
+Sound card modem support for 2400 baud AFSK modulation (7.3728MHz crystal)
+CONFIG_SOUNDMODEM_AFSK2400_7
+  This option enables the soundmodem driver 2400 baud AFSK modem,
+  compatible to TCM3105 modems (over-)clocked with a 7.3728MHz
+  crystal.  Note that the availability of this driver does _not_ imply
+  that I recommend building such links. It is only here since users
+  especially in eastern Europe have asked me to do so. In fact this
+  modulation scheme has many disadvantages, mainly its incompatibility
+  with many transceiver designs and the fact that the TCM3105 (if
+  used) is operated widely outside its specifications.
+
+Sound card modem support for 2400 baud AFSK modulation (8MHz crystal)
+CONFIG_SOUNDMODEM_AFSK2400_8
+  This option enables the soundmodem driver 2400 baud AFSK modem,
+  compatible to TCM3105 modems (over-)clocked with an 8MHz crystal.
+  Note that the availability of this driver does _not_ imply that I
+  recommend building such links. It is only here since users
+  especially in eastern Europe have asked me to do so. In fact this
+  modulation scheme has many disadvantages, mainly its incompatibility
+  with many transceiver designs and the fact that the TCM3105 (if
+  used) is operated widely outside its specifications.
+
+Sound card modem support for 2666 baud AFSK modulation
+CONFIG_SOUNDMODEM_AFSK2666
+  This option enables the soundmodem driver 2666 baud AFSK modem.
+  This modem is experimental, and not compatible to anything
+  else I know of.
+
+Sound card modem support for 4800 baud 8PSK modulation
+CONFIG_SOUNDMODEM_PSK4800
+  This option enables the soundmodem driver 4800 baud 8PSK modem.
+  This modem is experimental, and not compatible to anything
+  else I know of.
+
+Sound card modem support for 4800 baud HAPN-1 modulation
+CONFIG_SOUNDMODEM_HAPN4800
+  This option enables the soundmodem driver 4800 baud HAPN-1
+  compatible modem. This modulation seems to be widely used 'down
+  under' and in the Netherlands. Here, nobody uses it, so I could not
+  test if it works.  It is compatible to itself, however :-)
+
+Sound card modem support for 9600 baud FSK G3RUH modulation
+CONFIG_SOUNDMODEM_FSK9600
+  This option enables the soundmodem driver 9600 baud FSK modem,
+  compatible to the G3RUH standard. The demodulator requires about 4%
+  of the CPU power of a Pentium 75 CPU per channel. You can say Y to
+  both 1200 baud AFSK and 9600 baud FSK if you want (but obviously you
+  can only use one protocol at a time, depending on what the other end
+  can understand).
 
 PLIP (parallel port) support
 CONFIG_PLIP
@@ -4324,10 +4657,10 @@
 # LocalWords:  ipppd syncppp RFC MPP VJ downloaded icn NICCY Creatix shmem ufr
 # LocalWords:  ibp md ARCnet ether encap NDIS arcether ODI Amigas AmiTCP NetBSD
 # LocalWords:  initrd tue util DES funet des OnNet BIOSP smc Travan Iomega CMS
-# LocalWords:  FC DC dc PPA IOMEGA's ppa RNFS FMV Fujitsu ARPD arpd loran layes
+# LocalWords:  FC DC dc PPA ppa RNFS FMV Fujitsu ARPD arpd loran layes
 # LocalWords:  FRAD indiana framerelay DLCI DCLIs Sangoma SDLA mrouted sync sec
 # LocalWords:  Starmode Metricom MosquitoNet mosquitonet kbit nfsroot Digiboard
-# LocalWords:  DIGI Xe Xeve digiboard UMISC touchscreens mtu ethernets HBAs MEX
+# LocalWords:  DIGI Xe Xeve digiboard UMISC touchscreens mtu HBAs MEX
 # LocalWords:  Shifflett netcom js jshiffle WIC DECchip ELCP EtherPower dst RTC
 # LocalWords:  rtc SMP lp Digi Intl RightSwitch DGRS dgrs AFFS Amiga UFS SDL AP
 # LocalWords:  Solaris RISCom riscom syncPPP PCBIT pcbit sparc anu au artoo ufs
@@ -4336,7 +4669,7 @@
 # LocalWords:  Bernd informatik rwth aachen uae affs multihosting bytecode java
 # LocalWords:  applets applet JDK ncsa cabi SNI Alphatronix readme LANs scarab
 # LocalWords:  winsock RNIS caltech OSPF honour Honouring Mbit Localtalk DEFRAG
-# LocalWords:  localtalk download Packetwin Baycom baycom interwork ascii JNT
+# LocalWords:  download Packetwin Baycom baycom interwork ascii JNT
 # LocalWords:  Camtec proxying indyramp defragment defragmented UDP FAS FASXX
 # LocalWords:  FastSCSI SIO FDC qlogicfas QLogic qlogicisp setbaycom ife ee LJ
 # LocalWords:  ethz ch Travelmates ProAudioSpectrum ProAudio SoundMan SB SBPro
diff -u --recursive --new-file v2.0.34/linux/Documentation/IO-mapping.txt linux/Documentation/IO-mapping.txt
--- v2.0.34/linux/Documentation/IO-mapping.txt	Mon Sep 30 07:05:45 1996
+++ linux/Documentation/IO-mapping.txt	Mon Jul 13 13:47:25 1998
@@ -56,13 +56,13 @@
 where all the addresses actually point to the same thing, it's just seen 
 through different translations..
 
-Similarly, on the alpha, the normal translation is
+Similarly, on the Alpha, the normal translation is
 
  physical address:	0
  virtual address:	0xfffffc0000000000
  bus address:		0x40000000
 
-(but there are also alpha's where the physical address and the bus address
+(but there are also Alphas where the physical address and the bus address
 are the same). 
 
 Anyway, the way to look up all these translations, you do
@@ -169,7 +169,7 @@
 
 Ok, that just about covers the basics of accessing IO portably.  Questions?
 Comments? You may think that all the above is overly complex, but one day you
-might find yourself with a 500MHz alpha in front of you, and then you'll be
+might find yourself with a 500 MHz Alpha in front of you, and then you'll be
 happy that your driver works ;)
 
 Note that kernel versions 2.0.x (and earlier) mistakenly called the
diff -u --recursive --new-file v2.0.34/linux/Documentation/SMP.txt linux/Documentation/SMP.txt
--- v2.0.34/linux/Documentation/SMP.txt	Mon May  6 02:26:01 1996
+++ linux/Documentation/SMP.txt	Mon Jul 13 13:47:25 1998
@@ -19,7 +19,7 @@
 o	Clean up warnings/volatiles.
 o	Fix load_TR() for non contiguous processor ids
 o	Iterate over the slave timer requests if one is lost (keep a count per cpu)
-o	Distribute irq's (locking present just needs the 82489 to be asked
+o	Distribute IRQs (locking present just needs the 82489 to be asked
 	nicely).
 o	486 startup code.
 o	How to handle mixed FPU/non FPU processors.
diff -u --recursive --new-file v2.0.34/linux/Documentation/cdrom/sonycd535 linux/Documentation/cdrom/sonycd535
--- v2.0.34/linux/Documentation/cdrom/sonycd535	Sat Jul  1 09:05:58 1995
+++ linux/Documentation/cdrom/sonycd535	Mon Jul 13 13:47:25 1998
@@ -1,7 +1,7 @@
               README FOR LINUX SONY CDU-535/531 DRIVER
               ========================================
 
-This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux.
+This is the Sony CDU-535 (and 531) driver version 0.7 for Linux.
 I do not think I have the documentation to add features like DMA support
 so if anyone else wants to pursue it or help me with it, please do.
 (I need to see what was done for the CDU-31A driver -- perhaps I can
diff -u --recursive --new-file v2.0.34/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.0.34/linux/Documentation/devices.txt	Mon Jul 13 13:46:24 1998
+++ linux/Documentation/devices.txt	Mon Jul 13 13:47:25 1998
@@ -261,7 +261,7 @@
 		      ...
 
 		The metadisk driver is used to span a
-		filesystem across multiple physical disks.
+		file system across multiple physical disks.
 
  10 char	Non-serial mice, misc features
 		  0 = /dev/logibm	Logitech bus mouse
@@ -345,11 +345,11 @@
 		 33 = /dev/patmgr1	Sequencer patch manager
 		 34 = /dev/midi02	Third MIDI port
 		 50 = /dev/midi03	Fourth MIDI port
-    block	BIOS harddrive callback support
-		  0 = /dev/dos_hda	First BIOS harddrive whole disk
-		 64 = /dev/dos_hdb	Second BIOS harddrive whole disk
-		128 = /dev/dos_hdc	Third BIOS harddrive whole disk
-		192 = /dev/dos_hdd	Fourth BIOS harddrive whole disk
+    block	BIOS hard drive callback support
+		  0 = /dev/dos_hda	First BIOS hard drive whole disk
+		 64 = /dev/dos_hdb	Second BIOS hard drive whole disk
+		128 = /dev/dos_hdc	Third BIOS hard drive whole disk
+		192 = /dev/dos_hdd	Fourth BIOS hard drive whole disk
 
 		Partitions are handled in the same way as IDE disks
 		(see major number 3).
@@ -854,7 +854,7 @@
 /dev/cdwriter	CD-writer	symbolic	Current CD-writer device
 /dev/scanner	scanner		symbolic	Current scanner device
 /dev/modem	modem port	symbolic	Current dialout device
-/dev/root	root device	symbolic	Current root filesystem
+/dev/root	root device	symbolic	Current root file system
 /dev/swap	swap device	symbolic	Current swap device
 
 /dev/modem should not be used for a modem which supports dialin as
diff -u --recursive --new-file v2.0.34/linux/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt
--- v2.0.34/linux/Documentation/filesystems/affs.txt	Wed Jul 24 23:08:28 1996
+++ linux/Documentation/filesystems/affs.txt	Mon Jul 13 13:47:25 1998
@@ -121,7 +121,7 @@
 Although the Amiga and Linux file systems resemble each other, there
 are some, not always subtle, differences. One of them becomes apparent
 with symbolic links. While Linux has a file system with exactly one
-root directory, the Amiga has a seperate root directory for each
+root directory, the Amiga has a separate root directory for each
 file system (i. e. partition, floppy disk, ...). With the Amiga,
 these entities are called "volumes". They have symbolic names which
 can be used to access them. Thus, symbolic links can point to a
diff -u --recursive --new-file v2.0.34/linux/Documentation/ide.txt linux/Documentation/ide.txt
--- v2.0.34/linux/Documentation/ide.txt	Mon Aug  4 11:45:55 1997
+++ linux/Documentation/ide.txt	Mon Jul 13 13:47:25 1998
@@ -135,7 +135,7 @@
 Note that the first parameter reserves 8 contiguous ioports, whereas the
 second value denotes a single ioport. If in doubt, do a 'cat /proc/ioports'.
 
-In all probability the device uses these ports and irqs if it is attached
+In all probability the device uses these ports and IRQs if it is attached
 to the appropriate ide channel.  Pass the parameter for the correct ide
 channel to the kernel, as explained above.
 
@@ -180,7 +180,7 @@
 
 Courtesy of Scott Snyder, the driver supports ATAPI cdrom drives
 such as the NEC-260 and the new MITSUMI triple/quad speed drives.
-Such drives will be identified at boot time, just like a harddisk.
+Such drives will be identified at boot time, just like a hard disk.
 
 If for some reason your cdrom drive is *not* found at boot time, you can force
 the probe to look harder by supplying a kernel command line parameter
@@ -190,7 +190,7 @@
 or
 	hdd=cdrom	/* hdd = "slave" on second interface */
 
-For example, a GW2000 system might have a harddrive on the primary
+For example, a GW2000 system might have a hard drive on the primary
 interface (/dev/hda) and an IDE cdrom drive on the secondary interface
 (/dev/hdc).  To mount a CD in the cdrom drive, one would use something like:
 
@@ -258,6 +258,7 @@
 				older/odd IDE drives.
  "hdx=slow"		: insert a huge pause after each access to the data
 				port. Should be used only as a last resort.
+ "hdx=ide-scsi"		: use the ide-scsi driver for hdx
 
  "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in Mhz,
 				where "xx" is between 20 and 66 inclusive,
@@ -428,7 +429,7 @@
 must reside within the first 1024 cylinders of the drive.  If your linux
 root partition is *not* completely within the first 1024 cyls (quite common),
 then you can use LILO to boot linux from files on your DOS partition
-by doing the following after installing slackware (or whatever):
+by doing the following after installing Slackware (or whatever):
 
 	0. Boot from the "boot floppy" created during the installation
         1. Mount your DOS partition as /dos (and stick it in /etc/fstab)
diff -u --recursive --new-file v2.0.34/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.0.34/linux/Documentation/isdn/README	Mon Aug  4 17:33:59 1997
+++ linux/Documentation/isdn/README	Mon Jul 13 13:47:25 1998
@@ -353,7 +353,7 @@
    command "isdnctrl addphone <InterfaceName> in <caller-id>"
    Euro-ISDN does not transmit the leading '0' of the caller-id for an
    incoming call, therefore you should configure it accordingly.
-   If the real number for the dialout e.g. is "09311234567" the the number
+   If the real number for the dialout e.g. is "09311234567" the number
    to configure here is "9311234567". The pattern-match function 
    works similar to the shell mechanism.
 
@@ -374,7 +374,7 @@
    Returns the EAZ of an interface.
 
    "isdnctrl delphone <InterfaceName> in|out <number>" 
-   Deletes a number from one of the the access-lists of the interface.
+   Deletes a number from one of the access-lists of the interface.
 
    "isdnctrl delif <InterfaceName>" 
    Removes the interface (and possible slaves) from the kernel.
diff -u --recursive --new-file v2.0.34/linux/Documentation/isdn/README.sc linux/Documentation/isdn/README.sc
--- v2.0.34/linux/Documentation/isdn/README.sc	Mon Aug  4 17:33:59 1997
+++ linux/Documentation/isdn/README.sc	Mon Jul 13 13:47:25 1998
@@ -42,7 +42,7 @@
 ---------------
 
 The revision 2 Linux driver for SpellCaster ISA ISDN adapters is built
-upon ISDN4Linux available seperately or as included in Linux 2.0 and later.
+upon ISDN4Linux available separately or as included in Linux 2.0 and later.
 The driver will support a maximum of 4 adapters in any one system of any
 type including DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI for a
 maximum of 92 channels for host. The driver is supplied as a module in
@@ -81,7 +81,7 @@
 	  the driver and at the same time doubled the number of I/O ports
 	  probed increasing the likelyhood of finding an adapter.
 	- We now support all ISA adapter models with a single driver instead
-	  of seperate drivers for each model. The revision 2 driver supports
+	  of separate drivers for each model. The revision 2 driver supports
 	  the DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI in any
 	  combination up to a maximum of four adapters per system.
 	- On board PPP protocol support has been removed in favour of the
diff -u --recursive --new-file v2.0.34/linux/Documentation/memory-tuning.txt linux/Documentation/memory-tuning.txt
--- v2.0.34/linux/Documentation/memory-tuning.txt	Mon Aug 18 20:16:47 1997
+++ linux/Documentation/memory-tuning.txt	Mon Jul 13 13:47:25 1998
@@ -21,8 +21,8 @@
 
 The values selected as boot defaults are the following:  For a
 machine with n>=8 Megabytes of memory, set min_free_pages = n*2,
-free_pages_low = n*3 and free_pages_high = n*4.  Machines with less
-than 8 Megabytes or less as if they had 8 Megabytes.
+free_pages_low = n*3 and free_pages_high = n*4.  Machines with
+8 Megabytes or less behave as if they had 8 Megabytes.
 
 If "out of memory" errors sometimes occur, or if your machine does lots
 of networking, increasing min_free_pages to 64 or more may be a good
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/00-INDEX linux/Documentation/networking/00-INDEX
--- v2.0.34/linux/Documentation/networking/00-INDEX	Tue Apr  8 08:47:45 1997
+++ linux/Documentation/networking/00-INDEX	Mon Jul 13 13:47:26 1998
@@ -12,8 +12,14 @@
 	- info on the using the arcnet driver itself.
 ax25.txt
 	- info on using AX.25 and NET/ROM code for Linux
+depca.txt
+	- the Digital DEPCA/EtherWORKS DE1?? and DE2?? LANCE Ethernet driver
 framerelay.txt
 	- info on using Frame Relay/Data Link Connection Identifier (DLCI).
+ip_dynaddr.txt
+	- info on using dynamic IP addressing for diald and IP masquerading.
+ipx.txt
+	- info on the two possible modes of kernel IPX network support.
 masquerading.txt
 	- using IP masquerading, multiple machines using a single IP address.
 ncsa-telnet
@@ -22,10 +28,16 @@
 	- info and "insmod" parameters for all network driver modules.
 ppp.txt
 	- info on what software you should use to run PPP.
+so_bindtodevice.txt
+	- info on binding a socket to a specific device/interface.
 tcp.txt
 	- short blurb on how TCP output takes place.
+tlan.FAQ
+	- freq. asked questions on the TLAN (Compaq Netelligent) driver.
+tlan.README
+	- usage and supported hardware info for the Thunderlan driver.
 tulip.txt
-	- info on using DEC 21040/21041/21140 based PCI ethernet cards.
+	- info on using DEC 21040/21041/21140 based PCI Ethernet cards.
 vortex.txt
 	- info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) e'net cards.
 z8530drv.txt
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt
--- v2.0.34/linux/Documentation/networking/arcnet-hardware.txt	Mon May  6 02:26:01 1996
+++ linux/Documentation/networking/arcnet-hardware.txt	Mon Jul 13 13:47:26 1998
@@ -17,19 +17,19 @@
 INTRODUCTION TO ARCNET
 ----------------------
 
-ARCnet is a network type which works in a way similar to popular "ethernet"
+ARCnet is a network type which works in a way similar to popular Ethernet
 networks but which is also different in some very important ways.
 
-First of all, you can get ARCnet cards in at least two speeds: 2.5Mbps
-(slower than ethernet) and 100Mbps (faster than normal ethernet).  In fact,
+First of all, you can get ARCnet cards in at least two speeds: 2.5 Mbps
+(slower than Ethernet) and 100 Mbps (faster than normal Ethernet).  In fact,
 there are others as well, but these are less common.  The different hardware
 types, as far as I'm aware, are not compatible and so you cannot wire a
-100Mbps card to a 2.5Mbps card, and so on.  From what I hear, my driver does
-work with 100Mbps cards, but I haven't been able to verify this myself,
-since I only have the 2.5Mbps variety.  It is probably not going to saturate
-your 100Mbps card.  Stop complaining :)
+100 Mbps card to a 2.5 Mbps card, and so on.  From what I hear, my driver does
+work with 100 Mbps cards, but I haven't been able to verify this myself,
+since I only have the 2.5 Mbps variety.  It is probably not going to saturate
+your 100 Mbps card.  Stop complaining :)
 
-You also cannot connect an ARCnet card to any kind of ethernet card and
+You also cannot connect an ARCnet card to any kind of Ethernet card and
 expect it to work.  
 
 There are two "types" of ARCnet - STAR topology and BUS topology.  This
@@ -41,7 +41,7 @@
 Once you get past these little stumbling blocks, ARCnet is actually quite a
 well-designed standard.  It uses something called "modified token passing"
 which makes it completely incompatible with so-called "Token Ring" cards,
-but which makes transfers much more reliable than ethernet does.  In fact,
+but which makes transfers much more reliable than Ethernet does.  In fact,
 ARCnet will guarantee that a packet arrives safely at the destination, and
 even if it can't possibly be delivered properly (ie. because of a cable
 break, or because the destination computer does not exist) it will at least
@@ -53,9 +53,9 @@
 
 In addition, all known ARCnet cards have an (almost) identical programming
 interface.  This means that with one "arcnet" driver you can support any
-card; whereas, with ethernet, each manufacturer uses what is sometimes a
+card; whereas, with Ethernet, each manufacturer uses what is sometimes a
 completely different programming interface, leading to a lot of different,
-sometimes very similar, ethernet drivers.  Of course, always using the same
+sometimes very similar, Ethernet drivers.  Of course, always using the same
 programming interface also means that when high-performance hardware
 facilities like PCI busmastering DMA appear, it's hard to take advantage of
 them.  Let's not go into that.
@@ -63,10 +63,10 @@
 One thing that makes ARCnet cards difficult to program for, however, is the
 limit on their packet sizes; standard ARCnet can only send packets that are
 up to 508 bytes in length.  This is smaller than the internet "bare minimum"
-of 576 bytes, let alone the ethernet MTU of 1500.  To compensate, an extra
+of 576 bytes, let alone the Ethernet MTU of 1500.  To compensate, an extra
 level of encapsulation is defined by RFC1201, which I call "packet
 splitting," that allows "virtual packets" to grow as large as 64K each,
-although they are generally kept down to the ethernet-style 1500 bytes.
+although they are generally kept down to the Ethernet-style 1500 bytes.
 
 For more information on the advantages and disadvantages (mostly the
 advantages) of ARCnet networks, you might try the "ARCnet Trade Association"
@@ -319,7 +319,7 @@
 	- Avery's favourite: 0xD0000
 
   - the station address:  Every ARCnet card has its own "unique" network
-    address from 0 to 255.  Unlike ethernet, you can set this address
+    address from 0 to 255.  Unlike Ethernet, you can set this address
     yourself with a jumper or switch (or on some cards, with special
     software).  Since it's only 8 bits, you can only have 254 ARCnet cards
     on a network.  DON'T use 0 or 255, since these are reserved (although
@@ -1169,7 +1169,7 @@
 
 DIP Switches:
 
-	The dipswitches accessible on the accessible end of the card while
+	The DIP switches accessible on the accessible end of the card while
         it is installed, is used to set the arcnet address.  There are 8
         switches.  Use an address from 1 to 254.
 
@@ -1184,7 +1184,7 @@
 	11111110	1
 	11111111	0	(Don't use this!)
 
-	There is another dipswitch array of 8 switches at the top of the
+	There is another array of eight DIP switches at the top of the
         card.  There are five labelled MS0-MS4 which seem to control the
         memory address, and another three labelled IO0-IO2 which seem to
         control the base I/O address of the card.
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt
--- v2.0.34/linux/Documentation/networking/arcnet.txt	Wed Sep 11 07:57:12 1996
+++ linux/Documentation/networking/arcnet.txt	Mon Jul 13 13:47:26 1998
@@ -147,10 +147,10 @@
 
 Go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be
 available where you picked up this driver.  Think of your ARCnet as a
-souped-up (or down, as the case may be) ethernet card.
+souped-up (or down, as the case may be) Ethernet card.
 
 By the way, be sure to change all references from "eth0" to "arc0" in the
-HOWTOs.  Remember that ARCnet isn't a "true" ethernet, and the device name
+HOWTOs.  Remember that ARCnet isn't a "true" Ethernet, and the device name
 is DIFFERENT.
 
 
@@ -170,7 +170,7 @@
 How do I get it to work with...?
 --------------------------------
 
-NFS: Should be fine linux->linux, just pretend you're using ethernet cards. 
+NFS: Should be fine Linux->Linux, just pretend you're using Ethernet cards. 
         oak.oakland.edu:/simtel/msdos/nfs has some nice DOS clients.  There
         is also a DOS-based NFS server called SOSS.  It doesn't multitask
         quite the way Linux does (actually, it doesn't multitask AT ALL) but
@@ -196,7 +196,7 @@
 
 LAN Manager and Windows for Workgroups: These programs use protocols that
         are incompatible with the internet standard.  They try to pretend
-        the cards are ethernet, and confuse everyone else on the network. 
+        the cards are Ethernet, and confuse everyone else on the network. 
         
         However, v2.00 and higher of the Linux ARCnet driver supports this
         protocol via the 'arc0e' device.  See the section on "Multiprotocol
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/ax25.txt linux/Documentation/networking/ax25.txt
--- v2.0.34/linux/Documentation/networking/ax25.txt	Wed Jul  5 03:06:27 1995
+++ linux/Documentation/networking/ax25.txt	Mon Jul 13 13:47:26 1998
@@ -1,50 +1,16 @@
-This is version 029 of the new AX.25 and NET/ROM code for Linux. It
-incorporates many enhancements since the last release, notably the rewriting
-of the connected mode IP code and the IP over NET/ROM code. The opportunity
-has been taken to add the G8BPQ NET/ROM extensions and to add BPQ Ethernet
-support. The latter has been much eased by the use of the new variable
-length header code by Alan Cox.
+To use the amateur radio protocols within Linux you will need to get a
+suitable copy of the AX.25 Utilities. More detailed information about these
+and associated programs can be found on http://www.cs.nott.ac.uk/~jsn/.
+
+For more information about the AX.25, NET/ROM and ROSE protocol stacks, see
+the AX25-HOWTO written by Terry Dawson <terry@perf.no.itg.telstra.com.au>
+who is also the AX.25 Utilities maintainer.
+
+There is an active mailing list for discussing Linux amateur radio matters
+called linux-hams. To subscribe to it, send a message to
+Majordomo@vger.rutgers.edu with the words "subscribe linux-hams" in the body
+of the message, the subject field is ignored.
 
-To use the BPQ Ethernet option, first up the ethernet interface in the usual
-manner, the IP address of the interface is not that important but it will
-be required for the ARP table. Next create an ARP entry in the ARP table of
-type ax25 for the interface binding it to an AX.25 callsign, this callsign
-will be the callsign of that interface. By default BPQ Ethernet uses a
-multi-cast address, this implementation does not, instead the standard
-ethernet broadcast address is used. Therefore the NET.CFG file for the
-ODI driver should look similar to this:
+Jonathan G4KLX
 
------------------------------- cut here ------------------------------------
-
-LINK SUPPORT
-
-	MAX STACKS 1
-	MAX BOARDS 1
-
-LINK DRIVER E2000			; or other MLID to suit your card
-
-	INT 10				;
-	PORT 300			; to suit your card
-
-	FRAME ETHERNET_II
-
-	PROTOCOL BPQ 8FF ETHERNET_II	; required for BPQ - can change PID
-
-BPQPARMS				; optional - only needed if you want
-					; to override the default target addr
-
-	ETH_ADDR  FF:FF:FF:FF:FF:FF	; Target address
-
------------------------------ cut here -------------------------------------
-
-The above configuration assumes that only BPQ Ethernet is being used.
-
-It is not possible to run IP over AX.25 on the BPQ Ethernet port. To simply
-route IP frames to (say) eth0 would create standard ethernet IP frames and
-completely bypass the AX.25 code. However it is possible to use IP over
-NET/ROM across a BPQ Ethernet link, the performance of such a system is
-very acceptable indeed.
-
-Jonathan Naylor G4KLX
-
-g4klx@amsat.org
+jsn@cs.nott.ac.uk
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/baycom.txt linux/Documentation/networking/baycom.txt
--- v2.0.34/linux/Documentation/networking/baycom.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/baycom.txt	Mon Jul 13 13:47:26 1998
@@ -0,0 +1,131 @@
+		    LINUX DRIVER FOR BAYCOM MODEMS
+
+       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This document describes the Linux Kernel Driver for simple Baycom style
+amateur radio modems. The driver supports the following modems:
+
+ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+        of a modulator/demodulator chip, usually a TI TCM3105. The computer
+        is responsible for regenerating the receiver bit clock, as well as
+        for handling the HDLC protocol. The modem connects to a serial port,
+        hence the name. Since the serial port is not used as an async serial
+        port, the kernel driver for serial ports cannot be used, and this
+        driver only supports standard serial hardware (8250, 16450, 16550)
+
+par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+        The modem does all the filtering and regenerates the receiver clock.
+        Data is transferred from and to the PC via a shift register.
+        The shift register is filled with 16 bits and an interrupt is signalled.
+        The PC then empties the shift register in a burst. This modem connects
+        to the parallel port, hence the name. The modem leaves the 
+        implementation of the HDLC protocol and the scrambler polynomial to
+        the PC.
+
+picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
+        is protocol compatible to par96, but uses only three low power ICs
+        and can therefore be fed from the parallel port and does not require
+        an additional power supply. Furthermore, it incorporates a carrier
+        detect circuitry.
+
+All of the above modems only support half duplex communications. However,
+the driver supports the KISS (see below) fullduplex command. It then simply
+starts to send as soon as there's a packet to transmit and does not care
+about DCD, i.e. it starts to send even if there's someone else on the channel.
+This command is required by some implementations of the DAMA channel 
+access protocol.
+
+
+The Interface of the driver
+
+Unlike previous drivers, the driver is no longer a character device,
+but it is now a true kernel network interface. Installation is therefore
+simple. Once installed, four interfaces named bc[0-3] are available.
+sethdlc from the ax25 utilities may be used to set driver states etc.
+Users of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples: (use either method, not both)
+  insmod baycom mode="ser12*" iobase=0x3f8 irq=4
+  sethdlc -i bc0 -p type "ser12*" io 0x3f8 irq 4
+
+Both lines configure the first port to drive a ser12 modem at the first
+serial port (COM1 under DOS). The star ('*') instructs the driver to use
+the software DCD algorithm (see below).
+
+  insmod baycom mode="par96*" iobase=0x378 irq=7
+  sethdlc -i bc0 -p type "par96*" io 0x378 irq 7
+
+Both lines configure the first port to drive a par96 or par97 modem at the
+first parallel port (LPT1 under DOS). options=1 instructs the driver to use
+the software DCD algorithm (see below).
+  
+  insmod baycom mode="par96" iobase=0x278 irq=5
+  sethdlc -i bc0 -p type "par96" io 0x278 irq 5
+
+Both lines configure the first port to drive a picpar modem at the
+second parallel port (LPT2 under DOS). The driver uses the hardware DCD
+signalled by the picpar modem (see below).
+  
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+
+Hardware DCD versus Software DCD
+
+To avoid collisions on the air, the driver must know when the channel is
+busy. This is the task of the DCD circuitry/software. The driver may either
+utilise a software DCD algorithm (a star after the mode string) or use a DCD
+signal from the hardware (no star).
+
+ser12:  if software DCD is utilised, the radio's squelch should always be
+        open. It is highly recommended to use the software DCD algorithm,
+        as it is much faster than most hardware squelch circuitry. The
+        disadvantage is a slightly higher load on the system.
+
+par96:  the software DCD algorithm for this type of modem is rather poor.
+        The modem simply does not provide enough information to implement
+        a reasonable DCD algorithm in software. Therefore, if your radio
+        feeds the DCD input of the par96 modem, the use of the hardware
+        DCD circuitry is recommended.
+
+picpar: the picpar modem features a builtin DCD hardware, which is highly
+        recommended.
+
+
+
+Compatibility with the rest of the Linux kernel
+
+The serial driver, the line printer (lp) driver and the baycom driver compete
+for the same hardware resources. Of course only one driver can access a given
+interface at a time. The serial driver grabs all interfaces it can find at
+startup time. Therefore the baycom driver subsequently won't be able to
+access a serial port. You might therefore find it necessary to release
+a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
+# is the number of the interface. The baycom driver does not reserve any
+port at startup, unless one is specified on the 'insmod' command line. Another
+method to solve the problem is to compile all three drivers as modules and
+leave it to kerneld to load the correct driver depending on the application.
+
+
+Further reading
+
+Please take a look at http://www.ife.ee.ethz.ch/~sailer/ham/linux/hdlc.html
+for further informations on the driver.
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+Packet Radio: hb9jnx@hb9w.che.eu
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/soundmodem.txt linux/Documentation/networking/soundmodem.txt
--- v2.0.34/linux/Documentation/networking/soundmodem.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/soundmodem.txt	Mon Jul 13 13:47:26 1998
@@ -0,0 +1,96 @@
+	     LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS
+
+       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss)
+compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK
+AX.25 packet radio modems. Only half duplex operation is supported by
+the SoundBlaster driver. Fullduplex is supported for WSS chipsets capable
+of addressing two DMA channels. The driver needs some processing power!
+A 486DX/2 66MHz is a minimum requirement, otherwise interactive performance
+of the computer may become sluggish.
+
+
+The Interface of the driver
+
+The driver provides a kernel network drivers named sm[0-3]. sethdlc 
+from the ax25 utilities may be used to set driver states etc. Users
+of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Some sound cards need to be initialized before they operate in either
+SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this;
+you may use the standard linux sound driver to initialize the soundcard;
+compile it as a module, and do
+  insmod sound
+  rmmod sound
+The soundcard should then be initialized correctly. isapnptools is another
+package that can configure PnP soundcards. If your soundcard contains
+a Crystal CS423x chip, you may also try the setcrystal utility in the
+ax25-utilities package.
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the sethdlc
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples: (use either method, not both)
+  insmod soundmodem mode="sbc:afsk1200" iobase=0x220 irq=5 dma=1
+  sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1
+
+Both lines configure the first port to drive a soundblaster card
+in 1200 baud AFSK mode.
+
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+Input and output levels
+
+It is important that the input and output levels are adjusted properly.
+There are two utilities, available in the ax25 utilities distribution,
+to facilitate this: smmixer and smdiag. smdiag allows you to display
+the input signal in an oscilloscope like display or an eye diagram.
+smmixer allows you to adjust input/output levels. See the respective
+man pages. These two utilities have two counterparts: xfsmmixer and
+xfsmdiag. They both require the FORMS to compile, and provide a nicer
+user interface. Functionality is the same, though.
+
+
+Transmitter keying
+
+Since soundcards do not have a DC coupled output; PTT keying options include
+the following:
+* VOX circuitry
+* Serial port pin
+* Parallel port pin
+* MPU401 MIDI output via a retriggerable monoflop.
+Circuit schematics may be found at
+http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html.
+
+
+Compatibility with the rest of the Linux kernel
+
+The sound driver and the soundcard modem driver compete for the same
+hardware resources. Of course only one driver can access a given
+interface at a time. Worse yet, the sound driver grabs the soundcard
+at startup time. Therefore the soundcard modem driver subsequently won't
+be able to access the soundcard. You might therefore find it necessary to
+unload the sound driver before using the soundcard modem driver.
+
+
+Further reading
+
+Please take a look at http://www.ife.ee.ethz.ch/~sailer/ham/linux/hdlc.html
+for further informations on the driver.
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+Packet Radio: hb9jnx@hb9w.che.eu
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/tlan.README linux/Documentation/networking/tlan.README
--- v2.0.34/linux/Documentation/networking/tlan.README	Mon Jul 13 13:46:24 1998
+++ linux/Documentation/networking/tlan.README	Mon Jul 13 13:47:26 1998
@@ -1,6 +1,11 @@
-Caldera TLAN driver for Linux, version 0.42
+TLAN driver for Linux, version 0.43
 README
 
+Note: I (James) am not maintaining this driver anymore, as I no longer
+	have the equipment to do so.  So it is available to anyone who
+	wishes to take it over ;)  If someone needs to reach me about
+	it, my new email address is james@sovereign.org.
+
 
 I.  Supported Devices.
 
@@ -8,20 +13,32 @@
 
     Supported:
     Vendor ID	Device ID	Name
-    0e11	ae32		Compaq Netelligent 10/100 TX
-    0e11	ae34		Compaq Netelligent 10 T
+    0e11	ae32		Compaq Netelligent 10/100 TX PCI UTP
+    0e11	ae34		Compaq Netelligent 10 T PCI UTP
     0e11	ae35		Compaq Integrated NetFlex 3/P
-    0e11	ae43		Compaq ProLiant Integrated Netelligent 10/100 TX
-    0e11	ae40		Compaq Dual Port Netelligent 10/100 TX
-    0e11	b011		Compaq Deskpro 4000 5233MMX
+    0e11	ae40		Compaq Netelligent Dual 10/100 TX PCI UTP
+    0e11	ae43		Compaq Netelligent Integrated 10/100 TX UTP
+    0e11	b011		Compaq Netelligent 10/100 TX Embedded UTP
+    0e11	b012		Compaq Netelligent 10 T/2 PCI UTP/Coax
+    0e11	b030		Compaq Netelligent 10/100 TX UTP
     0e11	f130		Compaq NetFlex 3/P
     0e11	f150		Compaq NetFlex 3/P
+    108d	0012		Olicom OC-2325	
+    108d	0013		Olicom OC-2183
     108d	0014		Olicom OC-2326	
 
+
     Caveats:
     
-    I don't believe 100BaseTX daughterboards will work.  I am interested
-    in any reports.
+    I am not sure if 100BaseTX daughterboards (for those cards which
+    support such things) will work.  I haven't had any solid evidence
+    either way.
+
+    However, if a card supports 100BaseTx without requiring an add
+    on daughterboard, it should work with 100BaseTx.
+
+    The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested,
+    but I do not expect any problems.
     
 
 II.  Building the Driver.
@@ -77,6 +94,14 @@
 	   device that does not have an AUI/BNC connector will probably
 	   cause it to not function correctly.)
 
+	4. You can set duplex=1 to force half duplex, and duplex=2 to
+	   force full duplex.
+
+	5. You can set speed=10 to force 10Mbs operation, and speed=100Mbs
+	   to force 100Mbs operation. (I'm not sure what will happen
+	   if a card which only supports 10Mbs is forced into 100Mbs
+	   mode.)
+
 	3. If the driver is built into the kernel, you can use the 3rd
 	   and 4th parameters to set aui and debug respectively.  For
 	   example:
@@ -90,6 +115,10 @@
 
 		0x01 = aui
 		0x02 = use SA_INTERRUPT flag when reserving the irq.
+		0x04 = use half duplex
+		0x08 = use full duplex
+		0x10 = use 10BaseT
+		0x20 = use 100BaseTx
 
 
 IV.  Things to try if you have problems.
@@ -98,33 +127,8 @@
 	1. Make sure routing is correct.
 	2. If you are using a 2.1.x kernel, try to duplicate the
 	   problem on a 2.0.x (preferably 2.0.29 or 2.0.30) kernel.
-	3. Set debug to 7, either in tlan.c or through insmod as in
-	   section III.1 above.
-	4. Make sure klog is running so the kernel messages are
-	   being recorded somewhere.
-	5. Run the following sequence of programs in order (you
-	   may want to do this within an xterm, as background
-	   traffic may cause a lot of TLAN RECEIVED: messages
-	   on the console):
-
-	   ifconfig eth0 your.ip.address netmask your.net.mask up
-	   route add -net local.net.address eth0
-	   ifconfig
-	   ping some.computer.on.local.net
-	   ifconfig eth0 down
-
-	6. Mail the log of what occurred to me.  Also include the
-	   kernel version and what media/connector type (eg,
-	   10 BaseT/RJ45, 100 BaseTX/RJ45, Thinnet/BNC, etc).
-
 
 
-Please e-mail me with any comments, successes, or failures.  Thanks.
-
 There is also a tlan mailing list which you can join by sending "subscribe tlan"
-in the body of an email to majordomo@vuser.vu.union.edu.  I will announce new
-releases of the TLAN driver there.
-
-James
-james.banks@caldera.com
+in the body of an email to majordomo@vuser.vu.union.edu.
 
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/tulip.txt linux/Documentation/networking/tulip.txt
--- v2.0.34/linux/Documentation/networking/tulip.txt	Mon Apr 22 01:27:41 1996
+++ linux/Documentation/networking/tulip.txt	Mon Jul 13 13:47:26 1998
@@ -1,7 +1,7 @@
-                Tulip ethernet card driver
+                Tulip Ethernet Card Driver
 
 The Tulip driver is developed by Donald Becker and changed by
-Takashi Manabe. This driver is designed to work with PCI ethernet
+Takashi Manabe. This driver is designed to work with PCI Ethernet
 cards which use the DECchip DC21x4x family. This driver hopefully
 works with all of 1.2.x and 1.3.x kernels, but I tested only
 with 1.2.13, 1.3.39, 1.3.49, 1.3.52, 1.3.57 and later.
diff -u --recursive --new-file v2.0.34/linux/Documentation/networking/z8530drv.txt linux/Documentation/networking/z8530drv.txt
--- v2.0.34/linux/Documentation/networking/z8530drv.txt	Tue Aug 12 14:47:03 1997
+++ linux/Documentation/networking/z8530drv.txt	Mon Jul 13 13:47:26 1998
@@ -238,7 +238,7 @@
 clock dpll		# clock source: 
 			# 	dpll     = normal halfduplex operation
 			# 	external = MODEM provides own Rx/Tx clock
-			#	divider  = use fullduplex divider if
+			#	divider  = use full duplex divider if
 			#		   installed (1)
 mode nrzi		# HDLC encoding mode
 			#	nrzi = 1k2 MODEM, G3RUH 9k6 MODEM
@@ -463,7 +463,7 @@
      Example: sccparam /dev/scc3 speed 9600
 
 txdelay:
-     The delay (in units of 10ms) after keying of the 
+     The delay (in units of 10 ms) after keying of the 
      transmitter, until the first byte is sent. This is usually 
      called "TXDELAY" in a TNC.  When 0 is specified, the driver 
      will just wait until the CTS signal is asserted. This 
@@ -485,7 +485,7 @@
 
 slottime:
      This is the time between samples of the channel. It is 
-     expressed in units of 10ms.  About 200-300 ms (value 20-30) 
+     expressed in units of 10 ms.  About 200-300 ms (value 20-30) 
      seems to be a good value.
 
      Example: sccparam /dev/scc0 slot 20
@@ -497,7 +497,7 @@
      SCC before the transmitter is keyed down. The value depends 
      on the baudrate selected.  A few character times should be 
      sufficient, e.g. 40ms at 1200 baud. (value 4)
-     The value of this parameter is in 10ms units.
+     The value of this parameter is in 10 ms units.
 
      Example: sccparam /dev/scc2 4
 
@@ -520,9 +520,9 @@
 wait:
      The initial waittime before any transmit attempt, after the 
      frame has been queue for transmit.  This is the length of 
-     the first slot in CSMA mode.  In fullduplex modes it is
+     the first slot in CSMA mode.  In full duplex modes it is
      set to 0 for maximum performance.
-     The value of this parameter is in 10ms units. 
+     The value of this parameter is in 10 ms units. 
 
      Example: sccparam /dev/scc1 wait 4
 
@@ -547,7 +547,7 @@
      Example: sccparam /dev/scc3 min 10
 
 idle
-     This parameter specifies the maximum idle time in fullduplex 
+     This parameter specifies the maximum idle time in full duplex 
      2 mode, in seconds.  When no frames have been sent for this 
      time, the transmitter will be keyed down.  A value of 0 is
      has same result as the fullduplex mode 1. This parameter
diff -u --recursive --new-file v2.0.34/linux/Documentation/nfsroot.txt linux/Documentation/nfsroot.txt
--- v2.0.34/linux/Documentation/nfsroot.txt	Mon May  6 02:26:01 1996
+++ linux/Documentation/nfsroot.txt	Mon Jul 13 13:47:26 1998
@@ -109,7 +109,7 @@
 		less a value has been received by BOOTP.
 
   <hostname>	Name of the client. If empty, the client IP address is
-		used in ASCII-notation, or the value received by BOOTP.
+		used in ASCII notation, or the value received by BOOTP.
 
   <device>	Name of network device to use. If this is empty, all
 		devices are used for RARP requests, and the first one
diff -u --recursive --new-file v2.0.34/linux/Documentation/paride.txt linux/Documentation/paride.txt
--- v2.0.34/linux/Documentation/paride.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/paride.txt	Mon Jul 13 13:47:26 1998
@@ -0,0 +1,360 @@
+
+		Linux and parallel port IDE devices
+
+
+PARIDE-2.0.35   (c) 1997-8  Grant Guenther <grant@torque.net>
+
+*************************************************************************
+
+Special notes for the 2.0.35 version:
+
+(i)   This is the paride from 2.1.107 retrofitted to work with 2.0.34.
+     
+(ii)  PARPORT is _not_ supported.  If you obtain the PARPORT patches
+      for 2.0 and try to use them, it might work.  I have not tried
+      it.
+
+(iii) There is no guarantee of any ongoing support or future development
+      for this special retrofit.
+
+(iv)  I have not built or tested PARIDE with SMP support in 2.0.35,
+      use it at your own risk.
+
+*************************************************************************
+
+1. Introduction
+
+Owing to the simplicity and near universality of the parallel port interface
+to personal computers, many external devices such as portable hard-disk,
+CD-ROM, LS-120 and tape drives use the parallel port to connect to their
+host computer.  While some devices (notably scanners) use ad-hoc methods
+to pass commands and data through the parallel port interface, most 
+external devices are actually identical to an internal model, but with
+a parallel-port adapter chip added in.  Some of the original parallel port
+adapters were little more than mechanisms for multiplexing a SCSI bus.
+(The Iomega PPA-3 adapter used in the ZIP drives is an example of this
+approach).  Most current designs, however, take a different approach.
+The adapter chip reproduces a small ISA or IDE bus in the external device
+and the communication protocol provides operations for reading and writing
+device registers, as well as data block transfer functions.  Sometimes,
+the device being addressed via the parallel cable is a standard SCSI
+controller like an NCR 5380.  The "ditto" family of external tape
+drives use the ISA replicator to interface a floppy disk controller,
+which is then connected to a floppy-tape mechanism.  The vast majority
+of external parallel port devices, however, are now based on standard
+IDE type devices, which require no intermediate controller.  If one
+were to open up a parallel port CD-ROM drive, for instance, one would
+find a standard ATAPI CD-ROM drive, a power supply, and a single adapter
+that interconnected a standard PC parallel port cable and a standard
+IDE cable.  It is usually possible to exchange the CD-ROM device with
+any other device using the IDE interface. 
+
+The document describes the support in Linux for parallel port IDE
+devices.  It does not cover parallel port SCSI devices, "ditto" tape
+drives or scanners.  Many different devices are supported by the 
+parallel port IDE subsystem, including:
+
+	MicroSolutions backpack CD-ROM
+	MicroSolutions backpack PD/CD
+	MicroSolutions backpack hard-drives
+	MicroSolutions backpack 8000t tape drive
+	SyQuest EZ-135, EZ-230 & SparQ drives
+	Avatar Shark
+	Imation Superdisk LS-120
+	FreeCom Power CD
+	Hewlett-Packard 5GB tape drive
+	Hewlett-Packard 7100 and 7200 CD-RW drives
+
+as well as most of the clone and no-name products on the market.
+
+To support such a wide range of devices, PARIDE, the parallel port IDE
+subsystem, is actually structured in three parts.   There is a base
+paride module which provides a registry and some common methods for
+accessing the parallel ports.  The second component is a set of 
+high-level drivers for each of the different type of supported device: 
+
+	pd	IDE disk
+	pcd	ATAPI CD-ROM
+	pf	ATAPI disk
+	pt	ATAPI tape
+	pg	ATAPI generic
+
+(Currently, the pg driver is only used with CD-R drives).
+
+The high-level drivers function according to the relevant standards.
+The third component of PARIDE is a set of low-level protocol drivers
+for each of the parallel port IDE adapter chips.  Thanks to the interest
+and encouragement of Linux users from many parts of the world, 
+support is available for almost all known adapter protocols:
+
+        aten    ATEN EH-100                            (HK)
+        bpck    Microsolutions backpack                (US)
+        comm    DataStor (old-type) "commuter" adapter (TW)
+        dstr    DataStor EP-2000                       (TW)
+        epat    Shuttle EPAT                           (UK)
+        epia    Shuttle EPIA                           (UK)
+	fit2    FIT TD-2000			       (US)
+	fit3    FIT TD-3000			       (US)
+        frpw    Freecom Power                          (DE)
+        kbic    KingByte KBIC-951A and KBIC-971A       (TW)
+	ktti    KT Technology PHd adapter              (SG)
+        on20    OnSpec 90c20                           (US)
+        on26    OnSpec 90c26                           (US)
+
+
+2. Using the PARIDE subsystem
+
+While configuring the Linux kernel, you may choose either to build
+the PARIDE drivers into your kernel, or to build them as modules.
+
+In either case, you will need to select "Parallel port IDE device support"
+as well as at least one of the high-level drivers and at least one
+of the parallel port communication protocols.  If you do not know
+what kind of parallel port adapter is used in your drive, you could
+begin by checking the file names and any text files on your DOS 
+installation floppy.  Alternatively, you can look at the markings on
+the adapter chip itself.  That's usually sufficient to identify the
+correct device.  
+
+You can actually select all the protocol modules, and allow the PARIDE
+subsystem to try them all for you.
+
+For the "brand-name" products listed above, here are the protocol
+and high-level drivers that you would use:
+
+	Manufacturer		Model		Driver	Protocol
+	
+	MicroSolutions		CD-ROM		pcd	bpck
+	MicroSolutions		PD drive	pf	bpck
+	MicroSolutions		hard-drive	pd	bpck
+	MicroSolutions          8000t tape      pt      bpck
+	SyQuest			EZ, SparQ	pd	epat
+	Imation			Superdisk	pf	epat
+	Avatar			Shark		pd	epat
+	FreeCom			CD-ROM		pcd	frpw
+	Hewlett-Packard		5GB Tape	pt	epat
+	Hewlett-Packard		7100/7200	pg	epat
+
+2.1  Configuring built-in drivers
+
+We recommend that you get to know how the drivers work and how to
+configure them as loadable modules, before attempting to compile a
+kernel with the drivers built-in.
+
+If you built all of your PARIDE support directly into your kernel,
+and you have just a single parallel port IDE device, your kernel should
+locate it automatically for you.  If you have more than one device,
+you may need to give some command line options to your bootloader
+(eg: LILO), how to do that is beyond the scope of this document.
+
+The high-level drivers accept a number of command line parameters, all
+of which are documented in the source files in linux/drivers/block/paride.
+By default, each driver will automatically try all parallel ports it
+can find, and all protocol types that have been installed, until it finds
+a parallel port IDE adapter.  Once it finds one, the probe stops.  So,
+if you have more than one device, you will need to tell the drivers
+how to identify them.  This requires specifying the port address, the
+protocol identification number and, for some devices, the drive's
+chain ID.  While your system is booting, a number of messages are
+displayed on the console.  Like all such messages, they can be
+reviewed with the 'dmesg' command.  Among those messages will be
+some lines like:
+
+	paride: bpck registered as protocol 0
+	paride: epat registered as protocol 1
+
+The numbers will always be the same until you build a new kernel with
+different protocol selections.  You should note these numbers as you
+will need them to identify the devices.
+
+If you happen to be using a MicroSolutions backpack device, you will
+also need to know the unit ID number for each drive.  This is usually
+the last two digits of the drive's serial number (but read MicroSolutions'
+documentation about this).
+
+As an example, let's assume that you have a MicroSolutions PD/CD drive
+with unit ID number 36 connected to the parallel port at 0x378, a SyQuest 
+EZ-135 connected to the chained port on the PD/CD drive and also an 
+Imation Superdisk connected to port 0x278.  You could give the following 
+options on your boot command:
+
+	pd.drive0=0x378,1 pf.drive0=0x278,1 pf.drive1=0x378,0,36
+
+In the last option, pf.drive1 configures device /dev/pf1, the 0x378
+is the parallel port base address, the 0 is the protocol registration
+number and 36 is the chain ID.
+
+This (2.0.34) version of PARIDE does not support chained devices on the
+same parallel port.
+
+2.2  Loading and configuring PARIDE as modules
+
+It is much faster and simpler to get to understand the PARIDE drivers
+if you use them as loadable kernel modules.   
+
+Note:  using these drivers with the "kerneld" automatic module loading
+system is not recommended, and is not documented here.  
+
+To use PARIDE, you must begin by 
+
+	insmod paride
+
+this loads a base module which provides a registry for the protocols,
+among other tasks.
+
+Then, load as many of the protocol modules as you think you might need.
+As you load each module, it will register the protocols that it supports,
+and print a log message to your kernel log file and your console. For 
+example:
+
+	# insmod epat
+	paride: epat registered as protocol 0
+	# insmod kbic
+	paride: k951 registered as protocol 1
+        paride: k971 registered as protocol 2
+
+Finally, you can load high-level drivers for each kind of device that
+you have connected.  By default, each driver will autoprobe for a single 
+device, but you can support up to four similar devices by giving their
+individual co-ordinates when you load the driver.
+
+For example, if you had two no-name CD-ROM drives both using the
+KingByte KBIC-951A adapter, one on port 0x378 and the other on 0x3bc
+you could give the following command:
+
+	# insmod pcd drive0=0x378,1 drive1=0x3bc,1
+
+For most adapters, giving a port address and protocol number is sufficient,
+but check the source files in linux/drivers/block/paride for more 
+information.  (Hopefully someone will write some man pages one day !).
+
+As another example, here's what happens when PARPORT is installed, and
+a SyQuest EZ-135 is attached to port 0x378:
+
+	# insmod paride
+	paride: version 1.0 installed
+	# insmod epat
+	paride: epat registered as protocol 0
+	# insmod pd
+	pd: pd version 1.0, major 45, cluster 64, nice 0
+	pda: Sharing parport1 at 0x378
+	pda: epat 1.0, Shuttle EPAT chip c3 at 0x378, mode 5 (EPP-32), delay 1
+	pda: SyQuest EZ135A, 262144 blocks [128M], (512/16/32), removable media
+	 pda: pda1
+
+Note that the last line is the output from the generic partition table
+scanner - in this case it reports that it has found a disk with one partition.
+
+2.3  Using a PARIDE device
+
+Once the drivers have been loaded, you can access PARIDE devices in the
+same way as their traditional counterparts.  You will probably need to
+create the device "special files".  Here is a simple script that you can
+cut to a file and execute:
+
+#!/bin/bash
+#
+# mkd -- a script to create the device special files for the PARIDE subsystem
+#
+function mkdev {
+  mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
+}
+#
+function pd {
+  D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
+  mkdev pd$D b 45 $[ $1 * 16 ]
+  for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+  do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
+  done
+}
+#
+cd /dev
+#
+for u in 0 1 2 3 ; do pd $u ; done
+for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done 
+for u in 0 1 2 3 ; do mkdev pf$u  b 47 $u ; done 
+for u in 0 1 2 3 ; do mkdev pt$u  c 96 $u ; done 
+for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done 
+for u in 0 1 2 3 ; do mkdev pg$u  c 97 $u ; done 
+#
+# end of mkd
+
+With the device files and drivers in place, you can access PARIDE devices
+like any other Linux device.   For example, to mount a CD-ROM in pcd0, use:
+
+	mount /dev/pcd0 /cdrom
+
+If you have a fresh Avatar Shark cartridge, and the drive is pda, you
+might do something like:
+
+	fdisk /dev/pda		-- make a new partition table with
+				   partition 1 of type 83
+
+	mke2fs /dev/pda1	-- to build the file system
+
+	mkdir /shark		-- make a place to mount the disk
+
+	mount /dev/pda1 /shark
+
+Devices like the Imation superdisk work in the same way, except that
+they do not have a partition table.  For example to make a 120MB
+floppy that you could share with a DOS system:
+
+	mkdosfs /dev/pf0
+	mount /dev/pf0 /mnt
+
+2.4  Using the pg driver
+
+The pg driver can be used in conjunction with the cdrecord program
+to create CD-ROMs.  For more information, and the required patches 
+to cdrecord, please visit http://www.torque.net/parport/cdr.html .
+
+3. Troubleshooting
+
+While a lot of testing has gone into these drivers to make them work
+as smoothly as possible, problems will arise.  If you do have problems,
+please check all the obvious things first:  does the drive work in
+DOS with the manufacturer's drivers ?  If that doesn't yield any useful
+clues, then please make sure that only one drive is hooked to your system,
+and that no other device driver is using your parallel port (check in 
+/proc/ioports).  Then, load the appropriate drivers (you can load several 
+protocol modules if you want) as in:
+
+	# insmod paride
+	# insmod epat
+	# insmod bpck
+	# insmod kbic
+	...
+	# insmod pd verbose=1
+
+(using the correct driver for the type of device you have, of course).
+The verbose=1 parameter will cause the drivers to log a trace of their
+activity as they attempt to locate your drive.
+
+Use 'dmesg' to capture a log of all the PARIDE messages (any messages
+beginning with paride:, a protocol module's name or a driver's name) and
+include that with your bug report.  You can submit a bug report in one
+of two ways.  Either send it directly to the author of the PARIDE suite,
+by e-mail to grant@torque.net, or join the linux-parport mailing list
+and post your report there.
+
+You can join the linux-parport mailing list by sending a mail message
+to 
+		linux-parport-request@torque.net
+
+with the single word 
+
+		subscribe
+
+in the body of the mail message (not in the subject line).   Please be
+sure that your mail program is correctly set up when you do this,  as
+the list manager is a robot that will subscribe you using the reply
+address in your mail headers.  REMOVE any anti-spam gimmicks you may
+have in your mail headers, when sending mail to the list server.
+
+You might also find some useful information on the linux-parport
+web pages (although they are not always up to date) at
+
+	http://www.torque.net/parport/
+
+
diff -u --recursive --new-file v2.0.34/linux/Documentation/rtc.txt linux/Documentation/rtc.txt
--- v2.0.34/linux/Documentation/rtc.txt	Mon Jul 13 13:46:24 1998
+++ linux/Documentation/rtc.txt	Mon Jul 13 13:47:26 1998
@@ -158,7 +158,7 @@
 fflush(stderr);
 for (i=1; i<6; i++) {
 	struct timeval tv = {5, 0};	/* 5 second timeout on select */
-	struct fd_set readfds;
+	fd_set readfds;
 
 	FD_ZERO(&readfds);
 	FD_SET(fd, &readfds);
diff -u --recursive --new-file v2.0.34/linux/Documentation/smp.tex linux/Documentation/smp.tex
--- v2.0.34/linux/Documentation/smp.tex	Thu Jun  6 04:57:43 1996
+++ linux/Documentation/smp.tex	Mon Jul 13 13:47:26 1998
@@ -24,8 +24,8 @@
 \hfill Alan Cox, 1995
 
 
-The author wishes to thank Caldera Inc ( http://www.caldera.com )
-whose donation of an ASUS dual pentium board made this project possible, 
+The author wishes to thank Caldera Inc. ( http://www.caldera.com )
+whose donation of an ASUS dual Pentium board made this project possible, 
 and Thomas Radke, whose initial work on multiprocessor Linux formed 
 the backbone of this project.
 
@@ -35,7 +35,7 @@
 specification places much of the onus for hard work on the chipset and 
 hardware rather than the operating system.
 
-The Intel pentium processors have a wide variety of inbuilt facilities for 
+The Intel Pentium processors have a wide variety of built-in facilities for 
 supporting multiprocessing, including hardware cache coherency, built in 
 interprocessor interrupt handling and a set of atomic test and set, 
 exchange and similar operations. The cache coherency in particular makes the 
@@ -176,7 +176,7 @@
 The memory management core of the existing Linux system functions 
 adequately within the multiprocessor framework providing the locking is 
 used. Certain processor specific areas do need changing, in particular 
-invalidate() must invalidate the TLB's of all processors before it returns.
+invalidate() must invalidate the TLBs of all processors before it returns.
 
 
 \subsubsection{Miscellaneous Functions}
@@ -210,7 +210,7 @@
 extensions to standard kernel facilities to cope with multiple processors.
 
 \subsubsection{Initialisation}	
-The intel MP architecture captures all the processors except for a single 
+The Intel MP architecture captures all the processors except for a single 
 processor known as the 'boot processor' in the BIOS at boot time. Thus a 
 single processor enters the kernel bootup code. The first processor 
 executes the bootstrap code, loads and uncompresses the kernel. Having 
@@ -266,8 +266,8 @@
 appropriately. From then on the real APIC logical identity register is 
 read.
 
-Message passing is accomplished using a pair of IPI's on interrupt 13 
-(unused by the 80486 FPU's in SMP mode) and interrupt 16. Two are used in 
+Message passing is accomplished using a pair of IPIs on interrupt 13 
+(unused by the 80486 FPUs in SMP mode) and interrupt 16. Two are used in 
 order to separate messages that cannot be processed until the receiver 
 obtains the kernel spinlock from messages that can be processed 
 immediately. In effect IRQ 13 is a fast IRQ handler that does not obtain 
diff -u --recursive --new-file v2.0.34/linux/MAINTAINERS linux/MAINTAINERS
--- v2.0.34/linux/MAINTAINERS	Mon Jul 13 13:46:24 1998
+++ linux/MAINTAINERS	Mon Jul 13 13:47:26 1998
@@ -389,6 +389,12 @@
 L:	linux-scsi@vger.rutgers.edu
 S:	Maintained
 
+GSCD CDROM DRIVER
+P:	Oliver Raupach
+M:	oliver@mm.gop.de
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 SBPCD CDROM DRIVER
 P:	Eberhard Moenkeberg
 M:	emoenke@gwdg.de
diff -u --recursive --new-file v2.0.34/linux/Makefile linux/Makefile
--- v2.0.34/linux/Makefile	Mon Jul 13 13:46:24 1998
+++ linux/Makefile	Mon Jul 13 13:47:26 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 34
+SUBLEVEL = 35
 
 ARCH = i386
 
@@ -146,6 +146,10 @@
 DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
 endif
 
+ifeq ($(CONFIG_PARIDE),y)
+DRIVERS := $(DRIVERS) drivers/block/paride/paride.a
+endif
+
 include arch/$(ARCH)/Makefile
 
 ifdef SMP
@@ -323,6 +327,10 @@
 	rm -f include/linux/autoconf.h include/linux/version.h
 	rm -f drivers/sound/local.h drivers/sound/.defines
 	rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash
+	rm -f drivers/net/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h
+	rm -f drivers/net/soundmodem/sm_tbl_{hapn4800,psk4800}.h
+	rm -f drivers/net/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h
+	rm -f drivers/net/soundmodem/gentbl
 	rm -f .version .config* config.in config.old
 	rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
 	rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
diff -u --recursive --new-file v2.0.34/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- v2.0.34/linux/arch/alpha/kernel/bios32.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/alpha/kernel/bios32.c	Mon Jul 13 13:47:26 1998
@@ -345,13 +345,13 @@
 			 * Alpha implementation of the PCI interface:
 			 *
 			 * In sparse memory address space, the first
-			 * octant (16MB) of every 128MB segment is
-			 * aliased to the the very first 16MB of the
+			 * octant (16 MB) of every 128 MB segment is
+			 * aliased to the very first 16 MB of the
 			 * address space (i.e., it aliases the ISA
 			 * memory address space).  Thus, we try to
 			 * avoid allocating PCI devices in that range.
 			 * Can be allocated in 2nd-7th octant only.
-			 * Devices that need more than 112MB of
+			 * Devices that need more than 112 MB of
 			 * address space must be accessed through
 			 * dense memory space only!
 			 */
diff -u --recursive --new-file v2.0.34/linux/arch/alpha/math-emu/ieee-math.c linux/arch/alpha/math-emu/ieee-math.c
--- v2.0.34/linux/arch/alpha/math-emu/ieee-math.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/alpha/math-emu/ieee-math.c	Mon Jul 13 13:47:26 1998
@@ -733,19 +733,23 @@
  *       FPCR_INV if invalid operation occurred, etc.
  */
 unsigned long
-ieee_CVTTQ (int f, unsigned long a, unsigned long *b)
+ieee_CVTTQ (int f, unsigned long a, unsigned long *pb)
 {
 	unsigned int midway;
-	unsigned long ov, uv, res = 0;
+	unsigned long ov, uv, res, b;
 	fpclass_t a_type;
 	EXTENDED temp;
 
-	*b = 0;
 	a_type = extend_ieee(a, &temp, DOUBLE);
+
+	b = 0x7fffffffffffffff;
+	res = FPCR_INV;
 	if (a_type == NaN || a_type == INFTY)
-		return FPCR_INV;
+		goto out;
+
+	res = 0;
 	if (a_type == QNaN)
-		return 0;
+		goto out;
 
 	if (temp.e > 0) {
 		ov = 0;
@@ -757,7 +761,7 @@
 		if (ov || (temp.f[1] & 0xffc0000000000000))
 			res |= FPCR_IOV | FPCR_INE;
 	}
-	if (temp.e < 0) {
+	else if (temp.e < 0) {
 		while (temp.e < 0) {
 			++temp.e;
 			uv = temp.f[0] & 1;		/* save sticky bit */
@@ -765,7 +769,8 @@
 			temp.f[0] |= uv;
 		}
 	}
-	*b = ((temp.f[1] << 9) | (temp.f[0] >> 55)) & 0x7fffffffffffffff;
+	b = (temp.f[1] << 9) | (temp.f[0] >> 55);
+
 	/*
 	 * Notice: the fraction is only 52 bits long.  Thus, rounding
 	 * cannot possibly result in an integer overflow.
@@ -781,12 +786,12 @@
 		break;
 
 	      case ROUND_PINF:
-		if ((temp.f[0] & 0x003fffffffffffff) != 0)
+		if ((temp.f[0] & 0x007fffffffffffff) != 0)
 			++b;
 		break;
 
 	      case ROUND_NINF:
-		if ((temp.f[0] & 0x003fffffffffffff) != 0)
+		if ((temp.f[0] & 0x007fffffffffffff) != 0)
 			--b;
 		break;
 
@@ -794,12 +799,15 @@
 		/* no action needed */
 		break;
 	}
-	if ((temp.f[0] & 0x003fffffffffffff) != 0)
+	if ((temp.f[0] & 0x007fffffffffffff) != 0)
 		res |= FPCR_INE;
 
 	if (temp.s) {
-		*b = -*b;
+		b = -b;
 	}
+
+out:
+	*pb = b;
 	return res;
 }
 
diff -u --recursive --new-file v2.0.34/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.0.34/linux/arch/i386/defconfig	Mon Sep 22 13:44:01 1997
+++ linux/arch/i386/defconfig	Mon Jul 13 13:47:26 1998
@@ -58,6 +58,7 @@
 # CONFIG_BLK_DEV_MD is not set
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -110,9 +111,10 @@
 # CONFIG_EL1 is not set
 # CONFIG_EL2 is not set
 CONFIG_EL3=y
+# CONFIG_3C515 is not set
 # CONFIG_VORTEX is not set
-# CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_PCI is not set
 # CONFIG_NET_ISA is not set
 # CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
@@ -138,15 +140,42 @@
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
 # CONFIG_XIA_FS is not set
+CONFIG_NLS=y
+CONFIG_ISO9660_FS=y
 CONFIG_FAT_FS=y
 CONFIG_MSDOS_FS=y
-# CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 # CONFIG_ROOT_NFS is not set
 # CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.0.34/linux/arch/i386/kernel/entry.S	Mon Jul 13 13:46:25 1998
+++ linux/arch/i386/kernel/entry.S	Mon Jul 13 13:47:27 1998
@@ -374,6 +374,7 @@
 	jne signal_return
 2:	RESTORE_ALL
 	ALIGN
+	.globl signal_return
 signal_return:
 	movl %esp,%ecx
 	pushl %ecx
diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.0.34/linux/arch/i386/kernel/head.S	Mon Jul 13 13:46:25 1998
+++ linux/arch/i386/kernel/head.S	Mon Jul 13 13:47:27 1998
@@ -103,16 +103,17 @@
 checkCPUtype:
 #endif
 
-/* check if it is 486 or 386. */
+/* check Processor type: 386, 486, 6x86(L) or CPUID capable processor */
 /*
  * XXX - this does a lot of unnecessary setup.  Alignment checks don't
  * apply at our cpl of 0 and the stack ought to be aligned already, and
  * we don't need to preserve eflags.
  */
+
 	movl $3, SYMBOL_NAME(x86)
 	pushfl			# push EFLAGS
 	popl %eax		# get EFLAGS
-	movl %eax,%ecx		# save original EFLAGS
+	movl %eax,%ecx		# save original EFLAGS in ecx
 	xorl $0x40000,%eax	# flip AC bit in EFLAGS
 	pushl %eax		# copy to EFLAGS
 	popfl			# set EFLAGS
@@ -127,10 +128,11 @@
 	pushl %eax
 	popfl			# if we are on a straight 486DX, SX, or
 	pushfl			# 487SX we can't change it
-	popl %eax
-	xorl %ecx,%eax
+	popl %eax		# Also if we are on a Cyrix 6x86(L)
+	xorl %ecx,%eax		# OTOH 6x86MXs and MIIs check OK
 	andl $0x200000,%eax
-	je is486
+	je is486x
+
 isnew:	pushl %ecx		# restore original EFLAGS
 	popfl
 	incl SYMBOL_NAME(have_cpuid)	# we have CPUID
@@ -168,7 +170,72 @@
 	andl $0x80000011,%eax	# Save PG,PE,ET
 	orl $0x50022,%eax	# set AM, WP, NE and MP
 	jmp 2f
-is486:	pushl %ecx		# restore original EFLAGS
+
+/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
+ * clobbering the new BX chipset used with the Pentium II, which has a register
+ * at the same addresses as those used to access the Cyrix special configuration
+ * registers (CCRs).
+ */
+	/*
+	 * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
+	 * (and it _must_ be 5 divided by 2) while other CPUs change
+	 * them in undefined ways. We need to know this since we may
+	 * need to enable the CPUID instruction at least.
+	 * We couldn't use this test before since the PPro and PII behave
+	 * like Cyrix chips in this respect.
+	 */
+is486x:	xor %ax,%ax
+	sahf
+	movb $5,%ax
+	movb $2,%bx
+	div %bl
+	lahf
+	cmpb $2,%ah
+	jne ncyrix
+	/*
+	 * N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
+	 *      so do not try to "optimize" it! For the same reason we
+	 *	do all this with interrupts off.
+	 */
+#define setCx86(reg, val) \
+	movb reg,%ax;	\
+	outb %ax,$0x22;	\
+	movb val,%ax;	\
+	outb %ax,$0x23
+
+#define getCx86(reg) \
+	movb reg,%ax;	\
+	outb %ax,$0x22;	\
+	inb $0x23,%ax
+
+	cli
+	getCx86($0xc3)		# get CCR3
+	movb %ax,%cx		# Save old value
+	movb %ax,%bx
+	andb $0x0f,%bx		# Enable access to all config registers
+	orb $0x10,%bx		# by setting bit 4
+	setCx86($0xc3,%bx)
+
+	getCx86($0xe8)		# now we can get CCR4
+	orb $0x80,%ax		# and set bit 7 (CPUIDEN)
+	movb %ax,%bx		# to enable CPUID execution
+	setCx86($0xe8,%bx)
+
+        getCx86($0xfe)          # DIR0 : let's check this is a 6x86(L)
+        andb $0xf0,%ax		# should be 3xh
+	cmpb $0x30,%ax		# 
+	jne n6x86
+        getCx86($0xe9)          # CCR5 : we reset the SLOP bit
+        andb $0xfd,%ax		# so that udelay calculation
+        movb %ax,%bx		# is correct on 6x86(L) CPUs
+        setCx86($0xe9,%bx)
+	setCx86($0xc3,%cx)	# Restore old CCR3
+	sti
+	jmp isnew		# We enabled CPUID now
+
+n6x86:	setCx86($0xc3,%cx)	# Restore old CCR3
+	sti
+ncyrix:	pushl %ecx		# restore original EFLAGS
 	popfl
 	movl %cr0,%eax		# 486
 	andl $0x80000011,%eax	# Save PG,PE,ET
diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/ksyms.c linux/arch/i386/kernel/ksyms.c
--- v2.0.34/linux/arch/i386/kernel/ksyms.c	Tue Sep 16 14:40:50 1997
+++ linux/arch/i386/kernel/ksyms.c	Mon Jul 13 13:47:27 1998
@@ -5,6 +5,7 @@
 #include <linux/delay.h>
 
 #include <asm/semaphore.h>
+#include <asm/processor.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
@@ -12,6 +13,7 @@
 static struct symbol_table arch_symbol_table = {
 #include <linux/symtab_begin.h>
 	/* platform dependent support */
+	X(x86_capability),
 	X(dump_thread),
 	X(dump_fpu),
 	XNOVERS(__do_delay),
diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.0.34/linux/arch/i386/kernel/ptrace.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/i386/kernel/ptrace.c	Mon Jul 13 13:47:27 1998
@@ -323,41 +323,11 @@
 }
 #endif /* defined(CONFIG_MATH_EMULATION) */
 
-/* Put a word to the part of the user structure containing
- * floating point registers
+/*
  * Floating point support added to ptrace by Ramon Garcia,
  * ramon@juguete.quim.ucm.es
  */
 
-static int put_fpreg_word (struct task_struct *child,
-	unsigned long addr, long data)
-{
-	struct user *dummy = NULL;
-	if (addr < (long) (&dummy->i387.st_space))
-		return -EIO;
-	addr -= (long) (&dummy->i387.st_space);
-
-	if (!hard_math) {
-#ifdef CONFIG_MATH_EMULATION
-		write_emulator_word(child, addr, data);
-#else
-		return 0;
-#endif
-	}
-	else 
-#ifndef __SMP__
-		if (last_task_used_math == child) {
-			clts();
-			__asm__("fsave %0; fwait":"=m" (child->tss.i387));
-			last_task_used_math = current;
-			stts();
-		}
-#endif
-	*(long *)
-		((char *) (child->tss.i387.hard.st_space) + addr) = data;
-	return 0;
-}
-
 #ifdef CONFIG_MATH_EMULATION
 
 static unsigned long get_emulator_word(struct task_struct *child,
@@ -386,35 +356,54 @@
 }
 
 #endif /* defined(CONFIG_MATH_EMULATION) */
-/* Get a word from the part of the user structure containing
- * floating point registers
- */
-static unsigned long get_fpreg_word(struct task_struct *child,
-	unsigned long addr)
+
+static int putreg(struct task_struct *child,
+	unsigned long regno, unsigned long value)
 {
-	struct user *dummy = NULL;
-	unsigned long tmp;
-	addr -= (long) (&dummy->i387.st_space);
-	if (!hard_math) {
-#ifdef CONFIG_MATH_EMULATION
-		tmp = get_emulator_word(child, addr);
-#else
-		tmp = 0;
-#endif /* !defined(CONFIG_MATH_EMULATION) */
-	} else {
-#ifndef __SMP__
-		if (last_task_used_math == child) {
-			clts();
-			__asm__("fsave %0; fwait":"=m" (child->tss.i387));
-			last_task_used_math = current;
-			stts();
-		}
-#endif
-		tmp = *(long *)
-			((char *) (child->tss.i387.hard.st_space) +
-			 addr);
+	switch (regno >> 2) {
+		case ORIG_EAX:
+			return -EIO;
+		case FS:
+		case GS:
+		case DS:
+		case ES:
+			if (value && (value & 3) != 3)
+				return -EIO;
+			value &= 0xffff;
+			break;
+		case SS:
+		case CS:
+			if ((value & 3) != 3)
+				return -EIO;
+			value &= 0xffff;
+			break;
+		case EFL:
+			value &= FLAG_MASK;
+			value |= get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~FLAG_MASK;
 	}
-	return tmp;
+	put_stack_long(child, regno - sizeof(struct pt_regs), value);
+	return 0;
+}
+
+static unsigned long getreg(struct task_struct *child,
+	unsigned long regno)
+{
+	unsigned long retval = ~0UL;
+
+	switch (regno >> 2) {
+		case FS:
+		case GS:
+		case DS:
+		case ES:
+		case SS:
+		case CS:
+			retval = 0xffff;
+			/* fall through */
+		default:
+			regno = regno - sizeof(struct pt_regs);
+			retval &= get_stack_long(child, regno);
+	}
+	return retval;
 }
 
 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
@@ -490,39 +479,23 @@
 			unsigned long tmp;
 			int res;
 			
-  			if ((addr & 3 && 
-  			     (addr < (long) (&dummy->i387) ||
-  			      addr > (long) (&dummy->i387.st_space[20]) )) ||
-  			    addr < 0 || addr > sizeof(struct user) - 3)
+  			if ((addr & 3) || addr < 0
+			    || addr > sizeof(struct user) - 3)
 				return -EIO;
 			
 			res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
 			if (res)
 				return res;
 			tmp = 0;  /* Default return condition */
-  			if (addr >= (long) (&dummy->i387) &&
-  			    addr < (long) (&dummy->i387.st_space[20]) ) {
-#ifndef CONFIG_MATH_EMULATION
-				if (!hard_math)
-					return -EIO;
-#endif /* defined(CONFIG_MATH_EMULATION) */
-				tmp = get_fpreg_word(child, addr);
-  			} 
-			if(addr < 17*sizeof(long)) {
-			  addr = addr >> 2; /* temporary hack. */
-
-			  tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);
-			  if (addr == DS || addr == ES ||
-			      addr == FS || addr == GS ||
-			      addr == CS || addr == SS)
-			    tmp &= 0xffff;
-			};
-			if(addr >= (long) &dummy->u_debugreg[0] &&
-			   addr <= (long) &dummy->u_debugreg[7]){
+			if(addr < 17*sizeof(long))
+				tmp = getreg(child, addr);
+			else if(addr >= (long) &dummy->u_debugreg[0]
+				&& addr <= (long) &dummy->u_debugreg[7])
+			{
 				addr -= (long) &dummy->u_debugreg[0];
 				addr = addr >> 2;
 				tmp = child->debugreg[addr];
-			};
+			}
 			put_fs_long(tmp,(unsigned long *) data);
 			return 0;
 		}
@@ -533,50 +506,18 @@
 			return write_long(child,addr,data);
 
 		case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
-			if ((addr & 3 &&
-			     (addr < (long) (&dummy->i387.st_space[0]) ||
-			      addr > (long) (&dummy->i387.st_space[20]) )) ||
-			    addr < 0 || 
-			    addr > sizeof(struct user) - 3)
-				return -EIO;
-				
-			if (addr >= (long) (&dummy->i387.st_space[0]) &&
-			    addr < (long) (&dummy->i387.st_space[20]) ) {
-#ifndef CONFIG_MATH_EMULATION
-				if (!hard_math)
-					return -EIO;
-#endif /* defined(CONFIG_MATH_EMULATION) */
-				return put_fpreg_word(child, addr, data);
-			}
-			addr = addr >> 2; /* temporary hack. */
-			
-			if (addr == ORIG_EAX)
+  			if ((addr & 3) || addr < 0
+			    || addr > sizeof(struct user) - 3)
 				return -EIO;
-			if (addr == DS || addr == ES ||
-			    addr == FS || addr == GS ||
-			    addr == CS || addr == SS) {
-			    	data &= 0xffff;
-			    	if (data && (data & 3) != 3)
-					return -EIO;
-			}
-			if (addr == EFL) {   /* flags. */
-				data &= FLAG_MASK;
-				data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER)  & ~FLAG_MASK;
-			}
-		  /* Do not allow the user to set the debug register for kernel
-		     address space */
-		  if(addr < 17){
-			  if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
-				return -EIO;
-			return 0;
-			};
+
+			if(addr < 17*sizeof(long))
+				return putreg(child, addr, data);
 
 		  /* We need to be very careful here.  We implicitly
 		     want to modify a portion of the task_struct, and we
 		     have to be selective about what portions we allow someone
 		     to modify. */
 
-		  addr = addr << 2;  /* Convert back again */
 		  if(addr >= (long) &dummy->u_debugreg[0] &&
 		     addr <= (long) &dummy->u_debugreg[7]){
 
@@ -665,6 +606,108 @@
 			put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
 			return 0;
 		}
+
+		case PTRACE_GETREGS: { /* Get all gp regs from the child. */
+#ifdef CONFIG_MATH_EMULATION
+			if (!hard_math)
+				/* Not supported. */
+				return -EIO;
+#endif
+
+			if (verify_area(VERIFY_WRITE, (void *) data,
+					17*sizeof(long)))
+			  return -EIO;
+			for (i = 0; i < 17*sizeof(long);
+			     i += sizeof(long), data += sizeof(long))
+			  put_fs_long (getreg(child, i), (unsigned long *) data);
+			return 0;
+		  };
+
+		case PTRACE_SETREGS: { /* Set all gp regs in the child. */
+			unsigned long tmp;
+
+#ifdef CONFIG_MATH_EMULATION
+			if (!hard_math)
+				/* Not supported. */
+				return -EIO;
+#endif
+
+			if (verify_area(VERIFY_READ, (void *) data,
+					17*sizeof(long)))
+			  return -EIO;
+			for (i = 0; i < 17*sizeof(long);
+			     i += sizeof(long), data += sizeof(long))
+			  {
+			    tmp = get_fs_long ((unsigned long *) data);
+			    putreg(child, i, tmp);
+			  }
+			return 0;
+		  };
+
+		case PTRACE_GETFPREGS: { /* Get the child FPU state. */
+			unsigned long *tmp;
+
+#ifdef CONFIG_MATH_EMULATION
+			if (!hard_math)
+				/* Not supported. */
+				return -EIO;
+#endif
+
+			if (verify_area(VERIFY_WRITE, (void *) data,
+					sizeof(struct user_i387_struct)))
+			  return -EIO;
+			if ( !child->used_math ) {
+			  /* Simulate an empty FPU. */
+			  child->tss.i387.hard.cwd = 0xffff037f;
+			  child->tss.i387.hard.swd = 0xffff0000;
+			  child->tss.i387.hard.twd = 0xffffffff;
+			}
+			if (last_task_used_math == child)
+			  {
+			    clts();
+			    __asm__("fnsave %0; fwait":"=m" (child->tss.i387.hard));
+			    last_task_used_math = NULL;
+			    stts();
+			  }
+			tmp = (unsigned long *) &child->tss.i387.hard;
+			for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) )
+			  {
+			    put_fs_long (*tmp, (unsigned long *) data);
+			    data += sizeof(long);
+			    tmp++;
+			  }
+
+			return 0;
+		  };
+
+		case PTRACE_SETFPREGS: { /* Set the child FPU state. */
+			unsigned long *tmp;
+
+#ifdef CONFIG_MATH_EMULATION
+			if (!hard_math)
+				/* Not supported. */
+				return -EIO;
+#endif
+
+			if (verify_area(VERIFY_READ, (void *) data,
+					sizeof(struct user_i387_struct)))
+			  return -EIO;
+			child->used_math = 1;
+			if (last_task_used_math == child)
+			  {
+			    /* Discard the state of the FPU */
+			    last_task_used_math = NULL;
+			  }
+			tmp = (unsigned long *) &child->tss.i387.hard;
+			for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) )
+			  {
+			    *tmp = get_fs_long ((unsigned long *) data);
+			    data += sizeof(long);
+			    tmp++;
+			  }
+			child->flags &= ~PF_USEDFPU;
+			return 0;
+		  };
 
 		default:
 			return -EIO;
diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.0.34/linux/arch/i386/kernel/setup.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/i386/kernel/setup.c	Mon Jul 13 13:47:27 1998
@@ -32,6 +32,7 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/smp.h>
+#include <asm/io.h>
 
 /*
  * Tell us the machine setup..
@@ -49,7 +50,7 @@
 
 unsigned char Cx86_step = 0;
 static const char *Cx86_type[] = {
-	"unknown", "1.3", "1.4", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2"
+	"unknown", "1.3", "1.4", "1.5", "1.6", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2"
 	};
 
 char ignore_irq13 = 0;		/* set if exception 16 works */
@@ -224,7 +225,9 @@
 static const char * i586model(unsigned int nr)
 {
 	static const char *model[] = {
-		"0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83"
+		"0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83",
+		"Pentium MMX", NULL, NULL, "Mobile Pentium 75+",
+		"Mobile Pentium MMX"
 	};
 	if (nr < sizeof(model)/sizeof(char *))
 		return model[nr];
@@ -235,7 +238,7 @@
 {
 	unsigned char nr6x86 = 0;
 	static const char *model[] = {
-		"unknown", "6x86", "6x86L", "6x86MX", "6x86MXi"
+		"unknown", "6x86", "6x86L", "6x86MX", "MII"
 	};
 	switch (x86) {
 		case 5:
@@ -247,6 +250,10 @@
 		default:
 			nr6x86 = 0;
 	}
+
+	/* We must get the stepping number by reading DIR1 */
+	outb(0xff, 0x22); x86_mask=inb(0x23);
+
 	switch (x86_mask) {
 		case 0x03:
 			Cx86_step =  1;	/* 6x86MX Rev 1.3 */
@@ -254,20 +261,26 @@
 		case 0x04:
 			Cx86_step =  2;	/* 6x86MX Rev 1.4 */
 			break;
+		case 0x05:
+			Cx86_step =  3;	/* 6x86MX Rev 1.5 */
+			break;
+		case 0x06:
+			Cx86_step =  4;	/* 6x86MX Rev 1.6 */
+			break;
 		case 0x14:
-			Cx86_step =  3;	/* 6x86 Rev 2.4 */
+			Cx86_step =  5;	/* 6x86 Rev 2.4 */
 			break;
 		case 0x15:
-			Cx86_step =  4;	/* 6x86 Rev 2.5 */
+			Cx86_step =  6;	/* 6x86 Rev 2.5 */
 			break;
 		case 0x16:
-			Cx86_step =  5;	/* 6x86 Rev 2.6 */
+			Cx86_step =  7;	/* 6x86 Rev 2.6 */
 			break;
 		case 0x17:
-			Cx86_step =  6;	/* 6x86 Rev 2.7 or 3.7 */
+			Cx86_step =  8;	/* 6x86 Rev 2.7 or 3.7 */
 			break;
 		case 0x22:
-			Cx86_step =  7;	/* 6x86L Rev 4.2 */
+			Cx86_step =  9;	/* 6x86L Rev 4.2 */
 			break;
 		default:
 			Cx86_step = 0;
@@ -285,12 +298,44 @@
 	return NULL;
 }
 
+struct cpu_model_info {
+	int x86;
+	char *model_names[16];
+};
+
+static struct cpu_model_info amd_models[] = {
+	{ 4,
+	  { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4",
+	    "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }},
+	{ 5,
+	  { "K5/SSA5 (PR-75, PR-90, PR-100)", "K5 (PR-120, PR-133)",
+	    "K5 (PR-166)", "K5 (PR-200)", NULL, NULL,
+	    "K6 (166 - 266)", "K6 (166 - 300)", "K6-2 (200 - 450)",
+	    "K6-3D-Plus (200 - 450)", NULL, NULL, NULL, NULL, NULL, NULL }},
+};
+
+static const char * AMDmodel(void)
+{
+	const char *p=NULL;
+	int i;
+	
+	if (x86_model < 16)
+		for (i=0; i<sizeof(amd_models)/sizeof(struct cpu_model_info); i++)
+			if (amd_models[i].x86 == x86) {
+				p = amd_models[i].model_names[(int)x86_model];
+				break;
+			}
+	return p;
+}
+
 static const char * getmodel(int x86, int model)
 {
         const char *p = NULL;
         static char nbuf[12];
 	if (strncmp(x86_vendor_id, "Cyrix", 5) == 0)
 		p = Cx86model();
+	else if(strcmp(x86_vendor_id, "AuthenticAMD")==0)
+		p = AMDmodel();
 	else {
 		switch (x86) {
 			case 4:
diff -u --recursive --new-file v2.0.34/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.0.34/linux/arch/i386/kernel/traps.c	Tue Mar 10 13:19:08 1998
+++ linux/arch/i386/kernel/traps.c	Mon Jul 13 13:47:27 1998
@@ -192,13 +192,36 @@
 DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current)
 DO_ERROR(18, SIGSEGV, "reserved", reserved, current)
 
+/* signal_return is directly after ret_from_sys_call in entry.S */
+asmlinkage void ret_from_sys_call(void)	__asm__("ret_from_sys_call");
+asmlinkage void signal_return(void)	__asm__("signal_return");
+
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
 	if (regs->eflags & VM_MASK) {
 		handle_vm86_fault((struct vm86_regs *) regs, error_code);
 		return;
 	}
-	die_if_kernel("general protection",regs,error_code);
+
+	/* 
+	 * HACK HACK HACK  :)  Fixing the segment invalid on syscall return
+	 * barfage for 2.0 has been put into the too-hard basket but having
+	 * a user producing endless GPFs is unacceptable as well. - Paul G.
+	 */
+	if ((regs->cs & 3) != 3) {
+		if (regs->eip >= (unsigned long)ret_from_sys_call &&
+		    regs->eip < (unsigned long)signal_return) {
+			static int moancount = 0;
+			if (moancount < 5) {
+				printk(KERN_INFO "Ignoring GPF attempt from program \"%s\" (pid %d).\n",
+					current->comm, current->pid);
+				moancount++;
+			}
+			do_exit(SIGSEGV);
+		}
+		else 
+			die_if_kernel("general protection",regs,error_code);
+	}
 	current->tss.error_code = error_code;
 	current->tss.trap_no = 13;
 	force_sig(SIGSEGV, current);	
diff -u --recursive --new-file v2.0.34/linux/arch/m68k/atari/stram.c linux/arch/m68k/atari/stram.c
--- v2.0.34/linux/arch/m68k/atari/stram.c	Wed Dec 27 12:44:28 1995
+++ linux/arch/m68k/atari/stram.c	Mon Jul 13 13:47:27 1998
@@ -153,7 +153,7 @@
 /* ++roman:
  * 
  * New version of ST-Ram buffer allocation. Instead of using the
- * 1 MB - 4 KB that remain when the the ST-Ram chunk starts at $1000
+ * 1 MB - 4 KB that remain when the ST-Ram chunk starts at $1000
  * (1 MB granularity!), such buffers are reserved like this:
  *
  *  - If the kernel resides in ST-Ram anyway, we can take the buffer
diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/res_func.S linux/arch/m68k/fpsp040/res_func.S
--- v2.0.34/linux/arch/m68k/fpsp040/res_func.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/res_func.S	Mon Jul 13 13:47:27 1998
@@ -1889,7 +1889,7 @@
 |
 | Input: a0  points to a normalized number in internal extended format
 |	 d0  is the round precision (=1 for sgl; =2 for dbl)
-|	 d1  is the the single precision or double precision
+|	 d1  is the single precision or double precision
 |	     denorm threshold
 |
 | Output: (In the format for dest_sgl or dest_dbl)
diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/slogn.S linux/arch/m68k/fpsp040/slogn.S
--- v2.0.34/linux/arch/m68k/fpsp040/slogn.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/slogn.S	Mon Jul 13 13:47:27 1998
@@ -17,7 +17,7 @@
 |		result is provably monotonic in double precision.
 |
 |	Speed: The program slogn takes approximately 190 cycles for input 
-|		argument X such that |X-1| >= 1/16, which is the the usual 
+|		argument X such that |X-1| >= 1/16, which is the usual 
 |		situation. For those arguments, slognp1 takes approximately
 |		 210 cycles. For the less common arguments, the program will
 |		 run no worse than 10% slower.
diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/ssin.S linux/arch/m68k/fpsp040/ssin.S
--- v2.0.34/linux/arch/m68k/fpsp040/ssin.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/ssin.S	Mon Jul 13 13:47:27 1998
@@ -21,7 +21,7 @@
 |		result is provably monotonic in double precision.
 |
 |	Speed: The programs sSIN and sCOS take approximately 150 cycles for
-|		input argument X such that |X| < 15Pi, which is the the usual
+|		input argument X such that |X| < 15Pi, which is the usual
 |		situation. The speed for sSINCOS is approximately 190 cycles.
 |
 |	Algorithm:
diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/stan.S linux/arch/m68k/fpsp040/stan.S
--- v2.0.34/linux/arch/m68k/fpsp040/stan.S	Fri Apr 26 02:12:35 1996
+++ linux/arch/m68k/fpsp040/stan.S	Mon Jul 13 13:47:27 1998
@@ -16,7 +16,7 @@
 |		result is provably monotonic in double precision.
 |
 |	Speed: The program sTAN takes approximately 170 cycles for
-|		input argument X such that |X| < 15Pi, which is the the usual
+|		input argument X such that |X| < 15Pi, which is the usual
 |		situation.
 |
 |	Algorithm:
diff -u --recursive --new-file v2.0.34/linux/arch/m68k/fpsp040/sto_res.S linux/arch/m68k/fpsp040/sto_res.S
--- v2.0.34/linux/arch/m68k/fpsp040/sto_res.S	Tue Apr 23 21:31:24 1996
+++ linux/arch/m68k/fpsp040/sto_res.S	Mon Jul 13 13:47:27 1998
@@ -3,7 +3,7 @@
 |
 |	Takes the result and puts it in where the user expects it.
 |	Library functions return result in fp0.	If fp0 is not the
-|	users destination register then fp0 is moved to the the
+|	user's destination register then fp0 is moved to the
 |	correct floating-point destination register.  fp0 and fp1
 |	are then restored to the original contents. 
 |
diff -u --recursive --new-file v2.0.34/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c
--- v2.0.34/linux/arch/m68k/mm/memory.c	Wed May 15 23:05:10 1996
+++ linux/arch/m68k/mm/memory.c	Mon Jul 13 13:47:27 1998
@@ -138,7 +138,7 @@
 		return;
 	} else {
 		/*
-		 * move this descriptor the the front of the list, since
+		 * move this descriptor to the front of the list, since
 		 * it has one or more free tables.
 		 */
 		save_flags(flags);
diff -u --recursive --new-file v2.0.34/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.0.34/linux/arch/sparc/config.in	Thu Apr 25 03:22:05 1996
+++ linux/arch/sparc/config.in	Mon Jul 13 13:47:27 1998
@@ -40,6 +40,8 @@
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
diff -u --recursive --new-file v2.0.34/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.0.34/linux/arch/sparc/kernel/head.S	Mon May  6 02:26:03 1996
+++ linux/arch/sparc/kernel/head.S	Mon Jul 13 13:47:27 1998
@@ -549,7 +549,7 @@
 		bnz	srmmu_nviking			! is in mbus mode
 		 nop
 		
-		rd	%psr, %g3			! DONT TOUCH %g3
+		rd	%psr, %g3			! DO NOT TOUCH %g3
 		andn	%g3, PSR_ET, %g2
 		wr	%g2, 0x0, %psr
 		WRITE_PAUSE
@@ -560,10 +560,10 @@
 		set	AC_M_CTPR, %g4
 		lda	[%g4] ASI_M_MMUREGS, %g4
 		sll	%g4, 0x4, %g4			! We use this below
-							! DONT TOUCH %g4
+							! DO NOT TOUCH %g4
 
 		/* Set the AC bit in the Viking's MMU control reg. */
-		lda	[%g0] ASI_M_MMUREGS, %g5	! DONT TOUCH %g5
+		lda	[%g0] ASI_M_MMUREGS, %g5	! DO NOT TOUCH %g5
 		set	0x8000, %g6			! AC bit mask
 		or	%g5, %g6, %g6			! Or it in...
 		sta	%g6, [%g0] ASI_M_MMUREGS	! Close your eyes...
@@ -886,7 +886,7 @@
  * No, it doesn't work, have to play the save/readCWP/restore trick.
  */
 
-		wr	%g0, 0x0, %wim			! so we dont get a trap
+		wr	%g0, 0x0, %wim			! so we do not get a trap
 		WRITE_PAUSE
 
 		save
diff -u --recursive --new-file v2.0.34/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.0.34/linux/drivers/block/Config.in	Mon Aug  4 11:45:55 1997
+++ linux/drivers/block/Config.in	Mon Jul 13 13:47:27 1998
@@ -45,6 +45,8 @@
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
 if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
@@ -52,6 +54,10 @@
 fi
 tristate 'XT harddisk support' CONFIG_BLK_DEV_XD
 
+tristate 'Parallel port IDE device support' CONFIG_PARIDE 
+if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
+  source drivers/block/paride/Config.in
+fi
 
 if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
   define_bool CONFIG_BLK_DEV_HD y
diff -u --recursive --new-file v2.0.34/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.0.34/linux/drivers/block/Makefile	Mon Aug  4 11:45:55 1997
+++ linux/drivers/block/Makefile	Mon Jul 13 13:47:27 1998
@@ -109,6 +109,15 @@
   endif
 endif
 
+ifeq ($(CONFIG_PARIDE),y)
+SUB_DIRS        += paride
+MOD_SUB_DIRS    += paride
+else
+  ifeq ($(CONFIG_PARIDE),m)
+  MOD_SUB_DIRS  += paride
+  endif
+endif
+
 ifeq ($(CONFIG_BLK_DEV_MD),y)
 LX_OBJS += md.o
 
@@ -128,25 +137,21 @@
   endif
 endif
 
-#ifeq ($(CONFIG_MD_RAID1),y)
-#L_OBJS += raid1.o
-#else
-#  ifeq ($(CONFIG_MD_SUPPORT_RAID1),y)
-#    ifeq ($(CONFIG_MD_RAID1),m)
-#    M_OBJS += raid1.o
-#    endif
-#  endif
-#endif
-#
-#ifeq ($(CONFIG_MD_RAID5),y)
-#L_OBJS += raid5.o
-#else
-#  ifeq ($(CONFIG_MD_SUPPORT_RAID5),y)
-#    ifeq ($(CONFIG_MD_RAID5),m)
-#    M_OBJS += raid5.o
-#    endif
-#  endif
-#endif
+ifeq ($(CONFIG_MD_MIRRORING),y)
+L_OBJS += raid1.o
+else
+  ifeq ($(CONFIG_MD_MIRRORING),m)
+  M_OBJS += raid1.o
+  endif
+endif
+
+ifeq ($(CONFIG_MD_RAID5),y)
+L_OBJS += raid5.o
+else
+  ifeq ($(CONFIG_MD_RAID5),m)
+  M_OBJS += raid5.o
+  endif
+endif
 
 endif
 
diff -u --recursive --new-file v2.0.34/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v2.0.34/linux/drivers/block/hd.c	Thu Feb 29 21:50:39 1996
+++ linux/drivers/block/hd.c	Mon Jul 13 13:47:27 1998
@@ -1064,7 +1064,7 @@
 #define DEVICE_BUSY busy[target]
 #define USAGE access_count[target]
 #define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl)
-/* We assume that the the bios parameters do not change, so the disk capacity
+/* We assume that the BIOS parameters do not change, so the disk capacity
    will not change */
 #undef MAYBE_REINIT
 #define GENDISK_STRUCT hd_gendisk
diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.0.34/linux/drivers/block/ide-cd.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/ide-cd.c	Mon Jul 13 13:47:27 1998
@@ -2414,7 +2414,6 @@
 	case CDROMREADMODE2: {
 		struct cdrom_msf msf;
 		int blocksize, format, stat, lba;
-		struct atapi_toc *toc;
 		char *buf;
 
 		if (cmd == CDROMREADMODE1) {
diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.0.34/linux/drivers/block/ide-floppy.c	Mon Aug  4 11:45:55 1997
+++ linux/drivers/block/ide-floppy.c	Mon Jul 13 13:47:27 1998
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-floppy.c	Version 0.7 - ALPHA	Aug   4, 1997
+ * linux/drivers/block/ide-floppy.c	Version 0.71 - ALPHA	May  21, 1998
  *
  * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
  */
@@ -23,6 +23,11 @@
  *                       Fix potential null dereferencing with DEBUG_LOG.
  * Ver 0.6   Jul  3 97   Limit max sectors per read/write command to 64.
  * Ver 0.7   Aug  4 97   Increase irq timeout from 10 to 100 seconds.
+ * Ver 0.71  May 21 98   Disallow opening a write protected media for writing
+ *                       Limit max sectors only on IOMEGA ZIP 21.D
+ *                       Issue START cmd only if TEST_UNIT_READY fails
+ *                       Add CDROMEJECT ioctl
+ *                       Clean up error messages a bit
  */
 
 #include <linux/config.h>
@@ -41,6 +46,7 @@
 #include <linux/hdreg.h>
 #include <linux/genhd.h>
 #include <linux/malloc.h>
+#include <linux/cdrom.h>
 
 #include <asm/byteorder.h>
 #include <asm/irq.h>
@@ -80,10 +86,7 @@
  */
 #define IDEFLOPPY_PC_STACK		(10 + IDEFLOPPY_MAX_PC_RETRIES)
 
-/*
- *	Some drives fail read/write requests with 64 or more sectors.
- */
-#define IDEFLOPPY_MAX_SECTORS		64
+#define IDEFLOPPY_MAX_SECTORS		256
 
 /*
  *	Some drives require a longer irq timeout.
@@ -203,6 +206,8 @@
 	int blocks, block_size, bs_factor;			/* Current format */
 	idefloppy_capacity_descriptor_t capacity;		/* Last format capacity */
 	idefloppy_flexible_disk_page_t flexible_disk_page;	/* Copy of the flexible disk page */
+	int wp;
+	int max_sectors;
 
 	unsigned int flags;			/* Status/Action flags */
 } idefloppy_floppy_t;
@@ -950,10 +955,16 @@
 	pc->c[4] = start;
 }
 
+static void idefloppy_create_test_unit_ready_cmd (idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD;
+}
+
 static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
 {
 	int block = sector / floppy->bs_factor;
-	int blocks = IDEFLOPPY_MIN(rq->nr_sectors / floppy->bs_factor, IDEFLOPPY_MAX_SECTORS);
+	int blocks = IDEFLOPPY_MIN(rq->nr_sectors, floppy->max_sectors) / floppy->bs_factor;
 
 #if IDEFLOPPY_DEBUG_LOG
 	printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
@@ -1057,6 +1068,7 @@
 		return 1;
 	}
 	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+	floppy->wp = header->wp;
 	page = (idefloppy_flexible_disk_page_t *) (header + 1);
 
 	page->transfer_rate = ntohs (page->transfer_rate);
@@ -1075,8 +1087,10 @@
 	drive->bios_sect = page->sectors;
 	lba_capacity = floppy->blocks * floppy->block_size;
 	if (capacity != lba_capacity) {
-		printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
-			drive->name, capacity, lba_capacity);
+		if (!lba_capacity)
+			printk(KERN_NOTICE "%s: no media in the drive\n", drive->name);
+		else printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
+			     drive->name, capacity, lba_capacity);
 		capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
 		floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
 	}
@@ -1143,6 +1157,17 @@
 int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
+	idefloppy_pc_t pc;
+
+	if (cmd == CDROMEJECT) {
+		if (drive->usage > 1)
+			return -EBUSY;
+		idefloppy_create_prevent_cmd (&pc, 0);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		idefloppy_create_start_stop_cmd (&pc, 2);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		return 0;
+	}
 	return -EIO;
 }
 
@@ -1160,13 +1185,21 @@
 
 	MOD_INC_USE_COUNT;
 	if (drive->usage == 1) {
-		idefloppy_create_start_stop_cmd (&pc, 1);
-		(void) idefloppy_queue_pc_tail (drive, &pc);
+		idefloppy_create_test_unit_ready_cmd(&pc);
+		if (idefloppy_queue_pc_tail(drive, &pc)) {
+			idefloppy_create_start_stop_cmd (&pc, 1);
+			(void) idefloppy_queue_pc_tail (drive, &pc);
+		}
 		if (idefloppy_get_capacity (drive)) {
 			drive->usage--;
 			MOD_DEC_USE_COUNT;
 			return -EIO;
 		}
+		if (floppy->wp && (filp->f_mode & 2)) {
+			drive->usage--;
+			MOD_DEC_USE_COUNT;
+			return -EROFS;
+		}
 		set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
 		idefloppy_create_prevent_cmd (&pc, 1);
 		(void) idefloppy_queue_pc_tail (drive, &pc);
@@ -1328,46 +1361,6 @@
 }
 
 /*
- *	idefloppy_get_capabilities asks the floppy about its various
- *	parameters.
- */
-static void idefloppy_get_capabilities (ide_drive_t *drive)
-{
-	idefloppy_pc_t pc;
-	idefloppy_mode_parameter_header_t *header;
-	idefloppy_capabilities_page_t *capabilities;
-	
-	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT);
-	if (idefloppy_queue_pc_tail (drive,&pc)) {
-		printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n");
-		return;
-	}
-	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
-	capabilities = (idefloppy_capabilities_page_t *) (header + 1);
-
-	if (!capabilities->sflp)
-		printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name);
-
-#if IDEFLOPPY_DEBUG_INFO
-	printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n");
-	printk (KERN_INFO "Mode Parameter Header:\n");
-	printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length);
-	printk (KERN_INFO "Medium Type - %d\n",header->medium_type);
-	printk (KERN_INFO "WP - %d\n",header->wp);
-
-	printk (KERN_INFO "Capabilities Page:\n");
-	printk (KERN_INFO "Page code - %d\n",capabilities->page_code);
-	printk (KERN_INFO "Page length - %d\n",capabilities->page_length);
-	printk (KERN_INFO "PS - %d\n",capabilities->ps);
-	printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No");
-	printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No");
-	printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No");
-	printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No");
-	printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No");
-#endif /* IDEFLOPPY_DEBUG_INFO */
-}
-
-/*
  *	Driver initialization.
  */
 void idefloppy_setup (ide_drive_t *drive)
@@ -1382,7 +1375,10 @@
 	floppy->pc = floppy->pc_stack;
 	if (gcw.drq_type == 1)
 		set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
-
-	idefloppy_get_capabilities (drive);
+	if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 &&
+	    strcmp(drive->id->fw_rev, "21.D") == 0)
+		floppy->max_sectors = 64;
+	else
+		floppy->max_sectors = IDEFLOPPY_MAX_SECTORS;
 	(void) idefloppy_get_capacity (drive);
 }
diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.0.34/linux/drivers/block/ide-tape.c	Wed Nov 13 23:34:52 1996
+++ linux/drivers/block/ide-tape.c	Mon Jul 13 13:47:28 1998
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-tape.c	Version 1.9 - ALPHA	Nov   5, 1996
+ * linux/drivers/block/ide-tape.c	Version 1.91		May  21, 1998
  *
  * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
  *
@@ -195,15 +195,8 @@
  *                       MTTELL was sometimes returning incorrect results.
  *                       Return the real block size in the MTIOCGET ioctl.
  *                       Some error recovery bug fixes.
- *
- * We are currently in an *alpha* stage. The driver is not complete and not
- * much tested. I would strongly suggest to:
- *
- *	1. Connect the tape to a separate interface and irq.
- *	2. Be truly prepared for a kernel crash and the resulting data loss.
- *	3. Don't rely too much on the resulting backups.
- *
- * Other than that, enjoy !
+ * Ver 1.91  May 21 98   Add support for INTERRUPT DRQ devices.
+ *                       Add "speed == 0" work-around for HP COLORADO 5GB
  *
  * Here are some words from the first releases of hd.c, which are quoted
  * in ide.c and apply here as well:
@@ -1157,11 +1150,6 @@
 		printk ("ide-tape: The removable flag is not set\n");support=0;
 	}
 
-	if (gcw.drq_type != 2) {
-		printk ("ide-tape: Sorry, DRQ types other than Accelerated DRQ\n");
-		printk ("ide-tape: are still not supported by the driver\n");support=0;
-	}
-
 	if (gcw.packet_size != 0) {
 		printk ("ide-tape: Packet size is not 12 bytes long\n");
 		if (gcw.packet_size == 1)
@@ -1233,6 +1221,8 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 	unsigned int allocation_length;
+	struct idetape_id_gcw gcw;
+
 #if IDETAPE_ANTICIPATE_READ_WRITE_DSC
 	ide_hwif_t *hwif = HWIF(drive);
 	unsigned long t1, tmid, tn;
@@ -1260,7 +1250,10 @@
 	tape->chrdev_direction=idetape_direction_none;
 	tape->reset_issued=0;
 	tape->pc=&(tape->pc_stack [0]);
-	
+
+	*((unsigned short *) &gcw) = drive->id->config;
+	tape->drq_interrupt = (gcw.drq_type == 1) ? 1 : 0;
+
 #if IDETAPE_PIPELINE
 	tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
 #else
@@ -1397,6 +1390,15 @@
 	capabilities->speed=idetape_swap_short (capabilities->speed);
 	capabilities->buffer_size=idetape_swap_short (capabilities->buffer_size);
 
+	if (!capabilities->speed) {
+		printk("ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name);
+		capabilities->speed = 650;
+	}
+	if (!capabilities->max_speed) {
+		printk("ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name);
+		capabilities->max_speed = 650;
+	}
+
 	tape->capabilities=*capabilities;		/* Save us a copy */
 	tape->tape_block_size=capabilities->blk512 ? 512:1024;
 
@@ -1441,6 +1443,26 @@
 #endif /* IDETAPE_DEBUG_LOG */
 }
 
+static void idetape_transfer_pc (ide_drive_t *drive)
+{
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_packet_command_t *pc = tape->pc;
+	idetape_ireason_reg_t ireason;
+
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return;
+	}
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+	if (!ireason.b.cod || ireason.b.io) {
+		printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
+		ide_do_reset (drive);
+		return;
+	}
+	ide_set_handler (drive, &idetape_pc_intr, WAIT_CMD);	/* Set the interrupt routine */
+	ide_output_data (drive,pc->c,12/4);			/* Send the actual packet */
+}
+
 /*
  *	Packet Command Interface
  *
@@ -1489,7 +1511,6 @@
 {
 	idetape_tape_t *tape;
 	idetape_bcount_reg_t bcount;
-	idetape_ireason_reg_t ireason;
 	int dma_ok=0;
 
 	tape=&(drive->tape);
@@ -1562,37 +1583,21 @@
 	OUT_BYTE (bcount.b.high,IDETAPE_BCOUNTH_REG);
 	OUT_BYTE (bcount.b.low,IDETAPE_BCOUNTL_REG);
 	OUT_BYTE (drive->select.all,IDETAPE_DRIVESEL_REG);
-	
-	ide_set_handler (drive,handler,WAIT_CMD);			/* Set the interrupt routine */
-	OUT_BYTE (WIN_PACKETCMD,IDETAPE_ATACOMMAND_REG);		/* Issue the packet command */
-	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { 	/* Wait for DRQ to be ready - Assuming Accelerated DRQ */	
-		/*
-		 *	We currently only support tape drives which report
-		 *	accelerated DRQ assertion. For this case, specs
-		 *	allow up to 50us. We really shouldn't get here.
-		 *
-		 *	??? Still needs to think what to do if we reach
-		 *	here anyway.
-		 */
-		 
-		printk ("ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
-		return;
-	}
-	
-	ireason.all=IN_BYTE (IDETAPE_IREASON_REG);
-	if (!ireason.b.cod || ireason.b.io) {
-		printk ("ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
-		ide_do_reset (drive);
-		return;		
-	}
-		
-	ide_output_data (drive,pc->c,12/4);			/* Send the actual packet */
+
 #ifdef CONFIG_BLK_DEV_TRITON
 	if ((pc->dma_in_progress=dma_ok)) {			/* Begin DMA, if necessary */
 		pc->dma_error=0;
 		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
 	}
 #endif /* CONFIG_BLK_DEV_TRITON */
+
+	if (tape->drq_interrupt) {
+		ide_set_handler (drive, &idetape_transfer_pc, WAIT_CMD);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+		idetape_transfer_pc (drive);
+	}
 }
 
 /*
diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide-tape.h linux/drivers/block/ide-tape.h
--- v2.0.34/linux/drivers/block/ide-tape.h	Wed Nov  6 04:48:32 1996
+++ linux/drivers/block/ide-tape.h	Mon Jul 13 13:47:28 1998
@@ -511,6 +511,8 @@
 	idetape_pipeline_stage_t *last_stage;	/* New requests will be added to the pipeline here */
 	int error_in_pipeline_stage;		/* Set when an error was detected in one of the pipeline stages */	
 	
+	int drq_interrupt;
+	
 } idetape_tape_t;
 
 /*
diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.0.34/linux/drivers/block/ide.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/ide.c	Mon Jul 13 13:47:28 1998
@@ -273,6 +273,10 @@
  *			add work-around for BMI drives
  *			remove "LBA" from boot messages
  * Version 5.53.1	add UDMA "CRC retry" support
+ * Version 5.53.2	add Promise/33 auto-detection and DMA support
+ *			fix MC_ERR handling
+ *			fix mis-detection of NEC cdrom as floppy
+ *			issue ATAPI reset and re-probe after "no response"
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -1059,6 +1063,8 @@
 				rq->errors = ERROR_MAX;
 			else if (err & TRK0_ERR)	/* help it find track zero */
 				rq->errors |= ERROR_RECAL;
+			else if (err & MC_ERR)
+				drive->special.b.mc = 1;
 		}
 		if ((stat & DRQ_STAT) && rq->cmd != WRITE)
 			try_to_flush_leftover_data(drive);
@@ -2447,11 +2453,11 @@
 			return;
 		}
 #endif /* CONFIG_BLK_DEV_PROMISE */
-		switch (type) {
+		if (!drive->ide_scsi) switch (type) {
 			case 0:
 				if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
 					printk("cdrom or floppy?, assuming ");
-				if (drive->media != ide_cdrom) {
+				if (drive->media != ide_cdrom && !strstr(id->model, "CD-ROM")) {
 #ifdef CONFIG_BLK_DEV_IDEFLOPPY
 					printk("FLOPPY drive\n");
 					drive->media = ide_floppy;
@@ -2728,6 +2734,7 @@
 {
 	int rc;
 	ide_hwif_t *hwif = HWIF(drive);
+	unsigned long timeout;
 #ifdef CONFIG_BLK_DEV_IDEATAPI
 	if (drive->present) {	/* avoid waiting for inappropriate probes */
 		if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
@@ -2752,6 +2759,17 @@
 	{
 		if ((rc = try_to_identify(drive,cmd)))   /* send cmd and wait */
 			rc = try_to_identify(drive,cmd); /* failed: try again */
+		if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) {
+			printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT());
+			delay_50ms();
+			OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+			delay_50ms();
+			OUT_BYTE(WIN_SRST, IDE_COMMAND_REG);
+			timeout = jiffies;
+			while ((GET_STAT() & BUSY_STAT) && jiffies < timeout + WAIT_WORSTCASE)
+				delay_50ms();
+			rc = try_to_identify(drive, cmd);
+		}
 		if (rc == 1)
 			printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
 		(void) GET_STAT();		/* ensure drive irq is clear */
@@ -3085,7 +3103,7 @@
 	if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
 		const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
 				"serialize", "autotune", "noautotune",
-				"slow", NULL};
+				"slow", "ide-scsi", NULL};
 		unit = s[2] - 'a';
 		hw   = unit / MAX_DRIVES;
 		unit = unit % MAX_DRIVES;
@@ -3118,6 +3136,9 @@
 			case -8: /* "slow" */
 				drive->slow = 1;
 				goto done;
+			case -9: /* "ide-scsi" */
+				drive->ide_scsi = 1;
+				goto done;
 			case 3: /* cyl,head,sect */
 				drive->media	= ide_disk;
 				drive->cyl	= drive->bios_cyl  = vals[0];
@@ -3514,6 +3535,43 @@
 }
 
 #endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
+
+static void ide_probe_promise_20246(void)
+{
+	byte fn, bus;
+	unsigned short io[6], count = 0;
+	unsigned int reg, tmp, i;
+	ide_hwif_t *hwif;
+
+	memset(io, 0, 6 * sizeof(unsigned short));
+	if (pcibios_find_device(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, 0, &bus, &fn))
+		return;
+	printk("ide: Promise Technology IDE Ultra-DMA 33 on PCI bus %d function %d\n", bus, fn);
+	for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+		pcibios_read_config_dword(bus, fn, reg, &tmp);
+		if (tmp & PCI_BASE_ADDRESS_SPACE_IO)
+			io[count++] = tmp & PCI_BASE_ADDRESS_IO_MASK;
+	}
+	for (i = 2; i < 4; i++) {
+		hwif = ide_hwifs + i;
+		if (hwif->chipset == ide_generic) {
+			printk("ide%d: overridden with command line parameter\n", i);
+			return;
+		}
+		tmp = (i - 2) * 2;
+		if (!io[tmp] || !io[tmp + 1]) {
+			printk("ide%d: invalid port address %x, %x -- aborting\n", i, io[tmp], io[tmp + 1]);
+			return;
+		}
+		hwif->io_base = io[tmp];
+		hwif->ctl_port = io[tmp + 1] + 2;
+		hwif->noprobe = 0;
+	}
+#ifdef CONFIG_BLK_DEV_TRITON
+	ide_init_promise (bus, fn, &ide_hwifs[2], &ide_hwifs[3], io[4]);
+#endif /* CONFIG_BLK_DEV_TRITON */
+}
+
 #endif /* CONFIG_PCI */
 
 /*
@@ -3545,6 +3603,7 @@
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
 #endif /* CONFIG_BLK_DEV_TRITON */
+		ide_probe_promise_20246();
 	}
 #endif /* CONFIG_PCI */
 #ifdef CONFIG_BLK_DEV_CMD640
diff -u --recursive --new-file v2.0.34/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.0.34/linux/drivers/block/ide.h	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/ide.h	Mon Jul 13 13:47:28 1998
@@ -381,6 +381,7 @@
 #ifdef CONFIG_BLK_DEV_IDESCSI
 	void *scsi;			/* for ide-scsi.c */
 #endif /* CONFIG_BLK_DEV_IDESCSI */
+	byte		ide_scsi;	/* use ide-scsi driver */
 	} ide_drive_t;
 
 /*
@@ -428,7 +429,7 @@
 typedef enum {	ide_unknown,	ide_generic,	ide_triton,
 		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
 		ide_qd6580,	ide_umc8672,	ide_ht6560b,
-		ide_promise }
+		ide_promise,	ide_promise_udma }
 	hwif_chipset_t;
 
 typedef struct hwif_s {
@@ -742,4 +743,5 @@
 
 #ifdef CONFIG_BLK_DEV_TRITON
 void ide_init_triton (byte, byte);
+void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, unsigned short dma);
 #endif /* CONFIG_BLK_DEV_TRITON */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/linear.c linux/drivers/block/linear.c
--- v2.0.34/linux/drivers/block/linear.c	Sun May 19 21:50:46 1996
+++ linux/drivers/block/linear.c	Mon Jul 13 13:47:28 1998
@@ -161,6 +161,7 @@
 
   sz+=sprintf (page+sz, "\n");
 #endif
+  sz+=sprintf (page+sz, " %dk rounding", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -169,6 +170,8 @@
 {
   "linear",
   linear_map,
+  NULL,
+  NULL,
   linear_run,
   linear_stop,
   linear_status,
diff -u --recursive --new-file v2.0.34/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.0.34/linux/drivers/block/ll_rw_blk.c	Wed Feb 26 11:10:15 1997
+++ linux/drivers/block/ll_rw_blk.c	Mon Jul 13 13:47:28 1998
@@ -280,7 +280,30 @@
 	sti();
 }
 
-static void make_request(int major,int rw, struct buffer_head * bh)
+#define MAX_SECTORS 244
+
+static inline void attempt_merge (struct request *req)
+{
+	struct request *next = req->next;
+
+	if (!next)
+		return;
+	if (req->sector + req->nr_sectors != next->sector)
+		return;
+	if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS)
+		return;
+#if 0
+	printk ("%s: merge %ld, %ld + %ld == %ld\n", kdevname(req->rq_dev), req->sector, req->nr_sectors, next->nr_sectors, req->nr_sectors + next->nr_sectors);
+#endif	
+	req->bhtail->b_reqnext = next->bh;
+	req->bhtail = next->bhtail;
+	req->nr_sectors += next->nr_sectors;
+	next->rq_status = RQ_INACTIVE;
+	req->next = next->next;
+	wake_up (&wait_for_request);
+}
+
+void make_request(int major,int rw, struct buffer_head * bh)
 {
 	unsigned int sector, count;
 	struct request * req;
@@ -290,8 +313,12 @@
 	sector = bh->b_rsector;
 
 	/* Uhhuh.. Nasty dead-lock possible here.. */
-	if (buffer_locked(bh))
+	if (buffer_locked(bh)) {
+#if 0
+		printk("make_request(): buffer already locked\n");
+#endif
 		return;
+	}
 	/* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
 
 	lock_buffer(bh);
@@ -319,6 +346,9 @@
 			rw = READ;	/* drop into READ */
 		case READ:
 			if (buffer_uptodate(bh)) {
+#if 0
+				printk ("make_request(): buffer uptodate for READ\n");
+#endif
 				unlock_buffer(bh); /* Hmmph! Already have it */
 				return;
 			}
@@ -330,6 +360,9 @@
 			rw = WRITE;	/* drop into WRITE */
 		case WRITE:
 			if (!buffer_dirty(bh)) {
+#if 0
+				printk ("make_request(): buffer clean for WRITE\n");
+#endif
 				unlock_buffer(bh); /* Hmmph! Nothing to write */
 				return;
 			}
@@ -391,7 +424,7 @@
 				continue;
 			if (req->cmd != rw)
 				continue;
-			if (req->nr_sectors >= 244)
+			if (req->nr_sectors >= MAX_SECTORS)
 				continue;
 			if (req->rq_dev != bh->b_rdev)
 				continue;
@@ -399,6 +432,9 @@
 			if (req->sector + req->nr_sectors == sector) {
 				req->bhtail->b_reqnext = bh;
 				req->bhtail = bh;
+			    	req->nr_sectors += count;
+				/* Can we now merge this req with the next? */
+				attempt_merge(req);
 			/* or to the beginning? */
 			} else if (req->sector - count == sector) {
 			    	bh->b_reqnext = req->bh;
@@ -406,10 +442,10 @@
 			    	req->buffer = bh->b_data;
 			    	req->current_nr_sectors = count;
 			    	req->sector = sector;
+			    	req->nr_sectors += count;
 			} else
 				continue;
 
-		    	req->nr_sectors += count;
 			mark_buffer_clean(bh);
 		    	sti();
 		    	return;
@@ -512,6 +548,12 @@
 	for (i = 0; i < nr; i++) {
 		if (bh[i]) {
 			set_bit(BH_Req, &bh[i]->b_state);
+#ifdef CONFIG_BLK_DEV_MD
+			if (MAJOR(bh[i]->b_dev) == MD_MAJOR) {
+				md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]);
+				continue;
+			}
+#endif
 			make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
 		}
 	}
@@ -645,6 +687,9 @@
 #endif
 #ifdef CONFIG_BLK_DEV_XD
 	xd_init();
+#endif
+#ifdef CONFIG_PARIDE
+        { extern void paride_init(void); paride_init(); };
 #endif
 #ifdef CONFIG_BLK_DEV_FD
 	floppy_init();
diff -u --recursive --new-file v2.0.34/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.0.34/linux/drivers/block/md.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/md.c	Mon Jul 13 13:47:28 1998
@@ -9,6 +9,9 @@
 
    kerneld support by Boris Tobotras <boris@xtalk.msk.su>
 
+   RAID-1/RAID-5 extensions by:
+        Ingo Molnar, Miguel de Icaza, Gadi Oxman
+   
    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, or (at your option)
@@ -31,18 +34,27 @@
 #include <linux/proc_fs.h>
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
+#include <linux/smp_lock.h>
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
 #include <linux/errno.h>
+/*
+ * For kernel_thread()
+ */
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
 
 #define MAJOR_NR MD_MAJOR
 #define MD_DRIVER
 
 #include <linux/blk.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
 
 static struct hd_struct md_hd_struct[MAX_MD_DEV];
 static int md_blocksizes[MAX_MD_DEV];
+static struct md_thread md_threads[MAX_MD_THREADS];
 
 int md_size[MAX_MD_DEV]={0, };
 
@@ -91,8 +103,7 @@
 
   if (!hd)
   {
-    printk ("No gendisk entry for dev %s\n", kdevname(dev));
-    sprintf (name, "dev %s", kdevname(dev));
+    sprintf (name, "[dev %s]", kdevname(dev));
     return (name);
   }
 
@@ -117,23 +128,196 @@
   read_ahead[MD_MAJOR]=minra;
 }
 
+static int legacy_raid_sb (int minor, int pnum)
+{
+	int i, factor;
+
+	factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+
+	/*****
+	 * do size and offset calculations.
+	 */
+	for (i=0; i<md_dev[minor].nb_dev; i++) {
+		md_dev[minor].devices[i].size &= ~(factor - 1);
+		md_size[minor] += md_dev[minor].devices[i].size;
+		md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + 
+							md_dev[minor].devices[i-1].size) : 0;
+	}
+	return 0;
+}
+
+static void free_sb (struct md_dev *mddev)
+{
+	int i;
+	struct real_dev *realdev;
+
+	if (mddev->sb) {
+		free_page((unsigned long) mddev->sb);
+		mddev->sb = NULL;
+	}
+	for (i = 0; i <mddev->nb_dev; i++) {
+		realdev = mddev->devices + i;
+		if (realdev->sb) {
+			free_page((unsigned long) realdev->sb);
+			realdev->sb = NULL;
+		}
+	}
+}
+
+static int analyze_sb (int minor, int pnum)
+{
+	int i;
+	struct md_dev *mddev = md_dev + minor;
+	struct buffer_head *bh;
+	kdev_t dev;
+	struct real_dev *realdev;
+	u32 sb_offset, device_size;
+	md_superblock_t *sb = NULL;
+
+	/*
+	 * raid-0 and linear don't use a raid superblock
+	 */
+	if (pnum == RAID0 >> PERSONALITY_SHIFT || pnum == LINEAR >> PERSONALITY_SHIFT)
+		return legacy_raid_sb(minor, pnum);
+	
+	/*
+	 * Verify the raid superblock on each real device
+	 */
+	for (i = 0; i < mddev->nb_dev; i++) {
+		realdev = mddev->devices + i;
+		dev = realdev->dev;
+		device_size = blk_size[MAJOR(dev)][MINOR(dev)];
+		realdev->sb_offset = sb_offset = MD_NEW_SIZE_BLOCKS(device_size);
+		set_blocksize(dev, MD_SB_BYTES);
+		bh = bread(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+		if (bh) {
+			sb = (md_superblock_t *) bh->b_data;
+			if (sb->md_magic != MD_SB_MAGIC) {
+				printk("md: %s: invalid raid superblock magic (%x) on block %u\n", kdevname(dev), sb->md_magic, sb_offset);
+				goto abort;
+			}
+			if (!mddev->sb) {
+				mddev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL);
+				if (!mddev->sb)
+					goto abort;
+				memcpy(mddev->sb, sb, MD_SB_BYTES);
+			}
+			realdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL);
+			if (!realdev->sb)
+				goto abort;
+			memcpy(realdev->sb, bh->b_data, MD_SB_BYTES);
+
+			if (memcmp(mddev->sb, sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) {
+				printk(KERN_ERR "md: superblock inconsistenty -- run ckraid\n");
+				goto abort;
+			}
+			/*
+			 * Find the newest superblock version
+			 */
+			if (sb->utime != mddev->sb->utime) {
+				printk(KERN_ERR "md: superblock update time inconsistenty -- using the most recent one\n");
+				if (sb->utime > mddev->sb->utime)
+					memcpy(mddev->sb, sb, MD_SB_BYTES);
+			}
+			realdev->size = sb->size;
+		} else
+			printk(KERN_ERR "md: disabled device %s\n", kdevname(dev));
+	}
+	if (!mddev->sb) {
+		printk(KERN_ERR "md: couldn't access raid array %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+	sb = mddev->sb;
+
+	/*
+	 * Check if we can support this raid array
+	 */
+	if (sb->major_version != MD_MAJOR_VERSION || sb->minor_version > MD_MINOR_VERSION) {
+		printk("md: %s: unsupported raid array version %d.%d.%d\n", kdevname(MKDEV(MD_MAJOR, minor)),
+		sb->major_version, sb->minor_version, sb->patch_version);
+		goto abort;
+	}
+	if (sb->state != (1 << MD_SB_CLEAN)) {
+		printk(KERN_ERR "md: %s: raid array is not clean -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+	switch (sb->level) {
+		case 1:
+			md_size[minor] = sb->size;
+			break;
+		case 4:
+		case 5:
+			md_size[minor] = sb->size * (sb->raid_disks - 1);
+			break;
+		default:
+			printk(KERN_ERR "md: %s: unsupported raid level %d\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+			goto abort;
+	}
+	return 0;
+abort:
+	free_sb(mddev);
+	return 1;
+}
+
+int md_update_sb(int minor)
+{
+	struct md_dev *mddev = md_dev + minor;
+	struct buffer_head *bh;
+	md_superblock_t *sb = mddev->sb;
+	struct real_dev *realdev;
+	kdev_t dev;
+	int i;
+	u32 sb_offset;
+
+	sb->utime = CURRENT_TIME;
+	for (i = 0; i < mddev->nb_dev; i++) {
+		realdev = mddev->devices + i;
+		if (!realdev->sb)
+			continue;
+		dev = realdev->dev;
+		sb_offset = realdev->sb_offset;
+		set_blocksize(dev, MD_SB_BYTES);
+		printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset);
+		bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+		if (bh) {
+			sb = (md_superblock_t *) bh->b_data;
+			memcpy(sb, mddev->sb, MD_SB_BYTES);
+			memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4);
+			mark_buffer_uptodate(bh, 1);
+			mark_buffer_dirty(bh, 1);
+			ll_rw_block(WRITE, 1, &bh);
+			wait_on_buffer(bh);
+			bforget(bh);
+			fsync_dev(dev);
+			invalidate_buffers(dev);
+		} else
+			printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev));
+	}
+	return 0;
+}
 
 static int do_md_run (int minor, int repart)
 {
-  int pnum, i, min, current_ra, err;
-  
+  int pnum, i, min, factor, current_ra, err;
+
   if (!md_dev[minor].nb_dev)
     return -EINVAL;
   
   if (md_dev[minor].pers)
     return -EBUSY;
-  
+
   md_dev[minor].repartition=repart;
   
-  if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT))
+  if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT))
       >= MAX_PERSONALITY)
     return -EINVAL;
-  
+
+  /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */
+  if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){
+	  for (i = 0; i < md_dev [minor].nb_dev; i++)
+		  if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR)
+			  return -EINVAL;
+  }
   if (!pers[pnum])
   {
 #ifdef CONFIG_KERNELD
@@ -145,7 +329,7 @@
       return -EINVAL;
   }
   
-  min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+  factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
   
   for (i=0; i<md_dev[minor].nb_dev; i++)
     if (md_dev[minor].devices[i].size<min)
@@ -154,26 +338,37 @@
 	      partition_name (md_dev[minor].devices[i].dev), min);
       return -EINVAL;
     }
+
+  for (i=0; i<md_dev[minor].nb_dev; i++) {
+    fsync_dev(md_dev[minor].devices[i].dev);
+    invalidate_buffers(md_dev[minor].devices[i].dev);
+  }
   
   /* Resize devices according to the factor. It is used to align
      partitions size on a given chunk size. */
   md_size[minor]=0;
-  
-  for (i=0; i<md_dev[minor].nb_dev; i++)
-  {
-    md_dev[minor].devices[i].size &= ~(min - 1);
-    md_size[minor] += md_dev[minor].devices[i].size;
-    md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + md_dev[minor].devices[i-1].size) : 0;
-  }
+
+  /*
+   * Analyze the raid superblock
+   */ 
+  if (analyze_sb(minor, pnum))
+    return -EINVAL;
 
   md_dev[minor].pers=pers[pnum];
   
   if ((err=md_dev[minor].pers->run (minor, md_dev+minor)))
   {
     md_dev[minor].pers=NULL;
+    free_sb(md_dev + minor);
     return (err);
   }
-  
+
+  if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT)
+  {
+    md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN);
+    md_update_sb(minor);
+  }
+
   /* FIXME : We assume here we have blocks
      that are twice as large as sectors.
      THIS MAY NOT BE TRUE !!! */
@@ -191,7 +386,6 @@
   
   read_ahead[MD_MAJOR]=current_ra;
   
-  printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
   return (0);
 }
 
@@ -211,38 +405,40 @@
     /*  The device won't exist anymore -> flush it now */
     fsync_dev (inode->i_rdev);
     invalidate_buffers (inode->i_rdev);
+    if (md_dev[minor].sb)
+    {
+      md_dev[minor].sb->state |= 1 << MD_SB_CLEAN;
+      md_update_sb(minor);
+    }
     md_dev[minor].pers->stop (minor, md_dev+minor);
   }
   
   /* Remove locks. */
+  if (md_dev[minor].sb)
+    free_sb(md_dev + minor);
   for (i=0; i<md_dev[minor].nb_dev; i++)
     clear_inode (md_dev[minor].devices[i].inode);
-  
+
   md_dev[minor].nb_dev=md_size[minor]=0;
   md_hd_struct[minor].nr_sects=0;
   md_dev[minor].pers=NULL;
   
   set_ra ();			/* calculate new read_ahead */
   
-  printk ("STOP_DEV md%x\n", minor);
   return (0);
 }
 
 
 static int do_md_add (int minor, kdev_t dev)
 {
-  struct gendisk *gen_real;
   int i;
-  
-  if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
+
+  if (md_dev[minor].nb_dev==MAX_REAL)
     return -EINVAL;
   
   if (!fs_may_mount (dev) || md_dev[minor].pers)
     return -EBUSY;
-  
-  if (!(gen_real=find_gendisk (dev)))
-    return -ENOENT;
-  
+
   i=md_dev[minor].nb_dev++;
   md_dev[minor].devices[i].dev=dev;
   
@@ -258,7 +454,13 @@
   
   /* Sizes are now rounded at run time */
   
-  md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)];
+/*  md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/
+
+  if (blk_size[MAJOR(dev)][MINOR(dev)] == 0) {
+	printk("md_add(): zero device size, huh, bailing out.\n");
+  }
+
+  md_dev[minor].devices[i].size=blk_size[MAJOR(dev)][MINOR(dev)];
 
   printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
   return (0);
@@ -420,6 +622,27 @@
   return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size));
 }
   
+int md_make_request (int minor, int rw, struct buffer_head * bh)
+{
+	if (md_dev [minor].pers->make_request) {
+		if (buffer_locked(bh))
+			return 0;
+		if (rw == WRITE || rw == WRITEA) {
+			if (!buffer_dirty(bh))
+				return 0;
+			set_bit(BH_Lock, &bh->b_state);
+		}
+		if (rw == READ || rw == READA) {
+			if (buffer_uptodate(bh))
+				return 0;
+			set_bit (BH_Lock, &bh->b_state);
+		}
+		return (md_dev[minor].pers->make_request(md_dev+minor, rw, bh));
+	} else {
+		make_request (MAJOR(bh->b_rdev), rw, bh);
+		return 0;
+	}
+}
 
 static void do_md_request (void)
 {
@@ -427,6 +650,40 @@
   return;
 }  
 
+/*
+ * We run MAX_MD_THREADS from md_init() and arbitrate them in run time.
+ * This is not so elegant, but how can we use kernel_thread() from within
+ * loadable modules?
+ */
+struct md_thread *md_register_thread (void (*run) (void *), void *data)
+{
+	int i;
+	for (i = 0; i < MAX_MD_THREADS; i++) {
+		if (md_threads[i].run == NULL) {
+			md_threads[i].run = run;
+			md_threads[i].data = data;
+			return md_threads + i;
+		}
+	}
+	return NULL;
+}
+
+
+void md_unregister_thread (struct md_thread *thread)
+{
+	thread->run = NULL;
+	thread->data = NULL;
+	thread->flags = 0;
+}
+
+void md_wakeup_thread(struct md_thread *thread)
+{
+	set_bit(THREAD_WAKEUP, &thread->flags);
+	wake_up(&thread->wqueue);
+}
+
+struct buffer_head *efind_buffer(kdev_t dev, int block, int size);
+
 static struct symbol_table md_symbol_table=
 {
 #include <linux/symtab_begin.h>
@@ -435,11 +692,18 @@
   X(register_md_personality),
   X(unregister_md_personality),
   X(partition_name),
+  X(md_dev),
+  X(md_error),
+  X(md_register_thread),
+  X(md_unregister_thread),
+  X(md_update_sb),
+  X(md_map),
+  X(md_wakeup_thread),
+  X(efind_buffer),
 
 #include <linux/symtab_end.h>
 };
 
-
 static void md_geninit (struct gendisk *gdisk)
 {
   int i;
@@ -463,6 +727,17 @@
 	      });
 }
 
+int md_error (kdev_t mddev, kdev_t rdev)
+{
+    unsigned int minor = MINOR (mddev);
+    if (MAJOR(mddev) != MD_MAJOR || minor > MAX_MD_DEV)
+	panic ("md_error gets unknown device\n");
+    if (!md_dev [minor].pers)
+	panic ("md_error gets an error for an unknown device\n");
+    if (md_dev [minor].pers->error_handler)
+	return (md_dev [minor].pers->error_handler (md_dev+minor, rdev));
+    return 0;
+}
 
 int get_md_status (char *page)
 {
@@ -495,9 +770,13 @@
 		   partition_name(md_dev[i].devices[j].dev));
       size+=md_dev[i].devices[j].size;
     }
-    
-    if (md_dev[i].nb_dev)
-      sz+=sprintf (page+sz, " %d blocks", size);
+
+    if (md_dev[i].nb_dev) {
+      if (md_dev[i].pers)
+        sz+=sprintf (page+sz, " %d blocks", md_size[i]);
+      else
+        sz+=sprintf (page+sz, " %d blocks", size);
+    }
 
     if (!md_dev[i].pers)
     {
@@ -508,11 +787,8 @@
     if (md_dev[i].pers->max_invalid_dev)
       sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i));
 
-    sz+=sprintf (page+sz, " %dk %s\n", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)),
-		 md_dev[i].pers == pers[LINEAR>>PERSONALITY_SHIFT] ?
-		 "rounding" : "chunks");
-
     sz+=md_dev[i].pers->status (page+sz, i, md_dev+i);
+    sz+=sprintf (page+sz, "\n");
   }
 
   return (sz);
@@ -545,6 +821,32 @@
   return 0;
 } 
 
+int md_thread(void * arg)
+{
+	struct md_thread *thread = arg;
+
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "md_thread");
+
+#ifdef __SMP__
+	lock_kernel();
+	syscall_count++;
+#endif
+	for (;;) {
+		sti();
+		clear_bit(THREAD_WAKEUP, &thread->flags);
+		if (thread->run) {
+			thread->run(thread->data);
+			run_task_queue(&tq_disk);
+		}
+		current->signal = 0;
+		cli();
+		if (!test_bit(THREAD_WAKEUP, &thread->flags))
+			interruptible_sleep_on(&thread->wqueue);
+	}
+}
+
 void linear_init (void);
 void raid0_init (void);
 void raid1_init (void);
@@ -552,7 +854,11 @@
 
 int md_init (void)
 {
-  printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+  int i;
+
+  printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n",
+    MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION,
+    MAX_MD_DEV, MAX_REAL);
 
   if (register_blkdev (MD_MAJOR, "md", &md_fops))
   {
@@ -560,9 +866,17 @@
     return (-1);
   }
 
+  for (i = 0; i < MAX_MD_THREADS; i++) {
+    md_threads[i].run = NULL;
+    init_waitqueue(&md_threads[i].wqueue);
+    md_threads[i].flags = 0;
+    kernel_thread (md_thread, md_threads + i, 0);
+  }
+
   blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
   blk_dev[MD_MAJOR].current_request=NULL;
   read_ahead[MD_MAJOR]=INT_MAX;
+  memset(md_dev, 0, MAX_MD_DEV * sizeof (struct md_dev));
   md_gendisk.next=gendisk_head;
 
   gendisk_head=&md_gendisk;
@@ -572,6 +886,12 @@
 #endif
 #ifdef CONFIG_MD_STRIPED
   raid0_init ();
+#endif
+#ifdef CONFIG_MD_MIRRORING
+  raid1_init ();
+#endif
+#ifdef CONFIG_MD_RAID5
+  raid5_init ();
 #endif
   
   return (0);
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/Config.in linux/drivers/block/paride/Config.in
--- v2.0.34/linux/drivers/block/paride/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/Config.in	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,24 @@
+#
+# PARIDE configuration
+#
+comment 'Parallel IDE high-level drivers'
+dep_tristate '  Parallel port IDE disks' CONFIG_PARIDE_PD $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI CD-ROMs' CONFIG_PARIDE_PCD $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI disks' CONFIG_PARIDE_PF $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI tapes' CONFIG_PARIDE_PT $CONFIG_PARIDE
+dep_tristate '  Parallel port generic ATAPI devices' CONFIG_PARIDE_PG $CONFIG_PARIDE
+comment 'Parallel IDE protocol modules'
+dep_tristate '    ATEN EH-100 protocol' CONFIG_PARIDE_ATEN $CONFIG_PARIDE
+dep_tristate '    MicroSolutions backpack protocol' CONFIG_PARIDE_BPCK $CONFIG_PARIDE
+dep_tristate '    DataStor Commuter protocol' CONFIG_PARIDE_COMM $CONFIG_PARIDE
+dep_tristate '    DataStor EP-2000 protocol' CONFIG_PARIDE_DSTR $CONFIG_PARIDE
+dep_tristate '    FIT TD-2000 protocol' CONFIG_PARIDE_FIT2 $CONFIG_PARIDE
+dep_tristate '    FIT TD-3000 protocol' CONFIG_PARIDE_FIT3 $CONFIG_PARIDE
+dep_tristate '    Shuttle EPAT/EPEZ protocol' CONFIG_PARIDE_EPAT $CONFIG_PARIDE
+dep_tristate '    Shuttle EPIA protocol' CONFIG_PARIDE_EPIA $CONFIG_PARIDE
+dep_tristate '    FreeCom power protocol' CONFIG_PARIDE_FRPW $CONFIG_PARIDE
+dep_tristate '    KingByte KBIC-951A/971A protocols' CONFIG_PARIDE_KBIC $CONFIG_PARIDE
+dep_tristate '    KT PHd protocol' CONFIG_PARIDE_KTTI $CONFIG_PARIDE
+dep_tristate '    OnSpec 90c20 protocol' CONFIG_PARIDE_ON20 $CONFIG_PARIDE
+dep_tristate '    OnSpec 90c26 protocol' CONFIG_PARIDE_ON26 $CONFIG_PARIDE
+#
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/Makefile linux/drivers/block/paride/Makefile
--- v2.0.34/linux/drivers/block/paride/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/Makefile	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,174 @@
+#
+# Makefile for PARIDE
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := paride.a
+MX_OBJS  :=
+LX_OBJS  := 
+MI_OBJS  :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PARIDE),y)
+  LX_OBJS += paride.o
+else
+  ifeq ($(CONFIG_PARIDE),m)
+    MX_OBJS += paride.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PD),y)
+  LX_OBJS += pd.o
+else
+  ifeq ($(CONFIG_PARIDE_PD),m)
+    MX_OBJS += pd.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PCD),y)
+  LX_OBJS += pcd.o
+else
+  ifeq ($(CONFIG_PARIDE_PCD),m)
+    MX_OBJS += pcd.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PF),y)
+  LX_OBJS += pf.o
+else
+  ifeq ($(CONFIG_PARIDE_PF),m)
+    MX_OBJS += pf.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PT),y)
+  LX_OBJS += pt.o
+else
+  ifeq ($(CONFIG_PARIDE_PT),m)
+    MX_OBJS += pt.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PG),y)
+  LX_OBJS += pg.o
+else
+  ifeq ($(CONFIG_PARIDE_PG),m)
+    MX_OBJS += pg.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ATEN),y)
+  LX_OBJS += aten.o
+else
+  ifeq ($(CONFIG_PARIDE_ATEN),m)
+    MX_OBJS += aten.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_BPCK),y)
+  LX_OBJS += bpck.o
+else
+  ifeq ($(CONFIG_PARIDE_BPCK),m)
+    MX_OBJS += bpck.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_COMM),y)
+  LX_OBJS += comm.o
+else
+  ifeq ($(CONFIG_PARIDE_COMM),m)
+    MX_OBJS += comm.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_DSTR),y)
+  LX_OBJS += dstr.o
+else
+  ifeq ($(CONFIG_PARIDE_DSTR),m)
+    MX_OBJS += dstr.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_KBIC),y)
+  LX_OBJS += kbic.o
+else
+  ifeq ($(CONFIG_PARIDE_KBIC),m)
+    MX_OBJS += kbic.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPAT),y)
+  LX_OBJS += epat.o
+else
+  ifeq ($(CONFIG_PARIDE_EPAT),m)
+    MX_OBJS += epat.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPIA),y)
+  LX_OBJS += epia.o
+else
+  ifeq ($(CONFIG_PARIDE_EPIA),m)
+    MX_OBJS += epia.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FIT2),y)
+  LX_OBJS += fit2.o
+else
+  ifeq ($(CONFIG_PARIDE_FIT2),m)
+    MX_OBJS += fit2.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FIT3),y)
+  LX_OBJS += fit3.o
+else
+  ifeq ($(CONFIG_PARIDE_FIT3),m)
+    MX_OBJS += fit3.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FRPW),y)
+  LX_OBJS += frpw.o
+else
+  ifeq ($(CONFIG_PARIDE_FRPW),m)
+    MX_OBJS += frpw.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON20),y)
+  LX_OBJS += on20.o
+else
+  ifeq ($(CONFIG_PARIDE_ON20),m)
+    MX_OBJS += on20.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON26),y)
+  LX_OBJS += on26.o
+else
+  ifeq ($(CONFIG_PARIDE_ON26),m)
+    MX_OBJS += on26.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_KTTI),y)
+  LX_OBJS += ktti.o
+else
+  ifeq ($(CONFIG_PARIDE_KTTI),m)
+    MX_OBJS += ktti.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/aten.c linux/drivers/block/paride/aten.c
--- v2.0.34/linux/drivers/block/paride/aten.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/aten.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,172 @@
+/* 
+        aten.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+	aten.c is a low-level protocol driver for the ATEN EH-100
+	parallel port adapter.  The EH-100 supports 4-bit and 8-bit
+        modes only.  There is also an EH-132 which supports EPP mode
+        transfers.  The EH-132 is not yet supported.
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.05.05	init_proto, release_proto
+
+*/
+
+#define ATEN_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x20 };
+
+static void  aten_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+	r = regr + cont_map[cont] + 0x80;
+
+	w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
+}
+
+static int aten_read_regr( PIA *pi, int cont, int regr )
+
+{	int  a, b, r;
+
+        r = regr + cont_map[cont] + 0x40;
+
+	switch (pi->mode) {
+
+        case 0: w0(r); w2(0xe); w2(6); 
+		w2(7); w2(6); w2(0);
+		a = r1(); w0(0x10); b = r1(); w2(0xc);
+		return j44(a,b);
+
+        case 1: r |= 0x10;
+		w0(r); w2(0xe); w2(6); w0(0xff); 
+		w2(0x27); w2(0x26); w2(0x20);
+		a = r0();
+		w2(0x26); w2(0xc);
+		return a;
+	}
+	return -1;
+}
+
+static void aten_read_block( PIA *pi, char * buf, int count )
+
+{	int  k, a, b, c, d;
+
+	switch (pi->mode) {
+
+	case 0:	w0(0x48); w2(0xe); w2(6);
+		for (k=0;k<count/2;k++) {
+			w2(7); w2(6); w2(2);
+			a = r1(); w0(0x58); b = r1();
+			w2(0); d = r1(); w0(0x48); c = r1();
+			buf[2*k] = j44(c,d);
+			buf[2*k+1] = j44(a,b);
+		}
+		w2(0xc);
+		break;
+
+	case 1: w0(0x58); w2(0xe); w2(6);
+		for (k=0;k<count/2;k++) {
+			w2(0x27); w2(0x26); w2(0x22);
+			a = r0(); w2(0x20); b = r0();
+			buf[2*k] = b; buf[2*k+1] = a;
+		}
+		w2(0x26); w2(0xc);
+		break;
+	}
+}
+
+static void aten_write_block( PIA *pi, char * buf, int count )
+
+{	int k;
+
+	w0(0x88); w2(0xe); w2(6);
+	for (k=0;k<count/2;k++) {
+		w0(buf[2*k+1]); w2(0xe); w2(6);
+		w0(buf[2*k]); w2(7); w2(6);
+	}
+	w2(0xc);
+}
+
+static void aten_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(0xc);	
+}
+
+static void aten_disconnect ( PIA *pi )
+
+{       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void aten_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[2] = {"4-bit","8-bit"};
+
+        printk("%s: aten %s, ATEN EH-100 at 0x%x, ",
+                pi->device,ATEN_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void aten_init_proto( PIA *pi )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void aten_release_proto( PIA *pi )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol aten = {"aten",0,2,2,1,1,
+                           aten_write_regr,
+                           aten_read_regr,
+                           aten_write_block,
+                           aten_read_block,
+                           aten_connect,
+                           aten_disconnect,
+                           0,
+                           0,
+                           0,
+                           aten_log_adapter,
+                           aten_init_proto,
+                           aten_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &aten ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &aten );
+}
+
+#endif
+
+/* end of aten.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/bpck.c linux/drivers/block/paride/bpck.c
--- v2.0.34/linux/drivers/block/paride/bpck.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/bpck.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,482 @@
+/* 
+	bpck.c	(c) 1996-8  Grant R. Guenther <grant@torque.net>
+		            Under the terms of the GNU public license.
+
+	bpck.c is a low-level protocol driver for the MicroSolutions 
+	"backpack" parallel port IDE adapter.  
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.05.05 init_proto, release_proto, pi->delay 
+
+*/
+
+#define	BPCK_VERSION	"1.01" 
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#undef r2
+#undef w2
+
+#define PC			pi->private
+#define r2()			(PC=(in_p(2) & 0xff))
+#define w2(byte)  		{out_p(2,byte); PC = byte;}
+#define t2(pat)   		{PC ^= pat; out_p(2,PC);}
+#define e2()			{PC &= 0xfe; out_p(2,PC);}
+#define o2()			{PC |= 1; out_p(2,PC);}
+
+#define j44(l,h)     (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+   cont = 2 - use internal bpck register addressing
+*/
+
+static int  cont_map[3] = { 0x40, 0x48, 0 };
+
+static int bpck_read_regr( PIA *pi, int cont, int regr )
+
+{       int r, l, h;
+
+	r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0: w0(r & 0xf); w0(r); t2(2); t2(4);
+	        l = r1();
+        	t2(4);
+        	h = r1();
+        	return j44(l,h);
+
+	case 1: w0(r & 0xf); w0(r); t2(2);
+	        e2(); t2(0x20);
+		t2(4); h = r0();
+	        t2(1); t2(0x20);
+	        return h;
+
+	case 2:
+	case 3:
+	case 4: w0(r); w2(9); w2(0); w2(0x20);
+		h = r4();
+		w2(0);
+		return h;
+
+	}
+	return -1;
+}	
+
+static void bpck_write_regr( PIA *pi, int cont, int regr, int val )
+
+{	int	r;
+
+        r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0:
+	case 1: w0(r);
+		t2(2);
+		w0(val);
+		o2(); t2(4); t2(1);
+		break;
+
+	case 2:
+	case 3:
+	case 4: w0(r); w2(9); w2(0);
+		w0(val); w2(1); w2(3); w2(0);
+		break;
+
+	}
+}
+
+/* These macros access the bpck registers in native addressing */
+
+#define WR(r,v)		bpck_write_regr(pi,2,r,v)
+#define RR(r)		(bpck_read_regr(pi,2,r))
+
+static void bpck_write_block( PIA *pi, char * buf, int count )
+
+{	int i;
+
+	switch (pi->mode) {
+
+	case 0: WR(4,0x40);
+		w0(0x40); t2(2); t2(1);
+		for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+		WR(4,0);
+		break;
+
+	case 1: WR(4,0x50);
+                w0(0x40); t2(2); t2(1);
+                for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+                WR(4,0x10);
+		break;
+
+	case 2: WR(4,0x48);
+		w0(0x40); w2(9); w2(0); w2(1);
+		for (i=0;i<count;i++) w4(buf[i]);
+		w2(0);
+		WR(4,8);
+		break;
+
+        case 3: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(1);
+                for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]);
+                w2(0);
+                WR(4,8);
+                break;
+ 
+        case 4: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(1);
+                for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]);
+                w2(0);
+                WR(4,8);
+                break;
+ 	}
+}
+
+static void bpck_read_block( PIA *pi, char * buf, int count )
+
+{	int i, l, h;
+
+	switch (pi->mode) {
+
+      	case 0: WR(4,0x40);
+		w0(0x40); t2(2);
+		for (i=0;i<count;i++) {
+		    t2(4); l = r1();
+		    t2(4); h = r1();
+		    buf[i] = j44(l,h);
+		}
+		WR(4,0);
+		break;
+
+	case 1: WR(4,0x50);
+		w0(0x40); t2(2); t2(0x20);
+      	        for(i=0;i<count;i++) { t2(4); buf[i] = r0(); }
+	        t2(1); t2(0x20);
+	        WR(4,0x10);
+		break;
+
+	case 2: WR(4,0x48);
+		w0(0x40); w2(9); w2(0); w2(0x20);
+		for (i=0;i<count;i++) buf[i] = r4();
+		w2(0);
+		WR(4,8);
+		break;
+
+        case 3: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(0x20);
+                for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+                w2(0);
+                WR(4,8);
+                break;
+
+        case 4: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(0x20);
+                for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+                w2(0);
+                WR(4,8);
+                break;
+
+	}
+}
+
+static int bpck_probe_unit ( PIA *pi )
+
+{	int o1, o0, f7, id;
+	int t, s;
+
+	id = pi->unit;
+	s = 0;
+	w2(4); w2(0xe); r2(); t2(2); 
+	o1 = r1()&0xf8;
+	o0 = r0();
+	w0(255-id); w2(4); w0(id);
+	t2(8); t2(8); t2(8);
+	t2(2); t = r1()&0xf8;
+	f7 = ((id % 8) == 7);
+	if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; }
+	if ((t == o1) && ((!f7) || (s == o1)))  {
+		w2(0x4c); w0(o0);
+		return 0;	
+	}
+	t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
+	return 1;
+}
+	
+static void bpck_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+	w0(0xff-pi->unit); w2(4); w0(pi->unit);
+	t2(8); t2(8); t2(8); 
+	t2(2); t2(2);
+	
+	switch (pi->mode) {
+
+	case 0: t2(8); WR(4,0);
+		break;
+
+	case 1: t2(8); WR(4,0x10);
+		break;
+
+	case 2:
+        case 3:
+	case 4: w2(0); WR(4,8);
+		break;
+
+	}
+
+	WR(5,8);
+
+	if (pi->devtype == PI_PCD) {
+		WR(0x46,0x10);		/* fiddle with ESS logic ??? */
+		WR(0x4c,0x38);
+		WR(0x4d,0x88);
+		WR(0x46,0xa0);
+		WR(0x41,0);
+		WR(0x4e,8);
+		}
+}
+
+static void bpck_disconnect ( PIA *pi )
+
+{	w0(0); 
+	if (pi->mode >= 2) { w2(9); w2(0); } else t2(2);
+	w2(0x4c); w0(pi->saved_r0);
+} 
+
+static void bpck_force_spp ( PIA *pi )
+
+/* This fakes the EPP protocol to turn off EPP ... */
+
+{       pi->saved_r0 = r0();
+        w0(0xff-pi->unit); w2(4); w0(pi->unit);
+        t2(8); t2(8); t2(8); 
+        t2(2); t2(2);
+
+        w2(0); 
+        w0(4); w2(9); w2(0); 
+        w0(0); w2(1); w2(3); w2(0);     
+        w0(0); w2(9); w2(0);
+        w2(0x4c); w0(pi->saved_r0);
+}
+
+#define TEST_LEN  16
+
+static int bpck_test_proto( PIA *pi, char * scratch, int verbose )
+
+{	int i, e, l, h, om;
+	char buf[TEST_LEN];
+
+	bpck_force_spp(pi);
+
+	switch (pi->mode) {
+
+	case 0: bpck_connect(pi);
+		WR(0x13,0x7f);
+		w0(0x13); t2(2);
+		for(i=0;i<TEST_LEN;i++) {
+                    t2(4); l = r1();
+                    t2(4); h = r1();
+                    buf[i] = j44(l,h);
+		}
+		bpck_disconnect(pi);
+		break;
+
+        case 1: bpck_connect(pi);
+		WR(0x13,0x7f);
+                w0(0x13); t2(2); t2(0x20);
+                for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); }
+                t2(1); t2(0x20);
+		bpck_disconnect(pi);
+		break;
+
+	case 2:
+	case 3:
+	case 4: om = pi->mode;
+		pi->mode = 0;
+		bpck_connect(pi);
+		WR(7,3);
+		WR(4,8);
+		bpck_disconnect(pi);
+
+		pi->mode = om;
+		bpck_connect(pi);
+		w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
+
+		switch (pi->mode) {
+		  case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4();
+			  break;
+		  case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w();
+                          break;
+		  case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l();
+                          break;
+		}
+
+		w2(0);
+		WR(7,0);
+		bpck_disconnect(pi);
+
+		break;
+
+	}
+
+	if (verbose) {
+	    printk("%s: bpck: 0x%x unit %d mode %d: ",
+		   pi->device,pi->port,pi->unit,pi->mode);
+	    for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]);
+	    printk("\n");
+	}
+
+	e = 0;
+	for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++;
+	return e;
+}
+
+static void bpck_read_eeprom ( PIA *pi, char * buf )
+
+{       int i,j,k,n,p,v,f, om, od;
+
+	bpck_force_spp(pi);
+
+	om = pi->mode;  od = pi->delay;
+	pi->mode = 0; pi->delay = 6;
+
+	bpck_connect(pi);
+	
+	n = 0;
+	WR(4,0);
+	for (i=0;i<64;i++) {
+	    WR(6,8);  
+	    WR(6,0xc);
+	    p = 0x100;
+	    for (k=0;k<9;k++) {
+		f = (((i + 0x180) & p) != 0) * 2;
+		WR(6,f+0xc); 
+		WR(6,f+0xd); 
+		WR(6,f+0xc);
+		p = (p >> 1);
+	    }
+	    for (j=0;j<2;j++) {
+		v = 0;
+		for (k=0;k<8;k++) {
+		    WR(6,0xc); 
+		    WR(6,0xd); 
+		    WR(6,0xc); 
+		    f = RR(0);
+		    v = 2*v + (f == 0x84);
+		}
+		buf[2*i+1-j] = v;
+	    }
+	}
+	WR(6,8);
+	WR(6,0);
+	WR(5,8);
+
+	bpck_disconnect(pi);
+
+        if (om >= 2) {
+                bpck_connect(pi);
+                WR(7,3);
+                WR(4,8);
+                bpck_disconnect(pi);
+        }
+
+	pi->mode = om; pi->delay = od;
+}
+
+static int bpck_test_port ( PIA *pi ) 	/* check for 8-bit port */
+
+{	int	i, r, m;
+
+	w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
+	m = -1;
+	if (r == i) m = 2;
+	if (r == (255-i)) m = 0;
+
+	w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i);
+	if (r != (255-i)) m = -1;
+	
+	if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); }
+	if (m == 2) { w2(0x26); w2(0xc); }
+
+	if (m == -1) return 0;
+	return 5;
+}
+
+static void bpck_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{	char	*mode_string[5] = { "4-bit","8-bit","EPP-8",
+				    "EPP-16","EPP-32" };
+
+#ifdef DUMP_EEPROM
+	int i;
+#endif
+
+	bpck_read_eeprom(pi,scratch);
+
+#ifdef DUMP_EEPROM
+	if (verbose) {
+	   for(i=0;i<128;i++)
+		if ((scratch[i] < ' ') || (scratch[i] > '~'))
+		    scratch[i] = '.';
+	   printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch);
+	   printk("%s:              %64.64s\n",pi->device,&scratch[64]);
+	}
+#endif
+
+	printk("%s: bpck %s, backpack %8.8s unit %d",
+		pi->device,BPCK_VERSION,&scratch[110],pi->unit);
+	printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port,
+		pi->mode,mode_string[pi->mode],pi->delay);
+}
+
+static void bpck_init_proto( PIA *pi)
+
+{	MOD_INC_USE_COUNT;
+}
+
+static void bpck_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol bpck = { "bpck",0,5,2,1,256,
+			  bpck_write_regr,
+			  bpck_read_regr,
+			  bpck_write_block,
+			  bpck_read_block,
+			  bpck_connect,
+			  bpck_disconnect,
+			  bpck_test_port,
+			  bpck_probe_unit,
+		          bpck_test_proto,
+			  bpck_log_adapter,
+			  bpck_init_proto,
+			  bpck_release_proto
+			};
+
+#ifdef MODULE
+
+int	init_module(void)
+
+{	return pi_register(&bpck) - 1;
+}
+
+void	cleanup_module(void)
+
+{	pi_unregister(&bpck);
+}
+
+#endif
+
+/* end of bpck.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/comm.c linux/drivers/block/paride/comm.c
--- v2.0.34/linux/drivers/block/paride/comm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/comm.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,228 @@
+/* 
+        comm.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+	comm.c is a low-level protocol driver for some older models
+	of the DataStor "Commuter" parallel to IDE adapter.  Some of
+	the parallel port devices marketed by Arista currently
+	use this adapter.
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.05.05  init_proto, release_proto
+
+*/
+
+#define COMM_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+*/
+
+#define j44(a,b)	(((a>>3)&0x0f)|((b<<1)&0xf0))
+
+#define P1	w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
+#define P2	w2(5);w2(7);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x10 };
+
+static int comm_read_regr( PIA *pi, int cont, int regr )
+
+{       int     l, h, r;
+
+        r = regr + cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0: w0(r); P1; w0(0);
+        	w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
+                return j44(l,h);
+
+        case 1: w0(r+0x20); P1; 
+        	w0(0); w2(0x26); h = r0(); w2(4);
+                return h;
+
+	case 2:
+	case 3:
+        case 4: w3(r+0x20); r1(); 
+        	w2(0x24); h = r4(); w2(4);
+                return h;
+
+        }
+        return -1;
+}       
+
+static void comm_write_regr( PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = regr + cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(r); P1; w0(val); P2;
+		break;
+
+	case 2:
+	case 3:
+        case 4: w3(r); r1(); w4(val); 
+                break;
+        }
+}
+
+static void comm_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); w0(0xff); w2(6);
+        w2(4); w0(0xaa); w2(6);
+        w2(4); w0(0x00); w2(6);
+        w2(4); w0(0x87); w2(6);
+        w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
+}
+
+static void comm_disconnect ( PIA *pi )
+
+{       w2(0); w2(0); w2(0); w2(4); 
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void comm_read_block( PIA *pi, char * buf, int count )
+
+{       int     i, l, h;
+
+        switch (pi->mode) {
+        
+        case 0: w0(0x48); P1;
+                for(i=0;i<count;i++) {
+                        w0(0); w2(6); l = r1();
+                        w0(0x80); h = r1(); w2(4);
+                        buf[i] = j44(l,h);
+                }
+                break;
+
+        case 1: w0(0x68); P1; w0(0);
+                for(i=0;i<count;i++) {
+                        w2(0x26); buf[i] = r0(); w2(0x24);
+                }
+		w2(4);
+		break;
+		
+	case 2: w3(0x68); r1(); w2(0x24);
+		for (i=0;i<count;i++) buf[i] = r4();
+		w2(4);
+		break;
+
+        case 3: w3(0x68); r1(); w2(0x24);
+                for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+                w2(4);
+                break;
+
+        case 4: w3(0x68); r1(); w2(0x24);
+                for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+                w2(4);
+                break;
+		
+	}
+}
+
+/* NB: Watch out for the byte swapped writes ! */
+
+static void comm_write_block( PIA *pi, char * buf, int count )
+
+{       int	k;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1: w0(0x68); P1;
+        	for (k=0;k<count;k++) {
+                        w2(5); w0(buf[k^1]); w2(7);
+                }
+                w2(5); w2(4);
+                break;
+
+        case 2: w3(0x48); r1();
+                for (k=0;k<count;k++) w4(buf[k^1]);
+                break;
+
+        case 3: w3(0x48); r1();
+                for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+                break;
+
+        case 4: w3(0x48); r1();
+                for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+                break;
+
+
+        }
+}
+
+static void comm_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: comm %s, DataStor Commuter at 0x%x, ",
+                pi->device,COMM_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void comm_init_proto(PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void comm_release_proto(PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol comm = {"comm",0,5,2,1,1,
+                           comm_write_regr,
+                           comm_read_regr,
+                           comm_write_block,
+                           comm_read_block,
+                           comm_connect,
+                           comm_disconnect,
+                           0,
+                           0,
+                           0,
+                           comm_log_adapter,
+                           comm_init_proto,
+                           comm_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &comm ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &comm );
+}
+
+#endif
+
+/* end of comm.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/dstr.c linux/drivers/block/paride/dstr.c
--- v2.0.34/linux/drivers/block/paride/dstr.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/dstr.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,243 @@
+/* 
+        dstr.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        dstr.c is a low-level protocol driver for the 
+        DataStor EP2000 parallel to IDE adapter chip.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define DSTR_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+		3  EPP-16
+		4  EPP-32
+*/
+
+#define j44(a,b)  (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80))
+
+#define P1	w2(5);w2(0xd);w2(5);w2(4);
+#define P2	w2(5);w2(7);w2(5);w2(4);
+#define P3      w2(6);w2(4);w2(6);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x20, 0x40 };
+
+static int dstr_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+        r = regr + cont_map[cont];
+
+	w0(0x81); P1;
+	if (pi->mode) { w0(0x11); } else { w0(1); }
+	P2; w0(r); P1;
+
+        switch (pi->mode)  {
+
+        case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
+                return j44(a,b);
+
+        case 1: w0(0); w2(0x26); a = r0(); w2(4);
+                return a;
+
+	case 2:
+	case 3:
+        case 4: w2(0x24); a = r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void dstr_write_regr(  PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = regr + cont_map[cont];
+
+	w0(0x81); P1; 
+	if (pi->mode >= 2) { w0(0x11); } else { w0(1); }
+	P2; w0(r); P1;
+	
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(val); w2(5); w2(7); w2(5); w2(4);
+		break;
+
+	case 2:
+	case 3:
+        case 4: w4(val); 
+                break;
+        }
+}
+
+#define  CCP(x)  w0(0xff);w2(0xc);w2(4);\
+		 w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\
+		 w0(x);w2(5);w2(4);
+
+static void dstr_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); CCP(0xe0); w0(0xff);
+}
+
+static void dstr_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void dstr_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        w0(0x81); P1;
+        if (pi->mode) { w0(0x19); } else { w0(9); }
+	P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+        switch (pi->mode) {
+
+        case 0: for (k=0;k<count;k++) {
+                        w2(6); a = r1(); w2(4);
+                        w2(6); b = r1(); w2(4);
+                        buf[k] = j44(a,b);
+                } 
+                break;
+
+        case 1: w0(0);
+                for (k=0;k<count;k++) {
+                        w2(0x26); buf[k] = r0(); w2(0x24);
+                }
+                w2(4);
+                break;
+
+        case 2: w2(0x24); 
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4);
+                break;
+
+        case 3: w2(0x24); 
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4);
+                break;
+
+        case 4: w2(0x24); 
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4);
+                break;
+
+        }
+}
+
+static void dstr_write_block( PIA *pi, char * buf, int count )
+
+{       int	k;
+
+        w0(0x81); P1;
+        if (pi->mode) { w0(0x19); } else { w0(9); }
+        P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1: for (k=0;k<count;k++) {
+                        w2(5); w0(buf[k]); w2(7);
+                }
+                w2(5); w2(4);
+                break;
+
+        case 2: w2(0xc5);
+                for (k=0;k<count;k++) w4(buf[k]);
+		w2(0xc4);
+                break;
+
+        case 3: w2(0xc5);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        case 4: w2(0xc5);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        }
+}
+
+
+static void dstr_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8",
+				   "EPP-16","EPP-32"};
+
+        printk("%s: dstr %s, DataStor EP2000 at 0x%x, ",
+                pi->device,DSTR_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void dstr_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void dstr_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol dstr = {"dstr",0,5,2,1,1,
+                           dstr_write_regr,
+                           dstr_read_regr,
+                           dstr_write_block,
+                           dstr_read_block,
+                           dstr_connect,
+                           dstr_disconnect,
+                           0,
+                           0,
+                           0,
+                           dstr_log_adapter,
+                           dstr_init_proto,
+                           dstr_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &dstr ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &dstr );
+}
+
+#endif
+
+/* end of dstr.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/epat.c linux/drivers/block/paride/epat.c
--- v2.0.34/linux/drivers/block/paride/epat.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/epat.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,321 @@
+/* 
+        epat.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+	This is the low level protocol driver for the EPAT parallel
+        to IDE adapter from Shuttle Technologies.  This adapter is
+        used in many popular parallel port disk products such as the
+        SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
+	
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define EPAT_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)		(((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b)		(((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont =  0   IDE register file
+   cont =  1   IDE control registers
+   cont =  2   internal EPAT registers
+*/
+
+static int cont_map[3] = { 0x18, 0x10, 0 };
+
+static void epat_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+	r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0:
+	case 1:
+	case 2:	w0(0x60+r); w2(1); w0(val); w2(4);
+		break;
+
+	case 3:
+	case 4:
+	case 5: w3(0x40+r); w4(val);
+		break;
+
+	}
+}
+
+static int epat_read_regr( PIA *pi, int cont, int regr )
+
+{	int  a, b, r;
+
+	r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0:	w0(r); w2(1); w2(3); 
+		a = r1(); w2(4); b = r1();
+		return j44(a,b);
+
+	case 1: w0(0x40+r); w2(1); w2(4);
+		a = r1(); b = r2(); w0(0xff);
+		return j53(a,b);
+
+	case 2: w0(0x20+r); w2(1); w2(0x25);
+		a = r0(); w2(4);
+		return a;
+
+	case 3:
+	case 4:
+	case 5: w3(r); w2(0x24); a = r4(); w2(4);
+		return a;
+
+	}
+	return -1;	/* never gets here */
+}
+
+static void epat_read_block( PIA *pi, char * buf, int count )
+
+{	int  k, ph, a, b;
+
+	switch (pi->mode) {
+
+	case 0:	w0(7); w2(1); w2(3); w0(0xff);
+		ph = 0;
+		for(k=0;k<count;k++) {
+			if (k == count-1) w0(0xfd);
+			w2(6+ph); a = r1();
+			if (a & 8) b = a; 
+			  else { w2(4+ph); b = r1(); }
+			buf[k] = j44(a,b);
+			ph =  1 - ph;
+		}
+		w0(0); w2(4);
+		break;
+
+	case 1: w0(0x47); w2(1); w2(5); w0(0xff);
+		ph = 0;
+		for(k=0;k<count;k++) {
+			if (k == count-1) w0(0xfd); 
+			w2(4+ph);
+			a = r1(); b = r2();
+			buf[k] = j53(a,b);
+			ph = 1 - ph;
+		}
+		w0(0); w2(4);
+		break;
+
+	case 2: w0(0x27); w2(1); w2(0x25); w0(0);
+		ph = 0;
+		for(k=0;k<count-1;k++) {
+			w2(0x24+ph);
+			buf[k] = r0();
+			ph = 1 - ph;
+		}
+		w2(0x26); w2(0x27); buf[count-1] = r0(); 
+		w2(0x25); w2(4);
+		break;
+
+	case 3: w3(0x80); w2(0x24);
+		for(k=0;k<count-1;k++) buf[k] = r4();
+		w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+		w2(4);
+		break;
+
+	case 4: w3(0x80); w2(0x24);
+		for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
+		buf[count-2] = r4();
+		w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+		w2(4);
+		break;
+
+	case 5: w3(0x80); w2(0x24);
+		for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
+		for(k=count-4;k<count-1;k++) buf[k] = r4();
+		w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+		w2(4);
+		break;
+
+	}
+}
+
+static void epat_write_block( PIA *pi, char * buf, int count )   
+
+{	int ph, k;
+
+	switch (pi->mode) {
+
+	case 0:
+	case 1:
+	case 2: w0(0x67); w2(1); w2(5);
+		ph = 0;
+		for(k=0;k<count;k++) {
+		  	w0(buf[k]);
+			w2(4+ph);
+			ph = 1 - ph;
+		}
+		w2(7); w2(4);
+		break;
+
+	case 3: w3(0xc0); 
+		for(k=0;k<count;k++) w4(buf[k]);
+		w2(4);
+		break;
+
+	case 4: w3(0xc0); 
+		for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]);
+		w2(4);
+		break;
+
+	case 5: w3(0xc0); 
+		for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]);
+		w2(4);
+		break;
+
+	}
+}
+
+/* these macros access the EPAT registers in native addressing */
+
+#define	WR(r,v)		epat_write_regr(pi,2,r,v)
+#define	RR(r)		(epat_read_regr(pi,2,r))
+
+/* and these access the IDE task file */
+
+#define WRi(r,v)         epat_write_regr(pi,0,r,v)
+#define RRi(r)           (epat_read_regr(pi,0,r))
+
+/* FIXME:  the CCP stuff should be fixed to handle multiple EPATs on a chain */
+
+#define CCP(x) 	w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+                w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
+
+static void epat_connect ( PIA *pi )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	CCP(0); CCP(0xe0);
+	w0(0); w2(1); w2(4);
+	if (pi->mode >= 3) {
+		w0(0); w2(1); w2(4); w2(0xc);
+		w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4);
+	}
+	WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10);
+}
+
+static void epat_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static int epat_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     k, j, f, cc;
+	int	e[2] = {0,0};
+
+        epat_connect(pi);
+	cc = RR(0xd);
+	epat_disconnect(pi);
+
+	epat_connect(pi);
+	for (j=0;j<2;j++) {
+  	    WRi(6,0xa0+j*0x10);
+            for (k=0;k<256;k++) {
+                WRi(2,k^0xaa);
+                WRi(3,k^0x55);
+                if (RRi(2) != (k^0xaa)) e[j]++;
+                }
+	    }
+        epat_disconnect(pi);
+
+        f = 0;
+        epat_connect(pi);
+        WR(0x13,1); WR(0x13,0); WR(0xa,0x11);
+        epat_read_block(pi,scratch,512);
+	
+        for (k=0;k<256;k++) {
+            if ((scratch[2*k] & 0xff) != k) f++;
+            if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++;
+        }
+        epat_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
+		   pi->device,pi->port,pi->mode,cc,e[0],e[1],f);
+	}
+	
+        return (e[0] && e[1]) || f;
+}
+
+static void epat_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{	int	ver;
+        char    *mode_string[6] = 
+		   {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"};
+
+	epat_connect(pi);
+	WR(0xa,0x38);		/* read the version code */
+        ver = RR(0xb);
+        epat_disconnect(pi);
+
+	printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ",
+		pi->device,EPAT_VERSION,ver,pi->port);
+	printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void epat_init_proto( PIA *pi)
+
+{  	MOD_INC_USE_COUNT;
+}
+
+static void epat_release_proto( PIA *pi)
+
+{	MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epat = {"epat",0,6,3,1,1,
+			   epat_write_regr,
+			   epat_read_regr,
+			   epat_write_block,
+			   epat_read_block,
+			   epat_connect,
+			   epat_disconnect,
+			   0,
+			   0,
+			   epat_test_proto,
+			   epat_log_adapter,
+			   epat_init_proto,
+			   epat_release_proto
+			  };
+
+
+#ifdef MODULE
+
+int	init_module(void)
+
+{	return pi_register( &epat) - 1;
+}
+
+void	cleanup_module(void)
+
+{	pi_unregister( &epat);
+}
+
+#endif
+
+/* end of epat.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/epia.c linux/drivers/block/paride/epia.c
--- v2.0.34/linux/drivers/block/paride/epia.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/epia.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,326 @@
+/* 
+        epia.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        epia.c is a low-level protocol driver for Shuttle Technologies 
+	EPIA parallel to IDE adapter chip.  This device is now obsolete
+	and has been replaced with the EPAT chip, which is supported
+	by epat.c, however, some devices based on EPIA are still
+	available.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+	1.02    GRG 1998.06.17 support older versions of EPIA
+
+*/
+
+#define EPIA_VERSION      "1.02"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads on port 1, 8-bit writes
+                1  5/3 reads on ports 1 & 2, 8-bit writes
+                2  8-bit reads and writes
+                3  8-bit EPP mode
+		4  16-bit EPP
+		5  32-bit EPP
+*/
+
+#define j44(a,b)                (((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b)                (((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont =  0   IDE register file
+   cont =  1   IDE control registers
+*/
+
+static int cont_map[2] = { 0, 0x80 };
+
+static int epia_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+	regr += cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0: r = regr^0x39;
+                w0(r); w2(1); w2(3); w0(r);
+                a = r1(); w2(1); b = r1(); w2(4);
+                return j44(a,b);
+
+        case 1: r = regr^0x31;
+                w0(r); w2(1); w0(r&0x37); 
+                w2(3); w2(5); w0(r|0xf0);
+                a = r1(); b = r2(); w2(4);
+                return j53(a,b);
+
+        case 2: r = regr^0x29;
+                w0(r); w2(1); w2(0X21); w2(0x23); 
+                a = r0(); w2(4);
+                return a;
+
+	case 3:
+	case 4:
+        case 5: w3(regr); w2(0x24); a = r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void epia_write_regr( PIA *pi, int cont, int regr, int val)
+
+{       int  r;
+
+	regr += cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1:
+        case 2: r = regr^0x19;
+                w0(r); w2(1); w0(val); w2(3); w2(4);
+                break;
+
+	case 3:
+	case 4:
+        case 5: r = regr^0x40;
+                w3(r); w4(val); w2(4);
+                break;
+        }
+}
+
+#define WR(r,v)         epia_write_regr(pi,0,r,v)
+#define RR(r)           (epia_read_regr(pi,0,r))
+
+/* The use of register 0x84 is entirely unclear - it seems to control
+   some EPP counters ...  currently we know about 3 different block
+   sizes:  the standard 512 byte reads and writes, 12 byte writes and 
+   2048 byte reads (the last two being used in the CDrom drivers.
+*/
+
+static void epia_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+        w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
+        w2(1); w2(4);
+        if (pi->mode >= 3) { 
+                w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
+                w2(0x24); w2(0x26); w2(4);
+        }
+        WR(0x86,8);  
+}
+
+static void epia_disconnect ( PIA *pi )
+
+{       /* WR(0x84,0x10); */
+        w0(pi->saved_r0);
+        w2(1); w2(4);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void epia_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, ph, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(0x81); w2(1); w2(3); w0(0xc1);
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(2+ph); a = r1();
+                        w2(4+ph); b = r1();
+                        buf[k] = j44(a,b);
+                        ph = 1 - ph;
+                } 
+                w0(0); w2(4);
+                break;
+
+        case 1: w0(0x91); w2(1); w0(0x10); w2(3); 
+                w0(0x51); w2(5); w0(0xd1); 
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(4+ph);
+                        a = r1(); b = r2();
+                        buf[k] = j53(a,b);
+                        ph = 1 - ph;
+                }
+                w0(0); w2(4);
+                break;
+
+        case 2: w0(0x89); w2(1); w2(0x23); w2(0x21); 
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(0x24+ph);
+                        buf[k] = r0();
+                        ph = 1 - ph;
+                }
+                w2(6); w2(4);
+                break;
+
+        case 3: if (count > 512) WR(0x84,3);
+		w3(0); w2(0x24);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4); WR(0x84,0);
+                break;
+
+        case 4: if (count > 512) WR(0x84,3);
+		w3(0); w2(0x24);
+		for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4); WR(0x84,0);
+                break;
+
+        case 5: if (count > 512) WR(0x84,3);
+		w3(0); w2(0x24);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4); WR(0x84,0);
+                break;
+
+        }
+}
+
+static void epia_write_block( PIA *pi, char * buf, int count )
+
+{       int     ph, k, last, d;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1:
+        case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5);
+                ph = 0;  last = 0x8000;
+                for (k=0;k<count;k++) {
+                        d = buf[k];
+                        if (d != last) { last = d; w0(d); }
+                        w2(4+ph);
+                        ph = 1 - ph;
+                }
+                w2(7); w2(4);
+                break;
+
+        case 3: if (count < 512) WR(0x84,1);
+		w3(0x40);
+                for (k=0;k<count;k++) w4(buf[k]);
+		if (count < 512) WR(0x84,0);
+                break;
+
+        case 4: if (count < 512) WR(0x84,1);
+		w3(0x40);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+		if (count < 512) WR(0x84,0);
+                break;
+
+        case 5: if (count < 512) WR(0x84,1);
+		w3(0x40);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+		if (count < 512) WR(0x84,0);
+                break;
+
+        }
+
+}
+
+static int epia_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k, f;
+	int	e[2] = {0,0};
+
+        epia_connect(pi);
+        for (j=0;j<2;j++) {
+            WR(6,0xa0+j*0x10);
+            for (k=0;k<256;k++) {
+                WR(2,k^0xaa);
+                WR(3,k^0x55);
+                if (RR(2) != (k^0xaa)) e[j]++;
+            }
+	    WR(2,1); WR(3,1);
+        }
+        epia_disconnect(pi);
+
+        f = 0;
+        epia_connect(pi);
+        WR(0x84,8);
+        epia_read_block(pi,scratch,512);
+        for (k=0;k<256;k++) {
+            if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++;
+            if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++;
+        }
+        WR(0x84,0);
+        epia_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+                   pi->device,pi->port,pi->mode,e[0],e[1],f);
+        }
+        
+        return (e[0] && e[1]) || f;
+
+}
+
+
+static void epia_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[6] = {"4-bit","5/3","8-bit",
+				   "EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: epia %s, Shuttle EPIA at 0x%x, ",
+                pi->device,EPIA_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void epia_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void epia_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epia = {"epia",0,6,3,1,1,
+                           epia_write_regr,
+                           epia_read_regr,
+                           epia_write_block,
+                           epia_read_block,
+                           epia_connect,
+                           epia_disconnect,
+                           0,
+                           0,
+                           epia_test_proto,
+                           epia_log_adapter,
+                           epia_init_proto,
+                           epia_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &epia ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &epia );
+}
+
+#endif
+
+/* end of epia.c */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/fit2.c linux/drivers/block/paride/fit2.c
--- v2.0.34/linux/drivers/block/paride/fit2.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/fit2.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,161 @@
+/* 
+        fit2.c        (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+	fit2.c is a low-level protocol driver for the older version
+        of the Fidelity International Technology parallel port adapter.  
+	This adapter is used in their TransDisk 2000 and older TransDisk
+	3000 portable hard-drives.  As far as I can tell, this device
+	supports 4-bit mode _only_.  
+
+	Newer models of the FIT products use an enhanced protocol.
+	The "fit3" protocol module should support current drives.
+
+*/
+
+#define FIT2_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+
+NB:  The FIT adapter does not appear to use the control registers.
+So, we map ALT_STATUS to STATUS and NO-OP writes to the device
+control register - this means that IDE reset will not work on these
+devices.
+
+*/
+
+static void  fit2_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	if (cont == 1) return;
+	w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4);
+}
+
+static int fit2_read_regr( PIA *pi, int cont, int regr )
+
+{	int  a, b, r;
+
+	if (cont) {
+	  if (regr != 6) return 0xff;
+	  r = 7;
+	} else r = regr + 0x10;
+
+	w2(0xc); w0(r); w2(4); w2(5); 
+	         w0(0); a = r1();
+	         w0(1); b = r1();
+	w2(4);
+
+	return j44(a,b);
+
+}
+
+static void fit2_read_block( PIA *pi, char * buf, int count )
+
+{	int  k, a, b, c, d;
+
+	w2(0xc); w0(0x10);
+
+	for (k=0;k<count/4;k++) {
+
+		w2(4); w2(5);
+		w0(0); a = r1(); w0(1); b = r1();
+		w0(3); c = r1(); w0(2); d = r1(); 
+		buf[4*k+0] = j44(a,b);
+		buf[4*k+1] = j44(d,c);
+
+                w2(4); w2(5);
+                       a = r1(); w0(3); b = r1();
+                w0(1); c = r1(); w0(0); d = r1(); 
+                buf[4*k+2] = j44(d,c);
+                buf[4*k+3] = j44(a,b);
+
+	}
+
+	w2(4);
+
+}
+
+static void fit2_write_block( PIA *pi, char * buf, int count )
+
+{	int k;
+
+
+	w2(0xc); w0(0); 
+	for (k=0;k<count/2;k++) {
+		w2(4); w0(buf[2*k]); 
+		w2(5); w0(buf[2*k+1]);
+	}
+	w2(4);
+}
+
+static void fit2_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(0xcc); 
+}
+
+static void fit2_disconnect ( PIA *pi )
+
+{       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void fit2_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       printk("%s: fit2 %s, FIT 2000 adapter at 0x%x, delay %d\n",
+                pi->device,FIT2_VERSION,pi->port,pi->delay);
+
+}
+
+static void fit2_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void fit2_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol fit2 = {"fit2",0,1,2,1,1,
+                           fit2_write_regr,
+                           fit2_read_regr,
+                           fit2_write_block,
+                           fit2_read_block,
+                           fit2_connect,
+                           fit2_disconnect,
+                           0,
+                           0,
+                           0,
+                           fit2_log_adapter,
+                           fit2_init_proto,
+                           fit2_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &fit2 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &fit2 );
+}
+
+#endif
+
+/* end of fit2.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/fit3.c linux/drivers/block/paride/fit3.c
--- v2.0.34/linux/drivers/block/paride/fit3.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/fit3.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,221 @@
+/* 
+        fit3.c        (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+	fit3.c is a low-level protocol driver for newer models 
+        of the Fidelity International Technology parallel port adapter.  
+	This adapter is used in their TransDisk 3000 portable 
+	hard-drives, as well as CD-ROM, PD-CD and other devices.
+
+	The TD-2000 and certain older devices use a different protocol.
+	Try the fit2 protocol module with them.
+
+        NB:  The FIT adapters do not appear to support the control 
+	registers.  So, we map ALT_STATUS to STATUS and NO-OP writes 
+	to the device control register - this means that IDE reset 
+	will not work on these devices.
+
+*/
+
+#define FIT3_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                (((a>>3)&0x0f)|((b<<1)&0xf0))
+
+#define w7(byte)                {out_p(7,byte);}
+#define r7()                    (in_p(7) & 0xff)
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+
+*/
+
+static void  fit3_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	if (cont == 1) return;
+
+	switch (pi->mode) {
+
+	case 0:
+	case 1: w2(0xc); w0(regr); w2(0x8); w2(0xc); 
+		w0(val); w2(0xd); 
+		w0(0);   w2(0xc);
+		break;
+
+	case 2: w2(0xc); w0(regr); w2(0x8); w2(0xc);
+		w4(val); w4(0);
+		w2(0xc);
+		break;
+
+	}
+}
+
+static int fit3_read_regr( PIA *pi, int cont, int regr )
+
+{	int  a, b;
+
+	if (cont) {
+	  if (regr != 6) return 0xff;
+	  regr = 7;
+	} 
+
+	switch (pi->mode) {
+
+	case 0: w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc);
+		w2(0xd); a = r1();
+		w2(0xf); b = r1(); 
+		w2(0xc);
+		return j44(a,b);
+
+	case 1: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
+		w2(0xec); w2(0xee); w2(0xef); a = r0(); 
+		w2(0xc);
+		return a;
+
+	case 2: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc); 
+		w2(0xec); 
+		a = r4(); b = r4(); 
+		w2(0xc);
+		return a;
+
+	}
+	return -1; 
+
+}
+
+static void fit3_read_block( PIA *pi, char * buf, int count )
+
+{	int  k, a, b, c, d;
+
+	switch (pi->mode) {
+
+	case 0: w2(0xc); w0(0x10); w2(0x8); w2(0xc);
+		for (k=0;k<count/2;k++) {
+		    w2(0xd); a = r1();
+		    w2(0xf); b = r1();
+		    w2(0xc); c = r1();
+		    w2(0xe); d = r1();
+		    buf[2*k  ] = j44(a,b);
+		    buf[2*k+1] = j44(c,d);
+		}
+		w2(0xc);
+		break;
+
+	case 1: w2(0xc); w0(0x90); w2(0x8); w2(0xc); 
+		w2(0xec); w2(0xee);
+		for (k=0;k<count/2;k++) {
+		    w2(0xef); a = r0();
+		    w2(0xee); b = r0();
+                    buf[2*k  ] = a;
+                    buf[2*k+1] = b;
+		}
+		w2(0xec); 
+		w2(0xc);
+		break;
+
+	case 2: w2(0xc); w0(0x90); w2(0x8); w2(0xc); 
+                w2(0xec);
+		for (k=0;k<count;k++) buf[k] = r4();
+                w2(0xc);
+		break;
+
+	}
+}
+
+static void fit3_write_block( PIA *pi, char * buf, int count )
+
+{	int k;
+
+        switch (pi->mode) {
+
+	case 0:
+        case 1: w2(0xc); w0(0); w2(0x8); w2(0xc);
+                for (k=0;k<count/2;k++) {
+ 		    w0(buf[2*k  ]); w2(0xd);
+ 		    w0(buf[2*k+1]); w2(0xc);
+		}
+		break;
+
+        case 2: w2(0xc); w0(0); w2(0x8); w2(0xc); 
+                for (k=0;k<count;k++) w4(buf[k]);
+                w2(0xc);
+		break;
+	}
+}
+
+static void fit3_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(0xc); w0(0); w2(0xa);
+	if (pi->mode == 2) { 
+		w2(0xc); w0(0x9); w2(0x8); w2(0xc); 
+		}
+}
+
+static void fit3_disconnect ( PIA *pi )
+
+{       w2(0xc); w0(0xa); w2(0x8); w2(0xc);
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void fit3_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[3] = {"4-bit","8-bit","EPP"};
+
+	printk("%s: fit3 %s, FIT 3000 adapter at 0x%x, "
+	       "mode %d (%s), delay %d\n",
+                pi->device,FIT3_VERSION,pi->port,
+		pi->mode,mode_string[pi->mode],pi->delay);
+
+}
+
+static void fit3_init_proto(PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void fit3_release_proto(PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol fit3 = {"fit3",0,3,2,1,1,
+                           fit3_write_regr,
+                           fit3_read_regr,
+                           fit3_write_block,
+                           fit3_read_block,
+                           fit3_connect,
+                           fit3_disconnect,
+                           0,
+                           0,
+                           0,
+                           fit3_log_adapter,
+                           fit3_init_proto,
+                           fit3_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &fit3 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &fit3 );
+}
+
+#endif
+
+/* end of fit3.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/frpw.c linux/drivers/block/paride/frpw.c
--- v2.0.34/linux/drivers/block/paride/frpw.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/frpw.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,310 @@
+/* 
+	frpw.c	(c) 1996-8  Grant R. Guenther <grant@torque.net>
+		            Under the terms of the GNU public license
+
+	frpw.c is a low-level protocol driver for the Freecom "Power"
+	parallel port IDE adapter.
+	
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+			       fix chip detect
+			       added EPP-16 and EPP-32
+
+*/
+
+#define	FRPW_VERSION	"1.01" 
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define cec4		w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
+#define j44(l,h)	(((l>>4)&0x0f)|(h&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x10 };
+
+static int frpw_read_regr( PIA *pi, int cont, int regr )
+
+{	int	h,l,r;
+
+	r = regr + cont_map[cont];
+
+	w2(4);
+	w0(r); cec4;
+	w2(6); l = r1();
+	w2(4); h = r1();
+	w2(4); 
+
+	return j44(l,h);
+
+}
+
+static void frpw_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+        r = regr + cont_map[cont];
+
+	w2(4); w0(r); cec4; 
+	w0(val);
+	w2(5);w2(7);w2(5);w2(4);
+}
+
+static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr )
+
+{       int     h, l, k, ph;
+
+        switch(pi->mode) {
+
+        case 0: w2(4); w0(regr); cec4;
+                for (k=0;k<count;k++) {
+                        w2(6); l = r1();
+                        w2(4); h = r1();
+                        buf[k] = j44(l,h);
+                }
+                w2(4);
+                break;
+
+        case 1: ph = 2;
+                w2(4); w0(regr + 0xc0); cec4;
+                w0(0xff);
+                for (k=0;k<count;k++) {
+                        w2(0xa4 + ph); 
+                        buf[k] = r0();
+                        ph = 2 - ph;
+                } 
+                w2(0xac); w2(0xa4); w2(4);
+                break;
+
+        case 2: w2(4); w0(regr + 0x80); cec4;
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(0xac); w2(0xa4);
+                w2(4);
+                break;
+
+	case 3: w2(4); w0(regr + 0x80); cec4;
+		for (k=0;k<count-2;k++) buf[k] = r4();
+		w2(0xac); w2(0xa4);
+		buf[count-2] = r4();
+		buf[count-1] = r4();
+		w2(4);
+		break;
+
+	case 4: w2(4); w0(regr + 0x80); cec4;
+                for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
+                w2(0xac); w2(0xa4);
+                buf[count-2] = r4();
+                buf[count-1] = r4();
+                w2(4);
+                break;
+
+	case 5: w2(4); w0(regr + 0x80); cec4;
+                for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
+                buf[count-4] = r4();
+                buf[count-3] = r4();
+                w2(0xac); w2(0xa4);
+                buf[count-2] = r4();
+                buf[count-1] = r4();
+                w2(4);
+                break;
+
+        }
+}
+
+static void frpw_read_block( PIA *pi, char * buf, int count)
+
+{	frpw_read_block_int(pi,buf,count,0x08);
+}
+
+static void frpw_write_block( PIA *pi, char * buf, int count )
+ 
+{	int	k;
+
+	switch(pi->mode) {
+
+	case 0:
+	case 1:
+	case 2: w2(4); w0(8); cec4; w2(5);
+        	for (k=0;k<count;k++) {
+			w0(buf[k]);
+			w2(7);w2(5);
+		}
+		w2(4);
+		break;
+
+	case 3: w2(4); w0(0xc8); cec4; w2(5);
+		for (k=0;k<count;k++) w4(buf[k]);
+		w2(4);
+		break;
+
+        case 4: w2(4); w0(0xc8); cec4; w2(5);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(4);
+                break;
+
+        case 5: w2(4); w0(0xc8); cec4; w2(5);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(4);
+                break;
+	}
+}
+
+static void frpw_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(4);
+}
+
+static void frpw_disconnect ( PIA *pi )
+
+{       w2(4); w0(0x20); cec4;
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+/* Stub logic to see if PNP string is available - used to distinguish
+   between the Xilinx and ASIC implementations of the Freecom adapter.
+*/
+
+static int frpw_test_pnp ( PIA *pi )
+
+/*  returns chip_type:   0 = Xilinx, 1 = ASIC   */
+
+{	int olddelay, a, b;
+
+	olddelay = pi->delay;
+	pi->delay = 10;
+
+	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	
+	w2(4); w0(4); w2(6); w2(7);
+	a = r1() & 0xff; w2(4); b = r1() & 0xff;
+	w2(0xc); w2(0xe); w2(4);
+
+	pi->delay = olddelay;
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+
+	return ((~a&0x40) && (b&0x40));
+} 
+
+/* We use the pi->private to remember the result of the PNP test.
+   To make this work, private = port*2 + chip.  Yes, I know it's
+   a hack :-(
+*/
+
+static int frpw_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k, r;
+	int	e[2] = {0,0};
+
+	if ((pi->private>>1) != pi->port)
+	   pi->private = frpw_test_pnp(pi) + 2*pi->port;
+
+	if (((pi->private%2) == 0) && (pi->mode > 2)) {
+	   if (verbose) 
+		printk("%s: frpw: Xilinx does not support mode %d\n",
+			pi->device, pi->mode);
+	   return 1;
+	}
+
+	if (((pi->private%2) == 1) && (pi->mode == 2)) {
+	   if (verbose)
+		printk("%s: frpw: ASIC does not support mode 2\n",
+			pi->device);
+	   return 1;
+	}
+
+	frpw_connect(pi);
+	for (j=0;j<2;j++) {
+                frpw_write_regr(pi,0,6,0xa0+j*0x10);
+                for (k=0;k<256;k++) {
+                        frpw_write_regr(pi,0,2,k^0xaa);
+                        frpw_write_regr(pi,0,3,k^0x55);
+                        if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
+                        }
+                }
+	frpw_disconnect(pi);
+
+	frpw_connect(pi);
+        frpw_read_block_int(pi,scratch,512,0x10);
+        r = 0;
+        for (k=0;k<128;k++) if (scratch[k] != k) r++;
+	frpw_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: frpw: port 0x%x, chip %d, mode %d, test=(%d,%d,%d)\n",
+                   pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r);
+        }
+
+        return (r || (e[0] && e[1]));
+}
+
+
+static void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[6] = {"4-bit","8-bit","EPP",
+				   "EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
+		FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void frpw_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+	pi->private = 0;
+}
+
+static void frpw_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol frpw = {"frpw",0,6,2,2,1,
+                           frpw_write_regr,
+                           frpw_read_regr,
+                           frpw_write_block,
+                           frpw_read_block,
+                           frpw_connect,
+                           frpw_disconnect,
+                           0,
+                           0,
+                           frpw_test_proto,
+                           frpw_log_adapter,
+                           frpw_init_proto,
+                           frpw_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &frpw ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &frpw );
+}
+
+#endif
+
+/* end of frpw.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/kbic.c linux/drivers/block/paride/kbic.c
--- v2.0.34/linux/drivers/block/paride/kbic.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/kbic.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,311 @@
+/*
+        kbic.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        This is a low-level driver for the KBIC-951A and KBIC-971A
+        parallel to IDE adapter chips from KingByte Information Systems.
+
+	The chips are almost identical, however, the wakeup code 
+	required for the 971A interferes with the correct operation of
+        the 951A, so this driver registers itself twice, once for
+	each chip.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define KBIC_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define r12w()			(delay_p,inw(pi->port+1)&0xffff) 
+
+#define j44(a,b)                ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+#define j53(w)                  (((w>>3)&0x1f)|((w>>4)&0xe0))
+
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x80, 0x40 };
+
+static int kbic_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, s;
+
+        s = cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8);
+	        a = r1(); w0(0x28); b = r1(); w2(4);
+		return j44(a,b);
+
+	case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8);
+		a = r12w(); w2(4);
+		return j53(a);
+
+	case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
+		a = r0(); w2(4);
+       		return a;
+
+	case 3:
+	case 4:
+	case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+		a = r4(); b = r4(); w2(4); w2(0); w2(4);
+		return a;
+
+	}
+	return -1;
+}       
+
+static void  kbic_write_regr( PIA *pi, int cont, int regr, int val)
+
+{       int  s;
+
+        s = cont_map[cont];
+
+        switch (pi->mode) {
+
+	case 0: 
+        case 1:
+	case 2:	w0(regr|0x10|s); w2(4); w2(6); w2(4); 
+		w0(val); w2(5); w2(4);
+		break;
+
+	case 3:
+	case 4:
+	case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+		w4(val); w4(val);
+		w2(4); w2(0); w2(4);
+                break;
+
+	}
+}
+
+static void k951_connect ( PIA *pi  )
+
+{ 	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); 
+}
+
+static void k951_disconnect ( PIA *pi )
+
+{      	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+}
+
+#define	CCP(x)	w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\
+		w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff);
+
+static void k971_connect ( PIA *pi  )
+
+{ 	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	CCP(0x20);
+        w2(4); 
+}
+
+static void k971_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+}
+
+/* counts must be congruent to 0 MOD 4, but all known applications
+   have this property.
+*/
+
+static void kbic_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(0x98); w2(4); w2(6); w2(4);
+                for (k=0;k<count/2;k++) {
+			w2(1); w0(8);    a = r1();
+			       w0(0x28); b = r1();
+			buf[2*k]   = j44(a,b);
+			w2(5);           b = r1();
+			       w0(8);    a = r1();
+			buf[2*k+1] = j44(a,b);
+			w2(4);
+                } 
+                break;
+
+        case 1: w0(0xb8); w2(4); w2(6); w2(4); 
+                for (k=0;k<count/4;k++) {
+                        w0(0xb8); 
+			w2(4); w2(5); 
+                        w0(8);    buf[4*k]   = j53(r12w());
+			w0(0xb8); buf[4*k+1] = j53(r12w());
+			w2(4); w2(5);
+			          buf[4*k+3] = j53(r12w());
+			w0(8);    buf[4*k+2] = j53(r12w());
+                }
+                w2(4);
+                break;
+
+        case 2: w0(0x88); w2(4); w2(6); w2(4);
+                for (k=0;k<count/2;k++) {
+                        w2(0xa0); w2(0xa1); buf[2*k] = r0();
+                        w2(0xa5); buf[2*k+1] = r0();
+                }
+                w2(4);
+                break;
+
+        case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4); w2(0); w2(4);
+                break;
+
+	case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4); w2(0); w2(4);
+                break;
+
+        case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4); w2(0); w2(4);
+                break;
+
+
+        }
+}
+
+static void kbic_write_block( PIA *pi, char * buf, int count )
+
+{       int     k;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1:
+        case 2: w0(0x90); w2(4); w2(6); w2(4); 
+		for(k=0;k<count/2;k++) {
+			w0(buf[2*k+1]); w2(0); w2(4); 
+			w0(buf[2*k]);   w2(5); w2(4); 
+		}
+		break;
+
+        case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+		for(k=0;k<count/2;k++) {
+			w4(buf[2*k+1]); 
+                        w4(buf[2*k]);
+                }
+		w2(4); w2(0); w2(4);
+		break;
+
+	case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+                w2(4); w2(0); w2(4);
+                break;
+
+        case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+                w2(4); w2(0); w2(4);
+                break;
+
+        }
+
+}
+
+static void kbic_log_adapter( PIA *pi, char * scratch, 
+			      int verbose, char * chip )
+
+{       char    *mode_string[6] = {"4-bit","5/3","8-bit",
+				   "EPP-8","EPP_16","EPP-32"};
+
+        printk("%s: kbic %s, KingByte %s at 0x%x, ",
+                pi->device,KBIC_VERSION,chip,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void k951_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{	kbic_log_adapter(pi,scratch,verbose,"KBIC-951A");
+}
+
+static void k971_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       kbic_log_adapter(pi,scratch,verbose,"KBIC-971A");
+}
+
+static void kbic_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void kbic_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol k951 = {"k951",0,6,3,1,1,
+                           kbic_write_regr,
+                           kbic_read_regr,
+                           kbic_write_block,
+                           kbic_read_block,
+                           k951_connect,
+                           k951_disconnect,
+                           0,
+                           0,
+                           0,
+                           k951_log_adapter,
+                           kbic_init_proto,
+                           kbic_release_proto
+  			  };
+
+
+struct pi_protocol k971 = {"k971",0,6,3,1,1,
+                           kbic_write_regr,
+                           kbic_read_regr,
+                           kbic_write_block,
+                           kbic_read_block,
+                           k971_connect,
+                           k971_disconnect,
+                           0,
+                           0,
+                           0,
+                           k971_log_adapter,
+                           kbic_init_proto,
+                           kbic_release_proto
+                          };
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       int s5,s7;
+
+	s5 = pi_register(&k951);
+	s7 = pi_register(&k971);
+
+	return (s5 || s7) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &k951 );
+	pi_unregister( &k971 );
+}
+
+#endif
+
+/* end of kbic.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/ktti.c linux/drivers/block/paride/ktti.c
--- v2.0.34/linux/drivers/block/paride/ktti.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/ktti.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,138 @@
+/* 
+        ktti.c        (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+	ktti.c is a low-level protocol driver for the KT Technology
+	parallel port adapter.  This adapter is used in the "PHd" 
+        portable hard-drives.  As far as I can tell, this device
+	supports 4-bit mode _only_.  
+
+*/
+
+#define KTTI_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x10, 0x08 };
+
+static void  ktti_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+	r = regr + cont_map[cont];
+
+	w0(r); w2(0xb); w2(0xa); w2(3); w2(6); 
+	w0(val); w2(3); w0(0); w2(6); w2(0xb);
+}
+
+static int ktti_read_regr( PIA *pi, int cont, int regr )
+
+{	int  a, b, r;
+
+        r = regr + cont_map[cont];
+
+        w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9); 
+	a = r1(); w2(0xc);  b = r1(); w2(9); w2(0xc); w2(9);
+	return j44(a,b);
+
+}
+
+static void ktti_read_block( PIA *pi, char * buf, int count )
+
+{	int  k, a, b;
+
+	for (k=0;k<count/2;k++) {
+		w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
+		a = r1(); w2(0xc); b = r1(); w2(9);
+		buf[2*k] = j44(a,b);
+		a = r1(); w2(0xc); b = r1(); w2(9);
+		buf[2*k+1] = j44(a,b);
+	}
+}
+
+static void ktti_write_block( PIA *pi, char * buf, int count )
+
+{	int k;
+
+	for (k=0;k<count/2;k++) {
+		w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6);
+		w0(buf[2*k]); w2(3);
+		w0(buf[2*k+1]); w2(6);
+		w2(0xb);
+	}
+}
+
+static void ktti_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(0xb); w2(0xa); w0(0); w2(3); w2(6);	
+}
+
+static void ktti_disconnect ( PIA *pi )
+
+{       w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4);
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void ktti_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       printk("%s: ktti %s, KT adapter at 0x%x, delay %d\n",
+                pi->device,KTTI_VERSION,pi->port,pi->delay);
+
+}
+
+static void ktti_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void ktti_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol ktti = {"ktti",0,1,2,1,1,
+                           ktti_write_regr,
+                           ktti_read_regr,
+                           ktti_write_block,
+                           ktti_read_block,
+                           ktti_connect,
+                           ktti_disconnect,
+                           0,
+                           0,
+                           0,
+                           ktti_log_adapter,
+                           ktti_init_proto,
+                           ktti_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &ktti ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &ktti );
+}
+
+#endif
+
+/* end of ktti.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/mkd linux/drivers/block/paride/mkd
--- v2.0.34/linux/drivers/block/paride/mkd	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/mkd	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# mkd -- a script to create the device special files for the PARIDE subsystem
+#
+#  block devices:  	pd (45), pcd (46), pf (47)
+#  character devices:	pt (96), pg (97)
+#
+function mkdev {
+  mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
+}
+#
+function pd {
+  D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
+  mkdev pd$D b 45 $[ $1 * 16 ]
+  for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+  do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
+  done
+}
+#
+cd /dev
+#
+for u in 0 1 2 3 ; do pd $u ; done
+for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done 
+for u in 0 1 2 3 ; do mkdev pf$u  b 47 $u ; done 
+for u in 0 1 2 3 ; do mkdev pt$u  c 96 $u ; done 
+for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done 
+for u in 0 1 2 3 ; do mkdev pg$u  c 97 $u ; done
+#
+# end of mkd
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/on20.c linux/drivers/block/paride/on20.c
--- v2.0.34/linux/drivers/block/paride/on20.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/on20.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,163 @@
+/* 
+	on20.c	(c) 1996-8  Grant R. Guenther <grant@torque.net>
+		            Under the terms of the GNU public license.
+
+        on20.c is a low-level protocol driver for the
+        Onspec 90c20 parallel to IDE adapter. 
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define	ON20_VERSION	"1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define op(f)	w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define vl(v)	w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
+
+#define j44(a,b)  (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int on20_read_regr( PIA *pi, int cont, int regr )
+
+{	int h,l, r ;
+
+        r = (regr<<2) + 1 + cont;
+
+        op(1); vl(r); op(0);
+
+	switch (pi->mode)  {
+
+        case 0:  w2(4); w2(6); l = r1();
+                 w2(4); w2(6); h = r1();
+                 w2(4); w2(6); w2(4); w2(6); w2(4);
+		 return j44(l,h);
+
+	case 1:  w2(4); w2(0x26); r = r0(); 
+                 w2(4); w2(0x26); w2(4);
+		 return r;
+
+	}
+	return -1;
+}	
+
+static void on20_write_regr( PIA *pi, int cont, int regr, int val )
+
+{	int r;
+
+	r = (regr<<2) + 1 + cont;
+
+	op(1); vl(r); 
+	op(0); vl(val); 
+	op(0); vl(val);
+}
+
+static void on20_connect ( PIA *pi)
+
+{	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+	w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4); 
+	if (pi->mode) { op(2); vl(8); op(2); vl(9); }
+	       else   { op(2); vl(0); op(2); vl(8); }
+}
+
+static void on20_disconnect ( PIA *pi )
+
+{	w2(4);w0(7);w2(4);w2(0xc);w2(4);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void on20_read_block( PIA *pi, char * buf, int count )
+
+{	int     k, l, h; 
+
+	op(1); vl(1); op(0);
+
+	for (k=0;k<count;k++) 
+	    if (pi->mode) {
+		w2(4); w2(0x26); buf[k] = r0();
+	    } else {
+		w2(6); l = r1(); w2(4);
+		w2(6); h = r1(); w2(4);
+		buf[k] = j44(l,h);
+	    }
+	w2(4);
+}
+
+static void on20_write_block(  PIA *pi, char * buf, int count )
+
+{	int	k;
+
+	op(1); vl(1); op(0);
+
+	for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); }
+	w2(4);
+}
+
+static void on20_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[2] = {"4-bit","8-bit"};
+
+        printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ",
+                pi->device,ON20_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void on20_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void on20_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on20 = {"on20",0,2,2,1,1,
+                           on20_write_regr,
+                           on20_read_regr,
+                           on20_write_block,
+                           on20_read_block,
+                           on20_connect,
+                           on20_disconnect,
+                           0,
+                           0,
+                           0,
+                           on20_log_adapter,
+                           on20_init_proto,
+                           on20_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &on20 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &on20 );
+}
+
+#endif
+
+/* end of on20.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/on26.c linux/drivers/block/paride/on26.c
--- v2.0.34/linux/drivers/block/paride/on26.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/on26.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,267 @@
+/* 
+        on26.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+        on26.c is a low-level protocol driver for the 
+        OnSpec 90c26 parallel to IDE adapter chip.
+
+*/
+
+/* Changes:
+
+        1.01    GRG 1998.05.06 init_proto, release_proto
+
+*/
+
+#define ON26_VERSION      "1.01"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+		3  EPP-16
+		4  EPP-32
+*/
+
+#define j44(a,b)  (((a>>4)&0x0f)|(b&0xf0))
+
+#define P1	w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define P2	w2(5);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int on26_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+	r = (regr<<2) + 1 + cont;
+
+        switch (pi->mode)  {
+
+        case 0: w0(1); P1; w0(r); P2; w0(0); P1; 
+		w2(6); a = r1(); w2(4);
+		w2(6); b = r1(); w2(4);
+		w2(6); w2(4); w2(6); w2(4);
+                return j44(a,b);
+
+        case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+		w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
+                return a;
+
+	case 2:
+	case 3:
+        case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+		w3(0); w3(0); w2(0x24); a = r4(); w2(4);
+		w2(0x24); r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void on26_write_regr( PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = (regr<<2) + 1 + cont;
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+		w0(val); P2; w0(val); P2;
+		break;
+
+	case 2:
+	case 3:
+        case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+		w3(0); w3(0); 
+		w2(5); w4(val); w2(4);
+		w2(5); w4(val); w2(4);
+                break;
+        }
+}
+
+#define  CCP(x)  w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+		 w0(0x87);w0(0x78);w0(x);w2(4);
+
+static void on26_connect ( PIA *pi )
+
+{       int	x;
+
+	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+        CCP(0x20);
+	w2(0xcd); w2(0xcc); w0(0xff);
+	x = 8; if (pi->mode) x = 9;
+
+	w0(2); P1; w0(8); P2;
+	w0(2); P1; w0(x); P2;
+}
+
+static void on26_disconnect ( PIA *pi )
+
+{       if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); }
+	              else { w0(4); P1; w0(4); P1; }
+	CCP(0x30);
+        w2(0xcd); w2(0xcc); w0(0xff);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void on26_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
+		udelay(10);
+		for (k=0;k<count;k++) {
+                        w2(6); a = r1();
+                        w2(4); b = r1();
+                        buf[k] = j44(a,b);
+                }
+		w0(2); P1; w0(8); P2; 
+                break;
+
+        case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
+		udelay(10);
+                for (k=0;k<count/2;k++) {
+                        w2(0x26); buf[2*k] = r0(); 
+			w2(0x24); buf[2*k+1] = r0();
+                }
+                w0(2); P1; w0(9); P2;
+                break;
+
+        case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+		w3(0); w3(0); w2(0x24);
+		udelay(10);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4);
+                break;
+
+        case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0x24);
+                udelay(10);
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4);
+                break;
+
+        case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0x24);
+                udelay(10);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4);
+                break;
+
+        }
+}
+
+static void on26_write_block( PIA *pi, char * buf, int count )
+
+{       int	k;
+
+        switch (pi->mode) {
+
+        case 0: 
+        case 1: w0(1); P1; w0(1); P2; 
+		w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1;
+		udelay(10);
+		for (k=0;k<count/2;k++) {
+                        w2(5); w0(buf[2*k]); 
+			w2(7); w0(buf[2*k+1]);
+                }
+                w2(5); w2(4);
+		w0(2); P1; w0(8+pi->mode); P2;
+                break;
+
+        case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+		w3(0); w3(0); w2(0xc5);
+		udelay(10);
+                for (k=0;k<count;k++) w4(buf[k]);
+		w2(0xc4);
+                break;
+
+        case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0xc5);
+                udelay(10);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0xc5);
+                udelay(10);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        }
+
+}
+
+static void on26_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8",
+				   "EPP-16","EPP-32"};
+
+        printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ",
+                pi->device,ON26_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void on26_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void on26_release_proto( PIA *pi)
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on26 = {"on26",0,5,2,1,1,
+                           on26_write_regr,
+                           on26_read_regr,
+                           on26_write_block,
+                           on26_read_block,
+                           on26_connect,
+                           on26_disconnect,
+                           0,
+                           0,
+                           0,
+                           on26_log_adapter,
+                           on26_init_proto,
+                           on26_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &on26 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &on26 );
+}
+
+#endif
+
+/* end of on26.c */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/paride.c linux/drivers/block/paride/paride.c
--- v2.0.34/linux/drivers/block/paride/paride.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/paride.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,514 @@
+/* 
+        paride.c  (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+	This is the base module for the family of device drivers
+        that support parallel port IDE devices.  
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.05.03	Use spinlocks
+	1.02	GRG 1998.05.05  init_proto, release_proto, ktti
+
+*/
+
+#define PI_VERSION      "1.02"
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include "spinlock.h"
+
+#ifdef CONFIG_PARPORT_MODULE
+#define CONFIG_PARPORT
+#endif
+
+#ifdef CONFIG_PARPORT
+#include <linux/parport.h>
+#endif
+
+#include "paride.h"
+
+#define MAX_PROTOS	32
+
+static struct pi_protocol	*protocols[MAX_PROTOS];
+
+/* spinlock_t	pi_spinlock = SPIN_LOCK_UNLOCKED; */
+
+void pi_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	pi->proto->write_regr(pi,cont,regr,val);
+}
+
+int pi_read_regr( PIA *pi, int cont, int regr)
+
+{	return pi->proto->read_regr(pi,cont,regr);
+}
+
+void pi_write_block( PIA *pi, char * buf, int count)
+
+{	pi->proto->write_block(pi,buf,count);
+}
+
+void pi_read_block( PIA *pi, char * buf, int count)
+
+{       pi->proto->read_block(pi,buf,count);
+}
+
+#ifdef CONFIG_PARPORT
+
+static void pi_wake_up( void *p)
+
+{       PIA  *pi = (PIA *) p;
+	long flags;
+	void (*cont)(void) = NULL;
+
+	spin_lock_irqsave(&pi_spinlock,flags);
+
+	if (pi->claim_cont && !parport_claim(pi->pardev)) {
+		cont = pi->claim_cont;
+		pi->claim_cont = NULL;
+		pi->claimed = 1;
+	}
+
+	spin_unlock_irqrestore(&pi_spinlock,flags);	
+
+	wake_up(&(pi->parq));
+
+	if (cont) cont();
+}
+
+#endif
+
+void pi_do_claimed( PIA *pi, void(*cont)(void))
+
+#ifdef CONFIG_PARPORT
+
+{	long flags;
+
+	spin_lock_irqsave(&pi_spinlock,flags); 
+
+	if (!pi->pardev || !parport_claim(pi->pardev)) {
+		pi->claimed = 1;
+		spin_unlock_irqrestore(&pi_spinlock,flags);
+		cont();
+	} else {
+		pi->claim_cont = cont;
+		spin_unlock_irqrestore(&pi_spinlock,flags);	
+	}
+}
+
+#else
+
+{	cont();
+}
+
+#endif
+
+static void pi_claim( PIA *pi)
+
+{	if (pi->claimed) return;
+	pi->claimed = 1;
+#ifdef CONFIG_PARPORT
+        if (pi->pardev)
+          while (parport_claim((struct pardevice *)(pi->pardev)))
+            sleep_on(&(pi->parq));
+#endif
+}
+
+static void pi_unclaim( PIA *pi)
+
+{	pi->claimed = 0;
+#ifdef CONFIG_PARPORT
+        if (pi->pardev) parport_release((struct pardevice *)(pi->pardev));
+#endif 
+}
+
+void pi_connect( PIA *pi)
+
+{	pi_claim(pi);
+	pi->proto->connect(pi);
+}
+
+void pi_disconnect( PIA *pi)
+
+{	pi->proto->disconnect(pi);
+	pi_unclaim(pi);
+}
+
+static void pi_unregister_parport( PIA *pi)
+
+{
+#ifdef CONFIG_PARPORT
+        if (pi->pardev) {
+           parport_unregister_device((struct pardevice *)(pi->pardev));
+	   pi->pardev = NULL;
+	}
+#endif
+}
+
+void pi_release( PIA *pi)
+
+{	pi_unregister_parport(pi);
+	if ((!pi->pardev)&&(pi->reserved)) 
+		release_region(pi->port,pi->reserved);
+	pi->proto->release_proto(pi);
+}
+
+#define WR(r,v)         pi_write_regr(pi,0,r,v)
+#define RR(r)           (pi_read_regr(pi,0,r))
+
+static int pi_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k;
+        int     e[2] = {0,0};
+
+	if (pi->proto->test_proto) {
+		pi_claim(pi);
+		j = pi->proto->test_proto(pi,scratch,verbose);
+		pi_unclaim(pi);
+		return j;
+	}
+
+        pi_connect(pi);
+
+        for (j=0;j<2;j++) {
+                WR(6,0xa0+j*0x10);
+                for (k=0;k<256;k++) {
+                        WR(2,k^0xaa);
+                        WR(3,k^0x55);
+                        if (RR(2) != (k^0xaa)) e[j]++;
+                        }
+                }
+
+        pi_disconnect(pi);
+
+        if (verbose)
+                printk("%s: %s: port 0x%x, mode  %d, test=(%d,%d)\n",
+                        pi->device,pi->proto->name,pi->port,
+			pi->mode,e[0],e[1]);
+
+        return (e[0] && e[1]);  /* not here if both > 0 */
+}
+
+int  pi_register( PIP *pr)
+
+{	int k;
+
+	for (k=0;k<MAX_PROTOS;k++) 
+	   if (protocols[k] && !strcmp(pr->name,protocols[k]->name)) {
+		printk("paride: %s protocol already registered\n",pr->name);
+		return 0;
+	   }
+	k = 0;
+	while((k<MAX_PROTOS) && (protocols[k])) k++;
+	if (k == MAX_PROTOS) {
+		printk("paride: protocol table full\n");
+		return 0;
+	}
+	MOD_INC_USE_COUNT;
+	protocols[k] = pr;
+	pr->index = k;
+	printk("paride: %s registered as protocol %d\n",pr->name,k);
+	return 1;
+}	
+
+void pi_unregister( PIP *pr)
+
+{	if (!pr) return;
+	if (protocols[pr->index] != pr) {
+		printk("paride: %s not registered\n",pr->name);
+		return;
+	} 
+	protocols[pr->index] = 0;
+	MOD_DEC_USE_COUNT;
+}
+
+static void pi_register_parport( PIA *pi, int verbose)
+
+{
+#ifdef CONFIG_PARPORT
+
+	struct parport	*pp;
+
+	pp = parport_enumerate();
+
+	while((pp)&&(pp->base != pi->port)) pp = pp->next;
+
+	if (!pp) return;
+
+	pi->pardev = (void *) parport_register_device(
+	      pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi);
+
+	pi->parq = NULL;
+
+	if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name);
+	
+	pi->parname = pp->name;
+
+#endif
+}
+
+static int pi_probe_mode( PIA *pi, int max, char * scratch, int verbose)
+
+{	int	best, range;
+
+	if (pi->mode != -1) {
+		if (pi->mode >= max) return 0;
+		range = 3;
+		if (pi->mode >= pi->proto->epp_first) range = 8;
+		if ((range == 8) && (pi->port % 8)) return 0;
+		if ((!pi->pardev) && check_region(pi->port,range)) return 0;
+		pi->reserved = range;
+		return (!pi_test_proto(pi,scratch,verbose));
+	}
+	best = -1;
+	for(pi->mode=0;pi->mode<max;pi->mode++) {
+		range = 3;
+		if (pi->mode >= pi->proto->epp_first) range = 8;
+		if ((range == 8) && (pi->port % 8)) break;
+		if ((!pi->pardev) && check_region(pi->port,range)) break;
+		pi->reserved = range;
+		if (!pi_test_proto(pi,scratch,verbose)) best = pi->mode;
+	}
+	pi->mode = best;
+	return (best > -1);
+}
+
+static int pi_probe_unit( PIA *pi, int unit, char * scratch, int verbose)
+
+{	int max,s,e;
+
+	s = unit; e = s+1;
+
+	if (s == -1) { 
+		s = 0; 
+		e = pi->proto->max_units; 
+	}
+
+	pi_register_parport(pi,verbose);
+
+	if ((!pi->pardev) && check_region(pi->port,3)) return 0;
+
+	if (pi->proto->test_port) {
+		pi_claim(pi);
+		max = pi->proto->test_port(pi);
+		pi_unclaim(pi);
+	}
+	else max = pi->proto->max_mode;
+
+	if (pi->proto->probe_unit) {
+	   pi_claim(pi);
+	   for (pi->unit=s;pi->unit<e;pi->unit++)
+	      if (pi->proto->probe_unit(pi)) {
+		 pi_unclaim(pi);
+		 if (pi_probe_mode(pi,max,scratch,verbose)) return 1;
+	         pi_unregister_parport(pi); 
+		 return 0;
+	         }
+	   pi_unclaim(pi);
+	   pi_unregister_parport(pi); 
+	   return 0;
+	   }
+
+	if (!pi_probe_mode(pi,max,scratch,verbose)) {
+	   pi_unregister_parport(pi); 
+	   return 0;
+	}
+	return 1;
+		
+}
+
+int pi_init(PIA *pi, int autoprobe, int port, int mode, 
+	    int unit, int protocol, int delay, char * scratch, 
+	    int devtype, int verbose, char *device )
+
+{	int p,k,s,e;
+	int lpts[7] = {0x3bc,0x378,0x278,0x268,0x27c,0x26c,0};
+
+	s = protocol; e = s+1;
+
+	if (autoprobe) {
+		s = 0; 
+		e = MAX_PROTOS;
+	} else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) ||
+		    (!protocols[s]) || (unit < 0) || 
+		    (unit >= protocols[s]->max_units)) {
+			printk("%s: Invalid parameters\n",device);
+			return 0;
+		        }
+
+	for (p=s;p<e;p++) {
+	  if (protocols[p]) {
+		pi->proto = protocols[p];
+		pi->private = 0;
+		pi->proto->init_proto(pi);
+		if (delay == -1) pi->delay = pi->proto->default_delay;
+		  else pi->delay = delay;
+		pi->devtype = devtype;
+		pi->device = device;
+
+		pi->parname = NULL;
+		pi->pardev = NULL;
+		pi->parq = NULL;
+		pi->claimed = 0;
+		pi->claim_cont = NULL;
+	        
+		pi->mode = mode;
+		if (port != -1) {
+			pi->port = port;
+			if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+			pi->port = 0;
+		} else { 
+			k = 0;
+			while ((pi->port = lpts[k++]))
+			   if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+      			if (pi->port) break;
+		}
+		pi->proto->release_proto(pi);
+	  }
+	}
+
+	if (!pi->port) {
+		if (autoprobe) printk("%s: Autoprobe failed\n",device);
+		else printk("%s: Adapter not found\n",device);
+		return 0;
+	}
+
+	if (!pi->pardev)
+	   request_region(pi->port,pi->reserved,pi->device);
+
+	if (pi->parname)
+	   printk("%s: Sharing %s at 0x%x\n",pi->device,
+			pi->parname,pi->port);
+
+	pi->proto->log_adapter(pi,scratch,verbose);
+
+	return 1;
+}
+
+#ifdef MODULE
+
+int	init_module(void)
+
+{	int k;
+
+	for (k=0;k<MAX_PROTOS;k++) protocols[k] = 0;
+	printk("paride: version %s installed\n",PI_VERSION);
+	return 0;
+}
+
+void	cleanup_module(void)
+
+{
+}
+
+#else
+
+void	paride_init( void )
+
+{
+
+#ifdef CONFIG_PARIDE_ATEN
+	{ extern struct pi_protocol aten;
+	  pi_register(&aten);
+	};
+#endif
+#ifdef CONFIG_PARIDE_BPCK
+        { extern struct pi_protocol bpck;
+          pi_register(&bpck);
+        };
+#endif
+#ifdef CONFIG_PARIDE_COMM
+        { extern struct pi_protocol comm;
+          pi_register(&comm);
+        };
+#endif
+#ifdef CONFIG_PARIDE_DSTR
+        { extern struct pi_protocol dstr;
+          pi_register(&dstr);
+        };
+#endif
+#ifdef CONFIG_PARIDE_EPAT
+        { extern struct pi_protocol epat;
+          pi_register(&epat);
+        };
+#endif
+#ifdef CONFIG_PARIDE_EPIA
+        { extern struct pi_protocol epia;
+          pi_register(&epia);
+        };
+#endif
+#ifdef CONFIG_PARIDE_FRPW
+        { extern struct pi_protocol frpw;
+          pi_register(&frpw);
+        };
+#endif
+#ifdef CONFIG_PARIDE_FIT2
+        { extern struct pi_protocol fit2;
+          pi_register(&fit2);
+        };
+#endif
+#ifdef CONFIG_PARIDE_FIT3
+        { extern struct pi_protocol fit3;
+          pi_register(&fit3);
+        };
+#endif
+#ifdef CONFIG_PARIDE_KBIC
+        { extern struct pi_protocol k951;
+          extern struct pi_protocol k971;
+          pi_register(&k951);
+          pi_register(&k971);
+        };
+#endif
+#ifdef CONFIG_PARIDE_KTTI
+        { extern struct pi_protocol ktti;
+          pi_register(&ktti);
+        };
+#endif
+#ifdef CONFIG_PARIDE_ON20
+        { extern struct pi_protocol on20;
+          pi_register(&on20);
+        };
+#endif
+#ifdef CONFIG_PARIDE_ON26
+        { extern struct pi_protocol on26;
+          pi_register(&on26);
+        };
+#endif
+
+#ifdef CONFIG_PARIDE_PD
+	{ extern int pd_init(void);
+	  pd_init();
+	};
+#endif
+#ifdef CONFIG_PARIDE_PCD
+        { extern int pcd_init(void);
+          pcd_init();
+        };
+#endif
+#ifdef CONFIG_PARIDE_PF
+        { extern int pf_init(void);
+          pf_init();
+        };
+#endif
+#ifdef CONFIG_PARIDE_PT
+        { extern int pt_init(void);
+          pt_init();
+        };
+#endif
+#ifdef CONFIG_PARIDE_PG
+        { extern int pg_init(void);
+          pg_init();
+        };
+#endif
+}
+
+#endif
+
+/* end of paride.c */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/paride.h linux/drivers/block/paride/paride.h
--- v2.0.34/linux/drivers/block/paride/paride.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/paride.h	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,164 @@
+/* 
+	paride.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>
+   		                    Under the terms of the GPL.
+
+   This file defines the interface between the high-level parallel
+   IDE device drivers (pd, pf, pcd, pt) and the adapter chips.
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.05.05	init_proto, release_proto
+*/
+
+#define PARIDE_H_VERSION 	"1.01"
+
+/* Some adapters need to know what kind of device they are in
+
+   Values for devtype:
+*/
+
+#define	PI_PD	0	/* IDE disk */
+#define PI_PCD	1	/* ATAPI CDrom */
+#define PI_PF   2	/* ATAPI disk */
+#define PI_PT	3	/* ATAPI tape */
+#define PI_PG   4       /* ATAPI generic */
+
+/* The paride module contains no state, instead the drivers allocate
+   a pi_adapter data structure and pass it to paride in every operation.
+
+*/
+
+struct pi_adapter  {
+
+	struct pi_protocol *proto;   /* adapter protocol */
+	int	port;		     /* base address of parallel port */
+	int	mode;		     /* transfer mode in use */
+	int     delay;		     /* adapter delay setting */
+	int	devtype;	     /* device type: PI_PD etc. */
+	char    *device;	     /* name of driver */
+	int     unit;		     /* unit number for chained adapters */
+	int	saved_r0;	     /* saved port state */
+	int	saved_r2;	     /* saved port state */
+	int	reserved;	     /* number of ports reserved */
+	int	private;	     /* for protocol module */
+
+	struct wait_queue *parq;     /* semaphore for parport sharing */
+	void	*pardev;	     /* pointer to pardevice */
+	char	*parname;	     /* parport name */
+	int	claimed;	     /* parport has already been claimed */
+	void (*claim_cont)(void);    /* continuation for parport wait */
+};
+
+typedef struct pi_adapter PIA;
+
+/* functions exported by paride to the high level drivers */
+
+extern int pi_init(PIA *pi, 
+	int autoprobe,		/* 1 to autoprobe */
+	int port, 		/* base port address */
+	int mode, 		/* -1 for autoprobe */
+	int unit,		/* unit number, if supported */
+	int protocol, 		/* protocol to use */
+	int delay, 		/* -1 to use adapter specific default */
+	char * scratch, 	/* address of 512 byte buffer */
+	int devtype,		/* device type: PI_PD, PI_PCD, etc ... */
+	int verbose,		/* log verbose data while probing */
+	char *device		/* name of the driver */
+	);			/* returns 0 on failure, 1 on success */
+
+extern void pi_release(PIA *pi);
+
+/* registers are addressed as (cont,regr)
+
+       	cont: 0 for command register file, 1 for control register(s)
+	regr: 0-7 for register number.
+
+*/
+
+extern void pi_write_regr(PIA *pi, int cont, int regr, int val);
+
+extern int pi_read_regr(PIA *pi, int cont, int regr);
+
+extern void pi_write_block(PIA *pi, char * buf, int count);
+
+extern void pi_read_block(PIA *pi, char * buf, int count);
+
+extern void pi_connect(PIA *pi);
+
+extern void pi_disconnect(PIA *pi);
+
+extern void pi_do_claimed(PIA *pi, void (*cont)(void));
+
+/* macros and functions exported to the protocol modules */
+
+#define delay_p			(pi->delay?udelay(pi->delay):0)
+#define out_p(offs,byte)	outb(byte,pi->port+offs); delay_p;
+#define in_p(offs)		(delay_p,inb(pi->port+offs))
+
+#define w0(byte)                {out_p(0,byte);}
+#define r0()                    (in_p(0) & 0xff)
+#define w1(byte)                {out_p(1,byte);}
+#define r1()                    (in_p(1) & 0xff)
+#define w2(byte)                {out_p(2,byte);}
+#define r2()                    (in_p(2) & 0xff)
+#define w3(byte)                {out_p(3,byte);}
+#define w4(byte)                {out_p(4,byte);}
+#define r4()                    (in_p(4) & 0xff)
+#define w4w(data)     		{outw(data,pi->port+4); delay_p;}
+#define w4l(data)     		{outl(data,pi->port+4); delay_p;}
+#define r4w()         		(delay_p,inw(pi->port+4)&0xffff)
+#define r4l()         		(delay_p,inl(pi->port+4)&0xffffffff)
+
+static inline u16 pi_swab16( char *b, int k)
+
+{ 	union { u16 u; char t[2]; } r;
+
+	r.t[0]=b[2*k+1]; r.t[1]=b[2*k];
+        return r.u;
+}
+
+static inline u32 pi_swab32( char *b, int k)
+
+{ 	union { u32 u; char f[4]; } r;
+
+	r.f[0]=b[4*k+1]; r.f[1]=b[4*k];
+	r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2];
+        return r.u;
+}
+
+struct pi_protocol {
+
+	char	name[8];	/* name for this protocol */
+	int	index;		/* index into protocol table */
+
+	int	max_mode;	/* max mode number */
+	int	epp_first;	/* modes >= this use 8 ports */
+	
+	int	default_delay;  /* delay parameter if not specified */
+	int	max_units;	/* max chained units probed for */
+
+	void (*write_regr)(PIA *,int,int,int);
+	int  (*read_regr)(PIA *,int,int);
+	void (*write_block)(PIA *,char *,int);
+	void (*read_block)(PIA *,char *,int);
+
+	void (*connect)(PIA *);
+	void (*disconnect)(PIA *);
+	
+	int  (*test_port)(PIA *);
+	int  (*probe_unit)(PIA *);
+	int  (*test_proto)(PIA *,char *,int);
+	void (*log_adapter)(PIA *,char *,int);
+	
+	void (*init_proto)(PIA *);
+	void (*release_proto)(PIA *);
+};
+
+typedef struct pi_protocol PIP;
+
+extern int pi_register( PIP * );
+extern void pi_unregister ( PIP * );
+
+/* end of paride.h */
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c
--- v2.0.34/linux/drivers/block/paride/pcd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pcd.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,807 @@
+/* 
+	pcd.c	(c) 1997-8  Grant R. Guenther <grant@torque.net>
+		            Under the terms of the GNU public license.
+
+
+	Special 2.0.34 version
+
+
+	This is high-level driver for parallel port ATAPI CDrom
+        drives based on chips supported by the paride module.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI CDrom drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pcd driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-6 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+		<slv>   ATAPI CDroms can be jumpered to master or slave.
+			Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+			first drive found.
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+                        
+            major       You may use this parameter to overide the
+                        default major number (46) that this driver
+                        will use.  Be sure to change the device
+                        name as well.
+
+            name        This parameter is a character string that
+                        contains the name the kernel will use for this
+                        device (in /proc output, for instance).
+                        (default "pcd")
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+            nice        This parameter controls the driver's use of
+                        idle CPU time, at the expense of some speed.
+ 
+	If this driver is built into the kernel, you can use kernel
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+	    pcd.drive0
+	    pcd.drive1
+	    pcd.drive2
+	    pcd.drive3
+	    pcd.nice
+
+        In addition, you can use the parameter pcd.disable to disable
+        the driver entirely.
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1997.01.24	Added test unit ready support
+	1.02    GRG 1998.05.06  Changes to pcd_completion, ready_wait,
+				and loosen interpretation of ATAPI
+			        standard for clearing error status.
+				Use spinlocks. Eliminate sti().
+	1.03    GRG 1998.06.16  Eliminated an Ugh
+
+*/
+
+#define	PCD_VERSION	"1.03s"
+#define PCD_MAJOR	46
+#define PCD_NAME	"pcd"
+#define PCD_UNITS	4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is off
+   by default.
+
+*/
+
+static int      verbose = 0;
+static int      major = PCD_MAJOR;
+static char     *name = PCD_NAME;
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pcd_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_DLY   5
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/cdrom.h>
+
+#include <asm/segment.h>
+#include "spinlock.h"
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pcd_stt[6] = {{"drive0",6,drive0},
+                         {"drive1",6,drive1},
+                         {"drive2",6,drive2},
+                         {"drive3",6,drive3},
+			 {"disable",1,&disable},
+                         {"nice",1,&nice}};
+
+void pcd_setup( char *str, int *ints)
+
+{       generic_setup(pcd_stt,6,str);
+}
+
+#endif
+
+#include "paride.h"
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR	major
+#define DEVICE_NAME "PCD"
+#define DEVICE_REQUEST do_pcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PCD_RETRIES	     5
+#define PCD_TMO		   800		/* timeout in jiffies */
+#define PCD_DELAY           50          /* spin delay in uS */
+#define PCD_READY_TMO	    20
+
+#define PCD_SPIN		(10000/PCD_DELAY)*PCD_TMO
+
+#define IDE_ERR		0x01
+#define IDE_DRQ         0x08
+#define IDE_READY       0x40
+#define IDE_BUSY        0x80
+
+int pcd_init(void);
+void cleanup_module( void );
+
+static int 	pcd_open(struct inode *inode, struct file *file);
+static void 	do_pcd_request(void);
+static void 	do_pcd_read(void);
+static int 	pcd_ioctl(struct inode *inode,struct file *file,
+                         unsigned int cmd, unsigned long arg);
+
+static void pcd_release (struct inode *inode, struct file *file);
+
+static int 	pcd_detect(void);
+static void     pcd_lock(int unit);
+static void     pcd_unlock(int unit);
+static void     pcd_eject(int unit);
+static int      pcd_check_media(int unit);
+static void     do_pcd_read_drq(void);
+
+static int pcd_blocksizes[PCD_UNITS];
+
+#define PCD_NAMELEN	8
+
+struct pcd_unit {
+	struct pi_adapter pia;	/* interface to paride layer */
+	struct pi_adapter *pi;
+	int drive;		/* master/slave */
+	int last_sense;		/* result of last request sense */
+	int access;		/* count of active opens */
+	int present;		/* does this unit exist ? */
+	char name[PCD_NAMELEN];	/* pcd0, pcd1, etc */
+	};
+
+struct pcd_unit pcd[PCD_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PCD pcd[unit]
+#define PI PCD.pi
+
+static char pcd_scratch[64];
+static char pcd_buffer[2048];           /* raw block buffer */
+static int pcd_bufblk = -1;             /* block in buffer, in CD units,
+                                           -1 for nothing there. See also
+					   pd_unit.
+					 */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pcd_unit = -1;		/* unit of current request & bufblk */
+static int pcd_retries;			/* retries on current request */
+static int pcd_busy = 0;		/* request being processed ? */
+static int pcd_sector;			/* address of next requested sector */
+static int pcd_count;			/* number of blocks still to do */
+static char * pcd_buf;			/* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pcd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	pcd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	pcd_open,		/* open */
+	pcd_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	NULL,                   /* media change ? */
+	NULL			/* revalidate new media */
+};
+
+static void pcd_init_units( void )
+
+{       int     unit, j;
+
+        pcd_drive_count = 0;
+        for (unit=0;unit<PCD_UNITS;unit++) {
+                PCD.pi = & PCD.pia;
+                PCD.access = 0;
+                PCD.present = 0;
+		PCD.last_sense = 0;
+                j = 0;
+                while ((j < PCD_NAMELEN-2) && (PCD.name[j]=name[j])) j++;
+                PCD.name[j++] = '0' + unit;
+                PCD.name[j] = 0;
+                PCD.drive = DU[D_SLV];
+                if (DU[D_PRT]) pcd_drive_count++;
+        }
+}
+
+int pcd_init (void)	/* preliminary initialisation */
+
+{       int 	i;
+
+	if (disable) return -1;
+
+	pcd_init_units();
+
+	if (pcd_detect()) return -1;
+
+	if (register_blkdev(MAJOR_NR,name,&pcd_fops)) {
+		printk("pcd: unable to get major number %d\n",MAJOR_NR);
+		return -1;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read ahead */
+
+	for (i=0;i<PCD_UNITS;i++) pcd_blocksizes[i] = 1024;
+        blksize_size[MAJOR_NR] = pcd_blocksizes;
+
+	return 0;
+}
+
+static int pcd_open (struct inode *inode, struct file *file)
+
+{	int unit = DEVICE_NR(inode->i_rdev);
+
+	if  ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+	if (file->f_mode & 2) return -EROFS;  /* wants to write ? */
+
+	MOD_INC_USE_COUNT;
+
+	if (pcd_check_media(unit)) {
+		MOD_DEC_USE_COUNT;
+		return -ENXIO;
+	}
+
+	pcd_lock(unit);
+
+	PCD.access++;
+	return 0;
+}
+
+static void do_pcd_request (void)
+
+{       int unit;
+
+	if (pcd_busy) return;
+        while (1) {
+	    if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+	    INIT_REQUEST;
+	    if (CURRENT->cmd == READ) {
+		unit = MINOR(CURRENT->rq_dev);
+		if (unit != pcd_unit) {
+			pcd_bufblk = -1;
+			pcd_unit = unit;
+		}
+	        pcd_sector = CURRENT->sector;
+	        pcd_count = CURRENT->nr_sectors;
+	        pcd_buf = CURRENT->buffer;
+		pcd_busy = 1;
+	        ps_set_intr(do_pcd_read,0,0,nice); 
+		return;
+	    } 
+	    else end_request(0);
+	}
+}
+
+static int pcd_ioctl(struct inode *inode,struct file *file,
+		    unsigned int cmd, unsigned long arg)
+
+/* we currently support only the EJECT ioctl. */
+
+{	int unit = DEVICE_NR(inode->i_rdev);
+	if  ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+	switch (cmd) {
+            case CDROMEJECT: if (PCD.access == 1) {
+				pcd_eject(unit);
+				return 0;
+			     }
+	    default:
+	        return -EINVAL;
+	}
+}
+
+static void pcd_release (struct inode *inode, struct file *file)
+
+{	kdev_t	devp;
+	int 	unit;
+
+	devp = inode->i_rdev;
+	unit = DEVICE_NR(devp);
+
+	if  ((unit >= PCD_UNITS) || (PCD.access <= 0)) 
+			return;
+       
+	PCD.access--;
+
+	if (!PCD.access) { 
+		fsync_dev(devp);
+
+                invalidate_inodes(devp);
+
+	    	invalidate_buffers(devp);
+	    	pcd_unlock(unit);
+
+	}
+
+	MOD_DEC_USE_COUNT;
+
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+int	init_module(void)
+
+{	int	err;
+
+	err = pcd_init();
+
+	return err;
+}
+
+void	cleanup_module(void)
+
+{	int unit;
+
+	unregister_blkdev(MAJOR_NR,name);
+	
+        for (unit=0;unit<PCD_UNITS;unit++) 
+           if (PCD.present) pi_release(PI);
+}
+
+#endif
+
+#define WR(c,r,v)       pi_write_regr(PI,c,r,v)
+#define RR(c,r)         (pi_read_regr(PI,c,r))
+
+static int pcd_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{	int j, r, e, s, p;
+
+	j = 0;
+	while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PCD_SPIN))
+		udelay(PCD_DELAY);
+
+	if ((r&(IDE_ERR&stop))||(j>=PCD_SPIN)) {
+	   s = RR(0,7);
+	   e = RR(0,1);
+	   p = RR(0,2);
+       	   if (j >= PCD_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+			   " loop=%d phase=%d\n",
+			    PCD.name,fun,msg,r,s,e,j,p);
+	   return (s<<8)+r;
+	}
+	return 0;
+}
+
+static int pcd_command( int unit, char * cmd, int dlen, char * fun )
+
+{	pi_connect(PI);
+
+        WR(0,6,0xa0 + 0x10*PCD.drive);
+
+	if (pcd_wait(unit,IDE_BUSY|IDE_DRQ,0,fun,"before command")) {
+		pi_disconnect(PI);
+		return -1;
+	}
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pcd_wait(unit,IDE_BUSY,IDE_DRQ,fun,"command DRQ")) {
+		pi_disconnect(PI);
+		return -1;
+	}
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PCD.name,fun);
+	   pi_disconnect(PI);
+           return -1;
+        }
+
+	pi_write_block(PI,cmd,12);
+
+	return 0;
+}
+
+static int pcd_completion( int unit, char * buf,  char * fun )
+
+{	int r, s, n;
+
+	r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion");
+
+	if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) { 
+	        n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc);
+	        pi_read_block(PI,buf,n);
+	}
+
+	s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done");
+
+	pi_disconnect(PI); 
+
+	return (r?r:s);
+}
+
+static void pcd_req_sense( int unit, int quiet )
+
+{	char	rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 };
+	char	buf[16];
+	int 	r;
+
+	r = pcd_command(unit,rs_cmd,16,"Request sense");
+	udelay(1000);
+	if (!r) pcd_completion(unit,buf,"Request sense");
+
+	PCD.last_sense = -1;
+	if (!r) {
+            if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+	                       PCD.name,buf[2]&0xf,buf[12],buf[13]);
+	    PCD.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+                                          | ((buf[13]&0xff)<<16) ;
+        } 
+}
+
+static int pcd_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{	int r;
+
+	r = pcd_command(unit,cmd,dlen,fun);
+	udelay(1000);
+	if (!r) r = pcd_completion(unit,buf,fun);
+	if (r) pcd_req_sense(unit,!fun);
+	
+	return r;
+}
+
+#define DBMSG(msg)	NULL
+
+static void pcd_lock(int unit)
+
+{	char	lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 };
+	char	cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 };
+
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd1")); 
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd2"));
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd3"));
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd4"));
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,"close door");
+
+        pcd_atapi(unit,lo_cmd,0,pcd_scratch,DBMSG("ld"));
+        pcd_atapi(unit,lo_cmd,0,pcd_scratch,"lock door");
+}
+
+static void pcd_unlock( int unit )
+
+{	char	un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 };
+
+	pcd_atapi(unit,un_cmd,0,pcd_scratch,"unlock door");
+}
+
+static void pcd_eject( int unit)
+
+{	char	ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 };
+
+	pcd_unlock(unit);
+	pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject");
+}
+
+#define PCD_RESET_TMO	30		/* in tenths of a second */
+
+static void pcd_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+static int pcd_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+   after a reset, but the specification is ambiguous concerning the last
+   two bytes, and different drives interpret the standard differently.
+*/
+
+{	int	i, k, flg;
+	int	expect[5] = {1,1,1,0x14,0xeb};
+
+	pi_connect(PI);
+	WR(0,6,0xa0 + 0x10*PCD.drive);
+	WR(0,7,8);
+
+	pcd_sleep(2);  		/* delay a bit*/
+
+	k = 0;
+	while ((k++ < PCD_RESET_TMO) && (RR(1,6)&IDE_BUSY))
+		pcd_sleep(10);
+
+	flg = 1;
+	for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+	if (verbose) {
+		printk("%s: Reset (%d) signature = ",PCD.name,k);
+		for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+		if (!flg) printk(" (incorrect)");
+		printk("\n");
+	}
+	
+	pi_disconnect(PI);
+	return flg-1;	
+}
+
+static int pcd_ready_wait( int unit, int tmo )
+
+{       char    tr_cmd[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+        int     k, p;
+
+        k = 0;
+        while (k < tmo) {
+          PCD.last_sense = 0;
+          pcd_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+          p = PCD.last_sense;
+          if (!p) return 0;
+	  if (!(((p & 0xffff) == 0x0402)||((p & 0xff) == 6))) return p;
+          k++;
+          pcd_sleep(100);
+        }
+        return 0x000020;        /* timeout */
+}
+
+static int pcd_check_media( int unit )
+
+{	char    rc_cmd[12] = { 0x25,0,0,0,0,0,0,0,0,0,0,0};
+
+	pcd_ready_wait(unit,PCD_READY_TMO);
+	return (pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("check media")));
+}
+
+static int pcd_identify( int unit, char * id )
+
+{	int k, s;
+	char   id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0};
+
+	pcd_bufblk = -1;
+
+        s = pcd_atapi(unit,id_cmd,36,pcd_buffer,"identify");
+
+	if (s) return -1;
+	if ((pcd_buffer[0] & 0x1f) != 5) {
+	  if (verbose) printk("%s: %s is not a CDrom\n",
+			PCD.name,PCD.drive?"Slave":"Master");
+	  return -1;
+	}
+	for (k=0;k<16;k++) id[k] = pcd_buffer[16+k]; id[16] = 0;
+	k = 16; while ((k >= 0) && (id[k] <= 0x20)) { id[k] = 0; k--; }
+
+	printk("%s: %s: %s\n",PCD.name,PCD.drive?"Slave":"Master",id);
+
+	return 0;
+}
+
+static int pcd_probe( int unit, int ms, char * id )
+
+/*	returns  0, with id set if drive is detected
+	        -1, if drive detection failed
+*/
+
+{	if (ms == -1) {
+            for (PCD.drive=0;PCD.drive<=1;PCD.drive++)
+	       if (!pcd_reset(unit) && !pcd_identify(unit,id)) 
+		  return 0;
+	} else {
+	    PCD.drive = ms;
+            if (!pcd_reset(unit) && !pcd_identify(unit,id)) 
+		return 0;
+	}
+	return -1;
+}
+
+static int pcd_detect( void )
+
+{	char    id[18];
+	int	k, unit;
+
+	printk("%s: %s version %s, major %d, nice %d\n",
+		name,name,PCD_VERSION,major,nice);
+
+	k = 0;
+	if (pcd_drive_count == 0) {  /* nothing spec'd - so autoprobe for 1 */
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pcd_buffer,
+                     PI_PCD,verbose,PCD.name)) {
+		if (!pcd_probe(unit,-1,id)) {
+			PCD.present = 1;
+			k++;
+		} else pi_release(PI);
+	    }
+
+	} else for (unit=0;unit<PCD_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+                        DU[D_PRO],DU[D_DLY],pcd_buffer,PI_PCD,verbose,
+                        PCD.name)) {
+		if (!pcd_probe(unit,DU[D_SLV],id)) {
+                        PCD.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+	if (k) return 0;
+	
+	printk("%s: No CDrom drive found\n",name);
+	return -1;
+}
+
+/* I/O request processing */
+
+static int pcd_ready( void )
+
+{	int	unit = pcd_unit;
+
+	return (((RR(1,6)&(IDE_BUSY|IDE_DRQ))==IDE_DRQ)) ;
+}
+
+static void pcd_transfer( void )
+
+{	int	k, o;
+
+	while (pcd_count && (pcd_sector/4 == pcd_bufblk)) {
+		o = (pcd_sector % 4) * 512;
+		for(k=0;k<512;k++) pcd_buf[k] = pcd_buffer[o+k];
+		pcd_count--;
+		pcd_buf += 512;
+		pcd_sector++;
+	}
+}
+
+static void pcd_start( void )
+
+{	int	unit = pcd_unit;
+	int	b, i;
+	char	rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0};
+	long    saved_flags;
+
+	pcd_bufblk = pcd_sector / 4;
+        b = pcd_bufblk;
+	for(i=0;i<4;i++) { 
+	   rd_cmd[5-i] = b & 0xff;
+	   b = b >> 8;
+	}
+
+	if (pcd_command(unit,rd_cmd,2048,"read block")) {
+		pcd_bufblk = -1; 
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+		pcd_busy = 0;
+		end_request(0);
+		do_pcd_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		return;
+	}
+
+	udelay(1000);
+
+	ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice);
+
+}
+
+static void do_pcd_read( void )
+
+
+{	int	unit = pcd_unit;
+	long    saved_flags;
+
+	pcd_busy = 1;
+	pcd_retries = 0;
+	pcd_transfer();
+	if (!pcd_count) {
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+		end_request(1);
+		pcd_busy = 0;
+		do_pcd_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		return;
+	}
+
+	pi_do_claimed(PI,pcd_start);
+}
+
+static void do_pcd_read_drq( void )
+
+{	int	unit = pcd_unit;
+	long    saved_flags;
+
+	if (pcd_completion(unit,pcd_buffer,"read block")) {
+                if (pcd_retries < PCD_RETRIES) {
+                        udelay(1000);
+                        pcd_retries++;
+			pi_do_claimed(PI,pcd_start);
+                        return;
+                        }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+		pcd_busy = 0;
+		pcd_bufblk = -1;
+		end_request(0);
+		do_pcd_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		return;
+	}
+
+	do_pcd_read();
+	spin_lock_irqsave(&io_request_lock,saved_flags);
+	do_pcd_request();
+	spin_unlock_irqrestore(&io_request_lock,saved_flags); 
+}
+	
+/* end of pcd.c */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c
--- v2.0.34/linux/drivers/block/paride/pd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pd.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,1107 @@
+/* 
+        pd.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+
+	Special 2.0.34 version.
+
+
+
+        This is the high-level driver for parallel port IDE hard
+        drives based on chips supported by the paride module.
+
+	By default, the driver will autoprobe for a single parallel
+	port IDE drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pd driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+ 
+	    drive0  	These four arguments can be arrays of	    
+	    drive1	1-8 integers as follows:
+	    drive2
+	    drive3	<prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>,<slv>
+
+			Where,
+
+		<prt>	is the base of the parallel port address for
+			the corresponding drive.  (required)
+
+		<pro>   is the protocol number for the adapter that
+			supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+			are initialised.  (0 if not given)
+
+		<uni>   for those adapters that support chained
+			devices, this is the unit selector for the
+		        chain of devices on the given port.  It should
+			be zero for devices that don't support chaining.
+			(0 if not given)
+
+		<mod>   this can be -1 to choose the best mode, or one
+		        of the mode numbers supported by the adapter.
+			(-1 if not given)
+
+		<geo>   this defaults to 0 to indicate that the driver
+			should use the CHS geometry provided by the drive
+			itself.  If set to 1, the driver will provide
+			a logical geometry with 64 heads and 32 sectors
+			per track, to be consistent with most SCSI
+		        drivers.  (0 if not given)
+
+		<sby>   set this to zero to disable the power saving
+			standby mode, if needed.  (1 if not given)
+
+		<dly>   some parallel ports require the driver to 
+			go more slowly.  -1 sets a default value that
+			should work with the chosen protocol.  Otherwise,
+			set this to a small integer, the larger it is
+			the slower the port i/o.  In some cases, setting
+			this to zero will speed up the device. (default -1)
+
+		<slv>   IDE disks can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+			
+
+            major       You may use this parameter to overide the
+                        default major number (45) that this driver
+                        will use.  Be sure to change the device
+                        name as well.
+
+            name        This parameter is a character string that
+                        contains the name the kernel will use for this
+                        device (in /proc output, for instance).
+			(default "pd")
+
+	    cluster	The driver will attempt to aggregate requests
+			for adjacent blocks into larger multi-block
+			clusters.  The maximum cluster size (in 512
+			byte sectors) is set with this parameter.
+			(default 64)
+
+	    verbose	This parameter controls the amount of logging
+			that is done while the driver probes for
+			devices.  Set it to 0 for a quiet load, or to 1
+			see all the progress messages.  (default 0)
+
+            nice        This parameter controls the driver's use of
+                        idle CPU time, at the expense of some speed.
+
+        If this driver is built into the kernel, you can use kernel
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pd.drive0
+            pd.drive1
+            pd.drive2
+            pd.drive3
+            pd.cluster
+            pd.nice
+
+        In addition, you can use the parameter pd.disable to disable
+        the driver entirely.
+ 
+*/
+
+/* Changes:
+
+	1.01	GRG 1997.01.24	Restored pd_reset()
+				Added eject ioctl
+	1.02    GRG 1998.05.06  SMP spinlock changes, 
+				Added slave support
+	1.03    GRG 1998.06.16  Eliminate an Ugh.
+
+*/
+
+#define PD_VERSION      "1.03s"
+#define PD_MAJOR	45
+#define PD_NAME		"pd"
+#define PD_UNITS	4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is off
+   by default.
+
+*/
+
+static int	verbose = 0;
+static int	major = PD_MAJOR;
+static char	*name = PD_NAME;
+static int	cluster = 64;	
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[8] = {0,0,0,-1,0,1,-1,-1};
+static int drive1[8] = {0,0,0,-1,0,1,-1,-1};
+static int drive2[8] = {0,0,0,-1,0,1,-1,-1};
+static int drive3[8] = {0,0,0,-1,0,1,-1,-1};
+
+static int (*drives[4])[8] = {&drive0,&drive1,&drive2,&drive3};
+static int pd_drive_count;
+
+#define D_PRT	0
+#define D_PRO   1
+#define D_UNI	2
+#define D_MOD	3
+#define D_GEO	4
+#define D_SBY	5
+#define D_DLY	6
+#define D_SLV   7
+
+#define	DU		(*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>	/* for the eject ioctl */
+
+#include "spinlock.h"
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pd_stt[7] = {{"drive0",8,drive0},
+		        {"drive1",8,drive1},
+		        {"drive2",8,drive2},
+		        {"drive3",8,drive3},
+			{"disable",1,&disable},
+		        {"cluster",1,&cluster},
+		        {"nice",1,&nice}};
+
+void pd_setup( char *str, int *ints)
+
+{	generic_setup(pd_stt,7,str);
+}
+
+#endif
+
+#include "paride.h"
+
+#define PD_BITS    4
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR   major
+#define DEVICE_NAME "PD"
+#define DEVICE_REQUEST do_pd_request
+#define DEVICE_NR(device) (MINOR(device)>>PD_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PD_PARTNS  	(1<<PD_BITS)
+#define PD_DEVS		PD_PARTNS*PD_UNITS
+
+/* numbers for "SCSI" geometry */
+
+#define PD_LOG_HEADS    64
+#define PD_LOG_SECTS    32
+
+#define PD_ID_OFF       54
+#define PD_ID_LEN       14
+
+#define PD_MAX_RETRIES  5
+#define PD_TMO          800             /* interrupt timeout in jiffies */
+#define PD_SPIN_DEL     50              /* spin delay in micro-seconds  */
+
+#define PD_SPIN         (10000/PD_SPIN_DEL)*PD_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+
+#define ERR_AMNF        0x00100
+#define ERR_TK0NF       0x00200
+#define ERR_ABRT        0x00400
+#define ERR_MCR         0x00800
+#define ERR_IDNF        0x01000
+#define ERR_MC          0x02000
+#define ERR_UNC         0x04000
+#define ERR_TMO         0x10000
+
+#define IDE_READ        	0x20
+#define IDE_WRITE       	0x30
+#define IDE_READ_VRFY		0x40
+#define IDE_INIT_DEV_PARMS	0x91
+#define IDE_STANDBY     	0x96
+#define IDE_ACKCHANGE   	0xdb
+#define IDE_DOORLOCK    	0xde
+#define IDE_DOORUNLOCK  	0xdf
+#define IDE_IDENTIFY    	0xec
+#define IDE_EJECT		0xed
+
+int pd_init(void);
+void pd_setup(char * str, int * ints);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static void pd_geninit(struct gendisk *ignored);
+static int pd_open(struct inode *inode, struct file *file);
+static void do_pd_request(void);
+static int pd_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+static void pd_release (struct inode *inode, struct file *file);
+static int pd_revalidate(kdev_t dev);
+static int pd_detect(void);
+static void do_pd_read(void);
+static void do_pd_read_start(void);
+static void do_pd_write(void);
+static void do_pd_write_start(void);
+static void do_pd_read_drq( void );
+static void do_pd_write_done( void );
+
+static int pd_identify (int unit);
+static void pd_media_check(int unit);
+static void pd_doorlock(int unit, int func);
+static int pd_check_media(kdev_t dev);
+static void pd_eject( int unit);
+
+static struct hd_struct pd_hd[PD_DEVS];
+static int pd_sizes[PD_DEVS];
+static int pd_blocksizes[PD_DEVS];
+
+#define PD_NAMELEN	8
+
+struct pd_unit {
+	struct pi_adapter pia;		/* interface to paride layer */
+	struct pi_adapter *pi;
+	int access;               	/* count of active opens ... */
+	int capacity;             	/* Size of this volume in sectors */
+	int heads;                	/* physical geometry */
+	int sectors;
+	int cylinders;
+	int drive;			/* master=0 slave=1 */
+	int changed;			/* Have we seen a disk change ? */
+	int removable;			/* removable media device  ?  */
+	int standby;
+	int alt_geom;
+	int present;
+	char name[PD_NAMELEN];		/* pda, pdb, etc ... */
+	};
+
+struct pd_unit pd[PD_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PD pd[unit]
+#define PI PD.pi
+
+static int pd_valid = 1;		/* serialise partition checks */
+static char pd_scratch[512];            /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pd_retries = 0;              /* i/o error retry count */
+static int pd_busy = 0;                 /* request being processed ? */
+static int pd_block;                    /* address of next requested block */
+static int pd_count;                    /* number of blocks still to do */
+static int pd_run;			/* sectors in current cluster */
+static int pd_cmd;			/* current command READ/WRITE */
+static int pd_unit;			/* unit of current request */
+static int pd_dev;			/* minor of current request */
+static int pd_poffs;			/* partition offset of current minor */
+static char * pd_buf;                   /* buffer for request in progress */
+
+static struct wait_queue *pd_wait_open = NULL;
+
+static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
+                             "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
+                             "IDNF","MC","UNC","???","TMO"};
+
+/* kernel glue structures */
+
+static struct gendisk pd_gendisk = {
+        PD_MAJOR,       /* Major number */
+        PD_NAME,        /* Major name */
+        PD_BITS,        /* Bits to shift to get real from partition */
+        PD_PARTNS,      /* Number of partitions per real */
+        PD_UNITS,       /* maximum number of real */
+        pd_geninit,     /* init function */
+        pd_hd,          /* hd struct */
+        pd_sizes,       /* block sizes */
+        0,              /* number */
+        NULL,           /* internal */
+        NULL            /* next */
+};
+
+static struct file_operations pd_fops = {
+        NULL,                   /* lseek - default */
+        block_read,             /* read - general block-dev read */
+        block_write,            /* write - general block-dev write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pd_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pd_open,                /* open */
+        pd_release,             /* release */
+        block_fsync,            /* fsync */
+        NULL,                   /* fasync */
+        pd_check_media,         /* media change ? */
+        pd_revalidate           /* revalidate new media */
+};
+
+void pd_init_units( void )
+
+{	int	unit, j;
+
+	pd_drive_count = 0;
+	for (unit=0;unit<PD_UNITS;unit++) {
+		PD.pi = & PD.pia;
+		PD.access = 0;
+		PD.changed = 1;
+		PD.capacity = 0;
+		PD.drive = DU[D_SLV];
+		PD.present = 0;
+		j = 0;
+		while ((j < PD_NAMELEN-2) && (PD.name[j]=name[j])) j++;
+		PD.name[j++] = 'a' + unit;
+		PD.name[j] = 0;
+		PD.alt_geom = DU[D_GEO];
+		PD.standby = DU[D_SBY];
+		if (DU[D_PRT]) pd_drive_count++;
+	}
+}
+
+int pd_init (void)
+
+{       int i;
+
+	if (disable) return -1;
+
+        if (register_blkdev(MAJOR_NR,name,&pd_fops)) {
+                printk("%s: unable to get major number %d\n",
+                        name,major);
+                return -1;
+        }
+        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+        read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
+        
+	pd_gendisk.major = major;
+	pd_gendisk.major_name = name;
+	pd_gendisk.next = gendisk_head;
+        gendisk_head = &pd_gendisk;
+
+        for(i=0;i<PD_DEVS;i++) pd_blocksizes[i] = 1024;
+        blksize_size[MAJOR_NR] = pd_blocksizes;
+
+        printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+                name,name,PD_VERSION,major,cluster,nice);
+	
+        return 0;
+}
+
+static void pd_geninit (struct gendisk *ignored)
+
+{	pd_init_units();
+	pd_gendisk.nr_real = pd_detect();
+
+#ifdef MODULE
+        if (!pd_gendisk.nr_real) cleanup_module();
+#endif
+
+}
+
+static int pd_open (struct inode *inode, struct file *file)
+
+{       int unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+        MOD_INC_USE_COUNT;
+
+        while (!pd_valid) sleep_on(&pd_wait_open);
+
+        PD.access++;
+
+        if (PD.removable) {
+		pd_media_check(unit);
+		pd_doorlock(unit,IDE_DOORLOCK);
+	}
+        return 0;
+}
+
+static int pd_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+
+{       struct hd_geometry *geo = (struct hd_geometry *) arg;
+        int dev, err, unit;
+
+        if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+        dev = MINOR(inode->i_rdev);
+	unit = DEVICE_NR(inode->i_rdev);
+        if (dev >= PD_DEVS) return -EINVAL;
+	if (!PD.present) return -ENODEV;
+
+        switch (cmd) {
+	    case CDROMEJECT:
+		if (PD.access == 1) pd_eject(unit);
+		return 0;
+            case HDIO_GETGEO:
+                if (!geo) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+                if (err) return err;
+
+		if (PD.alt_geom) {
+                    put_user(PD.capacity/(PD_LOG_HEADS*PD_LOG_SECTS), 
+		    		(short *) &geo->cylinders);
+                    put_user(PD_LOG_HEADS, (char *) &geo->heads);
+                    put_user(PD_LOG_SECTS, (char *) &geo->sectors);
+		} else {
+                    put_user(PD.cylinders, (short *) &geo->cylinders);
+                    put_user(PD.heads, (char *) &geo->heads);
+                    put_user(PD.sectors, (char *) &geo->sectors);
+		}
+                put_user(pd_hd[dev].start_sect,(long *)&geo->start);
+                return 0;
+            case BLKRASET:
+                if(!suser()) return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                if(arg > 0xff) return -EINVAL;
+                read_ahead[MAJOR(inode->i_rdev)] = arg;
+                return 0;
+            case BLKRAGET:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+                return (0);
+            case BLKGETSIZE:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(pd_hd[dev].nr_sects,(long *) arg);
+                return (0);
+            case BLKFLSBUF:
+                if(!suser())  return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                fsync_dev(inode->i_rdev);
+                invalidate_buffers(inode->i_rdev);
+                return 0;
+            case BLKRRPART:
+		if (!suser()) return -EACCES;
+                return pd_revalidate(inode->i_rdev);
+            RO_IOCTLS(inode->i_rdev,arg);
+            default:
+                return -EINVAL;
+        }
+}
+
+static void pd_release (struct inode *inode, struct file *file)
+
+{       kdev_t devp;
+	int	unit;
+
+        devp = inode->i_rdev;
+	unit = DEVICE_NR(devp);
+
+	if ((unit >= PD_UNITS) || (PD.access <= 0)) 
+		return;
+
+	PD.access--;
+
+        if (!PD.access)  {
+                fsync_dev(devp);
+
+		invalidate_inodes(devp);
+
+                invalidate_buffers(devp);
+		if (PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK);
+	}
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pd_check_media( kdev_t dev)
+
+{       int r, unit;
+
+	unit = DEVICE_NR(dev);
+	if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+        if (!PD.removable) return 0;
+	pd_media_check(unit);
+	r = PD.changed;
+	PD.changed = 0;
+	return r;
+}
+
+static int pd_revalidate(kdev_t dev)
+
+{       int p, unit, minor;
+        long flags;
+        kdev_t devp;
+
+        unit = DEVICE_NR(dev);
+        if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+        save_flags(flags);
+        cli(); 
+        if (PD.access > 1) {
+                restore_flags(flags);
+                return -EBUSY;
+        }
+        pd_valid = 0;
+        restore_flags(flags);   
+
+        for (p=(PD_PARTNS-1);p>=0;p--) {
+		minor = p + unit*PD_PARTNS;
+                devp = MKDEV(MAJOR_NR, minor);
+                fsync_dev(devp);
+
+                invalidate_inodes(devp);
+
+                invalidate_buffers(devp);
+                pd_hd[minor].start_sect = 0;
+                pd_hd[minor].nr_sects = 0;
+        }
+
+	pd_identify(unit);
+        resetup_one_dev(&pd_gendisk,unit);
+
+        pd_valid = 1;
+        wake_up(&pd_wait_open);
+
+        return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err, unit;
+
+        err = pd_init();
+        if (err) return err;
+
+	pd_geninit(&pd_gendisk);
+
+        if (!pd_gendisk.nr_real)  return -1;
+
+        pd_valid = 0;
+	for (unit=0;unit<PD_UNITS;unit++) 
+          if (PD.present) resetup_one_dev(&pd_gendisk,unit);
+        pd_valid = 1;
+
+        return 0;
+}
+
+void    cleanup_module(void)
+
+{       struct gendisk **gdp;
+	int unit;
+
+        unregister_blkdev(MAJOR_NR,name);
+
+        for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
+                if (*gdp == &pd_gendisk) break;
+        if (*gdp) *gdp = (*gdp)->next;
+
+	for (unit=0;unit<PD_UNITS;unit++) 
+	   if (PD.present) pi_release(PI);
+}
+
+#endif
+
+#define	WR(c,r,v)	pi_write_regr(PI,c,r,v)
+#define	RR(c,r)		(pi_read_regr(PI,c,r))
+
+#define DRIVE		(0xa0+0x10*PD.drive)
+
+/*  ide command interface */
+
+static void pd_print_error( int unit, char * msg, int status )
+
+{       int     i;
+
+	printk("%s: %s: status = 0x%x =",PD.name,msg,status);
+        for(i=0;i<18;i++) if (status & (1<<i)) printk(" %s",pd_errs[i]);
+	printk("\n");
+}
+
+static void pd_reset( int unit )    /* called only for MASTER drive */
+
+{       pi_connect(PI);
+	WR(1,6,4);
+        udelay(50);
+        WR(1,6,0);
+	pi_disconnect(PI);
+	udelay(250);
+}
+
+#define DBMSG(msg)	NULL
+
+static int pd_wait_for( int unit, int w, char * msg )    /* polled wait */
+
+{       int     k, r, e;
+
+        k=0;
+        while(k < PD_SPIN) { 
+            r = RR(1,6);
+            k++;
+            if (((r & w) == w) && !(r & STAT_BUSY)) break;
+            udelay(PD_SPIN_DEL);
+        }
+        e = (RR(0,1)<<8) + RR(0,7);
+        if (k >= PD_SPIN)  e |= ERR_TMO;
+        if ((e & (STAT_ERR|ERR_TMO)) && (msg != NULL)) 
+		pd_print_error(unit,msg,e);
+        return e;
+}
+
+static void pd_send_command( int unit, int n, int s, int h, 
+			     int c0, int c1, int func )
+
+{
+        WR(0,6,DRIVE+h);
+        WR(0,1,0);                /* the IDE task file */
+        WR(0,2,n);
+        WR(0,3,s);
+        WR(0,4,c0);
+        WR(0,5,c1);
+        WR(0,7,func);
+
+        udelay(1);
+}
+
+static void pd_ide_command( int unit, int func, int block, int count )
+
+/* Don't use this call if the capacity is zero. */
+
+{       int c1, c0, h, s;
+
+        s  = ( block % PD.sectors) + 1;
+        h  = ( block / PD.sectors) % PD.heads;
+        c0 = ( block / (PD.sectors*PD.heads)) % 256;
+        c1 = ( block / (PD.sectors*PD.heads*256));
+
+        pd_send_command(unit,count,s,h,c0,c1,func);
+}
+
+/* According to the ATA standard, the default CHS geometry should be
+   available following a reset.  Some Western Digital drives come up
+   in a mode where only LBA addresses are accepted until the device
+   parameters are initialised.
+*/
+
+static void pd_init_dev_parms( int unit )
+ 
+{       pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before init_dev_parms"));
+        pd_send_command(unit,PD.sectors,0,PD.heads-1,0,0,IDE_INIT_DEV_PARMS);
+        udelay(300);
+        pd_wait_for(unit,0,"Initialise device parameters");
+        pi_disconnect(PI);
+}
+
+static void pd_doorlock( int unit, int func )
+
+{       pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"Lock") & STAT_ERR) {
+                pi_disconnect(PI);
+                return;
+        }
+        pd_send_command(unit,1,0,0,0,0,func);
+        pd_wait_for(unit,STAT_READY,"Lock done");
+        pi_disconnect(PI);
+}
+
+static void pd_eject( int unit )
+
+{	pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before unlock on eject"));
+        pd_send_command(unit,1,0,0,0,0,IDE_DOORUNLOCK);
+        pd_wait_for(unit,0,DBMSG("after unlock on eject"));
+        pd_wait_for(unit,0,DBMSG("before eject"));
+        pd_send_command(unit,0,0,0,0,0,IDE_EJECT);
+        pd_wait_for(unit,0,DBMSG("after eject"));
+        pi_disconnect(PI);
+}
+
+static void pd_media_check( int unit )
+
+{       int 	r;
+
+        pi_connect(PI);
+        r = pd_wait_for(unit,STAT_READY,DBMSG("before media_check"));
+        if (!(r & STAT_ERR)) {
+                pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);  
+                r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after READ_VRFY"));
+        } else PD.changed = 1;   /* say changed if other error */
+        if (r & ERR_MC) {
+                PD.changed = 1;
+                pd_send_command(unit,1,0,0,0,0,IDE_ACKCHANGE);
+                pd_wait_for(unit,STAT_READY,DBMSG("RDY after ACKCHANGE"));
+		pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);
+                r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after VRFY"));
+        }
+        pi_disconnect(PI);
+
+}
+
+static void pd_standby_off( int unit )
+
+{       pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before STANDBY"));
+        pd_send_command(unit,0,0,0,0,0,IDE_STANDBY);
+        pd_wait_for(unit,0,DBMSG("after STANDBY"));
+        pi_disconnect(PI);
+}
+
+#define  word_val(n) ((pd_scratch[2*n]&0xff)+256*(pd_scratch[2*n+1]&0xff))
+
+static int pd_identify( int unit )
+
+{       int	j;
+	char id[PD_ID_LEN+1];
+
+/* WARNING:  here there may be dragons.  reset() applies to both drives,
+   but we call it only on probing the MASTER. This should allow most
+   common configurations to work, but be warned that a reset can clear
+   settings on the SLAVE drive.
+*/ 
+
+	if (PD.drive == 0) pd_reset(unit);
+
+        pi_connect(PI);
+	WR(0,6,DRIVE);
+        pd_wait_for(unit,0,DBMSG("before IDENT"));  
+        pd_send_command(unit,1,0,0,0,0,IDE_IDENTIFY);
+
+        if (pd_wait_for(unit,STAT_DRQ,DBMSG("IDENT DRQ")) & STAT_ERR) {
+                pi_disconnect(PI);
+                return 0;
+        }
+        pi_read_block(PI,pd_scratch,512);
+        pi_disconnect(PI);
+        PD.sectors = word_val(6);
+        PD.heads = word_val(3);
+        PD.cylinders  = word_val(1);
+        PD.capacity = PD.sectors*PD.heads*PD.cylinders;
+
+        for(j=0;j<PD_ID_LEN;j++) id[j^1] = pd_scratch[j+PD_ID_OFF];
+        j = PD_ID_LEN-1;
+        while ((j >= 0) && (id[j] <= 0x20)) j--;
+        j++; id[j] = 0;
+
+        PD.removable = (word_val(0) & 0x80);
+ 
+        printk("%s: %s, %s, %d blocks [%dM], (%d/%d/%d), %s media\n",
+                    PD.name,id,
+		    PD.drive?"slave":"master",
+		    PD.capacity,PD.capacity/2048,
+                    PD.cylinders,PD.heads,PD.sectors,
+                    PD.removable?"removable":"fixed");
+
+        if (PD.capacity) pd_init_dev_parms(unit);
+        if (!PD.standby) pd_standby_off(unit);
+
+	pd_hd[unit<<PD_BITS].nr_sects = PD.capacity;
+	pd_hd[unit<<PD_BITS].start_sect = 0;
+	
+        return 1;
+}
+
+static int pd_probe_drive( int unit )
+
+{	if (PD.drive == -1) {
+	  for (PD.drive=0;PD.drive<=1;PD.drive++)
+	     if (pd_identify(unit)) return 1;
+	  return 0;
+	  }
+	else return pd_identify(unit);
+}
+
+static int pd_detect( void )
+
+{       int	k, unit;
+
+	k = 0;
+	if (pd_drive_count == 0) {  /* nothing spec'd - so autoprobe for 1 */
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pd_scratch,
+	             PI_PD,verbose,PD.name)) {
+		if (pd_probe_drive(unit)) {
+			PD.present = 1;
+			k = 1;
+		} else pi_release(PI);
+	    }
+
+   	} else for (unit=0;unit<PD_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+			DU[D_PRO],DU[D_DLY],pd_scratch,
+			PI_PD,verbose,PD.name)) {
+                if (pd_probe_drive(unit)) {
+                        PD.present = 1;
+                        k = unit+1;
+                } else pi_release(PI);
+            }
+
+/* We lie about the number of drives found, as the generic partition
+   scanner assumes that the drives are numbered sequentially from 0.
+   This can result in some bogus error messages if non-sequential
+   drive numbers are used.
+*/
+	
+	if (k) return k; 
+
+        printk("%s: no valid drive found\n",name);
+        return 0;
+}
+
+/* The i/o request engine */
+
+static int pd_ready( void )
+
+{ 	int unit = pd_unit;
+
+	return (!(RR(1,6) & STAT_BUSY)) ;
+}
+
+static void do_pd_request (void)
+
+{       struct buffer_head * bh;
+	struct request * req;
+	int	unit;
+
+        if (pd_busy) return;
+repeat:
+        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        INIT_REQUEST;
+
+        pd_dev = MINOR(CURRENT->rq_dev);
+	pd_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+        pd_block = CURRENT->sector;
+        pd_count = CURRENT->nr_sectors;
+
+	bh = CURRENT->bh;
+	req = CURRENT;
+	if (bh->b_reqnext)
+		printk("%s: OUCH: b_reqnext != NULL\n",PD.name);
+
+        if ((pd_dev >= PD_DEVS) || 
+	    ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) {
+                end_request(0);
+                goto repeat;
+        }
+
+	pd_cmd = CURRENT->cmd;
+	pd_run = pd_count;
+        while ((pd_run <= cluster) &&
+	       (req = req->next) && 
+	       (pd_block+pd_run == req->sector) &&
+	       (pd_cmd == req->cmd) &&
+	       (pd_dev == MINOR(req->rq_dev)))
+			pd_run += req->nr_sectors;
+
+	pd_poffs = pd_hd[pd_dev].start_sect;
+        pd_block += pd_poffs;
+        pd_buf = CURRENT->buffer;
+        pd_retries = 0;
+
+	pd_busy = 1;
+        if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read);
+        else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write);
+        else {  pd_busy = 0;
+		end_request(0);
+                goto repeat;
+        }
+}
+
+static void pd_next_buf( int unit )
+
+{	long	saved_flags;
+
+	spin_lock_irqsave(&io_request_lock,saved_flags);
+	end_request(1);
+	if (!pd_run) {  spin_unlock_irqrestore(&io_request_lock,saved_flags);
+			return; 
+	}
+	
+/* paranoia */
+
+	if ((!CURRENT) ||
+	    (CURRENT->cmd != pd_cmd) ||
+	    (MINOR(CURRENT->rq_dev) != pd_dev) ||
+	    (CURRENT->rq_status == RQ_INACTIVE) ||
+	    (CURRENT->sector+pd_poffs != pd_block)) 
+		printk("%s: OUCH: request list changed unexpectedly\n",
+			PD.name);
+
+	pd_count = CURRENT->nr_sectors;
+	pd_buf = CURRENT->buffer;
+	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pd_read( void )
+
+{	ps_set_intr(do_pd_read_start,0,0,nice);
+}
+
+static void do_pd_read_start( void )
+ 
+{       int	unit = pd_unit;
+	long    saved_flags;
+
+	pd_busy = 1;
+
+        pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"do_pd_read") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_read_start);
+			return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pd_ide_command(unit,IDE_READ,pd_block,pd_run);
+        ps_set_intr(do_pd_read_drq,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_read_drq( void )
+
+{       int	unit = pd_unit;
+	long    saved_flags;
+
+	while (1) {
+            if (pd_wait_for(unit,STAT_DRQ,"do_pd_read_drq") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_read_start);
+                        return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+            }
+            pi_read_block(PI,pd_buf,512);
+            pd_count--; pd_run--;
+            pd_buf += 512;
+	    pd_block++;
+	    if (!pd_run) break;
+	    if (!pd_count) pd_next_buf(unit);
+        }
+        pi_disconnect(PI);
+	spin_lock_irqsave(&io_request_lock,saved_flags);
+        end_request(1);
+        pd_busy = 0;
+        do_pd_request();
+	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pd_write( void )
+
+{	 ps_set_intr(do_pd_write_start,0,0,nice);
+}
+
+static void do_pd_write_start( void )
+
+{       int 	unit = pd_unit;
+	long    saved_flags;
+
+	pd_busy = 1;
+
+        pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"do_pd_write") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+			pi_do_claimed(PI,do_pd_write_start);
+                        return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pd_ide_command(unit,IDE_WRITE,pd_block,pd_run);
+	while (1) {
+            if (pd_wait_for(unit,STAT_DRQ,"do_pd_write_drq") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_write_start);
+                        return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+                spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		return;
+            }
+            pi_write_block(PI,pd_buf,512);
+	    pd_count--; pd_run--;
+	    pd_buf += 512;
+	    pd_block++;
+	    if (!pd_run) break;
+	    if (!pd_count) pd_next_buf(unit);
+	}
+        ps_set_intr(do_pd_write_done,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_write_done( void )
+
+{       int	unit = pd_unit;
+	long    saved_flags;
+
+        if (pd_wait_for(unit,STAT_READY,"do_pd_write_done") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_write_start);
+                        return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pd_busy = 0;
+                do_pd_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pi_disconnect(PI);
+	spin_lock_irqsave(&io_request_lock,saved_flags);
+        end_request(1);
+        pd_busy = 0;
+        do_pd_request();
+	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+/* end of pd.c */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c
--- v2.0.34/linux/drivers/block/paride/pf.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pf.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,1080 @@
+/* 
+        pf.c    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+
+	Special 2.0.34 version
+
+
+        This is the high-level driver for parallel port ATAPI disk
+        drives based on chips supported by the paride module.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI disk drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pf driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-7 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+                <slv>   ATAPI CDroms can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+
+		<lun>   Some ATAPI devices support multiple LUNs.
+                        One example is the ATAPI PD/CD drive from
+                        Matshita/Panasonic.  This device has a 
+                        CD drive on LUN 0 and a PD drive on LUN 1.
+                        By default, the driver will search for the
+                        first LUN with a supported device.  Set 
+                        this parameter to force it to use a specific
+                        LUN.  (default -1)
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+
+	    major	You may use this parameter to overide the
+			default major number (47) that this driver
+			will use.  Be sure to change the device
+			name as well.
+
+	    name	This parameter is a character string that
+			contains the name the kernel will use for this
+			device (in /proc output, for instance).
+			(default "pf").
+
+            cluster     The driver will attempt to aggregate requests
+                        for adjacent blocks into larger multi-block
+                        clusters.  The maximum cluster size (in 512
+                        byte sectors) is set with this parameter.
+                        (default 64)
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+	    nice        This parameter controls the driver's use of
+			idle CPU time, at the expense of some speed.
+
+        If this driver is built into the kernel, you can use the
+        following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pf.drive0
+            pf.drive1
+            pf.drive2
+            pf.drive3
+	    pf.cluster
+            pf.nice
+
+        In addition, you can use the parameter pf.disable to disable
+        the driver entirely.
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.05.03  Changes for SMP.  Eliminate sti().
+				Fix for drives that don't clear STAT_ERR
+			        until after next CDB delivered.
+				Small change in pf_completion to round
+				up transfer size.
+	1.02    GRG 1998.06.16  Eliminated an Ugh
+
+*/
+
+#define PF_VERSION      "1.02s"
+#define PF_MAJOR	47
+#define PF_NAME		"pf"
+#define PF_UNITS	4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is off
+   by default.
+
+*/
+
+static int	verbose = 0;
+static int	major = PF_MAJOR;
+static char	*name = PF_NAME;
+static int      cluster = 64;
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[7] = {0,0,0,-1,-1,-1,-1};
+static int drive1[7] = {0,0,0,-1,-1,-1,-1};
+static int drive2[7] = {0,0,0,-1,-1,-1,-1};
+static int drive3[7] = {0,0,0,-1,-1,-1,-1};
+
+static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3};
+static int pf_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_LUN   5
+#define D_DLY   6
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>
+#include "spinlock.h"
+
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pf_stt[7] = {{"drive0",7,drive0},
+                        {"drive1",7,drive1},
+                        {"drive2",7,drive2},
+                        {"drive3",7,drive3},
+			{"disable",1,&disable},
+                        {"cluster",1,&cluster},
+                        {"nice",1,&nice}};
+
+void pf_setup( char *str, int *ints)
+
+{       generic_setup(pf_stt,7,str);
+}
+
+#endif
+
+#include "paride.h"
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR   major
+#define DEVICE_NAME "PF"
+#define DEVICE_REQUEST do_pf_request
+#define DEVICE_NR(device) MINOR(device)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+/* constants for faking geometry numbers */
+
+#define PF_FD_MAX	8192		/* use FD geometry under this size */
+#define PF_FD_HDS	2
+#define PF_FD_SPT	18
+#define PF_HD_HDS	64
+#define PF_HD_SPT	32
+
+#define PF_MAX_RETRIES  5
+#define PF_TMO          800             /* interrupt timeout in jiffies */
+#define PF_SPIN_DEL     50              /* spin delay in micro-seconds  */
+
+#define PF_SPIN         (10000/PF_SPIN_DEL)*PF_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+
+#define ATAPI_REQ_SENSE		0x03
+#define ATAPI_LOCK		0x1e
+#define ATAPI_DOOR		0x1b
+#define ATAPI_MODE_SENSE	0x5a
+#define ATAPI_CAPACITY		0x25
+#define ATAPI_IDENTIFY		0x12
+#define ATAPI_READ_10		0x28
+#define ATAPI_WRITE_10		0x2a
+
+int pf_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static int pf_open(struct inode *inode, struct file *file);
+static void do_pf_request(void);
+static int pf_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+
+static void pf_release (struct inode *inode, struct file *file);
+
+static int pf_detect(void);
+static void do_pf_read(void);
+static void do_pf_read_start(void);
+static void do_pf_write(void);
+static void do_pf_write_start(void);
+static void do_pf_read_drq( void );
+static void do_pf_write_done( void );
+
+static int pf_identify (int unit);
+static void pf_lock(int unit, int func);
+static void pf_eject(int unit);
+static int pf_check_media(kdev_t dev);
+
+static int pf_blocksizes[PF_UNITS];
+
+#define PF_NM           0
+#define PF_RO           1
+#define PF_RW           2
+
+#define PF_NAMELEN      8
+
+struct pf_unit {
+	struct pi_adapter pia;    /* interface to paride layer */
+	struct pi_adapter *pi;
+	int removable;		  /* removable media device  ?  */
+	int media_status;	  /* media present ?  WP ? */
+	int drive;		  /* drive */
+	int lun;
+	int access;               /* count of active opens ... */
+	int capacity;             /* Size of this volume in sectors */
+	int present;		  /* device present ? */
+	char name[PF_NAMELEN];	  /* pf0, pf1, ... */
+	};
+
+struct pf_unit pf[PF_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PF pf[unit]
+#define PI PF.pi
+
+static char pf_scratch[512];            /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pf_retries = 0;              /* i/o error retry count */
+static int pf_busy = 0;                 /* request being processed ? */
+static int pf_block;                    /* address of next requested block */
+static int pf_count;                    /* number of blocks still to do */
+static int pf_run;			/* sectors in current cluster */
+static int pf_cmd;			/* current command READ/WRITE */
+static int pf_unit;			/* unit of current request */
+static int pf_mask;			/* stopper for pseudo-int */
+static char * pf_buf;                   /* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pf_fops = {
+        NULL,                   /* lseek - default */
+        block_read,             /* read - general block-dev read */
+        block_write,            /* write - general block-dev write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pf_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pf_open,                /* open */
+        pf_release,             /* release */
+        block_fsync,            /* fsync */
+        NULL,                   /* fasync */
+        pf_check_media,         /* media change ? */
+        NULL                    /* revalidate new media */
+};
+
+void pf_init_units( void )
+
+{       int     unit, j;
+
+        pf_drive_count = 0;
+        for (unit=0;unit<PF_UNITS;unit++) {
+                PF.pi = & PF.pia;
+                PF.access = 0;
+                PF.media_status = PF_NM;
+                PF.capacity = 0;
+                PF.present = 0;
+		PF.drive = DU[D_SLV];
+		PF.lun = DU[D_LUN];
+                j = 0;
+                while ((j < PF_NAMELEN-2) && (PF.name[j]=name[j])) j++;
+                PF.name[j++] = '0' + unit;
+                PF.name[j] = 0;
+                if (DU[D_PRT]) pf_drive_count++;
+        }
+} 
+
+int pf_init (void)      /* preliminary initialisation */
+
+{       int i;
+
+	if (disable) return -1;
+
+	pf_init_units();
+
+	if (pf_detect()) return -1;
+	pf_busy = 0;
+
+        if (register_blkdev(MAJOR_NR,name,&pf_fops)) {
+                printk("pf_init: unable to get major number %d\n",
+                        major);
+                return -1;
+        }
+        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+        read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
+        
+	for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
+	blksize_size[MAJOR_NR] = pf_blocksizes;
+
+        return 0;
+}
+
+static int pf_open (struct inode *inode, struct file *file)
+
+{       int	unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PF_UNITS) || (!PF.present)) return -ENODEV;
+
+        MOD_INC_USE_COUNT;
+
+	pf_identify(unit);
+
+	if (PF.media_status == PF_NM) {
+		MOD_DEC_USE_COUNT;
+		return -ENODEV;
+		}
+
+	if ((PF.media_status == PF_RO) && (file ->f_mode & 2)) {
+		MOD_DEC_USE_COUNT;
+		return -EROFS;
+		}
+
+        PF.access++;
+        if (PF.removable) pf_lock(unit,1);
+
+        return 0;
+}
+
+static int pf_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+
+{       int err, unit;
+	struct hd_geometry *geo = (struct hd_geometry *) arg;
+
+        if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+        unit = DEVICE_NR(inode->i_rdev);
+        if (unit >= PF_UNITS) return -EINVAL;
+        if (!PF.present) return -ENODEV;
+
+        switch (cmd) {
+	    case CDROMEJECT: 
+		if (PF.access == 1) {
+			pf_eject(unit);
+			return 0;
+			}
+	    case HDIO_GETGEO:
+                if (!geo) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+                if (err) return err;
+                if (PF.capacity < PF_FD_MAX) {
+                    put_user(PF.capacity/(PF_FD_HDS*PF_FD_SPT),
+                                (short *) &geo->cylinders);
+                    put_user(PF_FD_HDS, (char *) &geo->heads);
+                    put_user(PF_FD_SPT, (char *) &geo->sectors);
+                } else {
+                    put_user(PF.capacity/(PF_HD_HDS*PF_HD_SPT), 
+				(short *) &geo->cylinders);
+                    put_user(PF_HD_HDS, (char *) &geo->heads);
+                    put_user(PF_HD_SPT, (char *) &geo->sectors);
+                }
+                put_user(0,(long *)&geo->start);
+                return 0;
+            case BLKRASET:
+                if(!suser()) return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                if(arg > 0xff) return -EINVAL;
+                read_ahead[MAJOR(inode->i_rdev)] = arg;
+                return 0;
+            case BLKRAGET:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+                return (0);
+            case BLKGETSIZE:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(PF.capacity,(long *) arg);
+                return (0);
+            case BLKFLSBUF:
+                if(!suser())  return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                fsync_dev(inode->i_rdev);
+                invalidate_buffers(inode->i_rdev);
+                return 0;
+            RO_IOCTLS(inode->i_rdev,arg);
+            default:
+                return -EINVAL;
+        }
+}
+
+
+static void pf_release (struct inode *inode, struct file *file)
+
+{       kdev_t devp;
+	int	unit;
+
+        devp = inode->i_rdev;
+        unit = DEVICE_NR(devp);
+
+        if ((unit >= PF_UNITS) || (PF.access <= 0)) 
+                return;
+
+	PF.access--;
+
+	if (!PF.access) {
+                fsync_dev(devp);
+
+		invalidate_inodes(devp);
+
+                invalidate_buffers(devp);
+		if (PF.removable) pf_lock(unit,0);
+        }
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pf_check_media( kdev_t dev)
+
+{       return 1;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err;
+
+        err = pf_init();
+
+        return err;
+}
+
+void    cleanup_module(void)
+
+{       int unit;
+
+        unregister_blkdev(MAJOR_NR,name);
+
+	for (unit=0;unit<PF_UNITS;unit++)
+	  if (PF.present) pi_release(PI);
+}
+
+#endif
+
+#define	WR(c,r,v)	pi_write_regr(PI,c,r,v)
+#define	RR(c,r)		(pi_read_regr(PI,c,r))
+
+#define LUN             (0x20*PF.lun)
+#define DRIVE           (0xa0+0x10*PF.drive)
+
+static int pf_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{       int j, r, e, s, p;
+
+        j = 0;
+        while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PF_SPIN))
+                udelay(PF_SPIN_DEL);
+
+        if ((r&(STAT_ERR&stop))||(j>=PF_SPIN)) {
+           s = RR(0,7);
+           e = RR(0,1);
+           p = RR(0,2);
+           if (j >= PF_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+                           " loop=%d phase=%d\n",
+                            PF.name,fun,msg,r,s,e,j,p);
+           return (e<<8)+s;
+        }
+        return 0;
+}
+
+static int pf_command( int unit, char * cmd, int dlen, char * fun )
+
+{       pi_connect(PI);
+
+        WR(0,6,DRIVE);
+
+        if (pf_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pf_wait(unit,STAT_BUSY,STAT_DRQ,fun,"command DRQ")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PF.name,fun);
+           pi_disconnect(PI);
+           return -1;
+        }
+
+        pi_write_block(PI,cmd,12);
+
+        return 0;
+}
+
+static int pf_completion( int unit, char * buf, char * fun )
+
+{       int r, s, n;
+
+        r = pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+			fun,"completion");
+
+        if ((RR(0,2)&2) && (RR(0,7)&STAT_DRQ)) { 
+                n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc);
+                pi_read_block(PI,buf,n);
+        }
+
+        s = pf_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+        pi_disconnect(PI); 
+
+        return (r?r:s);
+}
+
+static void pf_req_sense( int unit, int quiet )
+
+{       char    rs_cmd[12] = { ATAPI_REQ_SENSE,LUN,0,0,16,0,0,0,0,0,0,0 };
+        char    buf[16];
+        int     r;
+
+        r = pf_command(unit,rs_cmd,16,"Request sense");
+        udelay(1000);
+        if (!r) pf_completion(unit,buf,"Request sense");
+
+        if ((!r)&&(!quiet)) 
+                printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+                       PF.name,buf[2]&0xf,buf[12],buf[13]);
+}
+
+static int pf_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{       int r;
+
+        r = pf_command(unit,cmd,dlen,fun);
+        udelay(1000);
+        if (!r) r = pf_completion(unit,buf,fun);
+        if (r) pf_req_sense(unit,!fun);
+        
+        return r;
+}
+
+#define DBMSG(msg)      NULL
+
+static void pf_lock(int unit, int func)
+
+{	char	lo_cmd[12] = { ATAPI_LOCK,LUN,0,0,func,0,0,0,0,0,0,0 };
+
+        pf_atapi(unit,lo_cmd,0,pf_scratch,func?"unlock":"lock");
+}
+
+
+static void pf_eject( int unit )
+
+{	char	ej_cmd[12] = { ATAPI_DOOR,LUN,0,0,2,0,0,0,0,0,0,0 };
+
+	pf_lock(unit,0);
+	pf_atapi(unit,ej_cmd,0,pf_scratch,"eject");
+}
+
+#define PF_RESET_TMO   30              /* in tenths of a second */
+
+static void pf_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+
+static int pf_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+   after a reset, but the specification is ambiguous concerning the last
+   two bytes, and different drives interpret the standard differently.
+*/
+
+{	int	i, k, flg;
+	int	expect[5] = {1,1,1,0x14,0xeb};
+
+	pi_connect(PI);
+	WR(0,6,DRIVE);
+	WR(0,7,8);
+
+	pf_sleep(2);
+
+        k = 0;
+        while ((k++ < PF_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+                pf_sleep(10);
+
+	flg = 1;
+	for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+	if (verbose) {
+		printk("%s: Reset (%d) signature = ",PF.name,k);
+		for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+		if (!flg) printk(" (incorrect)");
+		printk("\n");
+	}
+	
+	pi_disconnect(PI);
+	return flg-1;	
+}
+
+static void pf_mode_sense( int unit )
+
+{       char    ms_cmd[12] = { ATAPI_MODE_SENSE,LUN,0,0,0,0,0,0,8,0,0,0};
+	char	buf[8];
+
+        pf_atapi(unit,ms_cmd,8,buf,DBMSG("mode sense"));
+	PF.media_status = PF_RW;
+	if (buf[3] & 0x80) PF.media_status = PF_RO;
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{	int	j,k,l;
+
+	j=0; l=0;
+	for (k=0;k<len;k++) 
+	   if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+		l=targ[j++]=buf[k+offs];
+	if (l==0x20) j--; targ[j]=0;
+}
+
+static int xl( char *buf, int offs )
+
+{	int	v,k;
+
+	v=0; 
+	for(k=0;k<4;k++) v=v*256+(buf[k+offs]&0xff);
+	return v;
+}
+
+static void pf_get_capacity( int unit )
+
+{	char    rc_cmd[12] = { ATAPI_CAPACITY,LUN,0,0,0,0,0,0,0,0,0,0};
+	char	buf[8];
+        int 	bs;
+
+	if (pf_atapi(unit,rc_cmd,8,buf,DBMSG("get capacity"))) {
+		PF.media_status = PF_NM;
+		return;
+	}
+	PF.capacity = xl(buf,0) + 1;  
+	bs = xl(buf,4);
+	if (bs != 512) {
+		PF.capacity = 0;
+		if (verbose) printk("%s: Drive %d, LUN %d,"
+		       		    " unsupported block size %d\n",
+			            PF.name,PF.drive,PF.lun,bs);
+		}
+}
+
+static int pf_identify( int unit )
+
+{	int 	dt, s;
+	char	*ms[2] = {"master","slave"};
+	char	mf[10], id[18];
+	char    id_cmd[12] = { ATAPI_IDENTIFY,LUN,0,0,36,0,0,0,0,0,0,0};
+	char	buf[36];
+
+        s = pf_atapi(unit,id_cmd,36,buf,"identify");
+	if (s) return -1;
+
+	dt = buf[0] & 0x1f;
+	if ((dt != 0) && (dt != 7)) {
+	  	if (verbose) 
+		   printk("%s: Drive %d, LUN %d, unsupported type %d\n",
+				PF.name,PF.drive,PF.lun,dt);
+	  	return -1;
+       	}
+
+	xs(buf,mf,8,8);
+	xs(buf,id,16,16);
+
+	PF.removable = (buf[1] & 0x80);
+
+	pf_mode_sense(unit);
+	pf_mode_sense(unit);
+	pf_mode_sense(unit);
+
+	pf_get_capacity(unit);
+
+        printk("%s: %s %s, %s LUN %d, type %d",
+		PF.name,mf,id,ms[PF.drive],PF.lun,dt);
+        if (PF.removable) printk(", removable");
+        if (PF.media_status == PF_NM) 
+                printk(", no media\n");
+        else {  if (PF.media_status == PF_RO) printk(", RO");
+                printk(", %d blocks\n",PF.capacity);
+        }
+
+	return 0;
+}
+
+static int pf_probe( int unit )
+
+/*	returns  0, with id set if drive is detected
+	        -1, if drive detection failed
+*/
+
+{	if (PF.drive == -1) {
+	   for (PF.drive=0;PF.drive<=1;PF.drive++)
+		if (!pf_reset(unit)) {
+		   if (PF.lun != -1) return pf_identify(unit);
+		   else for (PF.lun=0;PF.lun<8;PF.lun++) 
+                           if (!pf_identify(unit)) return 0;
+		}
+	} else {
+	   if (pf_reset(unit)) return -1;
+	   if (PF.lun != -1) return pf_identify(unit);
+	   for (PF.lun=0;PF.lun<8;PF.lun++) 
+	      if (!pf_identify(unit)) return 0;
+	}
+        return -1; 
+}
+
+static int pf_detect( void )
+
+{	int	k, unit;
+
+	printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+		name,name,PF_VERSION,major,cluster,nice);
+
+	k = 0;
+	if (pf_drive_count == 0) {
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pf_scratch,
+                        PI_PF,verbose,PF.name)) {
+	        if (!pf_probe(unit)) {
+			PF.present = 1;
+			k++;
+	        } else pi_release(PI);
+	    }
+
+	} else for (unit=0;unit<PF_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+			DU[D_PRO],DU[D_DLY],pf_scratch,PI_PF,verbose,
+			PF.name)) { 
+                if (!pf_probe(unit)) {
+                        PF.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+	if (k) return 0;
+
+	printk("%s: No ATAPI disk detected\n",name);
+	return -1;
+}
+
+/* The i/o request engine */
+
+static int pf_start( int unit, int cmd, int b, int c )
+
+{	int	i;
+	char	io_cmd[12] = {cmd,LUN,0,0,0,0,0,0,0,0,0,0};
+
+	for(i=0;i<4;i++) { 
+	   io_cmd[5-i] = b & 0xff;
+	   b = b >> 8;
+	}
+	
+	io_cmd[8] = c & 0xff;
+	io_cmd[7] = (c >> 8) & 0xff;
+
+	i = pf_command(unit,io_cmd,c*512,"start i/o");
+
+        udelay(1000);
+
+	return i;	
+}
+
+static int pf_ready( void )
+
+{	int	unit = pf_unit;
+
+	return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask));
+}
+
+static void do_pf_request (void)
+
+{       struct buffer_head * bh;
+	struct request * req;
+	int unit;
+
+        if (pf_busy) return;
+repeat:
+        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        INIT_REQUEST;
+
+        pf_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+        pf_block = CURRENT->sector;
+        pf_count = CURRENT->nr_sectors;
+
+	bh = CURRENT->bh;
+	req = CURRENT;
+	if (bh->b_reqnext)
+		printk("%s: OUCH: b_reqnext != NULL\n",PF.name);
+
+        if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) {
+                end_request(0);
+                goto repeat;
+        }
+
+	pf_cmd = CURRENT->cmd;
+	pf_run = pf_count;
+        while ((pf_run <= cluster) &&
+	       (req = req->next) && 
+	       (pf_block+pf_run == req->sector) &&
+	       (pf_cmd == req->cmd) &&
+	       (pf_unit == DEVICE_NR(req->rq_dev)))
+			pf_run += req->nr_sectors;
+
+        pf_buf = CURRENT->buffer;
+        pf_retries = 0;
+
+	pf_busy = 1;
+        if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read);
+        else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write);
+        else {  pf_busy = 0;
+		end_request(0);
+                goto repeat;
+        }
+}
+
+static void pf_next_buf( int unit )
+
+{	long	saved_flags;
+
+	spin_lock_irqsave(&io_request_lock,saved_flags);
+	end_request(1);
+	if (!pf_run) { spin_unlock_irqrestore(&io_request_lock,saved_flags);
+		       return; 
+	}
+	
+/* paranoia */
+
+	if ((!CURRENT) ||
+	    (CURRENT->cmd != pf_cmd) ||
+	    (DEVICE_NR(CURRENT->rq_dev) != pf_unit) ||
+	    (CURRENT->rq_status == RQ_INACTIVE) ||
+	    (CURRENT->sector != pf_block)) 
+		printk("%s: OUCH: request list changed unexpectedly\n",
+			PF.name);
+
+	pf_count = CURRENT->nr_sectors;
+	pf_buf = CURRENT->buffer;
+	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pf_read( void )
+
+/* detach from the calling context - in case the spinlock is held */
+
+{	ps_set_intr(do_pf_read_start,0,0,nice);
+}
+
+static void do_pf_read_start( void )
+
+{       int	unit = pf_unit;
+	long	saved_flags;
+
+	pf_busy = 1;
+
+	if (pf_start(unit,ATAPI_READ_10,pf_block,pf_run)) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_read_start);
+			return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+	pf_mask = STAT_DRQ;
+        ps_set_intr(do_pf_read_drq,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_read_drq( void )
+
+{       int	unit = pf_unit;
+	long	saved_flags;
+	
+	while (1) {
+            if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+			"read block","completion") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+			pf_req_sense(unit,0);
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_read_start);
+                        return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+            }
+            pi_read_block(PI,pf_buf,512);
+            pf_count--; pf_run--;
+            pf_buf += 512;
+	    pf_block++;
+	    if (!pf_run) break;
+	    if (!pf_count) pf_next_buf(unit);
+        }
+        pi_disconnect(PI);
+	spin_lock_irqsave(&io_request_lock,saved_flags); 
+        end_request(1);
+        pf_busy = 0;
+        do_pf_request();
+	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+static void do_pf_write( void )
+
+{	ps_set_intr(do_pf_write_start,0,0,nice);
+}
+
+static void do_pf_write_start( void )
+
+{       int	unit = pf_unit;
+	long	saved_flags;
+
+	pf_busy = 1;
+
+	if (pf_start(unit,ATAPI_WRITE_10,pf_block,pf_run)) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_write_start);
+			return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+
+	while (1) {
+            if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+			"write block","data wait") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_write_start);
+                        return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+            }
+            pi_write_block(PI,pf_buf,512);
+	    pf_count--; pf_run--;
+	    pf_buf += 512;
+	    pf_block++;
+	    if (!pf_run) break;
+	    if (!pf_count) pf_next_buf(unit);
+	}
+	pf_mask = 0;
+        ps_set_intr(do_pf_write_done,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_write_done( void )
+
+{       int	unit = pf_unit;
+	long	saved_flags;
+
+        if (pf_wait(unit,STAT_BUSY,0,"write block","done") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+			pi_do_claimed(PI,do_pf_write_start);
+                        return;
+                }
+		spin_lock_irqsave(&io_request_lock,saved_flags);
+                end_request(0);
+                pf_busy = 0;
+                do_pf_request();
+		spin_unlock_irqrestore(&io_request_lock,saved_flags);
+                return;
+        }
+        pi_disconnect(PI);
+	spin_lock_irqsave(&io_request_lock,saved_flags);
+        end_request(1);
+        pf_busy = 0;
+        do_pf_request();
+	spin_unlock_irqrestore(&io_request_lock,saved_flags);
+}
+
+/* end of pf.c */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c
--- v2.0.34/linux/drivers/block/paride/pg.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pg.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,682 @@
+/* 
+        pg.c    (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+
+	Special 2.0.35 version
+
+
+	The pg driver provides a simple character device interface for
+        sending ATAPI commands to a device.  With the exception of the
+	ATAPI reset operation, all operations are performed by a pair
+        of read and write operations to the appropriate /dev/pgN device.
+	A write operation delivers a command and any outbound data in
+        a single buffer.  Normally, the write will succeed unless the
+        device is offline or malfunctioning, or there is already another
+	command pending.  If the write succeeds, it should be followed
+        immediately by a read operation, to obtain any returned data and
+        status information.  A read will fail if there is no operation
+        in progress.
+
+	As a special case, the device can be reset with a write operation,
+        and in this case, no following read is expected, or permitted.
+
+	There are no ioctl() operations.  Any single operation
+	may transfer at most PG_MAX_DATA bytes.  Note that the driver must
+        copy the data through an internal buffer.  In keeping with all
+	current ATAPI devices, command packets are assumed to be exactly
+	12 bytes in length.
+
+	To permit future changes to this interface, the headers in the
+	read and write buffers contain a single character "magic" flag.
+        Currently this flag must be the character "P".
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI device, but if their individual parameters are
+        specified, the driver can handle up to 4 devices.
+
+	To use this device, you must have the following device 
+	special files defined:
+
+		/dev/pg0 b 97 0
+		/dev/pg1 b 97 1
+		/dev/pg2 b 97 2
+		/dev/pg3 b 97 3
+
+	(You'll need to change the 97 to something else if you use
+	the 'major' parameter to install the driver on a different
+        major number.)
+
+        The behaviour of the pg driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-6 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+                <slv>   ATAPI devices can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+
+	    major	You may use this parameter to overide the
+			default major number (97) that this driver
+			will use.  Be sure to change the device
+			name as well.
+
+	    name	This parameter is a character string that
+			contains the name the kernel will use for this
+			device (in /proc output, for instance).
+			(default "pg").
+
+            verbose     This parameter controls the amount of logging
+                        that is done by the driver.  Set it to 0 for 
+			quiet operation, to 1 to enable progress
+			messages while the driver probes for devices,
+			or to 2 for full debug logging.  (default 0)
+
+        If this driver is built into the kernel, you can use 
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pg.drive0
+            pg.drive1
+            pg.drive2
+            pg.drive3
+
+        In addition, you can use the parameter pg.disable to disable
+        the driver entirely.
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.06.16	Bug fixes
+*/
+
+#define PG_VERSION      "1.01s"
+#define PG_MAJOR	97
+#define PG_NAME		"pg"
+#define PG_UNITS	4
+
+#ifndef PI_PG
+#define PI_PG	4
+#endif
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is 0
+   by default.
+
+*/
+
+static int	verbose = 0;
+static int	major = PG_MAJOR;
+static char	*name = PG_NAME;
+static int      disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pg_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_DLY   5
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/mtio.h>
+#include <linux/pg.h>
+
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pg_stt[5] = {{"drive0",6,drive0},
+                        {"drive1",6,drive1},
+                        {"drive2",6,drive2},
+                        {"drive3",6,drive3},
+			{"disable",1,&disable}};
+
+void pg_setup( char *str, int *ints)
+
+{       generic_setup(pg_stt,5,str);
+}
+
+#endif
+
+#include "paride.h"
+
+#define PG_SPIN_DEL     50              /* spin delay in micro-seconds  */
+#define PG_SPIN         200
+#define PG_TMO		HZ
+#define PG_CMD_TMO	3*HZ
+#define PG_RESET_TMO	10*HZ
+
+#define STAT_ERR        0x01
+#define STAT_INDEX      0x02
+#define STAT_ECC        0x04
+#define STAT_DRQ        0x08
+#define STAT_SEEK       0x10
+#define STAT_WRERR      0x20
+#define STAT_READY      0x40
+#define STAT_BUSY       0x80
+
+#define ATAPI_IDENTIFY		0x12
+
+int pg_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+
+static int pg_open(struct inode *inode, struct file *file);
+static void pg_release (struct inode *inode, struct file *file);
+static int pg_read(struct inode *inode, struct file *filp, char *buf, int count);
+static int pg_write(struct inode *inode, struct file *filp, 
+			const char *buf, int count);
+static int pg_detect(void);
+
+static int pg_identify (int unit, int log);
+
+#define PG_NAMELEN      8
+
+struct pg_unit {
+	struct pi_adapter pia;    /* interface to paride layer */
+	struct pi_adapter *pi;
+	int busy;        	  /* write done, read expected */
+	int start;		  /* jiffies at command start */
+	int dlen;		  /* transfer size requested */
+	int timeout;		  /* timeout requested */
+	int status;		  /* last sense key */
+	int drive;		  /* drive */
+	int access;               /* count of active opens ... */
+	int present;		  /* device present ? */
+	char *bufptr;
+	char name[PG_NAMELEN];	  /* pg0, pg1, ... */
+	};
+
+struct pg_unit pg[PG_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PG pg[unit]
+#define PI PG.pi
+
+static char pg_scratch[512];            /* scratch block buffer */
+
+/* kernel glue structures */
+
+static struct file_operations pg_fops = {
+        NULL,                   /* lseek - default */
+        pg_read,                /* read */
+        pg_write,               /* write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        NULL,                   /* ioctl */
+        NULL,                   /* mmap */
+        pg_open,                /* open */
+        pg_release,             /* release */
+        NULL,                   /* fsync */
+        NULL,                   /* fasync */
+        NULL,                   /* media change ? */
+        NULL                    /* revalidate new media */
+};
+
+void pg_init_units( void )
+
+{       int     unit, j;
+
+        pg_drive_count = 0;
+        for (unit=0;unit<PG_UNITS;unit++) {
+                PG.pi = & PG.pia;
+                PG.access = 0;
+                PG.busy = 0;
+                PG.present = 0;
+		PG.bufptr = NULL;
+		PG.drive = DU[D_SLV];
+                j = 0;
+                while ((j < PG_NAMELEN-2) && (PG.name[j]=name[j])) j++;
+                PG.name[j++] = '0' + unit;
+                PG.name[j] = 0;
+                if (DU[D_PRT]) pg_drive_count++;
+        }
+} 
+
+int pg_init (void)      /* preliminary initialisation */
+
+{       int unit;
+
+	if (disable) return -1;
+
+	pg_init_units();
+
+	if (pg_detect()) return -1;
+
+        if (register_chrdev(major,name,&pg_fops)) {
+                printk("pg_init: unable to get major number %d\n",
+                        major);
+	        for (unit=0;unit<PG_UNITS;unit++)
+        	  if (PG.present) pi_release(PI);
+                return -1;
+        }
+
+        return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err;
+
+        err = pg_init();
+
+        return err;
+}
+
+void    cleanup_module(void)
+
+{       int unit;
+
+        unregister_chrdev(major,name);
+
+	for (unit=0;unit<PG_UNITS;unit++)
+	  if (PG.present) pi_release(PI);
+}
+
+#endif
+
+#define	WR(c,r,v)	pi_write_regr(PI,c,r,v)
+#define	RR(c,r)		(pi_read_regr(PI,c,r))
+
+#define DRIVE           (0xa0+0x10*PG.drive)
+
+static void pg_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+static int pg_wait( int unit, int go, int stop, int tmo, char * msg )
+
+{       int j, r, e, s, p;
+
+	PG.status = 0;
+
+        j = 0;
+        while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(jiffies<tmo)) {
+                if (j++ < PG_SPIN) udelay(PG_SPIN_DEL);
+		else pg_sleep(1);
+	}
+
+        if ((r&(STAT_ERR&stop))||(jiffies>=tmo)) {
+           s = RR(0,7);
+           e = RR(0,1);
+           p = RR(0,2);
+           if (verbose > 1)
+	     printk("%s: %s: stat=0x%x err=0x%x phase=%d%s\n",
+                   PG.name,msg,s,e,p,(jiffies>=tmo)?" timeout":"");
+
+
+           if (jiffies>=tmo) e |= 0x100;
+	   PG.status = (e >> 4) & 0xff;
+           return -1;
+        }
+        return 0;
+}
+
+static int pg_command( int unit, char * cmd, int dlen, int tmo )
+
+{       int k;
+
+	pi_connect(PI);
+
+        WR(0,6,DRIVE);
+
+        if (pg_wait(unit,STAT_BUSY|STAT_DRQ,0,tmo,"before command")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pg_wait(unit,STAT_BUSY,STAT_DRQ,tmo,"command DRQ")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        if (RR(0,2) != 1) {
+           printk("%s: command phase error\n",PG.name);
+           pi_disconnect(PI);
+           return -1;
+        }
+
+        pi_write_block(PI,cmd,12);
+
+	if (verbose > 1) {
+		printk("%s: Command sent, dlen=%d packet= ", PG.name,dlen);
+		for (k=0;k<12;k++) printk("%02x ",cmd[k]&0xff);
+		printk("\n");
+	}
+        return 0;
+}
+
+static int pg_completion( int unit, char * buf, int tmo)
+
+{       int r, d, n, p;
+
+        r = pg_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+                        tmo,"completion");
+
+        PG.dlen = 0;
+
+        while (RR(0,7)&STAT_DRQ) {
+           d = (RR(0,4)+256*RR(0,5));
+           n = ((d+3)&0xfffc);
+           p = RR(0,2)&3;
+           if (p == 0) pi_write_block(PI,buf,n);
+           if (p == 2) pi_read_block(PI,buf,n);
+           if (verbose > 1) printk("%s: %s %d bytes\n",PG.name,
+                                    p?"Read":"Write",n);
+           PG.dlen += (1-p)*d;
+           buf += d;
+           r = pg_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+                        tmo,"completion");
+        }
+
+        pi_disconnect(PI);
+
+        return r;
+}
+
+static int pg_reset( int unit )
+
+{	int	i, k, flg;
+	int	expect[5] = {1,1,1,0x14,0xeb};
+
+	pi_connect(PI);
+	WR(0,6,DRIVE);
+	WR(0,7,8);
+
+	pg_sleep(2);
+
+        k = 0;
+        while ((k++ < PG_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+                pg_sleep(1);
+
+	flg = 1;
+	for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+	if (verbose) {
+		printk("%s: Reset (%d) signature = ",PG.name,k);
+		for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+		if (!flg) printk(" (incorrect)");
+		printk("\n");
+	}
+	
+	pi_disconnect(PI);
+	return flg-1;	
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{	int	j,k,l;
+
+	j=0; l=0;
+	for (k=0;k<len;k++) 
+	   if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+		l=targ[j++]=buf[k+offs];
+	if (l==0x20) j--; targ[j]=0;
+}
+
+static int pg_identify( int unit, int log )
+
+{	int 	s;
+	char	*ms[2] = {"master","slave"};
+	char	mf[10], id[18];
+	char    id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0};
+	char	buf[36];
+
+        s = pg_command(unit,id_cmd,36,jiffies+PG_TMO);
+	if (s) return -1;
+	s = pg_completion(unit,buf,jiffies+PG_TMO);
+	if (s) return -1;
+
+	if (log) {
+		xs(buf,mf,8,8);
+		xs(buf,id,16,16);
+        	printk("%s: %s %s, %s\n",PG.name,mf,id,ms[PG.drive]);
+	}
+
+	return 0;
+}
+
+static int pg_probe( int unit )
+
+/*	returns  0, with id set if drive is detected
+	        -1, if drive detection failed
+*/
+
+{	if (PG.drive == -1) {
+	   for (PG.drive=0;PG.drive<=1;PG.drive++)
+		if (!pg_reset(unit)) return pg_identify(unit,1);
+	} else {
+	   if (!pg_reset(unit)) return pg_identify(unit,1);
+	}
+        return -1; 
+}
+
+static int pg_detect( void )
+
+{	int	k, unit;
+
+	printk("%s: %s version %s, major %d\n",
+		name,name,PG_VERSION,major);
+
+	k = 0;
+	if (pg_drive_count == 0) {
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pg_scratch,
+                        PI_PG,verbose,PG.name)) {
+	        if (!pg_probe(unit)) {
+			PG.present = 1;
+			k++;
+	        } else pi_release(PI);
+	    }
+
+	} else for (unit=0;unit<PG_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+			DU[D_PRO],DU[D_DLY],pg_scratch,PI_PG,verbose,
+			PG.name)) { 
+                if (!pg_probe(unit)) {
+                        PG.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+	if (k) return 0;
+
+	printk("%s: No ATAPI device detected\n",name);
+	return -1;
+}
+
+#define DEVICE_NR(dev)	(MINOR(dev) % 128)
+
+static int pg_open (struct inode *inode, struct file *file)
+
+{       int	unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PG_UNITS) || (!PG.present)) return -ENODEV;
+
+        PG.access++;
+
+	if (PG.access > 1) {
+		PG.access--;
+		return -EBUSY;
+	}
+
+        MOD_INC_USE_COUNT;
+
+	if (PG.busy) {
+		pg_reset(unit);
+		PG.busy = 0;
+	}
+
+	pg_identify(unit,(verbose>1));
+
+
+	PG.bufptr = kmalloc(PG_MAX_DATA,GFP_KERNEL);
+	if (PG.bufptr == NULL) {
+		PG.access--;
+		MOD_DEC_USE_COUNT;
+		printk("%s: buffer allocation failed\n",PG.name);
+		return -ENOMEM;
+	}
+
+        return 0;
+}
+
+static void pg_release (struct inode *inode, struct file *file)
+{
+        int	unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PG_UNITS) || (PG.access <= 0)) 
+                return;
+
+	PG.access--;
+
+	kfree(PG.bufptr);
+	PG.bufptr = NULL;
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pg_write(struct inode *inode, struct file *filp, 
+		const char *buf, int count)
+
+{       int                     unit = DEVICE_NR(inode->i_rdev);
+        struct pg_write_hdr     hdr;
+        int                     hs = sizeof(hdr);
+
+	if (PG.busy) return -EBUSY;
+	if (count < hs) return -EINVAL;
+	
+	memcpy_fromfs((char *)&hdr,buf,hs);
+
+	if (hdr.magic != PG_MAGIC) return -EINVAL;
+	if (hdr.dlen > PG_MAX_DATA) return -EINVAL;
+	if ((count - hs) > PG_MAX_DATA) return -EINVAL;
+
+	if (hdr.func == PG_RESET) {
+		if (count != hs) return -EINVAL;
+		if (pg_reset(unit)) return -EIO;
+		return count;
+	}
+
+	if (hdr.func != PG_COMMAND) return -EINVAL;
+
+	PG.start = jiffies;
+	PG.timeout = hdr.timeout*HZ + HZ/2 + jiffies;
+
+	if (pg_command(unit,hdr.packet,hdr.dlen,jiffies+PG_CMD_TMO)) {
+		if (PG.status & 0x10) return -ETIME;
+		return -EIO;
+	}
+
+	PG.busy = 1;
+
+	memcpy_fromfs(PG.bufptr,buf+hs,count-hs);
+
+	return count;
+}
+
+static int pg_read(struct inode *inode, struct file *filp, char *buf, int count)
+
+{  	int			unit = DEVICE_NR(inode->i_rdev); 
+	struct pg_read_hdr 	hdr;
+	int			hs = sizeof(hdr);
+	int			copy;
+
+	if (!PG.busy) return -EINVAL;
+	if (count < hs) return -EINVAL;
+
+	PG.busy = 0;
+
+	if (pg_completion(unit,PG.bufptr,PG.timeout))
+	  if (PG.status & 0x10) return -ETIME;
+
+	hdr.magic = PG_MAGIC;
+	hdr.dlen = PG.dlen;
+	copy = 0;
+
+	if (hdr.dlen < 0) {
+        	hdr.dlen = -1 * hdr.dlen;
+		copy = hdr.dlen;
+		if (copy > (count - hs)) copy = count - hs;
+	}
+
+	hdr.duration = (jiffies - PG.start + HZ/2) / HZ;
+	hdr.scsi = PG.status & 0x0f;
+
+	memcpy_tofs(buf,(char *)&hdr,hs);
+	if (copy > 0) memcpy_tofs(buf+hs,PG.bufptr,copy);
+	
+	return copy+hs;
+}
+
+/* end of pg.c */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pseudo.h linux/drivers/block/paride/pseudo.h
--- v2.0.34/linux/drivers/block/paride/pseudo.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pseudo.h	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,145 @@
+/* 
+        pseudo.h    (c) 1997-8  Grant R. Guenther <grant@torque.net>
+                                Under the terms of the GNU public license.
+
+	This is the "pseudo-interrupt" logic for parallel port drivers.
+
+        This module is #included into each driver.  It makes one
+        function available:
+
+		ps_set_intr( void (*continuation)(void),
+			     int  (*ready)(void),
+			     int timeout,
+			     int nice )
+
+	Which will arrange for ready() to be evaluated frequently and
+	when either it returns true, or timeout jiffies have passed,
+	continuation() will be invoked.
+
+	If nice is true, the test will done approximately once a
+	jiffy.  If nice is 0, the test will also be done whenever
+	the scheduler runs (by adding it to a task queue).
+
+*/
+
+/* Changes:
+
+	1.01	1998.05.03	Switched from cli()/sti() to spinlocks
+
+*/
+	
+#define PS_VERSION	"1.01"
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+
+static void ps_timer_int( unsigned long data);
+static void ps_tq_int( void *data);
+
+static int ps_use_tq = 1;
+static void (* ps_continuation)(void);
+static int (* ps_ready)(void);
+static int ps_then;
+static int ps_timeout;
+static int ps_timer_active = 0;
+static int ps_tq_active = 0;
+
+/* static spinlock_t ps_spinlock = SPIN_LOCK_UNLOCKED; */
+
+static struct timer_list ps_timer = {0,0,0,0,ps_timer_int};
+static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL};
+
+static void ps_set_intr( void (*continuation)(void), 
+			 int (*ready)(void),
+			 int timeout, int nice )
+
+{       long	flags;
+
+	spin_lock_irqsave(&ps_spinlock,flags);
+
+	ps_continuation = continuation;
+	ps_ready = ready;
+        ps_then = jiffies;
+	ps_timeout = jiffies + timeout;
+	ps_use_tq = !nice;
+
+        if (ps_use_tq && !ps_tq_active) {
+#ifdef HAVE_DISABLE_HLT
+                disable_hlt();
+#endif
+		ps_tq_active = 1;
+                queue_task(&ps_tq,&tq_scheduler);
+	}
+
+        if (!ps_timer_active) {
+		ps_timer_active = 1;
+                ps_timer.expires = jiffies;
+                add_timer(&ps_timer);
+        }
+
+	spin_unlock_irqrestore(&ps_spinlock,flags);
+}
+
+static void ps_tq_int( void *data )
+
+{       void (*con)(void);
+	long flags;
+
+	spin_lock_irqsave(&ps_spinlock,flags);
+
+        con = ps_continuation;
+
+#ifdef HAVE_DISABLE_HLT
+        enable_hlt();
+#endif
+
+        ps_tq_active = 0;
+
+        if (!con) {
+		spin_unlock_irqrestore(&ps_spinlock,flags);
+		return;
+	}
+        if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) {
+                ps_continuation = NULL;
+        	spin_unlock_irqrestore(&ps_spinlock,flags);
+                con();
+                return;
+                }
+
+#ifdef HAVE_DISABLE_HLT
+        disable_hlt();
+#endif
+
+        ps_tq_active = 1;
+	queue_task(&ps_tq,&tq_scheduler);
+        spin_unlock_irqrestore(&ps_spinlock,flags);
+}
+
+static void ps_timer_int( unsigned long data)
+
+{       void (*con)(void);
+	long	flags;
+
+	spin_lock_irqsave(&ps_spinlock,flags);
+
+	con = ps_continuation;
+	ps_timer_active = 0;
+	if (!con) {
+	        spin_unlock_irqrestore(&ps_spinlock,flags);
+		return;
+	}
+        if (!ps_ready || ps_ready() || (jiffies >= ps_timeout)) {
+                ps_continuation = NULL;
+	        spin_unlock_irqrestore(&ps_spinlock,flags);
+                con();
+		return;
+		}
+	ps_timer_active = 1;
+        ps_timer.expires = jiffies;
+        add_timer(&ps_timer);
+        spin_unlock_irqrestore(&ps_spinlock,flags);
+}
+
+/* end of pseudo.h */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c
--- v2.0.34/linux/drivers/block/paride/pt.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pt.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,940 @@
+/* 
+        pt.c    (c) 1998  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+	Special 2.0.35 version
+
+        This is the high-level driver for parallel port ATAPI tape
+        drives based on chips supported by the paride module.
+
+	The driver implements both rewinding and non-rewinding
+	devices, filemarks, and the rewind ioctl.  It allocates
+	a small internal "bounce buffer" for each open device, but
+        otherwise expects buffering and blocking to be done at the
+        user level.  As with most block-structured tapes, short
+	writes are padded to full tape blocks, so reading back a file
+        may return more data than was actually written.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI tape drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+	The rewinding devices are named /dev/pt0, /dev/pt1, ...
+	while the non-rewinding devices are /dev/npt0, /dev/npt1, etc.
+
+        The behaviour of the pt driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-6 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+                <slv>   ATAPI devices can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+
+	    major	You may use this parameter to overide the
+			default major number (96) that this driver
+			will use.  Be sure to change the device
+			name as well.
+
+	    name	This parameter is a character string that
+			contains the name the kernel will use for this
+			device (in /proc output, for instance).
+			(default "pt").
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+        If this driver is built into the kernel, you can use 
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pt.drive0
+            pt.drive1
+            pt.drive2
+            pt.drive3
+
+        In addition, you can use the parameter pt.disable to disable
+        the driver entirely.
+
+*/
+
+/*   Changes:
+
+	1.01	GRG 1998.05.06	Round up transfer size, fix ready_wait,
+			        loosed interpretation of ATAPI standard
+				for clearing error status.
+				Eliminate sti();
+	1.02    GRG 1998.06.16  Eliminate an Ugh.
+
+*/
+
+#define PT_VERSION      "1.02s"
+#define PT_MAJOR	96
+#define PT_NAME		"pt"
+#define PT_UNITS	4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is on
+   by default.
+
+*/
+
+static int	verbose = 0;
+static int	major = PT_MAJOR;
+static char	*name = PT_NAME;
+static int      disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pt_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_DLY   5
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/mtio.h>
+
+#include <asm/segment.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pt_stt[5] = {{"drive0",6,drive0},
+                        {"drive1",6,drive1},
+                        {"drive2",6,drive2},
+                        {"drive3",6,drive3},
+			{"disable",1,&disable}};
+
+void pt_setup( char *str, int *ints)
+
+{       generic_setup(pt_stt,5,str);
+}
+
+#endif
+
+#include "paride.h"
+
+#define PT_MAX_RETRIES  5
+#define PT_TMO          800             /* interrupt timeout in jiffies */
+#define PT_SPIN_DEL     50              /* spin delay in micro-seconds  */
+#define PT_RESET_TMO    30		/* 3 seconds */
+#define PT_READY_TMO	60		/* 60 seconds */
+#define PT_REWIND_TMO	1200		/* 20 minutes */
+
+#define PT_SPIN         (10000/PT_SPIN_DEL)*PT_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+#define STAT_SENSE	0x1f000
+
+#define ATAPI_TEST_READY	0x00
+#define ATAPI_REWIND		0x01
+#define ATAPI_REQ_SENSE		0x03
+#define ATAPI_READ_6		0x08
+#define ATAPI_WRITE_6		0x0a
+#define ATAPI_WFM		0x10
+#define ATAPI_IDENTIFY		0x12
+#define ATAPI_MODE_SENSE	0x1a
+#define ATAPI_LOG_SENSE		0x4d
+
+int pt_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+
+static int pt_open(struct inode *inode, struct file *file);
+static int pt_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+static void pt_release (struct inode *inode, struct file *file);
+static int pt_read(struct inode *inode, struct file *filp, char *buf, int count);
+static int pt_write(struct inode *inode, struct file *filp, 
+		const char *buf, int count);
+static int pt_detect(void);
+
+static int pt_identify (int unit);
+
+/* bits in PT.flags */
+
+#define PT_MEDIA	1
+#define PT_WRITE_OK	2
+#define PT_REWIND	4
+#define PT_WRITING      8
+#define PT_READING     16
+#define PT_EOF	       32
+
+#define PT_NAMELEN      8
+#define PT_BUFSIZE  16384
+
+struct pt_unit {
+	struct pi_adapter pia;    /* interface to paride layer */
+	struct pi_adapter *pi;
+	int flags;        	  /* various state flags */
+	int last_sense;		  /* result of last request sense */
+	int drive;		  /* drive */
+	int access;               /* count of active opens ... */
+	int bs;			  /* block size */
+	int capacity;             /* Size of tape in KB */
+	int present;		  /* device present ? */
+	char *bufptr;
+	char name[PT_NAMELEN];	  /* pf0, pf1, ... */
+	};
+
+struct pt_unit pt[PT_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PT pt[unit]
+#define PI PT.pi
+
+static char pt_scratch[512];            /* scratch block buffer */
+
+/* kernel glue structures */
+
+static struct file_operations pt_fops = {
+        NULL,                   /* lseek - default */
+        pt_read,                /* read */
+        pt_write,               /* write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pt_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pt_open,                /* open */
+        pt_release,             /* release */
+        NULL,                   /* fsync */
+        NULL,                   /* fasync */
+        NULL,                   /* media change ? */
+        NULL                    /* revalidate new media */
+};
+
+void pt_init_units( void )
+
+{       int     unit, j;
+
+        pt_drive_count = 0;
+        for (unit=0;unit<PT_UNITS;unit++) {
+                PT.pi = & PT.pia;
+                PT.access = 0;
+                PT.flags = 0;
+		PT.last_sense = 0;
+                PT.present = 0;
+		PT.bufptr = NULL;
+		PT.drive = DU[D_SLV];
+                j = 0;
+                while ((j < PT_NAMELEN-2) && (PT.name[j]=name[j])) j++;
+                PT.name[j++] = '0' + unit;
+                PT.name[j] = 0;
+                if (DU[D_PRT]) pt_drive_count++;
+        }
+} 
+
+int pt_init (void)      /* preliminary initialisation */
+
+{       int unit;
+
+	if (disable) return -1;
+
+	pt_init_units();
+
+	if (pt_detect()) return -1;
+
+        if (register_chrdev(major,name,&pt_fops)) {
+                printk("pt_init: unable to get major number %d\n",
+                        major);
+	        for (unit=0;unit<PT_UNITS;unit++)
+        	  if (PT.present) pi_release(PI);
+                return -1;
+        }
+
+        return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err;
+
+        err = pt_init();
+
+        return err;
+}
+
+void    cleanup_module(void)
+
+{       int unit;
+
+        unregister_chrdev(major,name);
+
+	for (unit=0;unit<PT_UNITS;unit++)
+	  if (PT.present) pi_release(PI);
+}
+
+#endif
+
+#define	WR(c,r,v)	pi_write_regr(PI,c,r,v)
+#define	RR(c,r)		(pi_read_regr(PI,c,r))
+
+#define DRIVE           (0xa0+0x10*PT.drive)
+
+static int pt_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{       int j, r, e, s, p;
+
+        j = 0;
+        while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PT_SPIN))
+                udelay(PT_SPIN_DEL);
+
+        if ((r&(STAT_ERR&stop))||(j>=PT_SPIN)) {
+           s = RR(0,7);
+           e = RR(0,1);
+           p = RR(0,2);
+           if (j >= PT_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+                           " loop=%d phase=%d\n",
+                            PT.name,fun,msg,r,s,e,j,p);
+           return (e<<8)+s;
+        }
+        return 0;
+}
+
+static int pt_command( int unit, char * cmd, int dlen, char * fun )
+
+{       pi_connect(PI);
+
+        WR(0,6,DRIVE);
+
+        if (pt_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pt_wait(unit,STAT_BUSY,STAT_DRQ,fun,"command DRQ")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PT.name,fun);
+           pi_disconnect(PI);
+           return -1;
+        }
+
+        pi_write_block(PI,cmd,12);
+
+        return 0;
+}
+
+static int pt_completion( int unit, char * buf, char * fun )
+
+{       int r, s, n, p;
+
+        r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+			fun,"completion");
+
+        if (RR(0,7)&STAT_DRQ) { 
+           n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc);
+	   p = RR(0,2)&3;
+	   if (p == 0) pi_write_block(PI,buf,n);
+	   if (p == 2) pi_read_block(PI,buf,n);
+        }
+
+        s = pt_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+        pi_disconnect(PI); 
+
+        return (r?r:s);
+}
+
+static void pt_req_sense( int unit, int quiet )
+
+{       char    rs_cmd[12] = { ATAPI_REQ_SENSE,0,0,0,16,0,0,0,0,0,0,0 };
+        char    buf[16];
+        int     r;
+
+        r = pt_command(unit,rs_cmd,16,"Request sense");
+        udelay(1000);
+        if (!r) pt_completion(unit,buf,"Request sense");
+
+	PT.last_sense = -1;
+        if (!r) {
+	    if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+                                    PT.name,buf[2]&0xf,buf[12],buf[13]);
+	    PT.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+					 | ((buf[13]&0xff)<<16) ;
+	} 
+}
+
+static int pt_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{       int r;
+
+        r = pt_command(unit,cmd,dlen,fun);
+        udelay(1000);
+        if (!r) r = pt_completion(unit,buf,fun);
+        if (r) pt_req_sense(unit,!fun);
+        
+        return r;
+}
+
+static void pt_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+static int pt_poll_dsc( int unit, int pause, int tmo, char *msg )
+
+{	int	k, e, s;
+
+	k = 0;
+	while (k < tmo) {
+		pt_sleep(pause);
+		k++;
+		pi_connect(PI);
+		WR(0,6,DRIVE);
+		s = RR(0,7);
+		e = RR(0,1);
+		pi_disconnect(PI);
+		if (s & (STAT_ERR|STAT_SEEK)) break;
+	}
+	if ((k >= tmo) || (s & STAT_ERR)) {
+	   if (k >= tmo) printk("%s: %s DSC timeout\n",PT.name,msg);
+	     else printk("%s: %s stat=0x%x err=0x%x\n",PT.name,msg,s,e);
+	   pt_req_sense(unit,0);
+	   return 0;
+	}
+	return 1;
+}
+
+static void pt_media_access_cmd( int unit, int tmo, char *cmd, char *fun)
+
+{	if (pt_command(unit,cmd,0,fun)) {
+		pt_req_sense(unit,0);
+		return;
+	}
+	pi_disconnect(PI);
+	pt_poll_dsc(unit,100,tmo,fun);
+}
+
+static void pt_rewind( int unit )
+
+{	char	rw_cmd[12] = {ATAPI_REWIND,0,0,0,0,0,0,0,0,0,0,0};
+
+	pt_media_access_cmd(unit,PT_REWIND_TMO,rw_cmd,"rewind");
+}
+
+static void pt_write_fm( int unit )
+
+{	char	wm_cmd[12] = {ATAPI_WFM,0,0,0,1,0,0,0,0,0,0,0};
+
+        pt_media_access_cmd(unit,PT_TMO,wm_cmd,"write filemark");
+}
+
+#define DBMSG(msg)      NULL
+
+static int pt_reset( int unit )
+
+{	int	i, k, flg;
+	int	expect[5] = {1,1,1,0x14,0xeb};
+
+	pi_connect(PI);
+	WR(0,6,DRIVE);
+	WR(0,7,8);
+
+	pt_sleep(2);
+
+        k = 0;
+        while ((k++ < PT_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+                pt_sleep(10);
+
+	flg = 1;
+	for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+	if (verbose) {
+		printk("%s: Reset (%d) signature = ",PT.name,k);
+		for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+		if (!flg) printk(" (incorrect)");
+		printk("\n");
+	}
+	
+	pi_disconnect(PI);
+	return flg-1;	
+}
+
+static int pt_ready_wait( int unit, int tmo )
+
+{	char	tr_cmd[12] = {ATAPI_TEST_READY,0,0,0,0,0,0,0,0,0,0,0};
+	int	k, p;
+
+	k = 0;
+	while (k < tmo) {
+	  PT.last_sense = 0;
+	  pt_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+	  p = PT.last_sense;
+	  if (!p) return 0;
+	  if (!(((p & 0xffff) == 0x0402)||((p & 0xff) == 6))) return p;
+	  k++;
+          pt_sleep(100);
+	}
+	return 0x000020;	/* timeout */
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{	int	j,k,l;
+
+	j=0; l=0;
+	for (k=0;k<len;k++) 
+	   if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+		l=targ[j++]=buf[k+offs];
+	if (l==0x20) j--; targ[j]=0;
+}
+
+static int xn( char *buf, int offs, int size )
+
+{	int	v,k;
+
+	v=0; 
+	for(k=0;k<size;k++) v=v*256+(buf[k+offs]&0xff);
+	return v;
+}
+
+static int pt_identify( int unit )
+
+{	int 	dt, s;
+	char	*ms[2] = {"master","slave"};
+	char	mf[10], id[18];
+	char    id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0};
+        char    ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,128,0,0,0,0,0,0,0};
+	char    ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,128,0,0,0};
+	char	buf[36];
+
+        s = pt_atapi(unit,id_cmd,36,buf,"identify");
+	if (s) return -1;
+
+	dt = buf[0] & 0x1f;
+	if (dt != 1) {
+	  	if (verbose) 
+		   printk("%s: Drive %d, unsupported type %d\n",
+				PT.name,PT.drive,dt);
+	  	return -1;
+       	}
+
+	xs(buf,mf,8,8);
+	xs(buf,id,16,16);
+
+	PT.flags = 0;
+	PT.capacity = 0;
+	PT.bs = 0;
+
+	if (!pt_ready_wait(unit,PT_READY_TMO)) PT.flags |= PT_MEDIA;
+
+        if (!pt_atapi(unit,ms_cmd,36,buf,"mode sense")) {
+          if (!(buf[2] & 0x80)) PT.flags |= PT_WRITE_OK;
+	  PT.bs = xn(buf,10,2);
+	}
+
+        if (!pt_atapi(unit,ls_cmd,36,buf,"log sense")) 
+		PT.capacity = xn(buf,24,4);
+
+        printk("%s: %s %s, %s",
+		PT.name,mf,id,ms[PT.drive]);
+        if (!(PT.flags & PT_MEDIA)) 
+                printk(", no media\n");
+        else {  if (!(PT.flags & PT_WRITE_OK)) printk(", RO");
+                printk(", blocksize %d, %d MB\n",
+		       PT.bs,PT.capacity/1024);
+        }
+
+	return 0;
+}
+
+static int pt_probe( int unit )
+
+/*	returns  0, with id set if drive is detected
+	        -1, if drive detection failed
+*/
+
+{	if (PT.drive == -1) {
+	   for (PT.drive=0;PT.drive<=1;PT.drive++)
+		if (!pt_reset(unit)) return pt_identify(unit);
+	} else {
+	   if (!pt_reset(unit)) return pt_identify(unit);
+	}
+        return -1; 
+}
+
+static int pt_detect( void )
+
+{	int	k, unit;
+
+	printk("%s: %s version %s, major %d\n",
+		name,name,PT_VERSION,major);
+
+	k = 0;
+	if (pt_drive_count == 0) {
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pt_scratch,
+                        PI_PT,verbose,PT.name)) {
+	        if (!pt_probe(unit)) {
+			PT.present = 1;
+			k++;
+	        } else pi_release(PI);
+	    }
+
+	} else for (unit=0;unit<PT_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+			DU[D_PRO],DU[D_DLY],pt_scratch,PI_PT,verbose,
+			PT.name)) { 
+                if (!pt_probe(unit)) {
+                        PT.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+	if (k) return 0;
+
+	printk("%s: No ATAPI tape drive detected\n",name);
+	return -1;
+}
+
+#define DEVICE_NR(dev)	(MINOR(dev) % 128)
+
+static int pt_open (struct inode *inode, struct file *file)
+
+{       int	unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PT_UNITS) || (!PT.present)) return -ENODEV;
+
+        PT.access++;
+
+	if (PT.access > 1) {
+		PT.access--;
+		return -EBUSY;
+	}
+
+        MOD_INC_USE_COUNT;
+
+	pt_identify(unit);
+
+	if (!PT.flags & PT_MEDIA) {
+		PT.access--;
+		MOD_DEC_USE_COUNT;
+		return -ENODEV;
+		}
+
+	if ((!PT.flags & PT_WRITE_OK) && (file ->f_mode & 2)) {
+		PT.access--;
+		MOD_DEC_USE_COUNT;
+		return -EROFS;
+		}
+
+	if (!(MINOR(inode->i_rdev) & 128))
+		PT.flags |= PT_REWIND;
+
+	PT.bufptr = kmalloc(PT_BUFSIZE,GFP_KERNEL);
+	if (PT.bufptr == NULL) {
+		PT.access--;
+		MOD_DEC_USE_COUNT;
+		printk("%s: buffer allocation failed\n",PT.name);
+		return -ENOMEM;
+	}
+
+        return 0;
+}
+
+static int pt_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+{
+	int unit;
+	struct mtop mtop;
+
+        if (!inode || !inode->i_rdev)
+		return -EINVAL;
+        unit = DEVICE_NR(inode->i_rdev);
+        if (unit >= PT_UNITS)
+		return -EINVAL;
+        if (!PT.present)
+		return -ENODEV;
+
+        switch (cmd) {
+	    case MTIOCTOP:	
+		memcpy_fromfs((char *)&mtop, (char *)arg, 
+			           sizeof(struct mtop));
+
+		switch (mtop.mt_op) {
+
+		    case MTREW: 
+			pt_rewind(unit);
+			return 0;
+
+		    default:	
+			printk("%s: Unimplemented mt_op %d\n",PT.name,
+					mtop.mt_op);
+			return -EINVAL;
+		}
+
+            default:
+		printk("%s: Unimplemented ioctl 0x%x\n",PT.name,cmd);
+                return -EINVAL;
+
+        }
+}
+
+
+static void pt_release (struct inode *inode, struct file *file)
+{
+        int	unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PT_UNITS) || (PT.access <= 0)) 
+                return;
+
+	if (PT.flags & PT_WRITING) pt_write_fm(unit);
+
+	if (PT.flags & PT_REWIND) pt_rewind(unit);	
+
+	PT.access--;
+
+	kfree(PT.bufptr);
+	PT.bufptr = NULL;
+
+        MOD_DEC_USE_COUNT;
+
+}
+
+static int pt_read(struct inode *inode, struct file *filp, char *buf, int count)
+
+{	int	unit = DEVICE_NR(inode->i_rdev);
+	char	rd_cmd[12] = {ATAPI_READ_6,1,0,0,0,0,0,0,0,0,0,0};
+	int	k, n, r, p, s, t, b;
+
+	if (!(PT.flags & (PT_READING|PT_WRITING))) {
+	    PT.flags |= PT_READING;
+	    if (pt_atapi(unit,rd_cmd,0,NULL,"start read-ahead"))
+			return -EIO;
+	} else if (PT.flags & PT_WRITING) return -EIO;
+
+	if (PT.flags & PT_EOF) return 0;
+
+	t = 0;
+
+	while (count > 0) {
+
+	    if (!pt_poll_dsc(unit,1,PT_TMO,"read")) return -EIO;
+
+	    n = count;
+	    if (n > 32768) n = 32768;   /* max per command */
+	    b = (n-1+PT.bs)/PT.bs;
+	    n = b*PT.bs;		/* rounded up to even block */
+
+	    rd_cmd[4] = b;
+
+	    r = pt_command(unit,rd_cmd,n,"read");
+
+	    udelay(1000);
+
+	    if (r) {
+	        pt_req_sense(unit,0);
+	        return -EIO;
+	    }
+
+	    while (1) {
+
+	        r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+                                           DBMSG("read DRQ"),"");
+
+	        if (r & STAT_SENSE) {
+	            pi_disconnect(PI);
+		    pt_req_sense(unit,0);
+		    return -EIO;
+	        }
+
+	        if (r) PT.flags |= PT_EOF; 
+
+	        s = RR(0,7);
+
+	        if (!(s & STAT_DRQ)) break;
+
+	    	n = (RR(0,4)+256*RR(0,5));
+	    	p = (RR(0,2)&3);
+	    	if (p != 2) {
+		    pi_disconnect(PI);
+		    printk("%s: Phase error on read: %d\n",PT.name,p);
+		    return -EIO;
+	    	}
+
+	        while (n > 0) {
+		    k = n;
+		    if (k > PT_BUFSIZE) k = PT_BUFSIZE; 
+		    pi_read_block(PI,PT.bufptr,k);
+		    n -= k;
+		    b = k;
+		    if (b > count) b = count;
+		    memcpy_tofs(buf+t,PT.bufptr,b);
+		    t += b;
+		    count -= b;
+	        }
+
+	    }
+	    pi_disconnect(PI);
+	    if (PT.flags & PT_EOF) break;
+	}
+
+	return t;
+
+}
+
+static int pt_write(struct inode *inode, struct file *filp, 
+			const char *buf, int count)
+
+{	int unit = DEVICE_NR(inode->i_rdev);
+        char    wr_cmd[12] = {ATAPI_WRITE_6,1,0,0,0,0,0,0,0,0,0,0};
+        int     k, n, r, p, s, t, b;
+
+	if (!(PT.flags & PT_WRITE_OK)) return -EROFS;
+
+        if (!(PT.flags & (PT_READING|PT_WRITING))) {
+            PT.flags |= PT_WRITING;
+            if (pt_atapi(unit,wr_cmd,0,NULL,"start buffer-available mode"))
+                        return -EIO;
+        } else if (PT.flags&PT_READING) return -EIO;
+
+	if (PT.flags & PT_EOF) return -ENOSPC;
+
+	t = 0;
+
+	while (count > 0) {
+
+	    if (!pt_poll_dsc(unit,1,PT_TMO,"write")) return -EIO;
+
+            n = count;
+            if (n > 32768) n = 32768;	/* max per command */
+            b = (n-1+PT.bs)/PT.bs;
+            n = b*PT.bs;                /* rounded up to even block */
+
+            wr_cmd[4] = b;
+
+            r = pt_command(unit,wr_cmd,n,"write");
+
+            udelay(1000);
+
+            if (r) {			/* error delivering command only */
+                pt_req_sense(unit,0);
+                return -EIO;
+            }
+
+	    while (1) {
+
+                r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+			                        DBMSG("write DRQ"),NULL);
+
+                if (r & STAT_SENSE) {
+                    pi_disconnect(PI);
+                    pt_req_sense(unit,0);
+                    return -EIO;
+                }
+
+                if (r) PT.flags |= PT_EOF;
+
+	        s = RR(0,7);
+
+	        if (!(s & STAT_DRQ)) break;
+
+                n = (RR(0,4)+256*RR(0,5));
+                p = (RR(0,2)&3);
+                if (p != 0) {
+                    pi_disconnect(PI);
+                    printk("%s: Phase error on write: %d \n",PT.name,p);
+                    return -EIO;
+                }
+
+                while (n > 0) {
+		    k = n;
+		    if (k > PT_BUFSIZE) k = PT_BUFSIZE;
+		    b = k;
+		    if (b > count) b = count;
+		    memcpy_fromfs(PT.bufptr,buf+t,b);
+                    pi_write_block(PI,PT.bufptr,k);
+		    t += b;
+		    count -= b;
+		    n -= k;
+                }
+
+	    }
+	    pi_disconnect(PI);
+	    if (PT.flags & PT_EOF) break;
+	}
+
+	return t;
+}
+
+/* end of pt.c */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/setup.h linux/drivers/block/paride/setup.h
--- v2.0.34/linux/drivers/block/paride/setup.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/setup.h	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,69 @@
+/*
+	setup.h	   (c) 1997-8   Grant R. Guenther <grant@torque.net>
+		                Under the terms of the GNU public license.
+
+        This is a table driven setup function for kernel modules
+        using the module.variable=val,... command line notation.
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.05.05	Allow negative and defaulted values
+
+*/
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+struct setup_tab_t {
+
+	char	*tag;	/* variable name */
+	int	size;	/* number of elements in array */
+	int	*iv;	/* pointer to variable */
+};
+
+typedef struct setup_tab_t STT;
+
+/*  t 	  is a table that describes the variables that can be set
+	  by gen_setup
+    n	  is the number of entries in the table
+    ss	  is a string of the form:
+
+		<tag>=[<val>,...]<val>
+*/
+
+static void generic_setup( STT t[], int n, char *ss )
+
+{	int	j,k, sgn;
+
+	k = 0;
+	for (j=0;j<n;j++) {
+		k = strlen(t[j].tag);
+		if (strncmp(ss,t[j].tag,k) == 0) break;
+	}
+	if (j == n) return;
+
+	if (ss[k] == 0) {
+		t[j].iv[0] = 1;
+		return;
+	}
+
+	if (ss[k] != '=') return;
+	ss += (k+1);
+
+	k = 0;
+	while (ss && (k < t[j].size)) {
+		if (!*ss) break;
+		sgn = 1;
+		if (*ss == '-') { ss++; sgn = -1; }
+		if (!*ss) break;
+		if (isdigit(*ss))
+		  t[j].iv[k] = sgn * simple_strtoul(ss,NULL,0);
+		k++; 
+		if ((ss = strchr(ss,',')) != NULL) ss++;
+	}
+}
+
+/* end of setup.h */
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/paride/spinlock.h linux/drivers/block/paride/spinlock.h
--- v2.0.34/linux/drivers/block/paride/spinlock.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/spinlock.h	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,6 @@
+/* spinlock.h -- dummy version for PARIDE-2.0.34 */
+
+#define spin_lock_irqsave(a,b) 		{ save_flags(b); cli(); }
+#define spin_unlock_irqrestore(a,b) 	restore_flags(b);
+
+
diff -u --recursive --new-file v2.0.34/linux/drivers/block/raid0.c linux/drivers/block/raid0.c
--- v2.0.34/linux/drivers/block/raid0.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/raid0.c	Mon Jul 13 13:47:28 1998
@@ -243,6 +243,7 @@
 		 data->strip_zone[j].size);
   }
 #endif
+  sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -251,11 +252,14 @@
 {
   "raid0",
   raid0_map,
+  NULL,				/* no special make_request */
+  NULL,				/* no special end_request */
   raid0_run,
   raid0_stop,
   raid0_status,
   NULL,				/* no ioctls */
-  0
+  0,
+  NULL,				/* no error_handler */
 };
 
 
diff -u --recursive --new-file v2.0.34/linux/drivers/block/raid1.c linux/drivers/block/raid1.c
--- v2.0.34/linux/drivers/block/raid1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/raid1.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,766 @@
+/************************************************************************
+ * raid1.c : Multiple Devices driver for Linux
+ *           Copyright (C) 1996 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-1 management functions.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid1.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+/*
+ * The following can be used to debug the driver
+ */
+/*#define RAID1_DEBUG*/
+#ifdef RAID1_DEBUG
+#define PRINTK(x)   do { printk x; } while (0);
+#else
+#define PRINTK(x)   do { ; } while (0);
+#endif
+
+
+static struct md_personality raid1_personality;
+static struct md_thread *raid1_thread = NULL;
+struct buffer_head *raid1_retry_list = NULL;
+
+static int __raid1_map (struct md_dev *mddev, kdev_t *rdev,
+		        unsigned long *rsector, unsigned long size)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	int i, n = raid_conf->raid_disks;
+
+	/*
+	 * Later we do read balancing on the read side 
+	 * now we use the first available disk.
+	 */
+
+	PRINTK(("raid1_map().\n"));
+
+	for (i=0; i<n; i++) {
+		if (raid_conf->mirrors[i].operational) {
+			*rdev = raid_conf->mirrors[i].dev;
+			return (0);
+		}
+	}
+
+	printk (KERN_ERR "raid1_map(): huh, no more operational devices?\n");
+	return (-1);
+}
+
+static int raid1_map (struct md_dev *mddev, kdev_t *rdev,
+		      unsigned long *rsector, unsigned long size)
+{
+	return 0;
+}
+
+void raid1_reschedule_retry (struct buffer_head *bh)
+{
+	struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->private_bh);
+
+	PRINTK(("raid1_reschedule_retry().\n"));
+
+	r1_bh->next_retry = raid1_retry_list;
+	raid1_retry_list = bh;
+	md_wakeup_thread(raid1_thread);
+}
+
+/*
+ * raid1_end_buffer_io() is called when we have finished servicing a mirrored
+ * operation and are ready to return a success/failture code to the buffer
+ * cache layer.
+ */
+static inline void raid1_end_buffer_io (struct buffer_head *bh, int uptodate)
+{
+	/*
+	 * kfree() can sleep and we try to keep this bh operation atomic.
+	 */
+	struct raid1_bh * tmp = (struct raid1_bh *) bh->private_bh;
+
+	clear_bit (BH_MD, &bh->b_state);
+	bh->private_bh = NULL;
+	bh->personality = NULL;
+	mark_buffer_uptodate(bh, uptodate);
+	unlock_buffer(bh);
+	kfree(tmp);
+}
+
+void raid1_end_request (struct buffer_head *bh, int uptodate)
+{
+	struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->private_bh);
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	PRINTK(("raid1_end_request().\n"));
+
+	/*
+	 * this branch is our 'one mirror IO has finished' event handler:
+	 */
+	if (!uptodate)
+		md_error (bh->b_dev, bh->b_rdev);
+	else {
+		/*
+		 * Set BH_Uptodate in our master buffer_head, so that
+		 * we will return a good error code for to the higher
+		 * levels even if IO on some other mirrored buffer fails.
+		 *
+		 * The 'master' represents the complex operation to 
+		 * user-side. So if something waits for IO, then it will
+		 * wait for the 'master' buffer_head.
+		 */
+		set_bit (BH_Uptodate, &r1_bh->state);
+	}
+
+	/*
+	 * We split up the read and write side, imho they are 
+	 * conceptually different.
+	 */
+
+	if ( (r1_bh->cmd == READ) || (r1_bh->cmd == READA) ) {
+
+		PRINTK(("raid1_end_request(), read branch.\n"));
+
+		/*
+		 * we have only one buffer_head on the read side
+		 */
+		if (uptodate) {
+			PRINTK(("raid1_end_request(), read branch, uptodate.\n"));
+			raid1_end_buffer_io (bh, uptodate);
+			restore_flags(flags);
+			return;
+		}
+		/*
+		 * oops, read error:
+		 */
+		printk(KERN_ERR "raid1: %s: rescheduling block %lu\n", 
+				 kdevname(bh->b_dev), bh->b_blocknr);
+		raid1_reschedule_retry (bh);
+		restore_flags(flags);
+		return;
+	}
+
+	/*
+	 * WRITE or WRITEA.
+	 */
+	PRINTK(("raid1_end_request(), write branch.\n"));
+
+	/*
+	 * lets see if all mirrored write operations have finished 
+	 * already [we have irqs off, so we can decrease]:
+	 */
+
+	if (!--r1_bh->remaining) {
+		struct md_dev *mddev = r1_bh->mddev;
+		struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+		int i, n = raid_conf->raid_disks;
+
+		PRINTK(("raid1_end_request(), remaining == 0.\n"));
+
+		/*
+		 * kfree() can sleep? really? if yes then we are
+		 * doomed here ...
+		 */
+		for ( i=0; i<n; i++) {
+			if (r1_bh->mirror_bh[i]) kfree(r1_bh->mirror_bh[i]);
+		}
+
+		/*
+		 * the 'master' bh is the one that is used in page IO,
+		 * perhaps someone is waiting on it. Lets erase all
+		 * signs of mirroring, and lets finish the bh operation:
+		 *
+		 * In particular, the "uptodate" value which we return
+		 * to the higher level represents the entire mirror set.
+		 *
+		 * yes, and this is why i want to use the 'master' bh as
+		 * a 'representative'. Thats why i think it's not clean to
+		 * use the master bh for real IO. We mix concepts, which
+		 * isnt too good.
+		 *
+		 * a buffer_head is basically a user-side file buffer.
+		 * Normally it has direct relationship with the physical
+		 * device, but as in this case, we have an abstract mapping
+		 * between the file buffer and the physical layout. So i've
+		 * reverted all changes that do this mixing.
+		 *
+		 * we 'waste' about 76 bytes for the one more buffer_head,
+		 * but note that we will do the mirror bh allocation at once
+		 * in the future, so this isnt really a valid point, i think.
+		 *
+		 * Also i dont like the current way of mixing the user-side buffer
+		 * concept with the 'real' physical layout like raid0.c does
+		 * now: it increases the size of buffer_head even for nonstriped
+		 * devices, etc.
+		 *
+		 * IMHO, in the future, we should have a lightweight buffer_head
+		 * structure, which holds almost no physical device information.
+		 
+		 * Abstract relationship between buffers:
+		 * ===================================== 
+		 * 
+		 *           [user] 
+		 *              |
+		 *              |
+		 *    ['master' buffer_head] + [private_buffer_head]
+		 *                                      |
+		 *                                      |
+		 *                                      |
+		 *                        [additional 'sub'-buffer_heads]
+		 *                           |          |           | 
+		 *                         [dev1]     [dev2]      [dev3]
+		 * 
+ 
+		 * In this scheme it's not clean to use the 'master' as one of
+		 * the 'sub' buffer_heads. If you think about it, currently we can 
+		 * do this only because raid0 introduced it's own private_buffer_head
+		 * structure in buffer_head: rdev,rsector. And raid0 has a 1:1
+		 * relationship to the physical device. But this is really just a
+		 * special case. Once we have our megafast bh pools running, we could
+		 * clean up raid0.c too :))
+		 *
+		 * Not that it isnt clean, it is lethal if in the future we insert our 
+		 * sub buffer_heads into the global block cache. The master request
+		 * should be an IO operation label for the complex operation, nothing
+		 * more.
+		 *
+		 * So we have almost no performance arguments, and alot of cleanness
+		 * arguments.
+		 *
+		 * Comments? Gonna change it back to your way again if you can convince
+		 * me :)) --mingo
+		 *
+		 */
+		raid1_end_buffer_io ( r1_bh->master_bh, 
+				test_bit (BH_Uptodate, &r1_bh->state));
+	}
+	else PRINTK(("raid1_end_request(), remaining == %u.\n", r1_bh->remaining));
+	restore_flags(flags);
+}
+
+/* This routine checks if the undelying device is an md device and in that
+ * case it maps the blocks before putting the request on the queue
+ */
+static inline void
+map_and_make_request (int rw, struct buffer_head *bh)
+{
+	if (MAJOR (bh->b_rdev) == MD_MAJOR){
+		md_map (MINOR (bh->b_rdev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+	}
+	make_request (MAJOR (bh->b_rdev), rw, bh);
+}
+	
+static int
+raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	struct buffer_head *mirror_bh[MD_SB_DISKS];
+	struct raid1_bh * r1_bh;
+	int n = raid_conf->raid_disks, i, sum_bhs = 0, switch_disks = 0, sectors;
+	struct mirror_info *mirror;
+
+	PRINTK(("raid1_make_request().\n"));
+
+/*
+ * We put allocations at the beginning, to avoid sleeping while doing
+ * atomic operations of buffer heads. This might or might not make much
+ * difference, but lets rather be careful.
+ *
+ * but this has two side effects (probably non harmless):
+ *
+ *	1.	The buffer will not be locked while we sleep.
+ *	2.	The rest of the kernel will see BH_Req without
+ *		BH_Lock.
+ */
+	while (!( /* FIXME: now we are rather fault tolerant than nice */
+	r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL)
+	) )
+		printk ("raid1_make_request(#1): out of memory\n");
+	memset (r1_bh, 0, sizeof (struct raid1_bh));
+/*
+ * make_request() can abort the operation when READA or WRITEA are being
+ * used and no empty request is available.
+ *
+ * Currently, just replace the command with READ/WRITE.
+ */
+	if (rw == READA) rw = READ;
+	if (rw == WRITEA) rw = WRITE;
+
+	if (rw == WRITE || rw == WRITEA)
+		mark_buffer_clean(bh);		/* Too early ? */
+
+/*
+ * i think the read and write branch should be separated completely, since we want
+ * to do read balancing on the read side for example. Comments? :) --mingo
+ */
+
+	r1_bh->master_bh=bh;
+	r1_bh->mddev=mddev;
+	r1_bh->cmd = rw;
+
+	set_bit (BH_MD,	&bh->b_state);
+	bh->personality  = &raid1_personality;
+	bh->private_bh   = (void*)(r1_bh);
+
+	if (rw==READ || rw==READA) {
+		int last_used = raid_conf->last_used;
+		PRINTK(("raid1_make_request(), read branch.\n"));
+		mirror = raid_conf->mirrors + last_used;
+		bh->b_rdev = mirror->dev;
+		sectors = bh->b_size >> 9;
+		if (bh->b_blocknr * sectors == raid_conf->next_sect) {
+			raid_conf->sect_count += sectors;
+			if (raid_conf->sect_count >= mirror->sect_limit)
+				switch_disks = 1;
+		} else
+			switch_disks = 1;
+		raid_conf->next_sect = (bh->b_blocknr + 1) * sectors;
+		if (switch_disks) {
+			PRINTK(("read-balancing: switching %d -> %d (%d sectors)\n", last_used, mirror->next, raid_conf->sect_count));
+			raid_conf->sect_count = 0;
+			raid_conf->last_used = mirror->next;
+		}
+		PRINTK (("raid1 read queue: %d %d\n", MAJOR (bh->b_rdev), MINOR (bh->b_rdev)));
+
+		clear_bit (BH_Lock, &bh->b_state);
+		map_and_make_request (rw, bh);
+		return 0;
+	}
+
+	/*
+	 * WRITE or WRITEA.
+	 */
+/*
+ * btw, we have no more master disk. 'slave' is gone too :) [i hate that word :))]
+ *
+ * We are now using the master bh for a real IO. It seems important that:
+ *
+ * 1.	lock_buffer() will be called when we start to handle the request,
+ *	before we do anything (done by ll_rw_blk.c).
+ *
+ * 2.	It seems that Linus took great care to set mark_buffer_clean()
+ *	atomically with cli() in effect just when the buffer was placed
+ *	into the queue. To be compatible with this behavior, it would be
+ *	best to lock the buffer *first*, but mark it clean *last*, and to
+ *	do this by passing through the exact logic in ll_rw_blk.c.
+ *
+ * Note: i've reverted this #3 thing, see the big comment in this file.
+ *
+ * 3.	We are now called from within make_request(), so the real bh
+ *	will be automatically handled last when we return, so we only need
+ *	to add the rest of the buffers (but remember to include the
+ *	master bh in the remaining count).
+ */
+	PRINTK(("raid1_make_request(n=%d), write branch.\n",n));
+
+	for (i = 0; i < n; i++) {
+
+		if (!raid_conf->mirrors [i].operational) {
+			/*
+			 * the r1_bh->mirror_bh[i] pointer remains NULL
+			 */
+			mirror_bh[i] = NULL;
+			continue;
+		}
+
+	/*
+	 * We should use a private pool (size depending on NR_REQUEST),
+	 * to avoid writes filling up the memory with bhs
+	 *
+	 * Such pools are much faster than kmalloc anyways (so we waste almost 
+	 * nothing by not using the master bh when writing and win alot of cleanness)
+	 *
+	 * but for now we are cool enough. --mingo
+	 *
+	 * It's safe to sleep here, buffer heads cannot be used in a shared
+	 * manner in the write branch. Look how we lock the buffer at the beginning
+	 * of this function to grok the difference ;)
+	 */
+		while (!( /* FIXME: now we are rather fault tolerant than nice */
+		mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL)
+		) )
+			printk ("raid1_make_request(#2): out of memory\n");
+		memset (mirror_bh[i], 0, sizeof (struct buffer_head));
+
+	/*
+	 * prepare mirrored bh (fields ordered for max mem throughput):
+	 */
+		mirror_bh [i]->b_blocknr    = bh->b_blocknr;
+		mirror_bh [i]->b_dev        = bh->b_dev;
+		mirror_bh [i]->b_rdev 	    = raid_conf->mirrors [i].dev;
+		mirror_bh [i]->b_rsector    = bh->b_rsector;
+		mirror_bh [i]->b_state      =   (1<<BH_MD)      | (1<<BH_Req) | 
+						(1<<BH_Touched) | (1<<BH_Dirty);
+		mirror_bh [i]->b_count      = 1;
+		mirror_bh [i]->b_size       = bh->b_size;
+		mirror_bh [i]->b_data       = bh->b_data;
+		mirror_bh [i]->b_list       = BUF_LOCKED;
+		mirror_bh [i]->personality  = &raid1_personality;
+		mirror_bh [i]->private_bh   = (void*)(r1_bh);
+
+		r1_bh->mirror_bh[i] = mirror_bh[i];
+		sum_bhs++;
+	}
+
+	r1_bh->remaining = sum_bhs;
+
+	PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs));
+
+	/*
+	 * We have to be a bit careful about the semaphore above, thats why we
+	 * start the requests separately. Since kmalloc() could fail, sleep and
+	 * make_request() can sleep too, this is the safer solution. Imagine,
+	 * end_request decreasing the semaphore before we could have set it up ...
+	 * We could play tricks with the semaphore (presetting it and correcting
+	 * at the end if sum_bhs is not 'n' but we have to do end_request by hand
+	 * if all requests finish until we had a chance to set up the semaphore
+	 * correctly ... lots of races).
+	 */
+	for (i = 0; i < n; i++)
+		if (mirror_bh [i] != NULL)
+			map_and_make_request (rw, mirror_bh [i]);
+
+	return (0);
+}
+			   
+static int raid1_status (char *page, int minor, struct md_dev *mddev)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	int sz = 0, i;
+	
+	sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+	for (i = 0; i < raid_conf->raid_disks; i++)
+		sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_");
+	sz += sprintf (page+sz, "]");
+	return sz;
+}
+
+static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index)
+{
+	int disks = raid_conf->raid_disks;
+	int j;
+
+	for (j = 0; j < disks; j++)
+		if (raid_conf->mirrors [j].next == failed_index)
+			raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next;
+}
+
+static int raid1_error (struct md_dev *mddev, kdev_t dev)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	struct mirror_info *mirror;
+	md_superblock_t *sb = mddev->sb;
+	int disks = raid_conf->raid_disks;
+	int i;
+
+	PRINTK(("raid1_error called\n"));
+
+	if (raid_conf->working_disks == 1) {
+		/*
+		 * Uh oh, we can do nothing if this is our last disk, but
+		 * first check if this is a queued request for a device
+		 * which has just failed.
+		 */
+		for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++)
+			if (mirror->dev == dev && !mirror->operational)
+				return 0;
+		printk (KERN_ALERT "RAID1: only one disk left and IO error.\n");
+		return 0;
+	}
+
+	/* Mark disk as unusable */
+	for (i = 0, mirror = raid_conf->mirrors; i < disks; i++, mirror++) {
+		if (mirror->dev == dev && mirror->operational){
+			mirror->operational = 0;
+			raid1_fix_links (raid_conf, i);
+			sb->disks[mirror->number].state |= (1 << MD_FAULTY_DEVICE);
+			sb->disks[mirror->number].state &= ~(1 << MD_SYNC_DEVICE);
+			sb->disks[mirror->number].state &= ~(1 << MD_ACTIVE_DEVICE);
+			sb->active_disks--;
+			sb->working_disks--;
+			sb->failed_disks++;
+			mddev->sb_dirty = 1;
+			md_wakeup_thread(raid1_thread);
+			raid_conf->working_disks--;
+			printk (KERN_ALERT
+				"RAID1: Disk failure on %s, disabling device."
+				"Operation continuing on %d devices\n",
+				kdevname (dev), raid_conf->working_disks);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * This is a kernel thread which:
+ *
+ *	1.	Retries failed read operations on working mirrors.
+ *	2.	Updates the raid superblock when problems are encountered.
+ */
+void raid1d (void *data)
+{
+	struct buffer_head *bh;
+	kdev_t dev;
+	unsigned long flags;
+	struct raid1_bh * r1_bh;
+	struct md_dev *mddev;
+
+	PRINTK(("raid1d() active\n"));
+	save_flags(flags);
+	cli();
+	while (raid1_retry_list) {
+		bh = raid1_retry_list;
+		r1_bh = (struct raid1_bh *)(bh->private_bh);
+		raid1_retry_list = r1_bh->next_retry;
+		restore_flags(flags);
+
+		mddev = md_dev + MINOR(bh->b_dev);
+		if (mddev->sb_dirty) {
+			mddev->sb_dirty = 0;
+			md_update_sb(MINOR(bh->b_dev));
+		}
+		dev = bh->b_rdev;
+		__raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+		if (bh->b_rdev == dev) {
+			printk (KERN_ALERT 
+					"raid1: %s: unrecoverable I/O read error for block %lu\n",
+						kdevname(bh->b_dev), bh->b_blocknr);
+			raid1_end_buffer_io (bh, 0);
+		} else {
+			printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n", 
+					  kdevname(bh->b_dev), bh->b_blocknr);
+			clear_bit (BH_Lock, &bh->b_state);
+			map_and_make_request (r1_bh->cmd, bh);
+		}
+		cli();
+	}
+	restore_flags(flags);
+	
+}
+
+/*
+ * This will catch the scenario in which one of the mirrors was
+ * mounted as a normal device rather than as a part of a raid set.
+ */
+static int check_consistenty (struct md_dev *mddev)
+{
+	struct raid1_data *raid_conf = mddev->private;
+	kdev_t dev;
+	struct buffer_head *bh = NULL;
+	int i, rc = 0;
+	char *buffer = NULL;
+
+	for (i = 0; i < raid_conf->raid_disks; i++) {
+		if (!raid_conf->mirrors[i].operational)
+			continue;
+		dev = raid_conf->mirrors[i].dev;
+		set_blocksize(dev, 4096);
+		if ((bh = bread(dev, 0, 4096)) == NULL)
+			break;
+		if (!buffer) {
+			buffer = (char *) __get_free_page(GFP_KERNEL);
+			if (!buffer)
+				break;
+			memcpy(buffer, bh->b_data, 4096);
+		} else if (memcmp(buffer, bh->b_data, 4096)) {
+			rc = 1;
+			break;
+		}
+		bforget(bh);
+		fsync_dev(dev);
+		invalidate_buffers(dev);
+		bh = NULL;
+	}
+	if (buffer)
+		free_page((unsigned long) buffer);
+	if (bh) {
+		dev = bh->b_dev;
+		bforget(bh);
+		fsync_dev(dev);
+		invalidate_buffers(dev);
+	}
+	return rc;
+}
+
+static int raid1_run (int minor, struct md_dev *mddev)
+{
+	struct raid1_data *raid_conf;
+	int i, j, raid_disk;
+	md_superblock_t *sb = mddev->sb;
+	md_descriptor_t *descriptor;
+	struct real_dev *realdev;
+
+	MOD_INC_USE_COUNT;
+
+	if (sb->level != 1) {
+		printk("raid1: %s: raid level not set to mirroring (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	/****
+	 * copy the now verified devices into our private RAID1 bookkeeping area:
+	 *
+	 * [whatever we allocate in raid1_run(), should be freed in raid1_stop()]
+	 */
+
+	while (!( /* FIXME: now we are rather fault tolerant than nice */
+	mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL)
+	) )
+		printk ("raid1_run(): out of memory\n");
+	raid_conf = mddev->private;
+	memset(raid_conf, 0, sizeof(*raid_conf));
+
+	PRINTK(("raid1_run(%d) called.\n", minor));
+
+  	for (i = 0; i < mddev->nb_dev; i++) {
+  		realdev = &mddev->devices[i];
+		if (!realdev->sb) {
+			printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+			continue;
+		}
+
+		/*
+		 * This is important -- we are using the descriptor on
+		 * the disk only to get a pointer to the descriptor on
+		 * the main superblock, which might be more recent.
+		 */
+		descriptor = &sb->disks[realdev->sb->descriptor.number];
+		if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+			printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev));
+			continue;
+		}
+		if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+			if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+				printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev));
+				continue;
+			}
+			raid_disk = descriptor->raid_disk;
+			if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+				printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+				continue;
+			}
+			if (raid_conf->mirrors[raid_disk].operational) {
+				printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk);
+				continue;
+			}
+			printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk);
+			raid_conf->mirrors[raid_disk].number = descriptor->number;
+			raid_conf->mirrors[raid_disk].raid_disk = raid_disk;
+			raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev;
+			raid_conf->mirrors[raid_disk].operational = 1;
+			raid_conf->mirrors[raid_disk].sect_limit = 128;
+			raid_conf->working_disks++;
+		}
+	}
+	if (!raid_conf->working_disks) {
+		printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		kfree(raid_conf);
+		mddev->private = NULL;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+	raid_conf->raid_disks = sb->raid_disks;
+	raid_conf->mddev = mddev;
+
+	for (j = 0; !raid_conf->mirrors[j].operational; j++);
+	raid_conf->last_used = j;
+	for (i = raid_conf->raid_disks - 1; i >= 0; i--) {
+		if (raid_conf->mirrors[i].operational) {
+			PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j));
+			raid_conf->mirrors[i].next = j;
+			j = i;
+		}
+	}
+
+	if (check_consistenty(mddev)) {
+		printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n");
+		sb->state |= 1 << MD_SB_ERRORS;
+		kfree(raid_conf);
+		mddev->private = NULL;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+	/*
+	 * Regenerate the "device is in sync with the raid set" bit for
+	 * each device.
+	 */
+	for (i = 0; i < sb->nr_disks ; i++) {
+		sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+		for (j = 0; j < sb->raid_disks; j++) {
+			if (!raid_conf->mirrors[j].operational)
+				continue;
+			if (sb->disks[i].number == raid_conf->mirrors[j].number)
+				sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+		}
+	}
+	sb->active_disks = raid_conf->working_disks;
+
+	printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks);
+	/* Ok, everything is just fine now */
+	return (0);
+}
+
+static int raid1_stop (int minor, struct md_dev *mddev)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+
+	kfree (raid_conf);
+	mddev->private = NULL;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct md_personality raid1_personality=
+{
+	"raid1",
+	raid1_map,
+	raid1_make_request,
+	raid1_end_request,
+	raid1_run,
+	raid1_stop,
+	raid1_status,
+	NULL,			/* no ioctls */
+	0,
+	raid1_error
+};
+
+int raid1_init (void)
+{
+	if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL)
+		return -EBUSY;
+	return register_md_personality (RAID1, &raid1_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return raid1_init();
+}
+
+void cleanup_module (void)
+{
+	md_unregister_thread (raid1_thread);
+	unregister_md_personality (RAID1);
+}
+#endif
diff -u --recursive --new-file v2.0.34/linux/drivers/block/raid5.c linux/drivers/block/raid5.c
--- v2.0.34/linux/drivers/block/raid5.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/raid5.c	Mon Jul 13 13:47:28 1998
@@ -0,0 +1,1504 @@
+/*****************************************************************************
+ * raid5.c : Multiple Devices driver for Linux
+ *           Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-5 management functions.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid5.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+struct buffer_head *efind_buffer(kdev_t dev, int block, int size);
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+static struct md_personality raid5_personality;
+
+struct stripe_head {
+	struct stripe_head	*hash_next, **hash_pprev; /* hash pointers */
+	struct stripe_head	*handle_next;		/* completed during hash scan pointers */
+	struct raid5_data	*raid_conf;
+	struct buffer_head	*bh_old[MD_SB_DISKS];	/* disk image */
+	struct buffer_head	*bh_new[MD_SB_DISKS];	/* buffers of the MD device (present in buffer cache) */
+	struct buffer_head	*bh_copy[MD_SB_DISKS];	/* copy on write of bh_new (bh_new can change from under us) */
+	int			cmd_new[MD_SB_DISKS];	/* READ/WRITE for new */
+	int			new[MD_SB_DISKS];	/* buffer added since the last handle_stripe() */
+	unsigned long		sector;			/* sector of this row */
+	int			size;			/* buffers size */
+	int			pd_idx;			/* parity disk index */
+	int			nr_pending;		/* nr of pending cmds */
+	__u32			state;			/* state flags */
+	int			cmd;			/* stripe cmd */
+	int			count;			/* nr of waiters */
+	int			write_method;		/* reconstruct-write / read-modify-write */
+	int			phase;			/* PHASE_BEGIN, ..., PHASE_COMPLETE */
+	struct wait_queue	*wait;			/* processes waiting for this stripe */
+};
+
+/*
+ * Phase
+ */
+#define PHASE_BEGIN		0
+#define PHASE_READ_OLD		1
+#define PHASE_WRITE		2
+#define PHASE_READ		3
+#define PHASE_COMPLETE		4
+
+/*
+ * Write method
+ */
+#define METHOD_NONE		0
+#define RECONSTRUCT_WRITE	1
+#define READ_MODIFY_WRITE	2
+
+/*
+ * Stripe state
+ */
+#define STRIPE_LOCKED		0
+#define STRIPE_ERROR		1
+
+/*
+ * Stripe commands
+ */
+#define STRIPE_NONE		0
+#define	STRIPE_WRITE		1
+#define STRIPE_READ		2
+
+/*
+ * Stripe cache
+ */
+#define RAID5_STRIPE_POOL_SIZE	128
+#define HASH_PAGES		1
+#define HASH_PAGES_ORDER	0
+#define NR_HASH			(HASH_PAGES * PAGE_SIZE / sizeof(struct stripe_head *))
+#define HASH_MASK		(NR_HASH - 1)
+#define stripe_hash(sect, size)	(stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK])
+
+int nr_stripes = 0, nr_locked_stripes = 0, nr_pending_stripes = 0;
+struct stripe_head **stripe_hashtbl;
+static struct wait_queue *raid5_wait_for_stripe = NULL;
+struct stripe_head *stripe_handle_list = NULL, *stripe_handle_tail = NULL;
+
+/*
+ * Free buffers pool
+ */  
+#define RAID5_POOL_SIZE	3000
+static int nr_free_buffers = 0, nr_used_buffers = 0, max_nr_used_buffers = 0;
+static struct buffer_head *raid5_buffer_list = NULL;
+static struct wait_queue *raid5_wait_for_bh = NULL;
+
+/*
+ * The following can be used to debug the driver
+ */
+#define RAID5_DEBUG	0
+
+#if RAID5_DEBUG
+#define PRINTK(x)   do { printk x; } while (0);
+static int nr_pending = 0, free_1024 = 0, free_4096 = 0, used_1024 = 0, used_4096 = 0;
+#else
+#define PRINTK(x)   do { ; } while (0)
+#endif
+
+static inline int stripe_locked(struct stripe_head *sh)
+{
+	return test_bit(STRIPE_LOCKED, &sh->state);
+}
+
+static inline int stripe_error(struct stripe_head *sh)
+{
+	return test_bit(STRIPE_ERROR, &sh->state);
+}
+
+/*
+ * Stripes are locked whenever new buffers can't be added to them.
+ */
+static inline void lock_stripe(struct stripe_head *sh)
+{
+	if (!set_bit(STRIPE_LOCKED, &sh->state)) {
+		PRINTK(("locking stripe %lu\n", sh->sector));
+		nr_locked_stripes++;
+	}
+}
+
+static inline void unlock_stripe(struct stripe_head *sh)
+{
+	if (clear_bit(STRIPE_LOCKED, &sh->state)) {
+		PRINTK(("unlocking stripe %lu\n", sh->sector));
+		nr_locked_stripes--;
+		wake_up(&sh->wait);
+	}
+}
+
+static inline void finish_stripe(struct stripe_head *sh)
+{
+	unlock_stripe(sh);
+	sh->cmd = STRIPE_NONE;
+	sh->phase = PHASE_COMPLETE;
+	nr_pending_stripes--;
+	wake_up(&raid5_wait_for_stripe);
+}
+
+static void unplug_devices(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int i;
+
+	for (i = 0; i < raid_conf->raid_disks; i++)
+		unplug_device(blk_dev + MAJOR(raid_conf->disks[i].dev));
+}
+
+static void raid5d (void *data);
+
+void __wait_on_stripe(struct stripe_head *sh)
+{
+	struct wait_queue wait = { current, NULL };
+
+	PRINTK(("wait_on_stripe %lu\n", sh->sector));
+	sh->count++;
+	add_wait_queue(&sh->wait, &wait);
+repeat:
+	current->state = TASK_UNINTERRUPTIBLE;
+	if (stripe_locked(sh)) {
+		schedule();
+		goto repeat;
+	}
+	PRINTK(("wait_on_stripe %lu done\n", sh->sector));
+	remove_wait_queue(&sh->wait, &wait);
+	sh->count--;
+	current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_stripe(struct stripe_head *sh)
+{
+	if (stripe_locked(sh))
+		__wait_on_stripe(sh);
+}
+
+static inline void remove_hash(struct stripe_head *sh)
+{
+	PRINTK(("remove_hash(), stripe %lu\n", sh->sector));
+
+	if (sh->hash_pprev) {
+		if (sh->hash_next)
+			sh->hash_next->hash_pprev = sh->hash_pprev;
+		*sh->hash_pprev = sh->hash_next;
+		sh->hash_pprev = NULL;
+		nr_stripes--;
+	}
+}
+
+static inline void insert_hash(struct stripe_head *sh)
+{
+	struct stripe_head **shp = &stripe_hash(sh->sector, sh->size);
+
+	PRINTK(("insert_hash(), stripe %lu, nr_stripes %d\n", sh->sector, nr_stripes));
+
+	if ((sh->hash_next = *shp) != NULL)
+		(*shp)->hash_pprev = &sh->hash_next;
+	*shp = sh;
+	sh->hash_pprev = shp;
+	nr_stripes++;
+}
+
+static void add_bh (struct buffer_head *bh)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	bh->b_next = raid5_buffer_list;
+	raid5_buffer_list = bh;
+	nr_free_buffers++;
+#if RAID5_DEBUG
+	if (bh->b_size == 1024)
+		free_1024++;
+	if (bh->b_size == 4096)
+		free_4096++;
+#endif
+	restore_flags(flags);
+}
+
+static void raid5_kfree_bh (struct buffer_head *bh)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	nr_used_buffers--;
+#if RAID5_DEBUG
+	if (bh->b_size == 1024)
+		used_1024--;
+	if (bh->b_size == 4096)
+		used_4096--;
+#endif
+	if (nr_free_buffers < RAID5_POOL_SIZE) {
+#if 0 /* This can magically catch races :-) */
+		char *b_data = ((volatile struct buffer_head *) bh)->b_data;
+		int b_size = ((volatile struct buffer_head *) bh)->b_size;
+		memset (bh, 0, sizeof (struct buffer_head));
+		((volatile struct buffer_head *) bh)->b_data = b_data;
+		((volatile struct buffer_head *) bh)->b_size = b_size;
+#endif
+		add_bh (bh);
+		wake_up (&raid5_wait_for_bh);
+	} else {
+		if (bh->b_size == PAGE_SIZE)
+			free_page ((unsigned long) bh->b_data);
+		else
+			kfree (bh->b_data);
+#if 0
+		memset (bh, 0, sizeof (struct buffer_head));
+#endif
+		kfree (bh);
+	}
+#if RAID5_DEBUG
+	printk ("kfree_bh: nr_free == %d, nr_used == %d, max_nr_used == %d\n", nr_free_buffers, nr_used_buffers, max_nr_used_buffers);
+#endif
+	restore_flags(flags);
+}
+
+static void raid5_kfree_old_bh(struct stripe_head *sh, int i)
+{
+	if (!sh->bh_old[i]) {
+		printk("raid5_kfree_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+		return;
+	}
+	raid5_kfree_bh(sh->bh_old[i]);
+	sh->bh_old[i] = NULL;
+}
+
+static void raid5_update_old_bh(struct stripe_head *sh, int i)
+{
+	PRINTK(("stripe %lu, idx %d, updating cache copy\n", sh->sector, i));
+	if (!sh->bh_copy[i]) {
+		printk("raid5_update_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+		return;
+	}
+	if (sh->bh_old[i])
+		raid5_kfree_old_bh(sh, i);
+	sh->bh_old[i] = sh->bh_copy[i];
+	sh->bh_copy[i] = NULL;
+}
+
+static void kfree_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int disks = raid_conf->raid_disks, j;
+
+	PRINTK(("kfree_stripe called, stripe %lu\n", sh->sector));
+	if (sh->phase != PHASE_COMPLETE || stripe_locked(sh) || sh->count) {
+		printk("raid5: kfree_stripe(), sector %lu, phase %d, locked %d, count %d\n", sh->sector, sh->phase, stripe_locked(sh), sh->count);
+		return;
+	}
+	for (j = 0; j < disks; j++) {
+		if (sh->bh_old[j])
+			raid5_kfree_old_bh(sh, j);
+		if (sh->bh_new[j] || sh->bh_copy[j])
+			printk("raid5: bug: sector %lu, new %p, copy %p\n", sh->sector, sh->bh_new[j], sh->bh_copy[j]);
+	}
+	remove_hash(sh);
+	kfree(sh);
+}
+
+static int shrink_stripe_cache(int nr)
+{
+	struct stripe_head *sh;
+	int i, count = 0;
+	static int clock = 0;
+
+	PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, nr_stripes, clock));
+	for (i = 0; i < NR_HASH; i++) {
+repeat:
+		sh = stripe_hashtbl[(i + clock) & HASH_MASK];
+		for (; sh; sh = sh->hash_next) {
+			if (sh->phase != PHASE_COMPLETE)
+				continue;
+			if (stripe_locked(sh))
+				continue;
+			if (sh->count)
+				continue;
+			kfree_stripe(sh);
+			if (++count == nr) {
+				PRINTK(("shrink completed, nr_stripes %d\n", nr_stripes));
+				clock = (i + clock) & HASH_MASK;
+				return nr;
+			}
+			goto repeat;
+		}
+	}
+	PRINTK(("shrink completed, nr_stripes %d\n", nr_stripes));
+	return count;
+}
+
+static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+	struct stripe_head *sh;
+
+	if (raid_conf->buffer_size != size) {
+		PRINTK(("switching size, %d --> %d\n", raid_conf->buffer_size, size));
+		shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE);
+		raid_conf->buffer_size = size;
+	}
+
+	PRINTK(("find_stripe, sector %lu\n", sector));
+	for (sh = stripe_hash(sector, size); sh; sh = sh->hash_next)
+		if (sh->sector == sector && sh->raid_conf == raid_conf) {
+			if (sh->size == size) {
+				PRINTK(("found stripe %lu\n", sector));
+				return sh;
+			} else {
+				PRINTK(("switching size for %lu, %d --> %d\n", sector, sh->size, size));
+				kfree_stripe(sh);
+				break;
+			}
+		}
+	PRINTK(("stripe %lu not in cache\n", sector));
+	return NULL;
+}
+
+static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+	struct stripe_head *sh = NULL, *tmp;
+
+	PRINTK(("kmalloc_stripe called\n"));
+
+	while (nr_stripes > RAID5_STRIPE_POOL_SIZE) {
+		shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE / 8);
+		if (nr_stripes <= RAID5_STRIPE_POOL_SIZE)
+			break;
+		md_wakeup_thread(raid_conf->thread);
+		PRINTK(("waiting for some stripes to complete\n"));
+		sleep_on(&raid5_wait_for_stripe);
+	}
+	md_wakeup_thread(raid_conf->thread);
+	sh = kmalloc(sizeof(*sh), GFP_KERNEL);
+
+	/*
+	 * The above might have slept, so perhaps another process
+	 * already created the stripe for us..
+	 */
+	if ((tmp = find_stripe(raid_conf, sector, size)) != NULL) { 
+		kfree(sh);
+		wait_on_stripe(tmp);
+		return tmp;
+	}
+	if (sh) {
+		memset(sh, 0, sizeof(*sh));
+		sh->phase = PHASE_COMPLETE;
+		sh->cmd = STRIPE_NONE;
+		sh->raid_conf = raid_conf;
+		sh->sector = sector;
+		sh->size = size;
+		insert_hash(sh);
+	}
+	return sh;
+}
+
+static struct stripe_head *get_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+	struct stripe_head *sh;
+
+	PRINTK(("get_stripe, sector %lu\n", sector));
+	sh = find_stripe(raid_conf, sector, size);
+	if (sh)
+		wait_on_stripe(sh);
+	else
+		sh = kmalloc_stripe(raid_conf, sector, size);
+	return sh;
+}
+
+static struct buffer_head *remove_bh (int b_size)
+{
+	struct buffer_head *bh, *bhp = NULL;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if ((bh = raid5_buffer_list) == NULL)
+		return NULL;
+	do {
+		if (bh->b_size == b_size || b_size == -1)
+			break;
+		bhp = bh;
+		bh = bh->b_next;
+	} while (bh);
+	if (!bh)
+		return NULL;
+	if (bhp)
+		bhp->b_next = bh->b_next;
+	else
+		raid5_buffer_list = bh->b_next;
+#if RAID5_DEBUG
+	if (bh->b_size == 1024)
+		free_1024--;
+	if (bh->b_size == 4096)
+		free_4096--;
+#endif
+	nr_free_buffers--;
+	if (!nr_free_buffers && raid5_buffer_list)
+		printk ("raid5: bug: buffer_list != NULL, nr_free_buffers == 0\n");
+	restore_flags(flags);
+	return bh;
+}
+
+
+static void shrink_buffers (int num)
+{
+	struct buffer_head *bh;
+
+	while (num--) {
+		if ((bh = remove_bh(-1)) == NULL)
+			return;
+		if (bh->b_size == PAGE_SIZE)
+			free_page ((unsigned long) bh->b_data);
+		else
+			kfree (bh->b_data);
+		kfree (bh);
+	}
+}
+
+static void grow_buffers (int num, int b_size, int priority)
+{
+	struct buffer_head *bh;
+
+	while (num--) {
+		bh = kmalloc (sizeof (struct buffer_head), priority);
+		if (!bh)
+			break;
+		memset (bh, 0, sizeof (struct buffer_head));
+		if (b_size == PAGE_SIZE)
+			bh->b_data = (char *) __get_free_page (priority);
+		else
+			bh->b_data = kmalloc (b_size, priority);
+		if (!bh->b_data) {
+			kfree (bh);
+			break;
+		}
+		bh->b_size = b_size;
+		add_bh (bh);
+	}
+}
+
+static struct buffer_head *raid5_kmalloc_bh (struct stripe_head *sh, int b_size)
+{
+	struct buffer_head *bh;
+	struct raid5_data *raid_conf = sh->raid_conf;
+	unsigned long flags;
+
+	bh = remove_bh(b_size);
+	if (!bh && nr_free_buffers > RAID5_POOL_SIZE / 10)
+		shrink_buffers (RAID5_POOL_SIZE / 10);
+	if (!bh && nr_used_buffers < RAID5_POOL_SIZE) {
+#if 0
+		grow_buffers (200, b_size, GFP_BUFFER);
+#else
+		grow_buffers (200, b_size, GFP_KERNEL);
+#endif
+		bh = remove_bh(b_size);
+	}
+	if (bh == NULL && nr_used_buffers > RAID5_POOL_SIZE / 2) {
+		shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE / 2);
+		bh = remove_bh(b_size);
+	}
+
+	while (bh == NULL && nr_used_buffers > 3 * RAID5_POOL_SIZE / 4) {
+		md_wakeup_thread(raid_conf->thread);
+		run_task_queue (&tq_disk);
+		unplug_devices(sh);
+		PRINTK(("waiting for bh\n"));
+		sleep_on (&raid5_wait_for_bh);
+		bh = remove_bh(b_size);
+	}
+	if (bh == NULL) {
+		grow_buffers (200, b_size, GFP_KERNEL);
+		bh = remove_bh(b_size);
+	}
+	if (bh) {
+		save_flags(flags);
+		cli();
+		nr_used_buffers++;
+		if (nr_used_buffers > max_nr_used_buffers)
+			max_nr_used_buffers = nr_used_buffers;
+#if RAID5_DEBUG
+		if (bh->b_size == 1024)
+			used_1024++;
+		if (bh->b_size == 4096)
+			used_4096++;
+		printk ("kmalloc_bh: free, used, pending, max = %d, %d, %d, %d\n", nr_free_buffers, nr_used_buffers, nr_pending, max_nr_used_buffers);
+		printk ("kmalloc_bh: free1, used1, free4, used4 = %d, %d, %d, %d\n", free_1024, used_1024, free_4096, used_4096);
+#endif
+		restore_flags(flags);
+	}
+	return bh;
+}
+
+static inline void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate)
+{
+	struct buffer_head *bh = sh->bh_new[i];
+
+	sh->bh_new[i] = NULL;
+	clear_bit (BH_MD, &bh->b_state);
+	bh->private_bh = NULL;
+	bh->personality = NULL;
+	mark_buffer_uptodate(bh, uptodate);
+	unlock_buffer(bh);
+	if (!uptodate)
+		printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for "
+		       "block %lu\n", kdevname(bh->b_dev), bh->b_blocknr);
+}
+
+static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate)
+{
+	if (uptodate)
+		set_bit(BH_Uptodate, &bh->b_state);
+	else
+		clear_bit(BH_Uptodate, &bh->b_state);
+}
+
+static void raid5_end_request (struct buffer_head * bh, int uptodate)
+{
+	struct stripe_head *sh = bh->private_bh;
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int disks = raid_conf->raid_disks, i;
+	unsigned long flags;
+
+	PRINTK(("end_request %lu, nr_pending %d\n", sh->sector, sh->nr_pending));
+	save_flags(flags);
+	cli();
+	raid5_mark_buffer_uptodate(bh, uptodate);
+	--sh->nr_pending;
+	if (!sh->nr_pending) {
+		md_wakeup_thread(raid_conf->thread);
+		atomic_inc(&raid_conf->nr_handle);
+		if (!stripe_handle_tail)
+			stripe_handle_list = sh;
+		else
+			stripe_handle_tail->handle_next = sh;
+		sh->handle_next = NULL;
+		stripe_handle_tail = sh;
+	}
+	if (!uptodate)
+		md_error(bh->b_dev, bh->b_rdev);
+	if (raid_conf->failed_disks) {
+		for (i = 0; i < disks; i++) {
+			if (raid_conf->disks[i].operational)
+				continue;
+			if (bh != sh->bh_old[i] && bh != sh->bh_new[i] && bh != sh->bh_copy[i])
+				continue;
+			set_bit(STRIPE_ERROR, &sh->state);
+		}
+	}
+	restore_flags(flags);
+}
+
+static int raid5_map (struct md_dev *mddev, kdev_t *rdev,
+		      unsigned long *rsector, unsigned long size)
+{
+	/* No complex mapping used: the core of the work is done in the
+	 * request routine
+	 */
+	return 0;
+}
+
+static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	struct md_dev *mddev = raid_conf->mddev;
+	int minor = (int) (mddev - md_dev);
+	char *b_data;
+
+	b_data = ((volatile struct buffer_head *) bh)->b_data;
+	memset (bh, 0, sizeof (struct buffer_head));
+	((volatile struct buffer_head *) bh)->b_data = b_data;
+
+	bh->personality	= &raid5_personality;
+	bh->private_bh  = (void *) sh;
+
+	bh->b_rdev	= raid_conf->disks[i].dev;
+	bh->b_dev	= MKDEV(MD_MAJOR, minor);
+	bh->b_rsector   = sh->sector;
+	bh->b_blocknr   = sh->sector / (sh->size >> 9);
+
+	bh->b_state	= (1 << BH_MD) | (1 << BH_Req);
+	bh->b_count	= 1;
+	bh->b_size	= sh->size;
+	bh->b_list	= BUF_LOCKED;
+}
+
+static int raid5_error (struct md_dev *mddev, kdev_t dev)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+	md_superblock_t *sb = mddev->sb;
+	struct disk_info *disk;
+	int i;
+
+	PRINTK(("raid5_error called\n"));
+	for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++)
+		if (disk->dev == dev && disk->operational) {
+			disk->operational = 0;
+			sb->disks[disk->number].state |= (1 << MD_FAULTY_DEVICE);
+			sb->disks[disk->number].state &= ~(1 << MD_SYNC_DEVICE);
+			sb->disks[disk->number].state &= ~(1 << MD_ACTIVE_DEVICE);
+			sb->active_disks--;
+			sb->working_disks--;
+			sb->failed_disks++;
+			mddev->sb_dirty = 1;
+			raid_conf->working_disks--;
+			raid_conf->failed_disks++;
+			md_wakeup_thread(raid_conf->thread);
+			printk (KERN_ALERT
+				"RAID5: Disk failure on %s, disabling device."
+				"Operation continuing on %d devices\n",
+				kdevname (dev), raid_conf->working_disks);
+		}
+	return 0;
+}	
+
+/*
+ * Input: a 'big' sector number, 
+ * Output: index of the data and parity disk, and the sector # in them.
+ */
+static inline unsigned long 
+raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_disks,
+			unsigned int * dd_idx, unsigned int * pd_idx, 
+			struct raid5_data *raid_conf)
+{
+	unsigned int  stripe;
+	int chunk_number, chunk_offset;
+	unsigned long new_sector;
+	int sectors_per_chunk = raid_conf->chunk_size >> 9;
+
+	/* First compute the information on this sector */
+
+	/*
+	 * Compute the chunk number and the sector offset inside the chunk
+	 */
+	chunk_number = r_sector / sectors_per_chunk;
+	chunk_offset = r_sector % sectors_per_chunk;
+
+	/*
+	 * Compute the stripe number
+	 */
+	stripe = chunk_number / data_disks;
+
+	/*
+	 * Compute the data disk and parity disk indexes inside the stripe
+	 */
+	*dd_idx = chunk_number % data_disks;
+
+	/*
+	 * Select the parity disk based on the user selected algorithm.
+	 */
+	if (raid_conf->level == 4)
+		*pd_idx = data_disks;
+	else switch (raid_conf->algorithm) {
+		case ALGORITHM_LEFT_ASYMMETRIC:
+			*pd_idx = data_disks - stripe % raid_disks;
+			if (*dd_idx >= *pd_idx)
+				(*dd_idx)++;
+			break;
+		case ALGORITHM_RIGHT_ASYMMETRIC:
+			*pd_idx = stripe % raid_disks;
+			if (*dd_idx >= *pd_idx)
+				(*dd_idx)++;
+			break;
+		case ALGORITHM_LEFT_SYMMETRIC:
+			*pd_idx = data_disks - stripe % raid_disks;
+			*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+			break;
+		case ALGORITHM_RIGHT_SYMMETRIC:
+			*pd_idx = stripe % raid_disks;
+			*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+			break;
+		default:
+			printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+	}
+
+	/*
+	 * Finally, compute the new sector number
+	 */
+	new_sector = stripe * sectors_per_chunk + chunk_offset;
+
+#if 0
+	if (	*dd_idx > data_disks || *pd_idx > data_disks || 
+		chunk_offset + bh->b_size / 512 > sectors_per_chunk	)
+
+		printk ("raid5: bug: dd_idx == %d, pd_idx == %d, chunk_offset == %d\n", 
+				*dd_idx, *pd_idx, chunk_offset);
+#endif
+
+	return new_sector;
+}
+
+static unsigned long compute_blocknr(struct stripe_head *sh, int i)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int raid_disks = raid_conf->raid_disks, data_disks = raid_disks - 1;
+	unsigned long new_sector = sh->sector, check;
+	int sectors_per_chunk = raid_conf->chunk_size >> 9;
+	unsigned long stripe = new_sector / sectors_per_chunk;
+	int chunk_offset = new_sector % sectors_per_chunk;
+	int chunk_number, dummy1, dummy2, dd_idx = i;
+	unsigned long r_sector, blocknr;
+
+	switch (raid_conf->algorithm) {
+		case ALGORITHM_LEFT_ASYMMETRIC:
+		case ALGORITHM_RIGHT_ASYMMETRIC:
+			if (i > sh->pd_idx)
+				i--;
+			break;
+		case ALGORITHM_LEFT_SYMMETRIC:
+		case ALGORITHM_RIGHT_SYMMETRIC:
+			if (i < sh->pd_idx)
+				i += raid_disks;
+			i -= (sh->pd_idx + 1);
+			break;
+		default:
+			printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+	}
+
+	chunk_number = stripe * data_disks + i;
+	r_sector = chunk_number * sectors_per_chunk + chunk_offset;
+	blocknr = r_sector / (sh->size >> 9);
+
+	check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, raid_conf);
+	if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) {
+		printk("compute_blocknr: map not correct\n");
+		return 0;
+	}
+	return blocknr;
+}
+
+static void xor_block(struct buffer_head *dest, struct buffer_head *source)
+{
+	int lines = dest->b_size / (sizeof (int)) / 8, i;
+	int *destp = (int *) dest->b_data, *sourcep = (int *) source->b_data;
+
+	for (i = lines; i > 0; i--) {
+		*(destp + 0) ^= *(sourcep + 0);
+		*(destp + 1) ^= *(sourcep + 1);
+		*(destp + 2) ^= *(sourcep + 2);
+		*(destp + 3) ^= *(sourcep + 3);
+		*(destp + 4) ^= *(sourcep + 4);
+		*(destp + 5) ^= *(sourcep + 5);
+		*(destp + 6) ^= *(sourcep + 6);
+		*(destp + 7) ^= *(sourcep + 7);
+		destp += 8;
+		sourcep += 8;
+	}
+}
+
+static void compute_block(struct stripe_head *sh, int dd_idx)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int i, disks = raid_conf->raid_disks;
+
+	PRINTK(("compute_block, stripe %lu, idx %d\n", sh->sector, dd_idx));
+
+	if (sh->bh_old[dd_idx] == NULL)
+		sh->bh_old[dd_idx] = raid5_kmalloc_bh(sh, sh->size);
+	raid5_build_block(sh, sh->bh_old[dd_idx], dd_idx);
+
+	memset(sh->bh_old[dd_idx]->b_data, 0, sh->size);
+	for (i = 0; i < disks; i++) {
+		if (i == dd_idx)
+			continue;
+		if (sh->bh_old[i]) {
+			xor_block(sh->bh_old[dd_idx], sh->bh_old[i]);
+			continue;
+		} else
+			printk("compute_block() %d, stripe %lu, %d not present\n", dd_idx, sh->sector, i);
+	}
+	raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1);
+}
+
+static void compute_parity(struct stripe_head *sh, int method)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int i, pd_idx = sh->pd_idx, disks = raid_conf->raid_disks;
+
+	PRINTK(("compute_parity, stripe %lu, method %d\n", sh->sector, method));
+	for (i = 0; i < disks; i++) {
+		if (i == pd_idx || !sh->bh_new[i])
+			continue;
+		if (!sh->bh_copy[i])
+			sh->bh_copy[i] = raid5_kmalloc_bh(sh, sh->size);
+		raid5_build_block(sh, sh->bh_copy[i], i);
+		mark_buffer_clean(sh->bh_new[i]);
+		memcpy(sh->bh_copy[i]->b_data, sh->bh_new[i]->b_data, sh->size);
+	}
+	if (sh->bh_copy[pd_idx] == NULL)
+		sh->bh_copy[pd_idx] = raid5_kmalloc_bh(sh, sh->size);
+	raid5_build_block(sh, sh->bh_copy[pd_idx], sh->pd_idx);
+
+	if (method == RECONSTRUCT_WRITE) {
+		memset(sh->bh_copy[pd_idx]->b_data, 0, sh->size);
+		for (i = 0; i < disks; i++) {
+			if (i == sh->pd_idx)
+				continue;
+			if (sh->bh_new[i]) {
+				xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+				continue;
+			}
+			if (sh->bh_old[i]) {
+				xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+				continue;
+			}
+		}
+	} else if (method == READ_MODIFY_WRITE) {
+		memcpy(sh->bh_copy[pd_idx]->b_data, sh->bh_old[pd_idx]->b_data, sh->size);
+		for (i = 0; i < disks; i++) {
+			if (i == sh->pd_idx)
+				continue;
+			if (sh->bh_new[i] && sh->bh_old[i]) {
+				xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+				xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+				continue;
+			}
+		}
+	}
+	raid5_mark_buffer_uptodate(sh->bh_copy[pd_idx], 1);
+}
+
+static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int dd_idx, int rw)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+
+	if (sh->bh_new[dd_idx])
+		printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector);
+
+	set_bit(BH_MD, &bh->b_state);
+	set_bit(BH_Lock, &bh->b_state);
+	bh->personality  = &raid5_personality;
+	bh->private_bh   = (void *) sh;
+	bh->b_rdev    = raid_conf->disks[dd_idx].dev;
+	bh->b_rsector = sh->sector;
+
+	if (sh->phase == PHASE_COMPLETE && sh->cmd == STRIPE_NONE) {
+		sh->phase = PHASE_BEGIN;
+		sh->cmd = (rw == READ) ? STRIPE_READ : STRIPE_WRITE;
+		nr_pending_stripes++;
+		atomic_inc(&raid_conf->nr_handle);
+	}
+	sh->bh_new[dd_idx] = bh;
+	sh->cmd_new[dd_idx] = rw;
+	sh->new[dd_idx] = 1;
+}
+
+static void complete_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int disks = raid_conf->raid_disks;
+	int i, new = 0;
+	
+	PRINTK(("complete_stripe %lu\n", sh->sector));
+	for (i = 0; i < disks; i++) {
+		if (sh->cmd == STRIPE_WRITE && i == sh->pd_idx)
+			raid5_update_old_bh(sh, i);
+		if (sh->bh_new[i]) {
+			if (!sh->new[i]) {
+#if 0
+				if (sh->cmd == STRIPE_WRITE) {
+					if (memcmp(sh->bh_new[i]->b_data, sh->bh_copy[i]->b_data, sh->size)) {
+						printk("copy differs, %s, sector %lu ",
+							test_bit(BH_Dirty, &sh->bh_new[i]->b_state) ? "dirty" : "clean",
+							sh->sector);
+					} else if (test_bit(BH_Dirty, &sh->bh_new[i]->b_state))
+						printk("sector %lu dirty\n", sh->sector);
+				}
+#endif
+				if (sh->cmd == STRIPE_WRITE)
+					raid5_update_old_bh(sh, i);
+				raid5_end_buffer_io(sh, i, 1);
+				continue;
+			} else
+				new++;
+		}
+		if (new && sh->cmd == STRIPE_WRITE)
+			printk("raid5: bug, completed STRIPE_WRITE with new == %d\n", new);
+	}
+	if (!new)
+		finish_stripe(sh);
+	else {
+		PRINTK(("stripe %lu, new == %d\n", sh->sector, new));
+		sh->phase = PHASE_BEGIN;
+	}
+}
+
+/*
+ * handle_stripe() is our main logic routine. Note that:
+ *
+ * 1.	lock_stripe() should be used whenever we can't accept additonal
+ *	buffers, either during short sleeping in handle_stripe() or
+ *	during io operations.
+ *
+ * 2.	We should be careful to set sh->nr_pending whenever we sleep,
+ *	to prevent re-entry of handle_stripe() for the same sh.
+ *
+ * 3.	raid_conf->failed_disks and disk->operational can be changed
+ *	from an interrupt. This complicates things a bit, but it allows
+ *	us to stop issuing requests for a failed drive as soon as possible.
+ */
+static void handle_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	struct md_dev *mddev = raid_conf->mddev;
+	int minor = (int) (mddev - md_dev);
+	struct buffer_head *bh;
+	int disks = raid_conf->raid_disks;
+	int i, nr = 0, nr_read = 0, nr_write = 0;
+	int nr_cache = 0, nr_cache_other = 0, nr_cache_overwrite = 0, parity = 0;
+	int nr_failed_other = 0, nr_failed_overwrite = 0, parity_failed = 0;
+	int reading = 0, nr_writing = 0;
+	int method1 = INT_MAX, method2 = INT_MAX;
+	int block;
+	unsigned long flags;
+	int operational[MD_SB_DISKS], failed_disks = raid_conf->failed_disks;
+
+	PRINTK(("handle_stripe(), stripe %lu\n", sh->sector));
+	if (sh->nr_pending) {
+		printk("handle_stripe(), stripe %lu, io still pending\n", sh->sector);
+		return;
+	}
+	if (sh->phase == PHASE_COMPLETE) {
+		printk("handle_stripe(), stripe %lu, already complete\n", sh->sector);
+		return;
+	}
+
+	atomic_dec(&raid_conf->nr_handle);
+
+	if (clear_bit(STRIPE_ERROR, &sh->state)) {
+		printk("raid5: restarting stripe %lu\n", sh->sector);
+		sh->phase = PHASE_BEGIN;
+	}
+
+	if ((sh->cmd == STRIPE_WRITE && sh->phase == PHASE_WRITE) ||
+	    (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ)) {
+		/*
+		 * Completed
+		 */
+		complete_stripe(sh);
+		if (sh->phase == PHASE_COMPLETE)
+			return;
+	}
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < disks; i++)
+		operational[i] = raid_conf->disks[i].operational;
+	failed_disks = raid_conf->failed_disks;
+	restore_flags(flags);
+
+	if (failed_disks > 1) {
+		for (i = 0; i < disks; i++) {
+			if (sh->bh_new[i]) {
+				raid5_end_buffer_io(sh, i, 0);
+				continue;
+			}
+		}
+		finish_stripe(sh);
+		return;
+	}
+
+	for (i = 0; i < disks; i++) {
+		if (sh->bh_old[i])
+			nr_cache++;
+		if (i == sh->pd_idx) {
+			if (sh->bh_old[i])
+				parity = 1;
+			else if(!operational[i])
+				parity_failed = 1;
+			continue;
+		}
+		if (!sh->bh_new[i]) {
+			if (sh->bh_old[i])
+				nr_cache_other++;
+			else if (!operational[i])
+				nr_failed_other++;
+			continue;
+		}
+		sh->new[i] = 0;
+		nr++;
+		if (sh->cmd_new[i] == READ)
+			nr_read++;
+		if (sh->cmd_new[i] == WRITE)
+			nr_write++;
+		if (sh->bh_old[i])
+			nr_cache_overwrite++;
+		else if (!operational[i])
+			nr_failed_overwrite++;
+	}
+
+	if (nr_write && nr_read)
+		printk("raid5: bug, nr_write == %d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd);
+
+	if (nr_write) {
+		/*
+		 * Attempt to add entries :-)
+		 */
+		if (nr_write != disks - 1) {
+			for (i = 0; i < disks; i++) {
+				if (i == sh->pd_idx)
+					continue;
+				if (sh->bh_new[i])
+					continue;
+				block = (int) compute_blocknr(sh, i);
+				bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size);
+				if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) {
+					PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block));
+					add_stripe_bh(sh, bh, i, WRITE);
+					sh->new[i] = 0;
+					nr++; nr_write++;
+					if (sh->bh_old[i]) {
+						nr_cache_overwrite++;
+						nr_cache_other--;
+					} else if (!operational[i]) {
+						nr_failed_overwrite++;
+						nr_failed_other--;
+					}
+				}
+			}
+		}
+		PRINTK(("handle_stripe() -- begin writing, stripe %lu\n", sh->sector));
+		/*
+		 * Writing, need to update parity buffer.
+		 *
+		 * Compute the number of I/O requests in the "reconstruct
+		 * write" and "read modify write" methods.
+		 */
+		if (!nr_failed_other)
+			method1 = (disks - 1) - (nr_write + nr_cache_other);
+		if (!nr_failed_overwrite && !parity_failed)
+			method2 = nr_write - nr_cache_overwrite + (1 - parity);
+
+		if (method1 == INT_MAX && method2 == INT_MAX)
+			printk("raid5: bug: method1 == method2 == INT_MAX\n");
+		PRINTK(("handle_stripe(), sector %lu, nr_write %d, method1 %d, method2 %d\n", sh->sector, nr_write, method1, method2));
+
+		if (!method1 || !method2) {
+			lock_stripe(sh);
+			sh->nr_pending++;
+			sh->phase = PHASE_WRITE;
+			compute_parity(sh, method1 <= method2 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE);
+			for (i = 0; i < disks; i++) {
+				if (!operational[i])
+					continue;
+				if (i == sh->pd_idx || sh->bh_new[i])
+					nr_writing++;
+			}
+
+			sh->nr_pending = nr_writing;
+			PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, sh->nr_pending));
+
+			for (i = 0; i < disks; i++) {
+				if (!operational[i])
+					continue;
+				bh = sh->bh_copy[i];
+				if (i != sh->pd_idx && ((bh == NULL) ^ (sh->bh_new[i] == NULL)))
+					printk("raid5: bug: bh == %p, bh_new[%d] == %p\n", bh, i, sh->bh_new[i]);
+				if (i == sh->pd_idx && !bh)
+					printk("raid5: bug: bh == NULL, i == pd_idx == %d\n", i);
+				if (bh) {
+					bh->b_state |= (1<<BH_Dirty);
+					PRINTK(("making request for buffer %d\n", i));
+					clear_bit(BH_Lock, &bh->b_state);
+					make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh);
+				}
+			}
+			return;
+		}
+
+		lock_stripe(sh);
+		sh->nr_pending++;
+		if (method1 < method2) {
+			sh->write_method = RECONSTRUCT_WRITE;
+			for (i = 0; i < disks; i++) {
+				if (i == sh->pd_idx)
+					continue;
+				if (sh->bh_new[i] || sh->bh_old[i])
+					continue;
+				sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size);
+				raid5_build_block(sh, sh->bh_old[i], i);
+				reading++;
+			}
+		} else {
+			sh->write_method = READ_MODIFY_WRITE;
+			for (i = 0; i < disks; i++) {
+				if (sh->bh_old[i])
+					continue;
+				if (!sh->bh_new[i] && i != sh->pd_idx)
+					continue;
+				sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size);
+				raid5_build_block(sh, sh->bh_old[i], i);
+				reading++;
+			}
+		}
+		sh->phase = PHASE_READ_OLD;
+		sh->nr_pending = reading;
+		PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending));
+		for (i = 0; i < disks; i++) {
+			if (!sh->bh_old[i])
+				continue;
+			if (buffer_uptodate(sh->bh_old[i]))
+				continue;
+		 	clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+			make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+		}
+	} else {
+		/*
+		 * Reading
+		 */
+		method1 = nr_read - nr_cache_overwrite;
+		lock_stripe(sh);
+		sh->nr_pending++;
+
+		PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1));
+		if (!method1 || (method1 == 1 && nr_cache == disks - 1)) {
+			PRINTK(("read %lu completed from cache\n", sh->sector));
+			for (i = 0; i < disks; i++) {
+				if (!sh->bh_new[i])
+					continue;
+				if (!sh->bh_old[i])
+					compute_block(sh, i);
+				memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+			}
+			sh->nr_pending--;
+			complete_stripe(sh);
+			return;
+		}
+		if (nr_failed_overwrite) {
+			sh->phase = PHASE_READ_OLD;
+			sh->nr_pending = (disks - 1) - nr_cache;
+			PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending));
+			for (i = 0; i < disks; i++) {
+				if (sh->bh_old[i])
+					continue;
+				if (!operational[i])
+					continue;
+				sh->bh_old[i] = raid5_kmalloc_bh(sh, sh->size);
+				raid5_build_block(sh, sh->bh_old[i], i);
+			 	clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+				make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+			}
+		} else {
+			sh->phase = PHASE_READ;
+			sh->nr_pending = nr_read - nr_cache_overwrite;
+			PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending));
+			for (i = 0; i < disks; i++) {
+				if (!sh->bh_new[i])
+					continue;
+				if (sh->bh_old[i]) {
+					memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+					continue;
+				}
+				clear_bit(BH_Lock, &sh->bh_new[i]->b_state);
+				make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_new[i]);
+			}
+		}
+	}
+}
+
+static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+	const unsigned int raid_disks = raid_conf->raid_disks;
+	const unsigned int data_disks = raid_disks - 1;
+	unsigned int  dd_idx, pd_idx;
+	unsigned long new_sector;
+
+	struct stripe_head *sh;
+
+	if (rw == READA) rw = READ;
+	if (rw == WRITEA) rw = WRITE;
+
+	new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks,
+						&dd_idx, &pd_idx, raid_conf);
+
+	PRINTK(("raid5_make_request, sector %lu\n", new_sector));
+	sh = get_stripe(raid_conf, new_sector, bh->b_size);
+	if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) {
+		printk("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd);
+		lock_stripe(sh);
+		if (!sh->nr_pending)
+			handle_stripe(sh);
+		wait_on_stripe(sh);
+	}
+	sh->pd_idx = pd_idx;
+	if (sh->phase != PHASE_COMPLETE && sh->phase != PHASE_BEGIN)
+		PRINTK(("stripe %lu catching the bus!\n", sh->sector));
+	add_stripe_bh(sh, bh, dd_idx, rw);
+
+	md_wakeup_thread(raid_conf->thread);
+	return 0;
+}
+
+/*
+ * This is our raid5 kernel thread.
+ *
+ * We scan the hash table for stripes which can be handled now.
+ * During the scan, completed stripes are saved for us by the interrupt
+ * handler, so that they will not have to wait for our next wakeup.
+ */
+static void raid5d (void *data)
+{
+	struct stripe_head *sh;
+	struct raid5_data *raid_conf = data;
+	struct md_dev *mddev = raid_conf->mddev;
+	int i, handled = 0, unplug = 0;
+	unsigned long flags;
+
+	PRINTK(("+++ raid5d active\n"));
+
+	if (mddev->sb_dirty) {
+		mddev->sb_dirty = 0;
+		md_update_sb((int) (mddev - md_dev));
+	}
+	save_flags(flags);
+	cli();
+	stripe_handle_list = stripe_handle_tail = NULL;
+	restore_flags(flags);
+
+	for (i = 0; i < NR_HASH; i++) {
+repeat:
+		sh = stripe_hashtbl[i];
+		for (; sh; sh = sh->hash_next) {
+			if (sh->raid_conf != raid_conf)
+				continue;
+			if (sh->phase == PHASE_COMPLETE)
+				continue;
+			if (sh->nr_pending)
+				continue;
+			if (sh->sector == raid_conf->next_sector) {
+				raid_conf->sector_count += (sh->size >> 9);
+				if (raid_conf->sector_count >= 128)
+					unplug = 1;
+			} else
+				unplug = 1;
+			if (unplug) {
+				PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count));
+				unplug_devices(sh);
+				unplug = 0;
+				raid_conf->sector_count = 0;
+			}
+			raid_conf->next_sector = sh->sector + (sh->size >> 9);
+			handled++;
+			handle_stripe(sh);
+			goto repeat;
+		}
+	}
+	if (raid_conf) {
+		PRINTK(("%d stripes handled, nr_handle %d\n", handled, raid_conf->nr_handle));
+		save_flags(flags);
+		cli();
+		if (!raid_conf->nr_handle)
+			clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags);
+	}
+	PRINTK(("--- raid5d inactive\n"));
+}
+
+static int raid5_run (int minor, struct md_dev *mddev)
+{
+	struct raid5_data *raid_conf;
+	int i, j, raid_disk;
+	md_superblock_t *sb = mddev->sb;
+	md_descriptor_t *descriptor;
+	struct real_dev *realdev;
+
+	MOD_INC_USE_COUNT;
+
+	if (sb->level != 5 && sb->level != 4) {
+		printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+	mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL);
+	raid_conf = mddev->private;
+	memset (raid_conf, 0, sizeof (*raid_conf));
+	raid_conf->mddev = mddev;
+
+	PRINTK(("raid5_run(%d) called.\n", minor));
+
+  	for (i = 0; i < mddev->nb_dev; i++) {
+  		realdev = &mddev->devices[i];
+		if (!realdev->sb) {
+			printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+			continue;
+		}
+
+		/*
+		 * This is important -- we are using the descriptor on
+		 * the disk only to get a pointer to the descriptor on
+		 * the main superblock, which might be more recent.
+		 */
+		descriptor = &sb->disks[realdev->sb->descriptor.number];
+		if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+			printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev));
+			continue;
+		}
+		if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+			if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+				printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev));
+				continue;
+			}
+			raid_disk = descriptor->raid_disk;
+			if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+				printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+				continue;
+			}
+			if (raid_conf->disks[raid_disk].operational) {
+				printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk);
+				continue;
+			}
+			printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk);
+	
+			raid_conf->disks[raid_disk].number = descriptor->number;
+			raid_conf->disks[raid_disk].raid_disk = raid_disk;
+			raid_conf->disks[raid_disk].dev = mddev->devices[i].dev;
+			raid_conf->disks[raid_disk].operational = 1;
+
+			raid_conf->working_disks++;
+		}
+	}
+	raid_conf->raid_disks = sb->raid_disks;
+	raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks;
+	raid_conf->mddev = mddev;
+	raid_conf->chunk_size = sb->chunk_size;
+	raid_conf->level = sb->level;
+	raid_conf->algorithm = sb->parity_algorithm;
+
+	if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) {
+		printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+	if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) {
+		printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+	if (raid_conf->failed_disks > 1) {
+		printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks);
+		goto abort;
+	}
+
+#if 0
+	if (check_consistenty(mddev)) {
+		printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n");
+		sb->state |= 1 << MD_SB_ERRORS;
+		goto abort;
+	}
+#endif
+
+	if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) {
+		printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+
+	/*
+	 * Regenerate the "device is in sync with the raid set" bit for
+	 * each device.
+	 */
+	for (i = 0; i < sb->nr_disks ; i++) {
+		sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+		for (j = 0; j < sb->raid_disks; j++) {
+			if (!raid_conf->disks[j].operational)
+				continue;
+			if (sb->disks[i].number == raid_conf->disks[j].number)
+				sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+		}
+	}
+	sb->active_disks = raid_conf->working_disks;
+
+	if (sb->active_disks == sb->raid_disks)
+		printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+	else
+		printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+
+	/* Ok, everything is just fine now */
+	return (0);
+abort:
+	if (raid_conf)
+		kfree(raid_conf);
+	mddev->private = NULL;
+	printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+	MOD_DEC_USE_COUNT;
+	return -EIO;
+}
+
+static int raid5_stop (int minor, struct md_dev *mddev)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+
+	md_unregister_thread(raid_conf->thread);
+	kfree (raid_conf);
+	shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE);
+	shrink_buffers(RAID5_POOL_SIZE);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int raid5_status (char *page, int minor, struct md_dev *mddev)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+	md_superblock_t *sb = mddev->sb;
+	int sz = 0, i;
+
+	sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm);
+	sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+	for (i = 0; i < raid_conf->raid_disks; i++)
+		sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_");
+	sz += sprintf (page+sz, "]");
+	return sz;
+}
+
+static struct md_personality raid5_personality=
+{
+	"raid5",
+	raid5_map,
+	raid5_make_request,
+	raid5_end_request,
+	raid5_run,
+	raid5_stop,
+	raid5_status,
+	NULL,			/* no ioctls */
+	0,
+	raid5_error
+};
+
+int raid5_init (void)
+{
+	if ((stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL)
+		return -ENOMEM;
+	memset(stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE);
+	return register_md_personality (RAID5, &raid5_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return raid5_init();
+}
+
+void cleanup_module (void)
+{
+	free_pages((unsigned long) stripe_hashtbl, HASH_PAGES_ORDER);
+	shrink_stripe_cache(RAID5_STRIPE_POOL_SIZE);
+	shrink_buffers(RAID5_POOL_SIZE);
+	unregister_md_personality (RAID5);
+}
+#endif
diff -u --recursive --new-file v2.0.34/linux/drivers/block/rd.c linux/drivers/block/rd.c
--- v2.0.34/linux/drivers/block/rd.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/rd.c	Mon Jul 13 13:47:28 1998
@@ -582,7 +582,7 @@
 static unsigned insize = 0;  /* valid bytes in inbuf */
 static unsigned inptr = 0;   /* index of next byte to be processed in inbuf */
 static unsigned outcnt = 0;  /* bytes in output buffer */
-static exit_code = 0;
+static int exit_code = 0;
 static long bytes_out = 0;
 static struct file *crd_infp, *crd_outfp;
 
diff -u --recursive --new-file v2.0.34/linux/drivers/block/triton.c linux/drivers/block/triton.c
--- v2.0.34/linux/drivers/block/triton.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/triton.c	Mon Jul 13 13:47:28 1998
@@ -374,7 +374,7 @@
 			 * safely use __get_free_page() here instead
 			 * of __get_dma_pages() -- no ISA limitations.
 			 */
-			dmatable = __get_free_page(GFP_KERNEL);
+			dmatable = __get_free_pages(GFP_KERNEL, 1, 0);
 		}
 		if (dmatable) {
 			hwif->dmatable = (unsigned long *) dmatable;
@@ -503,3 +503,25 @@
 quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
 }
 
+void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, unsigned short dma)
+{
+	int rc;
+	unsigned short pcicmd;
+	unsigned int bmiba = 0;
+
+	printk("ide: Enabling DMA for Promise Technology IDE Ultra-DMA 33 on PCI bus %d function %d, port 0x%04x\n", bus, fn, dma);
+	if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) || (pcicmd & 1) == 0 || (pcicmd & 4) == 0)
+		goto abort;
+	if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba)))
+		goto abort;
+	bmiba &= 0xfff0;	/* extract port base address */
+	if (bmiba != dma || !bmiba)
+		goto abort;
+	hwif0->chipset = ide_promise_udma;
+	hwif1->chipset = ide_promise_udma;
+	init_triton_dma(hwif0, bmiba);
+	init_triton_dma(hwif1, bmiba + 0x08);
+	return;
+abort:
+	printk(KERN_WARNING "ide: Promise/33 not configured correctly (BIOS)\n");
+}
diff -u --recursive --new-file v2.0.34/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.0.34/linux/drivers/cdrom/cm206.c	Sat Aug 17 11:19:26 1996
+++ linux/drivers/cdrom/cm206.c	Mon Jul 13 13:47:28 1998
@@ -739,7 +739,7 @@
   uch * q = cd->q;
   uch ct;			/* current track */
   int binary=0;
-  const skip = 3*60*75;
+  const int skip = 3*60*75;
 
   for (i=track; i>0; i--) if (cd->toc[i].track) {
     min = fsm2lba(cd->toc[i].fsm);
diff -u --recursive --new-file v2.0.34/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
--- v2.0.34/linux/drivers/cdrom/mcd.c	Wed Nov 13 23:30:59 1996
+++ linux/drivers/cdrom/mcd.c	Mon Jul 13 13:47:28 1998
@@ -311,7 +311,10 @@
 	{
 		i = updateToc();
 		if (i < 0)
-			return i;	/* error reading TOC */
+			/* Hermann.Lauer@IWR.Uni-Heidelberg.De:
+		 	We _can_ open the door even without a CD */
+			if (cmd != CDROMEJECT)
+				return i;	/* error reading TOC */
 	}
 
 	switch (cmd)
diff -u --recursive --new-file v2.0.34/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c
--- v2.0.34/linux/drivers/cdrom/sonycd535.c	Mon Apr  1 21:43:06 1996
+++ linux/drivers/cdrom/sonycd535.c	Mon Jul 13 13:47:28 1998
@@ -4,7 +4,7 @@
  * This is a modified version of the CDU-31A device driver (see below).
  * Changes were made using documentation for the CDU-531 (which Sony
  * assures me is very similar to the 535) and partial disassembly of the
- * DOS driver.  I used Minyard's driver and replaced the the CDU-31A
+ * DOS driver.  I used Minyard's driver and replaced the CDU-31A
  * commands with the CDU-531 commands.  This was complicated by a different
  * interface protocol with the drive.  The driver is still polled.
  *
@@ -31,6 +31,10 @@
  *  More changes to support CDU-510/515 series
  *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
  *
+ * 1997-11-18
+ *  Blocksize awareness
+ *      Dong Liu <qian!dliu@arrow.njit.edu>
+ *
  * Things to do:
  *  - handle errors and status better, put everything into a single word
  *  - use interrupts (code mostly there, but a big hole still missing)
@@ -589,11 +593,13 @@
  *    The routine returns number of bytes read in if successful, otherwise
  *  it returns one of the standard error returns.
  ***************************************************************************/
+
+static int sonycd535_block_size = 2048;
+
 static int
 seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
 					   Byte **buff, int buf_size)
 {
-	const int block_size = 2048;
 	Byte cmd_buff[7];
 	int  i;
 	int  read_status;
@@ -601,7 +607,7 @@
 	Byte *data_buff;
 	int  sector_count = 0;
 
-	if (buf_size < ((long)block_size) * n_blocks)
+	if (buf_size < sonycd535_block_size * n_blocks)
 		return NO_ROOM;
 
 	set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
@@ -626,7 +632,7 @@
 			if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
 				/* data is ready, read it */
 				data_buff = buff[sector_count++];
-				for (i = 0; i < block_size; i++)
+				for (i = 0; i < sonycd535_block_size; i++)
 					*data_buff++ = inb(data_reg);	/* unrolling this loop does not seem to help */
 				break;			/* exit the timeout loop */
 			}
@@ -639,7 +645,7 @@
 	/* read all the data, now read the status */
 	if ((i = read_exec_status(status)) != 0)
 		return i;
-	return block_size * sector_count;
+	return sonycd535_block_size * sector_count;
 }	/* seek_and_read_N_blocks() */
 
 /****************************************************************************
@@ -1594,6 +1600,7 @@
 					return -EIO;
 				}
 				blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+				blksize_size[MAJOR_NR] = &sonycd535_block_size;
 				read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read-ahead */
 
 				sony_toc = (struct s535_sony_toc *)
diff -u --recursive --new-file v2.0.34/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v2.0.34/linux/drivers/char/ChangeLog	Thu Jun  6 12:23:08 1996
+++ linux/drivers/char/ChangeLog	Mon Jul 13 13:47:28 1998
@@ -253,7 +253,7 @@
 Fri Feb 17 09:34:09 1995  Theodore Y. Ts'o  (tytso@rt-11)
 
 	* serial.c (rs_interrupt_single, rs_interrupt, rs_interrupt_multi): 
-		Change the the number of passes made from 64 to be 256,
+		Change the number of passes made from 64 to be 256,
 		configurable with the #define RS_ISR_PASS_LIMIT.
 
 	* serial.c (rs_init, set_serial_info, get_serial_info, rs_close):
diff -u --recursive --new-file v2.0.34/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.0.34/linux/drivers/char/Makefile	Mon Jul 13 13:46:26 1998
+++ linux/drivers/char/Makefile	Mon Jul 13 13:47:28 1998
@@ -217,14 +217,6 @@
   endif
 endif  
 
-ifeq ($(CONFIG_BAYCOM),y)
-L_OBJS += baycom.o
-else
-  ifeq ($(CONFIG_BAYCOM),m)
-  M_OBJS += baycom.o
-  endif
-endif  
-
 ifdef CONFIG_TGA_CONSOLE
 L_OBJS += tga.o
   ifdef CONFIG_VGA_CONSOLE
diff -u --recursive --new-file v2.0.34/linux/drivers/char/README.stallion linux/drivers/char/README.stallion
--- v2.0.34/linux/drivers/char/README.stallion	Wed Apr 24 02:48:04 1996
+++ linux/drivers/char/README.stallion	Mon Jul 13 13:47:29 1998
@@ -1,52 +1,183 @@
 
-Stallion Multiport Serial Drivers
----------------------------------
+Stallion Multiport Serial Driver Readme
+---------------------------------------
+
+Copyright (C) 1994-1998,  Stallion Technologies (support@stallion.com).
+
+Version:   5.4.4
+Date:      20MAR98
 
-Version: 1.1.3
-Date:    23APR96
-Author:  Greg Ungerer (gerg@stallion.oz.au)
 
 
 1. INTRODUCTION
 
-There are two drivers that work with the different families of Stallion
-multiport serial boards. One is for the Stallion smart boards - that is
-EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent
-multiport boards - EasyConnection 8/64, ONboard, Brumby and Stallion.
-
-If you are using any of the Stallion intelligent multiport boards (Brumby,
-ONboard, Stallion, EasyConnection 8/64) with Linux you will need to get the
-driver utility package. This package is available at most of the Linux
-archive sites (and on CD's that contain these archives). The file will be
-called stallion-X.X.X.tar.gz where X.X.X will be the version number. In
-particular this package contains the board embedded executable images that
-are required for these boards. It also contains the downloader program.
-These boards cannot be used without this.
-
-The following ftp sites (and their mirrors) definitely have the stallion
-driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu.
-
-ftp.stallion.com:/drivers/ata5/Linux/stallion-1.1.2.tar.gz
-tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-1.1.2.tar.gz
-sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-1.1.2.tar.gz
-
-If you are using the EasyIO or EasyConnection 8/32 boards then you don't
-need this package. Although it does have a handy script to create the
-/dev device nodes for these boards, and a serial stats display program.
-
-If you require DIP switch settings, EISA/MCA configuration files, or any
-other information related to Stallion boards then have a look at Stallion's
-web pages at http://www.stallion.com.
+This is a Linux driver for some of the Stallion Technologies range of
+multiport serial boards. There are really two drivers in this package.
+One is for the Stallion smart boards, the other for the true Stallion
+intelligent multiport boards.
+
+The drivers included in this package are intended as a replacement for
+those shipped with Linux kernel versions in the 2.0.X series. For later
+versions of the kernel (2.1.0 and above) use the driver source supplied
+with the kernel. The drivers in this package specifically add support
+for the most recent releases of Stallion hardware - which are not supported
+in the Stallion drivers supplied in the 2.0.X kernels. The drivers in this
+package do not support kernel versions earlier than 2.0.0.
+
+The other utilities supplied in this package can be used with Stallion
+drivers on any version of the kernel.
+
+If you have any trouble getting Stallion boards to work in Linux systems,
+please contact Stallion Technologies support department via email or phone.
+Contact information for Stallion Technologies offices is included in the
+file "Offices" contained in this distribution.
+
+Please note the disclaimers set out in the GNU general public license
+included with this driver package.
+
+All host driver source is included in this package, and is copyrighted under
+the GNU GPL. The board "firmware" code in this package is copyright Stallion
+Technologies (the files cdk.sys and 2681.sys).
+
+
+1.1 SMART MULTIPORT BOARD DRIVER
+
+This driver supports the EasyIO and EasyConnection 8/32 range of boards.
+These boards are not classic intelligent multiport boards, but are host
+based multiport boards that use Cirrus Logic CL-CD1400 UART's, or on
+newer versions of the hardware use the Signetics 26C198 UART. Both of
+these are high performance UART's with built in FIFO's, automatic flow
+control and a host of other features.
+
+The EasyIO range of cards comes in 4 forms, the EasyIO-4, EasyIO-8,
+EasyIO-8M and EasyIO-8-PCI. The first three are ISA based boards while
+the last is a PCI bus board. All of these are non-expandable, low cost,
+multiport boards with 4 or 8 RS-232C ports. Each ISA EasyIO board requires 8
+bytes of I/O address space and 1 interrupt. The PCI EasyIO board uses 64
+bytes of I/O address space and 1 interrupt. On EISA and PCI systems it is
+possible to share 1 interrupt between multiple boards. The EasyIO-4 has 10
+pin RJ connectors, and the EasyIO-8 comes with a dongle cable with either 10
+pin RJ connectors or DB-25 connectors. The EasyIO-8M has 6 pin RJ connectors.
+
+The EasyConnection 8/32 family of boards is a relatively low cost modular
+range of multiport serial boards. The EasyConnection 8/32 boards can be
+configured to have from 8 to 32 serial ports by plugging in external serial
+port modules that contain either 8 or 16 ports each. There is a wide range
+of external modules available that offer: DB-25 connectors, RJ-45 connectors
+(both with RS-232 D and E compatible drivers), and also RS-422 and RS-485
+ports. The EasyConnection 8/32 boards come in ISA, PCI and MCA bus versions.
+The board takes the form of a host adapter card, with an external connector
+cable that plugs into the external modules. The external modules just clip
+together to add ports (BTW, they are NOT hot pluggable). Each ISA
+EasyConnection 8/32 board requires two separate I/O address ranges, one two
+bytes in size and a secondary region of 32 bytes. Each PCI EasyConnection
+8/32 requires two regions of I/O address space, normally these will be
+automatically allocated by the system BIOS at power on time. Each MCA
+EasyConnection board requires one I/O address region 64 bytes in size. All
+board types also require one interrupt. On EISA systems multiple boards can
+share one interrupt. The secondary I/O range of the ISA board (the 32 byte
+range) can be shared between multiple boards on any bus type.
+
+The EasyConnection 8/64-PCI family is similar to the EasyConnection 8/32-PCI
+board, and uses the same external modules. It is supported by the smart
+board driver - not the intelligent board driver. It uses 2 regions of I/O
+address space, both 64 bytes in size, and 1 interrupt.
+
+
+
+1.2 INTELLIGENT MULTIPORT BOARD DRIVER
+
+This driver is for Stallion's range of true intelligent multiport boards.
+It supports the EasyConnection 8/64, ONboard and Brumby families of multiport
+boards. The EasyConnection 8/64 and ONboard boards come in ISA, EISA and
+Microchannel bus versions. The Brumby boards are only available in ISA
+versions. This driver can also work with the original Stallion board, but
+these are no longer supported by Stallion Technologies.
+
+The EasyConnection 8/64 family of boards is a medium cost, high performance,
+modular range of intelligent multiport serial boards. The EasyConnection 8/64
+boards can be configured to have from 8 to 64 serial ports by plugging in
+external serial port modules that contain either 8 or 16 ports each (these
+modules are the same used by the EasyConnection 8/32 board). There is a wide
+range of external modules available that offer: DB-25 connectors, RJ-45
+connectors (both with RS-232 D and E compatible drivers), and also RS-422 and
+RS-485 ports. The board takes the form of a host adapter card, with an external
+connector cable that plugs into the external modules. The external modules
+just clip together to add ports (BTW, they are NOT hot pluggable). Each
+EasyConnection 8/64 board requires 4 bytes of I/O address space and a region
+of memory space. The size of the memory region required depends on the exact
+board type. The EISA version requires 64 Kbytes of address space (that can
+reside anywhere in the 4 Gigabyte physical address space). The ISA and MCA
+boards require 4 Kbytes of address space (which must reside in the lower
+1 Mbyte of physical address space - typically in the c8000 to e0000 range).
+No interrupts are required. The physical memory region of multiple
+EasyConnection 8/64 boards can be shared, but each board must have a separate
+I/O address.
+
+The ONboard family of boards are traditional intelligent multiport serial
+boards. They are Stallion's older range of boards with a limited expansion
+capability. They come in 4, 8, 12, 16 and 32 port versions. The board uses
+the same base card (which has 4 ports on it) and is expanded to more ports via
+a mezzanine board that attaches directly onto the base card. External panels
+plug into the ONboard providing RS-232C ports with DB-25 plugs. An RS-422
+DB-25 dual interface panel is also available. The ISA and microchannel
+ONboards require 16 bytes of I/O address space and 64K bytes of memory
+space. The memory space can be anywhere in the 16 Mbyte ISA bus address
+range. No interrupt is required. The EISA ONboard requires 64 Kbytes of
+memory space that can be anywhere in the 4 Gigabyte physical address space.
+All ONboard boards can share their memory region with other ONboards (or
+EasyConnection 8/64 boards).
+
+The Brumby family of boards are traditional, low cost intelligent multiport
+serial boards. They are non-expandable and come in 4, 8 and 16 port versions.
+They are only available for the ISA bus. The serial ports are all on DB-25
+"dongle" cables that attach to the rear of the board. Each Brumby board
+requires 16 bytes of I/O address space and 16 Kbytes of memory space. No
+interrupts are required.
+
+The original Stallion boards are old. They went out of production some years
+back and are no longer supported. They offer limited expandability and are
+available in 8 or 16 port configurations. An external panel houses 16 RS-232C
+ports with DB-9 connectors. They require 16 bytes of I/O address space, and
+either 64K or 128K of memory space. No interrupt is required.
+
+That's the boards supported by the second driver. The ONboard, Brumby and
+Stallion boards are Stallion's older range of intelligent multiports - so
+there are lots of them around. They only support a maximum baud rate of
+38400. The EasyConnection 8/64 is a true high performance intelligent
+multiport board, having much greater throughput than any of Stallion's
+older boards. It also supports speeds up to 460800 baud.
+
+
+1.3 HOW TO GET BOARDS
+
+Stallion Technologies has offices all over the world, as well as many more
+distributors and resellers. To find out about local availability please
+contact the nearest Stallion office and they can give you all the information
+you need. Look in the "Offices" file in the driver package for a current list
+of Stallion Technologies offices.
+
+Another good source of information about the Stallion range of boards and
+local availability is on the Stallion Web page. Check it out at
+http://www.stallion.com.
 
 
 
 2. INSTALLATION
 
+This version of the driver is intended for kernel versions 2.0.0 and later.
+It will not work on earlier kernel versions, due to kernel interface changes.
+(Note that older versions of these drivers do work on older kernels.)
+If you are using a more recent development kernel (versions 2.1.X and
+greater) you should use the Stallion drivers supplied with that kernel,
+they are more up to date.
+
 The drivers can be used as loadable modules or compiled into the kernel.
-You can choose which when doing a "make config" on the kernel.
+Depending on which form of driver loading you decide to use, the installation
+procedure will be a little different.
 
 All ISA, EISA and MCA boards that you want to use need to be entered into
-the driver(s) configuration structures. All PCI boards will be automatically
+the driver(s) configuration structures. PCI boards will be automatically
 detected when you load the driver - so they do not need to be entered into
 the driver(s) configuration structure. (Note that kernel PCI BIOS32 support
 is required to use PCI boards.)
@@ -54,45 +185,135 @@
 Entering ISA, EISA and MCA boards into the driver(s) configuration structure
 involves editing the driver(s) source file. It's pretty easy if you follow
 the instructions below. Both drivers can support up to 4 boards. The smart
-card driver (the stallion.c driver) supports any combination of EasyIO and
-EasyConnection 8/32 boards (up to a total of 4). The intelligent driver
-supports any combination of ONboards, Brumbys, Stallions and EasyConnection
-8/64 boards (up to a total of 4).
-
-To set up the driver(s) for the boards that you want to use you need to
-edit the appropriate driver file and add configuration entries.
-
-If using EasyIO or EasyConnection 8/32 ISA or MCA boards, do:
+card driver supports any combination of EasyIO, EasyConnection 8/32 and
+EasyConnection 8/64-PCI boards (up to a total of 4). The intelligent driver
+supports any combination of ONboards, Brumbys, Stallions and
+EasyConnection 8/64 boards (up to a total of 4).
+
+
+2.1 LOADABLE MODULE DRIVERS
+
+You will need the gcc compiler and make installed on your system to make the
+driver modules. You will also need to have the kernel source on the system,
+and have at least done a "make config" and "make dep" on it. (If you haven't
+done this before then you may want to read the kernel source README file,
+usually found in /usr/src/linux.)
+
+To build the driver modules:
+1. Setup the driver configuration for the boards. If using EasyIO or
+   EasyConnection 8/32 ISA or MCA boards, do:
    vi stallion.c
       - find the definition of the stl_brdconf array (of structures)
         near the top of the file
       - modify this to match the boards you are going to install
 	(the comments before this structure should help)
       - save and exit
-
-If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do:
+   If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do:
+   vi istallion.c
+      - find the definition of the stli_brdconf array (of structures)
+        near the top of the file
+      - modify this to match the boards you are going to install
+	(the comments before this structure should help)
+      - save and exit
+2. cp stallion.h cd1400.h sc26198.h /usr/include/linux/include/linux
+   cp istallion.h cdk.h comstats.h /usr/include/linux/include/linux
+3. make modules
+   This will compile the driver modules, as stallion and istallion.
+
+The stallion module is the EasyIO, EasyConnection 8/32 and
+EasyConnection 8/64-PCI driver, the istallion module is the ONboard,
+Brumby, Stallion and EasyConnection 8/64 driver.
+
+To load up the smart board driver use:
+    insmod ./stallion
+This will load the EasyIO and EasyConnection 8/32 driver. It will output a
+message to say that it loaded and print the driver version number. It
+will also print out whether it found the configured boards or not. (These
+messages may appear in your /var/adm/messages file depending on how the
+klogd and syslogd daemons are setup on your system).
+
+To load the intelligent board driver use:
+    insmod ./istallion
+It will output similar messages to the smart board driver.
+
+
+2.2 STATIC DRIVERS (KERNEL LINKED)
+
+You will need to build a new kernel to link in the Stallion drivers. The first
+thing you need is to have the full kernel source. Most people will have this.
+The following assumes that the kernel source is in /usr/src/linux.
+
+To install the drivers:
+1. cp stallion.c istallion.c /usr/src/linux/drivers/char
+   cp stallion.h cd1400.h sc26198.h /usr/include/linux/include/linux
+   cp istallion.h cdk.h comstats.h /usr/include/linux/include/linux
+2. cd /usr/src/linux/drivers/char
+3. Setup the driver configuration for the boards. If using EasyIO,
+   EasyConnection 8/32 or EasyConnection 8/64-PCI boards, do:
+   vi stallion.c
+      - find the definition of the stl_brdconf array (of structures)
+        near the top of the file
+      - modify this to match the boards you are going to install
+	(the comments before this structure should help)
+      - save and exit
+   If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do:
    vi istallion.c
       - find the definition of the stli_brdconf array (of structures)
         near the top of the file
       - modify this to match the boards you are going to install
 	(the comments before this structure should help)
       - save and exit
+4. cd /usr/src/linux
+5. build a new kernel - if you haven't done this before you may want to
+   read the README file in /usr/src/linux.
+
+Once you have a new kernel built, reboot to start it up. On startup the
+driver will output a message to say it is operational (with the driver
+version number). It will also print out if it could find the boards listed
+in its configuration structure or not.
+
+
+2.3 INTELLIGENT DRIVER OPERATION
+
+The intelligent boards also need to have their "firmware" code downloaded
+to them. This is done via a user level application supplied in the driver
+package called "stlload". Compile this program where ever you dropped the
+package files, by typing "make". In its simplest form you can then type
+    ./stlload -i cdk.sys
+in this directory and that will download board 0 (assuming board 0 is an
+EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do:
+    ./stlload -i 2681.sys
+
+Normally you would want all boards to be downloaded as part of the standard
+system startup. To achieve this, add one of the lines above into the
+/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add
+the "-b <brd-number>" option to the line. You will need to download code for
+every board. You should probably move the stlload program into a system
+directory, such as /usr/sbin. Also, the default location of the cdk.sys image
+file in the stlload down-loader is /usr/lib/stallion. Create that directory
+and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put
+them anyway). As an example your /etc/rc.d/rc.S file might have the
+following lines added to it (if you had 3 boards):
+    /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys
+    /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys
+    /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys
 
-Once you have set up the board configurations then you are ready to build
-the kernel or modules.
+The image files cdk.sys and 2681.sys are specific to the board types. The
+cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly
+the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards.
+If you load the wrong image file into a board it will fail to start up, and
+of course the ports will not be operational!
 
-When the new kernel is booted, or the loadable module loaded then the
-driver will emit some kernel trace messages about whether the configured
-boards where detected or not. Depending on how your system logger is set
-up these may come out on the console, or just be logged to
-/var/adm/messages. You should check the messages to confirm that all is well.
+If you are using the module version of the driver you might want to put the
+insmod calls in the startup script as well (before the download lines
+obviously).
 
 
-2.1 SHARING INTERRUPTS
+2.4 SHARING INTERRUPTS
 
-It is possible to share interrupts between multiple EasyIO and
-EasyConnection 8/32 boards in an EISA system. To do this you will need to
-do a couple of things:
+As mentioned in the introduction, it is possible to share interrupts between
+multiple EasyIO and EasyConnection 8/32 boards in an EISA system. To do this
+you will need to do a couple of things:
 
 1. When entering the board resources into the stallion.c file you need to
    mark the boards as using level triggered interrupts. Do this by replacing
@@ -109,17 +330,17 @@
    that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32
    EISA configuration files required are supplied by Stallion Technologies
    on the DOS Utilities floppy (usually supplied in the box with the board
-   when purchased. If not, you can pick it up from Stallion's FTP site,
-   ftp.stallion.com). You will need to edit the board resources to choose
-   level triggered interrupts, and make sure to set each board's interrupt
-   to the same IRQ number.
+   when purchased. If not, you can pick it up from Stallion's FTP site
+   ftp.stallion.com or web site http://www.stallion.com). You will need to
+   edit the board resources to choose level triggered interrupts, and make
+   sure to set each board's interrupt to the same IRQ number.
 
 You must complete both the above steps for this to work. When you reboot
 or load the driver your EasyIO and EasyConnection 8/32 boards will be
 sharing interrupts.
 
 
-2.2 USING HIGH SHARED MEMORY
+2.5 USING HIGH SHARED MEMORY
 
 The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of
 using shared memory addresses above the usual 640K - 1Mb range. The ONboard
@@ -128,75 +349,52 @@
 ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus
 addressing limit).
 
-The higher than 1Mb memory addresses are fully supported by this driver.
-Just enter the address as you normally would for a lower than 1Mb address
-(in the drivers board configuration structure).
+The istallion driver offers direct support for these higher memory regions.
+To use them just enter the high memory address as if it were a low memory
+address (in the driver board configuration structure).
+
 
+2.6 LINUX KERNEL VERSIONS 2.1.X
 
+There may be some minor differences between the driver source code in this
+package and that in the Linux kernel source. This will be due to changes
+needed in the drivers so that they work correctly on newer kernels. The
+driver source included in this package is intended for use with 2.0.X
+series kernels. If you have a kernel version 2.1.0 or later then use the
+source provided with the kernel - it will be more up to date. Stallion
+Technologies regularly submits the latest driver source to be included in
+the new kernel source releases.
 
-2.3 TROUBLE SHOOTING
+
+2.7 TROUBLE SHOOTING
 
 If a board is not found by the driver but is actually in the system then the
 most likely problem is that the I/O address is wrong. Change it in the driver
-stallion.c or istallion.c configuration structure and rebuild the kernel or
-modules, or change it on the board. On EasyIO and EasyConnection 8/32 boards
-the IRQ is software programmable, so if there is a conflict you may need to
-change the IRQ used for a board in the stallion.c configuration structure.
-There are no interrupts to worry about for ONboard, Brumby, Stallion or
-EasyConnection 8/64 boards. The memory region on EasyConnection 8/64 and
-ONboard boards is software programmable, but not on the Brumbys or Stallions.
+stallion.c or istallion.c configuration structure and rebuild the kernel
+or modules, or change it on the board. On EasyIO and EasyConnection 8/32
+boards the IRQ is software programmable, so if there is a conflict you may
+need to change the IRQ used for a board in the stallion.c configuration
+structure. There are no interrupts to worry about for ONboard, Brumby,
+Stallion or EasyConnection 8/64 boards. The memory region on EasyConnection
+8/64 and ONboard boards is software programmable, but not on the Brumbys or
+Stallions.
 
 
 
 3. USING THE DRIVERS
 
-3.1 INTELLIGENT DRIVER OPERATION
-
-The intelligent boards also need to have their "firmware" code downloaded
-to them. This is done via a user level application supplied in the driver
-package called "stlload". Compile this program where ever you dropped the
-package files, by typing "make". In its simplest form you can then type
-    ./stlload -i cdk.sys
-in this directory and that will download board 0 (assuming board 0 is an
-EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do:
-    ./stlload -i 2681.sys
-
-Normally you would want all boards to be downloaded as part of the standard
-system startup. To achieve this, add one of the lines above into the
-/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add
-the "-b <brd-number>" option to the line. You will need to download code for
-every board. You should probably move the stlload program into a system
-directory, such as /usr/sbin. Also, the default location of the cdk.sys image
-file in the stlload down-loader is /usr/lib/stallion. Create that directory
-and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put
-them anyway). As an example your /etc/rc.d/rc.S file might have the
-following lines added to it (if you had 3 boards):
-    /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys
-    /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys
-    /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys
-
-The image files cdk.sys and 2681.sys are specific to the board types. The
-cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly
-the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards.
-If you load the wrong image file into a board it will fail to start up, and
-of course the ports will not be operational!
-
-If you are using the modularized version of the driver you might want to put
-the insmod calls in the startup script as well (before the download lines
-obviously).
-
-
-3.2 USING THE SERIAL PORTS
-
 Once the driver is installed you will need to setup some device nodes to
-access the serial ports. The simplest method is to use the stallion utility
-"mkdevnods" script. It will automatically create all possible device entries
-required for all 4 boards. This will create the normal serial port devices as
-/dev/ttyE# where # is the port number starting from 0. A bank of 64 minor
-device numbers is allocated to each board, so the first port on the second
-board is port 64, etc. A set of callout type devices is also created. They
-are created as the devices /dev/cue# where # is the same as for the ttyE
-devices.
+access the serial ports. Use the supplied "mkdevnods" script to automatically
+create all required device entries for one board. This will create the normal
+serial port devices as /dev/ttyE# where # is the port number starting from 0.
+A set of callout type devices is also created. They are created as the devices
+/dev/cue# where # is the same as for the ttyE devices.
+
+A bank of 64 minor device numbers is allocated to each board. To create
+device nodes for ports on multiple boards supply a number of boards argument
+to the "mkdevnods" script. For example to create nodes for four boards use
+"mkdevnods 4". This means that the first port on the second board is port 64,
+the first port on the third board is 128, etc.
 
 For the most part the Stallion driver tries to emulate the standard PC system
 COM ports and the standard Linux serial driver. The idea is that you should
@@ -214,9 +412,18 @@
 also be used (excepting the ability to auto-configure the I/O and IRQ
 addresses of boards). Higher baud rates are supported in the usual fashion
 through setserial or using the CBAUDEX extensions. Note that the EasyIO and
-EasyConnection (all types) support 57600 and 115200 baud. The older boards
-including ONboard, Brumby and the original Stallion support a maximum baud
-rate of 38400.
+EasyConnection (all types) support 57600 and 115200 baud, and the newer XP
+versions also support 230400 and 460800 baud. The older boards including
+ONboard, Brumby and the original Stallion support a maximum baud rate of
+38400.
+
+This driver should work with anything that works on standard Linux serial
+ports. Having said that, it has been used on at least the following types of
+"things" under Linux:
+    a) standard dumb terminals (using agetty, getty)
+    b) serial mice (under X)
+    c) modems (using cu, uucp, minicom, seyon, uugetty)
+    d) slip and ppp connections
 
 If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO
 by Greg Hankins. It will explain everything you need to know!
@@ -225,6 +432,11 @@
 
 4. NOTES
 
+The major device numbers used by this driver are conformant with the Linux
+Device Registry, so they shouldn't clash with any other devices. Also the
+device naming scheme is the "standard" used by most Linux serial port
+devices.
+
 You can use both drivers at once if you have a mix of board types installed
 in a system. However to do this you will need to change the major numbers
 used by one of the drivers. Currently both drivers use major numbers 24, 25
@@ -234,20 +446,17 @@
 major numbers 60, 61 and 62. You will also need to create device nodes with
 different names for the ports, for example ttyF# and cuf#.
 
-The original Stallion board is no longer supported by Stallion Technologies.
-Although it is known to work with the istallion driver.
-
 Finding a free physical memory address range can be a problem. The older
 boards like the Stallion and ONboard need large areas (64K or even 128K), so
 they can be very difficult to get into a system. If you have 16 Mb of RAM
 then you have no choice but to put them somewhere in the 640K -> 1Mb range.
 ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some
-systems. If you have an original Stallion board, "V4.0" or Rev.O, then you
-need a 64K memory address space, so again 0xd0000 and 0xe0000 are good.
-Older Stallion boards are a much bigger problem. They need 128K of address
-space and must be on a 128K boundary. If you don't have a VGA card then
-0xc0000 might be usable - there is really no other place you can put them
-below 1Mb.
+systems. If you have an original Stallion board, "V4.0" or Rev.O,
+then you need a 64K memory address space, so again 0xd0000 and 0xe0000 are
+good. Older Stallion boards are a much bigger problem. They need 128K of
+address space and must be on a 128K boundary. If you don't have a VGA card
+then 0xc0000 might be usable - there is really no other place you can put
+them below 1Mb.
 
 Both the ONboard and old Stallion boards can use higher memory addresses as
 well, but you must have less than 16Mb of RAM to be able to use them. Usual
@@ -265,19 +474,48 @@
 ranges is the best option. Typically the 2Gb range is convenient for them,
 and gets them well out of the way.
 
+There is a new utility program included called "stlstty". Most people
+will not need to use this. If you have an ONboard/16 which has partial
+signals on the upper 12 ports then this program can be used to set the
+upper ports to have modem control instead of hardware flow control. Use
+the "mapcts maprts" flag options to this utility on the port(s) that you
+wish to do this mapping on, eg
+    ./stlstty maprts mapcts < /dev/cue0
+This enables RTS to act like DTR and CTS to act like DCD on the specified
+port.
+
 The ports of the EasyIO-8M board do not have DCD or DTR signals. So these
-ports cannot be used as real modem devices. Generally, when using these
+ports cannot be used as real modem devices. Generally when using these
 ports you should only use the cueX devices.
 
-The driver utility package contains a couple of very useful programs. One 
-is a serial port statistics collection and display program - very handy
-for solving serial port problems. The other is an extended option setting
-program that works with the intelligent boards.
+There is another new utility in this package that reports statistics on
+the serial ports. You will need to have the curses libray installed on
+your system to build it.
+
+To build the statistics display program type:
+    make stlstats
+Once compiled simply run it (you will need to be root) and it will display
+a port sumary for the first board and panel installed. Use the digits to
+select different board numbers, or 'n' to cycle through the panels on a
+board. To look at detailed port information then hit 'p', that will display
+detailed port 0 information. Use the digits and letters 'a' through 'f' to
+select the different ports (on this board and panel).
+
+
+
+5. ACKNOWLEDGEMENTS
+
+This driver is loosely based on code written by Theodore T'so, Linus
+Torvalds, and others, so a big thanks to them all.
 
 
 
-5. DISCLAIMER
+6. DISCLAIMER
 
-I do not speak for Stallion Technologies in any capacity, officially or
-unofficially.
+The information contained in this document is believed to be accurate and
+reliable. However, no responsibility is assumed by Stallion Technologies
+Pty. Ltd. for its use, nor any infringements of patents or other rights
+of third parties resulting from its use. Stallion Technologies reserves
+the right to modify the design of its products and will endeavour to change
+the information in manuals and accompanying documentation accordingly.
 
diff -u --recursive --new-file v2.0.34/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.0.34/linux/drivers/char/apm_bios.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/char/apm_bios.c	Mon Jul 13 13:47:29 1998
@@ -36,7 +36,7 @@
  *         Linux 1.3.85
  *    1.1: support user-space standby and suspend, power off after system
  *         halted, Linux 1.3.98
- *    1.2: When resetting RTC after resume, take care so that the the time
+ *    1.2: When resetting RTC after resume, take care so that the time
  *         is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
  *         <jtoth@princeton.edu>); improve interaction between
  *         screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
diff -u --recursive --new-file v2.0.34/linux/drivers/char/baycom.c linux/drivers/char/baycom.c
--- v2.0.34/linux/drivers/char/baycom.c	Mon Jul  8 00:21:45 1996
+++ linux/drivers/char/baycom.c	Mon Jul 13 13:47:29 1998
@@ -1,2327 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	baycom.c  -- baycom ser12 and par96 radio modem driver.
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	This program is free software; you can redistribute it and/or modify
- *	it under the terms of the GNU General Public License as published by
- *	the Free Software Foundation; either version 2 of the License, or
- *	(at your option) any later version.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  Supported modems
- *
- *  ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
- *         of a modulator/demodulator chip, usually a TI TCM3105. The computer
- *         is responsible for regenerating the receiver bit clock, as well as
- *         for handling the HDLC protocol. The modem connects to a serial port,
- *         hence the name. Since the serial port is not used as an async serial
- *         port, the kernel driver for serial ports cannot be used, and this
- *         driver only supports standard serial hardware (8250, 16450, 16550)
- *  
- *  par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
- *         The modem does all the filtering and regenerates the receiver clock.
- *         Data is transferred from and to the PC via a shift register.
- *         The shift register is filled with 16 bits and an interrupt is
- *         signalled. The PC then empties the shift register in a burst. This
- *         modem connects to the parallel port, hence the name. The modem
- *         leaves the implementation of the HDLC protocol and the scrambler
- *         polynomial to the PC.
- *  
- *  par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
- *         modem is protocol compatible to par96, but uses only three low
- *         power ICs and can therefore be fed from the parallel port and
- *         does not require an additional power supply.
- *
- *
- *  Command line options (insmod command line)
- * 
- *  major    major number the driver should use; default 60 
- *  modem    modem type of the first channel (minor 0); 1=ser12,
- *           2=par96/par97, any other value invalid
- *  iobase   base address of the port; common values are for ser12 0x3f8,
- *           0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc
- *  irq      interrupt line of the port; common values are for ser12 3,4
- *           and for par96/par97 7
- *  options  0=use hardware DCD, 1=use software DCD
- * 
- *
- *  History:
- *   0.1  03.05.96  Renamed from ser12 0.5 and added support for par96
- *                  Various resource allocation cleanups
- *   0.2  12.05.96  Changed major to allocated 51. Integrated into kernel
- *                  source tree
- *   0.3  04.06.96  Major bug fixed (forgot to wake up after write) which
- *                  interestingly manifested only with kernel ax25
- *                  (the slip line discipline)
- *                  introduced bottom half and tq_baycom
- *                  HDLC processing now done with interrupts on
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/version.h>
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <asm/segment.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/interrupt.h>
-#include <linux/tqueue.h>
-#include <linux/baycom.h>
-
-/* --------------------------------------------------------------------- */
-
-#define BAYCOM_TYPE_NORMAL 0		/* not used */
-#define TTY_DRIVER_TYPE_BAYCOM 6
-
-/*
- * ser12 options:
- * BAYCOM_OPTIONS_SOFTDCD: if undefined, you must use the transmitters
- * hardware carrier detect circuitry, the driver will report DCD as soon as
- * there are transitions on the input line. Advantage: lower interrupt load
- * on the system. Disadvantage: slower, since hardware carrier detect
- * circuitry is usually slow.
- */
-
-#define BUFLEN_RX 8192
-#define BUFLEN_TX 8192
-
-#define NR_PORTS 4
-
-#define KISS_VERBOSE
-
-#define BAYCOM_MAGIC 0x3105bac0
-
-/* --------------------------------------------------------------------- */
-
-/*
- * user settable parameters (from the command line)
- */
-#ifndef MODULE
-static
-#endif /* MODULE */
-int major = BAYCOM_MAJOR;
-
-/* --------------------------------------------------------------------- */
-
-static struct tty_struct *baycom_table[NR_PORTS];
-static struct termios *baycom_termios[NR_PORTS];
-static struct termios *baycom_termios_locked[NR_PORTS];
-
-static int baycom_refcount;
-
-static struct tty_driver baycom_driver;
-
-static struct {
-	int modem, iobase, irq, options;
-} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, };
-
-/* --------------------------------------------------------------------- */
-
-#define RBR(iobase) (iobase+0)
-#define THR(iobase) (iobase+0)
-#define IER(iobase) (iobase+1)
-#define IIR(iobase) (iobase+2)
-#define FCR(iobase) (iobase+2)
-#define LCR(iobase) (iobase+3)
-#define MCR(iobase) (iobase+4)
-#define LSR(iobase) (iobase+5)
-#define MSR(iobase) (iobase+6)
-#define SCR(iobase) (iobase+7)
-#define DLL(iobase) (iobase+0)
-#define DLM(iobase) (iobase+1)
-
-#define SER12_EXTENT 8
-
-#define LPT_DATA(iobase)    (iobase+0)
-#define LPT_STATUS(iobase)  (iobase+1)
-#define LPT_CONTROL(iobase) (iobase+2)
-#define LPT_IRQ_ENABLE      0x10
-#define PAR96_BURSTBITS 16
-#define PAR96_BURST     4
-#define PAR96_PTT       2
-#define PAR96_TXBIT     1
-#define PAR96_ACK       0x40
-#define PAR96_RXBIT     0x20
-#define PAR96_DCD       0x10
-#define PAR97_POWER     0xf8
-
-#define PAR96_EXTENT 3
-
-/* ---------------------------------------------------------------------- */
-
-struct access_params {
-	int tx_delay;
-	int tx_tail;
-	int slottime;
-	int ppersist;
-	int fulldup;
-};
-
-struct hdlc_state_rx {
-	int rx_state;	/* 0 = sync hunt, != 0 receiving */
-	unsigned int bitstream;
-	unsigned int bitbuf;
-	int numbits;
-	unsigned int shreg1, shreg2;
-
-	int len;
-	unsigned char *bp;
-	unsigned char buffer[BAYCOM_MAXFLEN+2];	   /* make room for CRC */
-};
-
-struct hdlc_state_tx {
-	/*
-	 * 0 = send flags
-	 * 1 = send txtail (flags)
-	 * 2 = send packet
-	 */
-	int tx_state;	
-	int numflags;
-	unsigned int bitstream;
-	unsigned int current_byte;
-	unsigned char ptt;
-
-	unsigned int bitbuf;
-	int numbits;
-	unsigned int shreg1, shreg2;
-
-	int len;
-	unsigned char *bp;
-	unsigned char buffer[BAYCOM_MAXFLEN+2];		/* make room for CRC */
-};
-
-struct modem_state_ser12 {
-	unsigned char last_sample;
-	unsigned char interm_sample;
-	unsigned int bit_pll;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-	unsigned char last_rxbit;
-	unsigned char tx_bit;
-};
-
-struct modem_state_par96 {
-	int dcd_count;
-	unsigned int dcd_shreg;
-	unsigned long descram;
-	unsigned long scram;
-};
-
-struct modem_state {
-	unsigned char dcd;
-	short arb_divider;
-	unsigned char flags;
-	struct modem_state_ser12 ser12;
-	struct modem_state_par96 par96;
-};
-
-struct packet_buffer {
-	unsigned int rd;
-	unsigned int wr;
-	
-	unsigned int buflen;
-	unsigned char *buffer;
-};
-
-struct packet_hdr {
-	unsigned int next;
-	unsigned int len;
-	/* packet following */
-};
-
-#ifdef BAYCOM_DEBUG
-struct bit_buffer {
-	unsigned int rd;
-	unsigned int wr;
-	unsigned int shreg;
-	unsigned char buffer[64];
-};
-
-struct debug_vals {
-	unsigned long last_jiffies;
-	unsigned cur_intcnt;
-	unsigned last_intcnt;
-	int cur_pllcorr;
-	int last_pllcorr;
-};
-#endif /* BAYCOM_DEBUG */
-
-struct kiss_decode {
-	unsigned char dec_state; /* 0 = hunt FEND */
-	unsigned char escaped;
-	unsigned char pkt_buf[BAYCOM_MAXFLEN+1];
-	unsigned int wr;
-};
-
-/* ---------------------------------------------------------------------- */
-
-struct baycom_state {
-	int magic;
-
-	unsigned char modem_type;
-
-	unsigned int iobase;
-	unsigned int irq;
-	unsigned int options;
-
-	int opened;
-	struct tty_struct *tty;
-
-#ifdef BAYCOM_USE_BH
-	struct tq_struct tq_receiver, tq_transmitter, tq_arbitrate;
-#endif /* BAYCOM_USE_BH */
-
-	struct packet_buffer rx_buf;
-	struct packet_buffer tx_buf;
-
-	struct access_params ch_params;
-
-	struct hdlc_state_rx hdlc_rx;
-	struct hdlc_state_tx hdlc_tx;
-
-	int calibrate;
-
-	struct modem_state modem;
-
-#ifdef BAYCOM_DEBUG
-	struct bit_buffer bitbuf_channel;
-	struct bit_buffer bitbuf_hdlc;
-	
-	struct debug_vals debug_vals;
-#endif /* BAYCOM_DEBUG */
-
-	struct kiss_decode kiss_decode;
-
-	struct baycom_statistics stat;
-};
-
-/* --------------------------------------------------------------------- */
-
-struct baycom_state baycom_state[NR_PORTS];
-
-#ifdef BAYCOM_USE_BH
-DECLARE_TASK_QUEUE(tq_baycom);
-#endif /* BAYCOM_USE_BH */
-
-/* --------------------------------------------------------------------- */
-
-/*
- * the CRC routines are stolen from WAMPES
- * by Dieter Deyke
- */
-
-static const unsigned short crc_ccitt_table[] = {
-	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
-};
-
-/*---------------------------------------------------------------------------*/
-
-static inline void append_crc_ccitt(unsigned char *buffer, int len)
-{
- 	unsigned int crc = 0xffff;
-
-	for (;len>0;len--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
-	crc ^= 0xffff;
-	*buffer++ = crc;
-	*buffer++ = crc >> 8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static inline int check_crc_ccitt(const unsigned char *buf,int cnt)
-{
-	unsigned int crc = 0xffff;
-
-	for (; cnt > 0; cnt--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
-	return (crc & 0xffff) == 0xf0b8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-#if 0
-static int calc_crc_ccitt(const unsigned char *buf,int cnt)
-{
-	unsigned int crc = 0xffff;
-
-	for (; cnt > 0; cnt--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
-	crc ^= 0xffff;
-	return (crc & 0xffff);
-}
-#endif
-
-/* ---------------------------------------------------------------------- */
-
-static int store_packet(struct packet_buffer *buf, unsigned char *data,
-	char from_user, unsigned int len)
-{
-	unsigned int free;
-	struct packet_hdr *hdr;
-	unsigned int needed = sizeof(struct packet_hdr)+len;
-	
-	free = buf->rd-buf->wr;
-	if(buf->rd <= buf->wr) {
-		free = buf->buflen - buf->wr;
-		if((free < needed) && (buf->rd >= needed)) {
-			hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
-			hdr->next = 0;
-			hdr->len = 0;
-			buf->wr = 0;
-			free = buf->rd;
-		}
-	}
-	if(free < needed) return 0;		/* buffer overrun */
-	hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
-	if (from_user) 
-		memcpy_fromfs(hdr+1,data,len);
-	else
-		memcpy(hdr+1,data,len);
-	hdr->len = len;
-	hdr->next = buf->wr+needed;
-	if (hdr->next + sizeof(struct packet_hdr) >= buf->buflen)
-		hdr->next = 0;
-	buf->wr = hdr->next;
-	return 1;
-}
-	
-/* ---------------------------------------------------------------------- */
-
-static void get_packet(struct packet_buffer *buf, unsigned char **data,
-	unsigned int *len)
-{
-	struct packet_hdr *hdr;
-	
-	*data = NULL;
-	*len = 0;
-	if (buf->rd == buf->wr)
-		return;
-	hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
-	while (!(hdr->len)) {
-		buf->rd = hdr->next;
-		if (buf->rd == buf->wr)
-			return;
-		hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
-	}
-	*data = (unsigned char *)(hdr+1);
-	*len = hdr->len;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void ack_packet(struct packet_buffer *buf)
-{
-	struct packet_hdr *hdr;
-	
-	if (buf->rd == buf->wr)
-		return;
-	hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
-	buf->rd = hdr->next;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int store_kiss_packet(struct packet_buffer *buf, unsigned char *data,
-	unsigned int len)
-{
-	unsigned char *bp = data;
-	int ln = len;
-	/*
-	 * variables of buf
-	 */
-	unsigned int rd;
-	unsigned int wr;
-	unsigned int buflen;
-	unsigned char *buffer;
-
-	if (!len || !data || !buf)
-		return 0;
-	buflen = buf->buflen;
-	rd = buf->rd;
-	wr = buf->wr;
-	buffer = buf->buffer;
-	
-#define ADD_CHAR(c) {\
-		buffer[wr++] = c;\
-		if (wr >= buflen) wr = 0;\
-		if (wr == rd) return 0;\
-	}
-#define ADD_KISSCHAR(c) {\
-		if (((c) & 0xff) == KISS_FEND) {\
-			ADD_CHAR(KISS_FESC);\
-			ADD_CHAR(KISS_TFEND);\
-		} else if (((c) & 0xff) == KISS_FESC) {\
-			ADD_CHAR(KISS_FESC);\
-			ADD_CHAR(KISS_TFESC);\
-		} else {\
-			ADD_CHAR(c);\
-		}\
-	}
-
-	ADD_CHAR(KISS_FEND);
-	ADD_KISSCHAR(KISS_CMD_DATA);
-	for(; ln > 0; ln--,bp++) {
-		ADD_KISSCHAR(*bp);
-	}
-	ADD_CHAR(KISS_FEND);
-	buf->wr = wr;
-#undef ADD_CHAR
-#undef ADD_KISSCHAR
-	return 1;
-}
-
-/* ---------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit)
-{
-	unsigned char new;
-
-	if (!buf) return;
-	new = buf->shreg & 1;
-	buf->shreg >>= 1;
-	if (bit)
-		buf->shreg |= 0x80;
-	if (new) {
-		buf->buffer[buf->wr] = buf->shreg;
-		buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-		buf->shreg = 0x80;
-	}
-}
-
-static inline void add_bitbuffer_word(struct bit_buffer * buf, 
-				      unsigned int bits)
-{
-	buf->buffer[buf->wr] = bits & 0xff;
-	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-	buf->buffer[buf->wr] = (bits >> 8) & 0xff;
-	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-
-}
-#endif /* BAYCOM_DEBUG */
-
-/* ---------------------------------------------------------------------- */
-
-static inline unsigned int tenms_to_2flags(struct baycom_state *bc, 
-					  unsigned int tenms)
-{
-	switch (bc->modem_type) {
-	case BAYCOM_MODEM_SER12:
-		return tenms * 3 / 4;
-	case BAYCOM_MODEM_PAR96:
-		return tenms * 6;
-	default:
-		return 0;
-	}
-}
-
-/* ---------------------------------------------------------------------- */
-/*
- * The HDLC routines
- */
-
-static inline int hdlc_rx_add_bytes(struct baycom_state *bc, 
-				    unsigned int bits, int num)
-{
-	int added = 0;
-	while (bc->hdlc_rx.rx_state && num >= 8) {
-		if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) {
-			bc->hdlc_rx.rx_state = 0;
-			return 0;
-		}
-		*bc->hdlc_rx.bp++ = bits >> (32-num);
-		bc->hdlc_rx.len++;
-		num -= 8;
-		added += 8;
-	}
-	return added;
-}
-
-static inline void hdlc_rx_flag(struct baycom_state *bc)
-{
-	if (bc->hdlc_rx.len < 4) 
-		return;
-	if (!check_crc_ccitt(bc->hdlc_rx.buffer, bc->hdlc_rx.len)) 
-		return;
-       	bc->stat.rx_packets++;
-	if (!store_kiss_packet(&bc->rx_buf,
-			       bc->hdlc_rx.buffer,
-			       bc->hdlc_rx.len-2))
-		bc->stat.rx_bufferoverrun++;
-}
-
-static void hdlc_rx_word(struct baycom_state *bc, unsigned int word)
-{
-	int i;
-	unsigned int mask1, mask2, mask3, mask4, mask5, mask6;
-	
-	if (!bc) return;
-
-	word &= 0xffff;
-#ifdef BAYCOM_DEBUG
-	add_bitbuffer_word(&bc->bitbuf_hdlc, word);
-#endif /* BAYCOM_DEBUG */
-       	bc->hdlc_rx.bitstream >>= 16;
-	bc->hdlc_rx.bitstream |= word << 16;
-	bc->hdlc_rx.bitbuf >>= 16;
-	bc->hdlc_rx.bitbuf |= word << 16;
-	bc->hdlc_rx.numbits += 16;
-	for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
-	    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
-	    i >= 0; 
-	    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
-	    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
-		if ((bc->hdlc_rx.bitstream & mask1) == mask1)
-			bc->hdlc_rx.rx_state = 0; /* abort received */
-		else if ((bc->hdlc_rx.bitstream & mask2) == mask3) {
-			/* flag received */
-			if (bc->hdlc_rx.rx_state) {
-				hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf << 
-						  (8 + i), bc->hdlc_rx.numbits
-						  - 8 - i);
-				hdlc_rx_flag(bc);
-			}
-			bc->hdlc_rx.len = 0;
-			bc->hdlc_rx.bp = bc->hdlc_rx.buffer;
-			bc->hdlc_rx.rx_state = 1;
-			bc->hdlc_rx.numbits = i;
-		} else if ((bc->hdlc_rx.bitstream & mask4) == mask5) {
-			/* stuffed bit */
-			bc->hdlc_rx.numbits--;
-			bc->hdlc_rx.bitbuf = (bc->hdlc_rx.bitbuf & (~mask6)) |
-				((bc->hdlc_rx.bitbuf & mask6) << 1);
-		}
-	}
-	bc->hdlc_rx.numbits -= hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf,
-						 bc->hdlc_rx.numbits);
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned int hdlc_tx_word(struct baycom_state *bc)
-{
-	unsigned int mask1, mask2, mask3;
-	int i;
-
-	if (!bc || !bc->hdlc_tx.ptt)
-		return 0;
-	for (;;) {
-		if (bc->hdlc_tx.numbits >= 16) {
-			unsigned int ret = bc->hdlc_tx.bitbuf & 0xffff;
-			bc->hdlc_tx.bitbuf >>= 16;
-			bc->hdlc_tx.numbits -= 16;
-			return ret;
-		}
-		switch (bc->hdlc_tx.tx_state) {
-		default:
-			bc->hdlc_tx.ptt = 0;
-			bc->hdlc_tx.tx_state = 0;
-			return 0;
-		case 0:
-		case 1:
-			if (bc->hdlc_tx.numflags) {
-				bc->hdlc_tx.numflags--;
-				bc->hdlc_tx.bitbuf |= 
-					0x7e7e << bc->hdlc_tx.numbits;
-				bc->hdlc_tx.numbits += 16;
-				break;
-			}
-			if (bc->hdlc_tx.tx_state == 1) {
-				bc->hdlc_tx.ptt = 0;
-				return 0;
-			}
-			get_packet(&bc->tx_buf, &bc->hdlc_tx.bp,
-				   &bc->hdlc_tx.len);
-			if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) {
-				bc->hdlc_tx.tx_state = 1;
-				bc->hdlc_tx.numflags = tenms_to_2flags
-					(bc, bc->ch_params.tx_tail);
-				break;
-			}
-			if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) {
-				bc->hdlc_tx.tx_state = 0;
-				bc->hdlc_tx.numflags = 1;
-				ack_packet(&bc->tx_buf);
-				break;
-			}
-			memcpy(bc->hdlc_tx.buffer, bc->hdlc_tx.bp, 
-			       bc->hdlc_tx.len);
-			ack_packet(&bc->tx_buf);
-			bc->hdlc_tx.bp = bc->hdlc_tx.buffer;
-			append_crc_ccitt(bc->hdlc_tx.buffer, bc->hdlc_tx.len);
-			/* the appended CRC */
-			bc->hdlc_tx.len += 2; 
-			bc->hdlc_tx.tx_state = 2;
-			bc->hdlc_tx.bitstream = 0;
-			bc->stat.tx_packets++;
-			break;
-		case 2:
-			if (!bc->hdlc_tx.len) {
-				bc->hdlc_tx.tx_state = 0;
-				bc->hdlc_tx.numflags = 1;
-				break;
-			}
-			bc->hdlc_tx.len--;
-			bc->hdlc_tx.bitbuf |= *bc->hdlc_tx.bp <<
-				bc->hdlc_tx.numbits;
-			bc->hdlc_tx.bitstream >>= 8;
-			bc->hdlc_tx.bitstream |= (*bc->hdlc_tx.bp++) << 16;
-			mask1 = 0x1f000;
-			mask2 = 0x10000;
-			mask3 = 0xffffffff >> (31-bc->hdlc_tx.numbits);
-			bc->hdlc_tx.numbits += 8;
-			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
-			    mask3 = (mask3 << 1) | 1) {
-				if ((bc->hdlc_tx.bitstream & mask1) != mask1) 
-					continue;
-				bc->hdlc_tx.bitstream &= ~mask2;
-				bc->hdlc_tx.bitbuf = 
-					(bc->hdlc_tx.bitbuf & mask3) |
-						((bc->hdlc_tx.bitbuf & 
-						 (~mask3)) << 1);
-				bc->hdlc_tx.numbits++;
-				mask3 = (mask3 << 1) | 1;
-			}
-			break;
-		}
-	}
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned short random_seed;
-
-static inline unsigned short random_num(void)
-{
-	random_seed = 28629 * random_seed + 157;
-	return random_seed;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static inline void tx_arbitrate(struct baycom_state *bc)
-{
-	unsigned char *bp;
-	unsigned int len;
-	
-	if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd)
-		return;
-	get_packet(&bc->tx_buf, &bp, &len);
-	if (!bp || !len)
-		return;
-	
-	if (!bc->ch_params.fulldup) {
-		if ((random_num() % 256) > bc->ch_params.ppersist)
-			return;
-	}
-	bc->hdlc_tx.tx_state = 0;
-	bc->hdlc_tx.numflags = tenms_to_2flags(bc, bc->ch_params.tx_delay);
-	bc->hdlc_tx.numbits = bc->hdlc_tx.bitbuf = bc->hdlc_tx.bitstream = 0;
-	bc->hdlc_tx.ptt = 1;
-	bc->stat.ptt_keyed++;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static void inline baycom_int_freq(struct baycom_state *bc)
-{
-	unsigned long cur_jiffies = jiffies;
-	/* 
-	 * measure the interrupt frequency
-	 */
-	bc->debug_vals.cur_intcnt++;
-	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
-		bc->debug_vals.last_jiffies = cur_jiffies;
-		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
-		bc->debug_vals.cur_intcnt = 0;
-		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
-		bc->debug_vals.cur_pllcorr = 0;
-	}
-}
-#endif /* BAYCOM_DEBUG */
-
-/* --------------------------------------------------------------------- */
-
-static inline void rx_chars_to_flip(struct baycom_state *bc) 
-{
-	int flip_free;
-	unsigned int cnt;
-	unsigned int new_rd;
-	unsigned long flags;
-
-	if ((!bc) || (!bc->tty) || (bc->tty->flip.count >= TTY_FLIPBUF_SIZE) ||
-	    (bc->rx_buf.rd == bc->rx_buf.wr) || 
-	    (!bc->tty->flip.char_buf_ptr) ||
-	    (!bc->tty->flip.flag_buf_ptr))
-		return;
-	for(;;) {
-		flip_free = TTY_FLIPBUF_SIZE - bc->tty->flip.count;
-		if (bc->rx_buf.rd <= bc->rx_buf.wr)
-			cnt = bc->rx_buf.wr - bc->rx_buf.rd;
-		else
-			cnt = bc->rx_buf.buflen - bc->rx_buf.rd;
-		if ((flip_free <= 0) || (!cnt)) {
-			tty_schedule_flip(bc->tty);
-			return;
-		}
-		if (cnt > flip_free)
-			cnt = flip_free;
-		save_flags(flags); cli();
-		memcpy(bc->tty->flip.char_buf_ptr, bc->rx_buf.buffer+bc->rx_buf.rd, cnt);
-		memset(bc->tty->flip.flag_buf_ptr, TTY_NORMAL, cnt);
-		bc->tty->flip.count += cnt;
-		bc->tty->flip.char_buf_ptr += cnt;
-		bc->tty->flip.flag_buf_ptr += cnt;
-		restore_flags(flags);
-		new_rd = bc->rx_buf.rd+cnt;
-		if (new_rd >= bc->rx_buf.buflen)
-			new_rd -= bc->rx_buf.buflen;
-		bc->rx_buf.rd = new_rd;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== SER12 specific routines =========================
- */
-
-static void inline ser12_set_divisor(struct baycom_state *bc, 
-				     unsigned char divisor)
-{
-	outb(0x81, LCR(bc->iobase));	/* DLAB = 1 */
-	outb(divisor, DLL(bc->iobase));
-	outb(0, DLM(bc->iobase));
-	outb(0x01, LCR(bc->iobase));	/* word length = 6 */
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * must call the TX arbitrator every 10ms
- */
-#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
-			       36 : 24)
-#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
-				240 : 12)
-
-static void baycom_ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev_id;
-	unsigned char cur_s;
-	
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	/*
-	 * make sure the next interrupt is generated;
-	 * 0 must be used to power the modem; the modem draws its
-	 * power from the TxD line
-	 */	
-	outb(0x00, THR(bc->iobase));
-	rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
-	baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
-	/*
-	 * check if transmitter active
-	 */
-	if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
-		ser12_set_divisor(bc, 12); /* one interrupt per channel bit */
-		/*
-		 * first output the last bit (!) then call HDLC transmitter,
-		 * since this may take quite long
-		 */
-		outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase));
-		if (bc->hdlc_tx.shreg1 <= 1) {
-			if (bc->calibrate > 0) {
-				bc->hdlc_tx.shreg1 = 0x10000;
-				bc->calibrate--;
-			} else {
-#ifdef BAYCOM_USE_BH
-				bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
-				bc->hdlc_tx.shreg2 = 0;
-				queue_task_irq_off(&bc->tq_transmitter, 
-						   &tq_baycom);
-				mark_bh(BAYCOM_BH);
-#ifdef HDLC_LOOPBACK
-				bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
-				queue_task_irq_off(&bc->tq_receiver, 
-						   &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#else /* BAYCOM_USE_BH */
-				bc->hdlc_tx.shreg1 = hdlc_tx_word(bc) 
-					| 0x10000;
-#ifdef HDLC_LOOPBACK
-				hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
-#endif /* HDLC_LOOPBACK */
-#endif /* BAYCOM_USE_BH */
-			}	
-		}
-		if (!(bc->hdlc_tx.shreg1 & 1))
-			bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit;
-		bc->hdlc_tx.shreg1 >>= 1;
-		return;
-	}
-	/*
-	 * do demodulator
-	 */
-	outb(0x0d, MCR(bc->iobase));			/* transmitter off */
-	cur_s = inb(MSR(bc->iobase)) & 0x10;	/* the CTS line */
-#ifdef BAYCOM_DEBUG
-	add_bitbuffer(&bc->bitbuf_channel, cur_s);
-#endif /* BAYCOM_DEBUG */
-	bc->modem.ser12.dcd_shreg <<= 1;
-	if(cur_s != bc->modem.ser12.last_sample) {
-		bc->modem.ser12.dcd_shreg |= 1;
-
-		if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-			unsigned int dcdspos, dcdsneg;
-
-			dcdspos = dcdsneg = 0;
-			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
-			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
-				dcdspos += 2;
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
-
-			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
-		} else
-			bc->modem.ser12.dcd_sum0--;
-	}
-	bc->modem.ser12.last_sample = cur_s;
-	if(!bc->modem.ser12.dcd_time) {
-		bc->modem.dcd = (bc->modem.ser12.dcd_sum0 + 
-				 bc->modem.ser12.dcd_sum1 +
-				 bc->modem.ser12.dcd_sum2) < 0;
-		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
-		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
-		/* offset to ensure DCD off on silent input */
-		bc->modem.ser12.dcd_sum0 = 2;
-		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
-	}
-	bc->modem.ser12.dcd_time--;
-	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-		/*
-		 * PLL code for the improved software DCD algorithm
-		 */
-		if (bc->modem.ser12.interm_sample) {
-			/*
-			 * intermediate sample; set timing correction to normal
-			 */
-			ser12_set_divisor(bc, 4);
-		} else {
-			/*
-			 * do PLL correction and call HDLC receiver
-			 */
-			switch (bc->modem.ser12.dcd_shreg & 7) {
-			case 1: /* transition too late */
-				ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
-				break;
-			case 4:	/* transition too early */
-				ser12_set_divisor(bc, 3);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
-				break;
-			default:
-				ser12_set_divisor(bc, 4);
-				break;
-			}
-			bc->hdlc_rx.shreg1 >>= 1;
-			if (bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit)
-				bc->hdlc_rx.shreg1 |= 0x10000;
-			bc->modem.ser12.last_rxbit = 
-				bc->modem.ser12.last_sample;
-		}
-		if (++bc->modem.ser12.interm_sample >= 3)
-			bc->modem.ser12.interm_sample = 0;		
-	} else {
-		/*
-		 * PLL algorithm for the hardware squelch DCD algorithm
-		 */
-		if (bc->modem.ser12.interm_sample) {
-			/*
-			 * intermediate sample; set timing correction to normal
-			 */
-			ser12_set_divisor(bc, 6);
-		} else {
-			/*
-			 * do PLL correction and call HDLC receiver
-			 */
-			switch (bc->modem.ser12.dcd_shreg & 3) {
-			case 1: /* transition too late */
-				ser12_set_divisor(bc, 7);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
-				break;
-			case 2:	/* transition too early */
-				ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
-				break;
-			default:
-				ser12_set_divisor(bc, 6);
-				break;
-			}
-			bc->hdlc_rx.shreg1 >>= 1;
-			if (bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit)
-				bc->hdlc_rx.shreg1 |= 0x10000;
-			bc->modem.ser12.last_rxbit = 
-				bc->modem.ser12.last_sample;
-		}
-		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
-	}
-	if (bc->hdlc_rx.shreg1 & 1) {
-#ifdef BAYCOM_USE_BH
-		bc->hdlc_rx.shreg2 = (bc->hdlc_rx.shreg1 >> 1) | 0x10000;
-		queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-		mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-		hdlc_rx_word(bc, bc->hdlc_rx.shreg1 >> 1);
-#endif /* BAYCOM_USE_BH */
-		bc->hdlc_rx.shreg1 = 0x10000;
-	}
-	if (--bc->modem.arb_divider <= 0) {
-#ifdef BAYCOM_USE_BH
-		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
-		mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-		tx_arbitrate(bc);
-#endif /* BAYCOM_USE_BH */
-		bc->modem.arb_divider = bc->ch_params.slottime * 
-			SER12_ARB_DIVIDER(bc);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
-	c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
-	{ "unknown", "8250", "16450", "16550", "16550A" };
-
-static enum uart ser12_check_uart(unsigned int iobase)
-{
-	unsigned char b1,b2,b3;
-	enum uart u;
-	enum uart uart_tab[] =
-		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
-	b1 = inb(MCR(iobase));
-	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
-	b2 = inb(MSR(iobase));
-	outb(0x1a, MCR(iobase));
-	b3 = inb(MSR(iobase)) & 0xf0;
-	outb(b1, MCR(iobase));			/* restore old values */
-	outb(b2, MSR(iobase));
-	if (b3 != 0x90) 
-		return c_uart_unknown;
-	inb(RBR(iobase));
-	inb(RBR(iobase));
-	outb(0x01, FCR(iobase));		/* enable FIFOs */
-	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
-	if (u == c_uart_16450) {
-		outb(0x5a, SCR(iobase));
-		b1 = inb(SCR(iobase));
-		outb(0xa5, SCR(iobase));
-		b2 = inb(SCR(iobase));
-		if ((b1 != 0x5a) || (b2 != 0xa5)) 
-			u = c_uart_8250;
-	}
-	return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_allocate_resources(unsigned int iobase, unsigned int irq,
-				    unsigned int options)
-{
-	enum uart u;
-
-	if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
-		return -ENXIO;
-	if (check_region(iobase, SER12_EXTENT))
-		return -EACCES;
-	if ((u = ser12_check_uart(iobase)) == c_uart_unknown)
-		return -EIO;
-	request_region(iobase, SER12_EXTENT, "baycom_ser12");
-	outb(0, FCR(iobase));		/* disable FIFOs */
-	outb(0x0d, MCR(iobase));
-	printk(KERN_INFO "baycom: ser12 at iobase 0x%x irq %u options 0x%x "
-	       "uart %s\n", iobase, irq, options, uart_str[u]);
-	return 0;
-}
-	
-/* --------------------------------------------------------------------- */
-
-static void ser12_deallocate_resources(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
-		return;
-	/*
-	 * disable interrupts
-	 */
-	outb(0, IER(bc->iobase));
-	outb(1, MCR(bc->iobase));
-	/* 
-	 * this should prevent kernel: Trying to free IRQx
-	 * messages
-	 */
-	if (bc->opened > 0)
-		free_irq(bc->irq, bc);
-	release_region(bc->iobase, SER12_EXTENT);
-	bc->modem_type = BAYCOM_MODEM_INVALID;
-	printk(KERN_INFO "baycom: release ser12 at iobase 0x%x irq %u\n",
-	       bc->iobase, bc->irq);
-	bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_on_open(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
-		return -ENXIO;
-	/*
-	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
-	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
-	 * depending on the usage of the software DCD routine
-	 */
-	ser12_set_divisor(bc, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
-	outb(0x0d, MCR(bc->iobase));
-	outb(0, IER(bc->iobase));
-	if (request_irq(bc->irq, baycom_ser12_interrupt, SA_INTERRUPT, 
-			"baycom_ser12", bc))
-		return -EBUSY;
-	/*
-	 * enable transmitter empty interrupt
-	 */
-	outb(2, IER(bc->iobase));  
-	/* 
-	 * the value here serves to power the modem
-	 */     
-	outb(0x00, THR(bc->iobase));
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_on_close(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
-		return;
-	/*
-	 * disable interrupts
-	 */
-	outb(0, IER(bc->iobase));
-	outb(1, MCR(bc->iobase));
-	free_irq(bc->irq, bc);	
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== PAR96 specific routines =========================
- */
-
-#define PAR96_DESCRAM_TAP1 0x20000
-#define PAR96_DESCRAM_TAP2 0x01000
-#define PAR96_DESCRAM_TAP3 0x00001
-
-#define PAR96_DESCRAM_TAPSH1 17
-#define PAR96_DESCRAM_TAPSH2 12
-#define PAR96_DESCRAM_TAPSH3 0
-
-#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
-#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	register struct baycom_state *bc = (struct baycom_state *)dev_id;
-	int i;
-	unsigned int data, descx, mask, mask2;
-	
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-
-	rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
-	baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
-	/*
-	 * check if transmitter active
-	 */
-	if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
-		/*
-		 * first output the last 16 bits (!) then call HDLC
-		 * transmitter, since this may take quite long
-		 * do the differential encoder and the scrambler on the fly
-		 */
-		data = bc->hdlc_tx.shreg1;
-		for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
-			unsigned char val = PAR97_POWER;
-			bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
-						 (bc->modem.par96.scram & 1));
-			if (!(data & 1))
-				bc->modem.par96.scram ^= 1;
-			if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
-				bc->modem.par96.scram ^= 
-					(PAR96_SCRAM_TAPN << 1);
-			if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
-				val |= PAR96_TXBIT;
-			outb(val, LPT_DATA(bc->iobase));
-			outb(val | PAR96_BURST, LPT_DATA(bc->iobase));
-		}
-		if (bc->calibrate > 0) {
-			bc->hdlc_tx.shreg1 = 0x10000;
-			bc->calibrate--;
-		} else {
-#ifdef BAYCOM_USE_BH
-			bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
-			bc->hdlc_tx.shreg2 = 0;
-			queue_task_irq_off(&bc->tq_transmitter, &tq_baycom);
-			mark_bh(BAYCOM_BH);
-#ifdef HDLC_LOOPBACK
-			bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
-			queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#else /* BAYCOM_USE_BH */
-			bc->hdlc_tx.shreg1 = hdlc_tx_word(bc);
-#ifdef HDLC_LOOPBACK
-			hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
-#endif /* HDLC_LOOPBACK */
-#endif /* BAYCOM_USE_BH */
-		}
-		return;
-	}
-	/*
-	 * do receiver; differential decode and descramble on the fly
-	 */
-	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
-		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
-		if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT)
-			bc->modem.par96.descram |= 1;
-		descx = bc->modem.par96.descram ^ 
-			(bc->modem.par96.descram >> 1);
-		/* now the diff decoded data is inverted in descram */
-		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase));
-		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
-			  (descx >> PAR96_DESCRAM_TAPSH2));
-		data >>= 1;
-		if (!(descx & 1))
-			data |= 0x8000;
-		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, 
-		     LPT_DATA(bc->iobase));
-	}
-#ifdef BAYCOM_USE_BH
-	bc->hdlc_rx.shreg2 = bc->hdlc_rx.shreg1;
-	bc->hdlc_rx.shreg1 = data | 0x10000;
-	queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-	mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-	hdlc_rx_word(bc, data);
-#endif /* BAYCOM_USE_BH */
-	/*
-	 * do DCD algorithm
-	 */
-	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
-			| (data << 16);
-		/* search for flags and set the dcd counter appropriately */
-		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; 
-		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
-			if ((bc->modem.par96.dcd_shreg & mask) == mask2)
-				bc->modem.par96.dcd_count = BAYCOM_MAXFLEN+4;
-		/* check for abort/noise sequences */
-		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; 
-		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
-			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
-			    (bc->modem.par96.dcd_count >= 0))
-				bc->modem.par96.dcd_count -= BAYCOM_MAXFLEN-10;
-		/* decrement and set the dcd variable */
-		if (bc->modem.par96.dcd_count >= 0)
-			bc->modem.par96.dcd_count -= 2;
-		bc->modem.dcd = bc->modem.par96.dcd_count > 0;
-	} else {
-		bc->modem.dcd = !!(inb(LPT_STATUS(bc->iobase))
-				   & PAR96_DCD);
-	}
-	if (--bc->modem.arb_divider <= 0) {
-#ifdef BAYCOM_USE_BH
-		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
-		mark_bh(BAYCOM_BH);
-#else /* BAYCOM_USE_BH */
-		tx_arbitrate(bc);
-#endif /* BAYCOM_USE_BH */
-		bc->modem.arb_divider = bc->ch_params.slottime * 6;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_check_lpt(unsigned int iobase)
-{
-	unsigned char b1,b2;
-	int i;
-
-	b1 = inb(LPT_DATA(iobase));
-	b2 = inb(LPT_CONTROL(iobase));
-	outb(0xaa, LPT_DATA(iobase));
-	i = inb(LPT_DATA(iobase)) == 0xaa;
-	outb(0x55, LPT_DATA(iobase));
-	i &= inb(LPT_DATA(iobase)) == 0x55;
-	outb(0x0a, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
-	outb(0x05, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
-	outb(b1, LPT_DATA(iobase));
-	outb(b2, LPT_CONTROL(iobase));
-	return !i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_allocate_resources(unsigned int iobase, unsigned int irq,
-				    unsigned int options)
-{
-	if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
-		return -ENXIO;
-	if (check_region(iobase, PAR96_EXTENT))
-		return -EACCES;
-	if (par96_check_lpt(iobase))
-		return -EIO;
-	request_region(iobase, PAR96_EXTENT, "baycom_par96");
-	outb(0, LPT_CONTROL(iobase));                 /* disable interrupt */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(iobase)); /* switch off PTT */
-	printk(KERN_INFO "baycom: par96 at iobase 0x%x irq %u options 0x%x\n", 
-	       iobase, irq, options);
-	return 0;
-}
-	
-/* --------------------------------------------------------------------- */
-
-static void par96_deallocate_resources(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
-		return;
-	outb(0, LPT_CONTROL(bc->iobase));      /* disable interrupt */
-	outb(PAR96_PTT, LPT_DATA(bc->iobase)); /* switch off PTT */
-	/* 
-	 * this should prevent kernel: Trying to free IRQx
-	 * messages
-	 */
-	if (bc->opened > 0)
-		free_irq(bc->irq, bc);
-	release_region(bc->iobase, PAR96_EXTENT);
-	bc->modem_type = BAYCOM_MODEM_INVALID;
-	printk(KERN_INFO "baycom: release par96 at iobase 0x%x irq %u\n",
-	       bc->iobase, bc->irq);
-	bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_on_open(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
-		return -ENXIO;
-	outb(0, LPT_CONTROL(bc->iobase));      /* disable interrupt */
-	 /* switch off PTT */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
-	if (request_irq(bc->irq, baycom_par96_interrupt, SA_INTERRUPT, 
-			"baycom_par96", bc))
-		return -EBUSY;
-	outb(LPT_IRQ_ENABLE, LPT_CONTROL(bc->iobase));  /* enable interrupt */
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_on_close(struct baycom_state *bc) 
-{
-	if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
-		return;
-	outb(0, LPT_CONTROL(bc->iobase));  /* disable interrupt */
-	/* switch off PTT */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
-	free_irq(bc->irq, bc);	
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== Bottom half (soft interrupt) ====================
- */
-
-#ifdef BAYCOM_USE_BH
-static void bh_receiver(void *private)
-{
-	struct baycom_state *bc = (struct baycom_state *)private;
-	unsigned int temp;
-
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	if (!bc->hdlc_rx.shreg2)
-		return;
-	temp = bc->hdlc_rx.shreg2;
-	bc->hdlc_rx.shreg2 = 0;
-	hdlc_rx_word(bc, temp);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void bh_transmitter(void *private)
-{
-	struct baycom_state *bc = (struct baycom_state *)private;
-
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	if (bc->hdlc_tx.shreg2)
-		return;
-	bc->hdlc_tx.shreg2 = hdlc_tx_word(bc) | 0x10000;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void bh_arbitrate(void *private)
-{
-	struct baycom_state *bc = (struct baycom_state *)private;
-
-	if (!bc || bc->magic != BAYCOM_MAGIC)
-		return;
-	tx_arbitrate(bc);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_bottom_half(void)
-{
-	run_task_queue(&tq_baycom);
-}
-#endif /* BAYCOM_USE_BH */
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== TTY interface routines ==========================
- */
-
-static inline int baycom_paranoia_check(struct baycom_state *bc, 
-					const char *routine)
-{
-	if (!bc || bc->magic != BAYCOM_MAGIC) {
-		printk(KERN_ERR "baycom: bad magic number for baycom struct "
-		       "in routine %s\n", routine);
-		return 1;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * Here the tty driver code starts
- */
-
-static void baycom_put_fend(struct baycom_state *bc)
-{
-	if (bc->kiss_decode.wr <= 0 ||
-	    (bc->kiss_decode.pkt_buf[0] & 0xf0) != 0)
-		return;
-
-	switch (bc->kiss_decode.pkt_buf[0] & 0xf) {
-	case KISS_CMD_DATA:
-		if (bc->kiss_decode.wr <= 8) 
-			break;
-		if (!store_packet(&bc->tx_buf, bc->kiss_decode.pkt_buf+1, 0, 
-				  bc->kiss_decode.wr-1))
-			bc->stat.tx_bufferoverrun++;
-		break;
-
-	case KISS_CMD_TXDELAY:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.tx_delay = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: TX delay = %ums\n", 
-		       bc->ch_params.tx_delay * 10);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_PPERSIST:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: p-persistence = %u\n", 
-		       bc->ch_params.ppersist);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_SLOTTIME:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: slottime = %ums\n", 
-		       bc->ch_params.slottime * 10);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_TXTAIL:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.tx_tail = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: TX tail = %ums\n",
-		       bc->ch_params.tx_tail * 10);
-#endif /* KISS_VERBOSE */
-		break;
-
-	case KISS_CMD_FULLDUP:
-		if (bc->kiss_decode.wr < 2) 
-			break;
-		bc->ch_params.fulldup = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: %s duplex\n", 
-		       bc->ch_params.fulldup ? "full" : "half");
-#endif /* KISS_VERBOSE */
-		break;
-
-	default:
-#ifdef KISS_VERBOSE
-		printk(KERN_INFO "baycom: unhandled KISS packet code %u\n",
-		       bc->kiss_decode.pkt_buf[0] & 0xf);
-#endif /* KISS_VERBOSE */
-		break;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_put_char(struct tty_struct *tty, unsigned char ch)
-{
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return;
-	if (baycom_paranoia_check(bc = tty->driver_data, "put_char"))
-		return;
-		
-	if (ch == KISS_FEND) {
-		baycom_put_fend(bc);
-		bc->kiss_decode.wr = 0;
-		bc->kiss_decode.escaped = 0;
-		bc->kiss_decode.dec_state = 1;
-		return;
-	}
-	if (!bc->kiss_decode.dec_state)
-		return;
-	if (ch == KISS_FESC) {
-		bc->kiss_decode.escaped = 1;
-		return;
-	}
-	if (bc->kiss_decode.wr >= sizeof(bc->kiss_decode.pkt_buf)) {
-		bc->kiss_decode.wr = 0;
-		bc->kiss_decode.dec_state = 0;
-		return;
-	}
-	if (bc->kiss_decode.escaped) {
-		if (ch == KISS_TFEND)
-			bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = 
-				KISS_FEND;
-		else if (ch == KISS_TFESC)
-			bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = 
-				KISS_FESC;
-		else {
-			bc->kiss_decode.wr = 0;
-			bc->kiss_decode.dec_state = 0;
-		}
-		bc->kiss_decode.escaped = 0;
-		return;
-	}
-	bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = ch;
-}
-	
-/* --------------------------------------------------------------------- */
-
-static int baycom_write(struct tty_struct * tty, int from_user,
-	const unsigned char *buf, int count)
-{
-	int c;
-	const unsigned char *bp;
-	struct baycom_state *bc;
-		
-	if (!tty || !buf || count <= 0)
-		return count;
-	
-	if (baycom_paranoia_check(bc = tty->driver_data, "write"))
-		return count; 
-		
-	if (from_user) {
-		for(c = count, bp = buf; c > 0; c--,bp++)
-			baycom_put_char(tty, get_user(bp));
-	} else {
-		for(c = count, bp = buf; c > 0; c--,bp++)
-			baycom_put_char(tty, *bp);
-	}
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-		tty->ldisc.write_wakeup)
-			(tty->ldisc.write_wakeup)(tty);
-	wake_up_interruptible(&tty->write_wait);
-	return count;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_write_room(struct tty_struct *tty)
-{
-	int free;
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return 0;
-	if (baycom_paranoia_check(bc = tty->driver_data, "write_room"))
-		return 0;
-		
-	free = bc->tx_buf.rd - bc->tx_buf.wr;
-	if (free <= 0) {
-		free = bc->tx_buf.buflen - bc->tx_buf.wr;
-		if (free < bc->tx_buf.rd)
-			free = bc->tx_buf.rd;	/* we may fold */
-	}
-
-	return free / 2; /* a rather pessimistic estimate */
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_chars_in_buffer(struct tty_struct *tty)
-{
-	int cnt;
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return 0;
-	if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer"))
-		return 0;
-
-	cnt = bc->tx_buf.wr - bc->tx_buf.rd;
-	if (cnt < 0)
-		cnt += bc->tx_buf.buflen;
-		
-	return cnt;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_flush_buffer(struct tty_struct *tty)
-{
-	struct baycom_state *bc;
-		
-	if (!tty)
-		return;
-	if (baycom_paranoia_check(bc = tty->driver_data, "flush_buffer"))
-		return;
-
-	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-		tty->ldisc.write_wakeup)
-			(tty->ldisc.write_wakeup)(tty);
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void baycom_dealloc_hw(struct baycom_state *bc) 
-{
-	if (!bc || bc->magic != BAYCOM_MAGIC || 
-	    bc->modem_type == BAYCOM_MODEM_INVALID)
-		return;
-	switch(bc->modem_type) {
-	case BAYCOM_MODEM_SER12:
-		ser12_deallocate_resources(bc);
-		break;
-	case BAYCOM_MODEM_PAR96:
-		par96_deallocate_resources(bc);
-		break;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_set_hardware(struct baycom_state *bc,
-			       unsigned int modem_type, unsigned int iobase, 
-			       unsigned int irq, unsigned int options)
-{
-	int i;
-
-	if (!bc)
-		return -EINVAL;
-
-	if (modem_type == BAYCOM_MODEM_SER12) {
-		i = ser12_allocate_resources(iobase, irq, options);
-		if (i < 0)
-			return i;
-	} else if (modem_type == BAYCOM_MODEM_PAR96) {
-		i = par96_allocate_resources(iobase, irq, options);
-		if (i < 0)
-			return i;
-	} else if (modem_type == BAYCOM_MODEM_INVALID) {
-		iobase = irq = options = 0;
-	} else {
-		return -ENXIO;
-	}
-	baycom_dealloc_hw(bc);
-	bc->modem_type = modem_type;
-	bc->iobase = iobase;
-	bc->irq = irq;
-	bc->options = options;
-	i = 0;
-	if (bc->opened > 0) {
-		switch(bc->modem_type) {
-		case BAYCOM_MODEM_SER12:
-			i = ser12_on_open(bc);
-			break;
-		case BAYCOM_MODEM_PAR96:
-			i = par96_on_open(bc);
-			break;
-		}
-	}
-	return i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct tty_struct *tty, struct file * file,
-	unsigned int cmd, unsigned long arg)
-{
-	int i;
-	struct baycom_state *bc;
-	struct baycom_params par;
-		
-	if (!tty)
-		return -EINVAL;
-	if (baycom_paranoia_check(bc = tty->driver_data, "ioctl"))
-		return -EINVAL;
-		
-	switch (cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-	case TIOCMGET:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-		if (i)
-			return i;
-		i = (bc->modem.dcd ? TIOCM_CAR : 0) |
-			(bc->hdlc_tx.ptt ? TIOCM_RTS : 0);
-		put_user(i, (int *) arg);
-		return 0;
-		
-	case BAYCOMCTL_GETDCD:
-		i = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned char));
-		if (!i)
-			put_user(bc->modem.dcd, (unsigned char *) arg);
-		return i;
-		
-	case BAYCOMCTL_GETPTT:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned char));
-		if (!i)
-			put_user(bc->hdlc_tx.ptt, (unsigned char *) arg);
-		return i;
-		
-	case BAYCOMCTL_PARAM_TXDELAY:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.tx_delay = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_PPERSIST:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.ppersist = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_SLOTTIME:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.slottime = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_TXTAIL:
-		if (arg > 255)
-			return -EINVAL;
-		bc->ch_params.tx_tail = arg;
-		return 0;
-		
-	case BAYCOMCTL_PARAM_FULLDUP:
-		bc->ch_params.fulldup = arg ? 1 : 0;
-		return 0;
-		
-	case BAYCOMCTL_CALIBRATE:
-		bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ?
-				       600 : 75);
-		return 0;
-
-	case BAYCOMCTL_GETPARAMS:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(par));
-		if (i)
-			return i;
-		par.modem_type = bc->modem_type;
-		par.iobase = bc->iobase;
-		par.irq = bc->irq;
-		par.options = bc->options;
-		par.tx_delay = bc->ch_params.tx_delay;
-		par.tx_tail = bc->ch_params.tx_tail;
-		par.slottime = bc->ch_params.slottime;
-		par.ppersist = bc->ch_params.ppersist;
-		par.fulldup = bc->ch_params.fulldup;
-		memcpy_tofs((void *)arg, &par, sizeof(par));
-		return 0;
-
-	case BAYCOMCTL_SETPARAMS:
-		if (!suser())
-			return -EPERM;
-		i = verify_area(VERIFY_READ, (void *) arg, 
-				sizeof(par));
-		if (i)
-			return i;
-		memcpy_fromfs(&par, (void *)arg, sizeof(par));
-		printk(KERN_INFO "baycom: changing hardware type: modem %u "
-		       "iobase 0x%x irq %u options 0x%x\n", par.modem_type,
-		       par.iobase, par.irq, par.options);
-		i = baycom_set_hardware(bc, par.modem_type, par.iobase,
-					par.irq, par.options); 
-		if (i)
-			return i;
-		bc->ch_params.tx_delay = par.tx_delay;
-		bc->ch_params.tx_tail = par.tx_tail;
-		bc->ch_params.slottime = par.slottime;
-		bc->ch_params.ppersist = par.ppersist;
-		bc->ch_params.fulldup = par.fulldup;
-		return 0;
-
-	case BAYCOMCTL_GETSTAT:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(struct baycom_statistics));
-		if (i)
-			return i;
-		memcpy_tofs((void *)arg, &bc->stat, 
-			    sizeof(struct baycom_statistics));
-		return 0;
-		
-
-#ifdef BAYCOM_DEBUG
-	case BAYCOMCTL_GETSAMPLES:
-		if (bc->bitbuf_channel.rd == bc->bitbuf_channel.wr) 
-			return -EAGAIN;
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned char));
-		if (!i) {
-			put_user(bc->bitbuf_channel.buffer
-				 [bc->bitbuf_channel.rd],
-				 (unsigned char *) arg);
-			bc->bitbuf_channel.rd = (bc->bitbuf_channel.rd+1) %
-				sizeof(bc->bitbuf_channel.buffer);
-		}
-		return i;
-		
-	case BAYCOMCTL_GETBITS:
-		if (bc->bitbuf_hdlc.rd == bc->bitbuf_hdlc.wr) 
-			return -EAGAIN;
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned char));
-		if (!i) {
-			put_user(bc->bitbuf_hdlc.buffer[bc->bitbuf_hdlc.rd],
-				 (unsigned char *) arg);
-			bc->bitbuf_hdlc.rd = (bc->bitbuf_hdlc.rd+1) %
-				sizeof(bc->bitbuf_hdlc.buffer);
-		}
-		return i;
-		
-	case BAYCOMCTL_DEBUG1:
-		i = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned long));
-		if (!i)
-			put_user((bc->rx_buf.wr-bc->rx_buf.rd) % 
-				 bc->rx_buf.buflen, (unsigned long *)arg);
-		return i;
-		
-	case BAYCOMCTL_DEBUG2:
-		i = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned long));
-		if (!i)
-			put_user(bc->debug_vals.last_intcnt, 
-				 (unsigned long *)arg);
-		return i;
-		
-	case BAYCOMCTL_DEBUG3:
-		i = verify_area(VERIFY_WRITE, (void *) arg, 
-				sizeof(unsigned long));
-		if (!i)
-			put_user((long)bc->debug_vals.last_pllcorr,
-				 (long *)arg);
-		return i;		
-#endif /* BAYCOM_DEBUG */
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-int baycom_open(struct tty_struct *tty, struct file * filp)
-{
-	int line;
-	struct baycom_state *bc;
-	int i;
-
-	if(!tty)
-		return -ENODEV;
-
-	line = MINOR(tty->device) - tty->driver.minor_start;
-	if (line < 0 || line >= NR_PORTS)
-		return -ENODEV;
-	bc = baycom_state+line;
-
-	if (bc->opened > 0) {
-		bc->opened++;
-		MOD_INC_USE_COUNT;
-		return 0;
-	}
-	/*
-	 * initialise some variables
-	 */
-	bc->calibrate = 0;
-
-	/*
-	 * allocate the buffer space
-	 */
-	if (bc->rx_buf.buffer)
-		kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-	if (bc->tx_buf.buffer)
-		kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-	bc->rx_buf.buflen = BUFLEN_RX;
-	bc->tx_buf.buflen = BUFLEN_TX;
-	bc->rx_buf.rd = bc->rx_buf.wr = 0;
-	bc->tx_buf.rd = bc->tx_buf.wr = 0;
-	bc->rx_buf.buffer = kmalloc(bc->rx_buf.buflen, GFP_KERNEL);
-	bc->tx_buf.buffer = kmalloc(bc->tx_buf.buflen, GFP_KERNEL);
-	if (!bc->rx_buf.buffer || !bc->tx_buf.buffer) {
-		if (bc->rx_buf.buffer)
-			kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-		if (bc->tx_buf.buffer)
-			kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-		bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
-		bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
-		return -ENOMEM;
-	}
-	/*
-	 * check if the modem type has been set
-	 */
-	switch(bc->modem_type) {
-	case BAYCOM_MODEM_SER12:
-		i = ser12_on_open(bc);
-		break;
-	case BAYCOM_MODEM_PAR96:
-		i = par96_on_open(bc);
-		break;
-	case BAYCOM_MODEM_INVALID:
-		/*
-		 * may open even if no hardware specified, in order to
-		 * subsequently allow the BAYCOMCTL_SETPARAMS ioctl
-		 */
-		i = 0;
-		break;
-	default:
-		return -ENODEV;
-	}
-	if (i) 
-		return i;
-
-	bc->opened++;
-	MOD_INC_USE_COUNT;
-
-	tty->driver_data = bc;
-	bc->tty = tty;
-
-	return 0;   
-}
-
-
-/* --------------------------------------------------------------------- */
-	
-static void baycom_close(struct tty_struct *tty, struct file * filp)
-{
-	struct baycom_state *bc;
-		
-	if(!tty) return;
-	if (baycom_paranoia_check(bc = tty->driver_data, "close"))
-		return;
-
-	MOD_DEC_USE_COUNT;
-	bc->opened--;
-	if (bc->opened <= 0) {
-		switch(bc->modem_type) {
-		case BAYCOM_MODEM_SER12:
-			ser12_on_close(bc);
-			break;
-		case BAYCOM_MODEM_PAR96:
-			par96_on_close(bc);
-			break;
-		}
-		tty->driver_data = NULL;
-		bc->tty = NULL;
-		bc->opened = 0;
-		/*
-		 * free the buffers 
-		 */
-		bc->rx_buf.rd = bc->rx_buf.wr = 0;
-		bc->tx_buf.rd = bc->tx_buf.wr = 0;
-		if (bc->rx_buf.buffer)
-			kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-		if (bc->tx_buf.buffer)
-			kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-		bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
-		bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * And now the modules code and kernel interface.
- */
-
-static void init_channel(struct baycom_state *bc)
-{
-	struct access_params dflt_ch_params = { 20, 2, 10, 40, 0 };
-
-	if (!bc)
-		return;
-
-	bc->hdlc_rx.rx_state = 0;
-
-	bc->hdlc_tx.tx_state = bc->hdlc_tx.numflags = 0;
-	bc->hdlc_tx.bitstream = 0;
-	bc->hdlc_tx.current_byte = bc->hdlc_tx.ptt = 0;
-
-	memset(&bc->modem, 0, sizeof(bc->modem));
-
-#ifdef BAYCOM_DEBUG
-	bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0;
-	bc->bitbuf_channel.shreg = 0x80;
-
-	bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0;
-	bc->bitbuf_hdlc.shreg = 0x80;
-#endif /* BAYCOM_DEBUG */
-
-	bc->kiss_decode.dec_state = bc->kiss_decode.escaped = 
-	bc->kiss_decode.wr = 0;
-
-	bc->ch_params = dflt_ch_params;
-
-#ifdef BAYCOM_USE_BH
-	bc->tq_receiver.next = bc->tq_transmitter.next =
-		bc->tq_arbitrate.next = NULL;
-	bc->tq_receiver.sync = bc->tq_transmitter.sync =
-		bc->tq_arbitrate.sync = 0;
-	bc->tq_receiver.data = bc->tq_transmitter.data =
-		bc->tq_arbitrate.data = bc;
-	bc->tq_receiver.routine = bh_receiver;
-	bc->tq_transmitter.routine = bh_transmitter;
-	bc->tq_arbitrate.routine = bh_arbitrate;
-#endif /* BAYCOM_USE_BH */
-}
-
-static void init_datastructs(void)
-{
-	int i;
-
-	for(i = 0; i < NR_PORTS; i++) {
-		struct baycom_state *bc = baycom_state+i;
-
-		bc->magic = BAYCOM_MAGIC;
-		bc->modem_type = BAYCOM_MODEM_INVALID;
-		bc->iobase = bc->irq = bc->options = bc->opened = 0;
-		bc->tty = NULL;
-
-		bc->rx_buf.rd = bc->rx_buf.wr = 0;
-		bc->rx_buf.buflen = 0;
-		bc->rx_buf.buffer = NULL;
-
-		bc->tx_buf.rd = bc->tx_buf.wr = 0;
-		bc->tx_buf.buflen = 0;
-		bc->tx_buf.buffer = NULL;
-
-		memset(&bc->stat, 0, sizeof(bc->stat));
-
-		init_channel(bc);
-	}
-}
-
-int baycom_init(void) {
-	int i, j;
-
-	/*
-	 * initialize the data structures
-	 */
-	init_datastructs();
-	/*
-	 * initialize bottom half handler
- 	 */
-#ifdef BAYCOM_USE_BH
-	init_bh(BAYCOM_BH, baycom_bottom_half);
-#endif /* BAYCOM_USE_BH */
-	/*
-	 * register the driver as tty driver
-	 */
-	memset(&baycom_driver, 0, sizeof(struct tty_driver));
-	baycom_driver.magic = TTY_DRIVER_MAGIC;
-	baycom_driver.name = "baycom";
-	baycom_driver.major = major;
-	baycom_driver.minor_start = 0;
-	baycom_driver.num = NR_PORTS;
-	baycom_driver.type = TTY_DRIVER_TYPE_BAYCOM;
-	baycom_driver.subtype = BAYCOM_TYPE_NORMAL;
-	baycom_driver.init_termios.c_iflag = 0;
-	baycom_driver.init_termios.c_oflag = 0;
-	baycom_driver.init_termios.c_cflag = CS8 | B1200 | CREAD | CLOCAL;
-	baycom_driver.init_termios.c_lflag = 0;
-	baycom_driver.flags = TTY_DRIVER_REAL_RAW;
-	baycom_driver.refcount = &baycom_refcount;
-	baycom_driver.table = baycom_table;
-	baycom_driver.termios = baycom_termios;
-	baycom_driver.termios_locked = baycom_termios_locked;
-	/*
-	 * the functions
-	 */
-	baycom_driver.open = baycom_open;
-	baycom_driver.close = baycom_close;
-	baycom_driver.write = baycom_write;
-	baycom_driver.put_char = baycom_put_char;
-	baycom_driver.flush_chars = NULL;
-	baycom_driver.write_room = baycom_write_room;
-	baycom_driver.chars_in_buffer = baycom_chars_in_buffer;
-	baycom_driver.flush_buffer = baycom_flush_buffer;
-	baycom_driver.ioctl = baycom_ioctl;
-	/*
-	 * cannot throttle the transmitter on this layer
-	 */
-	baycom_driver.throttle = NULL;
-	baycom_driver.unthrottle = NULL;
-	/*
-	 * no special actions on termio changes
-	 */
-	baycom_driver.set_termios = NULL;
-	/*
-	 * no XON/XOFF and no hangup on the radio port
-	 */
-	baycom_driver.stop = NULL;
-	baycom_driver.start = NULL;
-	baycom_driver.hangup = NULL;
-	baycom_driver.set_ldisc = NULL;
-
-	if (tty_register_driver(&baycom_driver)) {
-		printk(KERN_WARNING "baycom: tty_register_driver failed\n");
-		return -EIO;
-	}
-
-	for (i = 0; i < NR_PORTS && 
-	     baycom_ports[i].modem != BAYCOM_MODEM_INVALID; i++) {
-		j = baycom_set_hardware(baycom_state+i, 
-					baycom_ports[i].modem,
-					baycom_ports[i].iobase, 
-					baycom_ports[i].irq, 
-					baycom_ports[i].options);
-		if (j < 0) {
-			const char *s;
-			switch (-j) {
-			case ENXIO:
-				s = "invalid iobase and/or irq";
-				break;
-			case EACCES:
-				s = "io region already used";
-				break;
-			case EIO:
-				s = "no uart/lpt port at iobase";
-				break;
-			case EBUSY:
-				s = "interface already in use";
-				break;
-			case EINVAL:
-				s = "internal error";
-				break;
-			default:
-				s = "unknown error";
-				break;
-			}
-			printk(KERN_WARNING "baycom: modem %u iobase 0x%x "
-			       "irq %u: (%i) %s\n", baycom_ports[i].modem, 
-			       baycom_ports[i].iobase, baycom_ports[i].irq, 
-			       j, s);
-		}
-	}
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-int modem = BAYCOM_MODEM_INVALID;
-int iobase = 0x3f8;
-int irq = 4;
-int options = BAYCOM_OPTIONS_SOFTDCD;
-
-int init_module(void)
-{
-	int i;
-
-	printk(KERN_INFO "baycom: init_module called\n");
-
-	baycom_ports[0].modem = modem;
-	baycom_ports[0].iobase = iobase;
-	baycom_ports[0].irq = irq;
-	baycom_ports[0].options = options;
-	baycom_ports[1].modem = BAYCOM_MODEM_INVALID;
-
-	i = baycom_init();
-	if (i)
-		return i;
-
-	printk(KERN_INFO "baycom: version 0.3; "
-	       "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n");
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	int i;
-
-	printk(KERN_INFO "baycom: cleanup_module called\n");
-
-	disable_bh(BAYCOM_BH);
-	if (tty_unregister_driver(&baycom_driver))
-		printk(KERN_WARNING "baycom: failed to unregister tty "
-		       "driver\n");
-	for(i = 0; i < NR_PORTS; i++) {
-		struct baycom_state *bc = baycom_state+i;
-
-		if (bc->magic != BAYCOM_MAGIC)
-			printk(KERN_ERR "baycom: invalid magic in "
-			       "cleanup_module\n");
-		else {
-			baycom_dealloc_hw(bc);
-			/*
-			 * free the buffers 
-			 */
-			bc->rx_buf.rd = bc->rx_buf.wr = 0;
-			bc->tx_buf.rd = bc->tx_buf.wr = 0;
-			if (bc->rx_buf.buffer)
-				kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
-			if (bc->tx_buf.buffer)
-				kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
-			bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
-			bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
-		}
-	}
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: baycom=modem,io,irq,options[,modem,io,irq,options]
- * modem=1: ser12, modem=2: par96
- * options=0: hardware DCD, options=1: software DCD
- */
-
-void baycom_setup(char *str, int *ints)
-{
-	int i;
-
-	for (i = 0; i < NR_PORTS; i++) 
-		if (ints[0] >= 4*i+4) {
-			baycom_ports[i].modem = ints[4*i+1];
-			baycom_ports[i].iobase = ints[4*i+2];
-			baycom_ports[i].irq = ints[4*i+3];
-			baycom_ports[i].options = ints[4*i+4];
-		} else
-			baycom_ports[i].modem = BAYCOM_MODEM_INVALID;
-
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/char/ftape/ftape-bsm.h linux/drivers/char/ftape/ftape-bsm.h
--- v2.0.34/linux/drivers/char/ftape/ftape-bsm.h	Wed Mar  6 05:07:19 1996
+++ linux/drivers/char/ftape/ftape-bsm.h	Mon Jul 13 13:47:29 1998
@@ -44,7 +44,7 @@
 /*
  *      ftape-io.c defined global vars.
  */
-extern bad_sector_map_changed;
+extern int bad_sector_map_changed;
 
 /*
  *      ftape-io.c defined global functions.
diff -u --recursive --new-file v2.0.34/linux/drivers/char/ftape/ftape-ctl.h linux/drivers/char/ftape/ftape-ctl.h
--- v2.0.34/linux/drivers/char/ftape/ftape-ctl.h	Wed Oct 15 15:10:48 1997
+++ linux/drivers/char/ftape/ftape-ctl.h	Mon Jul 13 13:47:29 1998
@@ -61,7 +61,7 @@
  */
 extern int ftape_failure;
 extern int write_protected;
-extern ftape_offline;
+extern int ftape_offline;
 extern int formatted;
 extern int no_tape;
 extern history_record history;
diff -u --recursive --new-file v2.0.34/linux/drivers/char/ftape/kernel-interface.h linux/drivers/char/ftape/kernel-interface.h
--- v2.0.34/linux/drivers/char/ftape/kernel-interface.h	Wed Oct 15 15:10:48 1997
+++ linux/drivers/char/ftape/kernel-interface.h	Mon Jul 13 13:47:29 1998
@@ -59,7 +59,7 @@
 /*      kernel global functions not (yet) standard accessible
  *      (linked at load time by modules package).
  */
-asmlinkage extern sys_sgetmask(void);
-asmlinkage extern sys_ssetmask(int);
+asmlinkage extern int sys_sgetmask(void);
+asmlinkage extern int sys_ssetmask(int);
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v2.0.34/linux/drivers/char/istallion.c	Mon May  6 02:26:05 1996
+++ linux/drivers/char/istallion.c	Mon Jul 13 13:47:29 1998
@@ -3,6 +3,7 @@
 /*
  *	istallion.c  -- stallion intelligent multiport serial driver.
  *
+ *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This code is loosely based on the Linux serial driver, written by
@@ -73,6 +74,8 @@
 #define BRD_ECPE	24
 #define	BRD_ECPMC	25
 #define	BRD_ECHPCI	26
+#define	BRD_ECH64PCI	27
+#define	BRD_EASYIOPCI	28
 
 #define	BRD_BRUMBY	BRD_BRUMBY4
 
@@ -90,6 +93,12 @@
  *	boards can share the same shared memory address space. No interrupt
  *	is required for this board type.
  *	Another example:
+ *		{ BRD_ECPE, 0x5000, 0, 0x80000000, 0, 0 },
+ *	This line will configure an EasyConnection 8/64 EISA in slot 5 and
+ *	shared memory address of 0x80000000 (2 GByte). Multiple
+ *	EasyConnection 8/64 EISA boards can share the same shared memory
+ *	address space. No interrupt is required for this board type.
+ *	Another example:
  *		{ BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 },
  *	This line will configure an ONboard (ISA type) at io address 240,
  *	and shared memory address of d0000. Multiple ONboards can share
@@ -156,8 +165,8 @@
  *	Define our local driver identity first. Set up stuff to deal with
  *	all the local structures required by a serial tty driver.
  */
-static char	*stli_drvname = "Stallion Intelligent Multiport Serial Driver";
-static char	*stli_drvversion = "1.1.3";
+static char	*stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
+static char	*stli_drvversion = "5.4.4";
 static char	*stli_serialname = "ttyE";
 static char	*stli_calloutname = "cue";
 
@@ -281,6 +290,8 @@
 	"EC8/64-EI",
 	"EC8/64-MC",
 	"EC8/32-PCI",
+	"EC8/64-PCI",
+	"EasyIO-PCI",
 };
 
 /*
@@ -436,6 +447,7 @@
 #define	ECH_PNLSTATUS	2
 #define	ECH_PNL16PORT	0x20
 #define	ECH_PNLIDMASK	0x07
+#define	ECH_PNLXPID	0x40
 #define	ECH_PNLINTRPEND	0x80
 
 /*
@@ -473,9 +485,9 @@
 /*
  *	Define the maximal baud rate, and the default baud base for ports.
  */
-#define	STL_MAXBAUD	230400
+#define	STL_MAXBAUD	460800
 #define	STL_BAUDBASE	115200
-#define	STL_CLOSEDELAY	50
+#define	STL_CLOSEDELAY	(5 * HZ / 10)
 
 /*****************************************************************************/
 
@@ -492,7 +504,7 @@
  */
 static unsigned int	stli_baudrates[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
 };
 
 /*****************************************************************************/
@@ -530,19 +542,16 @@
 static void	stli_flushbuffer(struct tty_struct *tty);
 static void	stli_hangup(struct tty_struct *tty);
 
-static int	stli_initbrds(void);
 static int	stli_brdinit(stlibrd_t *brdp);
-static int	stli_initecp(stlibrd_t *brdp);
-static int	stli_initonb(stlibrd_t *brdp);
-static int	stli_eisamemprobe(stlibrd_t *brdp);
-static int	stli_findeisabrds(void);
-static int	stli_initports(stlibrd_t *brdp);
 static int	stli_startbrd(stlibrd_t *brdp);
+static int	stli_memopen(struct inode *ip, struct file *fp);
+static void	stli_memclose(struct inode *ip, struct file *fp);
 static int	stli_memread(struct inode *ip, struct file *fp, char *buf, int count);
 static int	stli_memwrite(struct inode *ip, struct file *fp, const char *buf, int count);
 static int	stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static void	stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp);
 static void	stli_poll(unsigned long arg);
-static int	stli_hostcmd(stlibrd_t *brdp, int channr);
+static int	stli_hostcmd(stlibrd_t *brdp, stliport_t *portp);
 static int	stli_initopen(stlibrd_t *brdp, stliport_t *portp);
 static int	stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
 static int	stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
@@ -561,6 +570,7 @@
 static int	stli_setserial(stliport_t *portp, struct serial_struct *sp);
 static int	stli_getbrdstats(combrd_t *bp);
 static int	stli_getportstats(stliport_t *portp, comstats_t *cp);
+static int	stli_portcmdstats(stliport_t *portp);
 static int	stli_clrportstats(stliport_t *portp, comstats_t *cp);
 static int	stli_getportstruct(unsigned long arg);
 static int	stli_getbrdstruct(unsigned long arg);
@@ -601,6 +611,13 @@
 
 static stliport_t *stli_getport(int brdnr, int panelnr, int portnr);
 
+static inline int	stli_initbrds(void);
+static inline int	stli_initecp(stlibrd_t *brdp);
+static inline int	stli_initonb(stlibrd_t *brdp);
+static inline int	stli_findeisabrds(void);
+static inline int	stli_eisamemprobe(stlibrd_t *brdp);
+static inline int	stli_initports(stlibrd_t *brdp);
+
 /*****************************************************************************/
 
 /*
@@ -617,8 +634,8 @@
 	NULL,
 	stli_memioctl,
 	NULL,
-	NULL,
-	NULL,
+	stli_memopen,
+	stli_memclose,
 	NULL
 };
 
@@ -678,7 +695,8 @@
 	printk("cleanup_module()\n");
 #endif
 
-	printk(KERN_INFO "Unloading %s: version %s\n", stli_drvname, stli_drvversion);
+	printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle,
+		stli_drvversion);
 
 	save_flags(flags);
 	cli();
@@ -695,12 +713,14 @@
 	i = tty_unregister_driver(&stli_serial);
 	j = tty_unregister_driver(&stli_callout);
 	if (i || j) {
-		printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+		printk("STALLION: failed to un-register tty driver, "
+			"errno=%d,%d\n", -i, -j);
 		restore_flags(flags);
 		return;
 	}
 	if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
-		printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i);
+		printk("STALLION: failed to un-register serial memory device, "
+			"errno=%d\n", -i);
 
 	if (stli_tmpwritebuf != (char *) NULL)
 		kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE);
@@ -722,10 +742,8 @@
 
 		if (brdp->memaddr >= 0x100000)
 			vfree(brdp->membase);
-		if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC))
-			release_region(brdp->iobase, ECP_IOSIZE);
-		else
-			release_region(brdp->iobase, ONB_IOSIZE);
+		if (brdp->iosize > 0)
+			release_region(brdp->iobase, brdp->iosize);
 		kfree_s(brdp, sizeof(stlibrd_t));
 		stli_brds[i] = (stlibrd_t *) NULL;
 	}
@@ -756,7 +774,8 @@
 	int		brdnr, portnr, rc;
 
 #if DEBUG
-	printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+	printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty,
+		(int) filp, tty->device);
 #endif
 
 	minordev = MINOR(tty->device);
@@ -778,6 +797,8 @@
 	if (portp->devnr < 1)
 		return(-ENODEV);
 
+	MOD_INC_USE_COUNT;
+
 /*
  *	Check if this port is in the middle of closing. If so then wait
  *	until it is closed then return error status based on flag settings.
@@ -842,10 +863,10 @@
 			return(-EBUSY);
 		if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
 			if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
-					(portp->session != current->session))
+			    (portp->session != current->session))
 				return(-EBUSY);
 			if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
-					(portp->pgrp != current->pgrp))
+			    (portp->pgrp != current->pgrp))
 				return(-EBUSY);
 		}
 		portp->flags |= ASYNC_CALLOUT_ACTIVE;
@@ -892,10 +913,14 @@
 	save_flags(flags);
 	cli();
 	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
+	if ((tty->count == 1) && (portp->refcount != 1))
+		portp->refcount = 1;
 	if (portp->refcount-- > 1) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
@@ -916,10 +941,8 @@
 	if (tty == stli_txcooktty)
 		stli_flushchars(tty);
 	tty->closing = 1;
-	if (test_bit(ST_TXBUSY, &portp->state)) {
-		if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-			tty_wait_until_sent(tty, portp->closing_wait);
-	}
+	if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, portp->closing_wait);
 
 	portp->flags &= ~ASYNC_INITIALIZED;
 	brdp = stli_brds[portp->brdnr];
@@ -929,7 +952,8 @@
 		if (test_bit(ST_CMDING, &portp->state))
 			set_bit(ST_DOSIGS, &portp->state);
 		else
-			stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig,
+				sizeof(asysigs_t), 0);
 	}
 	clear_bit(ST_TXBUSY, &portp->state);
 	clear_bit(ST_RXSTOP, &portp->state);
@@ -940,7 +964,6 @@
 	stli_flushbuffer(tty);
 
 	tty->closing = 0;
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 
 	if (portp->openwaitcnt) {
@@ -949,8 +972,10 @@
 		wake_up_interruptible(&portp->open_wait);
 	}
 
-	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE |
+		ASYNC_CLOSING);
 	wake_up_interruptible(&portp->close_wait);
+	MOD_DEC_USE_COUNT;
 	restore_flags(flags);
 }
 
@@ -981,23 +1006,27 @@
 	memset(&nt, 0, sizeof(asynotify_t));
 	nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
 	nt.signal = SG_DCD;
-	if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt,
+	    sizeof(asynotify_t), 0)) < 0)
 		return(rc);
 
 	tty = portp->tty;
 	if (tty == (struct tty_struct *) NULL)
 		return(-ENODEV);
 	stli_mkasyport(portp, &aport, tty->termios);
-	if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
+	    sizeof(asyport_t), 0)) < 0)
 		return(rc);
 
 	set_bit(ST_GETSIGS, &portp->state);
-	if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig,
+	    sizeof(asysigs_t), 1)) < 0)
 		return(rc);
 	if (clear_bit(ST_GETSIGS, &portp->state))
 		portp->sigs = stli_mktiocm(portp->asig.sigvalue);
 	stli_mkasysigs(&portp->asig, 1, 1);
-	if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+	    sizeof(asysigs_t), 0)) < 0)
 		return(rc);
 
 	return(0);
@@ -1021,7 +1050,8 @@
 	int			rc;
 
 #if DEBUG
-	printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+	printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
+		(int) brdp, (int) portp, (int) arg, wait);
 #endif
 
 /*
@@ -1054,8 +1084,8 @@
 	cp->openarg = arg;
 	cp->open = 1;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	EBRDDISABLE(brdp);
 
@@ -1101,7 +1131,8 @@
 	int			rc;
 
 #if DEBUG
-	printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+	printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
+		(int) brdp, (int) portp, (int) arg, wait);
 #endif
 
 	save_flags(flags);
@@ -1129,8 +1160,8 @@
 	cp->closearg = arg;
 	cp->close = 1;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	EBRDDISABLE(brdp);
 
@@ -1173,7 +1204,9 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+	printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
+		"copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
+		(int) arg, size, copyback);
 #endif
 
 	save_flags(flags);
@@ -1243,7 +1276,7 @@
 static void stli_delay(int len)
 {
 #if DEBUG
-	printk("stl_delay(len=%d)\n", len);
+	printk("stli_delay(len=%d)\n", len);
 #endif
 	if (len > 0) {
 		current->state = TASK_INTERRUPTIBLE;
@@ -1265,7 +1298,8 @@
 	int		rc, doclocal;
 
 #if DEBUG
-	printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", (int) brdp, (int) portp, (int) filp);
+	printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n",
+		(int) brdp, (int) portp, (int) filp);
 #endif
 
 	rc = 0;
@@ -1282,16 +1316,18 @@
 	save_flags(flags);
 	cli();
 	portp->openwaitcnt++;
-	if (portp->refcount > 0)
+	if (! tty_hung_up_p(filp))
 		portp->refcount--;
 
 	for (;;) {
 		if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) {
 			stli_mkasysigs(&portp->asig, 1, 1);
-			if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+			if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+			    &portp->asig, sizeof(asysigs_t), 0)) < 0)
 				break;
 		}
-		if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+		if (tty_hung_up_p(filp) ||
+		    ((portp->flags & ASYNC_INITIALIZED) == 0)) {
 			if (portp->flags & ASYNC_HUP_NOTIFY)
 				rc = -EBUSY;
 			else
@@ -1299,8 +1335,8 @@
 			break;
 		}
 		if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
-				((portp->flags & ASYNC_CLOSING) == 0) &&
-				(doclocal || (portp->sigs & TIOCM_CD))) {
+		    ((portp->flags & ASYNC_CLOSING) == 0) &&
+		    (doclocal || (portp->sigs & TIOCM_CD))) {
 			break;
 		}
 		if (current->signal & ~current->blocked) {
@@ -1338,10 +1374,12 @@
 	unsigned long		flags;
 
 #if DEBUG
-	printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+	printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n",
+		(int) tty, from_user, (int) buf, count);
 #endif
 
-	if ((tty == (struct tty_struct *) NULL) || (stli_tmpwritebuf == (char *) NULL))
+	if ((tty == (struct tty_struct *) NULL) ||
+	    (stli_tmpwritebuf == (char *) NULL))
 		return(0);
 	if (tty == stli_txcooktty)
 		stli_flushchars(tty);
@@ -1375,7 +1413,8 @@
 		tail = (unsigned int) ap->txq.tail;
 		if (tail != ((unsigned int) ap->txq.tail))
 			tail = (unsigned int) ap->txq.tail;
-		len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : (tail - head - 1);
+		len = (head >= tail) ? (portp->txsize - (head - tail) - 1) :
+			(tail - head - 1);
 		count = MIN(len, count);
 		EBRDDISABLE(brdp);
 
@@ -1430,8 +1469,8 @@
 			ap->changed.data &= ~DT_TXEMPTY;
 	}
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_TXBUSY, &portp->state);
 
@@ -1562,8 +1601,8 @@
 			ap->changed.data &= ~DT_TXEMPTY;
 	}
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_TXBUSY, &portp->state);
 
@@ -1729,12 +1768,14 @@
 	memcpy_fromfs(&sio, sp, sizeof(struct serial_struct));
 	if (!suser()) {
 		if ((sio.baud_base != portp->baud_base) ||
-				(sio.close_delay != portp->close_delay) ||
-				((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+		    (sio.close_delay != portp->close_delay) ||
+		    ((sio.flags & ~ASYNC_USR_MASK) !=
+		    (portp->flags & ~ASYNC_USR_MASK)))
 			return(-EPERM);
 	} 
 
-	portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+	portp->flags = (portp->flags & ~ASYNC_USR_MASK) |
+		(sio.flags & ASYNC_USR_MASK);
 	portp->baud_base = sio.baud_base;
 	portp->close_delay = sio.close_delay;
 	portp->closing_wait = sio.closing_wait;
@@ -1755,7 +1796,8 @@
 	int		rc;
 
 #if DEBUG
-	printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+	printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n",
+		(int) tty, (int) file, cmd, (int) arg);
 #endif
 
 	if (tty == (struct tty_struct *) NULL)
@@ -1769,6 +1811,12 @@
 	if (brdp == (stlibrd_t *) NULL)
 		return(0);
 
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ 	    (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+			return(-EIO);
+	}
+
 	rc = 0;
 
 	switch (cmd) {
@@ -1777,7 +1825,8 @@
 			tty_wait_until_sent(tty, 0);
 			if (! arg) {
 				val = 250;
-				rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+				rc = stli_cmdwait(brdp, portp, A_BREAK, &val,
+					sizeof(unsigned long), 0);
 			}
 		}
 		break;
@@ -1785,72 +1834,98 @@
 		if ((rc = tty_check_change(tty)) == 0) {
 			tty_wait_until_sent(tty, 0);
 			val = (arg ? (arg * 100) : 250);
-			rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+			rc = stli_cmdwait(brdp, portp, A_BREAK, &val,
+				sizeof(unsigned long), 0);
 		}
 		break;
 	case TIOCGSOFTCAR:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0)
-			put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(long))) == 0)
+			put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
+				(unsigned long *) arg);
 		break;
 	case TIOCSSOFTCAR:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0);
+			tty->termios->c_cflag =
+				(tty->termios->c_cflag & ~CLOCAL) |
+				(arg ? CLOCAL : 0);
 		}
 		break;
 	case TIOCMGET:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
-			if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
+			if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS,
+			    &portp->asig, sizeof(asysigs_t), 1)) < 0)
 				return(rc);
 			val = stli_mktiocm(portp->asig.sigvalue);
 			put_fs_long(val, (unsigned long *) arg);
 		}
 		break;
 	case TIOCMBIS:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1));
-			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_mkasysigs(&portp->asig,
+				((arg & TIOCM_DTR) ? 1 : -1),
+				((arg & TIOCM_RTS) ? 1 : -1));
+			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCMBIC:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1));
-			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_mkasysigs(&portp->asig,
+				((arg & TIOCM_DTR) ? 0 : -1),
+				((arg & TIOCM_RTS) ? 0 : -1));
+			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCMSET:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			stli_mkasysigs(&portp->asig, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0));
-			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_mkasysigs(&portp->asig,
+				((arg & TIOCM_DTR) ? 1 : 0),
+				((arg & TIOCM_RTS) ? 1 : 0));
+			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCGSERIAL:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
 			stli_getserial(portp, (struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
-			rc = stli_setserial(portp, (struct serial_struct *) arg);
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
+			rc = stli_setserial(portp, (struct serial_struct *)arg);
 		break;
 	case STL_GETPFLAG:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(unsigned long))) == 0)
 			put_fs_long(portp->pflag, (unsigned long *) arg);
 		break;
 	case STL_SETPFLAG:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned long))) == 0) {
 			portp->pflag = get_fs_long((unsigned long *) arg);
 			stli_setport(portp);
 		}
 		break;
 	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stli_getportstats(portp, (comstats_t *) arg);
 		break;
 	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stli_clrportstats(portp, (comstats_t *) arg);
 		break;
 	case TIOCSERCONFIG:
@@ -1898,13 +1973,15 @@
 		return;
 
 	tiosp = tty->termios;
-	if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+	if ((tiosp->c_cflag == old->c_cflag) &&
+	    (tiosp->c_iflag == old->c_iflag))
 		return;
 
 	stli_mkasyport(portp, &aport, tiosp);
 	stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
 	stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1);
-	stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+	stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+		sizeof(asysigs_t), 0);
 	if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0))
 		tty->hw_stopped = 0;
 	if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
@@ -1996,7 +2073,7 @@
 	memset(&actrl, 0, sizeof(asyctrl_t));
 	actrl.txctrl = CT_STOPFLOW;
 #if 0
-	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
 #endif
 }
 
@@ -2030,7 +2107,7 @@
 	memset(&actrl, 0, sizeof(asyctrl_t));
 	actrl.txctrl = CT_STARTFLOW;
 #if 0
-	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
 #endif
 }
 
@@ -2104,7 +2181,8 @@
 			set_bit(ST_DOFLUSHTX, &portp->state);
 			set_bit(ST_DOFLUSHRX, &portp->state);
 		} else {
-			stli_sendcmd(brdp, portp, A_SETSIGNALSF, &portp->asig, sizeof(asysigs_t), 0);
+			stli_sendcmd(brdp, portp, A_SETSIGNALSF,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 	}
 	restore_flags(flags);
@@ -2112,7 +2190,6 @@
 	clear_bit(ST_TXBUSY, &portp->state);
 	clear_bit(ST_RXSTOP, &portp->state);
 	set_bit(TTY_IO_ERROR, &tty->flags);
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 	portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
 	portp->refcount = 0;
@@ -2164,12 +2241,14 @@
 			ftype |= FLUSHRX;
 			clear_bit(ST_DOFLUSHRX, &portp->state);
 		}
-		stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0);
+		stli_sendcmd(brdp, portp, A_FLUSH, &ftype,
+			sizeof(unsigned long), 0);
 	}
 	restore_flags(flags);
 
 	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
 }
 
@@ -2193,7 +2272,9 @@
 	unsigned long		flags;
 
 #if DEBUG
-	printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+	printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
+		"copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
+		(int) arg, size, copyback);
 #endif
 
 	save_flags(flags);
@@ -2217,8 +2298,8 @@
 	cp->status = 0;
 	cp->cmd = cmd;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_CMDING, &portp->state);
 	EBRDDISABLE(brdp);
@@ -2307,7 +2388,8 @@
 	int	cmd;
 
 	if (test_bit(ST_DOSIGS, &portp->state)) {
-		if (test_bit(ST_DOFLUSHTX, &portp->state) && test_bit(ST_DOFLUSHRX, &portp->state))
+		if (test_bit(ST_DOFLUSHTX, &portp->state) &&
+		    test_bit(ST_DOFLUSHRX, &portp->state))
 			cmd = A_SETSIGNALSF;
 		else if (test_bit(ST_DOFLUSHTX, &portp->state))
 			cmd = A_SETSIGNALSFTX;
@@ -2318,11 +2400,13 @@
 		clear_bit(ST_DOFLUSHTX, &portp->state);
 		clear_bit(ST_DOFLUSHRX, &portp->state);
 		clear_bit(ST_DOSIGS, &portp->state);
-		memcpy((void *) &(cp->args[0]), (void *) &portp->asig, sizeof(asysigs_t));
+		memcpy((void *) &(cp->args[0]), (void *) &portp->asig,
+			sizeof(asysigs_t));
 		cp->status = 0;
 		cp->cmd = cmd;
 		set_bit(ST_CMDING, &portp->state);
-	} else if (test_bit(ST_DOFLUSHTX, &portp->state) || test_bit(ST_DOFLUSHRX, &portp->state)) {
+	} else if (test_bit(ST_DOFLUSHTX, &portp->state) ||
+	    test_bit(ST_DOFLUSHRX, &portp->state)) {
 		cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0);
 		cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0);
 		clear_bit(ST_DOFLUSHTX, &portp->state);
@@ -2342,15 +2426,17 @@
  *	enabled and interrupts off when called. Notice that by servicing the
  *	read data last we don't need to change the shared memory pointer
  *	during processing (which is a slow IO operation).
+ *	Return value indicates if this port is still awaiting actions from
+ *	the slave (like open, command, or even TX data being sent). If 0
+ *	then port is still busy, otherwise no longer busy.
  */
 
-static inline int stli_hostcmd(stlibrd_t *brdp, int channr)
+static inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
 {
 	volatile cdkasy_t	*ap;
 	volatile cdkctrl_t	*cp;
 	struct tty_struct	*tty;
 	asynotify_t		nt;
-	stliport_t		*portp;
 	unsigned long		oldsigs;
 	int			rc, donerx;
 
@@ -2358,7 +2444,6 @@
 	printk("stli_hostcmd(brdp=%x,channr=%d)\n", (int) brdp, channr);
 #endif
 
-	portp = brdp->ports[(channr - 1)];
 	ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
 	cp = &ap->ctrl;
 
@@ -2402,7 +2487,8 @@
 			if (rc > 0)
 				rc--;
 			if (portp->argp != (void *) NULL) {
-				memcpy(portp->argp, (void *) &(cp->args[0]), portp->argsize);
+				memcpy(portp->argp, (void *) &(cp->args[0]),
+					portp->argsize);
 				portp->argp = (void *) NULL;
 			}
 			cp->status = 0;
@@ -2429,12 +2515,14 @@
 			oldsigs = portp->sigs;
 			portp->sigs = stli_mktiocm(nt.sigvalue);
 			clear_bit(ST_GETSIGS, &portp->state);
-			if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+			if ((portp->sigs & TIOCM_CD) &&
+			    ((oldsigs & TIOCM_CD) == 0))
 				wake_up_interruptible(&portp->open_wait);
-			if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+			if ((oldsigs & TIOCM_CD) &&
+			    ((portp->sigs & TIOCM_CD) == 0)) {
 				if (portp->flags & ASYNC_CHECK_CD) {
 					if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
-							(portp->flags & ASYNC_CALLOUT_NOHUP))) {
+					    (portp->flags & ASYNC_CALLOUT_NOHUP))) {
 						if (tty != (struct tty_struct *) NULL)
 							queue_task_irq_off(&portp->tqhangup, &tq_scheduler);
 					}
@@ -2446,7 +2534,8 @@
 			clear_bit(ST_TXBUSY, &portp->state);
 		if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
 			if (tty != (struct tty_struct *) NULL) {
-				if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+				if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+				    tty->ldisc.write_wakeup) {
 					(tty->ldisc.write_wakeup)(tty);
 					EBRDENABLE(brdp);
 				}
@@ -2460,12 +2549,10 @@
 					tty->flip.count++;
 					*tty->flip.flag_buf_ptr++ = TTY_BREAK;
 					*tty->flip.char_buf_ptr++ = 0;
-#ifndef MODULE
 					if (portp->flags & ASYNC_SAK) {
 						do_SAK(tty);
 						EBRDENABLE(brdp);
 					}
-#endif
 					tty_schedule_flip(tty);
 				}
 			}
@@ -2490,7 +2577,74 @@
 		stli_read(brdp, portp);
 	}
 
-	return(0);
+	return((test_bit(ST_OPENING, &portp->state) ||
+		test_bit(ST_CLOSING, &portp->state) ||
+		test_bit(ST_CMDING, &portp->state) ||
+		test_bit(ST_TXBUSY, &portp->state) ||
+		test_bit(ST_RXING, &portp->state)) ? 0 : 1);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Service all ports on a particular board. Assumes that the boards
+ *	shared memory is enabled, and that the page pointer is pointed
+ *	at the cdk header structure.
+ */
+
+static inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp)
+{
+	stliport_t	*portp;
+	unsigned char	hostbits[(STL_MAXCHANS / 8) + 1];
+	unsigned char	slavebits[(STL_MAXCHANS / 8) + 1];
+	unsigned char	*slavep;
+	int		bitpos, bitat, bitsize;
+	int 		channr, nrdevs, slavebitchange;
+
+	bitsize = brdp->bitsize;
+	nrdevs = brdp->nrdevs;
+
+/*
+ *	Check if slave wants any service. Basically we try to do as
+ *	little work as possible here. There are 2 levels of service
+ *	bits. So if there is nothing to do we bail early. We check
+ *	8 service bits at a time in the inner loop, so we can bypass
+ *	the lot if none of them want service.
+ */
+	memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset),
+		bitsize);
+
+	memset(&slavebits[0], 0, bitsize);
+	slavebitchange = 0;
+
+	for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+		if (hostbits[bitpos] == 0)
+			continue;
+		channr = bitpos * 8;
+		for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
+			if (hostbits[bitpos] & bitat) {
+				portp = brdp->ports[(channr - 1)];
+				if (stli_hostcmd(brdp, portp)) {
+					slavebitchange++;
+					slavebits[bitpos] |= bitat;
+				}
+			}
+		}
+	}
+
+/*
+ *	If any of the ports are no longer busy then update them in the
+ *	slave request bits. We need to do this after, since a host port
+ *	service may initiate more slave requests.
+ */
+	if (slavebitchange) {
+		hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+		slavep = ((unsigned char *) hdrp) + brdp->slaveoffset;
+		for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+			if (slavebits[bitpos])
+				slavep[bitpos] &= ~slavebits[bitpos];
+		}
+	}
 }
 
 /*****************************************************************************/
@@ -2507,12 +2661,8 @@
 static void stli_poll(unsigned long arg)
 {
 	volatile cdkhdr_t	*hdrp;
-	unsigned char		bits[(STL_MAXCHANS / 8) + 1];
-	unsigned char		hostreq, slavereq;
-	stliport_t		*portp;
 	stlibrd_t		*brdp;
-	int			bitpos, bitat, bitsize;
-	int 			brdnr, channr, nrdevs;
+	int 			brdnr;
 
 	stli_timerlist.expires = STLI_TIMEOUT;
 	add_timer(&stli_timerlist);
@@ -2529,67 +2679,8 @@
 
 		EBRDENABLE(brdp);
 		hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-		hostreq = hdrp->hostreq;
-		slavereq = hdrp->slavereq;
-		bitsize = brdp->bitsize;
-		nrdevs = brdp->nrdevs;
-
-/*
- *		Check if slave wants any service. Basically we try to do as
- *		little work as possible here. There are 2 levels of service
- *		bits. So if there is nothing to do we bail early. We check
- *		8 service bits at a time in the inner loop, so we can bypass
- *		the lot if none of them want service.
- */
-		if (hostreq) {
-			memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize);
-
-			for (bitpos = 0; (bitpos < bitsize); bitpos++) {
-				if (bits[bitpos] == 0)
-					continue;
-				channr = bitpos * 8;
-				for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
-					if (bits[bitpos] & bitat) {
-						stli_hostcmd(brdp, channr);
-					}
-				}
-			}
-		}
-
-/*
- *		Check if any of the out-standing host commands have completed.
- *		It is a bit unfortunate that we need to check stuff that we
- *		initiated!  This ain't pretty, but it needs to be fast.
- */
-		if (slavereq) {
-			slavereq = 0;
-			hostreq = 0;
-			memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->slaveoffset), bitsize);
-
-			for (bitpos = 0; (bitpos < bitsize); bitpos++) {
-				if (bits[bitpos] == 0)
-					continue;
-				channr = bitpos * 8;
-				for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
-					if (bits[bitpos] & bitat) {
-						portp = brdp->ports[(channr - 1)];
-						if (test_bit(ST_OPENING, &portp->state) ||
-								test_bit(ST_CLOSING, &portp->state) ||
-								test_bit(ST_CMDING, &portp->state) ||
-								test_bit(ST_TXBUSY, &portp->state)) {
-							slavereq |= portp->reqbit;
-						} else {
-							bits[bitpos] &= ~bitat;
-							hostreq++;
-						}
-					}
-				}
-			}
-			hdrp->slavereq = slavereq;
-			if (hostreq)
-				memcpy((((unsigned char *) hdrp) + brdp->slaveoffset), &bits[0], bitsize);
-		}
-
+		if (hdrp->hostreq)
+			stli_brdpoll(brdp, hdrp);
 		EBRDDISABLE(brdp);
 	}
 }
@@ -2604,7 +2695,8 @@
 static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp)
 {
 #if DEBUG
-	printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp);
+	printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n",
+		(int) portp, (int) pp, (int) tiosp);
 #endif
 
 	memset(pp, 0, sizeof(asyport_t));
@@ -2615,7 +2707,7 @@
 	pp->baudout = tiosp->c_cflag & CBAUD;
 	if (pp->baudout & CBAUDEX) {
 		pp->baudout &= ~CBAUDEX;
-		if ((pp->baudout < 1) || (pp->baudout > 2))
+		if ((pp->baudout < 1) || (pp->baudout > 5))
 			tiosp->c_cflag &= ~CBAUDEX;
 		else
 			pp->baudout += 15;
@@ -2706,7 +2798,10 @@
 /*
  *	Transfer any persistent flags into the asyport structure.
  */
-	pp->pflag = portp->pflag;
+	pp->pflag = (portp->pflag & 0xffff);
+	pp->vmin = (portp->pflag & P_RXIMIN) ? 1 : 0;
+	pp->vtime = (portp->pflag & P_RXITIME) ? 1 : 0;
+	pp->cc[1] = (portp->pflag & P_RXTHOLD) ? 1 : 0;
 }
 
 /*****************************************************************************/
@@ -2765,7 +2860,7 @@
  *	we need to do here is set up the appropriate per port data structures.
  */
 
-static int stli_initports(stlibrd_t *brdp)
+static inline int stli_initports(stlibrd_t *brdp)
 {
 	stliport_t	*portp;
 	int		i, panelnr, panelport;
@@ -2855,11 +2950,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -2942,11 +3040,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+	printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n",
+		(int) brdp, (int) offset, line);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -2996,7 +3097,9 @@
 	unsigned char	val;
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -3071,11 +3174,14 @@
 	void	*ptr;
 
 #if DEBUG
-	printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 	} else {
 		ptr = brdp->membase + (offset % ONB_ATPAGESIZE);
@@ -3158,11 +3264,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+	printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n",
+		(int) brdp, (int) offset, line);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -3224,11 +3333,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -3282,11 +3394,14 @@
 	void	*ptr;
 
 #if DEBUG
-	printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 	} else {
 		ptr = brdp->membase + (offset % STAL_PAGESIZE);
@@ -3319,12 +3434,13 @@
  *	board types.
  */
 
-static int stli_initecp(stlibrd_t *brdp)
+static inline int stli_initecp(stlibrd_t *brdp)
 {
 	cdkecpsig_t	sig;
 	cdkecpsig_t	*sigsp;
 	unsigned int	status, nxtid;
-	int		panelnr;
+	char		*name;
+	int		panelnr, nrports;
 
 #if DEBUG
 	printk("stli_initecp(brdp=%x)\n", (int) brdp);
@@ -3336,6 +3452,11 @@
 	if ((brdp->iobase == 0) || (brdp->memaddr == 0))
 		return(-ENODEV);
 
+	brdp->iosize = ECP_IOSIZE;
+	if (check_region(brdp->iobase, brdp->iosize))
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, brdp->iobase);
+
 /*
  *	Based on the specific board type setup the common vars to access
  *	and enable shared memory. Set all board specific information now
@@ -3353,6 +3474,7 @@
 		brdp->getmemptr = stli_ecpgetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_ecpreset;
+		name = "serial(EC8/64)";
 		break;
 
 	case BRD_ECPE:
@@ -3366,6 +3488,7 @@
 		brdp->getmemptr = stli_ecpeigetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_ecpeireset;
+		name = "serial(EC8/64-EI)";
 		break;
 
 	case BRD_ECPMC:
@@ -3379,6 +3502,7 @@
 		brdp->getmemptr = stli_ecpmcgetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_ecpmcreset;
+		name = "serial(EC8/64-MCA)";
 		break;
 
 	default:
@@ -3410,10 +3534,11 @@
 	EBRDDISABLE(brdp);
 
 #if 0
-	printk("%s(%d): sig-> magic=%x romver=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
+	printk("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
 		__FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0],
-		(int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3],
-		(int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6],
+		(int) sig.panelid[1], (int) sig.panelid[2],
+		(int) sig.panelid[3], (int) sig.panelid[4],
+		(int) sig.panelid[5], (int) sig.panelid[6],
 		(int) sig.panelid[7]);
 #endif
 
@@ -3428,20 +3553,18 @@
 		status = sig.panelid[nxtid];
 		if ((status & ECH_PNLIDMASK) != nxtid)
 			break;
-		if (status & ECH_PNL16PORT) {
-			brdp->panels[panelnr] = 16;
-			brdp->nrports += 16;
-			nxtid += 2;
-		} else {
-			brdp->panels[panelnr] = 8;
-			brdp->nrports += 8;
-			nxtid++;
-		}
+
 		brdp->panelids[panelnr] = status;
+		nrports = (status & ECH_PNL16PORT) ? 16 : 8;
+		if ((nrports == 16) && ((status & ECH_PNLXPID) == 0))
+			nxtid++;
+		brdp->panels[panelnr] = nrports;
+		brdp->nrports += nrports;
+		nxtid++;
 		brdp->nrpanels++;
 	}
 
-	request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)");
+	request_region(brdp->iobase, brdp->iosize, name);
 	brdp->state |= BST_FOUND;
 	return(0);
 }
@@ -3453,10 +3576,11 @@
  *	This handles only these board types.
  */
 
-static int stli_initonb(stlibrd_t *brdp)
+static inline int stli_initonb(stlibrd_t *brdp)
 {
 	cdkonbsig_t	sig;
 	cdkonbsig_t	*sigsp;
+	char		*name;
 	int		i;
 
 #if DEBUG
@@ -3469,6 +3593,11 @@
 	if ((brdp->iobase == 0) || (brdp->memaddr == 0))
 		return(-ENODEV);
 
+	brdp->iosize = ONB_IOSIZE;
+	if (check_region(brdp->iobase, brdp->iosize))
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, brdp->iobase);
+
 /*
  *	Based on the specific board type setup the common vars to access
  *	and enable shared memory. Set all board specific information now
@@ -3494,6 +3623,7 @@
 			brdp->enabval = ONB_MEMENABHI;
 		else
 			brdp->enabval = ONB_MEMENABLO;
+		name = "serial(ONBoard)";
 		break;
 
 	case BRD_ONBOARDE:
@@ -3507,6 +3637,7 @@
 		brdp->getmemptr = stli_onbegetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_onbereset;
+		name = "serial(ONBoard/E)";
 		break;
 
 	case BRD_BRUMBY4:
@@ -3522,6 +3653,7 @@
 		brdp->getmemptr = stli_bbygetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_bbyreset;
+		name = "serial(Brumby)";
 		break;
 
 	case BRD_STALLION:
@@ -3535,6 +3667,7 @@
 		brdp->getmemptr = stli_stalgetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_stalreset;
+		name = "serial(Stallion)";
 		break;
 
 	default:
@@ -3572,7 +3705,7 @@
 #endif
 
 	if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) ||
-			(sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
+	    (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
 		return(-ENODEV);
 
 /*
@@ -3591,7 +3724,7 @@
 	}
 	brdp->panels[0] = brdp->nrports;
 
-	request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)");
+	request_region(brdp->iobase, brdp->iosize, name);
 	brdp->state |= BST_FOUND;
 	return(0);
 }
@@ -3626,14 +3759,16 @@
 	nrdevs = hdrp->nrdevs;
 
 #if 0
-	printk("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x slavep=%x\n",
+	printk("%s(%d): CDK version %d.%d.%d --> "
+		"nrdevs=%d memp=%x hostp=%x slavep=%x\n",
 		 __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification,
 		 hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp,
 		 (int) hdrp->slavep);
 #endif
 
 	if (nrdevs < (brdp->nrports + 1)) {
-		printk("STALLION: slave failed to allocate memory for all devices, devices=%d\n", nrdevs);
+		printk("STALLION: slave failed to allocate memory for all "
+			"devices, devices=%d\n", nrdevs);
 		brdp->nrports = nrdevs - 1;
 	}
 	brdp->nrdevs = nrdevs;
@@ -3671,6 +3806,8 @@
 		portp->portbit = (unsigned char) (0x1 << (i % 8));
 	}
 
+	hdrp->slavereq = 0xff;
+
 /*
  *	For each port setup a local copy of the RX and TX buffer offsets
  *	and sizes. We do this separate from the above, because we need to
@@ -3743,20 +3880,27 @@
 	case BRD_ECH:
 	case BRD_ECHMC:
 	case BRD_ECHPCI:
-		printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]);
+		printk("STALLION: %s board type not supported in this driver\n",
+			stli_brdnames[brdp->brdtype]);
 		return(ENODEV);
 	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+		printk("STALLION: unit=%d is unknown board type=%d\n",
+			brdp->brdnr, brdp->brdtype);
 		return(ENODEV);
 	}
 
 	if ((brdp->state & BST_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr);
+		printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n",
+			stli_brdnames[brdp->brdtype], brdp->brdnr,
+			brdp->iobase, (int) brdp->memaddr);
 		return(ENODEV);
 	}
 
 	stli_initports(brdp);
-	printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports);
+	printk("STALLION: %s found, unit=%d io=%x mem=%x "
+		"nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype],
+		brdp->brdnr, brdp->iobase, (int) brdp->memaddr,
+		brdp->nrpanels, brdp->nrports);
 	return(0);
 }
 
@@ -3767,7 +3911,7 @@
  *	might be. This is a bit if hack, but it is the best we can do.
  */
 
-static int stli_eisamemprobe(stlibrd_t *brdp)
+static inline int stli_eisamemprobe(stlibrd_t *brdp)
 {
 	cdkecpsig_t	ecpsig, *ecpsigp;
 	cdkonbsig_t	onbsig, *onbsigp;
@@ -3820,15 +3964,19 @@
 				continue;
 		}
 		if (brdp->brdtype == BRD_ECPE) {
-			ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, CDK_SIGADDR, __LINE__);
+			ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp,
+				CDK_SIGADDR, __LINE__);
 			memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
 			if (ecpsig.magic == ECP_MAGIC)
 				foundit = 1;
 		} else {
-			onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, CDK_SIGADDR, __LINE__);
+			onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp,
+				CDK_SIGADDR, __LINE__);
 			memcpy(&onbsig, onbsigp, sizeof(cdkonbsig_t));
-			if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) &&
-					(onbsig.magic2 == ONB_MAGIC2) && (onbsig.magic3 == ONB_MAGIC3))
+			if ((onbsig.magic0 == ONB_MAGIC0) &&
+			    (onbsig.magic1 == ONB_MAGIC1) &&
+			    (onbsig.magic2 == ONB_MAGIC2) &&
+			    (onbsig.magic3 == ONB_MAGIC3))
 				foundit = 1;
 		}
 		if (brdp->memaddr >= 0x100000)
@@ -3849,7 +3997,9 @@
 	if (! foundit) {
 		brdp->memaddr = 0;
 		brdp->membase = 0;
-		printk("STALLION: failed to probe shared memory region for %s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], (brdp->iobase >> 12));
+		printk("STALLION: failed to probe shared memory region for "
+			"%s in EISA slot=%d\n", stli_brdnames[brdp->brdtype],
+			(brdp->iobase >> 12));
 		return(-ENODEV);
 	}
 	return(0);
@@ -3867,7 +4017,7 @@
  *	do is go probing around in the usual places hoping we can find it.
  */
 
-static int stli_findeisabrds()
+static inline int stli_findeisabrds()
 {
 	stlibrd_t	*brdp;
 	unsigned int	iobase, eid;
@@ -3915,7 +4065,8 @@
  *		info table.
  */
 		if (stli_nrbrds >= STL_MAXBRDS) {
-			printk("STALLION: no room for more probed boards, maximum supported %d\n", STL_MAXBRDS);
+			printk("STALLION: no room for more probed boards, "
+				"maximum supported %d\n", STL_MAXBRDS);
 			break;
 		}
 
@@ -3925,7 +4076,8 @@
  */
 		brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
 		if (brdp == (stlibrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t));
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlibrd_t));
 			return(-ENOMEM);
 		}
 		memset(brdp, 0, sizeof(stlibrd_t));
@@ -3956,7 +4108,7 @@
  *	can find.
  */
 
-static int stli_initbrds()
+static inline int stli_initbrds()
 {
 	stlibrd_t	*brdp, *nxtbrdp;
 	stlconf_t	*confp;
@@ -3967,7 +4119,8 @@
 #endif
 
 	if (stli_nrbrds > STL_MAXBRDS) {
-		printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+		printk("STALLION: too many boards in configuration table, "
+			"truncating to %d\n", STL_MAXBRDS);
 		stli_nrbrds = STL_MAXBRDS;
 	}
 
@@ -3979,7 +4132,8 @@
 		confp = &stli_brdconf[i];
 		brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
 		if (brdp == (stlibrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t));
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlibrd_t));
 			return(-ENOMEM);
 		}
 		memset(brdp, 0, sizeof(stlibrd_t));
@@ -4013,7 +4167,9 @@
 				nxtbrdp = stli_brds[j];
 				if (nxtbrdp == (stlibrd_t *) NULL)
 					continue;
-				if ((brdp->membase >= nxtbrdp->membase) && (brdp->membase <= (nxtbrdp->membase + nxtbrdp->memsize - 1))) {
+				if ((brdp->membase >= nxtbrdp->membase) &&
+				    (brdp->membase <= (nxtbrdp->membase +
+				    nxtbrdp->memsize - 1))) {
 					stli_shared++;
 					break;
 				}
@@ -4053,7 +4209,8 @@
 	int		brdnr, size, n;
 
 #if DEBUG
-	printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip, (int) fp, (int) buf, count);
+	printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%d)\n", (int) ip,
+		(int) fp, (int) buf, count);
 #endif
 
 	brdnr = MINOR(ip->i_rdev);
@@ -4103,7 +4260,8 @@
 	int		brdnr, size, n;
 
 #if DEBUG
-	printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip, (int) fp, (int) buf, count);
+	printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%x)\n", (int) ip,
+		(int) fp, (int) buf, count);
 #endif
 
 	brdnr = MINOR(ip->i_rdev);
@@ -4205,31 +4363,28 @@
  *	what port to get stats for (used through board control device).
  */
 
-static int stli_getportstats(stliport_t *portp, comstats_t *cp)
+static int stli_portcmdstats(stliport_t *portp)
 {
 	unsigned long	flags;
 	stlibrd_t	*brdp;
 	int		rc;
 
-	if (portp == (stliport_t *) NULL) {
-		memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t));
-		portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port);
-		if (portp == (stliport_t *) NULL)
-			return(-ENODEV);
-	}
+	memset(&stli_comstats, 0, sizeof(comstats_t));
 
+	if (portp == (stliport_t *) NULL)
+		return(-ENODEV);
 	brdp = stli_brds[portp->brdnr];
 	if (brdp == (stlibrd_t *) NULL)
 		return(-ENODEV);
 
 	if (brdp->state & BST_STARTED) {
-		if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats, sizeof(asystats_t), 1)) < 0)
+		if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
+		    &stli_cdkstats, sizeof(asystats_t), 1)) < 0)
 			return(rc);
 	} else {
 		memset(&stli_cdkstats, 0, sizeof(asystats_t));
 	}
 
-	memset(&stli_comstats, 0, sizeof(comstats_t));
 	stli_comstats.brd = portp->brdnr;
 	stli_comstats.panel = portp->panelnr;
 	stli_comstats.port = portp->portnr;
@@ -4272,6 +4427,37 @@
 	stli_comstats.hwid = stli_cdkstats.hwid;
 	stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
 
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the port stats structure to user app. A NULL port struct
+ *	pointer passed in means that we need to find out from the app
+ *	what port to get stats for (used through board control device).
+ */
+
+static int stli_getportstats(stliport_t *portp, comstats_t *cp)
+{
+	stlibrd_t	*brdp;
+	int		rc;
+
+	if (portp == (stliport_t *) NULL) {
+		memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t));
+		portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
+			stli_comstats.port);
+		if (portp == (stliport_t *) NULL)
+			return(-ENODEV);
+	}
+
+	brdp = stli_brds[portp->brdnr];
+	if (brdp == (stlibrd_t *) NULL)
+		return(-ENODEV);
+
+	if ((rc = stli_portcmdstats(portp)) < 0)
+		return(rc);
+
 	memcpy_tofs(cp, &stli_comstats, sizeof(comstats_t));
 	return(0);
 }
@@ -4289,7 +4475,8 @@
 
 	if (portp == (stliport_t *) NULL) {
 		memcpy_fromfs(&stli_comstats, cp, sizeof(comstats_t));
-		portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port);
+		portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
+			stli_comstats.port);
 		if (portp == (stliport_t *) NULL)
 			return(-ENODEV);
 	}
@@ -4354,6 +4541,26 @@
 /*****************************************************************************/
 
 /*
+ *	Memory device open code. Need to keep track of opens and close
+ *	for module handling.
+ */
+
+static int stli_memopen(struct inode *ip, struct file *fp)
+{
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+/*****************************************************************************/
+
+static void stli_memclose(struct inode *ip, struct file *fp)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/*****************************************************************************/
+
+/*
  *	The "staliomem" device is also required to do some special operations on
  *	the board. We need to be able to send an interrupt to the board,
  *	reset it, and start/stop it.
@@ -4365,7 +4572,8 @@
 	int		brdnr, rc, done;
 
 #if DEBUG
-	printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+	printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip,
+		(int) fp, cmd, (int) arg);
 #endif
 
 /*
@@ -4376,27 +4584,34 @@
 
 	switch (cmd) {
 	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stli_getportstats((stliport_t *) NULL, (comstats_t *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stli_getportstats((stliport_t *) NULL,
+				(comstats_t *) arg);
 		done++;
 		break;
 	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stli_clrportstats((stliport_t *) NULL, (comstats_t *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stli_clrportstats((stliport_t *) NULL,
+				(comstats_t *) arg);
 		done++;
 		break;
 	case COM_GETBRDSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(combrd_t))) == 0)
 			rc = stli_getbrdstats((combrd_t *) arg);
 		done++;
 		break;
 	case COM_READPORT:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stliport_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stliport_t))) == 0)
 			rc = stli_getportstruct(arg);
 		done++;
 		break;
 	case COM_READBOARD:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlibrd_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stlibrd_t))) == 0)
 			rc = stli_getbrdstruct(arg);
 		done++;
 		break;
@@ -4450,7 +4665,7 @@
 
 int stli_init()
 {
-	printk(KERN_INFO "%s: version %s\n", stli_drvname, stli_drvversion);
+	printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion);
 
 	stli_initbrds();
 
@@ -4459,10 +4674,12 @@
  */
 	stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
 	if (stli_tmpwritebuf == (char *) NULL)
-		printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			STLI_TXBUFSIZE);
 	stli_txcookbuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
 	if (stli_txcookbuf == (char *) NULL)
-		printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			STLI_TXBUFSIZE);
 
 /*
  *	Set up a character driver for the shared memory region. We need this
diff -u --recursive --new-file v2.0.34/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.0.34/linux/drivers/char/lp.c	Thu Apr  3 19:15:34 1997
+++ linux/drivers/char/lp.c	Mon Jul 13 13:47:29 1998
@@ -89,7 +89,14 @@
 	while(wait != LP_WAIT(minor)) wait++;
 	/* control port takes strobe high */
 	outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
-	while(wait) wait--;
+	/* Wait until NBUSY line goes high */
+	count = 0;
+	do {
+		status = LP_S(minor);
+		count++;
+		if (need_resched)
+			schedule();
+	} while (LP_READY(minor, status) && (count<LP_CHAR(minor)));
 	/* take strobe low */
 	outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
 	/* update waittime statistics */
diff -u --recursive --new-file v2.0.34/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v2.0.34/linux/drivers/char/pcxx.c	Sun Dec 22 05:07:00 1996
+++ linux/drivers/char/pcxx.c	Mon Jul 13 13:47:29 1998
@@ -591,7 +591,7 @@
 ** worth noting that while I'm not sure what this hunk of code is supposed
 ** to do, it is not present in the serial.c driver.  Hmmm.  If you know,
 ** please send me a note.  brian@ilinx.com
-** Dont know either what this is supposed to do clameter@waterf.org.
+** Don't know either what this is supposed to do clameter@waterf.org.
 */
 		if(tty->ldisc.num != ldiscs[N_TTY].num) {
 			if(tty->ldisc.close)
diff -u --recursive --new-file v2.0.34/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.0.34/linux/drivers/char/stallion.c	Mon May  6 02:26:06 1996
+++ linux/drivers/char/stallion.c	Mon Jul 13 13:47:30 1998
@@ -3,6 +3,7 @@
 /*
  *	stallion.c  -- stallion multiport serial driver.
  *
+ *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This code is loosely based on the Linux serial driver, written by
@@ -37,12 +38,13 @@
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/cd1400.h>
+#include <linux/sc26198.h>
 #include <linux/comstats.h>
 #include <linux/stallion.h>
 #include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/ioport.h>
-#include <linux/config.h>	/* for CONFIG_PCI */
+#include <linux/config.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -55,16 +57,16 @@
 /*****************************************************************************/
 
 /*
- *	Define different board types. At the moment I have only declared
- *	those boards that this driver supports. But I will use the standard
- *	"assigned" board numbers. In the future this driver will support
- *	some of the other Stallion boards. Currently supported boards are
- *	abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32.
+ *	Define different board types. Use the standard Stallion "assigned"
+ *	board numbers. Boards supported in this driver are abbreviated as
+ *	EIO = EasyIO and ECH = EasyConnection 8/32.
  */
 #define	BRD_EASYIO	20
 #define	BRD_ECH		21
 #define	BRD_ECHMC	22
 #define	BRD_ECHPCI	26
+#define	BRD_ECH64PCI	27
+#define	BRD_EASYIOPCI	28
 
 /*
  *	Define a configuration structure to hold the board configuration.
@@ -126,12 +128,11 @@
 #define	STL_DRVTYPCALLOUT	2
 
 /*
- *	I haven't really decided (or measured) what TX buffer size gives
- *	a good balance between performance and memory usage. These seem
- *	to work pretty well...
+ *	Set the TX buffer size. Bigger is better, but we don't want
+ *	to chew too much memory with buffers!
  */
-#define	STL_TXBUFLOW		256
-#define	STL_TXBUFSIZE		2048
+#define	STL_TXBUFLOW		512
+#define	STL_TXBUFSIZE		4096
 
 /*****************************************************************************/
 
@@ -139,8 +140,8 @@
  *	Define our local driver identity first. Set up stuff to deal with
  *	all the local structures required by a serial tty driver.
  */
-static char	*stl_drvname = "Stallion Multiport Serial Driver";
-static char	*stl_drvversion = "1.1.3";
+static char	*stl_drvtitle = "Stallion Multiport Serial Driver";
+static char	*stl_drvversion = "5.4.4";
 static char	*stl_serialname = "ttyE";
 static char	*stl_calloutname = "cue";
 
@@ -185,6 +186,11 @@
 static stlport_t	stl_dummyport;
 
 /*
+ *	Define global place to put buffer overflow characters.
+ */
+static char		stl_unwanted[SC26198_RXFIFOSIZE];
+
+/*
  *	Keep track of what interrupts we have requested for us.
  *	We don't need to request an interrupt twice if it is being
  *	shared with another Stallion board.
@@ -198,7 +204,7 @@
 
 /*
  *	Per board state flags. Used with the state field of the board struct.
- *	Not really much here yet!
+ *	Not really much here!
  */
 #define	BRD_FOUND	0x1
 
@@ -210,6 +216,7 @@
 #define	ASYI_TXBUSY	1
 #define	ASYI_TXLOW	2
 #define	ASYI_DCDCHANGE	3
+#define	ASYI_TXFLOWED	4
 
 /*
  *	Define an array of board names as printable strings. Handy for
@@ -243,23 +250,33 @@
 	(char *) NULL,
 	(char *) NULL,
 	"EC8/32-PCI",
+	"EC8/64-PCI",
+	"EasyIO-PCI",
 };
 
 /*****************************************************************************/
 
 /*
  *	Hardware ID bits for the EasyIO and ECH boards. These defines apply
- *	to the directly accessible io ports of these boards (not the cd1400
- *	uarts - they are in cd1400.h).
+ *	to the directly accessible io ports of these boards (not the uarts -
+ *	they are in cd1400.h and sc26198.h).
  */
 #define	EIO_8PORTRS	0x04
 #define	EIO_4PORTRS	0x05
 #define	EIO_8PORTDI	0x00
 #define	EIO_8PORTM	0x06
+#define	EIO_MK3		0x03
 #define	EIO_IDBITMASK	0x07
+
+#define	EIO_BRDMASK	0xf0
+#define	ID_BRD4		0x10
+#define	ID_BRD8		0x20
+#define	ID_BRD16	0x30
+
 #define	EIO_INTRPEND	0x08
 #define	EIO_INTEDGE	0x00
 #define	EIO_INTLEVEL	0x08
+#define	EIO_0WS		0x10
 
 #define	ECH_ID		0xa0
 #define	ECH_IDBITMASK	0xe0
@@ -278,24 +295,10 @@
 #define	ECH_PNLSTATUS	2
 #define	ECH_PNL16PORT	0x20
 #define	ECH_PNLIDMASK	0x07
+#define	ECH_PNLXPID	0x40
 #define	ECH_PNLINTRPEND	0x80
-#define	ECH_ADDR2MASK	0x1e0
-
-#define	EIO_CLK		25000000
-#define	EIO_CLK8M	20000000
-#define	ECH_CLK		EIO_CLK
-
-/*
- *	Define the offsets within the register bank for all io registers.
- *	These io address offsets are common to both the EIO and ECH.
- */
-#define	EREG_ADDR	0
-#define	EREG_DATA	4
-#define	EREG_RXACK	5
-#define	EREG_TXACK	6
-#define	EREG_MDACK	7
 
-#define	EREG_BANKSIZE	8
+#define	ECH_ADDR2MASK	0x1e0
 
 /*
  *	Define the vector mapping bits for the programmable interrupt board
@@ -326,18 +329,51 @@
 		outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE),	\
 			stl_brds[(brdnr)]->ioctrl);
 
+#define	STL_CD1400MAXBAUD	230400
+#define	STL_SC26198MAXBAUD	460800
+
+#define	STL_BAUDBASE		115200
+#define	STL_CLOSEDELAY		(5 * HZ / 10)
+
+/*****************************************************************************/
+
+#ifdef CONFIG_PCI
+
 /*
- *	Define the cd1400 baud rate clocks. These are used when calculating
- *	what clock and divisor to use for the required baud rate. Also
- *	define the maximum baud rate allowed, and the default base baud.
+ *	Define the Stallion PCI vendor and device IDs.
  */
-static int	stl_cd1400clkdivs[] = {
-	CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
+#ifndef	PCI_VENDOR_ID_STALLION
+#define	PCI_VENDOR_ID_STALLION		0x124d
+#endif
+#ifndef PCI_DEVICE_ID_ECHPCI832
+#define	PCI_DEVICE_ID_ECHPCI832		0x0000
+#endif
+#ifndef PCI_DEVICE_ID_ECHPCI864
+#define	PCI_DEVICE_ID_ECHPCI864		0x0002
+#endif
+#ifndef PCI_DEVICE_ID_EIOPCI
+#define	PCI_DEVICE_ID_EIOPCI		0x0003
+#endif
+
+/*
+ *	Define structure to hold all Stallion PCI boards.
+ */
+typedef struct stlpcibrd {
+	unsigned short		vendid;
+	unsigned short		devid;
+	int			brdtype;
+} stlpcibrd_t;
+
+static stlpcibrd_t	stl_pcibrds[] = {
+	{ PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864, BRD_ECH64PCI },
+	{ PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI, BRD_EASYIOPCI },
+	{ PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, BRD_ECHPCI },
+	{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, BRD_ECHPCI },
 };
 
-#define	STL_MAXBAUD	230400
-#define	STL_BAUDBASE	115200
-#define	STL_CLOSEDELAY	50
+static int	stl_nrpcibrds = sizeof(stl_pcibrds) / sizeof(stlpcibrd_t);
+
+#endif
 
 /*****************************************************************************/
 
@@ -349,16 +385,14 @@
 
 /*
  *	Define a baud rate table that converts termios baud rate selector
- *	into the actual baud rate value. All baud rate calculates are based
- *	on the actual baud rate required.
+ *	into the actual baud rate value. All baud rate calculations are
+ *	based on the actual baud rate required.
  */
 static unsigned int	stl_baudrates[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
 };
 
-/*****************************************************************************/
-
 /*
  *	Define some handy local macros...
  */
@@ -392,15 +426,15 @@
 static void	stl_stop(struct tty_struct *tty);
 static void	stl_start(struct tty_struct *tty);
 static void	stl_flushbuffer(struct tty_struct *tty);
+static void	stl_waituntilsent(struct tty_struct *tty, int timeout);
 static void	stl_hangup(struct tty_struct *tty);
+static int	stl_memopen(struct inode *ip, struct file *fp);
+static void	stl_memclose(struct inode *ip, struct file *fp);
 static int	stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
 
-static int	stl_initbrds(void);
 static int	stl_brdinit(stlbrd_t *brdp);
-static int	stl_initeio(stlbrd_t *brdp);
-static int	stl_initech(stlbrd_t *brdp);
 static int	stl_initports(stlbrd_t *brdp, stlpanel_t *panelp);
-static int	stl_mapirq(int irq);
+static int	stl_mapirq(int irq, char *name);
 static void	stl_getserial(stlport_t *portp, struct serial_struct *sp);
 static int	stl_setserial(stlport_t *portp, struct serial_struct *sp);
 static int	stl_getbrdstats(combrd_t *bp);
@@ -408,28 +442,212 @@
 static int	stl_clrportstats(stlport_t *portp, comstats_t *cp);
 static int	stl_getportstruct(unsigned long arg);
 static int	stl_getbrdstruct(unsigned long arg);
-static void	stl_setreg(stlport_t *portp, int regnr, int value);
-static int	stl_getreg(stlport_t *portp, int regnr);
-static int	stl_updatereg(stlport_t *portp, int regnr, int value);
-static void	stl_setport(stlport_t *portp, struct termios *tiosp);
-static int	stl_getsignals(stlport_t *portp);
-static void	stl_setsignals(stlport_t *portp, int dtr, int rts);
-static void	stl_ccrwait(stlport_t *portp);
-static void	stl_enablerxtx(stlport_t *portp, int rx, int tx);
-static void	stl_startrxtx(stlport_t *portp, int rx, int tx);
-static void	stl_disableintrs(stlport_t *portp);
-static void	stl_sendbreak(stlport_t *portp, long len);
 static int	stl_waitcarrier(stlport_t *portp, struct file *filp);
 static void	stl_delay(int len);
 static void	stl_intr(int irq, void *dev_id, struct pt_regs *regs);
+static void	stl_eiointr(stlbrd_t *brdp);
+static void	stl_echatintr(stlbrd_t *brdp);
+static void	stl_echmcaintr(stlbrd_t *brdp);
+static void	stl_echpciintr(stlbrd_t *brdp);
+static void	stl_echpci64intr(stlbrd_t *brdp);
 static void	stl_offintr(void *private);
 static void	*stl_memalloc(int len);
 static stlport_t *stl_getport(int brdnr, int panelnr, int portnr);
 
+static inline int	stl_initbrds(void);
+static inline int	stl_initeio(stlbrd_t *brdp);
+static inline int	stl_initech(stlbrd_t *brdp);
+
 #ifdef	CONFIG_PCI
-static int	stl_findpcibrds(void);
+static inline int	stl_findpcibrds(void);
+static inline int	stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr);
 #endif
 
+/*
+ *	CD1400 uart specific handling functions.
+ */
+static void	stl_cd1400setreg(stlport_t *portp, int regnr, int value);
+static int	stl_cd1400getreg(stlport_t *portp, int regnr);
+static int	stl_cd1400updatereg(stlport_t *portp, int regnr, int value);
+static int	stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp);
+static void	stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+static void	stl_cd1400setport(stlport_t *portp, struct termios *tiosp);
+static int	stl_cd1400getsignals(stlport_t *portp);
+static void	stl_cd1400setsignals(stlport_t *portp, int dtr, int rts);
+static void	stl_cd1400ccrwait(stlport_t *portp);
+static void	stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx);
+static void	stl_cd1400startrxtx(stlport_t *portp, int rx, int tx);
+static void	stl_cd1400disableintrs(stlport_t *portp);
+static void	stl_cd1400sendbreak(stlport_t *portp, long len);
+static void	stl_cd1400flowctrl(stlport_t *portp, int state);
+static void	stl_cd1400sendflow(stlport_t *portp, int state);
+static void	stl_cd1400flush(stlport_t *portp);
+static int	stl_cd1400datastate(stlport_t *portp);
+static void	stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase);
+static void	stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase);
+static void	stl_cd1400txisr(stlpanel_t *panelp, int ioaddr);
+static void	stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr);
+static void	stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr);
+
+/*
+ *	SC26198 uart specific handling functions.
+ */
+static void	stl_sc26198setreg(stlport_t *portp, int regnr, int value);
+static int	stl_sc26198getreg(stlport_t *portp, int regnr);
+static int	stl_sc26198updatereg(stlport_t *portp, int regnr, int value);
+static int	stl_sc26198getglobreg(stlport_t *portp, int regnr);
+static int	stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp);
+static void	stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+static void	stl_sc26198setport(stlport_t *portp, struct termios *tiosp);
+static int	stl_sc26198getsignals(stlport_t *portp);
+static void	stl_sc26198setsignals(stlport_t *portp, int dtr, int rts);
+static void	stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx);
+static void	stl_sc26198startrxtx(stlport_t *portp, int rx, int tx);
+static void	stl_sc26198disableintrs(stlport_t *portp);
+static void	stl_sc26198sendbreak(stlport_t *portp, long len);
+static void	stl_sc26198flowctrl(stlport_t *portp, int state);
+static void	stl_sc26198sendflow(stlport_t *portp, int state);
+static void	stl_sc26198flush(stlport_t *portp);
+static int	stl_sc26198datastate(stlport_t *portp);
+static void	stl_sc26198wait(stlport_t *portp);
+static void	stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty);
+static void	stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase);
+static void	stl_sc26198txisr(stlport_t *port);
+static void	stl_sc26198rxisr(stlport_t *port, unsigned int iack);
+static void	stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch);
+static void	stl_sc26198rxbadchars(stlport_t *portp);
+static void	stl_sc26198otherisr(stlport_t *port, unsigned int iack);
+
+/*****************************************************************************/
+
+/*
+ *	Generic UART support structure.
+ */
+typedef struct uart {
+	int	(*panelinit)(stlbrd_t *brdp, stlpanel_t *panelp);
+	void	(*portinit)(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+	void	(*setport)(stlport_t *portp, struct termios *tiosp);
+	int	(*getsignals)(stlport_t *portp);
+	void	(*setsignals)(stlport_t *portp, int dtr, int rts);
+	void	(*enablerxtx)(stlport_t *portp, int rx, int tx);
+	void	(*startrxtx)(stlport_t *portp, int rx, int tx);
+	void	(*disableintrs)(stlport_t *portp);
+	void	(*sendbreak)(stlport_t *portp, long len);
+	void	(*flowctrl)(stlport_t *portp, int state);
+	void	(*sendflow)(stlport_t *portp, int state);
+	void	(*flush)(stlport_t *portp);
+	int	(*datastate)(stlport_t *portp);
+	void	(*intr)(stlpanel_t *panelp, unsigned int iobase);
+} uart_t;
+
+/*
+ *	Define some macros to make calling these functions nice and clean.
+ */
+#define	stl_panelinit		(* ((uart_t *) panelp->uartp)->panelinit)
+#define	stl_portinit		(* ((uart_t *) portp->uartp)->portinit)
+#define	stl_setport		(* ((uart_t *) portp->uartp)->setport)
+#define	stl_getsignals		(* ((uart_t *) portp->uartp)->getsignals)
+#define	stl_setsignals		(* ((uart_t *) portp->uartp)->setsignals)
+#define	stl_enablerxtx		(* ((uart_t *) portp->uartp)->enablerxtx)
+#define	stl_startrxtx		(* ((uart_t *) portp->uartp)->startrxtx)
+#define	stl_disableintrs	(* ((uart_t *) portp->uartp)->disableintrs)
+#define	stl_sendbreak		(* ((uart_t *) portp->uartp)->sendbreak)
+#define	stl_flowctrl		(* ((uart_t *) portp->uartp)->flowctrl)
+#define	stl_sendflow		(* ((uart_t *) portp->uartp)->sendflow)
+#define	stl_flush		(* ((uart_t *) portp->uartp)->flush)
+#define	stl_datastate		(* ((uart_t *) portp->uartp)->datastate)
+
+/*****************************************************************************/
+
+/*
+ *	CD1400 UART specific data initialization.
+ */
+static uart_t stl_cd1400uart = {
+	stl_cd1400panelinit,
+	stl_cd1400portinit,
+	stl_cd1400setport,
+	stl_cd1400getsignals,
+	stl_cd1400setsignals,
+	stl_cd1400enablerxtx,
+	stl_cd1400startrxtx,
+	stl_cd1400disableintrs,
+	stl_cd1400sendbreak,
+	stl_cd1400flowctrl,
+	stl_cd1400sendflow,
+	stl_cd1400flush,
+	stl_cd1400datastate,
+	stl_cd1400eiointr
+};
+
+/*
+ *	Define the offsets within the register bank of a cd1400 based panel.
+ *	These io address offsets are common to the EasyIO board as well.
+ */
+#define	EREG_ADDR	0
+#define	EREG_DATA	4
+#define	EREG_RXACK	5
+#define	EREG_TXACK	6
+#define	EREG_MDACK	7
+
+#define	EREG_BANKSIZE	8
+
+#define	CD1400_CLK	25000000
+#define	CD1400_CLK8M	20000000
+
+/*
+ *	Define the cd1400 baud rate clocks. These are used when calculating
+ *	what clock and divisor to use for the required baud rate. Also
+ *	define the maximum baud rate allowed, and the default base baud.
+ */
+static int	stl_cd1400clkdivs[] = {
+	CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
+};
+
+/*****************************************************************************/
+
+/*
+ *	SC26198 UART specific data initization.
+ */
+static uart_t stl_sc26198uart = {
+	stl_sc26198panelinit,
+	stl_sc26198portinit,
+	stl_sc26198setport,
+	stl_sc26198getsignals,
+	stl_sc26198setsignals,
+	stl_sc26198enablerxtx,
+	stl_sc26198startrxtx,
+	stl_sc26198disableintrs,
+	stl_sc26198sendbreak,
+	stl_sc26198flowctrl,
+	stl_sc26198sendflow,
+	stl_sc26198flush,
+	stl_sc26198datastate,
+	stl_sc26198intr
+};
+
+/*
+ *	Define the offsets within the register bank of a sc26198 based panel.
+ */
+#define	XP_DATA		0
+#define	XP_ADDR		1
+#define	XP_MODID	2
+#define	XP_STATUS	2
+#define	XP_IACK		3
+
+#define	XP_BANKSIZE	4
+
+/*
+ *	Define the sc26198 baud rate table. Offsets within the table
+ *	represent the actual baud rate selector of sc26198 registers.
+ */
+static unsigned int	sc26198_baudtable[] = {
+	50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600,
+	4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200,
+	230400, 460800, 921600
+};
+
+#define	SC26198_NRBAUDS		(sizeof(sc26198_baudtable) / sizeof(unsigned int))
+
 /*****************************************************************************/
 
 /*
@@ -444,8 +662,8 @@
 	NULL,
 	stl_memioctl,
 	NULL,
-	NULL,
-	NULL,
+	stl_memopen,
+	stl_memclose,
 	NULL
 };
 
@@ -487,7 +705,8 @@
 	printk("cleanup_module()\n");
 #endif
 
-	printk(KERN_INFO "Unloading %s: version %s\n", stl_drvname, stl_drvversion);
+	printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle,
+		stl_drvversion);
 
 	save_flags(flags);
 	cli();
@@ -501,12 +720,14 @@
 	i = tty_unregister_driver(&stl_serial);
 	j = tty_unregister_driver(&stl_callout);
 	if (i || j) {
-		printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+		printk("STALLION: failed to un-register tty driver, "
+			"errno=%d,%d\n", -i, -j);
 		restore_flags(flags);
 		return;
 	}
 	if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
-		printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i);
+		printk("STALLION: failed to un-register serial memory device, "
+			"errno=%d\n", -i);
 
 	if (stl_tmpwritebuf != (char *) NULL)
 		kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE);
@@ -515,33 +736,24 @@
 		brdp = stl_brds[i];
 		for (j = 0; (j < STL_MAXPANELS); j++) {
 			panelp = brdp->panels[j];
-			if (panelp != (stlpanel_t *) NULL) {
-				for (k = 0; (k < STL_PORTSPERPANEL); k++) {
-					portp = panelp->ports[k];
-					if (portp != (stlport_t *) NULL) {
-						if (portp->tty != (struct tty_struct *) NULL)
-							stl_hangup(portp->tty);
-						if (portp->tx.buf != (char *) NULL)
-							kfree_s(portp->tx.buf, STL_TXBUFSIZE);
-						kfree_s(portp, sizeof(stlport_t));
-					}
-				}
-				kfree_s(panelp, sizeof(stlpanel_t));
+			if (panelp == (stlpanel_t *) NULL)
+				continue;
+			for (k = 0; (k < STL_PORTSPERPANEL); k++) {
+				portp = panelp->ports[k];
+				if (portp == (stlport_t *) NULL)
+					continue;
+				if (portp->tty != (struct tty_struct *) NULL)
+					stl_hangup(portp->tty);
+				if (portp->tx.buf != (char *) NULL)
+					kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+				kfree_s(portp, sizeof(stlport_t));
 			}
-			
+			kfree_s(panelp, sizeof(stlpanel_t));
 		}
 
-		if (brdp->brdtype == BRD_ECH) {
-			release_region(brdp->ioaddr1, 2);
-			release_region(brdp->ioaddr2, 32);
-		} else if (brdp->brdtype == BRD_ECHPCI) {
-			release_region(brdp->ioaddr1, 4);
-			release_region(brdp->ioaddr2, 8);
-		} else if (brdp->brdtype == BRD_ECHMC) {
-			release_region(brdp->ioaddr1, 64);
-		} else if (brdp->brdtype == BRD_EASYIO) {
-			release_region(brdp->ioaddr1, 8);
-		}
+		release_region(brdp->ioaddr1, brdp->iosize1);
+		if (brdp->iosize2 > 0)
+			release_region(brdp->ioaddr2, brdp->iosize2);
 
 		kfree_s(brdp, sizeof(stlbrd_t));
 		stl_brds[i] = (stlbrd_t *) NULL;
@@ -576,7 +788,8 @@
 	int		brdnr, panelnr, portnr, rc;
 
 #if DEBUG
-	printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+	printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty,
+		(int) filp, tty->device);
 #endif
 
 	minordev = MINOR(tty->device);
@@ -603,6 +816,8 @@
 	if (portp == (stlport_t *) NULL)
 		return(-ENODEV);
 
+	MOD_INC_USE_COUNT;
+
 /*
  *	On the first open of the device setup the port hardware, and
  *	initialize the per port data structure.
@@ -651,10 +866,10 @@
 			return(-EBUSY);
 		if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
 			if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
-					(portp->session != current->session))
+			    (portp->session != current->session))
 				return(-EBUSY);
 			if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
-					(portp->pgrp != current->pgrp))
+			    (portp->pgrp != current->pgrp))
 				return(-EBUSY);
 		}
 		portp->flags |= ASYNC_CALLOUT_ACTIVE;
@@ -712,13 +927,14 @@
 	save_flags(flags);
 	cli();
 	portp->openwaitcnt++;
-	if (portp->refcount > 0)
+	if (! tty_hung_up_p(filp))
 		portp->refcount--;
 
 	for (;;) {
 		if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0)
 			stl_setsignals(portp, 1, 1);
-		if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+		if (tty_hung_up_p(filp) ||
+		    ((portp->flags & ASYNC_INITIALIZED) == 0)) {
 			if (portp->flags & ASYNC_HUP_NOTIFY)
 				rc = -EBUSY;
 			else
@@ -726,8 +942,8 @@
 			break;
 		}
 		if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
-				((portp->flags & ASYNC_CLOSING) == 0) &&
-				(doclocal || (portp->sigs & TIOCM_CD))) {
+		    ((portp->flags & ASYNC_CLOSING) == 0) &&
+		    (doclocal || (portp->sigs & TIOCM_CD))) {
 			break;
 		}
 		if (current->signal & ~current->blocked) {
@@ -763,10 +979,14 @@
 	save_flags(flags);
 	cli();
 	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
+	if ((tty->count == 1) && (portp->refcount != 1))
+		portp->refcount = 1;
 	if (portp->refcount-- > 1) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
@@ -781,14 +1001,14 @@
 
 /*
  *	May want to wait for any data to drain before closing. The BUSY
- *	flag keeps track of whether we are still sending or not - it allows
- *	for the FIFO in the cd1400.
+ *	flag keeps track of whether we are still sending or not - it is
+ *	very accurate for the cd1400, not quite so for the sc26198.
+ *	(The sc26198 has no "end-of-data" interrupt only empty FIFO)
  */
 	tty->closing = 1;
-	if (test_bit(ASYI_TXBUSY, &portp->istate)) {
-		if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-			tty_wait_until_sent(tty, portp->closing_wait);
-	}
+	if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, portp->closing_wait);
+	stl_waituntilsent(tty, (HZ / 2));
 
 	portp->flags &= ~ASYNC_INITIALIZED;
 	stl_disableintrs(portp);
@@ -808,7 +1028,6 @@
 		(tty->ldisc.flush_buffer)(tty);
 
 	tty->closing = 0;
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 
 	if (portp->openwaitcnt) {
@@ -817,8 +1036,10 @@
 		wake_up_interruptible(&portp->open_wait);
 	}
 
-	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE |
+		ASYNC_CLOSING);
 	wake_up_interruptible(&portp->close_wait);
+	MOD_DEC_USE_COUNT;
 	restore_flags(flags);
 }
 
@@ -858,10 +1079,12 @@
 	char		*head, *tail;
 
 #if DEBUG
-	printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+	printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n",
+		(int) tty, from_user, (int) buf, count);
 #endif
 
-	if ((tty == (struct tty_struct *) NULL) || (stl_tmpwritebuf == (char *) NULL))
+	if ((tty == (struct tty_struct *) NULL) ||
+	    (stl_tmpwritebuf == (char *) NULL))
 		return(0);
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
@@ -983,7 +1206,8 @@
 		return;
 
 #if 0
-	if (tty->stopped || tty->hw_stopped || (portp->tx.head == portp->tx.tail))
+	if (tty->stopped || tty->hw_stopped ||
+	    (portp->tx.head == portp->tx.tail))
 		return;
 #endif
 	stl_startrxtx(portp, -1, 1);
@@ -1066,7 +1290,6 @@
 #endif
 
 	memset(&sio, 0, sizeof(struct serial_struct));
-	sio.type = PORT_CIRRUS;
 	sio.line = portp->portnr;
 	sio.port = portp->ioaddr;
 	sio.flags = portp->flags;
@@ -1074,8 +1297,14 @@
 	sio.close_delay = portp->close_delay;
 	sio.closing_wait = portp->closing_wait;
 	sio.custom_divisor = portp->custom_divisor;
-	sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
 	sio.hub6 = 0;
+	if (portp->uartp == &stl_cd1400uart) {
+		sio.type = PORT_CIRRUS;
+		sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
+	} else {
+		sio.type = PORT_UNKNOWN;
+		sio.xmit_fifo_size = SC26198_TXFIFOSIZE;
+	}
 
 	brdp = stl_brds[portp->brdnr];
 	if (brdp != (stlbrd_t *) NULL)
@@ -1103,12 +1332,14 @@
 	memcpy_fromfs(&sio, sp, sizeof(struct serial_struct));
 	if (!suser()) {
 		if ((sio.baud_base != portp->baud_base) ||
-				(sio.close_delay != portp->close_delay) ||
-				((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+		    (sio.close_delay != portp->close_delay) ||
+		    ((sio.flags & ~ASYNC_USR_MASK) !=
+		    (portp->flags & ~ASYNC_USR_MASK)))
 			return(-EPERM);
 	} 
 
-	portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+	portp->flags = (portp->flags & ~ASYNC_USR_MASK) |
+		(sio.flags & ASYNC_USR_MASK);
 	portp->baud_base = sio.baud_base;
 	portp->close_delay = sio.close_delay;
 	portp->closing_wait = sio.closing_wait;
@@ -1126,7 +1357,8 @@
 	int		rc;
 
 #if DEBUG
-	printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+	printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n",
+		(int) tty, (int) file, cmd, (int) arg);
 #endif
 
 	if (tty == (struct tty_struct *) NULL)
@@ -1135,6 +1367,12 @@
 	if (portp == (stlport_t *) NULL)
 		return(-ENODEV);
 
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ 	    (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+			return(-EIO);
+	}
+
 	rc = 0;
 
 	switch (cmd) {
@@ -1152,53 +1390,69 @@
 		}
 		break;
 	case TIOCGSOFTCAR:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long))) == 0)
-			put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned long *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(long))) == 0)
+			put_fs_long(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
+				(unsigned long *) arg);
 		break;
 	case TIOCSSOFTCAR:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0);
+			tty->termios->c_cflag =
+				(tty->termios->c_cflag & ~CLOCAL) |
+				(arg ? CLOCAL : 0);
 		}
 		break;
 	case TIOCMGET:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			val = (unsigned long) stl_getsignals(portp);
 			put_fs_long(val, (unsigned long *) arg);
 		}
 		break;
 	case TIOCMBIS:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1), ((arg & TIOCM_RTS) ? 1 : -1));
+			stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : -1),
+				((arg & TIOCM_RTS) ? 1 : -1));
 		}
 		break;
 	case TIOCMBIC:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1), ((arg & TIOCM_RTS) ? 0 : -1));
+			stl_setsignals(portp, ((arg & TIOCM_DTR) ? 0 : -1),
+				((arg & TIOCM_RTS) ? 0 : -1));
 		}
 		break;
 	case TIOCMSET:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(long))) == 0) {
 			arg = get_fs_long((unsigned long *) arg);
-			stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0), ((arg & TIOCM_RTS) ? 1 : 0));
+			stl_setsignals(portp, ((arg & TIOCM_DTR) ? 1 : 0),
+				((arg & TIOCM_RTS) ? 1 : 0));
 		}
 		break;
 	case TIOCGSERIAL:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
 			stl_getserial(portp, (struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
 			rc = stl_setserial(portp, (struct serial_struct *) arg);
 		break;
 	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stl_getportstats(portp, (comstats_t *) arg);
 		break;
 	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stl_clrportstats(portp, (comstats_t *) arg);
 		break;
 	case TIOCSERCONFIG:
@@ -1234,11 +1488,13 @@
 		return;
 
 	tiosp = tty->termios;
-	if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+	if ((tiosp->c_cflag == old->c_cflag) &&
+	    (tiosp->c_iflag == old->c_iflag))
 		return;
 
 	stl_setport(portp, tiosp);
-	stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), -1);
+	stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0),
+		-1);
 	if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) {
 		tty->hw_stopped = 0;
 		stl_start(tty);
@@ -1257,7 +1513,6 @@
 static void stl_throttle(struct tty_struct *tty)
 {
 	stlport_t	*portp;
-	unsigned long	flags;
 
 #if DEBUG
 	printk("stl_throttle(tty=%x)\n", (int) tty);
@@ -1268,24 +1523,7 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	if (tty->termios->c_iflag & IXOFF) {
-		stl_ccrwait(portp);
-		stl_setreg(portp, CCR, CCR_SENDSCHR2);
-		portp->stats.rxxoff++;
-		stl_ccrwait(portp);
-	}
-	if (tty->termios->c_cflag & CRTSCTS) {
-		stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0));
-		stl_setreg(portp, MSVR2, 0);
-		portp->stats.rxrtsoff++;
-	}
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
+	stl_flowctrl(portp, 0);
 }
 
 /*****************************************************************************/
@@ -1297,7 +1535,6 @@
 static void stl_unthrottle(struct tty_struct *tty)
 {
 	stlport_t	*portp;
-	unsigned long	flags;
 
 #if DEBUG
 	printk("stl_unthrottle(tty=%x)\n", (int) tty);
@@ -1308,30 +1545,7 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	if (tty->termios->c_iflag & IXOFF) {
-		stl_ccrwait(portp);
-		stl_setreg(portp, CCR, CCR_SENDSCHR1);
-		portp->stats.rxxon++;
-		stl_ccrwait(portp);
-	}
-/*
- *	Question: should we return RTS to what it was before? It may have
- *	been set by an ioctl... Suppose not, since if you have hardware
- *	flow control set then it is pretty silly to go and set the RTS line
- *	by hand.
- */
-	if (tty->termios->c_cflag & CRTSCTS) {
-		stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
-		stl_setreg(portp, MSVR2, MSVR2_RTS);
-		portp->stats.rxrtson++;
-	}
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
+	stl_flowctrl(portp, 1);
 }
 
 /*****************************************************************************/
@@ -1354,7 +1568,6 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
 	stl_startrxtx(portp, -1, 0);
 }
 
@@ -1377,7 +1590,6 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
 	stl_startrxtx(portp, -1, 1);
 }
 
@@ -1417,7 +1629,6 @@
 		portp->tx.head = (char *) NULL;
 		portp->tx.tail = (char *) NULL;
 	}
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 	portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
 	portp->refcount = 0;
@@ -1429,7 +1640,6 @@
 static void stl_flushbuffer(struct tty_struct *tty)
 {
 	stlport_t	*portp;
-	unsigned long	flags;
 
 #if DEBUG
 	printk("stl_flushbuffer(tty=%x)\n", (int) tty);
@@ -1441,439 +1651,183 @@
 	if (portp == (stlport_t *) NULL)
 		return;
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_ccrwait(portp);
-	stl_setreg(portp, CCR, CCR_TXFLUSHFIFO);
-	stl_ccrwait(portp);
-	portp->tx.tail = portp->tx.head;
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
-
+	stl_flush(portp);
 	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
 }
 
 /*****************************************************************************/
 
-/*
- *	These functions get/set/update the registers of the cd1400 UARTs.
- *	Access to the cd1400 registers is via an address/data io port pair.
- *	(Maybe should make this inline...)
- */
-
-static int stl_getreg(stlport_t *portp, int regnr)
+static void stl_waituntilsent(struct tty_struct *tty, int timeout)
 {
-	outb((regnr + portp->uartaddr), portp->ioaddr);
-	return(inb(portp->ioaddr + EREG_DATA));
-}
+	stlport_t	*portp;
+	unsigned long	tend;
 
-static void stl_setreg(stlport_t *portp, int regnr, int value)
-{
-	outb((regnr + portp->uartaddr), portp->ioaddr);
-	outb(value, portp->ioaddr + EREG_DATA);
-}
+#if DEBUG
+	printk("stl_waituntilsent(tty=%x,timeout=%d)\n", (int) tty, timeout);
+#endif
 
-static int stl_updatereg(stlport_t *portp, int regnr, int value)
-{
-	outb((regnr + portp->uartaddr), portp->ioaddr);
-	if (inb(portp->ioaddr + EREG_DATA) != value) {
-		outb(value, portp->ioaddr + EREG_DATA);
-		return(1);
+	if (tty == (struct tty_struct *) NULL)
+		return;
+	portp = tty->driver_data;
+	if (portp == (stlport_t *) NULL)
+		return;
+
+	if (timeout == 0)
+		timeout = HZ;
+	tend = jiffies + timeout;
+
+	while (stl_datastate(portp)) {
+		if (current->signal & ~current->blocked)
+			break;
+		stl_delay(2);
+		if (jiffies >= tend)
+			break;
 	}
-	return(0);
 }
 
 /*****************************************************************************/
 
 /*
- *	Transmit interrupt handler. This has gotta be fast!  Handling TX
- *	chars is pretty simple, stuff as many as possible from the TX buffer
- *	into the cd1400 FIFO. Must also handle TX breaks here, since they
- *	are embedded as commands in the data stream. Oh no, had to use a goto!
- *	This could be optimized more, will do when I get time...
- *	In practice it is possible that interrupts are enabled but that the
- *	port has been hung up. Need to handle not having any TX buffer here,
- *	this is done by using the side effect that head and tail will also
- *	be NULL if the buffer has been freed.
+ *	All board interrupts are vectored through here first. This code then
+ *	calls off to the approrpriate board interrupt handlers.
  */
 
-static inline void stl_txisr(stlpanel_t *panelp, int ioaddr)
+static void stl_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
-	stlport_t	*portp;
-	int		len, stlen;
-	char		*head, *tail;
-	unsigned char	ioack, srer;
+	stlbrd_t	*brdp;
+	int		i;
 
 #if DEBUG
-	printk("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+	printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
 #endif
 
-	ioack = inb(ioaddr + EREG_TXACK);
-	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
-		printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
-		return;
+	for (i = 0; (i < stl_nrbrds); i++) {
+		if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL)
+			continue;
+		if (brdp->state == 0)
+			continue;
+		(* brdp->isr)(brdp);
 	}
-	portp = panelp->ports[(ioack >> 3)];
+}
+
+/*****************************************************************************/
 
 /*
- *	Unfortunately we need to handle breaks in the data stream, since
- *	this is the only way to generate them on the cd1400. Do it now if
- *	a break is to be sent.
+ *	Interrupt service routine for EasyIO board types.
  */
-	if (portp->brklen != 0) {
-		if (portp->brklen > 0) {
-			outb((TDR + portp->uartaddr), ioaddr);
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_DELAY, (ioaddr + EREG_DATA));
-			outb(portp->brklen, (ioaddr + EREG_DATA));
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
-			portp->brklen = -1;
-			goto stl_txalldone;
-		} else {
-			outb((COR2 + portp->uartaddr), ioaddr);
-			outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
-			portp->brklen = 0;
-		}
-	}
-
-	head = portp->tx.head;
-	tail = portp->tx.tail;
-	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
-	if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
-		set_bit(ASYI_TXLOW, &portp->istate);
-		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
-	}
 
-	if (len == 0) {
-		outb((SRER + portp->uartaddr), ioaddr);
-		srer = inb(ioaddr + EREG_DATA);
-		if (srer & SRER_TXDATA) {
-			srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
-		} else {
-			srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
-			clear_bit(ASYI_TXBUSY, &portp->istate);
-		}
-		outb(srer, (ioaddr + EREG_DATA));
-	} else {
-		len = MIN(len, CD1400_TXFIFOSIZE);
-		portp->stats.txtotal += len;
-		stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
-		outb((TDR + portp->uartaddr), ioaddr);
-		outsb((ioaddr + EREG_DATA), tail, stlen);
-		len -= stlen;
-		tail += stlen;
-		if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
-			tail = portp->tx.buf;
-		if (len > 0) {
-			outsb((ioaddr + EREG_DATA), tail, len);
-			tail += len;
-		}
-		portp->tx.tail = tail;
-	}
+static void stl_eiointr(stlbrd_t *brdp)
+{
+	stlpanel_t	*panelp;
+	unsigned int	iobase;
 
-stl_txalldone:
-	outb((EOSRR + portp->uartaddr), ioaddr);
-	outb(0, (ioaddr + EREG_DATA));
+	panelp = brdp->panels[0];
+	iobase = panelp->iobase;
+	while (inb(brdp->iostatus) & EIO_INTRPEND)
+		(* panelp->isr)(panelp, iobase);
 }
 
 /*****************************************************************************/
 
 /*
- *	Receive character interrupt handler. Determine if we have good chars
- *	or bad chars and then process appropriately. Good chars are easy
- *	just shove the lot into the RX buffer and set all status byte to 0.
- *	If a bad RX char then process as required. This routine needs to be
- *	fast!  In practice it is possible that we get an interrupt on a port
- *	that is closed. This can happen on hangups - since they completely
- *	shutdown a port not in user context. Need to handle this case.
+ *	Interrupt service routine for ECH-AT board types.
  */
 
-static inline void stl_rxisr(stlpanel_t *panelp, int ioaddr)
+static void stl_echatintr(stlbrd_t *brdp)
 {
-	stlport_t		*portp;
-	struct tty_struct	*tty;
-	unsigned int		ioack, len, buflen;
-	unsigned char		status;
-	char			ch;
-	static char		unwanted[CD1400_RXFIFOSIZE];
+	stlpanel_t	*panelp;
+	unsigned int	ioaddr;
+	int		bnknr;
 
-#if DEBUG
-	printk("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
-#endif
+	outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
 
-	ioack = inb(ioaddr + EREG_RXACK);
-	if ((ioack & panelp->ackmask) != 0) {
-		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
-		return;
-	}
-	portp = panelp->ports[(ioack >> 3)];
-	tty = portp->tty;
-
-	if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
-		outb((RDCR + portp->uartaddr), ioaddr);
-		len = inb(ioaddr + EREG_DATA);
-		if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
-				((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
-			outb((RDSR + portp->uartaddr), ioaddr);
-			insb((ioaddr + EREG_DATA), &unwanted[0], len);
-			portp->stats.rxlost += len;
-			portp->stats.rxtotal += len;
-		} else {
-			len = MIN(len, buflen);
-			if (len > 0) {
-				outb((RDSR + portp->uartaddr), ioaddr);
-				insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
-				memset(tty->flip.flag_buf_ptr, 0, len);
-				tty->flip.flag_buf_ptr += len;
-				tty->flip.char_buf_ptr += len;
-				tty->flip.count += len;
-				tty_schedule_flip(tty);
-				portp->stats.rxtotal += len;
-			}
-		}
-	} else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
-		outb((RDSR + portp->uartaddr), ioaddr);
-		status = inb(ioaddr + EREG_DATA);
-		ch = inb(ioaddr + EREG_DATA);
-		if (status & ST_PARITY)
-			portp->stats.rxparity++;
-		if (status & ST_FRAMING)
-			portp->stats.rxframing++;
-		if (status & ST_OVERRUN)
-			portp->stats.rxoverrun++;
-		if (status & ST_BREAK)
-			portp->stats.rxbreaks++;
-		if (status & ST_SCHARMASK) {
-			if ((status & ST_SCHARMASK) == ST_SCHAR1)
-				portp->stats.txxon++;
-			if ((status & ST_SCHARMASK) == ST_SCHAR2)
-				portp->stats.txxoff++;
-			goto stl_rxalldone;
-		}
-		if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
-			if (portp->rxmarkmsk & status) {
-				if (status & ST_BREAK) {
-					status = TTY_BREAK;
-#ifndef MODULE
-					if (portp->flags & ASYNC_SAK) {
-						do_SAK(tty);
-						BRDENABLE(portp->brdnr, portp->pagenr);
-					}
-#endif
-				} else if (status & ST_PARITY) {
-					status = TTY_PARITY;
-				} else if (status & ST_FRAMING) {
-					status = TTY_FRAME;
-				} else if(status & ST_OVERRUN) {
-					status = TTY_OVERRUN;
-				} else {
-					status = 0;
-				}
-			} else {
-				status = 0;
-			}
-			if (tty->flip.char_buf_ptr != (char *) NULL) {
-				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
-					*tty->flip.flag_buf_ptr++ = status;
-					*tty->flip.char_buf_ptr++ = ch;
-					tty->flip.count++;
-				}
-				tty_schedule_flip(tty);
+	while (inb(brdp->iostatus) & ECH_INTRPEND) {
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
 			}
 		}
-	} else {
-		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
-		return;
 	}
 
-stl_rxalldone:
-	outb((EOSRR + portp->uartaddr), ioaddr);
-	outb(0, (ioaddr + EREG_DATA));
+	outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
 }
 
 /*****************************************************************************/
 
 /*
- *	Modem interrupt handler. The is called when the modem signal line
- *	(DCD) has changed state. Leave most of the work to the off-level
- *	processing routine.
+ *	Interrupt service routine for ECH-MCA board types.
  */
 
-static inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr)
+static void stl_echmcaintr(stlbrd_t *brdp)
 {
-	stlport_t	*portp;
-	unsigned int	ioack;
-	unsigned char	misr;
-
-#if DEBUG
-	printk("stl_mdmisr(panelp=%x)\n", (int) panelp);
-#endif
-
-	ioack = inb(ioaddr + EREG_MDACK);
-	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
-		printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
-		return;
-	}
-	portp = panelp->ports[(ioack >> 3)];
+	stlpanel_t	*panelp;
+	unsigned int	ioaddr;
+	int		bnknr;
 
-	outb((MISR + portp->uartaddr), ioaddr);
-	misr = inb(ioaddr + EREG_DATA);
-	if (misr & MISR_DCD) {
-		set_bit(ASYI_DCDCHANGE, &portp->istate);
-		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
-		portp->stats.modem++;
+	while (inb(brdp->iostatus) & ECH_INTRPEND) {
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
+			}
+		}
 	}
-
-	outb((EOSRR + portp->uartaddr), ioaddr);
-	outb(0, (ioaddr + EREG_DATA));
 }
 
 /*****************************************************************************/
 
 /*
- *	Interrupt handler for EIO and ECH boards. This code ain't all that
- *	pretty, but the idea is to make it as fast as possible. This code is
- *	well suited to be assemblerized :-)  We don't use the general purpose
- *	register access functions here, for speed we will go strait to the
- *	io region.
+ *	Interrupt service routine for ECH-PCI board types.
  */
 
-static void stl_intr(int irq, void *dev_id, struct pt_regs *regs)
+static void stl_echpciintr(stlbrd_t *brdp)
 {
-	stlbrd_t	*brdp;
 	stlpanel_t	*panelp;
-	unsigned char	svrtype;
-	int		i, panelnr, iobase;
-
-#if DEBUG
-	printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
-#endif
+	unsigned int	ioaddr;
+	int		bnknr, recheck;
 
-	panelp = (stlpanel_t *) NULL;
-	for (i = 0; (i < stl_nrbrds); ) {
-		if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) {
-			i++;
-			continue;
-		}
-		if (brdp->state == 0) {
-			i++;
-			continue;
-		}
-/*
- *		The following section of code handles the subtle differences
- *		between board types. It is sort of similar, but different
- *		enough to handle each separately.
- */
-		if (brdp->brdtype == BRD_EASYIO) {
-			if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) {
-				i++;
-				continue;
-			}
-			panelp = brdp->panels[0];
-			iobase = panelp->iobase;
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			if (brdp->nrports > 4) {
-				outb((SVRR + 0x80), iobase);
-				svrtype |= inb(iobase + EREG_DATA);
-			}
-		} else if (brdp->brdtype == BRD_ECH) {
-			if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
-				i++;
-				continue;
-			}
-			outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
-			for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
-				panelp = brdp->panels[panelnr];
-				iobase = panelp->iobase;
-				if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-					break;
-				if (panelp->nrports > 8) {
-					iobase += 0x8;
-					if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-						break;
-				}
-			}	
-			if (panelnr >= brdp->nrpanels) {
-				i++;
-				continue;
+	while (1) {
+		recheck = 0;
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl);
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
+				recheck++;
 			}
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			outb((SVRR + 0x80), iobase);
-			svrtype |= inb(iobase + EREG_DATA);
-		} else if (brdp->brdtype == BRD_ECHPCI) {
-			iobase = brdp->ioaddr2;
-			for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
-				panelp = brdp->panels[panelnr];
-				outb(panelp->pagenr, brdp->ioctrl);
-				if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-					break;
-				if (panelp->nrports > 8) {
-					outb((panelp->pagenr + 1), brdp->ioctrl);
-					if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-						break;
-				}
-			}	
-			if (panelnr >= brdp->nrpanels) {
-				i++;
-				continue;
-			}
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			outb((SVRR + 0x80), iobase);
-			svrtype |= inb(iobase + EREG_DATA);
-		} else if (brdp->brdtype == BRD_ECHMC) {
-			if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
-				i++;
-				continue;
-			}
-			for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
-				panelp = brdp->panels[panelnr];
-				iobase = panelp->iobase;
-				if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-					break;
-				if (panelp->nrports > 8) {
-					iobase += 0x8;
-					if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-						break;
-				}
-			}	
-			if (panelnr >= brdp->nrpanels) {
-				i++;
-				continue;
-			}
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			outb((SVRR + 0x80), iobase);
-			svrtype |= inb(iobase + EREG_DATA);
-		} else {
-			printk("STALLION: unknown board type=%x\n", brdp->brdtype);
-			i++;
-			continue;
 		}
+		if (! recheck)
+			break;
+	}
+}
+
+/*****************************************************************************/
 
 /*
- *		We have determined what type of service is required for a
- *		port. From here on in the service of a port is the same no
- *		matter what the board type...
- */
-		if (svrtype & SVRR_RX)
-			stl_rxisr(panelp, iobase);
-		if (svrtype & SVRR_TX)
-			stl_txisr(panelp, iobase);
-		if (svrtype & SVRR_MDM)
-			stl_mdmisr(panelp, iobase);
+ *	Interrupt service routine for ECH-8/64-PCI board types.
+ */
 
-		if (brdp->brdtype == BRD_ECH)
-			outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+static void stl_echpci64intr(stlbrd_t *brdp)
+{
+	stlpanel_t	*panelp;
+	unsigned int	ioaddr;
+	int		bnknr;
+
+	while (inb(brdp->ioctrl) & 0x1) {
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
+			}
+		}
 	}
 }
 
@@ -1890,6 +1844,7 @@
 	unsigned int		oldsigs;
 
 	portp = private;
+
 #if DEBUG
 	printk("stl_offintr(portp=%x)\n", (int) portp);
 #endif
@@ -1901,7 +1856,8 @@
 		return;
 
 	if (test_bit(ASYI_TXLOW, &portp->istate)) {
-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
 			(tty->ldisc.write_wakeup)(tty);
 		wake_up_interruptible(&tty->write_wait);
 	}
@@ -1914,7 +1870,7 @@
 		if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
 			if (portp->flags & ASYNC_CHECK_CD) {
 				if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
-						(portp->flags & ASYNC_CALLOUT_NOHUP))) {
+				    (portp->flags & ASYNC_CALLOUT_NOHUP))) {
 					tty_hangup(tty);
 				}
 			}
@@ -1925,90 +1881,1233 @@
 /*****************************************************************************/
 
 /*
- *	Wait for the command register to be ready. We will poll this,
- *	since it won't usually take too long to be ready.
+ *	Map in interrupt vector to this driver. Check that we don't
+ *	already have this vector mapped, we might be sharing this
+ *	interrupt across multiple boards.
  */
 
-static void stl_ccrwait(stlport_t *portp)
+static int stl_mapirq(int irq, char *name)
 {
-	int	i;
+	int	rc, i;
 
-	for (i = 0; (i < CCR_MAXWAIT); i++) {
-		if (stl_getreg(portp, CCR) == 0) {
-			return;
+#if DEBUG
+	printk("stl_mapirq(irq=%d,name=%s)\n", irq, name);
+#endif
+
+	rc = 0;
+	for (i = 0; (i < stl_numintrs); i++) {
+		if (stl_gotintrs[i] == irq)
+			break;
+	}
+	if (i >= stl_numintrs) {
+		if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) {
+			printk("STALLION: failed to register interrupt "
+				"routine for %s irq=%d\n", name, irq);
+			rc = -ENODEV;
+		} else {
+			stl_gotintrs[stl_numintrs++] = irq;
 		}
 	}
-
-	printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+	return(rc);
 }
 
 /*****************************************************************************/
 
 /*
- *	Set up the cd1400 registers for a port based on the termios port
- *	settings.
+ *	Initialize all the ports on a panel.
  */
 
-static void stl_setport(stlport_t *portp, struct termios *tiosp)
+static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
 {
-	stlbrd_t	*brdp;
-	unsigned long	flags;
-	unsigned int	clkdiv, baudrate;
-	unsigned char	cor1, cor2, cor3;
-	unsigned char	cor4, cor5, ccr;
-	unsigned char	srer, sreron, sreroff;
-	unsigned char	mcor1, mcor2, rtpr;
-	unsigned char	clk, div;
+	stlport_t	*portp;
+	int		chipmask, i;
 
-	cor1 = 0;
-	cor2 = 0;
-	cor3 = 0;
-	cor4 = 0;
-	cor5 = 0;
-	ccr = 0;
-	rtpr = 0;
-	clk = 0;
-	div = 0;
-	mcor1 = 0;
-	mcor2 = 0;
-	sreron = 0;
-	sreroff = 0;
+#if DEBUG
+	printk("stl_initports(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
+#endif
 
-	brdp = stl_brds[portp->brdnr];
-	if (brdp == (stlbrd_t *) NULL)
-		return;
+	chipmask = stl_panelinit(brdp, panelp);
 
 /*
- *	Set up the RX char ignore mask with those RX error types we
- *	can ignore. We can get the cd1400 to help us out a little here,
- *	it will ignore parity errors and breaks for us.
+ *	All UART's are initialized (if found!). Now go through and setup
+ *	each ports data structures.
  */
-	portp->rxignoremsk = 0;
-	if (tiosp->c_iflag & IGNPAR) {
-		portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
-		cor1 |= COR1_PARIGNORE;
-	}
-	if (tiosp->c_iflag & IGNBRK) {
-		portp->rxignoremsk |= ST_BREAK;
-		cor4 |= COR4_IGNBRK;
+	for (i = 0; (i < panelp->nrports); i++) {
+		portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
+		if (portp == (stlport_t *) NULL) {
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlport_t));
+			break;
+		}
+		memset(portp, 0, sizeof(stlport_t));
+
+		portp->magic = STL_PORTMAGIC;
+		portp->portnr = i;
+		portp->brdnr = panelp->brdnr;
+		portp->panelnr = panelp->panelnr;
+		portp->uartp = panelp->uartp;
+		portp->clk = brdp->clk;
+		portp->baud_base = STL_BAUDBASE;
+		portp->close_delay = STL_CLOSEDELAY;
+		portp->closing_wait = 30 * HZ;
+		portp->normaltermios = stl_deftermios;
+		portp->callouttermios = stl_deftermios;
+		portp->tqueue.routine = stl_offintr;
+		portp->tqueue.data = portp;
+		portp->stats.brd = portp->brdnr;
+		portp->stats.panel = portp->panelnr;
+		portp->stats.port = portp->portnr;
+		panelp->ports[i] = portp;
+		stl_portinit(brdp, panelp, portp);
 	}
 
-	portp->rxmarkmsk = ST_OVERRUN;
-	if (tiosp->c_iflag & (INPCK | PARMRK))
-		portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
-	if (tiosp->c_iflag & BRKINT)
-		portp->rxmarkmsk |= ST_BREAK;
+	return(0);
+}
+
+/*****************************************************************************/
 
 /*
- *	Go through the char size, parity and stop bits and set all the
- *	option register appropriately.
+ *	Try to find and initialize an EasyIO board.
  */
-	switch (tiosp->c_cflag & CSIZE) {
-	case CS5:
-		cor1 |= COR1_CHL5;
-		break;
-	case CS6:
-		cor1 |= COR1_CHL6;
+
+static inline int stl_initeio(stlbrd_t *brdp)
+{
+	stlpanel_t	*panelp;
+	unsigned int	status;
+	char		*name;
+	int		rc;
+
+#if DEBUG
+	printk("stl_initeio(brdp=%x)\n", (int) brdp);
+#endif
+
+	brdp->ioctrl = brdp->ioaddr1 + 1;
+	brdp->iostatus = brdp->ioaddr1 + 2;
+
+	status = inb(brdp->iostatus);
+	if ((status & EIO_IDBITMASK) == EIO_MK3)
+		brdp->ioctrl++;
+
+/*
+ *	Handle board specific stuff now. The real difference is PCI
+ *	or not PCI.
+ */
+	if (brdp->brdtype == BRD_EASYIOPCI) {
+		brdp->iosize1 = 0x80;
+		brdp->iosize2 = 0x80;
+		name = "serial(EIO-PCI)";
+		outb(0x41, (brdp->ioaddr2 + 0x4c));
+	} else {
+		brdp->iosize1 = 8;
+		name = "serial(EIO)";
+		if ((brdp->irq < 0) || (brdp->irq > 15) ||
+		    (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n",
+				brdp->irq, brdp->brdnr);
+			return(-EINVAL);
+		}
+		outb((stl_vecmap[brdp->irq] | EIO_0WS |
+			((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)),
+			brdp->ioctrl);
+	}
+
+	if (check_region(brdp->ioaddr1, brdp->iosize1)) {
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, brdp->ioaddr1);
+	}
+	if (brdp->iosize2 > 0) {
+		if (check_region(brdp->ioaddr2, brdp->iosize2)) {
+			printk("STALLION: Warning, unit %d I/O address %x "
+				"conflicts with another device\n",
+				brdp->brdnr, brdp->ioaddr2);
+		}
+	}
+ 
+/*
+ *	Everything looks OK, so lets go ahead and probe for the hardware.
+ */
+	brdp->clk = CD1400_CLK;
+	brdp->isr = stl_eiointr;
+
+	switch (status & EIO_IDBITMASK) {
+	case EIO_8PORTM:
+		brdp->clk = CD1400_CLK8M;
+		/* fall thru */
+	case EIO_8PORTRS:
+	case EIO_8PORTDI:
+		brdp->nrports = 8;
+		break;
+	case EIO_4PORTRS:
+		brdp->nrports = 4;
+		break;
+	case EIO_MK3:
+		switch (status & EIO_BRDMASK) {
+		case ID_BRD4:
+			brdp->nrports = 4;
+			break;
+		case ID_BRD8:
+			brdp->nrports = 8;
+			break;
+		case ID_BRD16:
+			brdp->nrports = 16;
+			break;
+		default:
+			return(-ENODEV);
+		}
+		break;
+	default:
+		return(-ENODEV);
+	}
+
+/*
+ *	We have verfied that the board is actually present, so now we
+ *	can complete the setup.
+ */
+	request_region(brdp->ioaddr1, brdp->iosize1, name);
+	if (brdp->iosize2 > 0)
+		request_region(brdp->ioaddr2, brdp->iosize2, name);
+
+	panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+	if (panelp == (stlpanel_t *) NULL) {
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			sizeof(stlpanel_t));
+		return(-ENOMEM);
+	}
+	memset(panelp, 0, sizeof(stlpanel_t));
+
+	panelp->magic = STL_PANELMAGIC;
+	panelp->brdnr = brdp->brdnr;
+	panelp->panelnr = 0;
+	panelp->nrports = brdp->nrports;
+	panelp->iobase = brdp->ioaddr1;
+	panelp->hwid = status;
+	if ((status & EIO_IDBITMASK) == EIO_MK3) {
+		panelp->uartp = (void *) &stl_sc26198uart;
+		panelp->isr = stl_sc26198intr;
+	} else {
+		panelp->uartp = (void *) &stl_cd1400uart;
+		panelp->isr = stl_cd1400eiointr;
+	}
+
+	brdp->panels[0] = panelp;
+	brdp->nrpanels = 1;
+	brdp->state |= BRD_FOUND;
+	brdp->hwid = status;
+	rc = stl_mapirq(brdp->irq, name);
+	return(rc);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Try to find an ECH board and initialize it. This code is capable of
+ *	dealing with all types of ECH board.
+ */
+
+static inline int stl_initech(stlbrd_t *brdp)
+{
+	stlpanel_t	*panelp;
+	unsigned int	status, nxtid, ioaddr, conflict;
+	int		panelnr, banknr, i;
+	char		*name;
+
+#if DEBUG
+	printk("stl_initech(brdp=%x)\n", (int) brdp);
+#endif
+
+	status = 0;
+	conflict = 0;
+
+/*
+ *	Set up the initial board register contents for boards. This varies a
+ *	bit between the different board types. So we need to handle each
+ *	separately. Also do a check that the supplied IRQ is good.
+ */
+	switch (brdp->brdtype) {
+
+	case BRD_ECH:
+		brdp->isr = stl_echatintr;
+		brdp->ioctrl = brdp->ioaddr1 + 1;
+		brdp->iostatus = brdp->ioaddr1 + 1;
+		status = inb(brdp->iostatus);
+		if ((status & ECH_IDBITMASK) != ECH_ID)
+			return(-ENODEV);
+		if ((brdp->irq < 0) || (brdp->irq > 15) ||
+		    (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n",
+				brdp->irq, brdp->brdnr);
+			return(-EINVAL);
+		}
+		status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
+		status |= (stl_vecmap[brdp->irq] << 1);
+		outb((status | ECH_BRDRESET), brdp->ioaddr1);
+		brdp->ioctrlval = ECH_INTENABLE |
+			((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
+		for (i = 0; (i < 10); i++)
+			outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+		brdp->iosize1 = 2;
+		brdp->iosize2 = 32;
+		name = "serial(EC8/32)";
+		outb(status, brdp->ioaddr1);
+		break;
+
+	case BRD_ECHMC:
+		brdp->isr = stl_echmcaintr;
+		brdp->ioctrl = brdp->ioaddr1 + 0x20;
+		brdp->iostatus = brdp->ioctrl;
+		status = inb(brdp->iostatus);
+		if ((status & ECH_IDBITMASK) != ECH_ID)
+			return(-ENODEV);
+		if ((brdp->irq < 0) || (brdp->irq > 15) ||
+		    (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n",
+				brdp->irq, brdp->brdnr);
+			return(-EINVAL);
+		}
+		outb(ECHMC_BRDRESET, brdp->ioctrl);
+		outb(ECHMC_INTENABLE, brdp->ioctrl);
+		brdp->iosize1 = 64;
+		name = "serial(EC8/32-MC)";
+		break;
+
+	case BRD_ECHPCI:
+		brdp->isr = stl_echpciintr;
+		brdp->ioctrl = brdp->ioaddr1 + 2;
+		brdp->iosize1 = 4;
+		brdp->iosize2 = 8;
+		name = "serial(EC8/32-PCI)";
+		break;
+
+	case BRD_ECH64PCI:
+		brdp->isr = stl_echpci64intr;
+		brdp->ioctrl = brdp->ioaddr2 + 0x40;
+		outb(0x43, (brdp->ioaddr1 + 0x4c));
+		brdp->iosize1 = 0x80;
+		brdp->iosize2 = 0x80;
+		name = "serial(EC8/64-PCI)";
+		break;
+
+	default:
+		printk("STALLION: unknown board type=%d\n", brdp->brdtype);
+		return(-EINVAL);
+		break;
+	}
+
+/*
+ *	Check boards for possible IO address conflicts. We won't actually
+ *	do anything about it here, just issue a warning...
+ */
+	conflict = check_region(brdp->ioaddr1, brdp->iosize1) ?
+		brdp->ioaddr1 : 0;
+	if ((conflict == 0) && (brdp->iosize2 > 0))
+		conflict = check_region(brdp->ioaddr2, brdp->iosize2) ?
+			brdp->ioaddr2 : 0;
+	if (conflict) {
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, conflict);
+	}
+
+	request_region(brdp->ioaddr1, brdp->iosize1, name);
+	if (brdp->iosize2 > 0)
+		request_region(brdp->ioaddr2, brdp->iosize2, name);
+
+/*
+ *	Scan through the secondary io address space looking for panels.
+ *	As we find'em allocate and initialize panel structures for each.
+ */
+	brdp->clk = CD1400_CLK;
+	brdp->hwid = status;
+
+	ioaddr = brdp->ioaddr2;
+	banknr = 0;
+	panelnr = 0;
+	nxtid = 0;
+
+	for (i = 0; (i < STL_MAXPANELS); i++) {
+		if (brdp->brdtype == BRD_ECHPCI) {
+			outb(nxtid, brdp->ioctrl);
+			ioaddr = brdp->ioaddr2;
+		}
+		status = inb(ioaddr + ECH_PNLSTATUS);
+		if ((status & ECH_PNLIDMASK) != nxtid)
+			break;
+		panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+		if (panelp == (stlpanel_t *) NULL) {
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlpanel_t));
+			break;
+		}
+		memset(panelp, 0, sizeof(stlpanel_t));
+		panelp->magic = STL_PANELMAGIC;
+		panelp->brdnr = brdp->brdnr;
+		panelp->panelnr = panelnr;
+		panelp->iobase = ioaddr;
+		panelp->pagenr = nxtid;
+		panelp->hwid = status;
+		brdp->bnk2panel[banknr] = panelp;
+		brdp->bnkpageaddr[banknr] = nxtid;
+		brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
+
+		if (status & ECH_PNLXPID) {
+			panelp->uartp = (void *) &stl_sc26198uart;
+			panelp->isr = stl_sc26198intr;
+			if (status & ECH_PNL16PORT) {
+				panelp->nrports = 16;
+				brdp->bnk2panel[banknr] = panelp;
+				brdp->bnkpageaddr[banknr] = nxtid;
+				brdp->bnkstataddr[banknr++] = ioaddr + 4 +
+					ECH_PNLSTATUS;
+			} else {
+				panelp->nrports = 8;
+			}
+		} else {
+			panelp->uartp = (void *) &stl_cd1400uart;
+			panelp->isr = stl_cd1400echintr;
+			if (status & ECH_PNL16PORT) {
+				panelp->nrports = 16;
+				panelp->ackmask = 0x80;
+				if (brdp->brdtype != BRD_ECHPCI)
+					ioaddr += EREG_BANKSIZE;
+				brdp->bnk2panel[banknr] = panelp;
+				brdp->bnkpageaddr[banknr] = ++nxtid;
+				brdp->bnkstataddr[banknr++] = ioaddr +
+					ECH_PNLSTATUS;
+			} else {
+				panelp->nrports = 8;
+				panelp->ackmask = 0xc0;
+			}
+		}
+
+		nxtid++;
+		ioaddr += EREG_BANKSIZE;
+		brdp->nrports += panelp->nrports;
+		brdp->panels[panelnr++] = panelp;
+		if ((brdp->brdtype != BRD_ECHPCI) &&
+		    (ioaddr >= (brdp->ioaddr2 + brdp->iosize2)))
+			break;
+	}
+
+	brdp->nrpanels = panelnr;
+	brdp->nrbnks = banknr;
+	if (brdp->brdtype == BRD_ECH)
+		outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+
+	brdp->state |= BRD_FOUND;
+	i = stl_mapirq(brdp->irq, name);
+	return(i);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Initialize and configure the specified board.
+ *	Scan through all the boards in the configuration and see what we
+ *	can find. Handle EIO and the ECH boards a little differently here
+ *	since the initial search and setup is very different.
+ */
+
+static int stl_brdinit(stlbrd_t *brdp)
+{
+	int	i;
+
+#if DEBUG
+	printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+#endif
+
+	switch (brdp->brdtype) {
+	case BRD_EASYIO:
+	case BRD_EASYIOPCI:
+		stl_initeio(brdp);
+		break;
+	case BRD_ECH:
+	case BRD_ECHMC:
+	case BRD_ECHPCI:
+	case BRD_ECH64PCI:
+		stl_initech(brdp);
+		break;
+	default:
+		printk("STALLION: unit=%d is unknown board type=%d\n",
+			brdp->brdnr, brdp->brdtype);
+		return(ENODEV);
+	}
+
+	stl_brds[brdp->brdnr] = brdp;
+	if ((brdp->state & BRD_FOUND) == 0) {
+		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n",
+			stl_brdnames[brdp->brdtype], brdp->brdnr,
+			brdp->ioaddr1, brdp->irq);
+		return(ENODEV);
+	}
+
+	for (i = 0; (i < STL_MAXPANELS); i++)
+		if (brdp->panels[i] != (stlpanel_t *) NULL)
+			stl_initports(brdp, brdp->panels[i]);
+
+	printk("STALLION: %s found, unit=%d io=%x irq=%d "
+		"nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],
+		brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
+		brdp->nrports);
+	return(0);
+}
+
+/*****************************************************************************/
+
+#ifdef	CONFIG_PCI
+
+/*
+ *	We have a Stallion board. Allocate a board structure and
+ *	initialize it. Read its IO and IRQ resources from PCI
+ *	configuration space.
+ */
+
+static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr)
+{
+	unsigned int	bar[4];
+	stlbrd_t	*brdp;
+	int		i, rc;
+	unsigned char	irq;
+
+#if DEBUG
+	printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n",
+		brdtype, busnr, devnr);
+#endif
+
+	brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+	if (brdp == (stlbrd_t *) NULL) {
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			sizeof(stlbrd_t));
+		return(-ENOMEM);
+	}
+
+	memset(brdp, 0, sizeof(stlbrd_t));
+	brdp->magic = STL_BOARDMAGIC;
+	brdp->brdnr = stl_nrbrds++;
+	brdp->brdtype = brdtype;
+
+/*
+ *	Read in all the BAR registers from this board. Different Stallion
+ *	boards use these in different ways, so we just read in the whole
+ *	lot and then figure out what is what later.
+ */
+	for (i = 0; (i < 4); i++) {
+		rc = pcibios_read_config_dword(busnr, devnr,
+			(PCI_BASE_ADDRESS_0 + (i * 0x4)), &bar[i]);
+		if (rc) {
+			printk("STALLION: failed to read BAR register %d "
+				"from PCI board, errno=%x\n", i, rc);
+			return(0);
+		}
+	}
+
+	rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq);
+	if (rc) {
+		printk("STALLION: failed to read INTERRUPT register "
+			"from PCI board, errno=%x\n", rc);
+		return(0);
+	}
+
+#if DEBUG
+	printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__,
+		bar[0], bar[1], bar[2], bar[3], irq);
+#endif
+
+/*
+ *	We have all resources from the board, so lets setup the actual
+ *	board structure now.
+ */
+	switch (brdtype) {
+	case BRD_ECHPCI:
+		brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		break;
+	case BRD_ECH64PCI:
+		brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		break;
+	case BRD_EASYIOPCI:
+		brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		break;
+	default:
+		printk("STALLION: unknown PCI board type=%d\n", brdtype);
+		break;
+	}
+
+	brdp->irq = irq;
+	stl_brdinit(brdp);
+
+	return(0);
+}
+
+
+/*****************************************************************************/
+
+/*
+ *	Find all Stallion PCI boards that might be installed. Initialize each
+ *	one as it is found.
+ */
+
+
+static inline int stl_findpcibrds()
+{
+	unsigned char	busnr, devnr;
+	unsigned short	class;
+	int		i, rc, brdtypnr;
+
+#if DEBUG
+	printk("stl_findpcibrds()\n");
+#endif
+
+	if (! pcibios_present())
+		return(0);
+
+	for (i = 0; (i < stl_nrpcibrds); i++) {
+		for (brdtypnr = 0; ; brdtypnr++) {
+
+			rc = pcibios_find_device(stl_pcibrds[i].vendid,
+				stl_pcibrds[i].devid, brdtypnr, &busnr, &devnr);
+			if (rc)
+				break;
+
+/*
+ *			Check that we can handle more boards...
+ */
+			if (stl_nrbrds >= STL_MAXBRDS) {
+				printk("STALLION: too many boards found, "
+					"maximum supported %d\n", STL_MAXBRDS);
+				i = stl_nrpcibrds;
+				break;
+			}
+
+/*
+ *			Found a device on the PCI bus that has our vendor and
+ *			device ID. Need to check now that it is really us.
+ */
+			rc = pcibios_read_config_word(busnr, devnr,
+				PCI_CLASS_DEVICE, &class);
+			if (rc) {
+				printk("STALLION: failed to read class type "
+					"from PCI board, errno=%x\n", rc);
+				continue;
+			}
+			if (class == PCI_CLASS_STORAGE_IDE)
+				continue;
+
+			rc = stl_initpcibrd(stl_pcibrds[i].brdtype, busnr,
+				devnr);
+			if (rc)
+				return(rc);
+		}
+	}
+
+	return(0);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ *	Scan through all the boards in the configuration and see what we
+ *	can find. Handle EIO and the ECH boards a little differently here
+ *	since the initial search and setup is too different.
+ */
+
+static inline int stl_initbrds()
+{
+	stlbrd_t	*brdp;
+	stlconf_t	*confp;
+	int		i;
+
+#if DEBUG
+	printk("stl_initbrds()\n");
+#endif
+
+	if (stl_nrbrds > STL_MAXBRDS) {
+		printk("STALLION: too many boards in configuration table, "
+			"truncating to %d\n", STL_MAXBRDS);
+		stl_nrbrds = STL_MAXBRDS;
+	}
+
+/*
+ *	Firstly scan the list of static boards configured. Allocate
+ *	resources and initialize the boards as found.
+ */
+	for (i = 0; (i < stl_nrbrds); i++) {
+		confp = &stl_brdconf[i];
+		brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+		if (brdp == (stlbrd_t *) NULL) {
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlbrd_t));
+			return(-ENOMEM);
+		}
+		memset(brdp, 0, sizeof(stlbrd_t));
+
+		brdp->magic = STL_BOARDMAGIC;
+		brdp->brdnr = i;
+		brdp->brdtype = confp->brdtype;
+		brdp->ioaddr1 = confp->ioaddr1;
+		brdp->ioaddr2 = confp->ioaddr2;
+		brdp->irq = confp->irq;
+		brdp->irqtype = confp->irqtype;
+		stl_brdinit(brdp);
+	}
+
+#ifdef CONFIG_PCI
+/*
+ *	If the PCI BIOS support is compiled in then let's go looking for
+ *	ECH-PCI boards.
+ */
+	stl_findpcibrds();
+#endif
+
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the board stats structure to user app.
+ */
+
+static int stl_getbrdstats(combrd_t *bp)
+{
+	stlbrd_t	*brdp;
+	stlpanel_t	*panelp;
+	int		i;
+
+	memcpy_fromfs(&stl_brdstats, bp, sizeof(combrd_t));
+	if (stl_brdstats.brd >= STL_MAXBRDS)
+		return(-ENODEV);
+	brdp = stl_brds[stl_brdstats.brd];
+	if (brdp == (stlbrd_t *) NULL)
+		return(-ENODEV);
+
+	memset(&stl_brdstats, 0, sizeof(combrd_t));
+	stl_brdstats.brd = brdp->brdnr;
+	stl_brdstats.type = brdp->brdtype;
+	stl_brdstats.hwid = brdp->hwid;
+	stl_brdstats.state = brdp->state;
+	stl_brdstats.ioaddr = brdp->ioaddr1;
+	stl_brdstats.ioaddr2 = brdp->ioaddr2;
+	stl_brdstats.irq = brdp->irq;
+	stl_brdstats.nrpanels = brdp->nrpanels;
+	stl_brdstats.nrports = brdp->nrports;
+	for (i = 0; (i < brdp->nrpanels); i++) {
+		panelp = brdp->panels[i];
+		stl_brdstats.panels[i].panel = i;
+		stl_brdstats.panels[i].hwid = panelp->hwid;
+		stl_brdstats.panels[i].nrports = panelp->nrports;
+	}
+
+	memcpy_tofs(bp, &stl_brdstats, sizeof(combrd_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Resolve the referenced port number into a port struct pointer.
+ */
+
+static stlport_t *stl_getport(int brdnr, int panelnr, int portnr)
+{
+	stlbrd_t	*brdp;
+	stlpanel_t	*panelp;
+
+	if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
+		return((stlport_t *) NULL);
+	brdp = stl_brds[brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return((stlport_t *) NULL);
+	if ((panelnr < 0) || (panelnr >= brdp->nrpanels))
+		return((stlport_t *) NULL);
+	panelp = brdp->panels[panelnr];
+	if (panelp == (stlpanel_t *) NULL)
+		return((stlport_t *) NULL);
+	if ((portnr < 0) || (portnr >= panelp->nrports))
+		return((stlport_t *) NULL);
+	return(panelp->ports[portnr]);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the port stats structure to user app. A NULL port struct
+ *	pointer passed in means that we need to find out from the app
+ *	what port to get stats for (used through board control device).
+ */
+
+static int stl_getportstats(stlport_t *portp, comstats_t *cp)
+{
+	unsigned char	*head, *tail;
+	unsigned long	flags;
+
+	if (portp == (stlport_t *) NULL) {
+		memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t));
+		portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
+			stl_comstats.port);
+		if (portp == (stlport_t *) NULL)
+			return(-ENODEV);
+	}
+
+	portp->stats.state = portp->istate;
+	portp->stats.flags = portp->flags;
+	portp->stats.hwid = portp->hwid;
+
+	portp->stats.ttystate = 0;
+	portp->stats.cflags = 0;
+	portp->stats.iflags = 0;
+	portp->stats.oflags = 0;
+	portp->stats.lflags = 0;
+	portp->stats.rxbuffered = 0;
+
+	save_flags(flags);
+	cli();
+	if (portp->tty != (struct tty_struct *) NULL) {
+		if (portp->tty->driver_data == portp) {
+			portp->stats.ttystate = portp->tty->flags;
+			portp->stats.rxbuffered = portp->tty->flip.count;
+			if (portp->tty->termios != (struct termios *) NULL) {
+				portp->stats.cflags = portp->tty->termios->c_cflag;
+				portp->stats.iflags = portp->tty->termios->c_iflag;
+				portp->stats.oflags = portp->tty->termios->c_oflag;
+				portp->stats.lflags = portp->tty->termios->c_lflag;
+			}
+		}
+	}
+	restore_flags(flags);
+
+	head = portp->tx.head;
+	tail = portp->tx.tail;
+	portp->stats.txbuffered = ((head >= tail) ? (head - tail) :
+		(STL_TXBUFSIZE - (tail - head)));
+
+	portp->stats.signals = (unsigned long) stl_getsignals(portp);
+
+	memcpy_tofs(cp, &portp->stats, sizeof(comstats_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Clear the port stats structure. We also return it zeroed out...
+ */
+
+static int stl_clrportstats(stlport_t *portp, comstats_t *cp)
+{
+	if (portp == (stlport_t *) NULL) {
+		memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t));
+		portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
+			stl_comstats.port);
+		if (portp == (stlport_t *) NULL)
+			return(-ENODEV);
+	}
+
+	memset(&portp->stats, 0, sizeof(comstats_t));
+	portp->stats.brd = portp->brdnr;
+	portp->stats.panel = portp->panelnr;
+	portp->stats.port = portp->portnr;
+	memcpy_tofs(cp, &portp->stats, sizeof(comstats_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the entire driver ports structure to a user app.
+ */
+
+static int stl_getportstruct(unsigned long arg)
+{
+	stlport_t	*portp;
+
+	memcpy_fromfs(&stl_dummyport, (void *) arg, sizeof(stlport_t));
+	portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
+		 stl_dummyport.portnr);
+	if (portp == (stlport_t *) NULL)
+		return(-ENODEV);
+	memcpy_tofs((void *) arg, portp, sizeof(stlport_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the entire driver board structure to a user app.
+ */
+
+static int stl_getbrdstruct(unsigned long arg)
+{
+	stlbrd_t	*brdp;
+
+	memcpy_fromfs(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t));
+	if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS))
+		return(-ENODEV);
+	brdp = stl_brds[stl_dummybrd.brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return(-ENODEV);
+	memcpy_tofs((void *) arg, brdp, sizeof(stlbrd_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Memory device open code. Need to keep track of opens and close
+ *	for module handling.
+ */
+
+static int stl_memopen(struct inode *ip, struct file *fp)
+{
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+/*****************************************************************************/
+
+static void stl_memclose(struct inode *ip, struct file *fp)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/*****************************************************************************/
+
+/*
+ *	The "staliomem" device is also required to do some special operations
+ *	on the board and/or ports. In this driver it is mostly used for stats
+ *	collection.
+ */
+
+static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	int	brdnr, rc;
+
+#if DEBUG
+	printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip,
+		(int) fp, cmd, (int) arg);
+#endif
+
+	brdnr = MINOR(ip->i_rdev);
+	if (brdnr >= STL_MAXBRDS)
+		return(-ENODEV);
+	rc = 0;
+
+	switch (cmd) {
+	case COM_GETPORTSTATS:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stl_getportstats((stlport_t *) NULL,
+				(comstats_t *) arg);
+		break;
+	case COM_CLRPORTSTATS:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stl_clrportstats((stlport_t *) NULL,
+				(comstats_t *) arg);
+		break;
+	case COM_GETBRDSTATS:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(combrd_t))) == 0)
+			rc = stl_getbrdstats((combrd_t *) arg);
+		break;
+	case COM_READPORT:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stlport_t))) == 0)
+			rc = stl_getportstruct(arg);
+		break;
+	case COM_READBOARD:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stlbrd_t))) == 0)
+			rc = stl_getbrdstruct(arg);
+		break;
+	default:
+		rc = -ENOIOCTLCMD;
+		break;
+	}
+
+	return(rc);
+}
+
+/*****************************************************************************/
+
+int stl_init(void)
+{
+	printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion);
+
+	stl_initbrds();
+
+/*
+ *	Allocate a temporary write buffer.
+ */
+	stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
+	if (stl_tmpwritebuf == (char *) NULL)
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			STL_TXBUFSIZE);
+
+/*
+ *	Set up a character driver for per board stuff. This is mainly used
+ *	to do stats ioctls on the ports.
+ */
+	if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
+		printk("STALLION: failed to register serial board device\n");
+
+/*
+ *	Set up the tty driver structure and register us as a driver.
+ *	Also setup the callout tty device.
+ */
+	memset(&stl_serial, 0, sizeof(struct tty_driver));
+	stl_serial.magic = TTY_DRIVER_MAGIC;
+	stl_serial.name = stl_serialname;
+	stl_serial.major = STL_SERIALMAJOR;
+	stl_serial.minor_start = 0;
+	stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
+	stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
+	stl_serial.subtype = STL_DRVTYPSERIAL;
+	stl_serial.init_termios = stl_deftermios;
+	stl_serial.flags = TTY_DRIVER_REAL_RAW;
+	stl_serial.refcount = &stl_refcount;
+	stl_serial.table = stl_ttys;
+	stl_serial.termios = stl_termios;
+	stl_serial.termios_locked = stl_termioslocked;
+	
+	stl_serial.open = stl_open;
+	stl_serial.close = stl_close;
+	stl_serial.write = stl_write;
+	stl_serial.put_char = stl_putchar;
+	stl_serial.flush_chars = stl_flushchars;
+	stl_serial.write_room = stl_writeroom;
+	stl_serial.chars_in_buffer = stl_charsinbuffer;
+	stl_serial.ioctl = stl_ioctl;
+	stl_serial.set_termios = stl_settermios;
+	stl_serial.throttle = stl_throttle;
+	stl_serial.unthrottle = stl_unthrottle;
+	stl_serial.stop = stl_stop;
+	stl_serial.start = stl_start;
+	stl_serial.hangup = stl_hangup;
+	stl_serial.flush_buffer = stl_flushbuffer;
+
+	stl_callout = stl_serial;
+	stl_callout.name = stl_calloutname;
+	stl_callout.major = STL_CALLOUTMAJOR;
+	stl_callout.subtype = STL_DRVTYPCALLOUT;
+
+	if (tty_register_driver(&stl_serial))
+		printk("STALLION: failed to register serial driver\n");
+	if (tty_register_driver(&stl_callout))
+		printk("STALLION: failed to register callout driver\n");
+
+	return(0);
+}
+
+/*****************************************************************************/
+/*                       CD1400 HARDWARE FUNCTIONS                           */
+/*****************************************************************************/
+
+/*
+ *	These functions get/set/update the registers of the cd1400 UARTs.
+ *	Access to the cd1400 registers is via an address/data io port pair.
+ *	(Maybe should make this inline...)
+ */
+
+static int stl_cd1400getreg(stlport_t *portp, int regnr)
+{
+	outb((regnr + portp->uartaddr), portp->ioaddr);
+	return(inb(portp->ioaddr + EREG_DATA));
+}
+
+static void stl_cd1400setreg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr + portp->uartaddr), portp->ioaddr);
+	outb(value, portp->ioaddr + EREG_DATA);
+}
+
+static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr + portp->uartaddr), portp->ioaddr);
+	if (inb(portp->ioaddr + EREG_DATA) != value) {
+		outb(value, portp->ioaddr + EREG_DATA);
+		return(1);
+	}
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Inbitialize the UARTs in a panel. We don't care what sort of board
+ *	these ports are on - since the port io registers are almost
+ *	identical when dealing with ports.
+ */
+
+static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
+{
+	unsigned int	gfrcr;
+	int		chipmask, i, j;
+	int		nrchips, uartaddr, ioaddr;
+
+#if DEBUG
+	printk("stl_panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
+#endif
+
+	BRDENABLE(panelp->brdnr, panelp->pagenr);
+
+/*
+ *	Check that each chip is present and started up OK.
+ */
+	chipmask = 0;
+	nrchips = panelp->nrports / CD1400_PORTS;
+	for (i = 0; (i < nrchips); i++) {
+		if (brdp->brdtype == BRD_ECHPCI) {
+			outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
+			ioaddr = panelp->iobase;
+		} else {
+			ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
+		}
+		uartaddr = (i & 0x01) ? 0x080 : 0;
+		outb((GFRCR + uartaddr), ioaddr);
+		outb(0, (ioaddr + EREG_DATA));
+		outb((CCR + uartaddr), ioaddr);
+		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+		outb((GFRCR + uartaddr), ioaddr);
+		for (j = 0; (j < CCR_MAXWAIT); j++) {
+			if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
+				break;
+		}
+		if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
+			printk("STALLION: cd1400 not responding, "
+				"brd=%d panel=%d chip=%d\n",
+				panelp->brdnr, panelp->panelnr, i);
+			continue;
+		}
+		chipmask |= (0x1 << i);
+		outb((PPR + uartaddr), ioaddr);
+		outb(PPR_SCALAR, (ioaddr + EREG_DATA));
+	}
+
+	BRDDISABLE(panelp->brdnr);
+	return(chipmask);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Initialize hardware specific port registers.
+ */
+
+static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
+{
+#if DEBUG
+	printk("stl_cd1400portinit(brdp=%x,panelp=%x,portp=%x)\n",
+		(int) brdp, (int) panelp, (int) portp);
+#endif
+
+	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) ||
+	    (portp == (stlport_t *) NULL))
+		return;
+
+	portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
+		(portp->portnr < 8)) ? 0 : EREG_BANKSIZE);
+	portp->uartaddr = (portp->portnr & 0x04) << 5;
+	portp->pagenr = panelp->pagenr + (portp->portnr >> 3);
+
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, LIVR, (portp->portnr << 3));
+	portp->hwid = stl_cd1400getreg(portp, GFRCR);
+	BRDDISABLE(portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Wait for the command register to be ready. We will poll this,
+ *	since it won't usually take too long to be ready.
+ */
+
+static void stl_cd1400ccrwait(stlport_t *portp)
+{
+	int	i;
+
+	for (i = 0; (i < CCR_MAXWAIT); i++) {
+		if (stl_cd1400getreg(portp, CCR) == 0) {
+			return;
+		}
+	}
+
+	printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n",
+		portp->portnr, portp->panelnr, portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Set up the cd1400 registers for a port based on the termios port
+ *	settings.
+ */
+
+static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp)
+{
+	stlbrd_t	*brdp;
+	unsigned long	flags;
+	unsigned int	clkdiv, baudrate;
+	unsigned char	cor1, cor2, cor3;
+	unsigned char	cor4, cor5, ccr;
+	unsigned char	srer, sreron, sreroff;
+	unsigned char	mcor1, mcor2, rtpr;
+	unsigned char	clk, div;
+
+	cor1 = 0;
+	cor2 = 0;
+	cor3 = 0;
+	cor4 = 0;
+	cor5 = 0;
+	ccr = 0;
+	rtpr = 0;
+	clk = 0;
+	div = 0;
+	mcor1 = 0;
+	mcor2 = 0;
+	sreron = 0;
+	sreroff = 0;
+
+	brdp = stl_brds[portp->brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return;
+
+/*
+ *	Set up the RX char ignore mask with those RX error types we
+ *	can ignore. We can get the cd1400 to help us out a little here,
+ *	it will ignore parity errors and breaks for us.
+ */
+	portp->rxignoremsk = 0;
+	if (tiosp->c_iflag & IGNPAR) {
+		portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
+		cor1 |= COR1_PARIGNORE;
+	}
+	if (tiosp->c_iflag & IGNBRK) {
+		portp->rxignoremsk |= ST_BREAK;
+		cor4 |= COR4_IGNBRK;
+	}
+
+	portp->rxmarkmsk = ST_OVERRUN;
+	if (tiosp->c_iflag & (INPCK | PARMRK))
+		portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
+	if (tiosp->c_iflag & BRKINT)
+		portp->rxmarkmsk |= ST_BREAK;
+
+/*
+ *	Go through the char size, parity and stop bits and set all the
+ *	option register appropriately.
+ */
+	switch (tiosp->c_cflag & CSIZE) {
+	case CS5:
+		cor1 |= COR1_CHL5;
+		break;
+	case CS6:
+		cor1 |= COR1_CHL6;
 		break;
 	case CS7:
 		cor1 |= COR1_CHL7;
@@ -2064,8 +3163,8 @@
 		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
 			baudrate = (portp->baud_base / portp->custom_divisor);
 	}
-	if (baudrate > STL_MAXBAUD)
-		baudrate = STL_MAXBAUD;
+	if (baudrate > STL_CD1400MAXBAUD)
+		baudrate = STL_CD1400MAXBAUD;
 
 	if (baudrate > 0) {
 		for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
@@ -2101,165 +3200,396 @@
 			cor2 |= COR2_IXM;
 	}
 
-	if (tiosp->c_cflag & CRTSCTS) {
-		cor2 |= COR2_CTSAE;
-		mcor1 |= FIFO_RTSTHRESHOLD;
-	}
+	if (tiosp->c_cflag & CRTSCTS) {
+		cor2 |= COR2_CTSAE;
+		mcor1 |= FIFO_RTSTHRESHOLD;
+	}
+
+/*
+ *	All cd1400 register values calculated so go through and set
+ *	them all up.
+ */
+
+#if DEBUG
+	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
+		portp->portnr, portp->panelnr, portp->brdnr);
+	printk("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n",
+		cor1, cor2, cor3, cor4, cor5);
+	printk("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n",
+		mcor1, mcor2, rtpr, sreron, sreroff);
+	printk("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
+	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+#endif
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3));
+	srer = stl_cd1400getreg(portp, SRER);
+	stl_cd1400setreg(portp, SRER, 0);
+	if (stl_cd1400updatereg(portp, COR1, cor1))
+		ccr = 1;
+	if (stl_cd1400updatereg(portp, COR2, cor2))
+		ccr = 1;
+	if (stl_cd1400updatereg(portp, COR3, cor3))
+		ccr = 1;
+	if (ccr) {
+		stl_cd1400ccrwait(portp);
+		stl_cd1400setreg(portp, CCR, CCR_CORCHANGE);
+	}
+	stl_cd1400setreg(portp, COR4, cor4);
+	stl_cd1400setreg(portp, COR5, cor5);
+	stl_cd1400setreg(portp, MCOR1, mcor1);
+	stl_cd1400setreg(portp, MCOR2, mcor2);
+	if (baudrate > 0) {
+		stl_cd1400setreg(portp, TCOR, clk);
+		stl_cd1400setreg(portp, TBPR, div);
+		stl_cd1400setreg(portp, RCOR, clk);
+		stl_cd1400setreg(portp, RBPR, div);
+	}
+	stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
+	stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
+	stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
+	stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
+	stl_cd1400setreg(portp, RTPR, rtpr);
+	mcor1 = stl_cd1400getreg(portp, MSVR1);
+	if (mcor1 & MSVR1_DCD)
+		portp->sigs |= TIOCM_CD;
+	else
+		portp->sigs &= ~TIOCM_CD;
+	stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Set the state of the DTR and RTS signals.
+ */
+
+static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts)
+{
+	unsigned char	msvr1, msvr2;
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n",
+		(int) portp, dtr, rts);
+#endif
+
+	msvr1 = 0;
+	msvr2 = 0;
+	if (dtr > 0)
+		msvr1 = MSVR1_DTR;
+	if (rts > 0)
+		msvr2 = MSVR2_RTS;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	if (rts >= 0)
+		stl_cd1400setreg(portp, MSVR2, msvr2);
+	if (dtr >= 0)
+		stl_cd1400setreg(portp, MSVR1, msvr1);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the state of the signals.
+ */
+
+static int stl_cd1400getsignals(stlport_t *portp)
+{
+	unsigned char	msvr1, msvr2;
+	unsigned long	flags;
+	int		sigs;
+
+#if DEBUG
+	printk("stl_cd1400getsignals(portp=%x)\n", (int) portp);
+#endif
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	msvr1 = stl_cd1400getreg(portp, MSVR1);
+	msvr2 = stl_cd1400getreg(portp, MSVR2);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+
+	sigs = 0;
+	sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
+	sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
+	sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
+	sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
+#if 0
+	sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
+	sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
+#else
+	sigs |= TIOCM_DSR;
+#endif
+	return(sigs);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Enable/Disable the Transmitter and/or Receiver.
+ */
+
+static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx)
+{
+	unsigned char	ccr;
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
+#endif
+	ccr = 0;
+
+	if (tx == 0)
+		ccr |= CCR_TXDISABLE;
+	else if (tx > 0)
+		ccr |= CCR_TXENABLE;
+	if (rx == 0)
+		ccr |= CCR_RXDISABLE;
+	else if (rx > 0)
+		ccr |= CCR_RXENABLE;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400ccrwait(portp);
+	stl_cd1400setreg(portp, CCR, ccr);
+	stl_cd1400ccrwait(portp);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
 
 /*
- *	All register cd1400 register values calculated so go through and set
- *	them all up.
+ *	Start/stop the Transmitter and/or Receiver.
  */
 
+static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx)
+{
+	unsigned char	sreron, sreroff;
+	unsigned long	flags;
+
 #if DEBUG
-	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
-	printk("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
-	printk("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
-	printk("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
-	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+	printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
 #endif
 
+	sreron = 0;
+	sreroff = 0;
+	if (tx == 0)
+		sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
+	else if (tx == 1)
+		sreron |= SRER_TXDATA;
+	else if (tx >= 2)
+		sreron |= SRER_TXEMPTY;
+	if (rx == 0)
+		sreroff |= SRER_RXDATA;
+	else if (rx > 0)
+		sreron |= SRER_RXDATA;
+
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x3));
-	srer = stl_getreg(portp, SRER);
-	stl_setreg(portp, SRER, 0);
-	if (stl_updatereg(portp, COR1, cor1))
-		ccr = 1;
-	if (stl_updatereg(portp, COR2, cor2))
-		ccr = 1;
-	if (stl_updatereg(portp, COR3, cor3))
-		ccr = 1;
-	if (ccr) {
-		stl_ccrwait(portp);
-		stl_setreg(portp, CCR, CCR_CORCHANGE);
-	}
-	stl_setreg(portp, COR4, cor4);
-	stl_setreg(portp, COR5, cor5);
-	stl_setreg(portp, MCOR1, mcor1);
-	stl_setreg(portp, MCOR2, mcor2);
-	if (baudrate > 0) {
-		stl_setreg(portp, TCOR, clk);
-		stl_setreg(portp, TBPR, div);
-		stl_setreg(portp, RCOR, clk);
-		stl_setreg(portp, RBPR, div);
-	}
-	stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
-	stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
-	stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
-	stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
-	stl_setreg(portp, RTPR, rtpr);
-	mcor1 = stl_getreg(portp, MSVR1);
-	if (mcor1 & MSVR1_DCD)
-		portp->sigs |= TIOCM_CD;
-	else
-		portp->sigs &= ~TIOCM_CD;
-	stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, SRER,
+		((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
 	BRDDISABLE(portp->brdnr);
+	if (tx > 0)
+		set_bit(ASYI_TXBUSY, &portp->istate);
 	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Set the state of the DTR and RTS signals.
+ *	Disable all interrupts from this port.
  */
 
-static void stl_setsignals(stlport_t *portp, int dtr, int rts)
+static void stl_cd1400disableintrs(stlport_t *portp)
 {
-	unsigned char	msvr1, msvr2;
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+	printk("stl_cd1400disableintrs(portp=%x)\n", (int) portp);
 #endif
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, SRER, 0);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
 
-	msvr1 = 0;
-	msvr2 = 0;
-	if (dtr > 0)
-		msvr1 = MSVR1_DTR;
-	if (rts > 0)
-		msvr2 = MSVR2_RTS;
+/*****************************************************************************/
+
+static void stl_cd1400sendbreak(stlport_t *portp, long len)
+{
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400sendbreak(portp=%x,len=%d)\n",
+		(int) portp, (int) len);
+#endif
 
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	if (rts >= 0)
-		stl_setreg(portp, MSVR2, msvr2);
-	if (dtr >= 0)
-		stl_setreg(portp, MSVR1, msvr1);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, COR2,
+		(stl_cd1400getreg(portp, COR2) | COR2_ETC));
+	stl_cd1400setreg(portp, SRER,
+		((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
 	BRDDISABLE(portp->brdnr);
+	len = len / 5;
+	portp->brklen = (len > 255) ? 255 : len;
+	portp->stats.txbreaks++;
 	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the state of the signals.
+ *	Take flow control actions...
  */
 
-static int stl_getsignals(stlport_t *portp)
+static void stl_cd1400flowctrl(stlport_t *portp, int state)
 {
-	unsigned char	msvr1, msvr2;
-	unsigned long	flags;
-	int		sigs;
+	struct tty_struct	*tty;
+	unsigned long		flags;
 
 #if DEBUG
-	printk("stl_getsignals(portp=%x)\n", (int) portp);
+	printk("stl_cd1400flowctrl(portp=%x,state=%x)\n", (int) portp, state);
 #endif
 
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
+
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	msvr1 = stl_getreg(portp, MSVR1);
-	msvr2 = stl_getreg(portp, MSVR2);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+
+	if (state) {
+		if (tty->termios->c_iflag & IXOFF) {
+			stl_cd1400ccrwait(portp);
+			stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
+			portp->stats.rxxon++;
+			stl_cd1400ccrwait(portp);
+		}
+/*
+ *		Question: should we return RTS to what it was before? It may
+ *		have been set by an ioctl... Suppose not, since if you have
+ *		hardware flow control set then it is pretty silly to go and
+ *		set the RTS line by hand.
+ */
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_cd1400setreg(portp, MCOR1,
+				(stl_cd1400getreg(portp, MCOR1) |
+				FIFO_RTSTHRESHOLD));
+			stl_cd1400setreg(portp, MSVR2, MSVR2_RTS);
+			portp->stats.rxrtson++;
+		}
+	} else {
+		if (tty->termios->c_iflag & IXOFF) {
+			stl_cd1400ccrwait(portp);
+			stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
+			portp->stats.rxxoff++;
+			stl_cd1400ccrwait(portp);
+		}
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_cd1400setreg(portp, MCOR1,
+				(stl_cd1400getreg(portp, MCOR1) & 0xf0));
+			stl_cd1400setreg(portp, MSVR2, 0);
+			portp->stats.rxrtsoff++;
+		}
+	}
+
 	BRDDISABLE(portp->brdnr);
-	sigs = 0;
-	sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
-	sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
-	sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
-	sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
-	sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
-	sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
 	restore_flags(flags);
-	return(sigs);
 }
 
 /*****************************************************************************/
 
 /*
- *	Enable/Disable the Transmitter and/or Receiver.
+ *	Send a flow control character...
  */
 
-static void stl_enablerxtx(stlport_t *portp, int rx, int tx)
+static void stl_cd1400sendflow(stlport_t *portp, int state)
+{
+	struct tty_struct	*tty;
+	unsigned long		flags;
+
+#if DEBUG
+	printk("stl_cd1400sendflow(portp=%x,state=%x)\n", (int) portp, state);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	if (state) {
+		stl_cd1400ccrwait(portp);
+		stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
+		portp->stats.rxxon++;
+		stl_cd1400ccrwait(portp);
+	} else {
+		stl_cd1400ccrwait(portp);
+		stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
+		portp->stats.rxxoff++;
+		stl_cd1400ccrwait(portp);
+	}
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+static void stl_cd1400flush(stlport_t *portp)
 {
-	unsigned char	ccr;
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+	printk("stl_cd1400flush(portp=%x)\n", (int) portp);
 #endif
-	ccr = 0;
 
-	if (tx == 0)
-		ccr |= CCR_TXDISABLE;
-	else if (tx > 0)
-		ccr |= CCR_TXENABLE;
-	if (rx == 0)
-		ccr |= CCR_RXDISABLE;
-	else if (rx > 0)
-		ccr |= CCR_RXENABLE;
+	if (portp == (stlport_t *) NULL)
+		return;
 
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_ccrwait(portp);
-	stl_setreg(portp, CCR, ccr);
-	stl_ccrwait(portp);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400ccrwait(portp);
+	stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO);
+	stl_cd1400ccrwait(portp);
+	portp->tx.tail = portp->tx.head;
 	BRDDISABLE(portp->brdnr);
 	restore_flags(flags);
 }
@@ -2267,137 +3597,400 @@
 /*****************************************************************************/
 
 /*
- *	Start/stop the Transmitter and/or Receiver.
+ *	Return the current state of data flow on this port. This is only
+ *	really interresting when determining if data has fully completed
+ *	transmission or not... This is easy for the cd1400, it accurately
+ *	maintains the busy port flag.
+ */
+
+static int stl_cd1400datastate(stlport_t *portp)
+{
+#if DEBUG
+	printk("stl_cd1400datastate(portp=%x)\n", (int) portp);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return(0);
+
+	return(test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Interrupt service routine for cd1400 EasyIO boards.
+ */
+
+static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase)
+{
+	unsigned char	svrtype;
+
+#if DEBUG
+	printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n",
+		(int) panelp, iobase);
+#endif
+
+	outb(SVRR, iobase);
+	svrtype = inb(iobase + EREG_DATA);
+	if (panelp->nrports > 4) {
+		outb((SVRR + 0x80), iobase);
+		svrtype |= inb(iobase + EREG_DATA);
+	}
+
+	if (svrtype & SVRR_RX)
+		stl_cd1400rxisr(panelp, iobase);
+	else if (svrtype & SVRR_TX)
+		stl_cd1400txisr(panelp, iobase);
+	else if (svrtype & SVRR_MDM)
+		stl_cd1400mdmisr(panelp, iobase);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Interrupt service routine for cd1400 panels.
+ */
+
+static void stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase)
+{
+	unsigned char	svrtype;
+
+#if DEBUG
+	printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp,
+		iobase);
+#endif
+
+	outb(SVRR, iobase);
+	svrtype = inb(iobase + EREG_DATA);
+	outb((SVRR + 0x80), iobase);
+	svrtype |= inb(iobase + EREG_DATA);
+	if (svrtype & SVRR_RX)
+		stl_cd1400rxisr(panelp, iobase);
+	else if (svrtype & SVRR_TX)
+		stl_cd1400txisr(panelp, iobase);
+	else if (svrtype & SVRR_MDM)
+		stl_cd1400mdmisr(panelp, iobase);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Transmit interrupt handler. This has gotta be fast!  Handling TX
+ *	chars is pretty simple, stuff as many as possible from the TX buffer
+ *	into the cd1400 FIFO. Must also handle TX breaks here, since they
+ *	are embedded as commands in the data stream. Oh no, had to use a goto!
+ *	This could be optimized more, will do when I get time...
+ *	In practice it is possible that interrupts are enabled but that the
+ *	port has been hung up. Need to handle not having any TX buffer here,
+ *	this is done by using the side effect that head and tail will also
+ *	be NULL if the buffer has been freed.
+ */
+
+static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr)
+{
+	stlport_t	*portp;
+	int		len, stlen;
+	char		*head, *tail;
+	unsigned char	ioack, srer;
+
+#if DEBUG
+	printk("stl_cd1400txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+	ioack = inb(ioaddr + EREG_TXACK);
+	if (((ioack & panelp->ackmask) != 0) ||
+	    ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
+		printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
+		return;
+	}
+	portp = panelp->ports[(ioack >> 3)];
+
+/*
+ *	Unfortunately we need to handle breaks in the data stream, since
+ *	this is the only way to generate them on the cd1400. Do it now if
+ *	a break is to be sent.
+ */
+	if (portp->brklen != 0) {
+		if (portp->brklen > 0) {
+			outb((TDR + portp->uartaddr), ioaddr);
+			outb(ETC_CMD, (ioaddr + EREG_DATA));
+			outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
+			outb(ETC_CMD, (ioaddr + EREG_DATA));
+			outb(ETC_DELAY, (ioaddr + EREG_DATA));
+			outb(portp->brklen, (ioaddr + EREG_DATA));
+			outb(ETC_CMD, (ioaddr + EREG_DATA));
+			outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
+			portp->brklen = -1;
+			goto stl_txalldone;
+		} else {
+			outb((COR2 + portp->uartaddr), ioaddr);
+			outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC),
+				(ioaddr + EREG_DATA));
+			portp->brklen = 0;
+		}
+	}
+
+	head = portp->tx.head;
+	tail = portp->tx.tail;
+	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+	if ((len == 0) || ((len < STL_TXBUFLOW) &&
+	    (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+		set_bit(ASYI_TXLOW, &portp->istate);
+		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+	}
+
+	if (len == 0) {
+		outb((SRER + portp->uartaddr), ioaddr);
+		srer = inb(ioaddr + EREG_DATA);
+		if (srer & SRER_TXDATA) {
+			srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
+		} else {
+			srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
+			clear_bit(ASYI_TXBUSY, &portp->istate);
+		}
+		outb(srer, (ioaddr + EREG_DATA));
+	} else {
+		len = MIN(len, CD1400_TXFIFOSIZE);
+		portp->stats.txtotal += len;
+		stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+		outb((TDR + portp->uartaddr), ioaddr);
+		outsb((ioaddr + EREG_DATA), tail, stlen);
+		len -= stlen;
+		tail += stlen;
+		if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+			tail = portp->tx.buf;
+		if (len > 0) {
+			outsb((ioaddr + EREG_DATA), tail, len);
+			tail += len;
+		}
+		portp->tx.tail = tail;
+	}
+
+stl_txalldone:
+	outb((EOSRR + portp->uartaddr), ioaddr);
+	outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *	Receive character interrupt handler. Determine if we have good chars
+ *	or bad chars and then process appropriately. Good chars are easy
+ *	just shove the lot into the RX buffer and set all status byte to 0.
+ *	If a bad RX char then process as required. This routine needs to be
+ *	fast!  In practice it is possible that we get an interrupt on a port
+ *	that is closed. This can happen on hangups - since they completely
+ *	shutdown a port not in user context. Need to handle this case.
  */
 
-static void stl_startrxtx(stlport_t *portp, int rx, int tx)
+static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr)
 {
-	unsigned char	sreron, sreroff;
-	unsigned long	flags;
+	stlport_t		*portp;
+	struct tty_struct	*tty;
+	unsigned int		ioack, len, buflen;
+	unsigned char		status;
+	char			ch;
 
 #if DEBUG
-	printk("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+	printk("stl_cd1400rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
 #endif
 
-	sreron = 0;
-	sreroff = 0;
-	if (tx == 0)
-		sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
-	else if (tx == 1)
-		sreron |= SRER_TXDATA;
-	else if (tx >= 2)
-		sreron |= SRER_TXEMPTY;
-	if (rx == 0)
-		sreroff |= SRER_RXDATA;
-	else if (rx > 0)
-		sreron |= SRER_RXDATA;
+	ioack = inb(ioaddr + EREG_RXACK);
+	if ((ioack & panelp->ackmask) != 0) {
+		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+		return;
+	}
+	portp = panelp->ports[(ioack >> 3)];
+	tty = portp->tty;
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron));
-	BRDDISABLE(portp->brdnr);
-	if (tx > 0)
-		set_bit(ASYI_TXBUSY, &portp->istate);
-	restore_flags(flags);
+	if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
+		outb((RDCR + portp->uartaddr), ioaddr);
+		len = inb(ioaddr + EREG_DATA);
+		if ((tty == (struct tty_struct *) NULL) ||
+		    (tty->flip.char_buf_ptr == (char *) NULL) ||
+		    ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+			outb((RDSR + portp->uartaddr), ioaddr);
+			insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
+			portp->stats.rxlost += len;
+			portp->stats.rxtotal += len;
+		} else {
+			len = MIN(len, buflen);
+			if (len > 0) {
+				outb((RDSR + portp->uartaddr), ioaddr);
+				insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
+				memset(tty->flip.flag_buf_ptr, 0, len);
+				tty->flip.flag_buf_ptr += len;
+				tty->flip.char_buf_ptr += len;
+				tty->flip.count += len;
+				tty_schedule_flip(tty);
+				portp->stats.rxtotal += len;
+			}
+		}
+	} else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
+		outb((RDSR + portp->uartaddr), ioaddr);
+		status = inb(ioaddr + EREG_DATA);
+		ch = inb(ioaddr + EREG_DATA);
+		if (status & ST_PARITY)
+			portp->stats.rxparity++;
+		if (status & ST_FRAMING)
+			portp->stats.rxframing++;
+		if (status & ST_OVERRUN)
+			portp->stats.rxoverrun++;
+		if (status & ST_BREAK)
+			portp->stats.rxbreaks++;
+		if (status & ST_SCHARMASK) {
+			if ((status & ST_SCHARMASK) == ST_SCHAR1)
+				portp->stats.txxon++;
+			if ((status & ST_SCHARMASK) == ST_SCHAR2)
+				portp->stats.txxoff++;
+			goto stl_rxalldone;
+		}
+		if ((tty != (struct tty_struct *) NULL) &&
+		    ((portp->rxignoremsk & status) == 0)) {
+			if (portp->rxmarkmsk & status) {
+				if (status & ST_BREAK) {
+					status = TTY_BREAK;
+					if (portp->flags & ASYNC_SAK) {
+						do_SAK(tty);
+						BRDENABLE(portp->brdnr, portp->pagenr);
+					}
+				} else if (status & ST_PARITY) {
+					status = TTY_PARITY;
+				} else if (status & ST_FRAMING) {
+					status = TTY_FRAME;
+				} else if(status & ST_OVERRUN) {
+					status = TTY_OVERRUN;
+				} else {
+					status = 0;
+				}
+			} else {
+				status = 0;
+			}
+			if (tty->flip.char_buf_ptr != (char *) NULL) {
+				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+					*tty->flip.flag_buf_ptr++ = status;
+					*tty->flip.char_buf_ptr++ = ch;
+					tty->flip.count++;
+				}
+				tty_schedule_flip(tty);
+			}
+		}
+	} else {
+		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+		return;
+	}
+
+stl_rxalldone:
+	outb((EOSRR + portp->uartaddr), ioaddr);
+	outb(0, (ioaddr + EREG_DATA));
 }
 
 /*****************************************************************************/
 
 /*
- *	Disable all interrupts from this port.
+ *	Modem interrupt handler. The is called when the modem signal line
+ *	(DCD) has changed state. Leave most of the work to the off-level
+ *	processing routine.
  */
 
-static void stl_disableintrs(stlport_t *portp)
+static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr)
 {
-	unsigned long	flags;
+	stlport_t	*portp;
+	unsigned int	ioack;
+	unsigned char	misr;
 
 #if DEBUG
-	printk("stl_disableintrs(portp=%x)\n", (int) portp);
+	printk("stl_cd1400mdmisr(panelp=%x)\n", (int) panelp);
 #endif
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_setreg(portp, SRER, 0);
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
+
+	ioack = inb(ioaddr + EREG_MDACK);
+	if (((ioack & panelp->ackmask) != 0) ||
+	    ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
+		printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
+		return;
+	}
+	portp = panelp->ports[(ioack >> 3)];
+
+	outb((MISR + portp->uartaddr), ioaddr);
+	misr = inb(ioaddr + EREG_DATA);
+	if (misr & MISR_DCD) {
+		set_bit(ASYI_DCDCHANGE, &portp->istate);
+		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+		portp->stats.modem++;
+	}
+
+	outb((EOSRR + portp->uartaddr), ioaddr);
+	outb(0, (ioaddr + EREG_DATA));
 }
 
 /*****************************************************************************/
+/*                      SC26198 HARDWARE FUNCTIONS                           */
+/*****************************************************************************/
 
-static void stl_sendbreak(stlport_t *portp, long len)
+/*
+ *	These functions get/set/update the registers of the sc26198 UARTs.
+ *	Access to the sc26198 registers is via an address/data io port pair.
+ *	(Maybe should make this inline...)
+ */
+
+static int stl_sc26198getreg(stlport_t *portp, int regnr)
 {
-	unsigned long	flags;
+	outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+	return(inb(portp->ioaddr + XP_DATA));
+}
 
-#if DEBUG
-	printk("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
-#endif
+static void stl_sc26198setreg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+	outb(value, (portp->ioaddr + XP_DATA));
+}
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC));
-	stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
-	BRDDISABLE(portp->brdnr);
-	len = len / 5;
-	portp->brklen = (len > 255) ? 255 : len;
-	portp->stats.txbreaks++;
-	restore_flags(flags);
+static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+	if (inb(portp->ioaddr + XP_DATA) != value) {
+		outb(value, (portp->ioaddr + XP_DATA));
+		return(1);
+	}
+	return(0);
 }
 
 /*****************************************************************************/
 
 /*
- *	Map in interrupt vector to this driver. Check that we don't
- *	already have this vector mapped, we might be sharing this
- *	interrupt across multiple boards.
+ *	Functions to get and set the sc26198 global registers.
  */
 
-static int stl_mapirq(int irq)
+static int stl_sc26198getglobreg(stlport_t *portp, int regnr)
 {
-	int	rc, i;
-
-#if DEBUG
-	printk("stl_mapirq(irq=%d)\n", irq);
-#endif
+	outb(regnr, (portp->ioaddr + XP_ADDR));
+	return(inb(portp->ioaddr + XP_DATA));
+}
 
-	rc = 0;
-	for (i = 0; (i < stl_numintrs); i++) {
-		if (stl_gotintrs[i] == irq)
-			break;
-	}
-	if (i >= stl_numintrs) {
-		if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) {
-			printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
-			rc = -ENODEV;
-		} else {
-			stl_gotintrs[stl_numintrs++] = irq;
-		}
-	}
-	return(rc);
+#if 0
+static void stl_sc26198setglobreg(stlport_t *portp, int regnr, int value)
+{
+	outb(regnr, (portp->ioaddr + XP_ADDR));
+	outb(value, (portp->ioaddr + XP_DATA));
 }
+#endif
 
 /*****************************************************************************/
 
 /*
- *	Try to find and initialize all the ports on a panel. We don't care
- *	what sort of board these ports are on - since the port io registers
- *	are almost identical when dealing with ports.
+ *	Inbitialize the UARTs in a panel. We don't care what sort of board
+ *	these ports are on - since the port io registers are almost
+ *	identical when dealing with ports.
  */
 
-static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
+static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
 {
-	stlport_t	*portp;
-	unsigned int	chipmask;
-	unsigned int	gfrcr;
-	int		nrchips, uartaddr, ioaddr;
-	int		i, j;
+	int	chipmask, i;
+	int	nrchips, ioaddr;
 
 #if DEBUG
-	printk("stl_initports(panelp=%x)\n", (int) panelp);
+	printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n",
+		(int) brdp, (int) panelp);
 #endif
 
 	BRDENABLE(panelp->brdnr, panelp->pagenr);
@@ -2406,773 +3999,959 @@
  *	Check that each chip is present and started up OK.
  */
 	chipmask = 0;
-	nrchips = panelp->nrports / CD1400_PORTS;
-	for (i = 0; (i < nrchips); i++) {
-		if (brdp->brdtype == BRD_ECHPCI) {
-			outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
-			ioaddr = panelp->iobase;
-		} else {
-			ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
-		}
-		uartaddr = (i & 0x01) ? 0x080 : 0;
-		outb((GFRCR + uartaddr), ioaddr);
-		outb(0, (ioaddr + EREG_DATA));
-		outb((CCR + uartaddr), ioaddr);
-		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
-		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
-		outb((GFRCR + uartaddr), ioaddr);
-		for (j = 0; (j < CCR_MAXWAIT); j++) {
-			if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
-				break;
-		}
-		if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
-			printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
-			continue;
-		}
-		chipmask |= (0x1 << i);
-		outb((PPR + uartaddr), ioaddr);
-		outb(PPR_SCALAR, (ioaddr + EREG_DATA));
-	}
+	nrchips = (panelp->nrports + 4) / SC26198_PORTS;
+	if (brdp->brdtype == BRD_ECHPCI)
+		outb(panelp->pagenr, brdp->ioctrl);
 
-/*
- *	All cd1400's are initialized (if found!). Now go through and setup
- *	each ports data structures. Also init the LIVR register of cd1400
- *	for each port.
- */
-	ioaddr = panelp->iobase;
-	for (i = 0; (i < panelp->nrports); i++) {
-		if (brdp->brdtype == BRD_ECHPCI) {
-			outb((panelp->pagenr + (i >> 3)), brdp->ioctrl);
-			ioaddr = panelp->iobase;
-		} else {
-			ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3));
-		}
-		if ((chipmask & (0x1 << (i / 4))) == 0)
-			continue;
-		portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
-		if (portp == (stlport_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
-			break;
-		}
-		memset(portp, 0, sizeof(stlport_t));
-		portp->magic = STL_PORTMAGIC;
-		portp->portnr = i;
-		portp->brdnr = panelp->brdnr;
-		portp->panelnr = panelp->panelnr;
-		portp->ioaddr = ioaddr;
-		portp->uartaddr = (i & 0x04) << 5;
-		portp->pagenr = panelp->pagenr + (i >> 3);
-		portp->clk = brdp->clk;
-		portp->baud_base = STL_BAUDBASE;
-		portp->close_delay = STL_CLOSEDELAY;
-		portp->closing_wait = 30 * HZ;
-		portp->normaltermios = stl_deftermios;
-		portp->callouttermios = stl_deftermios;
-		portp->tqueue.routine = stl_offintr;
-		portp->tqueue.data = portp;
-		portp->stats.brd = portp->brdnr;
-		portp->stats.panel = portp->panelnr;
-		portp->stats.port = portp->portnr;
-		stl_setreg(portp, CAR, (i & 0x03));
-		stl_setreg(portp, LIVR, (i << 3));
-		portp->hwid = stl_getreg(portp, GFRCR);
-		panelp->ports[i] = portp;
+	for (i = 0; (i < nrchips); i++) {
+		ioaddr = panelp->iobase + (i * 4); 
+		outb(SCCR, (ioaddr + XP_ADDR));
+		outb(CR_RESETALL, (ioaddr + XP_DATA));
+		outb(TSTR, (ioaddr + XP_ADDR));
+		if (inb(ioaddr + XP_DATA) != 0) {
+			printk("STALLION: sc26198 not responding, "
+				"brd=%d panel=%d chip=%d\n",
+				panelp->brdnr, panelp->panelnr, i);
+			continue;
+		}
+		chipmask |= (0x1 << i);
+		outb(GCCR, (ioaddr + XP_ADDR));
+		outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA));
+		outb(WDTRCR, (ioaddr + XP_ADDR));
+		outb(0xff, (ioaddr + XP_DATA));
 	}
 
 	BRDDISABLE(panelp->brdnr);
-	return(0);
+	return(chipmask);
 }
 
 /*****************************************************************************/
 
 /*
- *	Try to find and initialize an EasyIO board.
+ *	Initialize hardware specific port registers.
  */
 
-static int stl_initeio(stlbrd_t *brdp)
+static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
 {
-	stlpanel_t	*panelp;
-	unsigned int	status;
-	int		rc;
-
 #if DEBUG
-	printk("stl_initeio(brdp=%x)\n", (int) brdp);
+	printk("stl_sc26198portinit(brdp=%x,panelp=%x,portp=%x)\n",
+		(int) brdp, (int) panelp, (int) portp);
 #endif
 
-	brdp->ioctrl = brdp->ioaddr1 + 1;
-	brdp->iostatus = brdp->ioaddr1 + 2;
-	brdp->clk = EIO_CLK;
+	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) ||
+	    (portp == (stlport_t *) NULL))
+		return;
 
-	status = inb(brdp->iostatus);
-	switch (status & EIO_IDBITMASK) {
-	case EIO_8PORTM:
-		brdp->clk = EIO_CLK8M;
-		/* fall thru */
-	case EIO_8PORTRS:
-	case EIO_8PORTDI:
-		brdp->nrports = 8;
+	portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4);
+	portp->uartaddr = (portp->portnr & 0x07) << 4;
+	portp->pagenr = panelp->pagenr;
+	portp->hwid = 0x1;
+
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS);
+	BRDDISABLE(portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Set up the sc26198 registers for a port based on the termios port
+ *	settings.
+ */
+
+static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp)
+{
+	stlbrd_t	*brdp;
+	unsigned long	flags;
+	unsigned int	baudrate;
+	unsigned char	mr0, mr1, mr2, clk;
+	unsigned char	imron, imroff, iopr, ipr;
+
+	mr0 = 0;
+	mr1 = 0;
+	mr2 = 0;
+	clk = 0;
+	iopr = 0;
+	imron = 0;
+	imroff = 0;
+
+	brdp = stl_brds[portp->brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return;
+
+/*
+ *	Set up the RX char ignore mask with those RX error types we
+ *	can ignore.
+ */
+	portp->rxignoremsk = 0;
+	if (tiosp->c_iflag & IGNPAR)
+		portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING |
+			SR_RXOVERRUN);
+	if (tiosp->c_iflag & IGNBRK)
+		portp->rxignoremsk |= SR_RXBREAK;
+
+	portp->rxmarkmsk = SR_RXOVERRUN;
+	if (tiosp->c_iflag & (INPCK | PARMRK))
+		portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING);
+	if (tiosp->c_iflag & BRKINT)
+		portp->rxmarkmsk |= SR_RXBREAK;
+
+/*
+ *	Go through the char size, parity and stop bits and set all the
+ *	option register appropriately.
+ */
+	switch (tiosp->c_cflag & CSIZE) {
+	case CS5:
+		mr1 |= MR1_CS5;
 		break;
-	case EIO_4PORTRS:
-		brdp->nrports = 4;
+	case CS6:
+		mr1 |= MR1_CS6;
+		break;
+	case CS7:
+		mr1 |= MR1_CS7;
 		break;
 	default:
-		return(-ENODEV);
+		mr1 |= MR1_CS8;
+		break;
+	}
+
+	if (tiosp->c_cflag & CSTOPB)
+		mr2 |= MR2_STOP2;
+	else
+		mr2 |= MR2_STOP1;
+
+	if (tiosp->c_cflag & PARENB) {
+		if (tiosp->c_cflag & PARODD)
+			mr1 |= (MR1_PARENB | MR1_PARODD);
+		else
+			mr1 |= (MR1_PARENB | MR1_PAREVEN);
+	} else {
+		mr1 |= MR1_PARNONE;
 	}
 
-	request_region(brdp->ioaddr1, 8, "serial(EIO)");
+	mr1 |= MR1_ERRBLOCK;
+
+/*
+ *	Set the RX FIFO threshold at 8 chars. This gives a bit of breathing
+ *	space for hardware flow control and the like. This should be set to
+ *	VMIN.
+ */
+	mr2 |= MR2_RXFIFOHALF;
 
 /*
- *	Check that the supplied IRQ is good and then use it to setup the
- *	programmable interrupt bits on EIO board. Also set the edge/level
- *	triggered interrupt bit.
+ *	Calculate the baud rate timers. For now we will just assume that
+ *	the input and output baud are the same. The sc26198 has a fixed
+ *	baud rate table, so only discrete baud rates possible.
  */
-	if ((brdp->irq < 0) || (brdp->irq > 15) ||
-			(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-		printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
-		return(-EINVAL);
+	baudrate = tiosp->c_cflag & CBAUD;
+	if (baudrate & CBAUDEX) {
+		baudrate &= ~CBAUDEX;
+		if ((baudrate < 1) || (baudrate > 5))
+			tiosp->c_cflag &= ~CBAUDEX;
+		else
+			baudrate += 15;
+	}
+	baudrate = stl_baudrates[baudrate];
+	if ((tiosp->c_cflag & CBAUD) == B38400) {
+		if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baudrate = 57600;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baudrate = 115200;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+			baudrate = (portp->baud_base / portp->custom_divisor);
 	}
-	outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
+	if (baudrate > STL_SC26198MAXBAUD)
+		baudrate = STL_SC26198MAXBAUD;
 
-	panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
-	if (panelp == (stlpanel_t *) NULL) {
-		printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
-		return(-ENOMEM);
+	if (baudrate > 0) {
+		for (clk = 0; (clk < SC26198_NRBAUDS); clk++) {
+			if (baudrate <= sc26198_baudtable[clk])
+				break;
+		}
 	}
-	memset(panelp, 0, sizeof(stlpanel_t));
 
-	panelp->magic = STL_PANELMAGIC;
-	panelp->brdnr = brdp->brdnr;
-	panelp->panelnr = 0;
-	panelp->nrports = brdp->nrports;
-	panelp->iobase = brdp->ioaddr1;
-	panelp->hwid = status;
-	brdp->panels[0] = panelp;
-	brdp->nrpanels = 1;
-	brdp->state |= BRD_FOUND;
-	brdp->hwid = status;
-	rc = stl_mapirq(brdp->irq);
-	return(rc);
+/*
+ *	Check what form of modem signaling is required and set it up.
+ */
+	if (tiosp->c_cflag & CLOCAL) {
+		portp->flags &= ~ASYNC_CHECK_CD;
+	} else {
+		iopr |= IOPR_DCDCOS;
+		imron |= IR_IOPORT;
+		portp->flags |= ASYNC_CHECK_CD;
+	}
+
+/*
+ *	Setup sc26198 enhanced modes if we can. In particular we want to
+ *	handle as much of the flow control as possible automatically. As
+ *	well as saving a few CPU cycles it will also greatly improve flow
+ *	control reliability.
+ */
+	if (tiosp->c_iflag & IXON) {
+		mr0 |= MR0_SWFTX | MR0_SWFT;
+		imron |= IR_XONXOFF;
+	} else {
+		imroff |= IR_XONXOFF;
+	}
+	if (tiosp->c_iflag & IXOFF)
+		mr0 |= MR0_SWFRX;
+
+	if (tiosp->c_cflag & CRTSCTS) {
+		mr2 |= MR2_AUTOCTS;
+		mr1 |= MR1_AUTORTS;
+	}
+
+/*
+ *	All sc26198 register values calculated so go through and set
+ *	them all up.
+ */
+
+#if DEBUG
+	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
+		portp->portnr, portp->panelnr, portp->brdnr);
+	printk("    mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk);
+	printk("    iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff);
+	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+#endif
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IMR, 0);
+	stl_sc26198updatereg(portp, MR0, mr0);
+	stl_sc26198updatereg(portp, MR1, mr1);
+	stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK);
+	stl_sc26198updatereg(portp, MR2, mr2);
+	stl_sc26198updatereg(portp, IOPIOR,
+		((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr));
+
+	if (baudrate > 0) {
+		stl_sc26198setreg(portp, TXCSR, clk);
+		stl_sc26198setreg(portp, RXCSR, clk);
+	}
+
+	stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]);
+	stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]);
+
+	ipr = stl_sc26198getreg(portp, IPR);
+	if (ipr & IPR_DCD)
+		portp->sigs &= ~TIOCM_CD;
+	else
+		portp->sigs |= TIOCM_CD;
+
+	portp->imr = (portp->imr & ~imroff) | imron;
+	stl_sc26198setreg(portp, IMR, portp->imr);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Try to find an ECH board and initialize it. This code is capable of
- *	dealing with all types of ECH board.
+ *	Set the state of the DTR and RTS signals.
  */
 
-static int stl_initech(stlbrd_t *brdp)
+static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts)
 {
-	stlpanel_t	*panelp;
-	unsigned int	status, nxtid;
-	int		panelnr, ioaddr, i;
+	unsigned char	iopioron, iopioroff;
+	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_initech(brdp=%x)\n", (int) brdp);
+	printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n",
+		(int) portp, dtr, rts);
 #endif
 
-	status = 0;
+	iopioron = 0;
+	iopioroff = 0;
+	if (dtr == 0)
+		iopioroff |= IPR_DTR;
+	else if (dtr > 0)
+		iopioron |= IPR_DTR;
+	if (rts == 0)
+		iopioroff |= IPR_RTS;
+	else if (rts > 0)
+		iopioron |= IPR_RTS;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IOPIOR,
+		((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron));
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
 
 /*
- *	Set up the initial board register contents for boards. This varies a
- *	bit between the different board types. So we need to handle each
- *	separately. Also do a check that the supplied IRQ is good.
+ *	Return the state of the signals.
  */
-	if (brdp->brdtype == BRD_ECH) {
-		brdp->ioctrl = brdp->ioaddr1 + 1;
-		brdp->iostatus = brdp->ioaddr1 + 1;
-		status = inb(brdp->iostatus);
-		if ((status & ECH_IDBITMASK) != ECH_ID)
-			return(-ENODEV);
 
-		if ((brdp->irq < 0) || (brdp->irq > 15) ||
-				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
-			return(-EINVAL);
-		}
-		status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
-		status |= (stl_vecmap[brdp->irq] << 1);
-		outb((status | ECH_BRDRESET), brdp->ioaddr1);
-		brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
-		outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
-		outb(status, brdp->ioaddr1);
+static int stl_sc26198getsignals(stlport_t *portp)
+{
+	unsigned char	ipr;
+	unsigned long	flags;
+	int		sigs;
 
-		request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
-		request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
-	} else if (brdp->brdtype == BRD_ECHMC) {
-		brdp->ioctrl = brdp->ioaddr1 + 0x20;
-		brdp->iostatus = brdp->ioctrl;
-		status = inb(brdp->iostatus);
-		if ((status & ECH_IDBITMASK) != ECH_ID)
-			return(-ENODEV);
+#if DEBUG
+	printk("stl_sc26198getsignals(portp=%x)\n", (int) portp);
+#endif
 
-		if ((brdp->irq < 0) || (brdp->irq > 15) ||
-				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
-			return(-EINVAL);
-		}
-		outb(ECHMC_BRDRESET, brdp->ioctrl);
-		outb(ECHMC_INTENABLE, brdp->ioctrl);
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	ipr = stl_sc26198getreg(portp, IPR);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 
-		request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
-	} else if (brdp->brdtype == BRD_ECHPCI) {
-		brdp->ioctrl = brdp->ioaddr1 + 2;
-		request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
-		request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
-	}
+	sigs = 0;
+	sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD;
+	sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS;
+	sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR;
+	sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS;
+	sigs |= TIOCM_DSR;
+	return(sigs);
+}
 
-	brdp->clk = ECH_CLK;
-	brdp->hwid = status;
+/*****************************************************************************/
 
 /*
- *	Scan through the secondary io address space looking for panels.
- *	As we find'em allocate and initialize panel structures for each.
+ *	Enable/Disable the Transmitter and/or Receiver.
  */
-	ioaddr = brdp->ioaddr2;
-	panelnr = 0;
-	nxtid = 0;
 
-	for (i = 0; (i < STL_MAXPANELS); i++) {
-		if (brdp->brdtype == BRD_ECHPCI) {
-			outb(nxtid, brdp->ioctrl);
-			ioaddr = brdp->ioaddr2;
-		}
-		status = inb(ioaddr + ECH_PNLSTATUS);
-		if ((status & ECH_PNLIDMASK) != nxtid)
-			break;
-		panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
-		if (panelp == (stlpanel_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
-			break;
-		}
-		memset(panelp, 0, sizeof(stlpanel_t));
-		panelp->magic = STL_PANELMAGIC;
-		panelp->brdnr = brdp->brdnr;
-		panelp->panelnr = panelnr;
-		panelp->iobase = ioaddr;
-		panelp->pagenr = nxtid;
-		panelp->hwid = status;
-		if (status & ECH_PNL16PORT) {
-			if ((brdp->nrports + 16) > 32)
-				break;
-			panelp->nrports = 16;
-			panelp->ackmask = 0x80;
-			brdp->nrports += 16;
-			ioaddr += (EREG_BANKSIZE * 2);
-			nxtid += 2;
-		} else {
-			panelp->nrports = 8;
-			panelp->ackmask = 0xc0;
-			brdp->nrports += 8;
-			ioaddr += EREG_BANKSIZE;
-			nxtid++;
-		}
-		brdp->panels[panelnr++] = panelp;
-		brdp->nrpanels++;
-		if (ioaddr >= (brdp->ioaddr2 + 0x20))
-			break;
-	}
+static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx)
+{
+	unsigned char	ccr;
+	unsigned long	flags;
 
-	if (brdp->brdtype == BRD_ECH)
-		outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+#if DEBUG
+	printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
+#endif
 
-	brdp->state |= BRD_FOUND;
-	i = stl_mapirq(brdp->irq);
-	return(i);
+	ccr = portp->crenable;
+	if (tx == 0)
+		ccr &= ~CR_TXENABLE;
+	else if (tx > 0)
+		ccr |= CR_TXENABLE;
+	if (rx == 0)
+		ccr &= ~CR_RXENABLE;
+	else if (rx > 0)
+		ccr |= CR_RXENABLE;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, ccr);
+	BRDDISABLE(portp->brdnr);
+	portp->crenable = ccr;
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Initialize and configure the specified board.
- *	Scan through all the boards in the configuration and see what we
- *	can find. Handle EIO and the ECH boards a little differently here
- *	since the initial search and setup is too different.
+ *	Start/stop the Transmitter and/or Receiver.
  */
 
-static int stl_brdinit(stlbrd_t *brdp)
+static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx)
 {
-	int	i;
+	unsigned char	imr;
+	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+	printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
 #endif
 
-	switch (brdp->brdtype) {
-	case BRD_EASYIO:
-		stl_initeio(brdp);
-		break;
-	case BRD_ECH:
-	case BRD_ECHMC:
-	case BRD_ECHPCI:
-		stl_initech(brdp);
-		break;
-	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
-		return(ENODEV);
-	}
-
-	stl_brds[brdp->brdnr] = brdp;
-	if ((brdp->state & BRD_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
-		return(ENODEV);
-	}
-
-	for (i = 0; (i < STL_MAXPANELS); i++)
-		if (brdp->panels[i] != (stlpanel_t *) NULL)
-			stl_initports(brdp, brdp->panels[i]);
+	imr = portp->imr;
+	if (tx == 0)
+		imr &= ~IR_TXRDY;
+	else if (tx == 1)
+		imr |= IR_TXRDY;
+	if (rx == 0)
+		imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG);
+	else if (rx > 0)
+		imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG;
 
-	printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
-	return(0);
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IMR, imr);
+	BRDDISABLE(portp->brdnr);
+	portp->imr = imr;
+	if (tx > 0)
+		set_bit(ASYI_TXBUSY, &portp->istate);
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Find any ECH-PCI boards that might be installed. Initialize each
- *	one as it is found.
+ *	Disable all interrupts from this port.
  */
 
-#ifdef	CONFIG_PCI
-
-static int stl_findpcibrds()
+static void stl_sc26198disableintrs(stlport_t *portp)
 {
-	stlbrd_t	*brdp;
-	unsigned char	busnr, devnr, irq;
-	unsigned short	class;
-	unsigned int	ioaddr;
-	int		i, rc;
+	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_findpcibrds()\n");
+	printk("stl_sc26198disableintrs(portp=%x)\n", (int) portp);
 #endif
 
-	if (pcibios_present()) {
-		for (i = 0; (i < STL_MAXBRDS); i++) {
-			if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
-				break;
-
-/*
- *			Found a device on the PCI bus that has our vendor and
- *			device ID. Need to check now that it is really us.
- */
-			if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
-				printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			if (class == PCI_CLASS_STORAGE_IDE)
-				continue;
-
-			if (stl_nrbrds >= STL_MAXBRDS) {
-				printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
-				break;
-			}
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	portp->imr = 0;
+	stl_sc26198setreg(portp, IMR, 0);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
 
-/*
- *			We have a Stallion board. Allocate a board structure
- *			and initialize it. Read its IO and IRQ resources
- *			from conf space.
- */
-			brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-			if (brdp == (stlbrd_t *) NULL) {
-				printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
-				return(-ENOMEM);
-			}
-			memset(brdp, 0, sizeof(stlbrd_t));
-			brdp->magic = STL_BOARDMAGIC;
-			brdp->brdnr = stl_nrbrds++;
-			brdp->brdtype = BRD_ECHPCI;
+/*****************************************************************************/
 
-			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+static void stl_sc26198sendbreak(stlport_t *portp, long len)
+{
+	unsigned long	flags;
 
-			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
 #if DEBUG
-			printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
+	printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp,
+		(int) len);
 #endif
 
-			if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			brdp->irq = irq;
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (len / (1000 / HZ));
 
-			stl_brdinit(brdp);
-		}
-	}
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
+	BRDDISABLE(portp->brdnr);
+	portp->stats.txbreaks++;
 
-	return(0);
-}
+	schedule();
 
-#endif
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
 
 /*****************************************************************************/
 
 /*
- *	Scan through all the boards in the configuration and see what we
- *	can find. Handle EIO and the ECH boards a little differently here
- *	since the initial search and setup is too different.
+ *	Take flow control actions...
  */
 
-static int stl_initbrds()
+static void stl_sc26198flowctrl(stlport_t *portp, int state)
 {
-	stlbrd_t	*brdp;
-	stlconf_t	*confp;
-	int		i;
+	struct tty_struct	*tty;
+	unsigned long		flags;
+	unsigned char		mr0;
 
 #if DEBUG
-	printk("stl_initbrds()\n");
+	printk("stl_sc26198flowctrl(portp=%x,state=%x)\n", (int) portp, state);
 #endif
 
-	if (stl_nrbrds > STL_MAXBRDS) {
-		printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
-		stl_nrbrds = STL_MAXBRDS;
-	}
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
 
-/*
- *	Firstly scan the list of static boards configured. Allocate
- *	resources and initialize the boards as found.
- */
-	for (i = 0; (i < stl_nrbrds); i++) {
-		confp = &stl_brdconf[i];
-		brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-		if (brdp == (stlbrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
-			return(-ENOMEM);
-		}
-		memset(brdp, 0, sizeof(stlbrd_t));
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
 
-		brdp->magic = STL_BOARDMAGIC;
-		brdp->brdnr = i;
-		brdp->brdtype = confp->brdtype;
-		brdp->ioaddr1 = confp->ioaddr1;
-		brdp->ioaddr2 = confp->ioaddr2;
-		brdp->irq = confp->irq;
-		brdp->irqtype = confp->irqtype;
-		stl_brdinit(brdp);
+	if (state) {
+		if (tty->termios->c_iflag & IXOFF) {
+			mr0 = stl_sc26198getreg(portp, MR0);
+			stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+			stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
+			mr0 |= MR0_SWFRX;
+			portp->stats.rxxon++;
+			stl_sc26198wait(portp);
+			stl_sc26198setreg(portp, MR0, mr0);
+		}
+/*
+ *		Question: should we return RTS to what it was before? It may
+ *		have been set by an ioctl... Suppose not, since if you have
+ *		hardware flow control set then it is pretty silly to go and
+ *		set the RTS line by hand.
+ */
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_sc26198setreg(portp, MR1,
+				(stl_sc26198getreg(portp, MR1) | MR1_AUTORTS));
+			stl_sc26198setreg(portp, IOPIOR,
+				(stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS));
+			portp->stats.rxrtson++;
+		}
+	} else {
+		if (tty->termios->c_iflag & IXOFF) {
+			mr0 = stl_sc26198getreg(portp, MR0);
+			stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+			stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
+			mr0 &= ~MR0_SWFRX;
+			portp->stats.rxxoff++;
+			stl_sc26198wait(portp);
+			stl_sc26198setreg(portp, MR0, mr0);
+		}
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_sc26198setreg(portp, MR1,
+				(stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS));
+			stl_sc26198setreg(portp, IOPIOR,
+				(stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS));
+			portp->stats.rxrtsoff++;
+		}
 	}
 
-#ifdef CONFIG_PCI
-/*
- *	If the PCI BIOS support is compiled in then let's go looking for
- *	ECH-PCI boards.
- */
-	stl_findpcibrds();
-#endif
-
-	return(0);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the board stats structure to user app.
+ *	Send a flow control character.
  */
 
-static int stl_getbrdstats(combrd_t *bp)
+static void stl_sc26198sendflow(stlport_t *portp, int state)
 {
-	stlbrd_t	*brdp;
-	stlpanel_t	*panelp;
-	int		i;
+	struct tty_struct	*tty;
+	unsigned long		flags;
+	unsigned char		mr0;
 
-	memcpy_fromfs(&stl_brdstats, bp, sizeof(combrd_t));
-	if (stl_brdstats.brd >= STL_MAXBRDS)
-		return(-ENODEV);
-	brdp = stl_brds[stl_brdstats.brd];
-	if (brdp == (stlbrd_t *) NULL)
-		return(-ENODEV);
+#if DEBUG
+	printk("stl_sc26198sendflow(portp=%x,state=%x)\n", (int) portp, state);
+#endif
 
-	memset(&stl_brdstats, 0, sizeof(combrd_t));
-	stl_brdstats.brd = brdp->brdnr;
-	stl_brdstats.type = brdp->brdtype;
-	stl_brdstats.hwid = brdp->hwid;
-	stl_brdstats.state = brdp->state;
-	stl_brdstats.ioaddr = brdp->ioaddr1;
-	stl_brdstats.ioaddr2 = brdp->ioaddr2;
-	stl_brdstats.irq = brdp->irq;
-	stl_brdstats.nrpanels = brdp->nrpanels;
-	stl_brdstats.nrports = brdp->nrports;
-	for (i = 0; (i < brdp->nrpanels); i++) {
-		panelp = brdp->panels[i];
-		stl_brdstats.panels[i].panel = i;
-		stl_brdstats.panels[i].hwid = panelp->hwid;
-		stl_brdstats.panels[i].nrports = panelp->nrports;
-	}
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
 
-	memcpy_tofs(bp, &stl_brdstats, sizeof(combrd_t));
-	return(0);
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	if (state) {
+		mr0 = stl_sc26198getreg(portp, MR0);
+		stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+		stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
+		mr0 |= MR0_SWFRX;
+		portp->stats.rxxon++;
+		stl_sc26198wait(portp);
+		stl_sc26198setreg(portp, MR0, mr0);
+	} else {
+		mr0 = stl_sc26198getreg(portp, MR0);
+		stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+		stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
+		mr0 &= ~MR0_SWFRX;
+		portp->stats.rxxoff++;
+		stl_sc26198wait(portp);
+		stl_sc26198setreg(portp, MR0, mr0);
+	}
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
-/*
- *	Resolve the referenced port number into a port struct pointer.
- */
-
-static stlport_t *stl_getport(int brdnr, int panelnr, int portnr)
+static void stl_sc26198flush(stlport_t *portp)
 {
-	stlbrd_t	*brdp;
-	stlpanel_t	*panelp;
+	unsigned long	flags;
 
-	if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
-		return((stlport_t *) NULL);
-	brdp = stl_brds[brdnr];
-	if (brdp == (stlbrd_t *) NULL)
-		return((stlport_t *) NULL);
-	if ((panelnr < 0) || (panelnr >= brdp->nrpanels))
-		return((stlport_t *) NULL);
-	panelp = brdp->panels[panelnr];
-	if (panelp == (stlpanel_t *) NULL)
-		return((stlport_t *) NULL);
-	if ((portnr < 0) || (portnr >= panelp->nrports))
-		return((stlport_t *) NULL);
-	return(panelp->ports[portnr]);
+#if DEBUG
+	printk("stl_sc26198flush(portp=%x)\n", (int) portp);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, CR_TXRESET);
+	stl_sc26198setreg(portp, SCCR, portp->crenable);
+	BRDDISABLE(portp->brdnr);
+	portp->tx.tail = portp->tx.head;
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the port stats structure to user app. A NULL port struct
- *	pointer passed in means that we need to find out from the app
- *	what port to get stats for (used through board control device).
+ *	Return the current state of data flow on this port. This is only
+ *	really interresting when determining if data has fully completed
+ *	transmission or not... The sc26198 interrupt scheme cannot
+ *	determine when all data has actually drained, so we need to
+ *	check the port statusy register to be sure.
  */
 
-static int stl_getportstats(stlport_t *portp, comstats_t *cp)
+static int stl_sc26198datastate(stlport_t *portp)
 {
-	unsigned char	*head, *tail;
 	unsigned long	flags;
+	unsigned char	sr;
 
-	if (portp == (stlport_t *) NULL) {
-		memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t));
-		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
-		if (portp == (stlport_t *) NULL)
-			return(-ENODEV);
-	}
-
-	portp->stats.state = portp->istate;
-	portp->stats.flags = portp->flags;
-	portp->stats.hwid = portp->hwid;
-
-	portp->stats.ttystate = 0;
-	portp->stats.cflags = 0;
-	portp->stats.iflags = 0;
-	portp->stats.oflags = 0;
-	portp->stats.lflags = 0;
-	portp->stats.rxbuffered = 0;
+#if DEBUG
+	printk("stl_sc26198datastate(portp=%x)\n", (int) portp);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return(0);
+	if (test_bit(ASYI_TXBUSY, &portp->istate))
+		return(1);
 
 	save_flags(flags);
 	cli();
-	if (portp->tty != (struct tty_struct *) NULL) {
-		if (portp->tty->driver_data == portp) {
-			portp->stats.ttystate = portp->tty->flags;
-			portp->stats.rxbuffered = portp->tty->flip.count;
-			if (portp->tty->termios != (struct termios *) NULL) {
-				portp->stats.cflags = portp->tty->termios->c_cflag;
-				portp->stats.iflags = portp->tty->termios->c_iflag;
-				portp->stats.oflags = portp->tty->termios->c_oflag;
-				portp->stats.lflags = portp->tty->termios->c_lflag;
-			}
-		}
-	}
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	sr = stl_sc26198getreg(portp, SR);
+	BRDDISABLE(portp->brdnr);
 	restore_flags(flags);
 
-	head = portp->tx.head;
-	tail = portp->tx.tail;
-	portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)));
+	return((sr & SR_TXEMPTY) ? 0 : 1);
+}
 
-	portp->stats.signals = (unsigned long) stl_getsignals(portp);
+/*****************************************************************************/
 
-	memcpy_tofs(cp, &portp->stats, sizeof(comstats_t));
-	return(0);
+/*
+ *	Delay for a small amount of time, to give the sc26198 a chance
+ *	to process a command...
+ */
+
+static void stl_sc26198wait(stlport_t *portp)
+{
+	int	i;
+
+#if DEBUG
+	printk("stl_sc26198wait(portp=%x)\n", (int) portp);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return;
+
+	for (i = 0; (i < 20); i++)
+		stl_sc26198getglobreg(portp, TSTR);
 }
 
 /*****************************************************************************/
 
 /*
- *	Clear the port stats structure. We also return it zeroed out...
+ *	If we are TX flow controlled and in IXANY mode then we may
+ *	need to unflow control here. We gotta do this because of the
+ *	automatic flow control modes of the sc26198.
  */
 
-static int stl_clrportstats(stlport_t *portp, comstats_t *cp)
+static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty)
 {
-	if (portp == (stlport_t *) NULL) {
-		memcpy_fromfs(&stl_comstats, cp, sizeof(comstats_t));
-		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
-		if (portp == (stlport_t *) NULL)
-			return(-ENODEV);
-	}
+	unsigned char	mr0;
 
-	memset(&portp->stats, 0, sizeof(comstats_t));
-	portp->stats.brd = portp->brdnr;
-	portp->stats.panel = portp->panelnr;
-	portp->stats.port = portp->portnr;
-	memcpy_tofs(cp, &portp->stats, sizeof(comstats_t));
-	return(0);
+	mr0 = stl_sc26198getreg(portp, MR0);
+	stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+	stl_sc26198setreg(portp, SCCR, CR_HOSTXON);
+	stl_sc26198wait(portp);
+	stl_sc26198setreg(portp, MR0, mr0);
+	clear_bit(ASYI_TXFLOWED, &portp->istate);
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the entire driver ports structure to a user app.
+ *	Interrupt service routine for sc26198 panels.
  */
 
-static int stl_getportstruct(unsigned long arg)
+static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase)
 {
 	stlport_t	*portp;
+	unsigned int	iack;
 
-	memcpy_fromfs(&stl_dummyport, (void *) arg, sizeof(stlport_t));
-	portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
-		 stl_dummyport.portnr);
-	if (portp == (stlport_t *) NULL)
-		return(-ENODEV);
-	memcpy_tofs((void *) arg, portp, sizeof(stlport_t));
-	return(0);
+/* 
+ *	Work around bug in sc26198 chip... Cannot have A6 address
+ *	line of UART high, else iack will be returned as 0.
+ */
+	outb(0, (iobase + 1));
+
+	iack = inb(iobase + XP_IACK);
+	portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)];
+
+	if (iack & IVR_RXDATA)
+		stl_sc26198rxisr(portp, iack);
+	else if (iack & IVR_TXDATA)
+		stl_sc26198txisr(portp);
+	else
+		stl_sc26198otherisr(portp, iack);
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the entire driver board structure to a user app.
+ *	Transmit interrupt handler. This has gotta be fast!  Handling TX
+ *	chars is pretty simple, stuff as many as possible from the TX buffer
+ *	into the sc26198 FIFO.
+ *	In practice it is possible that interrupts are enabled but that the
+ *	port has been hung up. Need to handle not having any TX buffer here,
+ *	this is done by using the side effect that head and tail will also
+ *	be NULL if the buffer has been freed.
  */
 
-static int stl_getbrdstruct(unsigned long arg)
+static void stl_sc26198txisr(stlport_t *portp)
 {
-	stlbrd_t	*brdp;
+	unsigned int	ioaddr;
+	unsigned char	mr0;
+	int		len, stlen;
+	char		*head, *tail;
 
-	memcpy_fromfs(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t));
-	if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS))
-		return(-ENODEV);
-	brdp = stl_brds[stl_dummybrd.brdnr];
-	if (brdp == (stlbrd_t *) NULL)
-		return(-ENODEV);
-	memcpy_tofs((void *) arg, brdp, sizeof(stlbrd_t));
-	return(0);
+#if DEBUG
+	printk("stl_sc26198txisr(portp=%x)\n", (int) portp);
+#endif
+
+	ioaddr = portp->ioaddr;
+	head = portp->tx.head;
+	tail = portp->tx.tail;
+	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+	if ((len == 0) || ((len < STL_TXBUFLOW) &&
+	    (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+		set_bit(ASYI_TXLOW, &portp->istate);
+		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+	}
+
+	if (len == 0) {
+		outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR));
+		mr0 = inb(ioaddr + XP_DATA);
+		if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) {
+			portp->imr &= ~IR_TXRDY;
+			outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR));
+			outb(portp->imr, (ioaddr + XP_DATA));
+			clear_bit(ASYI_TXBUSY, &portp->istate);
+		} else {
+			mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY);
+			outb(mr0, (ioaddr + XP_DATA));
+		}
+	} else {
+		len = MIN(len, SC26198_TXFIFOSIZE);
+		portp->stats.txtotal += len;
+		stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+		outb(GTXFIFO, (ioaddr + XP_ADDR));
+		outsb((ioaddr + XP_DATA), tail, stlen);
+		len -= stlen;
+		tail += stlen;
+		if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+			tail = portp->tx.buf;
+		if (len > 0) {
+			outsb((ioaddr + XP_DATA), tail, len);
+			tail += len;
+		}
+		portp->tx.tail = tail;
+	}
 }
 
 /*****************************************************************************/
 
 /*
- *	The "staliomem" device is also required to do some special operations
- *	on the board and/or ports. In this driver it is mostly used for stats
- *	collection.
+ *	Receive character interrupt handler. Determine if we have good chars
+ *	or bad chars and then process appropriately. Good chars are easy
+ *	just shove the lot into the RX buffer and set all status byte to 0.
+ *	If a bad RX char then process as required. This routine needs to be
+ *	fast!  In practice it is possible that we get an interrupt on a port
+ *	that is closed. This can happen on hangups - since they completely
+ *	shutdown a port not in user context. Need to handle this case.
  */
 
-static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack)
 {
-	int	brdnr, rc;
+	struct tty_struct	*tty;
+	unsigned int		len, buflen, ioaddr;
 
 #if DEBUG
-	printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+	printk("stl_sc26198rxisr(portp=%x,iack=%x)\n", (int) portp, iack);
 #endif
 
-	brdnr = MINOR(ip->i_rdev);
-	if (brdnr >= STL_MAXBRDS)
-		return(-ENODEV);
-	rc = 0;
-
-	switch (cmd) {
-	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg);
-		break;
-	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg);
-		break;
-	case COM_GETBRDSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
-			rc = stl_getbrdstats((combrd_t *) arg);
-		break;
-	case COM_READPORT:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0)
-			rc = stl_getportstruct(arg);
-		break;
-	case COM_READBOARD:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0)
-			rc = stl_getbrdstruct(arg);
-		break;
-	default:
-		rc = -ENOIOCTLCMD;
-		break;
+	tty = portp->tty;
+	ioaddr = portp->ioaddr;
+	outb(GIBCR, (ioaddr + XP_ADDR));
+	len = inb(ioaddr + XP_DATA) + 1;
+
+	if ((iack & IVR_TYPEMASK) == IVR_RXDATA) {
+		if ((tty == (struct tty_struct *) NULL) ||
+		    (tty->flip.char_buf_ptr == (char *) NULL) ||
+		    ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+			outb(GRXFIFO, (ioaddr + XP_ADDR));
+			insb((ioaddr + XP_DATA), &stl_unwanted[0], len);
+			portp->stats.rxlost += len;
+			portp->stats.rxtotal += len;
+		} else {
+			len = MIN(len, buflen);
+			if (len > 0) {
+				outb(GRXFIFO, (ioaddr + XP_ADDR));
+				insb((ioaddr + XP_DATA), tty->flip.char_buf_ptr, len);
+				memset(tty->flip.flag_buf_ptr, 0, len);
+				tty->flip.flag_buf_ptr += len;
+				tty->flip.char_buf_ptr += len;
+				tty->flip.count += len;
+				tty_schedule_flip(tty);
+				portp->stats.rxtotal += len;
+			}
+		}
+	} else {
+		stl_sc26198rxbadchars(portp);
 	}
 
-	return(rc);
+/*
+ *	If we are TX flow controlled and in IXANY mode then we may need
+ *	to unflow control here. We gotta do this because of the automatic
+ *	flow control modes of the sc26198.
+ */
+	if (test_bit(ASYI_TXFLOWED, &portp->istate)) {
+		if ((tty != (struct tty_struct *) NULL) &&
+		    (tty->termios != (struct termios *) NULL) &&
+		    (tty->termios->c_iflag & IXANY)) {
+			stl_sc26198txunflow(portp, tty);
+		}
+	}
 }
 
 /*****************************************************************************/
 
-int stl_init(void)
+/*
+ *	Process an RX bad character.
+ */
+
+static void inline stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch)
 {
-	printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion);
+	struct tty_struct	*tty;
+	unsigned int		ioaddr;
 
-	stl_initbrds();
+	tty = portp->tty;
+	ioaddr = portp->ioaddr;
+
+	if (status & SR_RXPARITY)
+		portp->stats.rxparity++;
+	if (status & SR_RXFRAMING)
+		portp->stats.rxframing++;
+	if (status & SR_RXOVERRUN)
+		portp->stats.rxoverrun++;
+	if (status & SR_RXBREAK)
+		portp->stats.rxbreaks++;
+
+	if ((tty != (struct tty_struct *) NULL) &&
+	    ((portp->rxignoremsk & status) == 0)) {
+		if (portp->rxmarkmsk & status) {
+			if (status & SR_RXBREAK) {
+				status = TTY_BREAK;
+				if (portp->flags & ASYNC_SAK) {
+					do_SAK(tty);
+					BRDENABLE(portp->brdnr, portp->pagenr);
+				}
+			} else if (status & SR_RXPARITY) {
+				status = TTY_PARITY;
+			} else if (status & SR_RXFRAMING) {
+				status = TTY_FRAME;
+			} else if(status & SR_RXOVERRUN) {
+				status = TTY_OVERRUN;
+			} else {
+				status = 0;
+			}
+		} else {
+			status = 0;
+		}
+
+		if (tty->flip.char_buf_ptr != (char *) NULL) {
+			if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+				*tty->flip.flag_buf_ptr++ = status;
+				*tty->flip.char_buf_ptr++ = ch;
+				tty->flip.count++;
+			}
+			tty_schedule_flip(tty);
+		}
+
+		if (status == 0)
+			portp->stats.rxtotal++;
+	}
+}
+
+/*****************************************************************************/
 
 /*
- *	Allocate a temporary write buffer.
+ *	Process all characters in the RX FIFO of the UART. Check all char
+ *	status bytes as well, and process as required. We need to check
+ *	all bytes in the FIFO, in case some more enter the FIFO while we
+ *	are here. To get the exact character error type we need to switch
+ *	into CHAR error mode (that is why we need to make sure we empty
+ *	the FIFO).
  */
-	stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
-	if (stl_tmpwritebuf == (char *) NULL)
-		printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+
+static void stl_sc26198rxbadchars(stlport_t *portp)
+{
+	unsigned char	status, mr1;
+	char		ch;
 
 /*
- *	Set up a character driver for per board stuff. This is mainly used
- *	to do stats ioctls on the ports.
+ *	To get the precise error type for each character we must switch
+ *	back into CHAR error mode.
  */
-	if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
-		printk("STALLION: failed to register serial board device\n");
+	mr1 = stl_sc26198getreg(portp, MR1);
+	stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK));
+
+	while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) {
+		stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR);
+		ch = stl_sc26198getreg(portp, RXFIFO);
+		stl_sc26198rxbadch(portp, status, ch);
+	}
 
 /*
- *	Set up the tty driver structure and register us as a driver.
- *	Also setup the callout tty device.
+ *	To get correct interrupt class we must switch back into BLOCK
+ *	error mode.
  */
-	memset(&stl_serial, 0, sizeof(struct tty_driver));
-	stl_serial.magic = TTY_DRIVER_MAGIC;
-	stl_serial.name = stl_serialname;
-	stl_serial.major = STL_SERIALMAJOR;
-	stl_serial.minor_start = 0;
-	stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
-	stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
-	stl_serial.subtype = STL_DRVTYPSERIAL;
-	stl_serial.init_termios = stl_deftermios;
-	stl_serial.flags = TTY_DRIVER_REAL_RAW;
-	stl_serial.refcount = &stl_refcount;
-	stl_serial.table = stl_ttys;
-	stl_serial.termios = stl_termios;
-	stl_serial.termios_locked = stl_termioslocked;
-	
-	stl_serial.open = stl_open;
-	stl_serial.close = stl_close;
-	stl_serial.write = stl_write;
-	stl_serial.put_char = stl_putchar;
-	stl_serial.flush_chars = stl_flushchars;
-	stl_serial.write_room = stl_writeroom;
-	stl_serial.chars_in_buffer = stl_charsinbuffer;
-	stl_serial.ioctl = stl_ioctl;
-	stl_serial.set_termios = stl_settermios;
-	stl_serial.throttle = stl_throttle;
-	stl_serial.unthrottle = stl_unthrottle;
-	stl_serial.stop = stl_stop;
-	stl_serial.start = stl_start;
-	stl_serial.hangup = stl_hangup;
-	stl_serial.flush_buffer = stl_flushbuffer;
+	stl_sc26198setreg(portp, MR1, mr1);
+}
 
-	stl_callout = stl_serial;
-	stl_callout.name = stl_calloutname;
-	stl_callout.major = STL_CALLOUTMAJOR;
-	stl_callout.subtype = STL_DRVTYPCALLOUT;
+/*****************************************************************************/
 
-	if (tty_register_driver(&stl_serial))
-		printk("STALLION: failed to register serial driver\n");
-	if (tty_register_driver(&stl_callout))
-		printk("STALLION: failed to register callout driver\n");
+/*
+ *	Other interrupt handler. This includes modem signals, flow
+ *	control actions, etc. Most stuff is left to off-level interrupt
+ *	processing time.
+ */
 
-	return(0);
+static void stl_sc26198otherisr(stlport_t *portp, unsigned int iack)
+{
+	unsigned char	cir, ipr, xisr;
+
+#if DEBUG
+	printk("stl_sc26198otherisr(portp=%x,iack=%x)\n", (int) portp, iack);
+#endif
+
+	cir = stl_sc26198getglobreg(portp, CIR);
+
+	switch (cir & CIR_SUBTYPEMASK) {
+	case CIR_SUBCOS:
+		ipr = stl_sc26198getreg(portp, IPR);
+		if (ipr & IPR_DCDCHANGE) {
+			set_bit(ASYI_DCDCHANGE, &portp->istate);
+			queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+			portp->stats.modem++;
+		}
+		break;
+	case CIR_SUBXONXOFF:
+		xisr = stl_sc26198getreg(portp, XISR);
+		if (xisr & XISR_RXXONGOT) {
+			set_bit(ASYI_TXFLOWED, &portp->istate);
+			portp->stats.txxoff++;
+		}
+		if (xisr & XISR_RXXOFFGOT) {
+			clear_bit(ASYI_TXFLOWED, &portp->istate);
+			portp->stats.txxon++;
+		}
+		break;
+	case CIR_SUBBREAK:
+		stl_sc26198setreg(portp, SCCR, CR_BREAKRESET);
+		stl_sc26198rxbadchars(portp);
+		break;
+	default:
+		break;
+	}
 }
 
 /*****************************************************************************/
diff -u --recursive --new-file v2.0.34/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.0.34/linux/drivers/char/vt.c	Sun May 12 21:36:19 1996
+++ linux/drivers/char/vt.c	Mon Jul 13 13:47:30 1998
@@ -23,6 +23,7 @@
 
 #include <asm/io.h>
 #include <asm/segment.h>
+#include <asm/bitops.h>
 
 #include "kbd_kern.h"
 #include "vt_kern.h"
@@ -147,11 +148,16 @@
  * We also return immediately, which is what was implied within the X
  * comments - KDMKTONE doesn't put the process to sleep.
  */
+
+static unsigned int mksound_lock = 0;
+
 static void
 kd_nosound(unsigned long ignored)
 {
-	/* disable counter 2 */
-	outb(inb_p(0x61)&0xFC, 0x61);
+	/* if sound is being set up, don't turn it off */
+	if (!mksound_lock)
+               /* disable counter 2 */
+               outb(inb_p(0x61)&0xFC, 0x61);
 	return;
 }
 
@@ -165,25 +171,29 @@
 
 	if (hz > 20 && hz < 32767)
 		count = 1193180 / hz;
-	
-	cli();
-	del_timer(&sound_timer);
-	if (count) {
-		/* enable counter 2 */
-		outb_p(inb_p(0x61)|3, 0x61);
-		/* set command for counter 2, 2 byte write */
-		outb_p(0xB6, 0x43);
-		/* select desired HZ */
-		outb_p(count & 0xff, 0x42);
-		outb((count >> 8) & 0xff, 0x42);
-
-		if (ticks) {
-			sound_timer.expires = jiffies+ticks;
-			add_timer(&sound_timer);
-		}
-	} else
-		kd_nosound(0);
-	sti();
+        
+        /* ignore multiple simultaneous requests for sound */
+        if (!set_bit(0, &mksound_lock)) {
+        /* set_bit in 2.0.x is same as test-and-set in 2.1.x */
+                del_timer(&sound_timer);
+                if (count) {
+                        /* enable counter 2 */
+                        outb_p(inb_p(0x61)|3, 0x61);
+                        /* set command for counter 2, 2 byte write */
+                        outb_p(0xB6, 0x43);
+                        /* select desired HZ */
+                        outb_p(count & 0xff, 0x42);
+                        outb((count >> 8) & 0xff, 0x42);
+ 
+                        if (ticks) {
+                                sound_timer.expires = jiffies+ticks;
+                                add_timer(&sound_timer);
+                        }
+                } else
+                        kd_nosound(0);
+ 
+                mksound_lock = 0;
+        }	
 	return;
 }
 
diff -u --recursive --new-file v2.0.34/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.0.34/linux/drivers/isdn/isdn_net.c	Mon Jul 13 13:46:28 1998
+++ linux/drivers/isdn/isdn_net.c	Mon Jul 13 13:47:30 1998
@@ -229,7 +229,8 @@
 {
 	printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n",
 	       dev->name, reason);
-	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
+	if(skb->protocol==htons(ETH_P_IP))
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
 #if (LINUX_VERSION_CODE < 0x02010f)	/* 2.1.15 */
 		  ,dev
 #endif
diff -u --recursive --new-file v2.0.34/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c
--- v2.0.34/linux/drivers/isdn/sc/interrupt.c	Mon Aug  4 17:34:01 1997
+++ linux/drivers/isdn/sc/interrupt.c	Mon Jul 13 13:47:30 1998
@@ -32,7 +32,7 @@
 #include "message.h"
 #include "card.h"
 
-extern indicate_status(int, int, ulong, char *);
+extern int indicate_status(int, int, ulong, char *);
 extern void check_phystat(unsigned long);
 extern void dump_messages(int);
 extern int receivemessage(int, RspMessage *);
diff -u --recursive --new-file v2.0.34/linux/drivers/net/3c503.c linux/drivers/net/3c503.c
--- v2.0.34/linux/drivers/net/3c503.c	Thu Apr 11 23:49:36 1996
+++ linux/drivers/net/3c503.c	Mon Jul 13 13:47:30 1998
@@ -75,7 +75,7 @@
 static void el2_reset_8390(struct device *dev);
 static void el2_init_card(struct device *dev);
 static void el2_block_output(struct device *dev, int count,
-			     const unsigned char *buf, const start_page);
+			     const unsigned char *buf, const int start_page);
 static void el2_block_input(struct device *dev, int count, struct sk_buff *skb,
 			   int ring_offset);
 static void el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
@@ -432,7 +432,7 @@
  */
 static void
 el2_block_output(struct device *dev, int count,
-		 const unsigned char *buf, const start_page)
+		 const unsigned char *buf, const int start_page)
 {
     unsigned short int *wrd;
     int boguscount;		/* timeout counter */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/3c515.c linux/drivers/net/3c515.c
--- v2.0.34/linux/drivers/net/3c515.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/3c515.c	Mon Jul 13 13:47:31 1998
@@ -18,9 +18,9 @@
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
+static const int rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const mtu = 1500;
+static const int mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
@@ -149,7 +149,7 @@
 PCI cards, with the bus master interface extensively modified to work with
 the ISA bus.
 
-The card is capable of full-bus-master transfers with seperate
+The card is capable of full-bus-master transfers with separate
 lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
 DEC Tulip and Intel Speedo3.
 
diff -u --recursive --new-file v2.0.34/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.0.34/linux/drivers/net/3c59x.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/3c59x.c	Mon Jul 13 13:47:31 1998
@@ -20,9 +20,9 @@
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
+static const int rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const mtu = 1500;
+static const int mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
@@ -194,7 +194,7 @@
 versions of the FastEtherLink cards.  The supported product IDs are
   3c590, 3c592, 3c595, 3c597, 3c900, 3c905
 
-The ISA 3c515 is supported with a seperate driver, 3c515.c, included with
+The ISA 3c515 is supported with a separate driver, 3c515.c, included with
 the kernel source or available from
     cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
 
@@ -212,7 +212,7 @@
 series.  The primary interface is two programmed-I/O FIFOs, with an
 alternate single-contiguous-region bus-master transfer (see next).
 
-The 3c900 "Boomerang" series uses a full-bus-master interface with seperate
+The 3c900 "Boomerang" series uses a full-bus-master interface with separate
 lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
 DEC Tulip and Intel Speedo3.  The first chip version retains a compatible
 programmed-I/O interface that will be removed in the 'B' and subsequent
@@ -1342,7 +1342,7 @@
 }
 
 /*
- * Handle uncommon interrupt sources.  This is a seperate routine to minimize
+ * Handle uncommon interrupt sources.  This is a separate routine to minimize
  * the cache impact.
  */
 static void
diff -u --recursive --new-file v2.0.34/linux/drivers/net/Changelog.tlan linux/drivers/net/Changelog.tlan
--- v2.0.34/linux/drivers/net/Changelog.tlan	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/Changelog.tlan	Mon Jul 13 13:47:31 1998
@@ -1,5 +1,52 @@
 TLan Device Driver change log.
 
+0.43	- Changed the id strings of the Compaq devices to their real
+	  names.
+	- Added 2 other Olicom devices with a patch provided by
+	  Henrik Storner of Olicom.
+	- Changed AN so the driver won't try to autonegotiate with
+	  a partner that doesn't autonegotiate.
+	- Added a section to verify whether in full or half duplex.
+	- Added value checking for speed and duplex inputs.
+	- Added B012 and B030 Compaq devices.
+	  
+0.42	- Coverted tranceiver from misused per-tranceiver functions
+	  to a timer-oriented path that covers four possible classes
+	  of tranceivers:
+		1. Unmanaged
+		2. Manual configuration
+		3. Autonegotiation w/ manual configuration
+		4. Autonegotiation w/ auto configuration
+	- Added ability to force speed and duplex settings.
+	- Made speed, duplex, sa_int, etc, to be set per adapter with
+	  ether= command.
+	- Added support for Olicom OC-2326
+
+0.41	- Added non-bounce buffer paths.  Added TLan_FreeLists to
+	  dispose of unused sk_buff's at device close time.
+	- Discovered inlined functions aren't being inlined, or at
+	  least take up more space than macros would.
+
+0.40	- Refined polarity checking to handle case when polarity
+	  changes to normal from abnormal.
+	- Cleaned up TLan_Probe routine.
+	- Added an option for the SA_INTERRUPT flag to be set.
+	- Created FAQ.
+	- Removed all C++ style comments.
+	- Added error message if devices busmastering is inactive.
+	  Also will now skip device.
+	- Put cli and sti back into TLan_HandleInterrupt.  It makes
+	  me feel better.
+	- Moved the code that checks for boot parameter options to 
+	  tlan_probe.
+
+0.39	- Minor cosmetic cleanups (especially variable declarations).
+	- Changes low level TLAN functions to use dev structures instead
+	  individual data elements.
+        - Changed low level TLAN functions not to play with sti and cli
+	  if in an interrupt routine.
+	- Removed cli and sti from TLan_HandleInterrupt.
+
 0.38	- Added code to isolate the external PHY if the internal PHY is
 	  being used for AUI/BNC connectivity.  Also set the aui and
 	  debug variables from mem_start and mem_end if the driver is
@@ -19,7 +66,7 @@
 	  100Mbs should work now on 0xAE32.
 	- Fixed a small bug where heartbeat and PHY interrupts were
 	  always being enabled.
-	- Force the driver into Unmanaged PHY mode for 0xF130 devices,
+	- Force the the driver into Unmanaged PHY mode for 0xF130 devices,
 	  even if a managed (ie, the built-in one) PHY is detected.
 	- Moved the PHY initialization to after the onboard PHY is enabled,
 	  if selected.
diff -u --recursive --new-file v2.0.34/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.0.34/linux/drivers/net/Config.in	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/Config.in	Mon Jul 13 13:47:31 1998
@@ -25,16 +25,28 @@
 bool 'Radio network interfaces' CONFIG_NET_RADIO
 if [ "$CONFIG_NET_RADIO" != "n" ]; then
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'BAYCOM ser12 and par96 kiss emulation driver for AX.25' CONFIG_BAYCOM
-  fi
-  if [ "$CONFIG_AX25" = "y" ]; then
-    bool 'Gracilis PackeTwin support' CONFIG_PT
-    bool 'Ottawa PI and PI/2 support' CONFIG_PI
+    tristate 'BAYCOM ser12 and par96 driver for AX.25' CONFIG_BAYCOM
+    tristate 'Soundcard modem driver for AX.25' CONFIG_SOUNDMODEM
+    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
+    fi
   fi
+  tristate 'Serial port KISS driver for AX.25' CONFIG_MKISS
+  tristate 'BPQ Ethernet driver for AX.25' CONFIG_BPQETHER
+  tristate 'Gracilis PackeTwin support for AX.25' CONFIG_PT
+  tristate 'Ottawa PI and PI/2 support for AX.25' CONFIG_PI
+  tristate 'Z8530 SCC KISS emulation driver for AX.25' CONFIG_SCC
   tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
   tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
   tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC
-  tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
 fi
 #
 #	Ethernet
diff -u --recursive --new-file v2.0.34/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.0.34/linux/drivers/net/Makefile	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/Makefile	Mon Jul 13 13:47:31 1998
@@ -7,6 +7,10 @@
 # are difficult for users to deal with.
 include CONFIG
 
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
 L_TARGET := net.a
 L_OBJS   := auto_irq.o
 M_OBJS   :=
@@ -18,6 +22,8 @@
 CONFIG_8390_MODULE  :=
 CONFIG_SLHC_BUILTIN :=
 CONFIG_SLHC_MODULE  :=
+CONFIG_HDLCDRV_BUILTIN :=
+CONFIG_HDLCDRV_MODULE  :=
 
 ifeq ($(CONFIG_ISDN),y)
   ifeq ($(CONFIG_ISDN_PPP),y)
@@ -501,12 +507,36 @@
   endif
 endif
 
+ifeq ($(CONFIG_MKISS),y)
+L_OBJS += mkiss.o
+else
+  ifeq ($(CONFIG_MKISS),m)
+  M_OBJS += mkiss.o
+  endif
+endif
+
 ifeq ($(CONFIG_PI),y)
 L_OBJS += pi2.o
+else
+  ifeq ($(CONFIG_PI),m)
+  M_OBJS += pi2.o
+  endif
 endif
 
 ifeq ($(CONFIG_PT),y)
 L_OBJS += pt.o
+else
+  ifeq ($(CONFIG_PT),m)
+  M_OBJS += pt.o
+  endif
+endif
+
+ifeq ($(CONFIG_BPQETHER),y)
+L_OBJS += bpqether.o
+else
+  ifeq ($(CONFIG_BPQETHER),m)
+  M_OBJS += bpqether.o
+  endif
 endif
 
 # If anything built-in uses slhc, then build it into the kernel also.
@@ -516,6 +546,40 @@
 else
   ifdef CONFIG_SLHC_MODULE
   MX_OBJS += slhc.o
+  endif
+endif
+
+
+ifeq ($(CONFIG_BAYCOM),y)
+L_OBJS += baycom.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_BAYCOM),m)
+  CONFIG_HDLCDRV_MODULE = y
+  M_OBJS += baycom.o
+  endif
+endif
+
+ifeq ($(CONFIG_SOUNDMODEM),y)
+ALL_SUB_DIRS += soundmodem
+SUB_DIRS += soundmodem
+L_OBJS += soundmodem/soundmodem.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_SOUNDMODEM),m)
+  CONFIG_HDLCDRV_MODULE = y
+  ALL_SUB_DIRS += soundmodem
+  MOD_SUB_DIRS += soundmodem
+  endif
+endif
+
+# If anything built-in uses the hdlcdrv, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+ifdef CONFIG_HDLCDRV_BUILTIN
+LX_OBJS += hdlcdrv.o
+else
+  ifdef CONFIG_HDLCDRV_MODULE
+  MX_OBJS += hdlcdrv.o
   endif
 endif
 
diff -u --recursive --new-file v2.0.34/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.0.34/linux/drivers/net/Space.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/Space.c	Mon Jul 13 13:47:31 1998
@@ -153,13 +153,13 @@
 #if defined(CONFIG_SMC9194)
 	&& smc_init(dev)
 #endif
-#if defined(CONFIG_WD80x3) || defined(WD80x3)
+#if defined(CONFIG_WD80x3)
 	&& wd_probe(dev)
 #endif
-#if defined(CONFIG_EL2) || defined(EL2)	/* 3c503 */
+#if defined(CONFIG_EL2)		/* 3c503 */
 	&& el2_probe(dev)
 #endif
-#if defined(CONFIG_HPLAN) || defined(HPLAN)
+#if defined(CONFIG_HPLAN)
 	&& hp_probe(dev)
 #endif
 #if defined(CONFIG_HPLAN_PLUS)
@@ -255,9 +255,6 @@
 #ifdef CONFIG_TLAN
 	&& tlan_probe(dev)
 #endif
-#ifdef CONFIG_PCNET32
-	&& pcnet32_probe(dev)
-#endif
 #ifdef CONFIG_LANCE
 	&& lance_probe(dev)
 #endif
@@ -275,18 +272,6 @@
 #   define NEXT_DEV	(&sdla0_dev)
 #endif
 
-#ifdef CONFIG_NETROM
-	extern int nr_init(struct device *);
-	
-	static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, };
-	static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, };
-	static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, };
-	static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, };
-
-#   undef NEXT_DEV
-#   define	NEXT_DEV	(&nr0_dev)
-#endif
-
 /* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
 #ifdef CONFIG_ATP		/* AT-LAN-TEC (RealTek) pocket adaptor. */
 static struct device atp_dev = {
@@ -356,6 +341,16 @@
 #undef NEXT_DEV
 #define NEXT_DEV (&slip_bootstrap)
 #endif	/* SLIP */
+  
+#if defined(CONFIG_MKISS)
+	/* To be exact, this node just hooks the initialization
+	   routines to the device structures.			*/
+extern int mkiss_init_ctrl_dev(struct device *);
+static struct device mkiss_bootstrap = {
+  "mkiss_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, mkiss_init_ctrl_dev, };
+#undef NEXT_DEV
+#define NEXT_DEV (&mkiss_bootstrap)
+#endif	/* MKISS */
   
 #if defined(CONFIG_STRIP)
 extern int strip_init_ctrl_dev(struct device *);
diff -u --recursive --new-file v2.0.34/linux/drivers/net/baycom.c linux/drivers/net/baycom.c
--- v2.0.34/linux/drivers/net/baycom.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/baycom.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,1068 @@
+/*****************************************************************************/
+
+/*
+ *	baycom.c  -- baycom ser12 and par96 radio modem driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+ *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ *          is responsible for regenerating the receiver bit clock, as well as
+ *          for handling the HDLC protocol. The modem connects to a serial port,
+ *          hence the name. Since the serial port is not used as an async serial
+ *          port, the kernel driver for serial ports cannot be used, and this
+ *          driver only supports standard serial hardware (8250, 16450, 16550)
+ *
+ *  par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+ *          The modem does all the filtering and regenerates the receiver clock.
+ *          Data is transferred from and to the PC via a shift register.
+ *          The shift register is filled with 16 bits and an interrupt is
+ *          signalled. The PC then empties the shift register in a burst. This
+ *          modem connects to the parallel port, hence the name. The modem
+ *          leaves the implementation of the HDLC protocol and the scrambler
+ *          polynomial to the PC. This modem is no longer available (at least
+ *          from Baycom) and has been replaced by the PICPAR modem (see below).
+ *          You may however still build one from the schematics published in
+ *          cq-DL :-).
+ *
+ *  picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
+ *          modem is protocol compatible to par96, but uses only three low
+ *          power ICs and can therefore be fed from the parallel port and
+ *          does not require an additional power supply. It features
+ *          built in DCD circuitry. The driver should therefore be configured
+ *          for hardware DCD.
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     driver mode string. Valid choices are ser12 and par96. An
+ *           optional * enables software DCD.
+ *           2=par96/par97, any other value invalid
+ *  iobase   base address of the port; common values are for ser12 0x3f8,
+ *           0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc
+ *  irq      interrupt line of the port; common values are for ser12 3,4
+ *           and for par96/par97 7
+ *
+ *
+ *  History:
+ *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.3  26.04.96  init code/data tagged
+ */
+
+/*****************************************************************************/
+
+#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/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD  1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom";
+static const char bc_drvinfo[] = KERN_INFO "baycom: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom: version 0.3 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+	char *mode;
+	int iobase, irq;
+} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+#define LPT_DATA(iobase)    (iobase+0)
+#define LPT_STATUS(iobase)  (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE      0x10
+#define PAR96_BURSTBITS 16
+#define PAR96_BURST     4
+#define PAR96_PTT       2
+#define PAR96_TXBIT     1
+#define PAR96_ACK       0x40
+#define PAR96_RXBIT     0x20
+#define PAR96_DCD       0x10
+#define PAR97_POWER     0xf8
+
+#define PAR96_EXTENT 3
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	unsigned int options;
+
+	struct modem_state {
+		short arb_divider;
+		unsigned char flags;
+		unsigned int shreg;
+		struct modem_state_ser12 {
+			unsigned char last_sample;
+			unsigned char interm_sample;
+			unsigned int bit_pll;
+			unsigned int dcd_shreg;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
+			unsigned int dcd_time;
+			unsigned char last_rxbit;
+			unsigned char tx_bit;
+		} ser12;
+		struct modem_state_par96 {
+			int dcd_count;
+			unsigned int dcd_shreg;
+			unsigned long descram;
+			unsigned long scram;
+		} par96;
+	} modem;
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+static void inline ser12_set_divisor(struct device *dev,
+				     unsigned char divisor)
+{
+	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
+	outb(divisor, DLL(dev->base_addr));
+	outb(0, DLM(dev->base_addr));
+	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
+	/*
+	 * make sure the next interrupt is generated;
+	 * 0 must be used to power the modem; the modem draws its
+	 * power from the TxD line
+	 */
+	outb(0x00, THR(dev->base_addr));
+	/*
+	 * it is important not to set the divider while transmitting;
+	 * this reportedly makes some UARTs generating interrupts
+	 * in the hundredthousands per second region
+	 * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+	 */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * must call the TX arbitrator every 10ms
+ */
+#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+			       36 : 24)
+#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+				240 : 12)
+
+static inline void ser12_tx(struct device *dev, struct baycom_state *bc)
+{
+	/* one interrupt per channel bit */
+	ser12_set_divisor(dev, 12);
+	/*
+	 * first output the last bit (!) then call HDLC transmitter,
+	 * since this may take quite long
+	 */
+	outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+	if (bc->modem.shreg <= 1)
+		bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+	bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
+				   (bc->modem.shreg & 1));
+	bc->modem.shreg >>= 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void ser12_rx(struct device *dev, struct baycom_state *bc)
+{
+	unsigned char cur_s;
+	/*
+	 * do demodulator
+	 */
+	cur_s = inb(MSR(dev->base_addr)) & 0x10;	/* the CTS line */
+	hdlcdrv_channelbit(&bc->hdrv, cur_s);
+	bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
+		(cur_s != bc->modem.ser12.last_sample);
+	bc->modem.ser12.last_sample = cur_s;
+	if(bc->modem.ser12.dcd_shreg & 1) {
+		if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				dcdspos += 2;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		} else
+			bc->modem.ser12.dcd_sum0--;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+					   bc->modem.ser12.dcd_sum1 +
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+		/*
+		 * PLL code for the improved software DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 4);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 7) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 4:	/* transition too early */
+				ser12_set_divisor(dev, 3);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 4);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample ==
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit =
+				bc->modem.ser12.last_sample;
+		}
+		if (++bc->modem.ser12.interm_sample >= 3)
+			bc->modem.ser12.interm_sample = 0;
+		/*
+		 * DCD stuff
+		 */
+		if (bc->modem.ser12.dcd_shreg & 1) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				<< 1;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		}
+	} else {
+		/*
+		 * PLL algorithm for the hardware squelch DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 6);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 3) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 7);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 2:	/* transition too early */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 6);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample ==
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit =
+				bc->modem.ser12.last_sample;
+		}
+		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
+		/*
+		 * DCD stuff
+		 */
+		bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
+	}
+	outb(0x0d, MCR(dev->base_addr));		/* transmitter off */
+	if (bc->modem.shreg & 1) {
+		hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+		bc->modem.shreg = 0x10000;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+					   bc->modem.ser12.dcd_sum1 +
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+
+	baycom_int_freq(bc);
+	/*
+	 * check if transmitter active
+	 */
+	if (hdlcdrv_ptt(&bc->hdrv))
+		ser12_tx(dev, bc);
+	else {
+		ser12_rx(dev, bc);
+		if (--bc->modem.arb_divider <= 0) {
+			bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
+			sti();
+			hdlcdrv_arbitrate(dev, &bc->hdrv);
+		}
+	}
+	sti();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+	{ "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart ser12_check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	b1 = inb(MCR(iobase));
+	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
+	b2 = inb(MSR(iobase));
+	outb(0x1a, MCR(iobase));
+	b3 = inb(MSR(iobase)) & 0xf0;
+	outb(b1, MCR(iobase));			/* restore old values */
+	outb(b2, MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(RBR(iobase));
+	inb(RBR(iobase));
+	outb(0x01, FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, SCR(iobase));
+		b1 = inb(SCR(iobase));
+		outb(0xa5, SCR(iobase));
+		b2 = inb(SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	enum uart u;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SER12_EXTENT))
+		return -EACCES;
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = 1200;
+	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
+		return -EIO;
+	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0, IER(dev->base_addr));
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+			"baycom_ser12", dev))
+		return -EBUSY;
+	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
+	/*
+	 * enable transmitter empty interrupt
+	 */
+	outb(2, IER(dev->base_addr));
+	/*
+	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
+	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
+	 * depending on the usage of the software DCD routine
+	 */
+	ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
+	printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options "
+	       "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
+	       bc->options, uart_str[u]);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	outb(0, IER(dev->base_addr));
+	outb(1, MCR(dev->base_addr));
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, SER12_EXTENT);
+	printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== PAR96 specific routines =========================
+ */
+
+#define PAR96_DESCRAM_TAP1 0x20000
+#define PAR96_DESCRAM_TAP2 0x01000
+#define PAR96_DESCRAM_TAP3 0x00001
+
+#define PAR96_DESCRAM_TAPSH1 17
+#define PAR96_DESCRAM_TAPSH2 12
+#define PAR96_DESCRAM_TAPSH3 0
+
+#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
+#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static inline void par96_tx(struct device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data = hdlcdrv_getbits(&bc->hdrv);
+
+	for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
+		unsigned char val = PAR97_POWER;
+		bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
+					 (bc->modem.par96.scram & 1));
+		if (!(data & 1))
+			bc->modem.par96.scram ^= 1;
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
+			bc->modem.par96.scram ^=
+				(PAR96_SCRAM_TAPN << 1);
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
+			val |= PAR96_TXBIT;
+		outb(val, LPT_DATA(dev->base_addr));
+		outb(val | PAR96_BURST, LPT_DATA(dev->base_addr));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void par96_rx(struct device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data, mask, mask2, descx;
+
+	/*
+	 * do receiver; differential decode and descramble on the fly
+	 */
+	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
+		if (inb(LPT_STATUS(dev->base_addr)) & PAR96_RXBIT)
+			bc->modem.par96.descram |= 1;
+		descx = bc->modem.par96.descram ^
+			(bc->modem.par96.descram >> 1);
+		/* now the diff decoded data is inverted in descram */
+		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev->base_addr));
+		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
+			  (descx >> PAR96_DESCRAM_TAPSH2));
+		data >>= 1;
+		if (!(descx & 1))
+			data |= 0x8000;
+		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST,
+		     LPT_DATA(dev->base_addr));
+	}
+	hdlcdrv_putbits(&bc->hdrv, data);
+	/*
+	 * do DCD algorithm
+	 */
+	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
+			| (data << 16);
+		/* search for flags and set the dcd counter appropriately */
+		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if ((bc->modem.par96.dcd_shreg & mask) == mask2)
+				bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
+		/* check for abort/noise sequences */
+		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
+			    (bc->modem.par96.dcd_count >= 0))
+				bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
+		/* decrement and set the dcd variable */
+		if (bc->modem.par96.dcd_count >= 0)
+			bc->modem.par96.dcd_count -= 2;
+		hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
+	} else {
+		hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev->base_addr))
+					     & PAR96_DCD));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+
+	baycom_int_freq(bc);
+	/*
+	 * check if transmitter active
+	 */
+	if (hdlcdrv_ptt(&bc->hdrv))
+		par96_tx(dev, bc);
+	else {
+		par96_rx(dev, bc);
+		if (--bc->modem.arb_divider <= 0) {
+			bc->modem.arb_divider = 6;
+			sti();
+			hdlcdrv_arbitrate(dev, &bc->hdrv);
+		}
+	}
+	sti();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_check_lpt(unsigned int iobase)
+{
+	unsigned char b1,b2;
+	int i;
+
+	b1 = inb(LPT_DATA(iobase));
+	b2 = inb(LPT_CONTROL(iobase));
+	outb(0xaa, LPT_DATA(iobase));
+	i = inb(LPT_DATA(iobase)) == 0xaa;
+	outb(0x55, LPT_DATA(iobase));
+	i &= inb(LPT_DATA(iobase)) == 0x55;
+	outb(0x0a, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+	outb(0x05, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+	outb(b1, LPT_DATA(iobase));
+	outb(b2, LPT_CONTROL(iobase));
+	return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_open(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-PAR96_EXTENT ||
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (check_region(dev->base_addr, PAR96_EXTENT))
+		return -EACCES;
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = 9600;
+	if (par96_check_lpt(dev->base_addr))
+		return -EIO;
+	/* disable interrupt */
+	outb(0, LPT_CONTROL(dev->base_addr));
+	 /* switch off PTT */
+	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr));
+	printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n",
+	       bc_drvname, dev->base_addr, dev->irq, bc->options);
+	if (request_irq(dev->irq, par96_interrupt, SA_INTERRUPT,
+			"baycom_par96", dev))
+		return -EBUSY;
+	request_region(dev->base_addr, PAR96_EXTENT, "baycom_par96");
+	/* enable interrupt */
+	outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev->base_addr));
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_close(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/* disable interrupt */
+	outb(0, LPT_CONTROL(dev->base_addr));
+	/* switch off PTT */
+	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev->base_addr));
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, PAR96_EXTENT);
+	printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+	bc_drvname,
+	bc_drvinfo,
+	ser12_open,
+	ser12_close,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops par96_ops = {
+	bc_drvname,
+	bc_drvinfo,
+	par96_open,
+	par96_close,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops dummy_ops = {
+	bc_drvname,
+	bc_drvinfo,
+	NULL,
+	NULL,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, char *modestr)
+{
+	struct hdlcdrv_ops *newops = NULL;
+	unsigned long flags;
+
+	if (!strncmp(modestr, "off", 3))
+		newops = &dummy_ops;
+	else if (!strncmp(modestr, "ser12", 5))
+		newops = &ser12_ops;
+	else if (!strncmp(modestr, "par96", 5))
+		newops = &par96_ops;
+	else
+		return -EINVAL;
+	save_flags(flags);
+	cli();
+	bc->hdrv.ops = newops;
+	bc->options = !!strchr(modestr, '*');
+	restore_flags(flags);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+	int cmd2;
+
+	if (!dev || !dev->priv ||
+	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	bc = (struct baycom_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	if (get_user(cmd2, (int *)ifr->ifr_data))
+		return -EFAULT;
+	switch (hi->cmd) {
+	default:
+		break;
+
+	case HDLCDRVCTL_GETMODE:
+		if (bc->hdrv.ops == &ser12_ops)
+			strcpy(hi->data.modename, "ser12");
+		else if (bc->hdrv.ops == &par96_ops)
+			strcpy(hi->data.modename, "par96");
+		else if (bc->hdrv.ops == &dummy_ops)
+			strcpy(hi->data.modename, "off");
+		else
+			strcpy(hi->data.modename, "invalid");
+		if (bc->options & 1)
+			strcat(hi->data.modename, "*");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strcpy(hi->data.modename, "ser12,par96");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
+
+	}
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_init(void))
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct baycom_state *bc;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		sprintf(ifname, "bc%d", i);
+
+		if (!baycom_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			baycom_ports[i].iobase = baycom_ports[i].irq = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &dummy_ops,
+					     sizeof(struct baycom_state),
+					     ifname, baycom_ports[i].iobase,
+					     baycom_ports[i].irq, 0);
+		if (!j) {
+			bc = (struct baycom_state *)dev->priv;
+			if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+				set_hw = 0;
+			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       bc_drvname);
+		}
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode = NULL;
+static int iobase = 0x3f8;
+static int irq = 4;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; eg. ser12* or par96");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "baycom irq number");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12, par96 and picpar amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+	baycom_ports[0].mode = mode;
+	baycom_ports[0].iobase = iobase;
+	baycom_ports[0].irq = irq;
+	baycom_ports[1].mode = NULL;
+
+	return baycom_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+		if (bc) {
+			if (bc->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "baycom: invalid magic in "
+				       "cleanup_module\n");
+			else
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom=io,irq,mode
+ * mode: {ser12,par96}[*]
+ * * indicates sofware DCD
+ */
+
+__initfunc(void baycom_setup(char *str, int *ints))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 2)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", bc_drvname);
+		return;
+	}
+	baycom_ports[i].mode = str;
+	baycom_ports[i].iobase = ints[1];
+	baycom_ports[i].irq = ints[2];
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/bpqether.c linux/drivers/net/bpqether.c
--- v2.0.34/linux/drivers/net/bpqether.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/bpqether.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,685 @@
+/*
+ *	G8BPQ compatible "AX.25 via ethernet" driver release 003
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly
+ *	fail to work with new releases, misbehave and/or generally screw up.
+ *	It might even work.
+ *
+ *	This code REQUIRES 2.0.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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 is a "pseudo" network driver to allow AX.25 over Ethernet
+ *	using G8BPQ encapsulation. It has been extracted from the protocol
+ *	implementation because
+ *
+ *		- things got unreadable within the protocol stack
+ *		- to cure the protocol stack from "feature-ism"
+ *		- a protocol implementation shouldn't need to know on
+ *		  which hardware it is running
+ *		- user-level programs like the AX.25 utilities shouldn't
+ *		  need to know about the hardware.
+ *		- IP over ethernet encapsulated AX.25 was impossible
+ *		- rxecho.c did not work
+ *		- to have room for extensions
+ *		- it just deserves to "live" as an own driver
+ *
+ *	This driver can use any ethernet destination address, and can be
+ *	limited to accept frames from one dedicated ethernet card only.
+ *
+ *	Note that the driver sets up the BPQ devices automagically on
+ *	startup or (if started before the "insmod" of an ethernet device)
+ *	on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing
+ *	the ethernet device (in fact: as soon as another ethernet or bpq
+ *	device gets "ifconfig"ured).
+ *
+ *	I have heard that several people are thinking of experiments
+ *	with highspeed packet radio using existing ethernet cards.
+ *	Well, this driver is prepared for this purpose, just add
+ *	your tx key control and a txdelay / tailtime algorithm,
+ *	probably some buffering, and /voila/...
+ *
+ *	History
+ *	BPQ   001	Joerg(DL1BKE)		Extracted BPQ code from AX.25
+ *						protocol stack and added my own
+ *						yet existing patches
+ *	BPQ   002	Joerg(DL1BKE)		Scan network device list on
+ *						startup.
+ *	BPQ   003	Joerg(DL1BKE)		Ethernet destination address
+ *						and accepted source address
+ *						can be configured by an ioctl()
+ *						call.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/module.h>
+#include <linux/net_alias.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <linux/bpqether.h>
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+	{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_defaddr[AX25_ADDR_LEN] =
+	{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+
+static char bpq_eth_addr[6];
+
+static int bpq_rcv(struct sk_buff *, struct device *, struct packet_type *);
+static int bpq_device_event(struct notifier_block *, unsigned long, void *);
+static char *bpq_print_ethaddr(unsigned char *);
+
+static struct packet_type bpq_packet_type = {
+	0,		/* ntohs(ETH_P_BPQ),*/
+	0,		/* copy */
+	bpq_rcv,
+	NULL,
+	NULL,
+};
+
+static struct notifier_block bpq_dev_notifier = {
+	bpq_device_event,
+	0
+};
+
+
+#define MAXBPQDEV 100
+
+static struct bpqdev {
+	struct bpqdev *next;
+	char   ethname[14];		/* ether device name */
+	struct device *ethdev;		/* link to ethernet device */
+	struct device axdev;		/* bpq device (bpq#) */
+	struct enet_statistics stats;	/* some statistics */
+	char   dest_addr[6];		/* ether destination address */
+	char   acpt_addr[6];		/* accept ether frames from this address only */
+} *bpq_devices = NULL;
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Get the ethernet device for a BPQ device
+ */
+static __inline__ struct device *bpq_get_ether_dev(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	return (bpq != NULL) ? bpq->ethdev : NULL;
+}
+
+/*
+ *	Get the BPQ device for the ethernet device
+ */
+static __inline__ struct device *bpq_get_ax25_dev(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+		if (bpq->ethdev == dev)
+			return &bpq->axdev;
+
+	return NULL;
+}
+
+static __inline__ int dev_is_ethdev(struct device *dev)
+{
+	return (
+			dev->type == ARPHRD_ETHER
+			&& strncmp(dev->name, "dummy", 5)
+#ifdef CONFIG_NET_ALIAS
+			&& !net_alias_is(dev)
+#endif
+	);
+}
+
+/*
+ *	Sanity check: remove all devices that ceased to exists and
+ *	return '1' if the given BPQ device was affected.
+ */
+static int bpq_check_devices(struct device *dev)
+{
+	struct bpqdev *bpq, *bpq_prev;
+	int result = 0;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	bpq_prev = NULL;
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) {
+		if (!dev_get(bpq->ethname)) {
+			if (bpq_prev)
+				bpq_prev->next = bpq->next;
+			else
+				bpq_devices = bpq->next;
+
+			if (&bpq->axdev == dev)
+				result = 1;
+
+			unregister_netdev(&bpq->axdev);
+			kfree(bpq);
+		}
+
+		bpq_prev = bpq;
+	}
+
+	restore_flags(flags);
+
+	return result;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Receive an AX.25 frame via an ethernet interface.
+ */
+static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+	int len;
+	char * ptr;
+	struct ethhdr *eth = (struct ethhdr *)skb->mac.raw;
+	struct bpqdev *bpq;
+
+	skb->sk = NULL;		/* Initially we don't know who it's for */
+
+	dev = bpq_get_ax25_dev(dev);
+
+	if (dev == NULL || dev->start == 0) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	/*
+	 * if we want to accept frames from just one ethernet device
+	 * we check the source address of the sender.
+	 */
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	if (!(bpq->acpt_addr[0] & 0x01) && memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN)) {
+		printk(KERN_DEBUG "bpqether: wrong dest %s\n", bpq_print_ethaddr(eth->h_source));
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	((struct bpqdev *)dev->priv)->stats.rx_packets++;
+
+	len = skb->data[0] + skb->data[1] * 256 - 5;
+
+	skb_pull(skb, 2);	/* Remove the length bytes */
+	skb_trim(skb, len);	/* Set the length of the data */
+
+	ptr = skb_push(skb, 1);
+	*ptr = 0;
+
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+
+	return 0;
+}
+
+/*
+ * 	Send an AX.25 frame via an ethernet interface
+ */
+static int bpq_xmit(struct sk_buff *skb, struct device *dev)
+{
+	unsigned char *ptr;
+	struct bpqdev *bpq;
+	int size;
+
+	/*
+	 * Just to be *really* sure not to send anything if the interface
+	 * is down, the ethernet device may have gone.
+	 */
+	if (!dev->start) {
+		bpq_check_devices(dev);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -ENODEV;
+	}
+
+	skb_pull(skb, 1);
+	size = skb->len;
+
+	/*
+	 * The AX.25 code leaves enough room for the ethernet header, but
+	 * sendto() does not.
+	 */
+	if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {	/* Ough! */
+		struct sk_buff *newskb = alloc_skb(skb->len + AX25_BPQ_HEADER_LEN, GFP_ATOMIC);
+
+		if (newskb == NULL) {			/* Argh! */
+			printk(KERN_WARNING "bpq_xmit: not enough space to add BPQ Ether header\n");
+			dev_kfree_skb(skb, FREE_WRITE);
+			return -ENOMEM;
+		}
+
+		newskb->free = 1;
+		newskb->arp  = 1;
+		if (skb->sk != NULL)
+			atomic_add(newskb->truesize, &skb->sk->wmem_alloc);
+		newskb->sk   = skb->sk;
+
+		skb_reserve(newskb, AX25_BPQ_HEADER_LEN);
+		memcpy(skb_put(newskb, size), skb->data, size);
+		dev_kfree_skb(skb, FREE_WRITE);
+		skb = newskb;
+	}
+
+	skb->protocol = htons(ETH_P_AX25);
+
+	ptr = skb_push(skb, 2);
+
+	*ptr++ = (size + 5) % 256;
+	*ptr++ = (size + 5) / 256;
+
+	bpq = (struct bpqdev *)dev->priv;
+	bpq->stats.tx_packets++;
+
+	if ((dev = bpq_get_ether_dev(dev)) == NULL) {
+		bpq->stats.tx_dropped++;
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -ENODEV;
+	}
+
+	skb->dev = dev;
+	dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
+
+	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+
+	return 0;
+}
+
+/*
+ *	Statistics
+ */
+static struct enet_statistics *bpq_get_stats(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	return &bpq->stats;
+}
+
+/*
+ *	Set AX.25 callsign
+ */
+static int bpq_set_mac_address(struct device *dev, void *addr)
+{
+    struct sockaddr *sa = (struct sockaddr *)addr;
+
+    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+    return 0;
+}
+
+/*	Ioctl commands
+ *
+ *		SIOCSBPQETHOPT		reserved for enhancements
+ *		SIOCSBPQETHADDR		set the destination and accepted
+ *					source ethernet address (broadcast
+ *					or multicast: accept all)
+ */
+static int bpq_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	int err;
+	struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data;
+	struct bpqdev *bpq = dev->priv;
+	struct bpq_req req;
+
+	if (!suser())
+		return -EPERM;
+
+	if (bpq == NULL)		/* woops! */
+		return -ENODEV;
+
+	switch (cmd) {
+		case SIOCSBPQETHOPT:
+			if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0)
+				return err;
+			memcpy_fromfs(&req, ifr->ifr_data, sizeof(struct bpq_req));
+			switch (req.cmd) {
+				case SIOCGBPQETHPARAM:
+				case SIOCSBPQETHPARAM:
+				default:
+					return -EINVAL;
+			}
+
+			break;
+
+		case SIOCSBPQETHADDR:
+			if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0)
+				return err;
+			memcpy_fromfs(bpq->dest_addr, ethaddr->destination, ETH_ALEN);
+			memcpy_fromfs(bpq->acpt_addr, ethaddr->accept, ETH_ALEN);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * open/close a device
+ */
+static int bpq_open(struct device *dev)
+{
+	if (bpq_check_devices(dev))
+		return -ENODEV;		/* oops, it's gone */
+
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int bpq_close(struct device *dev)
+{
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ * currently unused
+ */
+static int bpq_dev_init(struct device *dev)
+{
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Proc filesystem
+ */
+static char * bpq_print_ethaddr(unsigned char *e)
+{
+	static char buf[18];
+
+	sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+		e[0], e[1], e[2], e[3], e[4], e[5]);
+
+	return buf;
+}
+
+int bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct bpqdev *bpqdev;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "dev   ether      destination        accept from\n");
+
+	for (bpqdev = bpq_devices; bpqdev != NULL; bpqdev = bpqdev->next) {
+		len += sprintf(buffer + len, "%-5s %-10s %s  ",
+			bpqdev->axdev.name, bpqdev->ethname,
+			bpq_print_ethaddr(bpqdev->dest_addr));
+
+		len += sprintf(buffer + len, "%s\n",
+			(bpqdev->acpt_addr[0] & 0x01) ? "*" : bpq_print_ethaddr(bpqdev->acpt_addr));
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Setup a new device.
+ */
+static int bpq_new_device(struct device *dev)
+{
+	int k;
+	unsigned char *buf;
+	struct bpqdev *bpq, *bpq2;
+
+	if ((bpq = (struct bpqdev *)kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	memset(bpq, 0, sizeof(struct bpqdev));
+
+	bpq->ethdev = dev;
+
+	bpq->ethname[sizeof(bpq->ethname)-1] = '\0';
+	strncpy(bpq->ethname, dev->name, sizeof(bpq->ethname)-1);
+
+	memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr));
+	memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
+
+	dev = &bpq->axdev;
+	buf = (unsigned char *)kmalloc(14, GFP_KERNEL);
+
+	for (k = 0; k < MAXBPQDEV; k++) {
+		struct device *odev;
+
+		sprintf(buf, "bpq%d", k);
+
+		if ((odev = dev_get(buf)) == NULL || bpq_check_devices(odev))
+			break;
+	}
+
+	if (k == MAXBPQDEV) {
+		kfree(bpq);
+		return -ENODEV;
+	}
+
+	dev->priv = (void *)bpq;	/* pointer back */
+	dev->name = buf;
+	dev->init = bpq_dev_init;
+
+	if (register_netdev(dev) != 0) {
+		kfree(bpq);
+                return -EIO;
+        }
+
+	for (k=0; k < DEV_NUMBUFFS; k++)
+		skb_queue_head_init(&dev->buffs[k]);
+
+	dev->hard_start_xmit = bpq_xmit;
+	dev->open	     = bpq_open;
+	dev->stop	     = bpq_close;
+	dev->set_mac_address = bpq_set_mac_address;
+	dev->get_stats	     = bpq_get_stats;
+	dev->do_ioctl	     = bpq_ioctl;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_defaddr, AX25_ADDR_LEN);
+
+	/* preset with reasonable values */
+
+	dev->flags      = 0;
+	dev->family     = AF_INET;
+
+#ifdef CONFIG_INET
+	dev->pa_addr    = in_aton("192.168.0.1");
+	dev->pa_brdaddr = in_aton("192.168.0.255");
+	dev->pa_mask    = in_aton("255.255.255.0");
+	dev->pa_alen    = 4;
+#endif
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header     = ax25_encapsulate;
+	dev->rebuild_header  = ax25_rebuild_header;
+#endif
+
+	dev->type            = ARPHRD_AX25;
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu             = AX25_DEF_PACLEN;
+	dev->addr_len        = AX25_ADDR_LEN;
+
+	cli();
+
+	if (bpq_devices == NULL) {
+		bpq_devices = bpq;
+	} else {
+		for (bpq2 = bpq_devices; bpq2->next != NULL; bpq2 = bpq2->next);
+		bpq2->next = bpq;
+	}
+
+	sti();
+
+	return 0;
+}
+
+
+/*
+ *	Handle device status changes.
+ */
+static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+
+	if (!dev_is_ethdev(dev))
+		return NOTIFY_DONE;
+
+	bpq_check_devices(NULL);
+
+	switch (event) {
+		case NETDEV_UP:		/* new ethernet device -> new BPQ interface */
+			if (bpq_get_ax25_dev(dev) == NULL)
+				bpq_new_device(dev);
+			break;
+
+		case NETDEV_DOWN:	/* ethernet device closed -> close BPQ interface */
+			if ((dev = bpq_get_ax25_dev(dev)) != NULL)
+				dev_close(dev);
+			break;
+
+		default:
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Initialize driver. To be called from af_ax25 if not compiled as a
+ * module
+ */
+int bpq_init(void)
+{
+	struct device *dev;
+
+	bpq_packet_type.type  = htons(ETH_P_BPQ);
+	dev_add_pack(&bpq_packet_type);
+
+	register_netdevice_notifier(&bpq_dev_notifier);
+
+	printk(KERN_INFO "AX.25 ethernet driver version 0.01\n");
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_AX25_BPQETHER, 8, "bpqether",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		bpq_get_info
+	});
+#endif
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (dev_is_ethdev(dev))
+			bpq_new_device(dev);
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	register_symtab(NULL);
+
+	return bpq_init();
+}
+
+void cleanup_module(void)
+{
+	struct bpqdev *bpq;
+
+	dev_remove_pack(&bpq_packet_type);
+
+	unregister_netdevice_notifier(&bpq_dev_notifier);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_AX25_BPQETHER);
+#endif
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+		unregister_netdev(&bpq->axdev);
+}
+#endif
diff -u --recursive --new-file v2.0.34/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v2.0.34/linux/drivers/net/dgrs.c	Tue Aug 12 16:05:23 1997
+++ linux/drivers/net/dgrs.c	Mon Jul 13 13:47:31 1998
@@ -405,7 +405,7 @@
 		 */
 		udelay(1);
 
-		csr = (volatile) priv->vplxdma[PLX_DMA_CSR/4];
+		csr = (volatile int) priv->vplxdma[PLX_DMA_CSR/4];
 
                 if (csr & PLX_DMA_CSR_0_DONE)
                         break;
@@ -876,7 +876,7 @@
 		/* Wait for old command to finish */
 		for (i = 0; i < 1000; ++i)
 		{
-			if ( (volatile) privN->bcomm->bc_filter_cmd <= 0 )
+			if ( (volatile int) privN->bcomm->bc_filter_cmd <= 0 )
 				break;
 			udelay(1);
 		}
diff -u --recursive --new-file v2.0.34/linux/drivers/net/e2100.c linux/drivers/net/e2100.c
--- v2.0.34/linux/drivers/net/e2100.c	Thu Feb 29 21:50:43 1996
+++ linux/drivers/net/e2100.c	Mon Jul 13 13:47:31 1998
@@ -101,7 +101,7 @@
 static void e21_block_input(struct device *dev, int count,
 						   struct sk_buff *skb, int ring_offset);
 static void e21_block_output(struct device *dev, int count,
-							 const unsigned char *buf, const start_page);
+							 const unsigned char *buf, const int start_page);
 static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 							int ring_page);
 
@@ -330,7 +330,7 @@
 
 static void
 e21_block_output(struct device *dev, int count, const unsigned char *buf,
-				 int start_page)
+				 const int start_page)
 {
 	short ioaddr = dev->base_addr;
 	volatile char *shared_mem = (char *)dev->mem_start;
diff -u --recursive --new-file v2.0.34/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.0.34/linux/drivers/net/eepro100.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/eepro100.c	Mon Jul 13 13:47:31 1998
@@ -1,4 +1,4 @@
-/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
+/* drivers/net/eepro100.c: An Intel i82557 Ethernet driver for Linux. */
 /*
    NOTICE: this version tested with kernels 1.3.72 and later only!
 	Written 1996-1998 by Donald Becker.
@@ -109,7 +109,7 @@
 I. Board Compatibility
 
 This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
-single-chip fast ethernet controller for PCI, as used on the Intel
+single-chip fast Ethernet controller for PCI, as used on the Intel
 EtherExpress Pro 100 adapter.
 
 II. Board-specific settings
@@ -163,7 +163,7 @@
 added asynchronous to the normal transmit queue, so we disable interrupts
 whenever the Tx descriptor ring is manipulated.
 
-A notable aspect of the these special configure commands is that they do
+A notable aspect of these special configure commands is that they do
 work with the normal Tx ring entry scavenge method.  The Tx ring scavenge
 is done at interrupt time using the 'dirty_tx' index, and checking for the
 command-complete bit.  While the setup frames may have the NoOp command on the
diff -u --recursive --new-file v2.0.34/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c
--- v2.0.34/linux/drivers/net/eth16i.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/eth16i.c	Mon Jul 13 13:47:31 1998
@@ -714,7 +714,7 @@
   creg[0] &= 0x0F;      /* Mask collision cnr */
   creg[2] &= 0x7F;      /* Mask DCLEN bit */
 
-#ifdef 0
+#if 0
 /* 
 	This was removed because the card was sometimes left to state
   	from which it couldn't be find anymore. If there is need
diff -u --recursive --new-file v2.0.34/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- v2.0.34/linux/drivers/net/ewrk3.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/ewrk3.c	Mon Jul 13 13:47:31 1998
@@ -1141,7 +1141,7 @@
 
   /*
   ** Clean out the TX and RX queues here (note that one entry
-  ** may get added to either the TXD or RX queues if the the TX or RX
+  ** may get added to either the TXD or RX queues if the TX or RX
   ** just starts processing a packet before the STOP_EWRK3 command
   ** is received. This will be flushed in the ewrk3_open() call).
   */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c
--- v2.0.34/linux/drivers/net/hdlcdrv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hdlcdrv.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,1037 @@
+/*****************************************************************************/
+
+/*
+ *	hdlcdrv.c  -- HDLC packet radio network driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *  The driver was derived from Donald Beckers skeleton.c
+ *	Written 1993-94 by Donald Becker.
+ *
+ *  History:
+ *   0.1  21.09.96  Started
+ *        18.10.96  Changed to new user space access routines 
+ *                  (copy_{to,from}_user)
+ *   0.2  21.11.96  various small changes
+ *   0.3  03.03.97  fixed (hopefully) IP not working with ax.25 as a module
+ *   0.4  16.04.97  init code/data tagged
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/hdlcdrv.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h> 
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+
+/* make genksyms happy */
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/net_alias.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x20115
+extern __inline__ void dev_init_buffers(struct device *dev)
+{
+        int i;
+        for(i=0;i<DEV_NUMBUFFS;i++)
+        {
+                skb_queue_head_init(&dev->buffs[i]);
+        }
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x20125
+#define test_and_set_bit set_bit
+#define test_and_clear_bit clear_bit
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+	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
+};
+
+/*---------------------------------------------------------------------------*/
+
+static inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ 	unsigned int crc = 0xffff;
+
+	for (;len>0;len--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
+	crc ^= 0xffff;
+	*buffer++ = crc;
+	*buffer++ = crc >> 8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	return (crc & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+static int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	crc ^= 0xffff;
+	return (crc & 0xffff);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The HDLC routines
+ */
+
+static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, 
+			     int num)
+{
+	int added = 0;
+	
+	while (s->hdlcrx.rx_state && num >= 8) {
+		if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
+			s->hdlcrx.rx_state = 0;
+			return 0;
+		}
+		*s->hdlcrx.bp++ = bits >> (32-num);
+		s->hdlcrx.len++;
+		num -= 8;
+		added += 8;
+	}
+	return added;
+}
+
+static void hdlc_rx_flag(struct device *dev, struct hdlcdrv_state *s)
+{
+	struct sk_buff *skb;
+	int pkt_len;
+	unsigned char *cp;
+
+	if (s->hdlcrx.len < 4) 
+		return;
+	if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) 
+		return;
+	pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
+	if (!(skb = dev_alloc_skb(pkt_len))) {
+		printk("%s: memory squeeze, dropping packet\n", 
+		       s->ifname);
+		s->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = dev;
+	cp = skb_put(skb, pkt_len);
+	*cp++ = 0; /* KISS kludge */
+	memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	s->stats.rx_packets++;
+}
+
+void hdlcdrv_receiver(struct device *dev, struct hdlcdrv_state *s)
+{
+	int i;
+	unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
+	
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx))
+		return;
+
+	while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
+		word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);	
+
+#ifdef HDLCDRV_DEBUG
+		hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
+#endif /* HDLCDRV_DEBUG */
+	       	s->hdlcrx.bitstream >>= 16;
+		s->hdlcrx.bitstream |= word << 16;
+		s->hdlcrx.bitbuf >>= 16;
+		s->hdlcrx.bitbuf |= word << 16;
+		s->hdlcrx.numbits += 16;
+		for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+		    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
+		    i >= 0; 
+		    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
+		    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+			if ((s->hdlcrx.bitstream & mask1) == mask1)
+				s->hdlcrx.rx_state = 0; /* abort received */
+			else if ((s->hdlcrx.bitstream & mask2) == mask3) {
+				/* flag received */
+				if (s->hdlcrx.rx_state) {
+					hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf 
+							  << (8+i),
+							  s->hdlcrx.numbits
+							  -8-i);
+					hdlc_rx_flag(dev, s);
+				}
+				s->hdlcrx.len = 0;
+				s->hdlcrx.bp = s->hdlcrx.buffer;
+				s->hdlcrx.rx_state = 1;
+				s->hdlcrx.numbits = i;
+			} else if ((s->hdlcrx.bitstream & mask4) == mask5) {
+				/* stuffed bit */
+				s->hdlcrx.numbits--;
+				s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
+					((s->hdlcrx.bitbuf & mask6) << 1);
+			}
+		}
+		s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
+						       s->hdlcrx.numbits);
+	}
+	clear_bit(0, &s->hdlcrx.in_hdlc_rx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void inline do_kiss_params(struct hdlcdrv_state *s,
+				  unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b)
+#else /* KISS_VERBOSE */	      
+#define PKP(a,b) 
+#endif /* KISS_VERBOSE */	      
+
+	if (len < 2)
+		return;
+	switch(data[0]) {
+	case PARAM_TXDELAY:
+		s->ch_params.tx_delay = data[1];
+		PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
+		break;
+	case PARAM_PERSIST:   
+		s->ch_params.ppersist = data[1];
+		PKP("p persistence = %u", s->ch_params.ppersist);
+		break;
+	case PARAM_SLOTTIME:  
+		s->ch_params.slottime = data[1];
+		PKP("slot time = %ums", s->ch_params.slottime);
+		break;
+	case PARAM_TXTAIL:    
+		s->ch_params.tx_tail = data[1];
+		PKP("TX tail = %ums", s->ch_params.tx_tail);
+		break;
+	case PARAM_FULLDUP:   
+		s->ch_params.fulldup = !!data[1];
+		PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
+		break;
+	default:
+		break;
+	}
+#undef PKP
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s)
+{
+	unsigned int mask1, mask2, mask3;
+	int i;
+	struct sk_buff *skb;
+	int pkt_len;
+
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx))
+		return;
+	for (;;) {
+		if (s->hdlctx.numbits >= 16) {
+			if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
+			s->hdlctx.bitbuf >>= 16;
+			s->hdlctx.numbits -= 16;
+		}
+		switch (s->hdlctx.tx_state) {
+		default:
+			clear_bit(0, &s->hdlctx.in_hdlc_tx);
+			return;
+		case 0:
+		case 1:
+			if (s->hdlctx.numflags) {
+				s->hdlctx.numflags--;
+				s->hdlctx.bitbuf |= 
+					0x7e7e << s->hdlctx.numbits;
+				s->hdlctx.numbits += 16;
+				break;
+			}
+			if (s->hdlctx.tx_state == 1) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			if (!(skb = skb_dequeue(&s->send_queue))) {
+				int flgs = tenms_to_2flags
+					(s, s->ch_params.tx_tail);
+				if (flgs < 2)
+					flgs = 2;
+				s->hdlctx.tx_state = 1;
+				s->hdlctx.numflags = flgs;
+				break;
+			}
+			if (skb->data[0] != 0) {
+				do_kiss_params(s, skb->data, skb->len);
+				dev_kfree_skb(skb, FREE_WRITE);
+				break;
+			}
+			pkt_len = skb->len-1; /* strip KISS byte */
+			if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				dev_kfree_skb(skb, FREE_WRITE);
+				break;
+			}
+			memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+			dev_kfree_skb(skb, FREE_WRITE);
+			s->hdlctx.bp = s->hdlctx.buffer;
+			append_crc_ccitt(s->hdlctx.buffer, pkt_len);
+			s->hdlctx.len = pkt_len+2; /* the appended CRC */
+			s->hdlctx.tx_state = 2;
+			s->hdlctx.bitstream = 0;
+			s->stats.tx_packets++;
+			break;
+		case 2:
+			if (!s->hdlctx.len) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				break;
+			}
+			s->hdlctx.len--;
+			s->hdlctx.bitbuf |= *s->hdlctx.bp <<
+				s->hdlctx.numbits;
+			s->hdlctx.bitstream >>= 8;
+			s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
+			mask1 = 0x1f000;
+			mask2 = 0x10000;
+			mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
+			s->hdlctx.numbits += 8;
+			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
+			    mask3 = (mask3 << 1) | 1) {
+				if ((s->hdlctx.bitstream & mask1) != mask1) 
+					continue;
+				s->hdlctx.bitstream &= ~mask2;
+				s->hdlctx.bitbuf = 
+					(s->hdlctx.bitbuf & mask3) |
+						((s->hdlctx.bitbuf & 
+						 (~mask3)) << 1);
+				s->hdlctx.numbits++;
+				mask3 = (mask3 << 1) | 1;
+			}
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void start_tx(struct device *dev, struct hdlcdrv_state *s)
+{
+	s->hdlctx.tx_state = 0;
+	s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
+	s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
+	hdlcdrv_transmitter(dev, s);
+	s->hdlctx.ptt = 1;
+	s->ptt_keyed++;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+	random_seed = 28629 * random_seed + 157;
+	return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_arbitrate(struct device *dev, struct hdlcdrv_state *s)
+{
+	if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || 
+	    skb_queue_empty(&s->send_queue)) 
+		return;
+	if (s->ch_params.fulldup) {
+		start_tx(dev, s);
+		return;
+	}
+	if (s->hdlcrx.dcd) {
+		s->hdlctx.slotcnt = s->ch_params.slottime;
+		return;
+	}
+	if ((--s->hdlctx.slotcnt) > 0)
+		return;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	if ((random_num() % 256) > s->ch_params.ppersist)
+		return;
+	start_tx(dev, s);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static inline int hdlcdrv_paranoia_check(struct device *dev,
+					const char *routine)
+{
+	if (!dev || !dev->priv || 
+	    ((struct hdlcdrv_state *)dev->priv)->magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "hdlcdrv: bad magic number for hdlcdrv_state "
+		       "struct in routine %s\n", routine);
+		return 1;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct hdlcdrv_state *sm;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet"))
+		return 0;
+	sm = (struct hdlcdrv_state *)dev->priv;
+	/*
+	 * If some higher layer thinks we've missed an tx-done interrupt
+         * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	 * itself. 
+	 */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+	skb_queue_tail(&sm->send_queue, skb);
+	dev->trans_start = jiffies;	
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *)addr;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); 
+	return 0;                                         
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20119
+static struct net_device_stats *hdlcdrv_get_stats(struct device *dev)
+#else
+static struct enet_statistics *hdlcdrv_get_stats(struct device *dev)
+#endif
+{
+	struct hdlcdrv_state *sm;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_get_stats"))
+		return NULL;
+	sm = (struct hdlcdrv_state *)dev->priv;
+	/* 
+	 * Get the current statistics.  This may be called with the
+	 * card open or closed. 
+	 */
+	return &sm->stats;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int hdlcdrv_open(struct device *dev)
+{
+	struct hdlcdrv_state *s;
+	int i;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_open"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (dev->start)
+		return 0;
+	if (!s->ops || !s->ops->open)
+		return -ENODEV;
+
+	dev->start = 1;
+	/*
+	 * initialise some variables
+	 */
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+	i = s->ops->open(dev);
+	if (i) {
+		dev->start = 0;
+		return i;
+	}
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* 
+ * The inverse routine to hdlcdrv_open(). 
+ */
+
+static int hdlcdrv_close(struct device *dev)
+{
+	struct hdlcdrv_state *s;
+	struct sk_buff *skb;
+	int i = 0;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_close"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (!dev->start)
+		return 0;
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (s->ops && s->ops->close)
+		i = s->ops->close(dev);
+        /* Free any buffers left in the hardware transmit queue */
+        while ((skb = skb_dequeue(&s->send_queue)))
+			dev_kfree_skb(skb, FREE_WRITE);
+	return i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct hdlcdrv_state *s;
+	struct hdlcdrv_ioctl bi;
+		
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_ioctl"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE) {
+		if (s->ops && s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, &bi, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		if (s->ops && s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, &bi, cmd);
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETCHANNELPAR:
+		bi.data.cp.tx_delay = s->ch_params.tx_delay;
+		bi.data.cp.tx_tail = s->ch_params.tx_tail;
+		bi.data.cp.slottime = s->ch_params.slottime;
+		bi.data.cp.ppersist = s->ch_params.ppersist;
+		bi.data.cp.fulldup = s->ch_params.fulldup;
+		break;
+
+	case HDLCDRVCTL_SETCHANNELPAR:
+		if (!suser())
+			return -EACCES;
+		s->ch_params.tx_delay = bi.data.cp.tx_delay;
+		s->ch_params.tx_tail = bi.data.cp.tx_tail;
+		s->ch_params.slottime = bi.data.cp.slottime;
+		s->ch_params.ppersist = bi.data.cp.ppersist;
+		s->ch_params.fulldup = bi.data.cp.fulldup;
+		s->hdlctx.slotcnt = 1;
+		return 0;
+		
+	case HDLCDRVCTL_GETMODEMPAR:
+		bi.data.mp.iobase = dev->base_addr;
+		bi.data.mp.irq = dev->irq;
+		bi.data.mp.dma = dev->dma;
+		bi.data.mp.dma2 = s->ptt_out.dma2;
+		bi.data.mp.seriobase = s->ptt_out.seriobase;
+		bi.data.mp.pariobase = s->ptt_out.pariobase;
+		bi.data.mp.midiiobase = s->ptt_out.midiiobase;
+		break;
+
+	case HDLCDRVCTL_SETMODEMPAR:
+		if ((!suser()) || dev->start)
+			return -EACCES;
+		dev->base_addr = bi.data.mp.iobase;
+		dev->irq = bi.data.mp.irq;
+		dev->dma = bi.data.mp.dma;
+		s->ptt_out.dma2 = bi.data.mp.dma2;
+		s->ptt_out.seriobase = bi.data.mp.seriobase;
+		s->ptt_out.pariobase = bi.data.mp.pariobase;
+		s->ptt_out.midiiobase = bi.data.mp.midiiobase;
+		return 0;	
+	
+	case HDLCDRVCTL_GETSTAT:
+		bi.data.cs.ptt = hdlcdrv_ptt(s);
+		bi.data.cs.dcd = s->hdlcrx.dcd;
+		bi.data.cs.ptt_keyed = s->ptt_keyed;
+		bi.data.cs.tx_packets = s->stats.tx_packets;
+		bi.data.cs.tx_errors = s->stats.tx_errors;
+		bi.data.cs.rx_packets = s->stats.rx_packets;
+		bi.data.cs.rx_errors = s->stats.rx_errors;
+		break;		
+
+	case HDLCDRVCTL_OLDGETSTAT:
+		bi.data.ocs.ptt = hdlcdrv_ptt(s);
+		bi.data.ocs.dcd = s->hdlcrx.dcd;
+		bi.data.ocs.ptt_keyed = s->ptt_keyed;
+#if LINUX_VERSION_CODE < 0x20100
+		bi.data.ocs.stats = s->stats;
+#endif
+		break;		
+
+	case HDLCDRVCTL_CALIBRATE:
+		s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
+		return 0;
+
+	case HDLCDRVCTL_GETSAMPLES:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
+		s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
+			sizeof(s->bitbuf_channel.buffer);
+		break;
+#endif /* HDLCDRV_DEBUG */
+				
+	case HDLCDRVCTL_GETBITS:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
+		s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
+			sizeof(s->bitbuf_hdlc.buffer);
+		break;		
+#endif /* HDLCDRV_DEBUG */
+
+	case HDLCDRVCTL_DRIVERNAME:
+		if (s->ops && s->ops->drvname) {
+			strncpy(bi.data.drivername, s->ops->drvname, 
+				sizeof(bi.data.drivername));
+			break;
+		}
+		bi.data.drivername[0] = '\0';
+		break;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+static int hdlcdrv_probe(struct device *dev)
+{
+	const struct hdlcdrv_channel_params dflt_ch_params = { 
+		20, 2, 10, 40, 0 
+	};
+	struct hdlcdrv_state *s;
+
+	if (!dev)
+		return -ENXIO;
+	/*
+	 * not a real probe! only initialize data structures
+	 */
+	s = (struct hdlcdrv_state *)dev->priv;
+	/*
+	 * initialize the hdlcdrv_state struct
+	 */
+	s->ch_params = dflt_ch_params;
+	s->ptt_keyed = 0;
+
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+#ifdef HDLCDRV_DEBUG
+	s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
+	s->bitbuf_channel.shreg = 0x80;
+
+	s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
+	s->bitbuf_hdlc.shreg = 0x80;
+#endif /* HDLCDRV_DEBUG */
+
+	/*
+	 * initialize the device struct
+	 */
+	dev->open = hdlcdrv_open;
+	dev->stop = hdlcdrv_close;
+	dev->do_ioctl = hdlcdrv_ioctl;
+	dev->hard_start_xmit = hdlcdrv_send_packet;
+	dev->get_stats = hdlcdrv_get_stats;
+
+	/* Fill in the fields of the device structure */
+
+	dev_init_buffers(dev);
+
+	skb_queue_head_init(&s->send_queue);
+	
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header = ax25_encapsulate;
+	dev->rebuild_header = ax25_rebuild_header;
+#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->hard_header = NULL;
+	dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->set_mac_address = hdlcdrv_set_mac_address;
+	
+	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
+	dev->hard_header_len = 73;         /* We do digipeaters now */
+	dev->mtu = 1500;                   /* eth_mtu is the default */
+	dev->addr_len = 7;                 /* sizeof an ax.25 address */
+	memcpy(dev->broadcast, ax25_bcast, 7);
+	memcpy(dev->dev_addr, ax25_test, 7);
+
+	/* New style flags */
+	dev->flags = 0;
+	dev->family = AF_INET;
+	dev->pa_addr = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask = 0;
+	dev->pa_alen = sizeof(unsigned long);
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+			     unsigned int privsize, char *ifname,
+			     unsigned int baseaddr, unsigned int irq, 
+			     unsigned int dma) 
+{
+	struct hdlcdrv_state *s;
+
+	if (!dev || !ops)
+		return -EACCES;
+	if (privsize < sizeof(struct hdlcdrv_state))
+		privsize = sizeof(struct hdlcdrv_state);
+	memset(dev, 0, sizeof(struct device));
+	if (!(s = dev->priv = kmalloc(privsize, GFP_KERNEL)))
+		return -ENOMEM;
+	/*
+	 * initialize part of the hdlcdrv_state struct
+	 */
+	memset(s, 0, privsize);
+	s->magic = HDLCDRV_MAGIC;
+	strncpy(s->ifname, ifname, sizeof(s->ifname));
+	s->ops = ops;
+	/*
+	 * initialize part of the device struct
+	 */
+	dev->name = s->ifname;
+	dev->if_port = 0;
+	dev->init = hdlcdrv_probe;
+	dev->start = 0;
+	dev->tbusy = 1;
+	dev->base_addr = baseaddr;
+	dev->irq = irq;
+	dev->dma = dma;
+	if (register_netdev(dev)) {
+		printk(KERN_WARNING "hdlcdrv: cannot register net "
+		       "device %s\n", s->ifname);
+		kfree(dev->priv);
+		return -ENXIO;
+	}
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_unregister_hdlcdrv(struct device *dev) 
+{
+	struct hdlcdrv_state *s;
+
+	if (!dev)
+		return -EINVAL;
+	if (!(s = (struct hdlcdrv_state *)dev->priv))
+		return -EINVAL;
+	if (s->magic != HDLCDRV_MAGIC)
+		return -EINVAL;
+	if (dev->start && s->ops->close)
+		s->ops->close(dev);
+	unregister_netdev(dev);
+	kfree(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+EXPORT_SYMBOL(hdlcdrv_receiver);
+EXPORT_SYMBOL(hdlcdrv_transmitter);
+EXPORT_SYMBOL(hdlcdrv_arbitrate);
+EXPORT_SYMBOL(hdlcdrv_register_hdlcdrv);
+EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv);
+
+#else
+
+static struct symbol_table hdlcdrv_syms = {
+#include <linux/symtab_begin.h>
+        X(hdlcdrv_receiver),
+        X(hdlcdrv_transmitter),
+        X(hdlcdrv_arbitrate),
+        X(hdlcdrv_register_hdlcdrv),
+        X(hdlcdrv_unregister_hdlcdrv),
+#include <linux/symtab_end.h>
+};
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int init_module(void))
+{
+	printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+	printk(KERN_INFO "hdlcdrv: version 0.4 compiled " __TIME__ " " __DATE__ "\n");
+#if LINUX_VERSION_CODE < 0x20115
+        register_symtab(&hdlcdrv_syms);
+#endif
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	printk(KERN_INFO "hdlcdrv: cleanup\n");
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/hp-plus.c linux/drivers/net/hp-plus.c
--- v2.0.34/linux/drivers/net/hp-plus.c	Thu Feb 29 21:50:44 1996
+++ linux/drivers/net/hp-plus.c	Mon Jul 13 13:47:31 1998
@@ -101,13 +101,13 @@
 static void hpp_mem_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void hpp_mem_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, const int start_page);
 static void hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						  int ring_page);
 static void hpp_io_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void hpp_io_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, const int start_page);
 static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						  int ring_page);
 
@@ -385,7 +385,7 @@
    It's always safe to round up, so we do. */
 static void
 hpp_io_block_output(struct device *dev, int count,
-					const unsigned char *buf, const start_page)
+					const unsigned char *buf, const int start_page)
 {
 	int ioaddr = dev->base_addr - NIC_OFFSET;
 	outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
@@ -395,7 +395,7 @@
 
 static void
 hpp_mem_block_output(struct device *dev, int count,
-				const unsigned char *buf, const start_page)
+				const unsigned char *buf, const int start_page)
 {
 	int ioaddr = dev->base_addr - NIC_OFFSET;
 	int option_reg = inw(ioaddr + HPP_OPTION);
diff -u --recursive --new-file v2.0.34/linux/drivers/net/hp.c linux/drivers/net/hp.c
--- v2.0.34/linux/drivers/net/hp.c	Thu Feb 29 21:50:44 1996
+++ linux/drivers/net/hp.c	Mon Jul 13 13:47:31 1998
@@ -65,7 +65,7 @@
 static void hp_block_input(struct device *dev, int count,
 					struct sk_buff *skb , int ring_offset);
 static void hp_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, const int start_page);
 
 static void hp_init_card(struct device *dev);
 
@@ -309,7 +309,7 @@
 
 static void
 hp_block_output(struct device *dev, int count,
-				const unsigned char *buf, const start_page)
+				const unsigned char *buf, const int start_page)
 {
 	int nic_base = dev->base_addr;
 	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
diff -u --recursive --new-file v2.0.34/linux/drivers/net/hp100.h linux/drivers/net/hp100.h
--- v2.0.34/linux/drivers/net/hp100.h	Tue Aug  5 09:11:35 1997
+++ linux/drivers/net/hp100.h	Mon Jul 13 13:47:31 1998
@@ -529,7 +529,7 @@
  */
 
 #define MAX_RX_PDL              30   /* Card limit = 31 */
-#define MAX_RX_FRAG             2    /* Dont need more... */
+#define MAX_RX_FRAG             2    /* Don't need more... */
 #define MAX_TX_PDL              29
 #define MAX_TX_FRAG             2   /* Limit = 31 */
 
diff -u --recursive --new-file v2.0.34/linux/drivers/net/mkiss.c linux/drivers/net/mkiss.c
--- v2.0.34/linux/drivers/net/mkiss.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/mkiss.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,1132 @@
+/*
+ *	MKISS Driver
+ *
+ *	This module:
+ *		This module 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 module implements the AX.25 protocol for kernel-based
+ *		devices like TTYs. It interfaces between a raw TTY, and the
+ *		kernel's AX.25 protocol layers, just like slip.c.
+ *		AX.25 needs to be seperated from slip.c while slip.c is no
+ *		longer a static kernel device since it is a module.
+ *		This method clears the way to implement other kiss protocols
+ *		like mkiss smack g8bpq ..... so far only mkiss is implemented.
+ *
+ * Hans Alblas <hans@esrac.ele.tue.nl>
+ *
+ *	History
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/major.h>
+
+#include <linux/timer.h>
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#include <net/ax25.h>
+
+#include "mkiss.h"
+
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#endif
+
+#ifdef MODULE
+#define AX25_VERSION    "AX25-MODULAR-NET3.019-NEWTTY"
+#define	min(a,b)	(a < b ? a : b)
+#else
+#define	AX25_VERSION	"AX25-NET3.019-NEWTTY"
+#endif
+
+#define NR_MKISS 4
+#define MKISS_SERIAL_TYPE_NORMAL 1
+
+struct mkiss_channel {
+	int magic;		/* magic word */
+	int init;		/* channel exists? */
+	struct tty_struct *tty; /* link to tty control structure */
+};
+
+typedef struct ax25_ctrl {
+	char if_name[8];	/* "ax0\0" .. "ax99999\0"	*/
+	struct ax_disp ctrl;	/* 				*/
+	struct device  dev;	/* the device			*/
+} ax25_ctrl_t;
+
+static ax25_ctrl_t **ax25_ctrls = NULL;
+int ax25_maxdev = AX25_MAXDEV;		/* Can be overridden with insmod! */
+
+static struct tty_ldisc	ax_ldisc;
+static struct tty_driver mkiss_driver;
+static int mkiss_refcount;
+static struct tty_struct *mkiss_table[NR_MKISS];
+static struct termios *mkiss_termios[NR_MKISS];
+static struct termios *mkiss_termios_locked[NR_MKISS];
+struct mkiss_channel MKISS_Info[NR_MKISS];
+
+static int ax25_init(struct device *);
+static int mkiss_init(void);
+static int mkiss_write(struct tty_struct *, int, const unsigned char *, int);
+static int kiss_esc(unsigned char *, unsigned char *, int);
+static void kiss_unesc(struct ax_disp *, unsigned char);
+
+/* Find a free channel, and link in this `tty' line. */
+static inline struct ax_disp *ax_alloc(void)
+{
+	ax25_ctrl_t *axp;
+	int i;
+
+	if (ax25_ctrls == NULL)		/* Master array missing ! */
+		return NULL;
+
+	for (i = 0; i < ax25_maxdev; i++) {
+		axp = ax25_ctrls[i];
+
+		/* Not allocated ? */
+		if (axp == NULL)
+			break;
+
+		/* Not in use ? */
+		if (!set_bit(AXF_INUSE, &axp->ctrl.flags))
+			break;
+	}
+
+	/* Sorry, too many, all slots in use */
+	if (i >= ax25_maxdev)
+		return NULL;
+
+	/* If no channels are available, allocate one */
+	if (axp == NULL && (ax25_ctrls[i] = (ax25_ctrl_t *)kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
+		axp = ax25_ctrls[i];
+		memset(axp, 0, sizeof(ax25_ctrl_t));
+
+		/* Initialize channel control data */
+		set_bit(AXF_INUSE, &axp->ctrl.flags);
+		sprintf(axp->if_name, "ax%d", i++);
+		axp->ctrl.tty      = NULL;
+		axp->dev.name      = axp->if_name;
+		axp->dev.base_addr = i;
+		axp->dev.priv      = (void *)&axp->ctrl;
+		axp->dev.next      = NULL;
+		axp->dev.init      = ax25_init;
+	}
+
+	if (axp != NULL) {
+		/*
+		 * register device so that it can be ifconfig'ed
+		 * ax25_init() will be called as a side-effect
+		 * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl !
+		 */
+		if (register_netdev(&axp->dev) == 0) {
+			/* (Re-)Set the INUSE bit.   Very Important! */
+			set_bit(AXF_INUSE, &axp->ctrl.flags);
+			axp->ctrl.dev = &axp->dev;
+			axp->dev.priv = (void *)&axp->ctrl;
+
+			return &axp->ctrl;
+		} else {
+			clear_bit(AXF_INUSE,&axp->ctrl.flags);
+			printk(KERN_ERR "ax_alloc() - register_netdev() failure.\n");
+		}
+	}
+
+	return NULL;
+}
+
+/* Free an AX25 channel. */
+static inline void ax_free(struct ax_disp *ax)
+{
+	/* Free all AX25 frame buffers. */
+	if (ax->rbuff)
+		kfree(ax->rbuff);
+	ax->rbuff = NULL;
+	if (ax->xbuff)
+		kfree(ax->xbuff);
+	ax->xbuff = NULL;
+	if (!clear_bit(AXF_INUSE, &ax->flags))
+		printk(KERN_ERR "%s: ax_free for already free unit.\n", ax->dev->name);
+}
+
+static void ax_changedmtu(struct ax_disp *ax)
+{
+	struct device *dev = ax->dev;
+	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+	int len;
+	unsigned long flags;
+
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	xbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC);
+	rbuff = (unsigned char *)kmalloc(len + 4, GFP_ATOMIC);
+
+	if (xbuff == NULL || rbuff == NULL)  {
+		printk(KERN_ERR "%s: unable to grow ax25 buffers, MTU change cancelled.\n",
+		       ax->dev->name);
+		dev->mtu = ax->mtu;
+		if (xbuff != NULL)
+			kfree(xbuff);
+		if (rbuff != NULL)
+			kfree(rbuff);
+		return;
+	}
+
+	save_flags(flags);
+	cli();
+
+	oxbuff    = ax->xbuff;
+	ax->xbuff = xbuff;
+	orbuff    = ax->rbuff;
+	ax->rbuff = rbuff;
+
+	if (ax->xleft) {
+		if (ax->xleft <= len) {
+			memcpy(ax->xbuff, ax->xhead, ax->xleft);
+		} else  {
+			ax->xleft = 0;
+			ax->tx_dropped++;
+		}
+	}
+
+	ax->xhead = ax->xbuff;
+
+	if (ax->rcount) {
+		if (ax->rcount <= len) {
+			memcpy(ax->rbuff, orbuff, ax->rcount);
+		} else  {
+			ax->rcount = 0;
+			ax->rx_over_errors++;
+			set_bit(AXF_ERROR, &ax->flags);
+		}
+	}
+
+	ax->mtu      = dev->mtu + 73;
+	ax->buffsize = len;
+
+	restore_flags(flags);
+
+	if (oxbuff != NULL)
+		kfree(oxbuff);
+	if (orbuff != NULL)
+		kfree(orbuff);
+}
+
+
+/* Set the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void ax_lock(struct ax_disp *ax)
+{
+	if (set_bit(0, (void *)&ax->dev->tbusy))
+		printk(KERN_ERR "%s: trying to lock already locked device!\n", ax->dev->name);
+}
+
+
+/* Clear the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void ax_unlock(struct ax_disp *ax)
+{
+	if (!clear_bit(0, (void *)&ax->dev->tbusy))
+		printk(KERN_ERR "%s: trying to unlock already unlocked device!\n", ax->dev->name);
+}
+
+/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
+static void ax_bump(struct ax_disp *ax)
+{
+	struct ax_disp *tmp_ax;
+	struct sk_buff *skb;
+	struct mkiss_channel *mkiss;
+	int count;
+
+        tmp_ax = ax;
+
+	if (ax->rbuff[0] > 0x0f) {
+		if (ax->mkiss != NULL) {
+			mkiss= ax->mkiss->tty->driver_data;
+			if (mkiss->magic == MKISS_DRIVER_MAGIC)
+				tmp_ax = ax->mkiss;
+		}
+ 	}
+
+	count = ax->rcount;
+
+	if ((skb = dev_alloc_skb(count)) == NULL) {
+		printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", ax->dev->name);
+		ax->rx_dropped++;
+		return;
+	}
+
+	skb->dev      = tmp_ax->dev;
+	memcpy(skb_put(skb,count), ax->rbuff, count);
+	skb->mac.raw  = skb->data;
+	skb->protocol = htons(ETH_P_AX25);
+	netif_rx(skb);
+	tmp_ax->rx_packets++;
+}
+
+/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
+static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len)
+{
+	unsigned char *p;
+	int actual, count;
+	struct mkiss_channel *mkiss = ax->tty->driver_data;
+
+	if (ax->mtu != ax->dev->mtu + 73)	/* Someone has been ifconfigging */
+		ax_changedmtu(ax);
+
+	if (len > ax->mtu) {		/* Sigh, shouldn't occur BUT ... */
+		len = ax->mtu;
+		printk(KERN_ERR "%s: truncating oversized transmit packet!\n", ax->dev->name);
+		ax->tx_dropped++;
+		ax_unlock(ax);
+		return;
+	}
+
+	p = icp;
+
+	if (mkiss->magic  != MKISS_DRIVER_MAGIC) {
+		count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
+		ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count);
+		ax->tx_packets++;
+		ax->dev->trans_start = jiffies;
+		ax->xleft = count - actual;
+		ax->xhead = ax->xbuff + actual;
+	} else {
+		count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len);
+		ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count);
+		ax->tx_packets++;
+		ax->mkiss->dev->trans_start = jiffies;
+		ax->mkiss->xleft = count - actual;
+		ax->mkiss->xhead = ax->mkiss->xbuff + actual;
+	}
+}
+
+/*
+ * Called by the driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+static void ax25_write_wakeup(struct tty_struct *tty)
+{
+	int actual;
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	struct mkiss_channel *mkiss;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+		return;
+	if (ax->xleft <= 0)  {
+		/* Now serial buffer is almost free & we can start
+		 * transmission of another packet
+		 */
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+		if (ax->mkiss != NULL) {
+			mkiss= ax->mkiss->tty->driver_data;
+	        	if (mkiss->magic  == MKISS_DRIVER_MAGIC)
+				ax_unlock(ax->mkiss);
+	        }
+
+		ax_unlock(ax);
+		mark_bh(NET_BH);
+		return;
+	}
+
+	actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft);
+	ax->xleft -= actual;
+	ax->xhead += actual;
+}
+
+/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
+static int ax_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	struct mkiss_channel *mkiss = ax->tty->driver_data;
+	struct ax_disp *tmp_ax;
+
+	tmp_ax = NULL;
+
+	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
+		if (skb->data[0] < 0x10)
+			skb->data[0] = skb->data[0] + 0x10;
+		tmp_ax = ax->mkiss;
+	}
+
+	if (!dev->start)  {
+		printk(KERN_ERR "%s: xmit call when iface is down\n", dev->name);
+		return 1;
+	}
+
+	if (tmp_ax != NULL)
+		if (tmp_ax->dev->tbusy)
+			return 1;
+
+	if (tmp_ax != NULL)
+		if (dev->tbusy) {
+			printk(KERN_ERR "mkiss: dev busy while serial dev is free\n");
+			ax_unlock(ax);
+	        }
+
+	if (dev->tbusy) {
+		/*
+		 * May be we must check transmitter timeout here ?
+		 *      14 Oct 1994 Dmitry Gorodchanin.
+		 */
+		if (jiffies - dev->trans_start  < 20 * HZ) {
+			/* 20 sec timeout not reached */
+			return 1;
+		}
+
+		printk(KERN_ERR "%s: transmit timed out, %s?\n", dev->name,
+		       (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ?
+		       "bad line quality" : "driver error");
+
+		ax->xleft = 0;
+		ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		ax_unlock(ax);
+	}
+
+	/* We were not busy, so we are now... :-) */
+	if (skb != NULL) {
+		ax_lock(ax);
+		if (tmp_ax != NULL)
+			ax_lock(tmp_ax);
+		ax_encaps(ax, skb->data, skb->len);
+		dev_kfree_skb(skb, FREE_WRITE);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+
+/* Return the frame type ID */
+static int ax_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+	  void *daddr, void *saddr, unsigned len)
+{
+#ifdef CONFIG_INET
+	if (type != htons(ETH_P_AX25))
+		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+#endif
+	return 0;
+}
+
+
+static int ax_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+	return ax25_rebuild_header(buff, dev, raddr, skb);
+#else
+	return 0;
+#endif
+}
+
+#endif /* CONFIG_{AX25,AX25_MODULE} */
+
+/* Open the low-level part of the AX25 channel. Easy! */
+static int ax_open(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	unsigned long len;
+
+	if (ax->tty == NULL)
+		return -ENODEV;
+
+	/*
+	 * Allocate the frame buffers:
+	 *
+	 * rbuff	Receive buffer.
+	 * xbuff	Transmit buffer.
+	 * cbuff        Temporary compression buffer.
+	 */
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	if ((ax->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto norbuff;
+
+	if ((ax->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto noxbuff;
+
+	ax->mtu	     = dev->mtu + 73;
+	ax->buffsize = len;
+	ax->rcount   = 0;
+	ax->xleft    = 0;
+
+	ax->flags   &= (1 << AXF_INUSE);      /* Clear ESCAPE & ERROR flags */
+	/* Needed because address '0' is special */
+	if (dev->pa_addr == 0)
+		dev->pa_addr = ntohl(0xC0A80001);
+	dev->tbusy  = 0;
+	dev->start  = 1;
+
+	return 0;
+
+	/* Cleanup */
+	kfree(ax->xbuff);
+
+noxbuff:
+	kfree(ax->rbuff);
+
+norbuff:
+	return -ENOMEM;
+}
+
+
+/* Close the low-level part of the AX25 channel. Easy! */
+static int ax_close(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	if (ax->tty == NULL)
+		return -EBUSY;
+
+	ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	return 0;
+}
+
+static int ax25_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of data has been received, which can now be decapsulated
+ * and sent on to the AX.25 layer for further processing.
+ */
+static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+
+	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+		return;
+
+	/*
+	 * Argh! mtu change time! - costs us the packet part received
+	 * at the change
+	 */
+	if (ax->mtu != ax->dev->mtu + 73)
+		ax_changedmtu(ax);
+
+	/* Read the characters out of the buffer */
+	while (count--) {
+		if (fp != NULL && *fp++) {
+			if (!set_bit(AXF_ERROR, &ax->flags))
+				ax->rx_errors++;
+			cp++;
+			continue;
+		}
+
+		kiss_unesc(ax, *cp++);
+	}
+}
+
+static int ax25_open(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	struct ax_disp *tmp_ax;
+	struct mkiss_channel *mkiss;
+	int err, cnt;
+
+	/* First make sure we're not already connected. */
+	if (ax && ax->magic == AX25_MAGIC)
+		return -EEXIST;
+
+	/* OK.  Find a free AX25 channel to use. */
+	if ((ax = ax_alloc()) == NULL)
+		return -ENFILE;
+
+	ax->tty = tty;
+	tty->disc_data = ax;
+
+	ax->mkiss = NULL;
+	tmp_ax    = NULL;
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	/* Restore default settings */
+	ax->dev->type = ARPHRD_AX25;
+
+	/* Perform the low-level AX25 initialization. */
+	if ((err = ax_open(ax->dev)))
+		return err;
+
+	mkiss= ax->tty->driver_data;
+
+	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
+		for (cnt = 1; cnt < ax25_maxdev; cnt++) {
+			if (ax25_ctrls[cnt]) {
+				if (ax25_ctrls[cnt]->dev.start) {
+					if (ax == &ax25_ctrls[cnt]->ctrl) {
+						cnt--;
+						tmp_ax = &ax25_ctrls[cnt]->ctrl;
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	if (tmp_ax != NULL) {
+		ax->mkiss     = tmp_ax;
+		tmp_ax->mkiss = ax;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	/* Done.  We have linked the TTY line to a channel. */
+	return ax->dev->base_addr;
+}
+
+static void ax25_close(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	int mkiss ;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return;
+
+	mkiss = ax->mode;
+	dev_close(ax->dev);
+
+	tty->disc_data = 0;
+	ax->tty        = NULL;
+
+	/* VSV = very important to remove timers */
+	ax_free(ax);
+	unregister_netdev(ax->dev);
+
+	MOD_DEC_USE_COUNT;
+}
+
+
+static struct enet_statistics *ax_get_stats(struct device *dev)
+{
+	static struct enet_statistics stats;
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	memset(&stats, 0, sizeof(struct enet_statistics));
+
+	stats.rx_packets     = ax->rx_packets;
+	stats.tx_packets     = ax->tx_packets;
+	stats.rx_dropped     = ax->rx_dropped;
+	stats.tx_dropped     = ax->tx_dropped;
+	stats.tx_errors      = ax->tx_errors;
+	stats.rx_errors      = ax->rx_errors;
+	stats.rx_over_errors = ax->rx_over_errors;
+
+	return &stats;
+}
+
+
+/************************************************************************
+ *			   STANDARD ENCAPSULATION	        	 *
+ ************************************************************************/
+
+int kiss_esc(unsigned char *s, unsigned char *d, int len)
+{
+	unsigned char *ptr = d;
+	unsigned char c;
+
+	/*
+	 * Send an initial END character to flush out any
+	 * data that may have accumulated in the receiver
+	 * due to line noise.
+	 */
+
+	*ptr++ = END;
+
+	while (len-- > 0) {
+		switch (c = *s++) {
+			case END:
+				*ptr++ = ESC;
+				*ptr++ = ESC_END;
+				break;
+			case ESC:
+				*ptr++ = ESC;
+				*ptr++ = ESC_ESC;
+				break;
+			default:
+				*ptr++ = c;
+				break;
+		}
+	}
+
+	*ptr++ = END;
+
+	return ptr - d;
+}
+
+static void kiss_unesc(struct ax_disp *ax, unsigned char s)
+{
+	switch (s) {
+		case END:
+			/* drop keeptest bit = VSV */
+			if (test_bit(AXF_KEEPTEST, &ax->flags))
+				clear_bit(AXF_KEEPTEST, &ax->flags);
+
+			if (!clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
+				ax_bump(ax);
+
+			clear_bit(AXF_ESCAPE, &ax->flags);
+			ax->rcount = 0;
+			return;
+
+		case ESC:
+			set_bit(AXF_ESCAPE, &ax->flags);
+			return;
+		case ESC_ESC:
+			if (clear_bit(AXF_ESCAPE, &ax->flags))
+				s = ESC;
+			break;
+		case ESC_END:
+			if (clear_bit(AXF_ESCAPE, &ax->flags))
+				s = END;
+			break;
+	}
+
+	if (!test_bit(AXF_ERROR, &ax->flags)) {
+		if (ax->rcount < ax->buffsize) {
+			ax->rbuff[ax->rcount++] = s;
+			return;
+		}
+
+		ax->rx_over_errors++;
+		set_bit(AXF_ERROR, &ax->flags);
+	}
+}
+
+
+int ax_set_mac_address(struct device *dev, void *addr)
+{
+	int err;
+
+	if ((err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN)) != 0)
+		return err;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy_fromfs(dev->dev_addr, addr, AX25_ADDR_LEN);
+
+	return 0;
+}
+
+static int ax_set_dev_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
+
+	return 0;
+}
+
+
+/* Perform I/O control on an active ax25 channel. */
+static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	int err;
+	unsigned int tmp;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return -EINVAL;
+
+	switch (cmd) {
+	 	case SIOCGIFNAME:
+			if ((err = verify_area(VERIFY_WRITE, arg, strlen(ax->dev->name) + 1)) != 0)
+				return err;
+			memcpy_tofs(arg, ax->dev->name, strlen(ax->dev->name) + 1);
+			return 0;
+
+		case SIOCGIFENCAP:
+			if ((err = verify_area(VERIFY_WRITE, arg, sizeof(int))) != 0)
+				return err;
+			put_user(4, (int *)arg);
+			return 0;
+
+		case SIOCSIFENCAP:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0)
+				return err;
+			tmp = get_user((int *)arg);
+	 		ax->mode = tmp;
+			ax->dev->addr_len        = AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
+			ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
+			ax->dev->type            = ARPHRD_AX25;
+			return 0;
+
+		 case SIOCSIFHWADDR:
+			return ax_set_mac_address(ax->dev, arg);
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int ax_open_dev(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	if (ax->tty==NULL)
+		return -ENODEV;
+
+	return 0;
+}
+
+/* Initialize AX25 control device -- register AX25 line discipline */
+int mkiss_init_ctrl_dev(void)
+{
+	int status;
+
+	if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */
+
+	if ((ax25_ctrls = (ax25_ctrl_t **)kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array !  No mkiss available\n");
+		return -ENOMEM;
+	}
+
+	/* Clear the pointer array, we allocate devices when we need them */
+	memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */
+
+	/* Fill in our line protocol discipline, and register it */
+	memset(&ax_ldisc, 0, sizeof(ax_ldisc));
+	ax_ldisc.magic  = TTY_LDISC_MAGIC;
+	ax_ldisc.flags  = 0;
+	ax_ldisc.open   = ax25_open;
+	ax_ldisc.close  = ax25_close;
+	ax_ldisc.read   = NULL;
+	ax_ldisc.write  = NULL;
+	ax_ldisc.ioctl  = (int (*)(struct tty_struct *, struct file *, unsigned int, unsigned long))ax25_disp_ioctl;
+	ax_ldisc.select = NULL;
+
+	ax_ldisc.receive_buf  = ax25_receive_buf;
+	ax_ldisc.receive_room = ax25_receive_room;
+	ax_ldisc.write_wakeup = ax25_write_wakeup;
+
+	if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0)
+		printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status);
+
+	mkiss_init();
+
+#ifdef MODULE
+	return status;
+#else
+	/*
+	 * Return "not found", so that dev_init() will unlink
+	 * the placeholder device entry for us.
+	 */
+	return ENODEV;
+#endif
+}
+
+
+/* Initialize the driver.  Called by network startup. */
+
+static int ax25_init(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	int i;
+
+	static char ax25_bcast[AX25_ADDR_LEN] =
+		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+	static char ax25_test[AX25_ADDR_LEN] =
+		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+
+	if (ax == NULL)		/* Allocation failed ?? */
+		return -ENODEV;
+
+	/* Set up the "AX25 Control Block". (And clear statistics) */
+	memset(ax, 0, sizeof (struct ax_disp));
+	ax->magic  = AX25_MAGIC;
+	ax->dev	   = dev;
+
+	/* Finish setting up the DEVICE info. */
+	dev->mtu             = AX_MTU;
+	dev->hard_start_xmit = ax_xmit;
+	dev->open            = ax_open_dev;
+	dev->stop            = ax_close;
+	dev->get_stats	     = ax_get_stats;
+#ifdef HAVE_SET_MAC_ADDR
+	dev->set_mac_address = ax_set_dev_mac_address;
+#endif
+	dev->hard_header_len = 0;
+	dev->addr_len        = 0;
+	dev->type            = ARPHRD_AX25;
+	dev->tx_queue_len    = 10;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_test,  AX25_ADDR_LEN);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header     = ax_header;
+	dev->rebuild_header  = ax_rebuild_header;
+#endif
+
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	/* New-style flags. */
+	dev->flags      = 0;
+	dev->family     = AF_INET;
+
+#ifdef CONFIG_INET
+	dev->pa_addr    = in_aton("192.168.0.1");
+	dev->pa_brdaddr = in_aton("192.168.0.255");
+	dev->pa_mask    = in_aton("255.255.255.0");
+	dev->pa_alen    = 4;
+#endif
+
+	return 0;
+}
+
+static int mkiss_open(struct tty_struct *tty, struct file *filp)
+{
+	struct mkiss_channel *mkiss;
+	int chan;
+
+	chan = MINOR(tty->device) - tty->driver.minor_start;
+
+	if (chan < 0 || chan >= NR_MKISS)
+		return -ENODEV;
+
+	mkiss = &MKISS_Info[chan];
+
+	mkiss->magic =  MKISS_DRIVER_MAGIC;
+	mkiss->init  = 1;
+	mkiss->tty   = tty;
+
+	tty->driver_data = mkiss;
+
+	tty->termios->c_iflag  = IGNBRK | IGNPAR;
+	tty->termios->c_cflag  = B9600 | CS8 | CLOCAL;
+	tty->termios->c_cflag &= ~CBAUD;
+
+	return 0;
+}
+
+static void mkiss_close(struct tty_struct *tty, struct file * filp)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (mkiss == NULL || mkiss->magic != MKISS_DRIVER_MAGIC)
+                return;
+
+	mkiss->tty   = NULL;
+	mkiss->init  = 0;
+	tty->stopped = 0;
+}
+
+static int mkiss_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+	return 0;
+}
+
+static int mkiss_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	/* Ignore serial ioctl's */
+	switch (cmd) {
+		case TCSBRK:
+		case TIOCMGET:
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+		case TCSETS:
+		case TCSETSF:		/* should flush first, but... */
+		case TCSETSW:		/* should wait until flush, but... */
+			return 0;
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+
+static void mkiss_dummy(struct tty_struct *tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return;
+
+	if (mkiss == NULL)
+		return;
+}
+
+static void mkiss_dummy2(struct tty_struct *tty, unsigned char ch)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return;
+
+	if (mkiss == NULL)
+		return;
+}
+
+
+static int mkiss_write_room(struct tty_struct * tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return 0;
+
+	if (mkiss == NULL)
+		return 0;
+
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+
+static int mkiss_chars_in_buffer(struct tty_struct *tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return 0;
+
+	if (mkiss == NULL)
+		return 0;
+
+	return 0;
+}
+
+
+static void mkiss_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	/* we don't do termios */
+}
+
+/* ******************************************************************** */
+/* * 			Init MKISS driver 			      * */
+/* ******************************************************************** */
+
+static int mkiss_init(void)
+{
+	memset(&mkiss_driver, 0, sizeof(struct tty_driver));
+
+	mkiss_driver.magic       = MKISS_DRIVER_MAGIC;
+	mkiss_driver.name        = "mkiss";
+	mkiss_driver.major       = MKISS_MAJOR;
+	mkiss_driver.minor_start = 0;
+	mkiss_driver.num         = NR_MKISS;
+	mkiss_driver.type        = TTY_DRIVER_TYPE_SERIAL;
+	mkiss_driver.subtype     = MKISS_SERIAL_TYPE_NORMAL;	/* not needed */
+
+	mkiss_driver.init_termios         = tty_std_termios;
+	mkiss_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
+	mkiss_driver.init_termios.c_cflag = B9600 | CS8 | CLOCAL;
+
+	mkiss_driver.flags           = TTY_DRIVER_REAL_RAW;
+	mkiss_driver.refcount        = &mkiss_refcount;
+	mkiss_driver.table           = mkiss_table;
+	mkiss_driver.termios         = (struct termios **)mkiss_termios;
+	mkiss_driver.termios_locked  = (struct termios **)mkiss_termios_locked;
+
+	mkiss_driver.ioctl           = mkiss_ioctl;
+	mkiss_driver.open            = mkiss_open;
+	mkiss_driver.close           = mkiss_close;
+	mkiss_driver.write           = mkiss_write;
+	mkiss_driver.write_room      = mkiss_write_room;
+	mkiss_driver.chars_in_buffer = mkiss_chars_in_buffer;
+	mkiss_driver.set_termios     = mkiss_set_termios;
+
+	/* some unused functions */
+	mkiss_driver.flush_buffer = mkiss_dummy;
+	mkiss_driver.throttle     = mkiss_dummy;
+	mkiss_driver.unthrottle   = mkiss_dummy;
+	mkiss_driver.stop         = mkiss_dummy;
+	mkiss_driver.start        = mkiss_dummy;
+	mkiss_driver.hangup       = mkiss_dummy;
+	mkiss_driver.flush_chars  = mkiss_dummy;
+	mkiss_driver.put_char     = mkiss_dummy2;
+
+	if (tty_register_driver(&mkiss_driver)) {
+		printk(KERN_ERR "Couldn't register Mkiss device\n");
+		return -EIO;
+	}
+
+	printk(KERN_INFO "AX.25 Multikiss device enabled\n");
+
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	register_symtab(NULL);
+
+	return mkiss_init_ctrl_dev();
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+	if (ax25_ctrls != NULL) {
+		for (i = 0; i < ax25_maxdev; i++) {
+			if (ax25_ctrls[i]) {
+				/*
+				 * VSV = if dev->start==0, then device
+				 * unregistred while close proc.
+				 */
+				if (ax25_ctrls[i]->dev.start)
+					unregister_netdev(&(ax25_ctrls[i]->dev));
+
+				kfree(ax25_ctrls[i]);
+				ax25_ctrls[i] = NULL;
+			}
+		}
+
+		kfree(ax25_ctrls);
+		ax25_ctrls = NULL;
+	}
+
+	if ((i = tty_register_ldisc(N_AX25, NULL)))
+		printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i);
+
+	if (tty_unregister_driver(&mkiss_driver))	/* remove devive */
+		printk(KERN_ERR "mkiss: can't unregister MKISS device\n");
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/mkiss.h linux/drivers/net/mkiss.h
--- v2.0.34/linux/drivers/net/mkiss.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/mkiss.h	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,56 @@
+/****************************************************************************
+ *	Defines for the Multi-KISS driver.
+ ****************************************************************************/
+
+#define AX25_MAXDEV	16		/* MAX number of AX25 channels;
+					   This can be overridden with
+					   insmod -oax25_maxdev=nnn	*/
+#define AX_MTU		236	
+
+/* SLIP/KISS protocol characters. */
+#define END             0300		/* indicates end of frame	*/
+#define ESC             0333		/* indicates byte stuffing	*/
+#define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
+#define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
+
+struct ax_disp {
+	int                magic;
+
+	/* Various fields. */
+	struct tty_struct  *tty;		/* ptr to TTY structure		*/
+	struct device      *dev;		/* easy for intr handling	*/
+	struct ax_disp     *mkiss;		/* mkiss txport if mkiss channel*/
+
+	/* These are pointers to the malloc()ed frame buffers. */
+	unsigned char      *rbuff;		/* receiver buffer		*/
+	int                rcount;		/* received chars counter       */
+	unsigned char      *xbuff;		/* transmitter buffer		*/
+	unsigned char      *xhead;		/* pointer to next byte to XMIT */
+	int                xleft;		/* bytes left in XMIT queue     */
+
+	/* SLIP interface statistics. */
+	unsigned long      rx_packets;		/* inbound frames counter	*/
+	unsigned long      tx_packets;		/* outbound frames counter      */
+	unsigned long      rx_errors;		/* Parity, etc. errors          */
+	unsigned long      tx_errors;		/* Planned stuff                */
+	unsigned long      rx_dropped;		/* No memory for skb            */
+	unsigned long      tx_dropped;		/* When MTU change              */
+	unsigned long      rx_over_errors;	/* Frame bigger then SLIP buf.  */
+
+	/* Detailed SLIP statistics. */
+	int                 mtu;		/* Our mtu (to spot changes!)   */
+	int                 buffsize;		/* Max buffers sizes            */
+
+
+	unsigned char       flags;		/* Flag values/ mode etc	*/
+#define AXF_INUSE	0		/* Channel in use               */
+#define AXF_ESCAPE	1               /* ESC received                 */
+#define AXF_ERROR	2               /* Parity, etc. error           */
+#define AXF_KEEPTEST	3		/* Keepalive test flag		*/
+#define AXF_OUTWAIT	4		/* is outpacket was flag	*/
+
+	int                 mode;
+};
+
+#define AX25_MAGIC		0x5316
+#define MKISS_DRIVER_MAGIC	1215
diff -u --recursive --new-file v2.0.34/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c
--- v2.0.34/linux/drivers/net/ne2k-pci.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/ne2k-pci.c	Mon Jul 13 13:47:31 1998
@@ -45,7 +45,7 @@
 #include "8390.h"
 
 /* Set statically or when loading the driver module. */
-static debug = 1;
+static int debug = 1;
 
 /* Some defines that people can play with if so inclined. */
 
@@ -189,14 +189,6 @@
 	if ( ! pcibios_present())
 		return -ENODEV;
 
-#ifndef MODULE
-	{
-		static unsigned version_printed = 0;
-		if (version_printed++ == 0)
-			printk(KERN_INFO "%s", version);
-	}
-#endif
-
 	for (;pci_index < 0xff; pci_index++) {
 		unsigned char pci_bus, pci_device_fn;
 		u8 pci_irq_line;
@@ -219,6 +211,14 @@
 				break;
 		if (pci_clone_list[i].vendor == 0)
 			continue;
+
+#ifndef MODULE
+		{
+			static unsigned version_printed = 0;
+			if (version_printed++ == 0)
+				printk(KERN_INFO "%s", version);
+		}
+#endif
 
 		pcibios_read_config_dword(pci_bus, pci_device_fn,
 								  PCI_BASE_ADDRESS_0, &pci_ioaddr);
diff -u --recursive --new-file v2.0.34/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v2.0.34/linux/drivers/net/net_init.c	Wed Nov  6 04:39:42 1996
+++ linux/drivers/net/net_init.c	Mon Jul 13 13:47:31 1998
@@ -318,7 +318,7 @@
 			for (i = 0; i < MAX_ETH_CARDS; ++i)
 				if (ethdev_index[i] == NULL) {
 					sprintf(dev->name, "eth%d", i);
-					printk("loading device '%s'...\n", dev->name);
+/*					printk("loading device '%s'...\n", dev->name);*/
 					ethdev_index[i] = dev;
 					break;
 				}
diff -u --recursive --new-file v2.0.34/linux/drivers/net/ni52.c linux/drivers/net/ni52.c
--- v2.0.34/linux/drivers/net/ni52.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/ni52.c	Mon Jul 13 13:47:31 1998
@@ -984,7 +984,7 @@
   }
 #endif
 
-#ifdef 0
+#if 0
   if(!at_least_one)
   { 
     int i;
diff -u --recursive --new-file v2.0.34/linux/drivers/net/pi2.c linux/drivers/net/pi2.c
--- v2.0.34/linux/drivers/net/pi2.c	Thu Feb 29 21:50:45 1996
+++ linux/drivers/net/pi2.c	Mon Jul 13 13:47:31 1998
@@ -53,6 +53,7 @@
    Oct  29, 1995 (ac)  A couple of minor fixes before this, and this release changes
    		       to the proper set_mac_address semantics which will break 
    		       a few programs I suspect.
+   Aug  18, 1996 (jsn) Converted to be used as a module.
 */
 
 /* The following #define invokes a hack that will improve performance (baud)
@@ -81,19 +82,12 @@
 #define DEF_B_SQUELDELAY 3	/* 30 mS */
 #define DEF_B_CLOCKMODE 0	/* Normal clock mode */
 
-static const char *version =
-"PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n";
-
 /* The following #define is only really required for the PI card, not
    the PI2 - but it's safer to leave it in. */
 #define REALLY_SLOW_IO 1
 
-#define PI2_MODULE 0
-
-#if PI2_MODULE > 0
+#include <linux/config.h>
 #include <linux/module.h>
-#endif
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
@@ -116,11 +110,10 @@
 #include <linux/skbuff.h>
 #include <linux/timer.h>
 #include <linux/if_arp.h>
-#include "pi2.h"
+#include <linux/pi2.h>
 #include "z8530.h"
 #include <net/ax25.h>
 
-
 struct mbuf {
     struct mbuf *next;
     int cnt;
@@ -553,7 +546,7 @@
 
 	    skb = dev_alloc_skb(sksize);
 	    if (skb == NULL) {
-		printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+		printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
 		lp->stats.rx_dropped++;
 		restore_flags(flags);
 		return;
@@ -642,7 +635,7 @@
 		sksize = pkt_len;
 		skb = dev_alloc_skb(sksize);
 		if (skb == NULL) {
-		    printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+		    printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
 		    lp->stats.rx_dropped++;
 		    restore_flags(flags);
 		    return;
@@ -1214,27 +1207,27 @@
     int ports[] =
     {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0};
 
-    printk(version);
+    printk(KERN_INFO "PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n");
 
     /* Only one card supported for now */
     for (port = &ports[0]; *port && !card_type; port++) {
 	ioaddr = *port;
 
 	if (check_region(ioaddr, PI_TOTAL_SIZE) == 0) {
-	    printk("PI: Probing for card at address %#3x\n",ioaddr);
+	    printk(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr);
 	    card_type = hw_probe(ioaddr);
 	}
     }
 
     switch (card_type) {
     case 1:
-	printk("PI: Found a PI card at address %#3x\n", ioaddr);
+	printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr);
 	break;
     case 2:
-	printk("PI: Found a PI2 card at address %#3x\n", ioaddr);
+	printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr);
 	break;
     default:
-	printk("PI: ERROR: No card found\n");
+	printk(KERN_ERR "PI: ERROR: No card found\n");
 	return -EIO;
     }
 
@@ -1256,6 +1249,7 @@
     register_netdev(&pi0b);
     pi0b.base_addr = ioaddr;
     pi0b.irq = 0;
+
     pi0b.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
 
     /* Now initialize them */
@@ -1275,8 +1269,9 @@
 	return 0;
 }
 
-static int pi_set_mac_address(struct device *dev, struct sockaddr *sa)
+static int pi_set_mac_address(struct device *dev, void *addr)
 {
+    struct sockaddr *sa = (struct sockaddr *)addr;
     memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);	/* addr is an AX.25 shifted ASCII */
     return 0;						/* mac address */
 }
@@ -1364,7 +1359,7 @@
 
 	lp->dmachan = dev->dma;
 	if (lp->dmachan < 1 || lp->dmachan > 3)
-	    printk("PI: DMA channel %d out of range\n", lp->dmachan);
+	    printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan);
 
 	/* chipset_init() was already called */
 
@@ -1381,7 +1376,7 @@
 	    /* 20 "jiffies" should be plenty of time... */
 	    dev->irq = autoirq_report(20);
 	    if (!dev->irq) {
-		printk(". Failed to detect IRQ line.\n");
+		printk(KERN_ERR "PI: Failed to detect IRQ line.\n");
 	    }
 	    save_flags(flags);
 	    cli();
@@ -1391,7 +1386,7 @@
 	    restore_flags(flags);
 	}
 
-	printk("PI: Autodetected IRQ %d, assuming DMA %d.\n",
+	printk(KERN_INFO "PI: Autodetected IRQ %d, assuming DMA %d.\n",
 	       dev->irq, dev->dma);
 
 	/* This board has jumpered interrupts. Snarf the interrupt vector
@@ -1400,7 +1395,7 @@
 	{
 	    int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", NULL);
 	    if (irqval) {
-		printk("PI: unable to get IRQ %d (irqval=%d).\n",
+		printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n",
 		       dev->irq, irqval);
 		return EAGAIN;
 	    }
@@ -1436,10 +1431,13 @@
     /* New-style flags. */
     dev->flags = 0;
     dev->family = AF_INET;
-    dev->pa_addr = 0;
-    dev->pa_brdaddr = 0;
-    dev->pa_mask = 0;
-    dev->pa_alen = 4;
+
+#ifdef CONFIG_INET
+    dev->pa_addr    = in_aton("192.168.0.1");
+    dev->pa_brdaddr = in_aton("192.168.0.255");
+    dev->pa_mask    = in_aton("255.255.255.0");
+    dev->pa_alen    = 4;
+#endif
 
     return 0;
 }
@@ -1487,6 +1485,9 @@
     dev->interrupt = 0;
     dev->start = 1;
     first_time = 0;
+
+    MOD_INC_USE_COUNT;
+
     return 0;
 }
 
@@ -1520,7 +1521,7 @@
 
 #if 0
     if (dev_a == NULL) {
-	printk("PI: pi_interrupt(): irq %d for unknown device.\n", irq);
+	printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq);
 	return;
     }
 #endif    
@@ -1589,6 +1590,9 @@
 	free_p(ptr);
 
     restore_flags(flags);
+
+    MOD_DEC_USE_COUNT;
+
     return 0;
 }
 
@@ -1681,6 +1685,28 @@
     return &lp->stats;
 }
 
+#ifdef MODULE
+int init_module(void)
+{
+    register_symtab(NULL);
+    return pi_init();
+}
+
+void cleanup_module(void)
+{
+    free_irq(pi0a.irq, NULL);	/* IRQs and IO Ports are shared */
+    release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE);
+    irq2dev_map[pi0a.irq] = NULL;
+
+    kfree(pi0a.priv);
+    pi0a.priv = NULL;
+    unregister_netdev(&pi0a);
+
+    kfree(pi0b.priv);
+    pi0b.priv = NULL;
+    unregister_netdev(&pi0b);
+}
+#endif
 
 /*
  * Local variables:
diff -u --recursive --new-file v2.0.34/linux/drivers/net/pi2.h linux/drivers/net/pi2.h
--- v2.0.34/linux/drivers/net/pi2.h	Tue Jun  6 01:22:10 1995
+++ linux/drivers/net/pi2.h	Mon Jul 13 13:47:31 1998
@@ -1,133 +0,0 @@
-
-#define DMA_BUFF_SIZE 2200
-
-/* Network statistics, with the same names as 'struct enet_statistics'. */
-#define netstats enet_statistics
-
-#define ON 1
-#define OFF 0
-
-
-/* Register offset info, specific to the PI
- * E.g., to read the data port on channel A, use
- *  inportb(pichan[dev].base + CHANA + DATA)
- */
-#define CHANB   0   /* Base of channel B regs */
-#define CHANA   2   /* Base of channel A regs */
-
-/* 8530 ports on each channel */
-#define CTL 0
-#define DATA    1
-
-#define DMAEN   0x4 /* Offset off DMA Enable register */
-
-/* Timer chip offsets */
-#define TMR0    0x8 /* Offset of timer 0 register */
-#define TMR1    0x9 /* Offset of timer 1 register */
-#define TMR2    0xA /* Offset of timer 2 register */
-#define TMRCMD  0xB /* Offset of timer command register */
-
-/* Timer chip equates */
-#define SC0 0x00 /* Select counter 0 */
-#define SC1 0x40 /* Select counter 1 */
-#define SC2 0x80 /* Select counter 2 */
-#define CLATCH  0x00 /* Counter latching operation */
-#define MSB 0x20 /* Read/load MSB only */
-#define LSB 0x10 /* Read/load LSB only */
-#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
-#define MODE0   0x00 /* Interrupt on terminal count */
-#define MODE1   0x02 /* Programmable one shot */
-#define MODE2   0x04 /* Rate generator */
-#define MODE3   0x06 /* Square wave rate generator */
-#define MODE4   0x08 /* Software triggered strobe */
-#define MODE5   0x0a /* Hardware triggered strobe */
-#define BCD 0x01 /* BCD counter */
-
-/* DMA controller registers */
-#define DMA_STAT    8   /* DMA controller status register */
-#define DMA_CMD     8   /* DMA controller command register */
-#define DMA_MASK        10  /* DMA controller mask register */
-#define DMA_MODE        11  /* DMA controller mode register */
-#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
-/* DMA data */
-#define DMA_DISABLE (0x04)  /* Disable channel n */
-#define DMA_ENABLE  (0x00)  /* Enable channel n */
-/* Single transfers, incr. address, auto init, writes, ch. n */
-#define DMA_RX_MODE (0x54)
-/* Single transfers, incr. address, no auto init, reads, ch. n */
-#define DMA_TX_MODE (0x48)
-
-#define SINGLE 3686400
-#define DOUBLE 7372800
-
-#define SIOCGPIPARAM		0x5000	/* get PI parameters */
-#define SIOCSPIPARAM		0x5001	/* set */
-#define SIOCGPIBAUD		0x5002	/* get only baud rate */
-#define SIOCSPIBAUD		0x5003	
-#define SIOCGPIDMA		0x5004	/* get only DMA */
-#define SIOCSPIDMA		0x5005	
-#define SIOCGPIIRQ		0x5006	/* get only IRQ */
-#define SIOCSPIIRQ		0x5007	
-
-struct pi_req  {
-    int cmd;
-    int speed;
-    int clockmode;
-    int txdelay;
-    unsigned char persist;
-    int slotime; 
-    int squeldelay;
-    int dmachan;    
-    int irq;    
-};
-
-#ifdef __KERNEL__
-
-/* Information that needs to be kept for each channel. */
-struct pi_local {
-    struct netstats stats; /* %%%dp*/
-    long open_time;             /* Useless example local info. */
-    unsigned long xtal; 
-
-    struct mbuf *rcvbuf;/* Buffer for current rx packet */
-    struct mbuf *rxdmabuf1; /* DMA rx buffer */
-    struct mbuf *rxdmabuf2; /* DMA rx buffer */
-
-    int bufsiz;         /* Size of rcvbuf */
-    char *rcp;          /* Pointer into rcvbuf */
-
-    struct sk_buff_head sndq;  /* Packets awaiting transmission */
-    int sndcnt;         /* Number of packets on sndq */
-    struct sk_buff *sndbuf;	/* Current buffer being transmitted */
-    char *txdmabuf;     /* Transmit DMA buffer */
-	char *txptr;		/* Used by B port tx */
-	int txcnt;			
-    char tstate;        /* Transmitter state */
-#define IDLE    0       /* Transmitter off, no data pending */
-#define ACTIVE  1       /* Transmitter on, sending data */
-#define UNDERRUN 2      /* Transmitter on, flushing CRC */
-#define FLAGOUT 3       /* CRC sent - attempt to start next frame */
-#define DEFER 4         /* Receive Active - DEFER Transmit */
-#define ST_TXDELAY 5    /* Sending leading flags */
-#define CRCOUT 6
-    char rstate;        /* Set when !DCD goes to 0 (TRUE) */
-/* Normal state is ACTIVE if Receive enabled */
-#define RXERROR 2       /* Error -- Aborting current Frame */
-#define RXABORT 3       /* ABORT sequence detected */
-#define TOOBIG 4        /* too large a frame to store */
-    int dev;            /* Device number */
-    int base;       /* Base of I/O registers */
-    int cardbase;     /* Base address of card */
-    int stata;        /* address of Channel A status regs */
-    int statb;        /* address of Channel B status regs */
-    int speed;        /* Line speed, bps */
-    int clockmode;    /* tapr 9600 modem clocking option */
-    int txdelay;      /* Transmit Delay 10 ms/cnt */
-    unsigned char persist;       /* Persistence (0-255) as a % */
-    int slotime;      /* Delay to wait on persistence hit */
-    int squeldelay;   /* Delay after XMTR OFF for squelch tail */
-    struct iface *iface;    /* Associated interface */
-    int dmachan;           /* DMA channel for this port */
-};
-
-#endif
diff -u --recursive --new-file v2.0.34/linux/drivers/net/pt.c linux/drivers/net/pt.c
--- v2.0.34/linux/drivers/net/pt.c	Thu Apr 11 23:49:38 1996
+++ linux/drivers/net/pt.c	Mon Jul 13 13:47:31 1998
@@ -32,6 +32,8 @@
  * 07/10/95 cs  Fixed for 1.3.30 (hopefully)
  * 26/11/95 cs  Fixed for 1.3.43, ala 29/10 for pi2.c by ac
  * 21/12/95 cs  Got rid of those nasty warnings when compiling, for 1.3.48
+ * 08/08/96 jsn Convert to use as a module. Removed send_kiss, empty_scc and
+ *		pt_loopback functions - they were unused.
  */
  
 /* 
@@ -64,7 +66,9 @@
 #define	PARAM_HARDWARE	6
 #define	PARAM_RETURN	255
 
+#include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -86,14 +90,10 @@
 #include <linux/skbuff.h>
 #include <linux/timer.h>
 #include <linux/if_arp.h>
-#include "pt.h"
+#include <linux/pt.h>
 #include "z8530.h"
 #include <net/ax25.h>
 
-static char *version =
-"PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n";
-
-
 struct mbuf {
     struct mbuf *next;
     int cnt;
@@ -133,9 +133,7 @@
 static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
 static int hw_probe(int ioaddr);
 static void tdelay(struct pt_local *lp, int time);
-static void empty_scc(struct pt_local *lp);
 static void chipset_init(struct device *dev);
-static void send_kiss(struct device *dev, unsigned char arg, unsigned char val);
 
 static char ax25_bcast[7] =
 {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
@@ -185,7 +183,9 @@
 	ptr = skb->data;
 	if (ptr[0] != 0 && skb->len >= 2)
 	{
-		printk("Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
+#ifdef PT_DEBUG
+		printk(KERN_DEBUG "PT: Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
+#endif
 		/* Kludge to get device */
 		if ((struct pt_local*)(&pt0b.priv) == lp)
 			dev = &pt0b;
@@ -197,21 +197,17 @@
 			case PARAM_TXDELAY:
 				/*TxDelay is in 10mS increments */
 				lp->txdelay = ptr[1] * 10;
-				send_kiss(dev, PARAM_TXDELAY, (u_char)(lp->txdelay/10));
 				break;
 			case PARAM_PERSIST:
 				lp->persist = ptr[1];
-				send_kiss(dev, PARAM_PERSIST, (u_char)(lp->persist));
 				break;
 			case PARAM_SLOTTIME:
 				lp->slotime = ptr[1];
-				send_kiss(dev, PARAM_SLOTTIME, (u_char)(lp->slotime/10));
 				break;
 			case PARAM_FULLDUP:
 				/* Yeah right, you wish!  Fullduplex is a little while to
 				 * go folks, but this is how you fire it up
 				 */
-				send_kiss(dev, PARAM_FULLDUP, 0);
 				break;
 			/* Perhaps we should have txtail here?? */
 		} /*switch */
@@ -226,7 +222,7 @@
     restore_flags(flags);
 
 #ifdef PT_DEBUG
-	printk("PTd hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
 #endif	
     skb_queue_tail(&lp->sndq, skb);
     if (kickflag) {
@@ -320,21 +316,6 @@
     dev_kfree_skb(skb, FREE_WRITE);
 }
 
-static void pt_loopback(struct pt_local *lp, int onoff)
-{
-    if (lp->base & CHANA) {
-        if (onoff == ON)
-            outb_p(pt_sercfg |= PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
-        else
-            outb_p(pt_sercfg &= ~PT_LOOPA_ON, lp->cardbase + SERIAL_CFG);
-    } else {    /* it's channel B */
-        if (onoff == ON)
-            outb_p(pt_sercfg |= PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
-        else
-            outb_p(pt_sercfg &= ~PT_LOOPB_ON, lp->cardbase + SERIAL_CFG);
-    }
-} /*pt_loopback */
-
 /* Fill in the MAC-level header */
 static int pt_header (struct sk_buff *skb, struct device *dev, unsigned short type,
 		void *daddr, void *saddr, unsigned len)
@@ -366,7 +347,7 @@
     int tc, br;
 
 #ifdef PT_DEBUG
-	printk("PTd scc_init(): (%d).\n", lp->base & CHANA);
+	printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA);
 #endif	
     save_flags(flags);
     cli();
@@ -486,8 +467,8 @@
 
 	struct pt_local *lp = (struct pt_local*) dev->priv;
 #ifdef PT_DEBUG
-	printk("PTd chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
-	printk("PTd chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);	
+	printk(KERN_DEBUG "PT: chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
+	printk(KERN_DEBUG "PT: chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);	
 #endif
 	/* Reset SCC if both channels are to be canned */
 	if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) ||
@@ -498,7 +479,7 @@
 	        outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG);
 	        outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG);
 #ifdef PT_DEBUG
-		printk("PTd chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
+		printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
 #endif			        
 	}
 	/* Reset individual channel */
@@ -522,20 +503,20 @@
     { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
       0x2b0, 0x300, 0x330, 0x3f0,  0};
 
-    printk(version);
+    printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n");
 
     for (port = &ports[0]; *port && !card_type; port++) {
         ioaddr = *port;
 
         if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) {
-            printk("PT: Probing for card at address %#3x\n", ioaddr);
+            printk(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr);
             card_type = hw_probe(ioaddr);
         }
     }
     if (card_type) {
-        printk("PT: Found a PT at address %#3x\n",ioaddr);
+        printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr);
     } else {
-        printk("PT: ERROR: No card found.\n");
+        printk(KERN_ERR "PT: ERROR: No card found.\n");
         return -EIO;
     }
 
@@ -641,7 +622,7 @@
 	long br;
 	int cmd = lp->base + CTL;
 #ifdef PT_DEBUG
-	printk("PTd pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
 #endif			
 	if (x == ON) {
 	    /* Ex ints off to avoid int */
@@ -738,8 +719,9 @@
         return 0;
 }
 
-static int pt_set_mac_address(struct device *dev, struct sockaddr *sa)
+static int pt_set_mac_address(struct device *dev, void *addr)
 {
+	struct sockaddr *sa = (struct sockaddr *)addr;
 	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);		/* addr is an AX.25 shifted ASCII */
 	return 0;		/* mac address */
 }
@@ -857,11 +839,11 @@
             restore_flags(flags);
 
             if (!dev->irq) {
-                printk("PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
+                printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
             }
         }
 
-        printk("PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
+        printk(KERN_INFO "PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
 
         /* This board has jumpered interrupts. Snarf the interrupt vector
          * now.  There is no point in waiting since no other device can use
@@ -870,7 +852,7 @@
         {
             int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", NULL);
             if (irqval) {
-                printk("PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
+                printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
                     dev->irq, irqval);
                 return EAGAIN;
             }
@@ -903,10 +885,13 @@
     /* New style flags */
     dev->flags = 0;
     dev->family = AF_INET;
-    dev->pa_addr = 0;
-    dev->pa_brdaddr = 0;
-    dev->pa_mask = 0;
-    dev->pa_alen = sizeof(unsigned long);
+
+#ifdef CONFIG_INET
+    dev->pa_addr    = in_aton("192.168.0.1");
+    dev->pa_brdaddr = in_aton("192.168.0.255");
+    dev->pa_mask    = in_aton("255.255.255.0");
+    dev->pa_alen    = 4;
+#endif
 
     return 0;
 } /* pt_probe() */
@@ -965,6 +950,8 @@
     dev->interrupt = 0;
     dev->start = 1;
     first_time = 0;
+
+    MOD_INC_USE_COUNT;
 	
     return 0;
 } /* pt_open() */
@@ -974,7 +961,7 @@
 	struct pt_local *lp = (struct pt_local *) dev->priv;
 
 #ifdef PT_DEBUG
-	printk("PTd pt_send_packet(): (%d)\n", lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA);
 #endif			
 	/* If some higher layer thinks we've missed an tx-done interrupt
 	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
@@ -1019,8 +1006,10 @@
 	restore_flags(flags);
 	
 #ifdef PT_DEBUG
-	printk("PTd pt_close(): Closing down channel (%d).\n", lp->base & CHANA);	
+	printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA);	
 #endif	
+
+	MOD_DEC_USE_COUNT;
 	
 	return 0;
 } /* pt_close() */
@@ -1153,7 +1142,7 @@
 	cmd = lp->base + CTL;
 
 #ifdef PT_DEBUG
-	printk("PTd pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
 #endif	
 	
 	switch (lp->tstate) 
@@ -1253,7 +1242,7 @@
 	   restore_flags(flags);
 	   return;
 	default:
-		printk("PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
+		printk(KERN_ERR "PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
 		pt_rts(lp, OFF);
 		lp->tstate = IDLE;
 		break;		
@@ -1270,7 +1259,7 @@
     char rse;
     struct sk_buff *skb;
     int sksize, pkt_len;
-    struct mbuf *cur_buf;
+    struct mbuf *cur_buf = NULL;
     unsigned char *cfix;
 
     save_flags(flags);
@@ -1280,7 +1269,7 @@
     rse = rdscc(lp->cardbase, cmd, R1);
     
 #ifdef PT_DEBUG
-    printk("PTd pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
+    printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
 #endif        
 
 	if (lp->dmachan && (rse & Rx_OVR))
@@ -1333,7 +1322,7 @@
      if (rse & END_FR)
      {
 #ifdef PT_DEBUG
-	printk("PTd pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
+	printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
 #endif	     
 		if (lp->dmachan)
 		{
@@ -1371,7 +1360,7 @@
         	         
         	     }
 #ifdef PT_DEBUG
-	printk("PTd pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
+	printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
 #endif                 
              } else {
                  /* We have a valid frame */
@@ -1392,7 +1381,7 @@
                  skb = dev_alloc_skb(sksize);
                  if (skb == NULL)
                  {
-                     printk("PT: %s: Memory squeeze, dropping packet.\n", dev->name);
+                     printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name);
                      lp->stats.rx_dropped++;
                      restore_flags(flags);
                      return;
@@ -1428,15 +1417,6 @@
      restore_flags(flags);
 } /* pt_rxisr() */
 
-/* Read the SCC channel till no more data in receiver */
-static void empty_scc(struct pt_local *lp)
-{
-    while( rdscc(lp->cardbase, lp->base + CTL, R0) & Rx_CH_AV) {
-        /* Get data from Rx buffer and toss it */
-        (void) inb_p(lp->base + DATA);
-    }
-} /* empty_scc()*/
-
 /*
  * This handles the two timer interrupts.
  * This is a real bugger, cause you have to rip it out of the pi's
@@ -1447,7 +1427,7 @@
     unsigned long flags;
 
 #ifdef PT_DEBUG
-	printk("PTd pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
 #endif		
 	
     save_flags(flags);
@@ -1467,9 +1447,9 @@
       
     default:
 	if (lp->base & CHANA)
- 	    printk("PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
+ 	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
 	else
-	    printk("PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
+	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
 	break;		
     } /* end switch */
     restore_flags(flags);
@@ -1498,11 +1478,11 @@
         {
         	/* Read interrupt vector from R2, channel B */
 #ifdef PT_DEBUG
-		printk("PTd pt_interrupt(): R3 = %#3x", st);
+		printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st);
 #endif		        
 /*        	st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/
 #ifdef PT_DEBUG
-		printk(" R2 = %#3x.\n", st);
+		printk(KERN_DEBUG "PI: R2 = %#3x.\n", st);
 #endif	
 			if (st & CHARxIP) {
 			    /* Channel A Rx */
@@ -1573,7 +1553,7 @@
     st = rdscc(lp->cardbase, cmd, R0);
 
 #ifdef PT_DEBUG
-	printk("PTd exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
+	printk(KERN_DEBUG "PT: exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
 #endif	
     /* Reset external status latch */
     wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
@@ -1588,7 +1568,7 @@
     {
     case ACTIVE:		/* Unexpected underrun */
 #ifdef PT_DEBUG
-	printk("PTd exisr(): unexpected underrun detected.\n");
+	printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n");
 #endif	    
         free_p(lp->sndbuf);
         lp->sndbuf = NULL;
@@ -1746,7 +1726,7 @@
     if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) )
     {
 #ifdef PT_DEBUG
-	printk("PTd exisr(): abort detected.\n");
+	printk(KERN_DEBUG "PT: exisr(): abort detected.\n");
 #endif        
   		/* read and dump all of SCC Rx FIFO */
         (void) rdscc(lp->cardbase, cmd, R8);
@@ -1765,7 +1745,7 @@
     if ( (st & DCD) != (lp->saved_RR0 & DCD))
     {
 #ifdef PT_DEBUG    
-        printk("PTd: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
+        printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
 #endif
 		if (st & DCD)
 		{
@@ -1773,7 +1753,7 @@
 			if (lp->rcvbuf->cnt > 0)
 			{
 #ifdef PT_DEBUG
-				printk("PTd pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
+				printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
 #endif					
 				/* wind back buffers */
 				lp->rcp = lp->rcvbuf->data;
@@ -1801,30 +1781,25 @@
 
 } /* pt_exisr() */
 
-/* This function is used to send the KISS params back to the kernel itself,
- * just like the TNCs do (I think)
- * It's a (bit of a) kludge
- */
-static void send_kiss(struct device *dev, unsigned char arg, unsigned char val)
+#ifdef MODULE
+int init_module(void)
 {
-	struct sk_buff *skb;
-	unsigned char *cfix;
-/*	struct pt_local *lp = (struct pt_local*)dev->priv;*/
-	
-	
-	skb = dev_alloc_skb(2);
-	if (skb == NULL)
-	{
-		printk("PT: send_kiss(): Memory squeeze, dropping KISS reply.\n");
-		return;
-	}
-	skb->dev = dev;
-	cfix = skb_put(skb, 2);
-	cfix[0]=arg;
-	cfix[1]=val;
-	skb->protocol=htons(ETH_P_AX25);
-	skb->mac.raw=skb->data;
-	IS_SKB(skb);
-	netif_rx(skb);
+    register_symtab(NULL);
+    return pt_init();
 }
-	
+
+void cleanup_module(void)
+{
+    free_irq(pt0a.irq, NULL);	/* IRQs and IO Ports are shared */
+    release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE);
+    irq2dev_map[pt0a.irq] = NULL;
+
+    kfree(pt0a.priv);
+    pt0a.priv = NULL;
+    unregister_netdev(&pt0a);
+    
+    kfree(pt0b.priv);
+    pt0b.priv = NULL;
+    unregister_netdev(&pt0b);
+}
+#endif
diff -u --recursive --new-file v2.0.34/linux/drivers/net/pt.h linux/drivers/net/pt.h
--- v2.0.34/linux/drivers/net/pt.h	Mon Dec 25 20:03:00 1995
+++ linux/drivers/net/pt.h	Mon Jul 13 13:47:31 1998
@@ -1,176 +0,0 @@
-/*
- * pt.h: Linux device driver for the Gracilis PackeTwin
- * Copyright (C) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
- *
- * Please read the notice appearing at the top of the file pt.c
- */
-#define DMA_BUFF_SIZE 2200
-
-/* Network statistics, with the same names as 'struct enet_statistics'. */
-#define netstats enet_statistics
-
-#define ON 1
-#define OFF 0
-
-
-/* Register offset info, specific to the PT
- * E.g., to read the data port on channel A, use
- *  inportb(pichan[dev].base + CHANA + DATA)
- */
-#define CHANB   0   /* Base of channel B regs */
-#define CHANA   2   /* Base of channel A regs */
-
-/* 8530 ports on each channel */
-#define CTL 0
-#define DATA    1
-
-#define DMAEN   0x8 /* Offset off DMA Enable register */
-
-/* Timer chip offsets */
-#define TMR0    0x4 /* Offset of timer 0 register */
-#define TMR1    0x5 /* Offset of timer 1 register */
-#define TMR2    0x6 /* Offset of timer 2 register */
-#define TMRCMD  0x7 /* Offset of timer command register */
-#define INT_REG	0x8
-#define TMR1CLR 0x9
-#define TMR2CLR 0xa
-
-/* Interrupt register equates */
-#define PT_SCC_MSK	0x1
-#define PT_TMR1_MSK	0x2
-#define PT_TMR2_MSK	0x4
-
-/* Serial/interrupt register equates */
-#define PT_DTRA_ON	0x1
-#define PT_DTRB_ON	0x2
-#define PT_EXTCLKA	0x4
-#define PT_EXTCLKB	0x8
-#define PT_LOOPA_ON	0x10
-#define PT_LOOPB_ON	0x20
-#define PT_EI		0x80
-
-/* Timer chip equates */
-#define SC0 0x00 /* Select counter 0 */
-#define SC1 0x40 /* Select counter 1 */
-#define SC2 0x80 /* Select counter 2 */
-#define CLATCH  0x00 /* Counter latching operation */
-#define MSB 0x20 /* Read/load MSB only */
-#define LSB 0x10 /* Read/load LSB only */
-#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
-#define MODE0   0x00 /* Interrupt on terminal count */
-#define MODE1   0x02 /* Programmable one shot */
-#define MODE2   0x04 /* Rate generator */
-#define MODE3   0x06 /* Square wave rate generator */
-#define MODE4   0x08 /* Software triggered strobe */
-#define MODE5   0x0a /* Hardware triggered strobe */
-#define BCD 0x01 /* BCD counter */
-
-/* DMA controller registers */
-#define DMA_STAT    8   /* DMA controller status register */
-#define DMA_CMD     8   /* DMA controller command register */
-#define DMA_MASK        10  /* DMA controller mask register */
-#define DMA_MODE        11  /* DMA controller mode register */
-#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
-/* DMA data */
-#define DMA_DISABLE (0x04)  /* Disable channel n */
-#define DMA_ENABLE  (0x00)  /* Enable channel n */
-/* Single transfers, incr. address, auto init, writes, ch. n */
-#define DMA_RX_MODE (0x54)
-/* Single transfers, incr. address, no auto init, reads, ch. n */
-#define DMA_TX_MODE (0x48)
-
-/* Write registers */
-#define DMA_CFG		0x08
-#define SERIAL_CFG	0x09
-#define INT_CFG		0x09	/* shares with serial config */
-#define DMA_CLR_FF	0x0a
-
-#define SINGLE 3686400
-#define DOUBLE 7372800
-#define XTAL   ((long) 6144000L)
-
-#define SIOCGPIPARAM		0x5000	/* get PI parameters */
-#define SIOCSPIPARAM		0x5001	/* set */
-#define SIOCGPIBAUD		0x5002	/* get only baud rate */
-#define SIOCSPIBAUD		0x5003	
-#define SIOCGPIDMA		0x5004	/* get only DMA */
-#define SIOCSPIDMA		0x5005	
-#define SIOCGPIIRQ		0x5006	/* get only IRQ */
-#define SIOCSPIIRQ		0x5007	
-
-struct pt_req  {
-    int cmd;
-    int speed;
-    int clockmode;
-    int txdelay;
-    unsigned char persist;
-    int slotime; 
-    int squeldelay;
-    int dmachan;    
-    int irq;    
-};
-
-/* SCC Interrupt vectors, if we have set 'status low' */
-#define CHBTxIV		0x00
-#define CHBEXTIV	0x02
-#define CHBRxIV		0x04
-#define CHBSRCIV	0x06
-#define CHATxIV		0x08
-#define CHAEXTIV	0x0a
-#define CHARxIV		0x0c
-#define CHASRCIV	0x0e
-
-
-#ifdef __KERNEL__
-
-/* Information that needs to be kept for each channel. */
-struct pt_local {
-    struct netstats stats; /* %%%dp*/
-    long open_time;             /* Useless example local info. */
-    unsigned long xtal; 
-
-    struct mbuf *rcvbuf;/* Buffer for current rx packet */
-    struct mbuf *rxdmabuf1; /* DMA rx buffer */
-    struct mbuf *rxdmabuf2; /* DMA rx buffer */
-
-    int bufsiz;         /* Size of rcvbuf */
-    char *rcp;          /* Pointer into rcvbuf */
-
-    struct sk_buff_head sndq;  /* Packets awaiting transmission */
-    int sndcnt;         /* Number of packets on sndq */
-    struct sk_buff *sndbuf;/* Current buffer being transmitted */
-    char *txdmabuf;     /* Transmit DMA buffer */
-	char *txptr;		/* Used by B port tx */
-	int txcnt;			
-    char tstate;        /* Transmitter state */
-#define IDLE    0       /* Transmitter off, no data pending */
-#define ACTIVE  1       /* Transmitter on, sending data */
-#define UNDERRUN 2      /* Transmitter on, flushing CRC */
-#define FLAGOUT 3       /* CRC sent - attempt to start next frame */
-#define DEFER 4         /* Receive Active - DEFER Transmit */
-#define ST_TXDELAY 5    /* Sending leading flags */
-#define CRCOUT 6
-    char rstate;        /* Set when !DCD goes to 0 (TRUE) */
-/* Normal state is ACTIVE if Receive enabled */
-#define RXERROR 2       /* Error -- Aborting current Frame */
-#define RXABORT 3       /* ABORT sequence detected */
-#define TOOBIG 4        /* too large a frame to store */
-	
-    int dev;            /* Device number */
-    int base;       /* Base of I/O registers */
-    int cardbase;     /* Base address of card */
-    int stata;        /* address of Channel A status regs */
-    int statb;        /* address of Channel B status regs */
-    int speed;        /* Line speed, bps */
-    int clockmode;    /* tapr 9600 modem clocking option */
-    int txdelay;      /* Transmit Delay 10 ms/cnt */
-    unsigned char persist;       /* Persistence (0-255) as a % */
-    int slotime;      /* Delay to wait on persistence hit */
-    int squeldelay;   /* Delay after XMTR OFF for squelch tail */
-    struct iface *iface;    /* Associated interface */
-    int dmachan;           /* DMA channel for this port */
-    char saved_RR0;	/* The saved version of RR) that we compare with */
-    int nrzi;			/* Do we use NRZI (or NRZ) */
-};
-
-#endif
diff -u --recursive --new-file v2.0.34/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.0.34/linux/drivers/net/slip.c	Sun May 12 21:17:23 1996
+++ linux/drivers/net/slip.c	Mon Jul 13 13:47:31 1998
@@ -66,10 +66,6 @@
 #include <linux/tty.h>
 #include <linux/errno.h>
 #include <linux/netdevice.h>
-#ifdef CONFIG_AX25
-#include <linux/timer.h>
-#include <net/ax25.h>
-#endif
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
@@ -294,11 +290,8 @@
 			set_bit(SLF_ERROR, &sl->flags);
 		}
 	}
-#ifdef CONFIG_AX25
-	sl->mtu      = dev->mtu + 73;
-#else
 	sl->mtu      = dev->mtu;
-#endif
+	
 	sl->buffsize = len;
 
 	restore_flags(flags);
@@ -386,10 +379,7 @@
 	skb->dev = sl->dev;
 	memcpy(skb_put(skb,count), sl->rbuff, count);
 	skb->mac.raw=skb->data;
-	if(sl->mode & SL_MODE_AX25)
-		skb->protocol=htons(ETH_P_AX25);
-	else
-		skb->protocol=htons(ETH_P_IP);
+	skb->protocol=htons(ETH_P_IP);
 	netif_rx(skb);
 	sl->rx_packets++;
 }
@@ -402,11 +392,8 @@
 	int actual, count;
 
 
-#ifdef CONFIG_AX25
-	if (sl->mtu != sl->dev->mtu + 73) {	/* Someone has been ifconfigging */
-#else
 	if (sl->mtu != sl->dev->mtu) {	/* Someone has been ifconfigging */
-#endif
+
 		sl_changedmtu(sl);
 	}
 
@@ -535,15 +522,6 @@
 sl_header(struct sk_buff *skb, struct device *dev, unsigned short type,
 	  void *daddr, void *saddr, unsigned len)
 {
-#ifdef CONFIG_AX25
-#ifdef CONFIG_INET
-	struct slip *sl = (struct slip*)(dev->priv);
-
-	if (sl->mode & SL_MODE_AX25 && type != htons(ETH_P_AX25))  {
-		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
-	}
-#endif
-#endif
 	return 0;
 }
 
@@ -553,15 +531,6 @@
 sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
 		  struct sk_buff *skb)
 {
-#ifdef CONFIG_AX25
-#ifdef CONFIG_INET
-	struct slip *sl = (struct slip*)(dev->priv);
-
-	if (sl->mode & SL_MODE_AX25) {
-		return ax25_rebuild_header(buff, dev, raddr, skb);
-	}
-#endif
-#endif
 	return 0;
 }
 
@@ -611,12 +580,7 @@
 		goto noslcomp;
 	}
 #endif
-
-#ifdef CONFIG_AX25
-	sl->mtu	     = dev->mtu + 73;
-#else
 	sl->mtu	     = dev->mtu;
-#endif
 	sl->buffsize = len;
 	sl->rcount   = 0;
 	sl->xleft    = 0;
@@ -701,11 +665,8 @@
 	 * Argh! mtu change time! - costs us the packet part received
 	 * at the change
 	 */
-#ifdef CONFIG_AX25
-	if (sl->mtu != sl->dev->mtu + 73)  {
-#else
 	if (sl->mtu != sl->dev->mtu)  {
-#endif
+
 		sl_changedmtu(sl);
 	}
 
@@ -762,11 +723,6 @@
 	/* Restore default settings */
 	sl->mode      = SL_MODE_DEFAULT;
 	sl->dev->type = ARPHRD_SLIP + sl->mode;
-#ifdef CONFIG_AX25	
-	if (sl->dev->type == 260) {		/* KISS */
-		sl->dev->type = ARPHRD_AX25;
-	}
-#endif	
 	/* Perform the low-level SLIP initialization. */
 	if ((err = sl_open(sl->dev)))  {
 		return err;
@@ -1008,31 +964,6 @@
 }
 #endif /* CONFIG_SLIP_MODE_SLIP6 */
 
-#ifdef CONFIG_AX25
-int
-sl_set_mac_address(struct device *dev, void *addr)
-{
-	int err;
-
-	err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN);
-	if (err)  {
-		return err;
-	}
-
-	memcpy_fromfs(dev->dev_addr, addr, AX25_ADDR_LEN);	/* addr is an AX.25 shifted ASCII mac address */
-
-	return 0;
-}
-
-static int
-sl_set_dev_mac_address(struct device *dev, void *addr)
-{
-	struct sockaddr *sa=addr;
-	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
-	return 0;
-}
-#endif /* CONFIG_AX25 */
-
 
 /* Perform I/O control on an active SLIP channel. */
 static int
@@ -1086,34 +1017,12 @@
 			return -EINVAL;
 		}
 #endif
-#ifndef CONFIG_AX25
-		if (tmp & SL_MODE_AX25) {
-			return -EINVAL;
-		}
-#else
-		if (tmp & SL_MODE_AX25) {
-			sl->dev->addr_len=AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
-			sl->dev->hard_header_len=AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
-		} else	{
-			sl->dev->addr_len=0;	/* No mac addr in slip mode */
-			sl->dev->hard_header_len=0;
-		}
-#endif
 		sl->mode = tmp;
 		sl->dev->type = ARPHRD_SLIP+sl->mode;
-#ifdef CONFIG_AX25		
-		if (sl->dev->type == 260)  {
-			sl->dev->type = ARPHRD_AX25;
-		}
-#endif		
 		return 0;
 
 	 case SIOCSIFHWADDR:
-#ifdef CONFIG_AX25
-		return sl_set_mac_address(sl->dev, arg);
-#else
 		return -EINVAL;
-#endif
 
 #ifdef CONFIG_SLIP_SMART
 	/* VSV changes start here */
@@ -1207,9 +1116,6 @@
 #if defined(SL_INCLUDE_CSLIP) && !defined(MODULE)
 	printk("CSLIP: code copyright 1989 Regents of the University of California.\n");
 #endif
-#ifdef CONFIG_AX25
-	printk(KERN_INFO "AX25: KISS encapsulation enabled.\n");
-#endif
 #ifdef CONFIG_SLIP_SMART
 	printk(KERN_INFO "SLIP linefill/keepalive option.\n");
 #endif	
@@ -1260,12 +1166,6 @@
 {
 	struct slip *sl = (struct slip*)(dev->priv);
 	int i;
-#ifdef CONFIG_AX25
-	static char ax25_bcast[AX25_ADDR_LEN] =
-		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
-	static char ax25_test[AX25_ADDR_LEN] =
-		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
-#endif
 
 	if (sl == NULL)		/* Allocation failed ?? */
 	  return -ENODEV;
@@ -1283,22 +1183,10 @@
 	dev->stop		= sl_close;
 	dev->hard_header	= sl_header;
 	dev->get_stats	        = sl_get_stats;
-#ifdef HAVE_SET_MAC_ADDR
-#ifdef CONFIG_AX25
-	dev->set_mac_address    = sl_set_dev_mac_address;
-#endif
-#endif
 	dev->hard_header_len	= 0;
 	dev->addr_len		= 0;
 	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
 	dev->tx_queue_len	= 10;
-#ifdef CONFIG_AX25
-	if (sl->dev->type == 260) {
-		sl->dev->type = ARPHRD_AX25;
-	}
-	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);	/* Only activated in AX.25 mode */
-	memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);	/*    ""      ""       ""    "" */
-#endif
 	dev->rebuild_header	= sl_rebuild_header;
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)  {
diff -u --recursive --new-file v2.0.34/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v2.0.34/linux/drivers/net/smc-ultra.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/smc-ultra.c	Mon Jul 13 13:47:31 1998
@@ -75,13 +75,13 @@
 static void ultra_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void ultra_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, const int start_page);
 static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						int ring_page);
 static void ultra_pio_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void ultra_pio_output(struct device *dev, int count,
-							 const unsigned char *buf, const start_page);
+							 const unsigned char *buf, const int start_page);
 static int ultra_close_card(struct device *dev);
 
 
@@ -385,7 +385,7 @@
 }
 
 static void ultra_pio_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page)
+							const unsigned char *buf, const int start_page)
 {
 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
 	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/smc-ultra32.c linux/drivers/net/smc-ultra32.c
--- v2.0.34/linux/drivers/net/smc-ultra32.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/smc-ultra32.c	Mon Jul 13 13:47:31 1998
@@ -68,7 +68,7 @@
 static void ultra32_block_input(struct device *dev, int count,
 				struct sk_buff *skb, int ring_offset);
 static void ultra32_block_output(struct device *dev, int count,
-				 const unsigned char *buf, const start_page);
+				 const unsigned char *buf, const int start_page);
 static int ultra32_close(struct device *dev);
 
 #define ULTRA32_CMDREG	0	/* Offset to ASIC command register. */
@@ -238,9 +238,8 @@
 static int ultra32_open(struct device *dev)
 {
 	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
-	int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : SA_SHIRQ;
 
-	if (request_irq(dev->irq, ei_interrupt, irq_flags, ei_status.name, dev))
+	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev))
 		return -EAGAIN;
 
 	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
@@ -268,7 +267,7 @@
 
 	outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */
 	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
-	free_irq(dev->irq, NULL);
+	free_irq(dev->irq, dev);
 	irq2dev_map[dev->irq] = 0;
 
 	NS8390_init(dev, 0);
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/Makefile linux/drivers/net/soundmodem/Makefile
--- v2.0.34/linux/drivers/net/soundmodem/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/Makefile	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,60 @@
+#
+# Makefile for the soundmodem device driver.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET	:= soundmodem.o
+
+O_OBJS		:= sm.o
+ifeq ($(CONFIG_SOUNDMODEM_SBC),y)
+O_OBJS		+= sm_sbc.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_WSS),y)
+O_OBJS		+= sm_wss.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y)
+O_OBJS		+= sm_afsk1200.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_7),y)
+O_OBJS		+= sm_afsk2400_7.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_8),y)
+O_OBJS		+= sm_afsk2400_8.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y)
+O_OBJS		+= sm_afsk2666.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_HAPN4800),y)
+O_OBJS		+= sm_hapn4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_PSK4800),y)
+O_OBJS		+= sm_psk4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_FSK9600),y)
+O_OBJS		+= sm_fsk9600.o
+endif
+
+M_OBJS		:= $(O_TARGET)
+
+all:		all_targets
+.PHONY:		all
+
+gentbl:		gentbl.c
+		$(HOSTCC) -Wall $< -o $@ -lm
+
+TBLHDR		:= sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h 
+TBLHDR		+= sm_tbl_afsk2666.h sm_tbl_psk4800.h
+TBLHDR		+= sm_tbl_hapn4800.h sm_tbl_fsk9600.h
+
+$(TBLHDR):	gentbl
+		./gentbl
+
+fastdep:	$(TBLHDR)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/gentbl.c linux/drivers/net/soundmodem/gentbl.c
--- v2.0.34/linux/drivers/net/soundmodem/gentbl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/gentbl.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,688 @@
+/*****************************************************************************/
+
+/*
+ *      gentbl.c  -- soundcard radio modem driver table generator.
+ *
+ *      Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_offscostab(FILE *f, unsigned int nbits)
+{
+	int i;
+
+	fprintf(f, "\n/*\n * small cosine table in U8 format\n */\n"
+		"#define OFFSCOSTABBITS %u\n"
+		"#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)\n\n", 
+		nbits);
+	fprintf(f, "static unsigned char offscostab[OFFSCOSTABSIZE] = {\n\t");
+	for (i = 0; i < (1<<nbits); i++) {
+		fprintf(f, "%4u", (int)
+			(128+127.0*cos(i*2.0*M_PI/(1<<nbits))));
+		if (i < (1<<nbits)-1) 
+			fprintf(f, "%s", (i & 7) == 7 ? ",\n\t" : ",");
+	}
+	fprintf(f, "\n};\n\n"
+		"#define OFFSCOS(x) offscostab[((x)>>%d)&0x%x]\n\n",
+		16-nbits, (1<<nbits)-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_costab(FILE *f, unsigned int nbits)
+{
+        int i;
+
+	fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n"
+		"static const short costab[%d] = {", (1<<nbits));
+        for (i = 0; i < (1<<nbits); i++) {
+                if (!(i & 7))
+                        fprintf(f, "\n\t");
+                fprintf(f, "%6d", (int)(32767.0*cos(i*2.0*M_PI/(1<<nbits))));
+                if (i != ((1<<nbits)-1))
+                        fprintf(f, ", ");
+        }
+        fprintf(f, "\n};\n\n#define COS(x) costab[((x)>>%d)&0x%x]\n"
+		"#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits,
+		(1<<nbits)-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK12_SAMPLE_RATE 9600
+#define AFSK12_TX_FREQ_LO 1200
+#define AFSK12_TX_FREQ_HI 2200
+#define AFSK12_CORRLEN 8
+
+static void gentbl_afsk1200(FILE *f)
+{
+        int i, v, sum;
+
+#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
+#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
+
+	fprintf(f, "\n/*\n * afsk1200 specific tables\n */\n"
+		"#define AFSK12_SAMPLE_RATE %u\n"
+		"#define AFSK12_TX_FREQ_LO %u\n"
+		"#define AFSK12_TX_FREQ_HI %u\n"
+		"#define AFSK12_CORRLEN %u\n\n",
+		AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO, 
+		AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
+	fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t");
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGLO(i)));
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_I %d\n\n"
+		"static const int afsk12_tx_lo_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+		sum += (v = 127.0*sin(ARGLO(i))); 
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %d\n\n"
+		"static const int afsk12_tx_hi_i[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGHI(i))); 
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %d\n\n"
+		"static const int afsk12_tx_hi_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++)  {
+		sum += (v = 127.0*sin(ARGHI(i)));
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+	fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum);
+#undef ARGLO
+#undef ARGHI
+}
+
+/* -------------------------------------------------------------------- */
+
+static const float fsk96_tx_coeff_4[32] = {
+              -0.001152,        0.000554,        0.002698,        0.002753,
+              -0.002033,       -0.008861,       -0.008002,        0.006607,
+               0.023727,        0.018905,       -0.018056,       -0.057957,
+              -0.044368,        0.055683,        0.207667,        0.322048,
+               0.322048,        0.207667,        0.055683,       -0.044368,
+              -0.057957,       -0.018056,        0.018905,        0.023727,
+               0.006607,       -0.008002,       -0.008861,       -0.002033,
+               0.002753,        0.002698,        0.000554,       -0.001152
+};
+
+static const float fsk96_tx_coeff_5[40] = {
+              -0.001009,       -0.000048,        0.001376,        0.002547,
+               0.002061,       -0.001103,       -0.005795,       -0.008170,
+              -0.004017,        0.006924,        0.018225,        0.019238,
+               0.002925,       -0.025777,       -0.048064,       -0.039683,
+               0.013760,        0.104144,        0.200355,        0.262346,
+               0.262346,        0.200355,        0.104144,        0.013760,
+              -0.039683,       -0.048064,       -0.025777,        0.002925,
+               0.019238,        0.018225,        0.006924,       -0.004017,
+              -0.008170,       -0.005795,       -0.001103,        0.002061,
+               0.002547,        0.001376,       -0.000048,       -0.001009
+};
+
+#define HAMMING(x) (0.54-0.46*cos(2*M_PI*(x)));
+
+static inline float hamming(float x)
+{
+	return 0.54-0.46*cos(2*M_PI*x);
+}
+
+static inline float sinc(float x)
+{
+	if (x == 0)
+		return 1;
+	x *= M_PI;
+	return sin(x)/x;
+}
+
+static void gentbl_fsk9600(FILE *f)
+{
+        int i, j, k, l, m;
+	float s;
+	float c[44];
+	float min, max;
+
+	fprintf(f, "\n/*\n * fsk9600 specific tables\n */\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+#if 0
+	memcpy(c+2, fsk96_tx_coeff_4, sizeof(fsk96_tx_coeff_4));
+#else
+	for (i = 0; i < 29; i++)
+		c[3+i] = sinc(1.2*((i-14.0)/4.0))*hamming(i/28.0)/3.5;
+#endif
+	fprintf(f, "static unsigned char fsk96_txfilt_4[] = {\n\t");
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 256; j++) {
+			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+				if (j & k) {
+					for (m = 0; m < 4; m++, l++)
+						s += c[l];
+				} else {
+					for (m = 0; m < 4; m++, l++)
+						s -= c[l];
+				}
+			}
+			s *= 0.75;
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 3 || j < 255)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+#ifdef VERBOSE
+	fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max);
+#endif
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+#if 0
+	memcpy(c+2, fsk96_tx_coeff_5, sizeof(fsk96_tx_coeff_5));
+#else
+	for (i = 0; i < 36; i++)
+		c[4+i] = sinc(1.2*((i-17.5)/5.0))*hamming(i/35.0)/4.5;
+#endif
+	fprintf(f, "static unsigned char fsk96_txfilt_5[] = {\n\t");
+	for (i = 0; i < 5; i++) {
+		for (j = 0; j < 256; j++) {
+			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+				if (j & k) {
+					for (m = 0; m < 5; m++, l++)
+						s += c[l];
+				} else {
+					for (m = 0; m < 5; m++, l++)
+						s -= c[l];
+				}
+			}
+			s *= 0.75;
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 4 || j < 255)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+#ifdef VERBOSE
+	fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max);
+#endif
+	fprintf(f, "\n};\n\n");
+}
+	
+/* -------------------------------------------------------------------- */
+
+#define AFSK26_SAMPLERATE  16000
+
+#define AFSK26_NUMCAR      2
+#define AFSK26_FIRSTCAR    2000
+#define AFSK26_MSK_LEN     6
+#define AFSK26_RXOVER      2
+
+#define AFSK26_DEMCORRLEN (2*AFSK26_MSK_LEN)
+
+#define AFSK26_WINDOW(x) ((1-cos(2.0*M_PI*(x)))/2.0)
+
+#define AFSK26_AMPL(x) (((x)?1.0:0.7))
+
+#undef AFSK26_AMPL
+#define AFSK26_AMPL(x) 1
+
+static void gentbl_afsk2666(FILE *f)
+{
+        int i, j, k, l, o, v, sumi, sumq;
+        float window[AFSK26_DEMCORRLEN*AFSK26_RXOVER];
+	int cfreq[AFSK26_NUMCAR];
+
+	fprintf(f, "\n/*\n * afsk2666 specific tables\n */\n"
+		"#define AFSK26_DEMCORRLEN %d\n"
+		"#define AFSK26_SAMPLERATE %d\n\n", AFSK26_DEMCORRLEN,
+		AFSK26_SAMPLERATE);
+	fprintf(f, "static const unsigned int afsk26_carfreq[%d] = { ",
+		AFSK26_NUMCAR);
+	for (i = 0; i < AFSK26_NUMCAR; i++) {
+		cfreq[i] = 0x10000*AFSK26_FIRSTCAR/AFSK26_SAMPLERATE+
+			0x10000*i/AFSK26_MSK_LEN/2;
+		fprintf(f, "0x%x", cfreq[i]);
+		if (i < AFSK26_NUMCAR-1)
+			fprintf(f, ", ");
+	}
+	fprintf(f, " };\n\n");
+        for (i = 0; i < AFSK26_DEMCORRLEN*AFSK26_RXOVER; i++)
+                window[i] = AFSK26_WINDOW(((float)i)/(AFSK26_DEMCORRLEN*
+						      AFSK26_RXOVER)) * 127.0;
+        fprintf(f, "\nstatic const struct {\n\t"
+               "int i[%d];\n\tint q[%d];\n} afsk26_dem_tables[%d][%d] = {\n", 
+               AFSK26_DEMCORRLEN, AFSK26_DEMCORRLEN, AFSK26_RXOVER, AFSK26_NUMCAR);
+        for (o = AFSK26_RXOVER-1; o >= 0; o--) {
+                fprintf(f, "\t{\n");
+                for (i = 0; i < AFSK26_NUMCAR; i++) {
+                        j = cfreq[i];
+                        fprintf(f, "\t\t{{ ");
+                        for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumi = 0; l >= 0; 
+                             l--, k = (k+j)&0xffffu) {
+				sumi += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
+					 cos(M_PI*k/32768.0));
+                                fprintf(f, "%6d%s", v, l ? ", " : " }, { ");
+			}
+                        for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumq = 0; l >= 0; 
+                             l--, k = (k+j)&0xffffu) {
+				sumq += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
+					 sin(M_PI*k/32768.0));
+                                fprintf(f, "%6d%s", v, l ? ", " : " }}");
+			}
+                        if (i < 1)
+                                fprintf(f, ",");
+                        fprintf(f, "\n#define AFSK26_DEM_SUM_I_%d_%d %d\n"
+				"#define AFSK26_DEM_SUM_Q_%d_%d %d\n", 
+				AFSK26_RXOVER-1-o, i, sumi, AFSK26_RXOVER-1-o, i, sumq);
+                }
+                fprintf(f, "\t}%s\n", o ? "," : "");
+        }
+        fprintf(f, "};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+#define ATAN_TABLEN 1024
+
+static void gentbl_atantab(FILE *f)
+{
+        int i;
+        short x;
+
+	fprintf(f, "\n/*\n"
+		" * arctan table (indexed by i/q; should really be indexed by i/(i+q)\n"
+		" */\n""#define ATAN_TABLEN %d\n\n"
+		"static const unsigned short atan_tab[ATAN_TABLEN+2] = {",
+               ATAN_TABLEN);
+        for (i = 0; i <= ATAN_TABLEN; i++) {
+                if (!(i & 7))
+                        fprintf(f, "\n\t");
+                x = atan(i / (float)ATAN_TABLEN) / M_PI * 0x8000;
+                fprintf(f, "%6d, ", x);
+        }
+        fprintf(f, "%6d\n};\n\n", x);
+	
+}
+
+/* -------------------------------------------------------------------- */
+
+#define PSK48_TXF_OVERSAMPLING 5
+#define PSK48_TXF_NUMSAMPLES 16
+#define PSK48_RXF_LEN     64
+
+static const float psk48_tx_coeff[80] = {
+              -0.000379,       -0.000640,       -0.000000,        0.000772,
+               0.000543,       -0.000629,       -0.001187,       -0.000000,
+               0.001634,        0.001183,       -0.001382,       -0.002603,
+              -0.000000,        0.003481,        0.002472,       -0.002828,
+              -0.005215,       -0.000000,        0.006705,        0.004678,
+              -0.005269,       -0.009584,       -0.000000,        0.012065,
+               0.008360,       -0.009375,       -0.017028,       -0.000000,
+               0.021603,        0.015123,       -0.017229,       -0.032012,
+              -0.000000,        0.043774,        0.032544,       -0.040365,
+              -0.084963,       -0.000000,        0.201161,        0.374060,
+               0.374060,        0.201161,       -0.000000,       -0.084963,
+              -0.040365,        0.032544,        0.043774,       -0.000000,
+              -0.032012,       -0.017229,        0.015123,        0.021603,
+              -0.000000,       -0.017028,       -0.009375,        0.008360,
+               0.012065,       -0.000000,       -0.009584,       -0.005269,
+               0.004678,        0.006705,       -0.000000,       -0.005215,
+              -0.002828,        0.002472,        0.003481,       -0.000000,
+              -0.002603,       -0.001382,        0.001183,        0.001634,
+              -0.000000,       -0.001187,       -0.000629,        0.000543,
+               0.000772,       -0.000000,       -0.000640,       -0.000379
+};
+
+static const float psk48_rx_coeff[PSK48_RXF_LEN] = {
+              -0.000219,        0.000360,        0.000873,        0.001080,
+               0.000747,       -0.000192,       -0.001466,       -0.002436,
+              -0.002328,       -0.000699,        0.002101,        0.004809,
+               0.005696,        0.003492,       -0.001633,       -0.007660,
+              -0.011316,       -0.009627,       -0.001780,        0.009712,
+               0.019426,        0.021199,        0.011342,       -0.008583,
+              -0.030955,       -0.044093,       -0.036634,       -0.002651,
+               0.054742,        0.123101,        0.184198,        0.220219,
+               0.220219,        0.184198,        0.123101,        0.054742,
+              -0.002651,       -0.036634,       -0.044093,       -0.030955,
+              -0.008583,        0.011342,        0.021199,        0.019426,
+               0.009712,       -0.001780,       -0.009627,       -0.011316,
+              -0.007660,       -0.001633,        0.003492,        0.005696,
+               0.004809,        0.002101,       -0.000699,       -0.002328,
+              -0.002436,       -0.001466,       -0.000192,        0.000747,
+               0.001080,        0.000873,        0.000360,       -0.000219
+};
+
+static void gentbl_psk4800(FILE *f)
+{
+        int i, j, k;
+        short x;
+
+	fprintf(f, "\n/*\n * psk4800 specific tables\n */\n"
+		"#define PSK48_TXF_OVERSAMPLING %d\n"
+		"#define PSK48_TXF_NUMSAMPLES %d\n\n"
+		"#define PSK48_SAMPLERATE  8000\n"
+		"#define PSK48_CAR_FREQ    2000\n"
+		"#define PSK48_PSK_LEN     5\n"
+		"#define PSK48_RXF_LEN     %u\n"
+		"#define PSK48_PHASEINC    (0x10000*PSK48_CAR_FREQ/PSK48_SAMPLERATE)\n"
+		"#define PSK48_SPHASEINC   (0x10000/(2*PSK48_PSK_LEN))\n\n"
+		"static const short psk48_tx_table[PSK48_TXF_OVERSAMPLING*"
+		"PSK48_TXF_NUMSAMPLES*8*2] = {", 
+		PSK48_TXF_OVERSAMPLING, PSK48_TXF_NUMSAMPLES, PSK48_RXF_LEN);
+        for (i = 0; i < PSK48_TXF_OVERSAMPLING; i++) {
+                for (j = 0; j < PSK48_TXF_NUMSAMPLES; j++) {
+                        fprintf(f, "\n\t");
+                        for (k = 0; k < 8; k++) {
+                                x = 32767.0 * cos(k*M_PI/4.0) * 
+                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+                                fprintf(f, "%6d, ", x);
+                        }
+                        fprintf(f, "\n\t");
+                        for (k = 0; k < 8; k++) {
+                                x = 32767.0 * sin(k*M_PI/4.0) * 
+                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+                                fprintf(f, "%6d", x);
+                                if (k != 7 || j != PSK48_TXF_NUMSAMPLES-1 || 
+                                    i != PSK48_TXF_OVERSAMPLING-1)
+                                        fprintf(f, ", ");
+                        }
+                }
+        }
+        fprintf(f, "\n};\n\n");
+
+	fprintf(f, "static const short psk48_rx_coeff[PSK48_RXF_LEN] = {\n\t");
+	for (i = 0; i < PSK48_RXF_LEN; i++) {
+		fprintf(f, "%6d", (int)(psk48_rx_coeff[i]*32767.0));
+		if (i < PSK48_RXF_LEN-1)
+			fprintf(f, ",%s", (i & 7) == 7 ? "\n\t" : "");
+	}
+	fprintf(f, "\n};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_hapn4800(FILE *f)
+{
+        int i, j, k, l;
+	float s;
+	float c[40];
+	float min, max;
+
+	fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n");
+	/*
+	 * firstly generate tables for the FM transmitter modulator
+	 */
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 24; i++)
+		c[8+i] = sinc(1.5*((i-11.5)/8.0))*hamming(i/23.0)/2.4;
+	for (i = 0; i < 24; i++)
+		c[i] -= c[i+8];
+	fprintf(f, "static unsigned char hapn48_txfilt_8[] = {\n\t");
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+				if (j & k)
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 7 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+#ifdef VERBOSE
+	fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max);
+#endif
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 30; i++)
+		c[10+i] = sinc(1.5*((i-14.5)/10.0))*hamming(i/29.0)/2.4;
+	for (i = 0; i < 30; i++)
+		c[i] -= c[i+10];
+	fprintf(f, "static unsigned char hapn48_txfilt_10[] = {\n\t");
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+				if (j & k) 
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 9 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+#ifdef VERBOSE
+	fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max);
+#endif
+	fprintf(f, "\n};\n\n");
+	/*
+	 * secondly generate tables for the PM transmitter modulator
+	 */
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 25; i++)
+		c[i] = sinc(1.4*((i-12.0)/8.0))*hamming(i/24.0)/6.3;
+	for (i = 0; i < 25; i++)
+		for (j = 1; j < 8; j++)
+			c[i] += c[i+j];
+	fprintf(f, "static unsigned char hapn48_txfilt_pm8[] = {\n\t");
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+				if (j & k)
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 7 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+#ifdef VERBOSE
+	fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max);
+#endif
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 31; i++)
+		c[10+i] = sinc(1.4*((i-15.0)/10.0))*hamming(i/30.0)/7.9;
+	for (i = 0; i < 31; i++)
+		for (j = 1; j < 10; j++)
+			c[i] += c[i+j];
+	fprintf(f, "static unsigned char hapn48_txfilt_pm10[] = {\n\t");
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+				if (j & k) 
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 9 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+#ifdef VERBOSE
+	fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max);
+#endif
+	fprintf(f, "\n};\n\n");
+	
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK24_SAMPLERATE  16000
+#define AFSK24_CORRLEN     14
+
+static void gentbl_afsk2400(FILE *f, float tcm3105clk)
+{
+	int i, sum, v;
+
+	fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n"
+		"#define AFSK24_TX_FREQ_LO %d\n"
+		"#define AFSK24_TX_FREQ_HI %d\n"
+		"#define AFSK24_BITPLL_INC %d\n"
+		"#define AFSK24_SAMPLERATE %d\n\n", tcm3105clk, 
+		(int)(tcm3105clk/3694.0), (int)(tcm3105clk/2015.0), 
+		0x10000*2400/AFSK24_SAMPLERATE, AFSK24_SAMPLERATE);
+
+#define ARGLO(x) 2.0*M_PI*(double)x*(tcm3105clk/3694.0)/(double)AFSK24_SAMPLERATE
+#define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE
+#define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0))
+
+	fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t");
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i));
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_I %d\n\n"
+		"static const int afsk24_tx_lo_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+		sum += (v = 127.0*sin(ARGLO(i))*WINDOW(i)); 
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %d\n\n"
+		"static const int afsk24_tx_hi_i[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGHI(i))*WINDOW(i)); 
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %d\n\n"
+		"static const int afsk24_tx_hi_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++)  {
+		sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i));
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+	fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum);
+#undef ARGLO
+#undef ARGHI
+#undef WINDOW
+}
+
+/* -------------------------------------------------------------------- */
+
+static char *progname;
+
+static void gentbl_banner(FILE *f)
+{
+	fprintf(f, "/*\n * THIS FILE IS GENERATED AUTOMATICALLY BY %s, "
+		"DO NOT EDIT!\n */\n\n", progname);
+}
+
+/* -------------------------------------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+	FILE *f;
+	
+	progname = argv[0];
+	if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk1200(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_afsk2666.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk2666(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_psk4800.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_psk4800(f);
+	gentbl_costab(f, 8);
+	gentbl_atantab(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_hapn4800.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_hapn4800(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_fsk9600.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_fsk9600(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_afsk2400_8.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk2400(f, 8000000);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_afsk2400_7.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk2400(f, 7372800);
+	fclose(f);
+	exit(0);
+}
+
+
+/* -------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c
--- v2.0.34/linux/drivers/net/soundmodem/sm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,899 @@
+/*****************************************************************************/
+
+/*
+ *	sm.c  -- soundcard radio modem driver.
+ *
+ *	Copyright (C) 1996-1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     mode string; eg. "wss:afsk1200"
+ *  iobase   base address of the soundcard; common values are 0x220 for sbc,
+ *           0x530 for wss
+ *  irq      interrupt number; common values are 7 or 5 for sbc, 11 for wss
+ *  dma      dma number; common values are 0 or 1
+ *
+ *
+ *  History:
+ *   0.1  21.09.96  Started
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.4  21.01.97  Separately compileable soundcard/modem modules
+ *   0.5  03.03.97  fixed LPT probing (check_lpt result was interpreted the wrong way round)
+ *   0.6  16.04.97  init code/data tagged
+ *   0.7  30.07.97  fixed halfduplex interrupt handlers/hotfix for CS423X
+ *   0.8  14.04.98  cleanups
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include "sm.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const char sm_drvname[] = "soundmodem";
+static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1998 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "soundmodem: version 0.8 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+	&sm_afsk1200_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
+	&sm_afsk2400_7_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
+	&sm_afsk2400_8_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+	&sm_afsk2666_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+	&sm_psk4800_tx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+	&sm_hapn4800_8_tx,
+	&sm_hapn4800_10_tx,
+	&sm_hapn4800_pm8_tx,
+	&sm_hapn4800_pm10_tx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+	&sm_fsk9600_4_tx,
+	&sm_fsk9600_5_tx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+	NULL
+};
+
+/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+	&sm_afsk1200_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
+	&sm_afsk2400_7_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
+	&sm_afsk2400_8_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+	&sm_afsk2666_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+	&sm_psk4800_rx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+	&sm_hapn4800_8_rx,
+	&sm_hapn4800_10_rx,
+	&sm_hapn4800_pm8_rx,
+	&sm_hapn4800_pm10_rx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+	&sm_fsk9600_4_rx,
+	&sm_fsk9600_5_rx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+	NULL
+};
+
+static const struct hardware_info *sm_hardware_table[] = {
+#ifdef CONFIG_SOUNDMODEM_SBC
+	&sm_hw_sbc,
+	&sm_hw_sbcfdx,
+#endif /* CONFIG_SOUNDMODEM_SBC */
+#ifdef CONFIG_SOUNDMODEM_WSS
+	&sm_hw_wss,
+	&sm_hw_wssfdx,
+#endif /* CONFIG_SOUNDMODEM_WSS */
+	NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+/* --------------------------------------------------------------------- */
+
+static struct device sm_device[NR_PORTS];
+
+static struct {
+	char *mode;
+	int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase;
+} sm_ports[NR_PORTS] = {
+	{ NULL, -1, 0, 0, 0, -1, -1, -1 },
+};
+
+/* --------------------------------------------------------------------- */
+
+#define UART_RBR(iobase) (iobase+0)
+#define UART_THR(iobase) (iobase+0)
+#define UART_IER(iobase) (iobase+1)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase)    (iobase+0)
+#define LPT_STATUS(iobase)  (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE      0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase)     (iobase)
+#define MIDI_STATUS(iobase)   (iobase+1)
+#define MIDI_READ_FULL 0x80   /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+/* ---------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+#define SP_SER  1
+#define SP_PAR  2
+#define SP_MIDI 4
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+static int check_lpt(unsigned int iobase)
+{
+	unsigned char b1,b2;
+	int i;
+
+	if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+		return 0;
+	if (check_region(iobase, LPT_EXTENT))
+		return 0;
+	b1 = inb(LPT_DATA(iobase));
+	b2 = inb(LPT_CONTROL(iobase));
+	outb(0xaa, LPT_DATA(iobase));
+	i = inb(LPT_DATA(iobase)) == 0xaa;
+	outb(0x55, LPT_DATA(iobase));
+	i &= inb(LPT_DATA(iobase)) == 0x55;
+	outb(0x0a, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+	outb(0x05, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+	outb(b1, LPT_DATA(iobase));
+	outb(b2, LPT_CONTROL(iobase));
+	return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+	{ "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+		return c_uart_unknown;
+	if (check_region(iobase, SER_EXTENT))
+		return c_uart_unknown;
+	b1 = inb(UART_MCR(iobase));
+	outb(b1 | 0x10, UART_MCR(iobase));	/* loopback mode */
+	b2 = inb(UART_MSR(iobase));
+	outb(0x1a, UART_MCR(iobase));
+	b3 = inb(UART_MSR(iobase)) & 0xf0;
+	outb(b1, UART_MCR(iobase));	   /* restore old values */
+	outb(b2, UART_MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(UART_RBR(iobase));
+	inb(UART_RBR(iobase));
+	outb(0x01, UART_FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, UART_SCR(iobase));
+		b1 = inb(UART_SCR(iobase));
+		outb(0xa5, UART_SCR(iobase));
+		b2 = inb(UART_SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int check_midi(unsigned int iobase)
+{
+	unsigned long timeout;
+	unsigned long flags;
+	unsigned char b;
+
+	if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+		return 0;
+	if (check_region(iobase, MIDI_EXTENT))
+		return 0;
+	timeout = jiffies + (HZ / 100);
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	save_flags(flags);
+	cli();
+	outb(0xff, MIDI_DATA(iobase));
+	b = inb(MIDI_STATUS(iobase));
+	restore_flags(flags);
+	if (!(b & MIDI_WRITE_EMPTY))
+		return 0;
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm)
+{
+	int invert_dcd = 0;
+	int invert_ptt = 0;
+
+	int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt;
+	int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
+
+	if (sm->hdrv.ptt_out.flags & SP_SER) {
+		outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
+		outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_PAR) {
+		outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
+		outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sm_output_open(struct sm_state *sm)
+{
+	enum uart u = c_uart_unknown;
+
+	sm->hdrv.ptt_out.flags = 0;
+	if (sm->hdrv.ptt_out.seriobase > 0 &&
+	    sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+	    ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
+		sm->hdrv.ptt_out.flags |= SP_SER;
+		request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
+		outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
+		/* 5 bits, 1 stop, no parity, no break, Div latch access */
+		outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
+		outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
+		outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
+		/* LCR and MCR set by output_status */
+	}
+	if (sm->hdrv.ptt_out.pariobase > 0 &&
+	    sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+	    !check_lpt(sm->hdrv.ptt_out.pariobase)) {
+		sm->hdrv.ptt_out.flags |= SP_PAR;
+		request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
+	}
+	if (sm->hdrv.ptt_out.midiiobase > 0 &&
+	    sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+	    check_midi(sm->hdrv.ptt_out.midiiobase)) {
+		sm->hdrv.ptt_out.flags |= SP_MIDI;
+		request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
+			       "sm midi ptt");
+	}
+	sm_output_status(sm);
+
+	printk(KERN_INFO "%s: ptt output:", sm_drvname);
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
+		       uart_str[u]);
+	if (sm->hdrv.ptt_out.flags & SP_PAR)
+		printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
+	if (sm->hdrv.ptt_out.flags & SP_MIDI)
+		printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
+	if (!sm->hdrv.ptt_out.flags)
+		printk(" none");
+	printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sm_output_close(struct sm_state *sm)
+{
+	/* release regions used for PTT output */
+	sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
+	sm_output_status(sm);
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_PAR)
+		release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_MIDI)
+		release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
+	sm->hdrv.ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev);
+static int sm_close(struct device *dev);
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+		    struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static const struct hdlcdrv_ops sm_ops = {
+	sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev)
+{
+	struct sm_state *sm;
+	int err;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_open: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+	if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
+		return -ENODEV;
+	sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
+	err = sm->hwdrv->open(dev, sm);
+	if (err)
+		return err;
+	sm_output_open(sm);
+	MOD_INC_USE_COUNT;
+	printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n",
+	       sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
+	       sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_close(struct device *dev)
+{
+	struct sm_state *sm;
+	int err = -ENODEV;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_close: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+
+	if (sm->hwdrv && sm->hwdrv->close)
+		err = sm->hwdrv && sm->hwdrv->close(dev, sm);
+	sm_output_close(sm);
+	MOD_DEC_USE_COUNT;
+	printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
+	       sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
+	return err;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, ':');
+	const struct hardware_info **hwp = sm_hardware_table;
+
+	if (!cp)
+		cp = mode;
+	else {
+		*cp++ = '\0';
+		while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode))
+			hwp++;
+		if (!hwp || !*hwp || !(*hwp)->hw_name)
+			return -EINVAL;
+		if ((*hwp)->loc_storage > sizeof(sm->hw)) {
+			printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
+			       sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage);
+			return -EINVAL;
+		}
+		sm->hwdrv = *hwp;
+	}
+	if (!*cp)
+		return 0;
+	if (sm->hwdrv && sm->hwdrv->sethw)
+		return sm->hwdrv->sethw(dev, sm, cp);
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+		    struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_state *sm;
+	struct sm_ioctl bi;
+	unsigned long flags;
+	unsigned int newdiagmode;
+	unsigned int newdiagflags;
+	char *cp;
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp = sm_modem_rx_table;
+	const struct hardware_info **hwp = sm_hardware_table;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE) {
+		if (!sm->hwdrv || !sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+		return -ENOIOCTLCMD;
+	}
+	switch (hi->cmd) {
+	default:
+		if (sm->hwdrv && sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETMODE:
+		cp = hi->data.modename;
+		if (sm->hwdrv && sm->hwdrv->hw_name)
+			cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
+		else
+			cp += sprintf(cp, "<unspec>:");
+		if (sm->mode_tx && sm->mode_tx->name)
+			cp += sprintf(cp, "%s", sm->mode_tx->name);
+		else
+			cp += sprintf(cp, "<unspec>");
+		if (!sm->mode_rx || !sm->mode_rx ||
+		    strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
+			if (sm->mode_rx && sm->mode_rx->name)
+				cp += sprintf(cp, ",%s", sm->mode_rx->name);
+			else
+				cp += sprintf(cp, ",<unspec>");
+		}
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (dev->start || !suser())
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return sethw(dev, sm, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		cp = hi->data.modename;
+		while (*hwp) {
+			if ((*hwp)->hw_name)
+				cp += sprintf("%s:,", (*hwp)->hw_name);
+			hwp++;
+		}
+		while (*mtp) {
+			if ((*mtp)->name)
+				cp += sprintf(">%s,", (*mtp)->name);
+			mtp++;
+		}
+		while (*mrp) {
+			if ((*mrp)->name)
+				cp += sprintf("<%s,", (*mrp)->name);
+			mrp++;
+		}
+		cp[-1] = '\0';
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
+		return 0;
+
+#ifdef SM_DEBUG
+	case SMCTL_GETDEBUG:
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
+		bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
+		bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
+		bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
+		bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
+		sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
+			sm->debug_vals.dma_residue = 0;
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
+#endif /* SM_DEBUG */
+
+	case SMCTL_DIAGNOSE:
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
+		newdiagmode = bi.data.diag.mode;
+		newdiagflags = bi.data.diag.flags;
+		if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
+			return -EINVAL;
+		bi.data.diag.mode = sm->diag.mode;
+		bi.data.diag.flags = sm->diag.flags;
+		bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
+		if (sm->diag.mode != newdiagmode) {
+			save_flags(flags);
+			cli();
+			sm->diag.ptr = -1;
+			sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+			sm->diag.mode = newdiagmode;
+			restore_flags(flags);
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (bi.data.diag.datalen > DIAGDATALEN)
+			bi.data.diag.datalen = DIAGDATALEN;
+		if (sm->diag.ptr < bi.data.diag.datalen) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (copy_to_user(bi.data.diag.data, sm->diag.data,
+				 bi.data.diag.datalen * sizeof(short)))
+			return -EFAULT;
+		bi.data.diag.flags |= SM_DIAGFLAG_VALID;
+		save_flags(flags);
+		cli();
+		sm->diag.ptr = -1;
+		sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+		sm->diag.mode = newdiagmode;
+		restore_flags(flags);
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+
+int sm_x86_capability = 0;
+
+__initfunc(static void i386_capability(void))
+{
+       unsigned long flags;
+       unsigned long fl1;
+       union {
+               struct {
+                       unsigned int ebx, edx, ecx;
+               } r;
+               unsigned char s[13];
+       } id;
+       unsigned int eax;
+
+       save_flags(flags);
+       flags |= 0x200000;
+       restore_flags(flags);
+       save_flags(flags);
+       fl1 = flags;
+       flags &= ~0x200000;
+       restore_flags(flags);
+       save_flags(flags);
+       if (!(fl1 & 0x200000) || (flags & 0x200000)) {
+               printk(KERN_WARNING "%s: cpu does not support CPUID\n", sm_drvname);
+               return;
+       }
+       __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) :
+                "0" (0));
+       id.s[12] = 0;
+       if (eax < 1) {
+               printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability "
+                      "list\n", sm_drvname, id.s);
+               return;
+       }
+       printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s);
+       __asm__ ("cpuid" : "=a" (eax), "=d" (sm_x86_capability) : "0" (1) : "ebx", "ecx");
+       printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15,
+              eax & 15, sm_x86_capability);
+}      
+#endif /* __i386__ */  
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+__initfunc(static int sm_init(void))
+#else /* MODULE */
+__initfunc(int sm_init(void))
+#endif /* MODULE */
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct sm_state *sm;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+	printk(sm_drvinfo);
+#ifdef __i386__
+       i386_capability();
+#endif /* __i386__ */  
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		sprintf(ifname, "sm%d", i);
+
+		if (!sm_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			sm_ports[i].iobase = sm_ports[i].irq = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state),
+					     ifname, sm_ports[i].iobase,
+					     sm_ports[i].irq, sm_ports[i].dma);
+		if (!j) {
+			sm = (struct sm_state *)dev->priv;
+			sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2;
+			sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
+			sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
+			sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
+			if (set_hw && sethw(dev, sm, sm_ports[i].mode))
+				set_hw = 0;
+			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       sm_drvname);
+		}
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode = NULL;
+static int iobase = -1;
+static int irq = -1;
+static int dma = -1;
+static int dma2 = -1;
+static int serio = 0;
+static int pario = 0;
+static int midiio = 0;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "s");
+MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "soundmodem base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "soundmodem interrupt");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "soundmodem dma channel");
+MODULE_PARM(dma2, "i");
+MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only");
+MODULE_PARM(serio, "i");
+MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port");
+MODULE_PARM(pario, "i");
+MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port");
+MODULE_PARM(midiio, "i");
+MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Soundcard amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+	if (mode) {
+		if (iobase == -1)
+			iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530;
+		if (irq == -1)
+			irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11;
+		if (dma == -1)
+			dma = 1;
+	}
+	sm_ports[0].mode = mode;
+	sm_ports[0].iobase = iobase;
+	sm_ports[0].irq = irq;
+	sm_ports[0].dma = dma;
+	sm_ports[0].dma2 = dma2;
+	sm_ports[0].seriobase = serio;
+	sm_ports[0].pariobase = pario;
+	sm_ports[0].midiiobase = midiio;
+	sm_ports[1].mode = NULL;
+
+	return sm_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	printk(KERN_INFO "sm: cleanup_module called\n");
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		struct sm_state *sm = (struct sm_state *)dev->priv;
+
+		if (sm) {
+			if (sm->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "sm: invalid magic in "
+				       "cleanup_module\n");
+			else
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode
+ * mode: hw:modem
+ * hw: sbc, wss, wssfdx
+ * modem: afsk1200, fsk9600
+ */
+
+__initfunc(void sm_setup(char *str, int *ints))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 3)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", sm_drvname);
+		return;
+	}
+	sm_ports[i].mode = str;
+	sm_ports[i].iobase = ints[1];
+	sm_ports[i].irq = ints[2];
+	sm_ports[i].dma = ints[3];
+	sm_ports[i].dma2 = (ints[0] >= 4) ? ints[4] : 0;
+	sm_ports[i].seriobase = (ints[0] >= 5) ? ints[5] : 0;
+	sm_ports[i].pariobase = (ints[0] >= 6) ? ints[6] : 0;
+	sm_ports[i].midiiobase = (ints[0] >= 7) ? ints[7] : 0;
+	if (i < NR_PORTS-1)
+		sm_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm.h linux/drivers/net/soundmodem/sm.h
--- v2.0.34/linux/drivers/net/soundmodem/sm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm.h	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,382 @@
+/*****************************************************************************/
+
+/*
+ *	sm.h  --  soundcard radio modem driver internal header.
+ *
+ *	Copyright (C) 1996-1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#ifndef _SM_H
+#define _SM_H
+
+/* ---------------------------------------------------------------------- */
+
+#include <linux/hdlcdrv.h>
+#include <linux/soundmodem.h>
+
+#define SM_DEBUG
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct sm_state {
+	struct hdlcdrv_state hdrv;
+
+	const struct modem_tx_info *mode_tx;
+	const struct modem_rx_info *mode_rx;
+
+	const struct hardware_info *hwdrv;
+
+	/*
+	 * Hardware (soundcard) access routines state
+	 */
+	struct {
+		void *ibuf;
+		unsigned int ifragsz;
+		unsigned int ifragptr;
+		unsigned int i16bit;
+		void *obuf;
+		unsigned int ofragsz;
+		unsigned int ofragptr;
+		unsigned int o16bit;
+		int ptt_cnt;
+	} dma;
+
+	union {
+		long hw[32/sizeof(long)];
+	} hw;
+
+	/*
+	 * state of the modem code
+	 */
+	union {
+		long m[48/sizeof(long)];
+	} m;
+	union {
+		long d[256/sizeof(long)];
+	} d;
+
+#define DIAGDATALEN 64
+	struct diag_data {
+		unsigned int mode;
+		unsigned int flags;
+		volatile int ptr;
+		short data[DIAGDATALEN];
+	} diag;
+
+
+#ifdef SM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		unsigned mod_cyc;
+		unsigned demod_cyc;
+		unsigned dma_residue;
+	} debug_vals;
+#endif /* SM_DEBUG */
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Mode definition structure
+ */
+
+struct modem_tx_info {
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+        void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int);
+        void (*modulator_s16)(struct sm_state *, short *, unsigned int);
+        void (*init)(struct sm_state *);
+};
+
+struct modem_rx_info {
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+	unsigned int overlap;
+	unsigned int sperbit;
+        void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int);
+        void (*demodulator_s16)(struct sm_state *, const short *, unsigned int);
+        void (*init)(struct sm_state *);
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Soundcard driver definition structure
+ */
+
+struct hardware_info {
+	char *hw_name; /* used for request_{region,irq,dma} */
+	unsigned int loc_storage;
+	/*
+	 * mode specific open/close
+	 */
+	int (*open)(struct device *, struct sm_state *);
+	int (*close)(struct device *, struct sm_state *);
+	int (*ioctl)(struct device *, struct sm_state *, struct ifreq *,
+		     struct hdlcdrv_ioctl *, int);
+	int (*sethw)(struct device *, struct sm_state *, char *);
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+extern const char sm_drvname[];
+extern const char sm_drvinfo[];
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== diagnostics stuff ===============================
+ */
+
+extern inline void diag_trigger(struct sm_state *sm)
+{
+	if (sm->diag.ptr < 0)
+		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
+			sm->diag.ptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
+#define SHRT_MIN (-SHRT_MAX-1)
+
+extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
+{
+	int val;
+
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void diag_add_one(struct sm_state *sm, int val)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
+	    sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (vali > SHRT_MAX)
+		vali = SHRT_MAX;
+	if (vali < SHRT_MIN)
+		vali = SHRT_MIN;
+	if (valq > SHRT_MAX)
+		valq = SHRT_MAX;
+	if (valq < SHRT_MIN)
+		valq = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = vali;
+	sm->diag.data[sm->diag.ptr++] = valq;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== utility functions ===============================
+ */
+
+extern inline unsigned int hweight32(unsigned int w)
+	__attribute__ ((unused));
+extern inline unsigned int hweight16(unsigned short w)
+	__attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+        __attribute__ ((unused));
+
+extern inline unsigned int hweight32(unsigned int w)
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+extern inline unsigned int hweight16(unsigned short w)
+{
+        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+        res = (res & 0x3333) + ((res >> 2) & 0x3333);
+        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+        res = (res & 0x33) + ((res >> 2) & 0x33);
+        return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+{
+	for (;;) {
+		if (!x)
+			return y;
+		if (!y)
+			return x;
+		if (x > y)
+			x %= y;
+		else
+			y %= x;
+	}
+}
+
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+{
+	return x * y / gcd(x, y);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== profiling =======================================
+ */
+
+
+#ifdef __i386__
+
+extern int sm_x86_capability;
+
+#define HAS_RDTSC (sm_x86_capability & 0x10)
+
+/*
+ * only do 32bit cycle counter arithmetic; we hope we won't overflow.
+ * in fact, overflowing modems would require over 2THz CPU clock speeds :-)
+ */
+
+#define time_exec(var,cmd)                                              \
+({                                                                      \
+	if (HAS_RDTSC) {                                                \
+		unsigned int cnt1, cnt2, cnt3;                          \
+		__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3));  \
+		cmd;                                                    \
+		__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3));  \
+		var = cnt2-cnt1;                                        \
+	} else {                                                        \
+		cmd;                                                    \
+	}                                                               \
+})
+
+#else /* __i386__ */
+
+#define time_exec(var,cmd) cmd
+
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+extern const struct modem_tx_info sm_afsk1200_tx;
+extern const struct modem_tx_info sm_afsk2400_7_tx;
+extern const struct modem_tx_info sm_afsk2400_8_tx;
+extern const struct modem_tx_info sm_afsk2666_tx;
+extern const struct modem_tx_info sm_psk4800_tx;
+extern const struct modem_tx_info sm_hapn4800_8_tx;
+extern const struct modem_tx_info sm_hapn4800_10_tx;
+extern const struct modem_tx_info sm_hapn4800_pm8_tx;
+extern const struct modem_tx_info sm_hapn4800_pm10_tx;
+extern const struct modem_tx_info sm_fsk9600_4_tx;
+extern const struct modem_tx_info sm_fsk9600_5_tx;
+
+extern const struct modem_rx_info sm_afsk1200_rx;
+extern const struct modem_rx_info sm_afsk2400_7_rx;
+extern const struct modem_rx_info sm_afsk2400_8_rx;
+extern const struct modem_rx_info sm_afsk2666_rx;
+extern const struct modem_rx_info sm_psk4800_rx;
+extern const struct modem_rx_info sm_hapn4800_8_rx;
+extern const struct modem_rx_info sm_hapn4800_10_rx;
+extern const struct modem_rx_info sm_hapn4800_pm8_rx;
+extern const struct modem_rx_info sm_hapn4800_pm10_rx;
+extern const struct modem_rx_info sm_fsk9600_4_rx;
+extern const struct modem_rx_info sm_fsk9600_5_rx;
+
+extern const struct hardware_info sm_hw_sbc;
+extern const struct hardware_info sm_hw_sbcfdx;
+extern const struct hardware_info sm_hw_wss;
+extern const struct hardware_info sm_hw_wssfdx;
+
+extern const struct modem_tx_info *sm_modem_tx_table[];
+extern const struct modem_rx_info *sm_modem_rx_table[];
+extern const struct hardware_info *sm_hardware_table[];
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm);
+/*void sm_output_open(struct sm_state *sm);*/
+/*void sm_output_close(struct sm_state *sm);*/
+
+/* --------------------------------------------------------------------- */
+
+extern void inline sm_int_freq(struct sm_state *sm)
+{
+#ifdef SM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	sm->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
+		sm->debug_vals.last_jiffies = cur_jiffies;
+		sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
+		sm->debug_vals.cur_intcnt = 0;
+	}
+#endif /* SM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* _SM_H */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_afsk1200.c linux/drivers/net/soundmodem/sm_afsk1200.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_afsk1200.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_afsk1200.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,272 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk1200.c  -- soundcard radio modem driver, 1200 baud AFSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk1200.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk12 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	unsigned char last_rxbit;
+};
+
+struct mod_state_afsk12 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int bit_pll;
+	unsigned int dds_inc;
+	unsigned int txphase;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { 
+	AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE,
+	AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE 
+};
+
+static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf, 
+			      unsigned int buflen)
+{
+	struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!((st->txphase++) & 7)) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+		}
+		st->dds_inc = dds_inc[st->tx_bit & 1];
+		*buf++ = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->dds_inc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!((st->txphase++) & 7)) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+		}
+		st->dds_inc = dds_inc[st->tx_bit & 1];
+		*buf++ = COS(st->bit_pll);
+		st->bit_pll += st->dds_inc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+
+	sum >>= 7;
+	return sum * sum;
+}
+
+extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+
+	sum >>= 15;
+	return sum * sum;
+}
+
+extern __inline__ int do_filter_1200_u8(const unsigned char *buf)
+{
+	int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
+	sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
+	sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
+	sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
+	return sum;
+}
+
+extern __inline__ int do_filter_1200_s16(const short *buf)
+{
+	int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
+	sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
+	sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
+	sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
+	return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static const int pll_corr[2] = { -0x1000, 0x1000 };
+
+static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_1200_u8(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr
+				[st->bit_pll < 0x9000];
+			j = 4 * hweight8(st->dcd_shreg & 0x38)
+				- hweight16(st->dcd_shreg & 0x7c0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_1200_s16(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr
+				[st->bit_pll < 0x9000];
+			j = 4 * hweight8(st->dcd_shreg & 0x38)
+				- hweight16(st->dcd_shreg & 0x7c0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, *buf, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_1200(struct sm_state *sm)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+
+       	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk1200_tx = {
+	"afsk1200", sizeof(struct mod_state_afsk12), 
+	AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk1200_rx = {
+	"afsk1200", sizeof(struct demod_state_afsk12), 
+	AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200, 
+	demodulator_1200_u8, demodulator_1200_s16, demod_init_1200
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_7.c linux/drivers/net/soundmodem/sm_afsk2400_7.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_7.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_afsk2400_7.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,296 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk2400_7.c  -- soundcard radio modem driver, 2400 baud AFSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+/*
+ * This driver is intended to be compatible with TCM3105 modems
+ * overclocked to 7.3728MHz. The mark and space frequencies therefore
+ * lie at 3658 and 1996 Hz.
+ * Note that I do _not_ recommend the building of such links, I provide
+ * this only for the users who live in the coverage area of such
+ * a "legacy" link.
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2400_7.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk24 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	unsigned char last_rxbit;
+};
+
+struct mod_state_afsk24 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int bit_pll;
+	unsigned int tx_seq;
+	unsigned int phinc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
+				AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
+
+static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = COS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 7;
+	return sum * sum;
+}
+
+extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 15;
+	return sum * sum;
+}
+
+extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
+{
+	int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+extern __inline__ int do_filter_2400_s16(const short *buf)
+{
+	int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_u8(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_s16(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, *buf, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2400(struct sm_state *sm)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+
+       	st->dcd_time = 120;
+	st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2400_7_tx = {
+	"afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, 
+	modulator_2400_u8, modulator_2400_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2400_7_rx = {
+	"afsk2400_7", sizeof(struct demod_state_afsk24), 
+	AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, 
+	demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_8.c linux/drivers/net/soundmodem/sm_afsk2400_8.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_afsk2400_8.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_afsk2400_8.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,296 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk2400_8.c  -- soundcard radio modem driver, 2400 baud AFSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+/*
+ * This driver is intended to be compatible with TCM3105 modems
+ * overclocked to 8MHz. The mark and space frequencies therefore
+ * lie at 3970 and 2165 Hz.
+ * Note that I do _not_ recommend the building of such links, I provide
+ * this only for the users who live in the coverage area of such
+ * a "legacy" link.
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2400_8.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk24 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	unsigned char last_rxbit;
+};
+
+struct mod_state_afsk24 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int bit_pll;
+	unsigned int tx_seq;
+	unsigned int phinc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
+				AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
+
+static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = COS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 7;
+	return sum * sum;
+}
+
+extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 15;
+	return sum * sum;
+}
+
+extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
+{
+	int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+extern __inline__ int do_filter_2400_s16(const short *buf)
+{
+	int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_u8(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_s16(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, *buf, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2400(struct sm_state *sm)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+
+       	st->dcd_time = 120;
+	st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2400_8_tx = {
+	"afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, 
+	modulator_2400_u8, modulator_2400_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2400_8_rx = {
+	"afsk2400_8", sizeof(struct demod_state_afsk24), 
+	AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, 
+	demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_afsk2666.c linux/drivers/net/soundmodem/sm_afsk2666.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_afsk2666.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_afsk2666.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,356 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk2666.c  -- soundcard radio modem driver, 2666 baud AFSK modem
+ *
+ *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2666.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk26 {
+	unsigned int shreg;
+	unsigned long descram;
+	int dem_sum[8];
+	int dem_sum_mean;
+	int dem_cnt;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+};
+
+struct mod_state_afsk26 {
+	unsigned int shreg;
+	unsigned long scram;
+	unsigned int bit_pll;
+	unsigned int phinc;
+	unsigned int tx_seq;
+};
+
+/* --------------------------------------------------------------------- */
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2666_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = ((st->scram << 1) | (st->scram & 1));
+			st->scram ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
+		}
+		if (st->tx_seq >= 6)
+			st->tx_seq = 0;
+		*buf = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2666_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = ((st->scram << 1) | (st->scram & 1));
+			st->scram ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
+		}
+		if (st->tx_seq >= 6)
+			st->tx_seq = 0;
+		*buf = COS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution12_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+
+	return sum;
+}
+
+extern __inline__ int convolution12_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+
+	sum >>= 8;
+	return sum;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if 0
+static int binexp(unsigned int i)
+{
+	int ret = 31;
+	
+	if (!i)
+		return 0;
+	if (i < 0x10000LU) {
+		i <<= 16;
+		ret -= 16;
+	}
+	if (i < 0x1000000LU) {
+		i <<= 8;
+		ret -= 8;
+	}
+	if (i < 0x10000000LU) {
+		i <<= 4;
+		ret -= 4;
+	}
+	if (i < 0x40000000LU) {
+		i <<= 2;
+		ret -= 2;
+	}
+	if (i < 0x80000000LU)
+		ret -= 1;
+	return ret;
+}
+
+static const sqrt_tab[16] = {
+	00000, 16384, 23170, 28378, 32768, 36636, 40132, 43348,
+	46341, 49152, 51811, 54340, 56756, 59073, 61303, 63455
+};
+
+
+static unsigned int int_sqrt_approx(unsigned int i)
+{
+	unsigned int j;
+
+	if (i < 16)
+		return sqrt_tab[i] >> 14;
+	j = binexp(i) >> 1;
+	i >>= (j * 2 - 2);
+      	return (sqrt_tab[i & 0xf] << j) >> 15;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+extern unsigned int est_pwr(int i, int q)
+{
+	unsigned int ui = abs(i);
+	unsigned int uq = abs(q);
+
+	if (uq > ui) {
+		unsigned int tmp;
+		tmp = ui;
+		ui = uq;
+		uq = tmp;
+	}
+	if (uq > (ui >> 1))
+		return 7*(ui>>3) + 9*(uq>>4);
+	else
+		return ui + (uq>>2);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_one_sample(struct sm_state *sm, struct demod_state_afsk26 *st, int curval,
+			     int loi, int loq, int hii, int hiq)
+{
+	static const int pll_corr[2] = { -0xa00, 0xa00 };
+	unsigned char curbit;
+	unsigned int descx;
+	int val;
+
+	/* 
+	 * estimate power
+	 */
+	val = est_pwr(hii, hiq) - est_pwr(loi, loq);
+	/*
+	 * estimate center value
+	 */
+	st->dem_sum[0] += val >> 8;
+	if ((++st->dem_cnt) >= 256) {
+		st->dem_cnt = 0;
+		st->dem_sum_mean = (st->dem_sum[0]+st->dem_sum[1]+
+				    st->dem_sum[2]+st->dem_sum[3]+
+				    st->dem_sum[4]+st->dem_sum[5]+
+				    st->dem_sum[6]+st->dem_sum[7]) >> 3;
+		memmove(st->dem_sum+1, st->dem_sum, 
+			sizeof(st->dem_sum)-sizeof(st->dem_sum[0]));
+		st->dem_sum[0] = 0;
+	}
+	/*
+	 * decision and bit clock regen
+	 */
+	val -= st->dem_sum_mean;
+	diag_add(sm, curval, val);
+	
+	st->dcd_shreg <<= 1;
+	st->bit_pll += 0x1555;
+	curbit = (val > 0);
+	if (st->last_sample ^ curbit) {
+		st->dcd_shreg |= 1;
+		st->bit_pll += pll_corr[st->bit_pll < (0x8000+0x1555)];
+		st->dcd_sum0 += 4*hweight8(st->dcd_shreg & 0x1e) -
+			hweight16(st->dcd_shreg & 0xfe00);
+	}
+	st->last_sample = curbit;
+	hdlcdrv_channelbit(&sm->hdrv, curbit);
+	if ((--st->dcd_time) <= 0) {
+		hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 + 
+					   st->dcd_sum2) < 0);
+		st->dcd_sum2 = st->dcd_sum1;
+		st->dcd_sum1 = st->dcd_sum0;
+		st->dcd_sum0 = 2; /* slight bias */
+		st->dcd_time = 400;
+	}
+	if (st->bit_pll >= 0x10000) {
+		st->bit_pll &= 0xffffu;
+		st->descram = (st->descram << 1) | curbit;
+		descx = st->descram ^ (st->descram >> 1);
+		descx ^= ((descx >> DESCRAM_TAPSH1) ^
+			  (descx >> DESCRAM_TAPSH2));
+		st->shreg >>= 1;
+		st->shreg |= (!(descx & 1)) << 16;
+		if (st->shreg & 1) {
+			hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+			st->shreg = 0x10000;
+		}
+		diag_trigger(sm);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2666_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
+
+	for (; buflen > 0; buflen--, buf++) {
+		demod_one_sample(sm, st, (*buf-0x80)<<8,
+				 convolution12_u8(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
+				 convolution12_u8(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
+				 convolution12_u8(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
+				 convolution12_u8(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
+		demod_one_sample(sm, st,  (*buf-0x80)<<8,
+				 convolution12_u8(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
+				 convolution12_u8(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
+				 convolution12_u8(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
+				 convolution12_u8(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2666_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
+
+	for (; buflen > 0; buflen--, buf++) {
+		demod_one_sample(sm, st, *buf,
+				 convolution12_s16(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
+				 convolution12_s16(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
+				 convolution12_s16(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
+				 convolution12_s16(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
+		demod_one_sample(sm, st, *buf, 
+				 convolution12_s16(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
+				 convolution12_s16(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
+				 convolution12_s16(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
+				 convolution12_s16(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2666(struct sm_state *sm)
+{
+	struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
+
+	st->dcd_time = 400;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2666_tx = {
+	"afsk2666", sizeof(struct mod_state_afsk26), AFSK26_SAMPLERATE, 2666, 
+	modulator_2666_u8, modulator_2666_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2666_rx = {
+	"afsk2666", sizeof(struct demod_state_afsk26), AFSK26_SAMPLERATE, 2666, 12, 6, 
+	demodulator_2666_u8, demodulator_2666_s16, demod_init_2666
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_fsk9600.c linux/drivers/net/soundmodem/sm_fsk9600.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_fsk9600.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_fsk9600.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,391 @@
+/*****************************************************************************/
+
+/*
+ *	sm_fsk9600.c  --  soundcard radio modem driver, 
+ *                        9600 baud G3RUH compatible FSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_fsk9600.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_fsk96 {
+	unsigned int shreg;
+	unsigned long descram;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+};
+
+struct mod_state_fsk96 {
+	unsigned int shreg;
+	unsigned long scram;
+	unsigned char tx_bit;
+	unsigned char *txtbl;
+	unsigned int txphase;
+};
+
+/* --------------------------------------------------------------------- */
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 4)
+			st->txphase = 0;
+		*buf++ = *st->txtbl;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 4)
+			st->txphase = 0;
+		*buf++ = ((*st->txtbl)-0x80) << 8;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x4000;
+		curbit = (*buf >= 0x80);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0xa000];
+			st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - 
+				!!(st->dcd_shreg & 0x10);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x4000;
+		curbit = (*buf >= 0);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0xa000];
+			st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - 
+				!!(st->dcd_shreg & 0x10);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, *buf);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 5)
+			st->txphase = 0;
+		*buf++ = *st->txtbl;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 5)
+			st->txphase = 0;
+		*buf++ = ((*st->txtbl)-0x80)<<8;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x3333;
+		curbit = (*buf >= 0x80);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0x9999];
+			st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - 
+				hweight8(st->dcd_shreg & 0x70);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x3333;
+		curbit = (*buf >= 0);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0x9999];
+			st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - 
+				hweight8(st->dcd_shreg & 0x70);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, *buf);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_9600(struct sm_state *sm)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+
+	st->dcd_time = 240;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_4_tx = {
+	"fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600,
+	modulator_9600_4_u8, modulator_9600_4_s16, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_4_rx = {
+	"fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4,
+	demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_5_tx = {
+	"fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600,
+	modulator_9600_5_u8, modulator_9600_5_s16, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_5_rx = {
+	"fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5, 
+	demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_hapn4800.c linux/drivers/net/soundmodem/sm_hapn4800.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_hapn4800.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_hapn4800.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,560 @@
+/*****************************************************************************/
+
+/*
+ *	sm_hapn4800.c  -- soundcard radio modem driver, 4800 baud HAPN modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  This module implements a (hopefully) HAPN (Hamilton Area Packet
+ *  Network) compatible 4800 baud modem.
+ *  The HAPN modem uses kind of "duobinary signalling" (not really,
+ *  duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling
+ *  instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see
+ *  Proakis, Digital Communications).
+ *  The code is untested. It is compatible with itself (i.e. it can decode
+ *  the packets it sent), but I could not test if it is compatible with
+ *  any "real" HAPN modem, since noone uses it in my region of the world.
+ *  Feedback therefore welcome.
+ */
+
+#include "sm.h"
+#include "sm_tbl_hapn4800.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_hapn48 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_bit;
+	unsigned char last_bit2;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	int lvlhi, lvllo;
+};
+
+struct mod_state_hapn48 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int tx_seq;
+	const unsigned char *tbl;
+};
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = ((int)(buf[-2])-0x80) << 8;
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x199a;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - 
+				hweight32(st->dcd_shreg & 0xe739ce70);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = buf[-2];
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x199a;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - 
+				hweight32(st->dcd_shreg & 0xe739ce70);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = ((int)(buf[-2])-0x80) << 8;
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - 
+				hweight32(st->dcd_shreg & 0xbbbbbbbb);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = buf[-2];
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - 
+				hweight32(st->dcd_shreg & 0xbbbbbbbb);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_hapn4800(struct sm_state *sm)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+
+	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_8_tx = {
+	"hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800, 
+	modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_8_rx = {
+	"hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, 
+	demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_10_tx = {
+	"hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800,
+	modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_10_rx = {
+	"hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, 
+	demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm8_tx = {
+	"hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800, 
+	modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm8_rx = {
+	"hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, 
+	demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm10_tx = {
+	"hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800,
+	modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm10_rx = {
+	"hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
+	demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_psk4800.c linux/drivers/net/soundmodem/sm_psk4800.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_psk4800.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_psk4800.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,418 @@
+/*****************************************************************************/
+
+/*
+ *	sm_psk4800.c  -- soundcard radio modem driver, 4800 baud 8PSK modem
+ *
+ *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_psk4800.h"
+
+/* --------------------------------------------------------------------- */
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+#define SCRAM_SHIFT 17
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_psk48 {
+	/*
+	 * input mixer and lowpass
+	 */
+	short infi[PSK48_RXF_LEN/2], infq[PSK48_RXF_LEN/2];
+	unsigned int downmixer;
+	int ovrphase;
+	short magi, magq;
+	/*
+	 * sampling instant recovery
+	 */
+	int pwrhist[5];
+	unsigned int s_phase;
+	int cur_sync;
+	/*
+	 * phase recovery
+	 */
+	short cur_phase_dev;
+	short last_ph_err;
+	unsigned short pskph;
+	unsigned int phase;
+	unsigned short last_pskph;
+	unsigned char cur_raw, last_raw, rawbits;
+	/*
+	 * decoding
+	 */
+	unsigned int shreg;
+	unsigned long descram;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+};
+
+struct mod_state_psk48 {
+	unsigned char txbits[PSK48_TXF_NUMSAMPLES];
+	unsigned short txphase;
+	unsigned int shreg;
+	unsigned long scram;
+	const short *tbl;
+	unsigned int txseq;
+};
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_4800_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
+	int i, j;
+	int si, sq;
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->txseq++) {
+			memmove(st->txbits+1, st->txbits, 
+				sizeof(st->txbits)-sizeof(st->txbits[0]));
+			for (i = 0; i < 3; i++) {
+				if (st->shreg <= 1)
+					st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+				st->scram = (st->scram << 1) |
+					(st->shreg & 1);
+				st->shreg >>= 1;
+				if (st->scram & SCRAM_TAP1)
+					st->scram ^= SCRAM_TAPN;
+			}
+			j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
+			st->txbits[0] -= (j ^ (j >> 1));
+			st->txbits[0] &= 7;
+			st->tbl = psk48_tx_table;
+		}
+		if (st->txseq >= PSK48_TXF_OVERSAMPLING)
+			st->txseq = 0;
+		for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
+			si += st->tbl[st->txbits[j]];
+			sq += st->tbl[st->txbits[j]+8];
+		}
+		*buf = ((si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 23) + 0x80;
+		st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_4800_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
+	int i, j;
+	int si, sq;
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->txseq++) {
+			memmove(st->txbits+1, st->txbits, 
+				sizeof(st->txbits)-sizeof(st->txbits[0]));
+			for (i = 0; i < 3; i++) {
+				if (st->shreg <= 1)
+					st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+				st->scram = (st->scram << 1) |
+					(st->shreg & 1);
+				st->shreg >>= 1;
+				if (st->scram & SCRAM_TAP1)
+					st->scram ^= SCRAM_TAPN;
+			}
+			j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
+			st->txbits[0] -= (j ^ (j >> 1));
+			st->txbits[0] &= 7;
+			st->tbl = psk48_tx_table;
+		}
+		if (st->txseq >= PSK48_TXF_OVERSAMPLING)
+			st->txseq = 0;
+		for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
+			si += st->tbl[st->txbits[j]];
+			sq += st->tbl[st->txbits[j]+8];
+		}
+		*buf = (si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 15;
+		st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ unsigned short tbl_atan(short q, short i)
+{
+	short tmp;
+	unsigned short argoffs = 0;
+
+	if (i == 0 && q == 0)
+		return 0;
+	switch (((q < 0) << 1) | (i < 0)) {
+	case 0:
+		break;
+	case 1:
+		tmp = q;
+		q = -i;
+		i = tmp;
+		argoffs = 0x4000;
+		break;
+	case 3:
+		q = -q;
+		i = -i;
+		argoffs = 0x8000;
+		break;
+	case 2:
+		tmp = -q;
+		q = i;
+		i = tmp;
+		argoffs = 0xc000;
+		break;
+	}
+	if (q > i) {
+		tmp = i / q * ATAN_TABLEN;
+		return (argoffs+0x4000-atan_tab[((i<<15)/q*ATAN_TABLEN>>15)])
+			&0xffffu;
+	}
+	return (argoffs+atan_tab[((q<<15)/i*ATAN_TABLEN)>>15])&0xffffu;
+}
+
+#define ATAN(q,i) tbl_atan(q, i)
+
+/* --------------------------------------------------------------------- */
+
+static void demod_psk48_baseband(struct sm_state *sm, struct demod_state_psk48 *st,
+				 short vali, short valq)
+{
+	int i, j;
+
+	st->magi = vali;
+	st->magq = valq;
+	memmove(st->pwrhist+1, st->pwrhist, 
+		sizeof(st->pwrhist)-sizeof(st->pwrhist[0]));
+	st->pwrhist[0] = st->magi * st->magi +
+		st->magq * st->magq;
+	st->cur_sync = ((st->pwrhist[4] >> 2) > st->pwrhist[2] && 
+			(st->pwrhist[0] >> 2) > st->pwrhist[2] &&
+			st-> pwrhist[3] > st->pwrhist[2] && 
+			st->pwrhist[1] > st->pwrhist[2]);
+	st->s_phase &= 0xffff;
+	st->s_phase += PSK48_SPHASEINC;
+	st->dcd_shreg <<= 1;
+	if (st->cur_sync) {
+		if (st->s_phase >= (0x8000 + 5*PSK48_SPHASEINC/2))
+			st->s_phase -= PSK48_SPHASEINC/6;
+		else
+			st->s_phase += PSK48_SPHASEINC/6;
+		st->dcd_sum0 = 4*hweight8(st->dcd_shreg & 0xf8)-
+			hweight16(st->dcd_shreg & 0x1f00);
+	}
+	if ((--st->dcd_time) <= 0) {
+		hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 + 
+					   st->dcd_sum2) < 0);
+		st->dcd_sum2 = st->dcd_sum1;
+		st->dcd_sum1 = st->dcd_sum0;
+		st->dcd_sum0 = 2; /* slight bias */
+		st->dcd_time = 240;
+	}
+	if (st->s_phase < 0x10000)
+		return;
+	/*
+	 * sample one constellation
+	 */
+	st->last_pskph = st->pskph;
+	st->pskph = (ATAN(st->magq, st->magi)-
+			       st->phase) & 0xffffu;
+	st->last_ph_err = (st->pskph & 0x1fffu) - 0x1000;
+	st->phase += st->last_ph_err/16;
+	st->last_raw = st->cur_raw;
+	st->cur_raw = ((st->pskph >> 13) & 7);
+	i = (st->cur_raw - st->last_raw) & 7;
+	st->rawbits = i ^ (i >> 1) ^ (i >> 2);
+	st->descram = (st->descram << 3) | (st->rawbits);
+	hdlcdrv_channelbit(&sm->hdrv, st->descram & 4);
+	hdlcdrv_channelbit(&sm->hdrv, st->descram & 2);
+	hdlcdrv_channelbit(&sm->hdrv, st->descram & 1);
+	i = (((st->descram >> DESCRAM_TAPSH1) & 7) ^
+	     ((st->descram >> DESCRAM_TAPSH2) & 7) ^
+	     ((st->descram >> DESCRAM_TAPSH3) & 7));
+ 	for (j = 4; j; j >>= 1) {
+		st->shreg >>= 1;
+		st->shreg |= (!!(i & j)) << 16;
+		if (st->shreg & 1) {
+			hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+			st->shreg = 0x10000;
+		}
+	}
+
+#if 0
+	st->dcd_shreg <<= 1;
+	st->bit_pll += 0x4000;
+	curbit = (*buf >= 0x80);
+	if (st->last_sample ^ curbit) {
+		st->dcd_shreg |= 1;
+		st->bit_pll += pll_corr
+			[st->bit_pll < 0xa000];
+		st->dcd_sum0 += 8 * 
+			hweight8(st->dcd_shreg & 0x0c) - 
+				!!(st->dcd_shreg & 0x10);
+	}
+	st->last_sample = curbit;
+	hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+	if ((--st->dcd_time) <= 0) {
+		hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+					   st->dcd_sum1 + 
+					   st->dcd_sum2) < 0);
+		st->dcd_sum2 = st->dcd_sum1;
+		st->dcd_sum1 = st->dcd_sum0;
+		st->dcd_sum0 = 2; /* slight bias */
+		st->dcd_time = 240;
+	}
+	if (st->bit_pll >= 0x10000) {
+		st->bit_pll &= 0xffffu;
+		st->descram = (st->descram << 1) | curbit;
+		descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+	}
+	diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+#endif
+	
+	diag_trigger(sm);
+	diag_add_constellation(sm, (vali*COS(st->phase)+ valq*SIN(st->phase)) >> 13, 
+			       (valq*COS(st->phase) - vali*SIN(st->phase)) >> 13);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_4800_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
+	int i, si, sq;
+	const short *coeff;
+
+	for (; buflen > 0; buflen--, buf++) {
+		memmove(st->infi+1, st->infi, 
+			sizeof(st->infi)-sizeof(st->infi[0]));
+		memmove(st->infq+1, st->infq, 
+			sizeof(st->infq)-sizeof(st->infq[0]));
+		si = *buf;
+		si &= 0xff;
+		si -= 128;
+		diag_add_one(sm, si << 8);
+		st->infi[0] = (si * COS(st->downmixer))>>7;
+		st->infq[0] = (si * SIN(st->downmixer))>>7;
+		st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
+		for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+		for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_4800_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
+	int i, si, sq;
+	const short *coeff;
+
+	for (; buflen > 0; buflen--, buf++) {
+		memmove(st->infi+1, st->infi, 
+			sizeof(st->infi)-sizeof(st->infi[0]));
+		memmove(st->infq+1, st->infq, 
+			sizeof(st->infq)-sizeof(st->infq[0]));
+		si = *buf;
+		diag_add_one(sm, si);
+		st->infi[0] = (si * COS(st->downmixer))>>15;
+		st->infq[0] = (si * SIN(st->downmixer))>>15;
+		st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
+		for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+		for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void mod_init_4800(struct sm_state *sm)
+{
+	struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
+
+	st->scram = 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_4800(struct sm_state *sm)
+{
+	struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
+
+	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_psk4800_tx = {
+	"psk4800", sizeof(struct mod_state_psk48), 
+	PSK48_SAMPLERATE, 4800,
+	modulator_4800_u8, modulator_4800_s16, mod_init_4800
+};
+
+const struct modem_rx_info sm_psk4800_rx = {
+	"psk4800", sizeof(struct demod_state_psk48), 
+	PSK48_SAMPLERATE, 4800, 1, PSK48_TXF_OVERSAMPLING, 
+	demodulator_4800_u8, demodulator_4800_s16, demod_init_4800
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_sbc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_sbc.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,941 @@
+/*****************************************************************************/
+
+/*
+ *	sm_sbc.c  -- soundcard radio modem driver soundblaster hardware driver
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include <linux/delay.h>
+#include "sm.h"
+#include "smdma.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_sbc {
+	unsigned char revhi, revlo;
+	unsigned char fmt[2];
+	unsigned int sr[2];
+};
+
+#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+/* 
+ * the sbc converter's registers 
+ */
+#define DSP_RESET(iobase)        (iobase+0x6)
+#define DSP_READ_DATA(iobase)    (iobase+0xa)
+#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
+#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
+#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
+#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
+#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
+#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
+#define SBC_EXTENT               16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+#define SBC_OUTPUT             0x14
+#define SBC_INPUT              0x24
+#define SBC_BLOCKSIZE          0x48
+#define SBC_HI_OUTPUT          0x91 
+#define SBC_HI_INPUT           0x99 
+#define SBC_LO_OUTPUT_AUTOINIT 0x1c
+#define SBC_LO_INPUT_AUTOINIT  0x2c
+#define SBC_HI_OUTPUT_AUTOINIT 0x90 
+#define SBC_HI_INPUT_AUTOINIT  0x98
+#define SBC_IMMED_INT          0xf2
+#define SBC_GET_REVISION       0xe1
+#define ESS_GET_REVISION       0xe7
+#define SBC_SPEAKER_ON         0xd1
+#define SBC_SPEAKER_OFF        0xd3
+#define SBC_DMA_ON             0xd0
+#define SBC_DMA_OFF            0xd4
+#define SBC_SAMPLE_RATE        0x40
+#define SBC_SAMPLE_RATE_OUT    0x41
+#define SBC_SAMPLE_RATE_IN     0x42
+#define SBC_MONO_8BIT          0xa0
+#define SBC_MONO_16BIT         0xa4
+#define SBC_STEREO_8BIT        0xa8
+#define SBC_STEREO_16BIT       0xac
+
+#define SBC4_OUT8_AI           0xc6
+#define SBC4_IN8_AI            0xce
+#define SBC4_MODE_UNS_MONO     0x00
+#define SBC4_MODE_SIGN_MONO    0x10
+
+#define SBC4_OUT16_AI          0xb6
+#define SBC4_IN16_AI           0xbe
+
+/* --------------------------------------------------------------------- */
+
+static int inline reset_dsp(struct device *dev)
+{
+	int i;
+
+	outb(1, DSP_RESET(dev->base_addr));
+	udelay(300);
+	outb(0, DSP_RESET(dev->base_addr));
+	for (i = 0; i < 0xffff; i++)
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
+			if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
+				return 1;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline write_dsp(struct device *dev, unsigned char data)
+{
+	int i;
+	
+	for (i = 0; i < 0xffff; i++)
+		if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
+			outb(data, DSP_WRITE_DATA(dev->base_addr));
+			return;
+		}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline read_dsp(struct device *dev, unsigned char *data)
+{
+	int i;
+
+	if (!data)
+		return 0;
+	for (i = 0; i < 0xffff; i++) 
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
+			*data = inb(DSP_READ_DATA(dev->base_addr));
+			return 1;
+		}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int config_resources(struct device *dev, struct sm_state *sm, int fdx)
+{
+	unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
+	unsigned long flags;
+
+	switch (dev->irq) {
+	case 2:
+	case 9:
+		irqreg |= 0x01;
+		break;
+
+	case 5:
+		irqreg |= 0x02;
+		break;
+
+	case 7:
+		irqreg |= 0x04;
+		break;
+
+	case 10:
+		irqreg |= 0x08;
+		break;
+		
+	default:
+		return -ENODEV;
+	}
+
+	switch (dev->dma) {
+	case 0:
+		dmareg |= 0x01;
+		break;
+
+	case 1:
+		dmareg |= 0x02;
+		break;
+
+	case 3:
+		dmareg |= 0x08;
+		break;
+
+	default:
+		return -ENODEV;
+	}
+		
+	if (fdx) {
+		switch (sm->hdrv.ptt_out.dma2) {
+		case 5:
+			dmareg |= 0x20;
+			break;
+			
+		case 6:
+			dmareg |= 0x40;
+			break;
+			
+		case 7:
+			dmareg |= 0x80;
+			break;
+			
+		default:
+			return -ENODEV;
+		}
+	}
+	save_flags(flags);
+	cli();
+	outb(0x80, DSP_MIXER_ADDR(dev->base_addr));
+	outb(irqreg, DSP_MIXER_DATA(dev->base_addr));
+	realirq = inb(DSP_MIXER_DATA(dev->base_addr));
+	outb(0x81, DSP_MIXER_ADDR(dev->base_addr));
+	outb(dmareg, DSP_MIXER_DATA(dev->base_addr));
+	realdma = inb(DSP_MIXER_DATA(dev->base_addr));
+	restore_flags(flags);
+	if ((~realirq) & irqreg || (~realdma) & dmareg) {
+		printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
+		       "and IRQ/DMA specified wrongly?\n", sm_drvname);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack_8bit(struct device *dev)
+{
+	inb(DSP_DATA_AVAIL(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack_16bit(struct device *dev)
+{
+	inb(DSP_INTACK_16BIT(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send)
+{
+        unsigned long flags;
+        static const unsigned char sbcmode[2][2] = {
+		{ SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, 
+		{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }
+	};
+	static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
+	static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
+	unsigned int nsamps;
+
+	send = !!send;
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        sbc_int_ack_8bit(dev);
+        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
+        write_dsp(dev, SCSTATE->fmt[send]);
+        write_dsp(dev, sbcskr[send]); 
+	nsamps = dma_setup(sm, send, dev->dma) - 1;
+        sbc_int_ack_8bit(dev);
+	if (SCSTATE->revhi >= 4) {
+		write_dsp(dev, sbc4mode[send]);
+		write_dsp(dev, SBC4_MODE_UNS_MONO);
+		write_dsp(dev, nsamps & 0xff);
+		write_dsp(dev, nsamps >> 8);
+	} else {
+		write_dsp(dev, SBC_BLOCKSIZE);
+		write_dsp(dev, nsamps & 0xff);
+		write_dsp(dev, nsamps >> 8);
+		write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
+		/* hispeed mode if sample rate > 13kHz */
+	}
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned int curfrag;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	cli();
+ 	sbc_int_ack_8bit(dev);
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag);
+	enable_dma(dev->dma);
+	sm_int_freq(sm);
+	sti();
+	if (sm->dma.ptt_cnt <= 0) {
+		dma_receive(sm, curfrag);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+		if (hdlcdrv_ptt(&sm->hdrv)) {
+			/* starting to transmit */
+			disable_dma(dev->dma);
+			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
+			dma_start_transmit(sm);
+			setup_dma_dsp(dev, sm, 1);
+			dma_transmit(sm);
+		}
+	} else if (dma_end_transmit(sm, curfrag)) {
+		/* stopping transmission */
+		disable_dma(dev->dma);
+		sti();
+		dma_init_receive(sm);
+		setup_dma_dsp(dev, sm, 0);
+        } else
+		dma_transmit(sm);
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_open(struct device *dev, struct sm_state *sm) 
+{
+	int err;
+	unsigned int dmasz, u;
+
+	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
+		       sizeof(struct sc_state_sbc), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SBC_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (!reset_dsp(dev)) {
+		printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
+		       sm_drvname, dev->base_addr);
+		return -ENODEV;
+	}
+	write_dsp(dev, SBC_GET_REVISION);
+	if (!read_dsp(dev, &SCSTATE->revhi) || 
+	    !read_dsp(dev, &SCSTATE->revlo))
+		return -ENODEV;
+	printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, 
+	       SCSTATE->revhi, SCSTATE->revlo);
+	if (SCSTATE->revhi < 2) {
+		printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
+		       "rev 2.00 required\n", sm_drvname);
+		return -ENODEV;
+	}
+	if (SCSTATE->revhi < 3 && 
+	    (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
+		printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
+		       "old, at least 3.00 required\n", sm_drvname,
+		       dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
+		return -ENODEV;
+	}
+	if (SCSTATE->revhi >= 4 && 
+	    (err = config_resources(dev, sm, 0))) {
+		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+		return err;
+	}
+	/*
+	 * initialize some variables
+	 */
+	dma_init_receive(sm);
+	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
+	u = NUM_FRAGMENTS * sm->dma.ofragsz;
+	if (u > dmasz)
+		dmasz = u;
+	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_dsp(dev, sm, 0);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	reset_dsp(dev);	
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, SBC_EXTENT);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+			continue;
+		if (!(*mtp)->modulator_u8)
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if (!(*mrp)->demodulator_u8)
+				continue;
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/
+							 sm->mode_rx->srate);
+				SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/
+							 sm->mode_tx->srate);
+				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				sm->dma.i16bit = sm->dma.o16bit = 0;
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+		     struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_ioctl bi;
+	unsigned long flags;
+	int i;
+	
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case SMCTL_GETMIXER:
+		i = 0;
+		bi.data.mix.sample_rate = sm->mode_rx->srate;
+		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+		bi.data.mix.mixer_type = SM_MIXER_INVALID;
+		switch (SCSTATE->revhi) {
+		case 2:
+			bi.data.mix.mixer_type = SM_MIXER_CT1335;
+			break;
+		case 3:
+			bi.data.mix.mixer_type = SM_MIXER_CT1345;
+			break;
+		case 4:
+			bi.data.mix.mixer_type = SM_MIXER_CT1745;
+			break;
+		}
+		if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
+		    bi.data.mix.reg < 0x80) {
+			save_flags(flags);
+			cli();
+			outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+			bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
+			restore_flags(flags);
+			i = 1;
+		}
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return i;
+		
+	case SMCTL_SETMIXER:
+		if (!suser())
+			return -EACCES;
+		switch (SCSTATE->revhi) {
+		case 2:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
+				return -EINVAL;
+			break;
+		case 3:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
+				return -EINVAL;
+			break;
+		case 4:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
+				return -EINVAL;
+			break;
+		default:
+			return -ENODEV;
+		}
+		if (bi.data.mix.reg >= 0x80)
+			return -EACCES;
+		save_flags(flags);
+		cli();
+		outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+		outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
+		restore_flags(flags);
+		return 0;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbc = {
+	"sbc", sizeof(struct sc_state_sbc), 
+	sbc_open, sbc_close, sbc_ioctl, sbc_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm)
+{
+        unsigned long flags;
+	unsigned int isamps, osamps;
+
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        sbc_int_ack_8bit(dev);
+        sbc_int_ack_16bit(dev);
+	/* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */
+	write_dsp(dev, SBC_SAMPLE_RATE_IN);
+	write_dsp(dev, SCSTATE->sr[0] >> 8);
+	write_dsp(dev, SCSTATE->sr[0] & 0xff);
+	write_dsp(dev, SBC_SAMPLE_RATE_OUT);
+	write_dsp(dev, SCSTATE->sr[1] >> 8);
+	write_dsp(dev, SCSTATE->sr[1] & 0xff);
+        write_dsp(dev, SBC_SPEAKER_ON);
+	if (sm->dma.o16bit) {
+		/*
+		 * DMA channel 1 (8bit) does input (capture),
+		 * DMA channel 2 (16bit) does output (playback)
+		 */
+		isamps = dma_setup(sm, 0, dev->dma) - 1;
+		osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1;
+		sbc_int_ack_8bit(dev);
+		sbc_int_ack_16bit(dev);
+		write_dsp(dev, SBC4_IN8_AI);
+		write_dsp(dev, SBC4_MODE_UNS_MONO);
+		write_dsp(dev, isamps & 0xff);
+		write_dsp(dev, isamps >> 8);
+		write_dsp(dev, SBC4_OUT16_AI);
+		write_dsp(dev, SBC4_MODE_SIGN_MONO);
+		write_dsp(dev, osamps & 0xff);
+		write_dsp(dev, osamps >> 8);
+	} else {
+		/*
+		 * DMA channel 1 (8bit) does output (playback),
+		 * DMA channel 2 (16bit) does input (capture)
+		 */
+		isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
+		osamps = dma_setup(sm, 1, dev->dma) - 1;
+		sbc_int_ack_8bit(dev);
+		sbc_int_ack_16bit(dev);
+		write_dsp(dev, SBC4_OUT8_AI);
+		write_dsp(dev, SBC4_MODE_UNS_MONO);
+		write_dsp(dev, osamps & 0xff);
+		write_dsp(dev, osamps >> 8);
+		write_dsp(dev, SBC4_IN16_AI);
+		write_dsp(dev, SBC4_MODE_SIGN_MONO);
+		write_dsp(dev, isamps & 0xff);
+		write_dsp(dev, isamps >> 8);
+	}
+	dma_init_receive(sm);
+	dma_init_transmit(sm);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned char intsrc, pbint = 0, captint = 0;
+	unsigned int ocfrag, icfrag;
+	unsigned long flags;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	save_flags(flags);
+	cli();
+	outb(0x82, DSP_MIXER_ADDR(dev->base_addr));
+	intsrc = inb(DSP_MIXER_DATA(dev->base_addr));
+	if (intsrc & 0x01) {
+		sbc_int_ack_8bit(dev);
+		if (sm->dma.o16bit) {
+			captint = 1;
+			disable_dma(dev->dma);
+			clear_dma_ff(dev->dma);
+			dma_ptr(sm, 0, dev->dma, &icfrag);
+			enable_dma(dev->dma);
+		} else {     
+			pbint = 1;
+			disable_dma(dev->dma);
+			clear_dma_ff(dev->dma);
+			dma_ptr(sm, 1, dev->dma, &ocfrag);
+			enable_dma(dev->dma);
+		}
+	}
+	if (intsrc & 0x02) {
+		sbc_int_ack_16bit(dev);
+		if (sm->dma.o16bit) {
+			pbint = 1;
+			disable_dma(sm->hdrv.ptt_out.dma2);
+			clear_dma_ff(sm->hdrv.ptt_out.dma2);
+			dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag);
+			enable_dma(sm->hdrv.ptt_out.dma2);
+		} else {
+			captint = 1;
+			disable_dma(sm->hdrv.ptt_out.dma2);
+			clear_dma_ff(sm->hdrv.ptt_out.dma2);
+			dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag);
+			enable_dma(sm->hdrv.ptt_out.dma2);
+		}
+	}
+	restore_flags(flags);
+	sm_int_freq(sm);
+	sti();
+	if (pbint) {
+		if (dma_end_transmit(sm, ocfrag))
+			dma_clear_transmit(sm);
+		dma_transmit(sm);
+	}
+	if (captint) { 
+		dma_receive(sm, icfrag);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+	}
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_open(struct device *dev, struct sm_state *sm) 
+{
+	int err;
+
+	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
+		       sizeof(struct sc_state_sbc), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SBC_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (!reset_dsp(dev)) {
+		printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
+		       sm_drvname, dev->base_addr);
+		return -ENODEV;
+	}
+	write_dsp(dev, SBC_GET_REVISION);
+	if (!read_dsp(dev, &SCSTATE->revhi) || 
+	    !read_dsp(dev, &SCSTATE->revlo))
+		return -ENODEV;
+	printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, 
+	       SCSTATE->revhi, SCSTATE->revlo);
+	if (SCSTATE->revhi < 4) {
+		printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname);
+		return -ENODEV;
+	}
+	if ((err = config_resources(dev, sm, 1))) {
+		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+		return err;
+	}
+	/*
+	 * initialize some variables
+	 */
+	if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
+		kfree(sm->dma.ibuf);
+		return -ENOMEM;
+	}
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		return -EBUSY;
+	}
+	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		free_dma(sm->hdrv.ptt_out.dma2);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_fdx_dsp(dev, sm);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+	reset_dsp(dev);	
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	free_dma(sm->hdrv.ptt_out.dma2);	
+	release_region(dev->base_addr, SBC_EXTENT);
+	kfree(sm->dma.ibuf);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 &&
+			    (*mrp)->srate == (*mtp)->srate) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->sr[0] = sm->mode_rx->srate;
+				SCSTATE->sr[1] = sm->mode_tx->srate;
+				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) {
+					sm->dma.i16bit = 1;
+					sm->dma.o16bit = 0;
+					sm->dma.ifragsz <<= 1;
+				} else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) {
+					sm->dma.i16bit = 0;
+					sm->dma.o16bit = 1;
+					sm->dma.ofragsz <<= 1;
+				} else {
+					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
+					       sm->mode_rx->name, sm->mode_tx->name);
+					sm->mode_tx = NULL;
+					sm->mode_rx = NULL;
+					return -EINVAL;
+				}
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	return sbc_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbcfdx = {
+	"sbcfdx", sizeof(struct sc_state_sbc), 
+	sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/sm_wss.c linux/drivers/net/soundmodem/sm_wss.c
--- v2.0.34/linux/drivers/net/soundmodem/sm_wss.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_wss.c	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,965 @@
+/*****************************************************************************/
+
+/*
+ *	sm_wss.c  -- soundcard radio modem driver, WSS (half duplex) driver
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include "sm.h"
+#include "smdma.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_wss {
+	unsigned char revwss, revid, revv, revcid;
+	unsigned char fmt[2];
+	unsigned char crystal;
+};
+
+#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+
+#define WSS_CONFIG(iobase)       (iobase+0)
+#define WSS_STATUS(iobase)       (iobase+3)
+#define WSS_CODEC_IA(iobase)     (iobase+4)
+#define WSS_CODEC_ID(iobase)     (iobase+5)
+#define WSS_CODEC_STATUS(iobase) (iobase+6)
+#define WSS_CODEC_DATA(iobase)   (iobase+7)
+
+#define WSS_EXTENT   8
+
+#define CS423X_HOTFIX
+
+/* --------------------------------------------------------------------- */
+
+static void write_codec(struct device *dev, unsigned char idx,
+			unsigned char data)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx, WSS_CODEC_IA(dev->base_addr));
+	outb(data, WSS_CODEC_ID(dev->base_addr));
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct device *dev, unsigned char idx)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr));
+	return inb(WSS_CODEC_ID(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern void inline wss_ack_int(struct device *dev)
+{
+	outb(0, WSS_CODEC_STATUS(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_srate_tab[16] = {
+	8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
+	-1, 37800, -1, 44100, 48000, 33075, 9600, 6620
+};
+
+static int wss_srate_index(int srate)
+{
+	int i;
+
+	for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
+		if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
+			return i;
+	return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, unsigned char fmt, 
+			     unsigned char fmt2, char fdx, char fullcalib)
+{
+	unsigned long time;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/* Clock and data format register */
+	write_codec(dev, 0x48, fmt);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 0x5c, fmt2 & 0xf0);
+		/* MCE and interface config reg */	
+		write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
+	} else 
+		/* MCE and interface config reg */
+		write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
+	outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
+	if (SCSTATE->crystal && !fullcalib)
+		return 0;
+	/*
+	 * wait for ACI start
+	 */
+	time = 1000;
+	while (!(read_codec(dev, 0x0b) & 0x20))
+		if (!(--time)) {
+			printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", 
+			       sm_drvname);
+			restore_flags(flags);
+			return -1;
+		}
+	/*
+	 * wait for ACI end
+	 */
+	sti();
+	time = jiffies + HZ/4;
+	while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
+	restore_flags(flags);
+	if ((signed)(jiffies - time) >= 0) {
+		printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", 
+		       sm_drvname);
+		return -1;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, 
+			  unsigned char src_l, unsigned char src_r, 
+			  int igain_l, int igain_r,
+			  int ogain_l, int ogain_r)
+{
+	unsigned char tmp, reg0, reg1, reg6, reg7;
+	static const signed char irqtab[16] = 
+	{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
+		  -1, -1 };
+	static const signed char dmatab[4] = { 1, 2, -1, 3 };
+	
+	tmp = inb(WSS_STATUS(dev->base_addr));
+	if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
+	    (tmp & 0x3f) != 0x0f) {
+		printk(KERN_WARNING "sm: WSS card id register not found, "
+		       "address 0x%lx, ID register 0x%02x\n", 
+		       dev->base_addr, (int)tmp);
+		/* return -1; */
+		SCSTATE->revwss = 0;
+	} else {
+		if ((tmp & 0x80) && ((dev->dma == 0) || 
+				     ((dev->irq >= 8) && (dev->irq != 9)))) {
+			printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
+			       "(except IRQ9) cannot be used on an 8bit "
+			       "card\n", sm_drvname);
+			return -1;
+		}		
+		if (dev->irq > 15 || irqtab[dev->irq] == -1) {
+			printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", 
+			       sm_drvname, (int)dev->irq);
+			return -1;
+		}
+		if (dev->dma > 3 || dmatab[dev->dma] == -1) {
+			printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", 
+			       sm_drvname, (int)dev->dma);
+			return -1;
+		}
+		tmp = irqtab[dev->irq] | dmatab[dev->dma];
+		/* irq probe */
+		outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
+		if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
+			outb(0, WSS_CONFIG(dev->base_addr));
+			printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", 
+			       sm_drvname, dev->irq);
+		}
+		outb(tmp, WSS_CONFIG(dev->base_addr));
+		SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
+	}
+	/*
+	 * initialize the codec
+	 */
+	if (igain_l < 0)
+		igain_l = 0;
+	if (igain_r < 0)
+		igain_r = 0;
+	if (ogain_l > 0)
+		ogain_l = 0;
+	if (ogain_r > 0)
+		ogain_r = 0;
+	reg0 = (src_l << 6) & 0xc0;
+	reg1 = (src_r << 6) & 0xc0;
+	if (reg0 == 0x80 && igain_l >= 20) {
+		reg0 |= 0x20;
+		igain_l -= 20;
+	}
+	if (reg1 == 0x80 && igain_r >= 20) {
+		reg1 |= 0x20;
+		igain_r -= 20;
+	}
+	if (igain_l > 23)
+		igain_l = 23;
+	if (igain_r > 23)
+		igain_r = 23;
+	reg0 |= igain_l * 2 / 3;
+	reg1 |= igain_r * 2 / 3;
+	reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
+	reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
+	write_codec(dev, 9, 0);
+	write_codec(dev, 0, 0x45);
+	if (read_codec(dev, 0) != 0x45)
+		goto codec_err;
+	write_codec(dev, 0, 0xaa);
+	if (read_codec(dev, 0) != 0xaa)
+		goto codec_err;
+	write_codec(dev, 12, 0x40); /* enable MODE2 */
+	write_codec(dev, 16, 0);
+	write_codec(dev, 0, 0x45);
+	SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
+	write_codec(dev, 0, 0xaa);
+	SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
+	if (SCSTATE->crystal) {
+		SCSTATE->revcid = read_codec(dev, 0x19);
+		SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
+		SCSTATE->revcid &= 7;
+		write_codec(dev, 0x10, 0x80); /* maximum output level */
+		write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
+		write_codec(dev, 0x12, 0x80); /* left line input control */
+		write_codec(dev, 0x13, 0x80); /* right line input control */
+		write_codec(dev, 0x16, 0); /* disable alternative freq sel */
+		write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
+		write_codec(dev, 0x1b, 0x00); /* left out no att */
+		write_codec(dev, 0x1d, 0x00); /* right out no att */
+	}
+
+	if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1))
+		goto codec_err;
+
+        write_codec(dev, 0, reg0); /* left input control */
+        write_codec(dev, 1, reg1); /* right input control */
+        write_codec(dev, 2, 0x80); /* left aux#1 input control */
+        write_codec(dev, 3, 0x80); /* right aux#1 input control */
+        write_codec(dev, 4, 0x80); /* left aux#2 input control */
+        write_codec(dev, 5, 0x80); /* right aux#2 input control */
+        write_codec(dev, 6, reg6); /* left dac control */
+        write_codec(dev, 7, reg7); /* right dac control */
+        write_codec(dev, 0xa, 0x2); /* pin control register */
+        write_codec(dev, 0xd, 0x0); /* digital mix control */
+	SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
+	/*
+	 * print revisions
+	 */
+	if (SCSTATE->crystal) 
+		printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
+		       " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
+		       (int)SCSTATE->revv, (int)SCSTATE->revcid);
+	else
+		printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", 
+		       sm_drvname, (int)SCSTATE->revwss, 
+		       (int)SCSTATE->revid);
+	return 0;
+ codec_err:
+	outb(0, WSS_CONFIG(dev->base_addr));
+	printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", 
+	       sm_drvname, dev->base_addr);
+	return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send)
+{
+        unsigned long flags;
+        static const unsigned char codecmode[2] = { 0x0e, 0x0d };
+	unsigned char oldcodecmode;
+	long abrt;
+	unsigned char fmt;
+	unsigned int numsamps;
+
+	send = !!send;
+	fmt = SCSTATE->fmt[send];
+	save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+	oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	wss_ack_int(dev);
+	if (read_codec(dev, 11) & 0x10) {
+		dma_setup(sm, oldcodecmode & 1, dev->dma);
+		abrt = 0;
+		while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
+	}
+#ifdef CS423X_HOTFIX
+	if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
+		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#else /* CS423X_HOTFIX */
+	if (read_codec(dev, 0x8) != fmt)
+		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#endif /* CS423X_HOTFIX */
+	numsamps = dma_setup(sm, send, dev->dma) - 1;
+	write_codec(dev, 15, numsamps & 0xff);
+	write_codec(dev, 14, numsamps >> 8);
+	write_codec(dev, 9, codecmode[send]);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned int curfrag;
+	unsigned int nums;
+
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
+	    sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	cli();
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
+	write_codec(dev, 15, nums  & 0xff);
+	write_codec(dev, 14, nums >> 8);
+	enable_dma(dev->dma);
+	sm_int_freq(sm);
+	sti();
+	if (sm->dma.ptt_cnt <= 0) {
+		dma_receive(sm, curfrag);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+		if (hdlcdrv_ptt(&sm->hdrv)) {
+			/* starting to transmit */
+			disable_dma(dev->dma);
+			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
+			dma_start_transmit(sm);
+			setup_dma_wss(dev, sm, 1);
+			dma_transmit(sm);
+		}
+	} else if (dma_end_transmit(sm, curfrag)) {
+		/* stopping transmission */
+		disable_dma(dev->dma);
+		dma_init_receive(sm);
+		setup_dma_wss(dev, sm, 0);
+        } else
+		dma_transmit(sm);
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_open(struct device *dev, struct sm_state *sm) 
+{
+	unsigned int dmasz, u;
+
+	if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
+		printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", 
+		       sizeof(struct sc_state_wss), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	dma_init_receive(sm);
+	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
+	u = NUM_FRAGMENTS * sm->dma.ofragsz;
+	if (u > dmasz)
+		dmasz = u;
+	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_wss(dev, sm, 0);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int i, j;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    ((j = wss_srate_index((*mrp)->srate)) >= 0)) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = j;
+				SCSTATE->fmt[1] = i;
+				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				/* prefer same data format if possible to minimize switching times */
+				sm->dma.i16bit = sm->dma.o16bit = 2;
+				if (sm->mode_rx->srate == sm->mode_tx->srate) {
+					if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16)
+						sm->dma.i16bit = sm->dma.o16bit = 1;
+					else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8)
+						sm->dma.i16bit = sm->dma.o16bit = 0;
+				}
+				if (sm->dma.i16bit == 2) {
+					if (sm->mode_rx->demodulator_s16)
+						sm->dma.i16bit = 1;
+					else if (sm->mode_rx->demodulator_u8)
+						sm->dma.i16bit = 0;
+				}
+				if (sm->dma.o16bit == 2) {
+					if (sm->mode_tx->modulator_s16)
+						sm->dma.o16bit = 1;
+					else if (sm->mode_tx->modulator_u8)
+						sm->dma.o16bit = 0;
+				}
+				if (sm->dma.i16bit == 2 ||  sm->dma.o16bit == 2) {
+					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
+					       sm->mode_rx->name, sm->mode_tx->name);
+					sm->mode_tx = NULL;
+					sm->mode_rx = NULL;
+					return -EINVAL;
+				}
+#ifdef __BIG_ENDIAN
+				/* big endian 16bit only works on crystal cards... */
+				if (sm->dma.i16bit) {
+					SCSTATE->fmt[0] |= 0xc0;
+					sm->dma.ifragsz <<= 1;
+				}
+				if (sm->dma.o16bit) {
+					SCSTATE->fmt[1] |= 0xc0;
+					sm->dma.ofragsz <<= 1;
+				}
+#else /* __BIG_ENDIAN */
+				if (sm->dma.i16bit) {
+					SCSTATE->fmt[0] |= 0x40;
+					sm->dma.ifragsz <<= 1;
+				}
+				if (sm->dma.o16bit) {
+					SCSTATE->fmt[1] |= 0x40;
+					sm->dma.ofragsz <<= 1;
+				}
+#endif /* __BIG_ENDIAN */
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+		     struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_ioctl bi;
+	int i;
+	
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case SMCTL_GETMIXER:
+		i = 0;
+		bi.data.mix.sample_rate = sm->mode_rx->srate;
+		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+		bi.data.mix.mixer_type = SCSTATE->crystal ? 
+			SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
+		if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) 
+		     >> bi.data.mix.reg) & 1) {
+			bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
+			i = 1;
+		}
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return i;
+
+	case SMCTL_SETMIXER:
+		if (!suser())
+			return -EACCES;
+		if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || 
+		     !SCSTATE->crystal) &&
+		    (bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
+		     bi.data.mix.reg >= 0x10))
+			return -EINVAL;
+		if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
+			return -EACCES;
+		write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
+		return 0;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wss = {
+	"wss", sizeof(struct sc_state_wss), 
+	wss_open, wss_close, wss_ioctl, wss_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm)
+{
+        unsigned long flags;
+	unsigned char oldcodecmode, codecdma;
+	long abrt;
+	unsigned int osamps, isamps;
+	
+        save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+	oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0); /* disable codec DMA */
+	wss_ack_int(dev);
+	if ((codecdma = read_codec(dev, 11)) & 0x10) {
+		dma_setup(sm, 1, dev->dma);
+		dma_setup(sm, 0, sm->hdrv.ptt_out.dma2);
+		abrt = 0;
+		while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000));
+	}
+       	wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1);
+	osamps = dma_setup(sm, 1, dev->dma) - 1;
+	isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
+	write_codec(dev, 15, osamps & 0xff);
+	write_codec(dev, 14, osamps >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, isamps & 0xff);
+		write_codec(dev, 30, isamps >> 8);
+	}
+	write_codec(dev, 9, 3);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned long flags;
+	unsigned char cry_int_src;
+	unsigned icfrag, ocfrag, isamps, osamps;
+
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
+	    sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	save_flags(flags);
+	cli();
+	if (SCSTATE->crystal) { 
+		/* Crystal has an essentially different interrupt handler! */
+		cry_int_src = read_codec(dev, 0x18);
+		wss_ack_int(dev);
+		if (cry_int_src & 0x10) {       /* playback interrupt */
+			disable_dma(dev->dma);
+			clear_dma_ff(dev->dma);
+			osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
+			write_codec(dev, 15, osamps & 0xff);
+			write_codec(dev, 14, osamps >> 8);
+			enable_dma(dev->dma);
+		}
+		if (cry_int_src & 0x20) {       /* capture interrupt */
+			disable_dma(sm->hdrv.ptt_out.dma2);
+			clear_dma_ff(sm->hdrv.ptt_out.dma2);
+			isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
+			write_codec(dev, 31, isamps & 0xff);
+			write_codec(dev, 30, isamps >> 8);
+			enable_dma(sm->hdrv.ptt_out.dma2);
+		}
+		restore_flags(flags);
+		sm_int_freq(sm);
+		sti();
+		if (cry_int_src & 0x10) {
+			if (dma_end_transmit(sm, ocfrag))
+				dma_clear_transmit(sm);
+			dma_transmit(sm);
+		}
+		if (cry_int_src & 0x20) { 
+			dma_receive(sm, icfrag);
+			hdlcdrv_arbitrate(dev, &sm->hdrv);
+		}
+		sm_output_status(sm);
+		hdlcdrv_transmitter(dev, &sm->hdrv);
+		hdlcdrv_receiver(dev, &sm->hdrv);
+		return;
+	}
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+	clear_dma_ff(dev->dma);
+	clear_dma_ff(sm->hdrv.ptt_out.dma2);
+	osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
+	isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
+	write_codec(dev, 15, osamps & 0xff);
+	write_codec(dev, 14, osamps >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, isamps & 0xff);
+		write_codec(dev, 30, isamps >> 8);
+	}
+	enable_dma(dev->dma);
+	enable_dma(sm->hdrv.ptt_out.dma2);
+	restore_flags(flags);
+	sm_int_freq(sm);
+	sti();
+	if (dma_end_transmit(sm, ocfrag))
+		dma_clear_transmit(sm);
+	dma_transmit(sm);
+	dma_receive(sm, icfrag);
+	hdlcdrv_arbitrate(dev, &sm->hdrv);
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_open(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
+		kfree(sm->dma.ibuf);
+		return -ENOMEM;
+	}
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		return -EBUSY;
+	}
+	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		free_dma(sm->hdrv.ptt_out.dma2);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+	setup_fdx_dma_wss(dev, sm);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	free_dma(sm->hdrv.ptt_out.dma2);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree(sm->dma.ibuf);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int i;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mtp)->srate == (*mrp)->srate) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
+				sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				sm->dma.i16bit = sm->dma.o16bit = 2;
+				if (sm->mode_rx->demodulator_s16) {
+					sm->dma.i16bit = 1;
+					sm->dma.ifragsz <<= 1;
+#ifdef __BIG_ENDIAN    /* big endian 16bit only works on crystal cards... */
+					SCSTATE->fmt[0] |= 0xc0;
+#else /* __BIG_ENDIAN */
+					SCSTATE->fmt[0] |= 0x40;
+#endif /* __BIG_ENDIAN */
+				} else if (sm->mode_rx->demodulator_u8)
+					sm->dma.i16bit = 0;
+				if (sm->mode_tx->modulator_s16) {
+					sm->dma.o16bit = 1;
+					sm->dma.ofragsz <<= 1;
+#ifdef __BIG_ENDIAN    /* big endian 16bit only works on crystal cards... */
+					SCSTATE->fmt[1] |= 0xc0;
+#else /* __BIG_ENDIAN */
+					SCSTATE->fmt[1] |= 0x40;
+#endif /* __BIG_ENDIAN */
+				} else if (sm->mode_tx->modulator_u8)
+					sm->dma.o16bit = 0;
+				if (sm->dma.i16bit == 2 ||  sm->dma.o16bit == 2) {
+					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
+					       sm->mode_rx->name, sm->mode_tx->name);
+					sm->mode_tx = NULL;
+					sm->mode_rx = NULL;
+					return -EINVAL;
+				}
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
+			HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
+			HDLCDRV_PARMASK_MIDIIOBASE;
+
+	return wss_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wssfdx = {
+	"wssfdx", sizeof(struct sc_state_wss), 
+	wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+#undef SCSTATE
diff -u --recursive --new-file v2.0.34/linux/drivers/net/soundmodem/smdma.h linux/drivers/net/soundmodem/smdma.h
--- v2.0.34/linux/drivers/net/soundmodem/smdma.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/smdma.h	Mon Jul 13 13:47:31 1998
@@ -0,0 +1,217 @@
+/*****************************************************************************/
+
+/*
+ *	smdma.h  --  soundcard radio modem driver dma buffer routines.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#ifndef _SMDMA_H
+#define _SMDMA_H
+
+/* ---------------------------------------------------------------------- */
+
+#include "sm.h"
+
+/* ---------------------------------------------------------------------- */
+
+#define DMA_MODE_AUTOINIT      0x10
+#define NUM_FRAGMENTS          4
+
+/*
+ * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
+ * for the modulator to fill the whole DMA buffer without underrun
+ * at the highest possible baud rate, otherwise the TX state machine will
+ * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
+ */ 
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== DMA buffer management ===========================
+ */
+
+/*
+ * returns the number of samples per fragment
+ */
+extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr)
+{
+	if (send) {
+		disable_dma(dmanr);
+		clear_dma_ff(dmanr);
+		set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+		set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf));
+		set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS);
+		enable_dma(dmanr);
+		if (sm->dma.o16bit)
+			return sm->dma.ofragsz/2;
+		return sm->dma.ofragsz;
+	} else {
+		disable_dma(dmanr);
+		clear_dma_ff(dmanr);
+		set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+		set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf));
+		set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS);
+		enable_dma(dmanr);
+		if (sm->dma.i16bit)
+			return sm->dma.ifragsz/2;
+		return sm->dma.ifragsz;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr,
+				       unsigned int *curfrag)
+{
+	unsigned int dmaptr, sz, frg, offs;
+	
+	dmaptr = get_dma_residue(dmanr);
+	if (send) {
+		sz = sm->dma.ofragsz * NUM_FRAGMENTS;
+		if (dmaptr == 0 || dmaptr > sz)
+			dmaptr = sz;
+		dmaptr--;
+		frg = dmaptr / sm->dma.ofragsz;
+		offs = (dmaptr % sm->dma.ofragsz) + 1;
+		*curfrag = NUM_FRAGMENTS - 1 - frg;
+#ifdef SM_DEBUG
+		if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
+			sm->debug_vals.dma_residue = offs;
+#endif /* SM_DEBUG */
+		if (sm->dma.o16bit)
+			return offs/2;
+		return offs;
+	} else {
+		sz = sm->dma.ifragsz * NUM_FRAGMENTS;
+		if (dmaptr == 0 || dmaptr > sz)
+			dmaptr = sz;
+		dmaptr--;
+		frg = dmaptr / sm->dma.ifragsz;
+		offs = (dmaptr % sm->dma.ifragsz) + 1;
+		*curfrag = NUM_FRAGMENTS - 1 - frg;
+#ifdef SM_DEBUG
+		if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
+			sm->debug_vals.dma_residue = offs;
+#endif /* SM_DEBUG */
+		if (sm->dma.i16bit)
+			return offs/2;
+		return offs;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag)
+{
+	unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS;
+
+	sm->dma.ofragptr = curfrag;
+	if (sm->dma.ptt_cnt <= 0) {
+		sm->dma.ptt_cnt = 0;
+		return 0;
+	}
+	sm->dma.ptt_cnt -= diff;
+	if (sm->dma.ptt_cnt <= 0) {
+		sm->dma.ptt_cnt = 0;
+		return -1;
+	}
+	return 0;
+}
+
+extern __inline__ void dma_transmit(struct sm_state *sm)
+{
+	void *p;
+
+	while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) {
+		p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz *
+			((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS);
+		if (sm->dma.o16bit) {
+			time_exec(sm->debug_vals.mod_cyc, 
+				  sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2));
+		} else {
+			time_exec(sm->debug_vals.mod_cyc, 
+				  sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz));
+		}
+		sm->dma.ptt_cnt++;
+	}
+}
+
+extern __inline__ void dma_init_transmit(struct sm_state *sm)
+{
+	sm->dma.ofragptr = 0;
+	sm->dma.ptt_cnt = 0;
+}
+
+extern __inline__ void dma_start_transmit(struct sm_state *sm)
+{
+	sm->dma.ofragptr = 0;
+	if (sm->dma.o16bit) {
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2));
+	} else {
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz));
+	}
+	sm->dma.ptt_cnt = 1;
+}
+
+extern __inline__ void dma_clear_transmit(struct sm_state *sm)
+{
+	sm->dma.ptt_cnt = 0;
+	memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag)
+{
+	void *p;
+
+	while (sm->dma.ifragptr != curfrag) {
+		if (sm->dma.ifragptr)
+			p = (unsigned char *)sm->dma.ibuf + 
+				sm->dma.ifragsz * sm->dma.ifragptr;
+		else {
+			p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz;
+			memcpy(p, sm->dma.ibuf, sm->dma.ifragsz);
+		}
+		if (sm->dma.o16bit) {
+			time_exec(sm->debug_vals.demod_cyc, 
+				  sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2));
+		} else {
+			time_exec(sm->debug_vals.demod_cyc, 
+				  sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz));
+		}
+		sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS;
+	}
+}
+
+extern __inline__ void dma_init_receive(struct sm_state *sm)
+{
+	sm->dma.ifragptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* _SMDMA_H */
+
+
+
diff -u --recursive --new-file v2.0.34/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v2.0.34/linux/drivers/net/strip.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/strip.c	Mon Jul 13 13:47:32 1998
@@ -14,53 +14,68 @@
  *		for kernel-based devices like TTY.  It interfaces between a
  *		raw TTY, and the kernel's INET protocol layers (via DDI).
  *
- * Version:	@(#)strip.c	0.9.8	June 1996
+ * Version:	@(#)strip.c	1.3	July 1997
  *
  * Author:	Stuart Cheshire <cheshire@cs.stanford.edu>
  *
- * Fixes:	v0.9 12th Feb 1996.
+ * Fixes:	v0.9 12th Feb 1996 (SC)
  *		New byte stuffing (2+6 run-length encoding)
  *		New watchdog timer task
  *		New Protocol key (SIP0)
  *		
- *		v0.9.1 3rd March 1996
+ *		v0.9.1 3rd March 1996 (SC)
  *		Changed to dynamic device allocation -- no more compile
  *		time (or boot time) limit on the number of STRIP devices.
  *		
- *		v0.9.2 13th March 1996
+ *		v0.9.2 13th March 1996 (SC)
  *		Uses arp cache lookups (but doesn't send arp packets yet)
  *		
- *		v0.9.3 17th April 1996
+ *		v0.9.3 17th April 1996 (SC)
  *		Fixed bug where STR_ERROR flag was getting set unneccessarily
+ *		(causing otherwise good packets to be unneccessarily dropped)
  *		
- *		v0.9.4 27th April 1996
+ *		v0.9.4 27th April 1996 (SC)
  *		First attempt at using "&COMMAND" Starmode AT commands
  *		
- *		v0.9.5 29th May 1996
+ *		v0.9.5 29th May 1996 (SC)
  *		First attempt at sending (unicast) ARP packets
  *		
- *		v0.9.6 5th June 1996
- *		Elliot put "message level" tags in every "printk" statement
+ *		v0.9.6 5th June 1996 (Elliot)
+ *		Put "message level" tags in every "printk" statement
  *		
- *		v0.9.7 13th June 1996
- *		Added support for the /proc fs (laik)
+ *		v0.9.7 13th June 1996 (laik)
+ *		Added support for the /proc fs
  *
- *              v0.9.8 July 1996
- *              Added packet logging (Mema)
- */
-
-/*
- * Undefine this symbol if you don't have PROC_NET_STRIP_STATUS
- * defined in include/linux/proc_fs.h
+ *              v0.9.8 July 1996 (Mema)
+ *              Added packet logging
+ *
+ *              v1.0 November 1996 (SC)
+ *              Fixed (severe) memory leaks in the /proc fs code
+ *              Fixed race conditions in the logging code
+ *
+ *              v1.1 January 1997 (SC)
+ *              Deleted packet logging (use tcpdump instead)
+ *              Added support for Metricom Firmware v204 features
+ *              (like message checksums)
+ *
+ *              v1.2 January 1997 (SC)
+ *              Put portables list back in
+ *
+ *              v1.3 July 1997 (SC)
+ *              Made STRIP driver set the radio's baud rate automatically.
+ *              It is no longer necessarily to manually set the radio's
+ *              rate permanently to 115200 -- the driver handles setting
+ *              the rate automatically.
  */
 
-#define DO_PROC_NET_STRIP_STATUS 1
-
-/*
- * Define this symbol if you want to enable STRIP packet tracing.
- */
+#ifdef MODULE
+static const char StripVersion[] = "1.3-STUART.CHESHIRE-MODULAR";
+#else
+static const char StripVersion[] = "1.3-STUART.CHESHIRE";
+#endif
 
-#define DO_PROC_NET_STRIP_TRACE 0
+#define TICKLE_TIMERS 0
+#define EXT_COUNTERS 1
 
 
 /************************************************************************/
@@ -101,13 +116,12 @@
 #include <linux/if_arp.h>
 #include <linux/if_strip.h>
 #include <linux/proc_fs.h>
+#include <linux/serial.h>
 #include <net/arp.h>
 
-#ifdef CONFIG_INET
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/time.h>
-#endif
 
 
 /************************************************************************/
@@ -145,13 +159,19 @@
     __u8 c[24];
 } MetricomAddressString;
 
+/* Encapsulation can expand packet of size x to 65/64x + 1
+ * Sent packet looks like "<CR>*<address>*<key><encaps payload><CR>"
+ *                           1 1   1-18  1  4         ?         1
+ * eg.                     <CR>*0000-1234*SIP0<encaps payload><CR>
+ * We allow 31 bytes for the stars, the key, the address and the <CR>s
+ */
+#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L)
+
 /*
- * Note: A Metricom packet looks like this: *<address>*<key><payload><CR>
- * eg. *0000-1234*SIP0<payload><CR>
- * A STRIP_Header is never really sent over the radio, but making a dummy header
- * for internal use within the kernel that looks like an Ethernet header makes
- * certain other software happier. For example, tcpdump already understands
- * Ethernet headers.
+ * A STRIP_Header is never really sent over the radio, but making a dummy
+ * header for internal use within the kernel that looks like an Ethernet
+ * header makes certain other software happier. For example, tcpdump
+ * already understands Ethernet headers.
  */
 
 typedef struct
@@ -161,83 +181,20 @@
     unsigned short  protocol;		/* The protocol type, using Ethernet codes */
 } STRIP_Header;
 
-typedef struct GeographicLocation
-{
-    char s[18];
-} GeographicLocation;
-
-typedef enum {
-    NodeValid = 0x1,
-    NodeHasWAN = 0x2,
-    NodeIsRouter = 0x4
-} NodeType;
-
-typedef struct MetricomNode
-{
-    NodeType type;                  /* Some flags about the type of node */
-    GeographicLocation gl;     /* The location of the node. */
-    MetricomAddress addr;      /* The metricom address of this node */
-    int poll_latency;          /* The latency to poll that node ? */
-    int rssi;                  /* The Receiver Signal Strength Indicator */
-    struct MetricomNode *next; /* The next node */
-} MetricomNode;
-
-enum { FALSE = 0, TRUE = 1 };
-
-/*
- * Holds the packet signature for an IP packet.
- */
 typedef struct
 {
-    IPaddr src;
-    /* Data is stored in the following field in network byte order. */
-    __u16 id;
-} IPSignature;
+    char c[60];
+} MetricomNode;
 
-/*
- * Holds the packet signature for an ARP packet.
- */
+#define NODE_TABLE_SIZE 32
 typedef struct
 {
-    IPaddr src;
-    /* Data is stored in the following field in network byte order. */
-    __u16 op;
-} ARPSignature;
-
-/*
- * Holds the signature of a packet.
- */
-typedef union
-{
-    IPSignature ip_sig;
-    ARPSignature arp_sig;
-    __u8 print_sig[6];
-} PacketSignature;
-
-typedef enum {
-    EntrySend = 0,
-    EntryReceive = 1
-} LogEntry;
-
-/* Structure for Packet Logging */
-typedef struct stripLog
-{
-    LogEntry entry_type;
-    u_long seqNum;
-    int packet_type;
-    PacketSignature sig;
-    MetricomAddress src;
-    MetricomAddress dest;
-    struct timeval timeStamp;
-    u_long rawSize;
-    u_long stripSize;
-    u_long slipSize;
-    u_long valid;
-} StripLog;
-
-#define ENTRY_TYPE_TO_STRING(X) ((X) ? "r" : "s")
+    struct timeval timestamp;
+    int            num_nodes;
+    MetricomNode   node[NODE_TABLE_SIZE];
+} MetricomNodeTable;
 
-#define BOOLEAN_TO_STRING(X) ((X) ? "true" : "false")
+enum { FALSE = 0, TRUE = 1 };
 
 /*
  * Holds the radio's firmware version.
@@ -245,7 +202,7 @@
 typedef struct
 {
     char c[50];
-} MetricomFirmwareVersion;
+} FirmwareVersion;
 
 /*
  * Holds the radio's serial number.
@@ -253,7 +210,7 @@
 typedef struct
 {
     char c[18];
-} MetricomSerialNumber;
+} SerialNumber;
 
 /*
  * Holds the radio's battery voltage.
@@ -261,7 +218,19 @@
 typedef struct
 {
     char c[11];
-} MetricomBatteryVoltage;
+} BatteryVoltage;
+
+typedef struct
+{
+    char c[8];
+} char8;
+
+enum
+{
+    NoStructure = 0,		/* Really old firmware */
+    StructuredMessages = 1,	/* Parsable AT response msgs */
+    ChecksummedMessages = 2	/* Parsable AT response msgs with checksums */
+} FirmwareLevel;
 
 struct strip
 {
@@ -291,6 +260,25 @@
     unsigned long      tx_dropped;		/* When MTU change		*/
     unsigned long      rx_over_errors;		/* Frame bigger then STRIP buf. */
 
+    unsigned long      pps_timer;		/* Timer to determine pps	*/
+    unsigned long      rx_pps_count;		/* Counter to determine pps	*/
+    unsigned long      tx_pps_count;		/* Counter to determine pps	*/
+    unsigned long      sx_pps_count;		/* Counter to determine pps	*/
+    unsigned long      rx_average_pps;		/* rx packets per second * 8	*/
+    unsigned long      tx_average_pps;		/* tx packets per second * 8	*/
+    unsigned long      sx_average_pps;		/* sent packets per second * 8	*/
+
+#ifdef EXT_COUNTERS
+    unsigned long      rx_bytes;                /* total received bytes */
+    unsigned long      tx_bytes;                /* total received bytes */
+    unsigned long      rx_rbytes;               /* bytes thru radio i/f */
+    unsigned long      tx_rbytes;               /* bytes thru radio i/f */
+    unsigned long      rx_sbytes;               /* tot bytes thru serial i/f */
+    unsigned long      tx_sbytes;               /* tot bytes thru serial i/f */
+    unsigned long      rx_ebytes;               /* tot stat/err bytes */
+    unsigned long      tx_ebytes;               /* tot stat/err bytes */
+#endif
+
     /*
      * Internal variables.
      */
@@ -299,52 +287,143 @@
     struct strip     **referrer;		/* The pointer that points to us*/
     int                discard;			/* Set if serial error		*/
     int                working;			/* Is radio working correctly?	*/
-    int                structured_messages;	/* Parsable AT response msgs?	*/
+    int                firmware_level;		/* Message structuring level	*/
+    int                next_command;		/* Next periodic command	*/
+    unsigned int       user_baud;		/* The user-selected baud rate  */
     int                mtu;			/* Our mtu (to spot changes!)	*/
     long               watchdog_doprobe;	/* Next time to test the radio	*/
     long               watchdog_doreset;	/* Time to do next reset	*/
     long               gratuitous_arp;		/* Time to send next ARP refresh*/
     long               arp_interval;		/* Next ARP interval		*/
     struct timer_list  idle_timer;		/* For periodic wakeup calls	*/
-    MetricomNode      *neighbor_list;		/* The list of neighbor nodes   */
-    int                neighbor_list_locked;    /* Indicates the list is locked */
-    MetricomFirmwareVersion firmware_version;	/* The radio's firmware version */
-    MetricomSerialNumber serial_number;		/* The radio's serial number    */
-    MetricomBatteryVoltage battery_voltage;     /* The radio's battery voltage  */
+    MetricomAddress    true_dev_addr;		/* True address of radio	*/
+    int                manual_dev_addr;		/* Hack: See note below         */
+
+    FirmwareVersion    firmware_version;	/* The radio's firmware version */
+    SerialNumber       serial_number;		/* The radio's serial number    */
+    BatteryVoltage     battery_voltage;		/* The radio's battery voltage  */
 
     /*
      * Other useful structures.
      */
 
     struct tty_struct *tty;			/* ptr to TTY structure		*/
-    char               if_name[8];		/* Dynamically generated name	*/
+    char8              if_name;			/* Dynamically generated name	*/
     struct device      dev;			/* Our device structure		*/
 
     /*
-     * Packet Logging Structures.
+     * Neighbour radio records
      */
 
-    u_long             num_sent;
-    u_long             num_received;
-
-    int next_entry;                            	/* The index of the oldest packet; */
-                                                /* Also the next to be logged. */
-    StripLog packetLog[610];
+    MetricomNodeTable  portables;
+    MetricomNodeTable  poletops;
 };
 
+/*
+ * Note: manual_dev_addr hack
+ * 
+ * It is not possible to change the hardware address of a Metricom radio,
+ * or to send packets with a user-specified hardware source address, thus
+ * trying to manually set a hardware source address is a questionable
+ * thing to do.  However, if the user *does* manually set the hardware
+ * source address of a STRIP interface, then the kernel will believe it,
+ * and use it in certain places. For example, the hardware address listed
+ * by ifconfig will be the manual address, not the true one.
+ * (Both addresses are listed in /proc/net/strip.)
+ * Also, ARP packets will be sent out giving the user-specified address as
+ * the source address, not the real address. This is dangerous, because
+ * it means you won't receive any replies -- the ARP replies will go to
+ * the specified address, which will be some other radio. The case where
+ * this is useful is when that other radio is also connected to the same
+ * machine. This allows you to connect a pair of radios to one machine,
+ * and to use one exclusively for inbound traffic, and the other
+ * exclusively for outbound traffic. Pretty neat, huh?
+ * 
+ * Here's the full procedure to set this up:
+ * 
+ * 1. "slattach" two interfaces, e.g. st0 for outgoing packets,
+ *    and st1 for incoming packets
+ * 
+ * 2. "ifconfig" st0 (outbound radio) to have the hardware address
+ *    which is the real hardware address of st1 (inbound radio).
+ *    Now when it sends out packets, it will masquerade as st1, and
+ *    replies will be sent to that radio, which is exactly what we want.
+ * 
+ * 3. Set the route table entry ("route add default ..." or
+ *    "route add -net ...", as appropriate) to send packets via the st0
+ *    interface (outbound radio). Do not add any route which sends packets
+ *    out via the st1 interface -- that radio is for inbound traffic only.
+ * 
+ * 4. "ifconfig" st1 (inbound radio) to have hardware address zero.
+ *    This tells the STRIP driver to "shut down" that interface and not
+ *    send any packets through it. In particular, it stops sending the
+ *    periodic gratuitous ARP packets that a STRIP interface normally sends.
+ *    Also, when packets arrive on that interface, it will search the
+ *    interface list to see if there is another interface who's manual
+ *    hardware address matches its own real address (i.e. st0 in this
+ *    example) and if so it will transfer ownership of the skbuff to
+ *    that interface, so that it looks to the kernel as if the packet
+ *    arrived on that interface. This is necessary because when the
+ *    kernel sends an ARP packet on st0, it expects to get a reply on
+ *    st0, and if it sees the reply come from st1 then it will ignore
+ *    it (to be accurate, it puts the entry in the ARP table, but
+ *    labelled in such a way that st0 can't use it).
+ * 
+ * Thanks to Petros Maniatis for coming up with the idea of splitting
+ * inbound and outbound traffic between two interfaces, which turned
+ * out to be really easy to implement, even if it is a bit of a hack.
+ * 
+ * Having set a manual address on an interface, you can restore it
+ * to automatic operation (where the address is automatically kept
+ * consistent with the real address of the radio) by setting a manual
+ * address of all ones, e.g. "ifconfig st0 hw strip FFFFFFFFFFFF"
+ * This 'turns off' manual override mode for the device address.
+ * 
+ * Note: The IEEE 802 headers reported in tcpdump will show the *real*
+ * radio addresses the packets were sent and received from, so that you
+ * can see what is really going on with packets, and which interfaces
+ * they are really going through.
+ */
+
 
 /************************************************************************/
 /* Constants								*/
 
-#ifdef MODULE
-static const char StripVersion[] = "0.9.8-STUART.CHESHIRE-MODULAR";
-#else
-static const char StripVersion[] = "0.9.8-STUART.CHESHIRE";
-#endif
-
-static const char TickleString1[] = "***&COMMAND*ATS305?\r";
-static const char TickleString2[] = "***&COMMAND*ATS305?\r\r"
-       "*&COMMAND*ATS300?\r\r*&COMMAND*ATS325?\r\r*&COMMAND*AT~I2 nn\r\r";
+/*
+ * CommandString1 works on all radios
+ * Other CommandStrings are only used with firmware that provides structured responses.
+ * 
+ * ats319=1 Enables Info message for node additions and deletions
+ * ats319=2 Enables Info message for a new best node
+ * ats319=4 Enables checksums
+ * ats319=8 Enables ACK messages
+ */
+
+static const int MaxCommandStringLength = 32;
+static const int CompatibilityCommand = 1;
+
+static const char CommandString0[] = "*&COMMAND*ATS319=7";	/* Turn on checksums & info messages */
+static const char CommandString1[] = "*&COMMAND*ATS305?";	/* Query radio name */
+static const char CommandString2[] = "*&COMMAND*ATS325?";	/* Query battery voltage */
+static const char CommandString3[] = "*&COMMAND*ATS300?";	/* Query version information */
+static const char CommandString4[] = "*&COMMAND*ATS311?";	/* Query poletop list */
+static const char CommandString5[] = "*&COMMAND*AT~LA";		/* Query portables list */
+typedef struct { const char *string; long length; } StringDescriptor;
+
+static const StringDescriptor CommandString[] =
+    {
+    { CommandString0, sizeof(CommandString0)-1 },
+    { CommandString1, sizeof(CommandString1)-1 },
+    { CommandString2, sizeof(CommandString2)-1 },
+    { CommandString3, sizeof(CommandString3)-1 },
+    { CommandString4, sizeof(CommandString4)-1 },
+    { CommandString5, sizeof(CommandString5)-1 }
+    };
+
+#define GOT_ALL_RADIO_INFO(S)      \
+    ((S)->firmware_version.c[0] && \
+     (S)->battery_voltage.c[0]  && \
+     memcmp(&(S)->true_dev_addr, zero_address.c, sizeof(zero_address)))
 
 static const char            hextable[16]      = "0123456789ABCDEF";
 
@@ -353,26 +432,27 @@
 
 static const MetricomKey     SIP0Key           = { { "SIP0" } };
 static const MetricomKey     ARP0Key           = { { "ARP0" } };
-static const MetricomKey     ERR_Key           = { { "ERR_" } };
 static const MetricomKey     ATR_Key           = { { "ATR " } };
+static const MetricomKey     ACK_Key           = { { "ACK_" } };
+static const MetricomKey     INF_Key           = { { "INF_" } };
+static const MetricomKey     ERR_Key           = { { "ERR_" } };
 
 static const long            MaxARPInterval    = 60 * HZ;          /* One minute */
 
 /*
- * Maximum Starmode packet length (including starmode address) is 1183 bytes.
- * Allowing 32 bytes for header, and 65/64 expansion for STRIP encoding,
- * that translates to a maximum payload MTU of 1132.
- */
-static const unsigned short  MAX_STRIP_MTU          = 1132;
-static const unsigned short  DEFAULT_STRIP_MTU      = 1024;
+ * Maximum Starmode packet length is 1183 bytes. Allowing 4 bytes for
+ * protocol key, 4 bytes for checksum, one byte for CR, and 65/64 expansion
+ * for STRIP encoding, that translates to a maximum payload MTU of 1155.
+ * Note: A standard NFS 1K data packet is a total of 0x480 (1152) bytes
+ * long, including IP header, UDP header, and NFS header. Setting the STRIP
+ * MTU to 1152 allows us to send default sized NFS packets without fragmentation.
+ */
+static const unsigned short  MAX_SEND_MTU          = 1152;
+static const unsigned short  MAX_RECV_MTU          = 1500; /* Hoping for Ethernet sized packets in the future! */
+static const unsigned short  DEFAULT_STRIP_MTU      = 1152;
 static const int             STRIP_MAGIC            = 0x5303;
 static const long            LongTime               = 0x7FFFFFFF;
 
-static const int             STRIP_NODE_LEN         = 64;
-static const char            STRIP_PORTABLE_CHAR    = 'P';
-static const char            STRIP_ROUTER_CHAR      = 'r';
-static const int             STRIP_PROC_BUFFER_SIZE = 4096;
-static const int             STRIP_LOG_INT_SIZE     = 10;
 
 /************************************************************************/
 /* Global variables							*/
@@ -383,10 +463,18 @@
 /************************************************************************/
 /* Macros								*/
 
+/* Returns TRUE if text T begins with prefix P */
+#define has_prefix(T,L,P) (((L) >= sizeof(P)-1) && !strncmp((T), (P), sizeof(P)-1))
+
+/* Returns TRUE if text T of length L is equal to string S */
+#define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1))
+
 #define READHEX(X) ((X)>='0' && (X)<='9' ? (X)-'0' :      \
                     (X)>='a' && (X)<='f' ? (X)-'a'+10 :   \
                     (X)>='A' && (X)<='F' ? (X)-'A'+10 : 0 )
 
+#define READHEX16(X) ((__u16)(READHEX(X)))
+
 #define READDEC(X) ((X)>='0' && (X)<='9' ? (X)-'0' : 0)
 
 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
@@ -394,17 +482,6 @@
 #define ELEMENTS_OF(X) (sizeof(X) / sizeof((X)[0]))
 #define ARRAY_END(X) (&((X)[ELEMENTS_OF(X)]))
 
-/* Encapsulation can expand packet of size x to 65/64x + 1              */
-/* Sent packet looks like "*<address>*<key><encaps payload><CR>"        */
-/*                         1   1-18  1  4         ?         1           */
-/* We allow 31 bytes for the stars, the key, the address and the <CR>   */
-#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L)
-
-#define IS_RADIO_ADDRESS(p) (                                                   \
-    isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \
-    (p)[4] == '-' &&                                                            \
-    isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8])    )
-
 #define JIFFIE_TO_SEC(X) ((X) / HZ)
 
 
@@ -604,7 +681,15 @@
                 }
                 /* else, we only have one so far, so switch to Stuff_Diff code */
                 code = Stuff_Diff;
-                /* and fall through to Stuff_Diff case below */
+                /* and fall through to Stuff_Diff case below
+                 * Note cunning cleverness here: case Stuff_Diff compares 
+                 * the current character with the previous two to see if it
+                 * has a run of three the same. Won't this be an error if
+                 * there aren't two previous characters stored to compare with?
+                 * No. Because we know the current character is *not* the same
+                 * as the previous one, the first test below will necessarily
+                 * fail and the send half of the "if" won't be executed.
+                 */
 
             /* Stuff_Diff: We have at least two *different* bytes encoded */
             case Stuff_Diff:
@@ -638,10 +723,10 @@
                 src++;    /* Consume the byte */
                 break;
         }
-    if (count == Stuff_MaxCount)
-    {
-        StuffData_FinishBlock(code + count);
-    }
+        if (count == Stuff_MaxCount)
+        {
+            StuffData_FinishBlock(code + count);
+        }
     }
     if (code == Stuff_NoCode)
     {
@@ -754,17 +839,59 @@
 /* General routines for STRIP						*/
 
 /*
+ * get_baud returns the current baud rate, as one of the constants defined in
+ * termbits.h
+ * If the user has issued a baud rate override using the 'setserial' command
+ * and the logical current rate is set to 38.4, then the true baud rate
+ * currently in effect (57.6 or 115.2) is returned.
+ */
+static unsigned int get_baud(struct tty_struct *tty)
+    {
+    if (!tty || !tty->termios) return(0);
+    if ((tty->termios->c_cflag & CBAUD) == B38400 && tty->driver_data)
+        {
+        struct async_struct *info = (struct async_struct *)tty->driver_data;
+        if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI ) return(B57600);
+        if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) return(B115200);
+        }
+    return(tty->termios->c_cflag & CBAUD);
+    }
+
+/*
+ * set_baud sets the baud rate to the rate defined by baudcode
+ * Note: The rate B38400 should be avoided, because the user may have
+ * issued a 'setserial' speed override to map that to a different speed.
+ * We could achieve a true rate of 38400 if we needed to by cancelling
+ * any user speed override that is in place, but that might annoy the
+ * user, so it is simplest to just avoid using 38400.
+ */
+static void set_baud(struct tty_struct *tty, unsigned int baudcode)
+    {
+    struct termios old_termios = *(tty->termios);
+    tty->termios->c_cflag &= ~CBAUD; /* Clear the old baud setting */
+    tty->termios->c_cflag |= baudcode; /* Set the new baud setting */
+    tty->driver.set_termios(tty, &old_termios);
+    }
+
+/*
  * Convert a string to a Metricom Address.
  */
 
-static void string_to_radio_address(MetricomAddress *addr, __u8 *p)
+#define IS_RADIO_ADDRESS(p) (                                                 \
+  isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \
+  (p)[4] == '-' &&                                                            \
+  isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8])    )
+
+static int string_to_radio_address(MetricomAddress *addr, __u8 *p)
 {
+    if (!IS_RADIO_ADDRESS(p)) return(1);
     addr->c[0] = 0;
     addr->c[1] = 0;
     addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]);
     addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]);
     addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]);
     addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]);
+    return(0);
 }
 
 /*
@@ -779,19 +906,18 @@
 
 /*
  * Note: Must make sure sx_size is big enough to receive a stuffed
- * MAX_STRIP_MTU packet. Additionally, we also want to ensure that it's
+ * MAX_RECV_MTU packet. Additionally, we also want to ensure that it's
  * big enough to receive a large radio neighbour list (currently 4K).
  */
 
 static int allocate_buffers(struct strip *strip_info)
 {
     struct device *dev = &strip_info->dev;
-    int stuffedlen = STRIP_ENCAP_SIZE(dev->mtu);
-    int sx_size    = MAX(stuffedlen, 4096);
-    int tx_size    = stuffedlen + sizeof(TickleString2);
-    __u8 *r = kmalloc(MAX_STRIP_MTU, GFP_ATOMIC);
-    __u8 *s = kmalloc(sx_size,       GFP_ATOMIC);
-    __u8 *t = kmalloc(tx_size,       GFP_ATOMIC);
+    int sx_size    = MAX(STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096);
+    int tx_size    = STRIP_ENCAP_SIZE(dev->mtu) + MaxCommandStringLength;
+    __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC);
+    __u8 *s = kmalloc(sx_size,      GFP_ATOMIC);
+    __u8 *t = kmalloc(tx_size,      GFP_ATOMIC);
     if (r && s && t)
     {
         strip_info->rx_buff = r;
@@ -823,10 +949,10 @@
     unsigned char *otbuff = strip_info->tx_buff;
     InterruptStatus intstat;
 
-    if (dev->mtu > MAX_STRIP_MTU)
+    if (dev->mtu > MAX_SEND_MTU)
     {
         printk(KERN_ERR "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n",
-            strip_info->dev.name, MAX_STRIP_MTU);
+            strip_info->dev.name, MAX_SEND_MTU);
         dev->mtu = old_mtu;
         return;
     }
@@ -855,9 +981,8 @@
             memcpy(strip_info->sx_buff, osbuff, strip_info->sx_count);
         else
         {
-            strip_info->sx_count = 0;
+            strip_info->discard = strip_info->sx_count;
             strip_info->rx_over_errors++;
-            strip_info->discard = 1;
         }
     }
 
@@ -886,9 +1011,9 @@
 static void strip_unlock(struct strip *strip_info)
 {
     /*
-     * Set the time to go off in one second.
+     * Set the timer to go off in one second.
      */
-    strip_info->idle_timer.expires  = jiffies + HZ;
+    strip_info->idle_timer.expires = jiffies + 1*HZ;
     add_timer(&strip_info->idle_timer);
     if (!clear_bit(0, (void *)&strip_info->dev.tbusy))
         printk(KERN_ERR "%s: trying to unlock already unlocked device!\n",
@@ -899,8 +1024,6 @@
 /************************************************************************/
 /* Callback routines for exporting information through /proc		*/
 
-#if DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE
-
 /*
  * This function updates the total amount of data printed so far. It then
  * determines if the amount of data printed into a buffer  has reached the
@@ -911,29 +1034,29 @@
  */
 static int 
 shift_buffer(char *buffer, int requested_offset, int requested_len,
-	     int *total, int *slop, char **buf)
+             int *total, int *slop, char **buf)
 {
     int printed;
 
     /* printk(KERN_DEBUG "shift: buffer: %d o: %d l: %d t: %d buf: %d\n",
-	   (int) buffer, requested_offset, requested_len, *total,
-	   (int) *buf); */
+           (int) buffer, requested_offset, requested_len, *total,
+           (int) *buf); */
     printed = *buf - buffer;
     if (*total + printed <= requested_offset) {
-	*total += printed;
-	*buf = buffer;
+        *total += printed;
+        *buf = buffer;
     }
     else {
-	if (*total < requested_offset) {
-	    *slop = requested_offset - *total;
-	}
-	*total = requested_offset + printed - *slop;
+        if (*total < requested_offset) {
+            *slop = requested_offset - *total;
+        }
+        *total = requested_offset + printed - *slop;
     }
     if (*total > requested_offset + requested_len) {
-	return 1;
+        return 1;
     }
     else {
-	return 0;
+        return 0;
     }
 }
 
@@ -944,12 +1067,12 @@
  */
 static int
 calc_start_len(char *buffer, char **start, int requested_offset,
-	       int requested_len, int total, char *buf)
+               int requested_len, int total, char *buf)
 {
     int return_len, buffer_len;
 
     buffer_len = buf - buffer;
-    if (buffer_len >= STRIP_PROC_BUFFER_SIZE - 1) {
+    if (buffer_len >= 4095) {
  	printk(KERN_ERR "STRIP: exceeded /proc buffer size\n");
     }
 
@@ -959,20 +1082,16 @@
      */
     return_len = total - requested_offset;
     if (return_len < 0) {
-	return_len = 0;
+        return_len = 0;
     }
     *start = buf - return_len;
     if (return_len > requested_len) {
-	return_len = requested_len;
+        return_len = requested_len;
     }
     /* printk(KERN_DEBUG "return_len: %d\n", return_len); */
     return return_len;
 }
 
-#endif DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE
-
-#if DO_PROC_NET_STRIP_STATUS
-
 /*
  * If the time is in the near future, time_delta prints the number of
  * seconds to go into the buffer and returns the address of the buffer.
@@ -990,553 +1109,236 @@
     return(buffer);
 }
 
-/*
- * This function prints radio status information into the specified
- * buffer.
- */
-static int
-sprintf_status_info(char *buffer, struct strip *strip_info)
-{
-    char temp_buffer[32];
-    MetricomAddressString addr_string;
-    char *buf;
-
-    buf = buffer;
-    buf += sprintf(buf, "Interface name\t\t%s\n", strip_info->if_name);
-    buf += sprintf(buf, " Radio working:\t\t%s\n",
-		   strip_info->working &&
-                   (long)jiffies - strip_info->watchdog_doreset < 0 ? "Yes" : "No");
-    (void) radio_address_to_string((MetricomAddress *)
-				   &strip_info->dev.dev_addr,
-				   &addr_string);
-    buf += sprintf(buf, " Device address:\t%s\n", addr_string.c);
-    buf += sprintf(buf, " Firmware version:\t%s\n",
-		   !strip_info->working             ? "Unknown" :
-		   !strip_info->structured_messages ? "Should be upgraded" :
-		   strip_info->firmware_version.c);
-    buf += sprintf(buf, " Serial number:\t\t%s\n", strip_info->serial_number.c);
-    buf += sprintf(buf, " Battery voltage:\t%s\n", strip_info->battery_voltage.c);
-    buf += sprintf(buf, " Transmit queue (bytes):%d\n", strip_info->tx_left);
-    buf += sprintf(buf, " Next watchdog probe:\t%s\n",
-		   time_delta(temp_buffer, strip_info->watchdog_doprobe));
-    buf += sprintf(buf, " Next watchdog reset:\t%s\n",
-		   time_delta(temp_buffer, strip_info->watchdog_doreset));
-    buf += sprintf(buf, " Next gratuitous ARP:\t%s\n",
-		   time_delta(temp_buffer, strip_info->gratuitous_arp));
-    buf += sprintf(buf, " Next ARP interval:\t%ld seconds\n",
-		   JIFFIE_TO_SEC(strip_info->arp_interval));
-    return buf - buffer;
-}
-
-static int
-sprintf_portables(char *buffer, struct strip *strip_info)
-{
-
-    MetricomAddressString addr_string;
-    MetricomNode          *node;
-    char *buf;
-
-    buf = buffer;
-    buf += sprintf(buf, " portables: name\t\tpoll_latency\tsignal strength\n");
-    for (node = strip_info->neighbor_list; node != NULL;
-	 node = node->next) {
-	if (!(node->type & NodeValid)) {
-	    break;
-	}
-	if (node->type & NodeHasWAN) {
-	    continue;
-	}
-	(void) radio_address_to_string(&node->addr, &addr_string);
-	buf += sprintf(buf, "  %s\t\t\t\t%d\t\t%d\n",
-		       addr_string.c, node->poll_latency, node->rssi);
-    }
-    return buf - buffer;
-}
-
-static int
-sprintf_poletops(char *buffer, struct strip *strip_info)
+static int sprintf_neighbours(char *buffer, MetricomNodeTable *table, char *title)
 {
-    MetricomNode    *node;
-    char *buf;
-
-    buf = buffer;
-    buf += sprintf(buf, " poletops: GPS\t\t\tpoll_latency\tsignal strength\n");
-    for (node = strip_info->neighbor_list;
-	 node != NULL; node = node->next) {
-	if (!(node->type & NodeValid)) {
-	    break;
-	}
-	if (!(node->type & NodeHasWAN)) {
-	    continue;
-	}
-	buf += sprintf(buf, "  %s\t\t\t%d\t\t%d\n",
-		       node->gl.s, node->poll_latency, node->rssi);
-    }
-    return buf - buffer;
+    /* We wrap this in a do/while loop, so if the table changes */
+    /* while we're reading it, we just go around and try again. */
+    struct timeval t;
+    char *ptr;
+    do
+        {
+        int i;
+        t = table->timestamp;
+        ptr = buffer;
+        if (table->num_nodes) ptr += sprintf(ptr, "\n %s\n", title);
+        for (i=0; i<table->num_nodes; i++)
+            {
+            InterruptStatus intstat = DisableInterrupts();
+            MetricomNode node = table->node[i];
+            RestoreInterrupts(intstat);
+            ptr += sprintf(ptr, "  %s\n", node.c);
+            }
+        } while (table->timestamp.tv_sec != t.tv_sec || table->timestamp.tv_usec != t.tv_usec);
+    return ptr - buffer;
 }
 
 /*
- * This function is exports status information from the STRIP driver through
- * the /proc file system. /proc filesystem should be fixed:
- *    1) slow (sprintfs here, a memory copy in the proc that calls this one)
- *    2) length of buffer not passed
- *    3) dummy isn't client data set when the callback was registered
- *    4) poorly documented (this function is called until the requested amount
- *       of data is returned, buffer is only 4K long, dummy is the permissions
- *       of the file (?), the proc_dir_entry passed to proc_net_register must
- *       be kmalloc-ed)
- */
-
-static int
-strip_get_status_info(char *buffer, char **start, off_t requested_offset,
-		      int requested_len, int dummy)
-{
-    char            *buf;
-    int             total = 0, slop = 0, len_exceeded;
-    InterruptStatus i_status;
-    struct strip    *strip_info;
-
-    buf = buffer;
-    buf += sprintf(buf, "strip_version: %s\n", StripVersion);
-
-    i_status = DisableInterrupts();
-    strip_info = struct_strip_list;
-    RestoreInterrupts(i_status);
-
-    while (strip_info != NULL) {
-	i_status = DisableInterrupts();
-	buf += sprintf_status_info(buf, strip_info);
-	RestoreInterrupts(i_status);
-	len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
-				    &total, &slop, &buf);
-	if (len_exceeded) {
-	    goto done;
-	}
-	strip_info->neighbor_list_locked = TRUE;
-	buf += sprintf_portables(buf, strip_info);
-	strip_info->neighbor_list_locked = FALSE;
-	len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
-				    &total, &slop, &buf);
-	if (len_exceeded) {
-	    goto done;
-	}
-	strip_info->neighbor_list_locked = TRUE;
-	buf += sprintf_poletops(buf, strip_info);
-	strip_info->neighbor_list_locked = FALSE;
-	len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
-				    &total, &slop, &buf);
-	if (len_exceeded) {
-	    goto done;
-	}
-	strip_info = strip_info->next;
-    }
-done:
-    return calc_start_len(buffer, start, requested_offset, requested_len,
-				total, buf);
-}
-
-#endif DO_PROC_NET_STRIP_STATUS
-
-#if DO_PROC_NET_STRIP_TRACE
-
-/*
- * Convert an Ethernet protocol to a string
- * Returns the number of characters printed.
+ * This function prints radio status information into the specified buffer.
+ * I think the buffer size is 4K, so this routine should never print more
+ * than 4K of data into it. With the maximum of 32 portables and 32 poletops
+ * reported, the routine outputs 3107 bytes into the buffer.
  */
-
-static int protocol_to_string(int protocol, __u8 *p)
-{
-    int printed;
-
-    switch (protocol) {
-    case ETH_P_IP:
-	printed = sprintf(p, "IP");
-	break;
-    case ETH_P_ARP:
-	printed = sprintf(p, "ARP");
-	break;
-    default: 
-	printed = sprintf(p, "%d", protocol);
-    }
-    return printed;
-}
-
 static int
-sprintf_log_entry(char *buffer, struct strip *strip_info, int packet_index)
+sprintf_status_info(char *buffer, struct strip *strip_info)
 {
-    StripLog *entry;
+    char temp[32];
+    char *p = buffer;
     MetricomAddressString addr_string;
-    __u8     sig_buf[24], *s;
-    char     *buf, proto_buf[10];
 
-    entry = &strip_info->packetLog[packet_index];
-    if (!entry->valid) {
-	return 0;
-    }
-    buf = buffer;
-    buf += sprintf(buf, "%-4s %s   %7lu ", strip_info->if_name,
-		   ENTRY_TYPE_TO_STRING(entry->entry_type), entry->seqNum);
-    (void) protocol_to_string(entry->packet_type, proto_buf);
-    buf += sprintf(buf, "%-4s", proto_buf);
-    s = entry->sig.print_sig;
-    sprintf(sig_buf, "%d.%d.%d.%d.%d.%d", s[0], s[1], s[2], s[3], s[4], s[5]);
-    buf += sprintf(buf, "%-24s", sig_buf);
-    (void) radio_address_to_string((MetricomAddress *) &entry->src,
-				   &addr_string);
-    buf += sprintf(buf, "%-10s", addr_string.c);
-    (void) radio_address_to_string((MetricomAddress *) &entry->dest,
-				   &addr_string);
-    buf += sprintf(buf, "%-10s", addr_string.c);
-    buf += sprintf(buf, "%8d %6d %5lu %6lu %5lu\n", entry->timeStamp.tv_sec,
-		   entry->timeStamp.tv_usec, entry->rawSize,
-		   entry->stripSize, entry->slipSize);
-    return buf - buffer;
-}
-
-/*
- * This function exports trace information from the STRIP driver through the
- * /proc file system.
- */
+    /* First, we must copy all of our data to a safe place, */
+    /* in case a serial interrupt comes in and changes it.  */
+    InterruptStatus intstat = DisableInterrupts();
+    int                tx_left             = strip_info->tx_left;
+    unsigned long      rx_average_pps      = strip_info->rx_average_pps;
+    unsigned long      tx_average_pps      = strip_info->tx_average_pps;
+    unsigned long      sx_average_pps      = strip_info->sx_average_pps;
+    int                working             = strip_info->working;
+    int                firmware_level      = strip_info->firmware_level;
+    long               watchdog_doprobe    = strip_info->watchdog_doprobe;
+    long               watchdog_doreset    = strip_info->watchdog_doreset;
+    long               gratuitous_arp      = strip_info->gratuitous_arp;
+    long               arp_interval        = strip_info->arp_interval;
+    FirmwareVersion    firmware_version    = strip_info->firmware_version;
+    SerialNumber       serial_number       = strip_info->serial_number;
+    BatteryVoltage     battery_voltage     = strip_info->battery_voltage;
+    char8              if_name             = strip_info->if_name;
+    MetricomAddress    true_dev_addr       = strip_info->true_dev_addr;
+    MetricomAddress    dev_dev_addr        = *(MetricomAddress*)strip_info->dev.dev_addr;
+    int                manual_dev_addr     = strip_info->manual_dev_addr;
+#ifdef EXT_COUNTERS
+    unsigned long      rx_bytes            = strip_info->rx_bytes;
+    unsigned long      tx_bytes            = strip_info->tx_bytes;
+    unsigned long      rx_rbytes           = strip_info->rx_rbytes;
+    unsigned long      tx_rbytes           = strip_info->tx_rbytes;
+    unsigned long      rx_sbytes           = strip_info->rx_sbytes;
+    unsigned long      tx_sbytes           = strip_info->tx_sbytes;
+    unsigned long      rx_ebytes           = strip_info->rx_ebytes;
+    unsigned long      tx_ebytes           = strip_info->tx_ebytes;
+#endif
+    RestoreInterrupts(intstat);
 
-static int
-strip_get_trace_info(char *buffer, char **start, off_t requested_offset,
-		     int requested_len, int dummy)
-{
-    char            *buf;
-    int             len_exceeded, total = 0, slop = 0, packet_index, oldest;
-    InterruptStatus i_status;
-    struct strip    *strip_info;
-
-    buf = buffer;
-    buf += sprintf(buf, "if   s/r seqnum  t   signature               ");
-    buf += sprintf(buf,
-		   "src       dest      sec      usec   raw   strip  slip\n");
-
-    i_status = DisableInterrupts();
-    strip_info = struct_strip_list;
-    oldest = strip_info->next_entry;
-    RestoreInterrupts(i_status);
-
-    /*
-     * If we disable interrupts for this entire loop,
-     * characters from the serial port could be lost,
-     * so we only disable interrupts when accessing
-     * a log entry. If more than STRIP_LOG_INT_SIZE
-     * packets are logged before the first entry is
-     * printed, then some of the entries could be
-     * printed out of order.
-     */
-    while (strip_info != NULL) {
-	for (packet_index = oldest + STRIP_LOG_INT_SIZE;
-	     packet_index != oldest;
-	     packet_index = (packet_index + 1) %
-		 ELEMENTS_OF(strip_info->packetLog)) {
-	    i_status = DisableInterrupts();
-	    buf += sprintf_log_entry(buf, strip_info, packet_index);
-	    RestoreInterrupts(i_status);
-	    len_exceeded = shift_buffer(buffer, requested_offset,
-					requested_len, &total, &slop, &buf);
-	    if (len_exceeded) {
-		goto done;
-	    }
-	}
-	strip_info = strip_info->next;
-    }
-done:
-    return calc_start_len(buffer, start, requested_offset, requested_len,
-			  total, buf);
-}
+    p += sprintf(p, "\nInterface name\t\t%s\n", if_name.c);
+    p += sprintf(p, " Radio working:\t\t%s\n", working ? "Yes" : "No");
+    radio_address_to_string(&true_dev_addr, &addr_string);
+    p += sprintf(p, " Radio address:\t\t%s\n", addr_string.c);
+    if (manual_dev_addr)
+    {
+        radio_address_to_string(&dev_dev_addr, &addr_string);
+        p += sprintf(p, " Device address:\t%s\n", addr_string.c);
+    }
+    p += sprintf(p, " Firmware version:\t%s", !working        ? "Unknown" :
+                                              !firmware_level ? "Should be upgraded" :
+                                              firmware_version.c);
+    if (firmware_level >= ChecksummedMessages) p += sprintf(p, " (Checksums Enabled)");
+    p += sprintf(p, "\n");
+    p += sprintf(p, " Serial number:\t\t%s\n", serial_number.c);
+    p += sprintf(p, " Battery voltage:\t%s\n", battery_voltage.c);
+    p += sprintf(p, " Transmit queue (bytes):%d\n", tx_left);
+    p += sprintf(p, " Receive packet rate:   %ld packets per second\n", rx_average_pps / 8);
+    p += sprintf(p, " Transmit packet rate:  %ld packets per second\n", tx_average_pps / 8);
+    p += sprintf(p, " Sent packet rate:      %ld packets per second\n", sx_average_pps / 8);
+    p += sprintf(p, " Next watchdog probe:\t%s\n", time_delta(temp, watchdog_doprobe));
+    p += sprintf(p, " Next watchdog reset:\t%s\n", time_delta(temp, watchdog_doreset));
+    p += sprintf(p, " Next gratuitous ARP:\t");
 
-static int slip_len(unsigned char *data, int len)
-{
-    static const unsigned char SLIP_END=0300;	/* indicates end of SLIP frame	*/
-    static const unsigned char SLIP_ESC=0333;	/* indicates SLIP byte stuffing	*/
-    int count = len;
-    while (--len >= 0)
+    if (!memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)))
+        p += sprintf(p, "Disabled\n");
+    else
     {
-	if (*data == SLIP_END || *data == SLIP_ESC) count++;
-	data++;
+        p += sprintf(p, "%s\n", time_delta(temp, gratuitous_arp));
+        p += sprintf(p, " Next ARP interval:\t%ld seconds\n", JIFFIE_TO_SEC(arp_interval));
     }
-    return(count);
-}
-
-/* Copied from kernel/sched.c */
-static void jiffiestotimeval(unsigned long jiffies, struct timeval *value)
-{
-    value->tv_usec = (jiffies % HZ) * (1000000.0 / HZ);
-    value->tv_sec = jiffies / HZ;
-    return;
-}
-
-/*
- * This function logs a packet.
- * A pointer to the packet itself is passed so that some of the data can be
- * used to compute a signature. The pointer should point the the
- * part of the packet following the STRIP_header.
- */
 
-static void packet_log(struct strip *strip_info, __u8 *packet,
-		       LogEntry entry_type, STRIP_Header *hdr, 
-		       int raw_size, int strip_size, int slip_size)
-{
-    StripLog *entry;
-    struct iphdr *iphdr;
-    struct arphdr *arphdr;
-
-    entry = &strip_info->packetLog[strip_info->next_entry];
-    if (entry_type == EntrySend) {
-	entry->seqNum = strip_info->num_sent++;
-    }
-    else {
-	entry->seqNum = strip_info->num_received++;
-    }
-    entry->entry_type = entry_type;
-    entry->packet_type = ntohs(hdr->protocol);
-    switch (entry->packet_type) {
-    case ETH_P_IP:
-	/*
-	 * The signature for IP is the sender's ip address and
-	 * the identification field.
-	 */
-	iphdr = (struct iphdr *) packet;
-	entry->sig.ip_sig.id = iphdr->id;
-	entry->sig.ip_sig.src.l = iphdr->saddr;
-	break;
-    case ETH_P_ARP:
-	/*
-	 * The signature for ARP is the sender's ip address and
-	 * the operation.
-	 */
-	arphdr = (struct arphdr *) packet;
-	entry->sig.arp_sig.op = arphdr->ar_op;
-        memcpy(&entry->sig.arp_sig.src.l, packet + 8 + arphdr->ar_hln,
-	       sizeof(entry->sig.arp_sig.src.l));
-        entry->sig.arp_sig.src.l = entry->sig.arp_sig.src.l;
-	break;
-    default:
-	printk(KERN_DEBUG "STRIP: packet_log: unknown packet type: %d\n",
-	       entry->packet_type);
-	break;
-    }
-    memcpy(&entry->src, &hdr->src_addr, sizeof(MetricomAddress));
-    memcpy(&entry->dest, &hdr->dst_addr, sizeof(MetricomAddress));
-
-    jiffiestotimeval(jiffies, &(entry->timeStamp));
-    entry->rawSize = raw_size;
-    entry->stripSize = strip_size;
-    entry->slipSize = slip_size;
-    entry->valid = 1;
+    if (working)
+        {
+#ifdef EXT_COUNTERS
+          p += sprintf(p, "\n");
+          p += sprintf(p, " Total bytes:         \trx:\t%lu\ttx:\t%lu\n", rx_bytes, tx_bytes);
+          p += sprintf(p, "  thru radio:         \trx:\t%lu\ttx:\t%lu\n", rx_rbytes, tx_rbytes);
+          p += sprintf(p, "  thru serial port:   \trx:\t%lu\ttx:\t%lu\n", rx_sbytes, tx_sbytes);
+          p += sprintf(p, " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", rx_ebytes, tx_ebytes);
+#endif
+        p += sprintf_neighbours(p, &strip_info->poletops, "Poletops:");
+        p += sprintf_neighbours(p, &strip_info->portables, "Portables:");
+        }
 
-    strip_info->next_entry = (strip_info->next_entry + 1) %
-	ELEMENTS_OF(strip_info->packetLog);
+    return p - buffer;
 }
 
-#endif DO_PROC_NET_STRIP_TRACE
-
 /*
- * This function parses the response to the ATS300? command,
- * extracting the radio version and serial number.
+ * This function is exports status information from the STRIP driver through
+ * the /proc file system.
  */
-static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end)
-{
-    __u8 *p, *value_begin, *value_end;
-    int len;
-    
-    /* Determine the beginning of the second line of the payload */
-    p = ptr;
-    while (p < end && *p != 10) p++;
-    if (p >= end) return;
-    p++;
-    value_begin = p;
-    
-    /* Determine the end of line */
-    while (p < end && *p != 10) p++;
-    if (p >= end) return;
-    value_end = p;
-    p++;
-     
-    len = value_end - value_begin;
-    len = MIN(len, sizeof(MetricomFirmwareVersion) - 1);
-    sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin);
-    
-    /* Look for the first colon */
-    while (p < end && *p != ':') p++;
-    if (p >= end) return;
-    /* Skip over the space */
-    p += 2;
-    len = sizeof(MetricomSerialNumber) - 1;
-    if (p + len <= end) {
-	sprintf(strip_info->serial_number.c, "%.*s", len, p);
-    }
-    else {
-     	printk(KERN_ERR "STRIP: radio serial number shorter (%d) than expected (%d)\n",
-     	       end - p, len);
-    }
-}
 
-/*
- * This function parses the response to the ATS325? command,
- * extracting the radio battery voltage.
- */
-static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end)
+static int get_status_info(char *buffer, char **start, off_t req_offset, int req_len, int dummy)
 {
-    int len;
+    int           total = 0, slop = 0;
+    struct strip *strip_info = struct_strip_list;
+    char         *buf = buffer;
 
-    len = sizeof(MetricomBatteryVoltage) - 1;
-    if (ptr + len <= end) {
-	sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr);
-    }
-    else {
- 	printk(KERN_ERR "STRIP: radio voltage string shorter (%d) than expected (%d)\n",
- 	       end - ptr, len);
-    }
+    buf += sprintf(buf, "strip_version: %s\n", StripVersion);
+    if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) goto exit;
+
+    while (strip_info != NULL)
+        {
+        buf += sprintf_status_info(buf, strip_info);
+        if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) break;
+        strip_info = strip_info->next;
+        }
+    exit:
+    return(calc_start_len(buffer, start, req_offset, req_len, total, buf));
 }
 
-/*
- * This function parses the response to the AT~I2 command,
- * which gives the names of the radio's nearest neighbors.
- * It relies on the format of the response.
- */
-static void get_radio_neighbors(struct strip *strip_info, __u8 *ptr, __u8 *end)
+static const char proc_strip_status_name[] = "strip";
+static struct proc_dir_entry proc_strip_get_status_info =
 {
-    __u8 *p, *line_begin;
-    int num_nodes_reported, num_nodes_counted;
-    MetricomNode *node, *last;
-
-    /* Check if someone is reading the list */
-    if (strip_info->neighbor_list_locked) {
-	return;
-    }
-
-    /* Determine the number of Nodes */
-    p = ptr;
-    num_nodes_reported = simple_strtoul(p, NULL, 10);
-    /* printk(KERN_DEBUG "num_nodes: %d\n", num_nodes_reported); */
-
-    /* Determine the beginning of the next line */
-    while (p < end && *p != 10) p++;
-    if (p >= end) return;
-    p++;
-
-    /*
-     * The node list should never be empty because we allocate one empty
-     * node when the strip_info is allocated. The nodes which were allocated
-     * when the number of neighbors was high but are no longer needed because
-     * there aren't as many neighbors any more are marked invalid. Invalid nodes
-     * are kept at the end of the list.
-     */
-    node = strip_info->neighbor_list;
-    last = node;
-    if (node == NULL) {
-	DumpData("Neighbor list is NULL:", strip_info, p, end);
-	return;
-    }	
-    line_begin = p;
-    num_nodes_counted = 0;
-    while (line_begin < end) {
- 	/* Check to see if the format is what we expect. */
- 	if ((line_begin + STRIP_NODE_LEN) > end) {
- 	    printk(KERN_ERR "STRIP: radio neighbor node string shorter (%d) than expected (%d)\n",
- 	       end - line_begin, STRIP_NODE_LEN);
-	    break;
- 	}
-
-	/* Get a node */
-	if (node == NULL) {
-	    node = kmalloc(sizeof(MetricomNode), GFP_ATOMIC);
-	    node->next = NULL;
-	}
-	node->type = NodeValid;
-
-	/* Fill the node in */
-
-	/* Determine if it has a GPS location and fill it in if it does. */
-	p = line_begin;
-	/* printk(KERN_DEBUG "node: %64s\n", p); */
-	if (p[0] != STRIP_PORTABLE_CHAR) {
-	    node->type |= NodeHasWAN;
-	    sprintf(node->gl.s, "%.*s", (int) sizeof(GeographicLocation) - 1, p);
-	}
-	
-	/* Determine if it is a router */
-	p = line_begin + 18;
-	if (p[0] == STRIP_ROUTER_CHAR) {
-	    node->type |= NodeIsRouter;
-	}
-
-	/* Could be a radio address or some weird poletop address. */
-	p = line_begin + 20;
-	/* printk(KERN_DEBUG "before addr: %6s\n", p); */
-	string_to_radio_address(&node->addr, p);
-	/* radio_address_to_string(&node->addr, addr_string);
-	printk(KERN_DEBUG "after addr: %s\n", addr_string);  */
-	
-	if (IS_RADIO_ADDRESS(p)) {
-	    string_to_radio_address(&node->addr, p);
-	}
-	else {
-	    memset(&node->addr, 0, sizeof(MetricomAddress));
-	}
-	
-	/* Get the poll latency. %$#!@ simple_strtoul can't skip white space */
-	p = line_begin + 41;
-	while (isspace(*p) && (p < end)) {
-	    p++;
-	}
-	node->poll_latency = simple_strtoul(p, NULL, 10);
-	
-	/* Get the signal strength. simple_strtoul doesn't do minus signs */
-	p = line_begin + 60;
-	node->rssi = -simple_strtoul(p, NULL, 10);
-	
-	if (last != node) {
-	    last->next = node;
-	    last = node;
-	}
-	node = node->next;
-	line_begin += STRIP_NODE_LEN;
-	num_nodes_counted++;
-    }
-
-    /* invalidate all remaining nodes */
-    for (;node != NULL; node = node->next) {
-	node->type &= ~NodeValid;
-    }
-
-    /*
-     * If the number of nodes reported is different
-     * from the number counted, might need to up the number
-     * requested.
-     */
-    if (num_nodes_reported != num_nodes_counted) {
-	printk(KERN_DEBUG "nodes reported: %d \tnodes counted: %d\n",
-	       num_nodes_reported, num_nodes_counted);
-    }	
-}
+    PROC_NET_STRIP_STATUS,		/* unsigned short low_ino */
+    sizeof(proc_strip_status_name)-1,	/* unsigned short namelen */
+    proc_strip_status_name,		/* const char *name */
+    S_IFREG | S_IRUGO,			/* mode_t mode */
+    1,					/* nlink_t nlink */
+    0, 0, 0,				/* uid_t uid, gid_t gid, unsigned long size */
+    &proc_net_inode_operations,		/* struct inode_operations * ops */
+    &get_status_info,			/* int (*get_info)(...) */
+    NULL,				/* void (*fill_inode)(struct inode *); */
+    NULL, NULL, NULL,			/* struct proc_dir_entry *next, *parent, *subdir; */
+    NULL				/* void *data; */
+};
 
 
 /************************************************************************/
 /* Sending routines							*/
 
 static void ResetRadio(struct strip *strip_info)
-{	
-    static const char InitString[] = "\rat\r\rate0q1dt**starmode\r\r**";
-
-    /* If the radio isn't working anymore, we should clear the old status information. */
+{
+    struct tty_struct *tty = strip_info->tty;
+    static const char init[] = "ate0q1dt**starmode\r**";
+    StringDescriptor s = { init, sizeof(init)-1 };
+
+    /* 
+     * If the radio isn't working anymore,
+     * we should clear the old status information.
+     */
     if (strip_info->working)
     {
         printk(KERN_INFO "%s: No response: Resetting radio.\n", strip_info->dev.name);
         strip_info->firmware_version.c[0] = '\0';
         strip_info->serial_number.c[0] = '\0';
         strip_info->battery_voltage.c[0] = '\0';
+        strip_info->portables.num_nodes = 0;
+        do_gettimeofday(&strip_info->portables.timestamp);
+        strip_info->poletops.num_nodes = 0;
+        do_gettimeofday(&strip_info->poletops.timestamp);
     }
+
+    strip_info->pps_timer      = jiffies;
+    strip_info->rx_pps_count   = 0;
+    strip_info->tx_pps_count   = 0;
+    strip_info->sx_pps_count   = 0;
+    strip_info->rx_average_pps = 0;
+    strip_info->tx_average_pps = 0;
+    strip_info->sx_average_pps = 0;
+
     /* Mark radio address as unknown */
-    *(MetricomAddress*)&strip_info->dev.dev_addr = zero_address;
+    *(MetricomAddress*)&strip_info->true_dev_addr = zero_address;
+    if (!strip_info->manual_dev_addr)
+        *(MetricomAddress*)strip_info->dev.dev_addr = zero_address;
     strip_info->working = FALSE;
-    strip_info->structured_messages = FALSE;
+    strip_info->firmware_level = NoStructure;
+    strip_info->next_command   = CompatibilityCommand;
     strip_info->watchdog_doprobe = jiffies + 10 * HZ;
     strip_info->watchdog_doreset = jiffies + 1 * HZ;
-    strip_info->tty->driver.write(strip_info->tty, 0, (char *)InitString, sizeof(InitString)-1);
+
+    /* If the user has selected a baud rate above 38.4 see what magic we have to do */
+    if (strip_info->user_baud > B38400)
+        {
+        /*
+         * Subtle stuff: Pay attention :-)
+         * If the serial port is currently at the user's selected (>38.4) rate,
+         * then we temporarily switch to 19.2 and issue the ATS304 command
+         * to tell the radio to switch to the user's selected rate.
+         * If the serial port is not currently at that rate, that means we just
+         * issued the ATS304 command last time through, so this time we restore
+         * the user's selected rate and issue the normal starmode reset string.
+         */
+        if (strip_info->user_baud == get_baud(tty))
+	    {
+	    static const char b0[] = "ate0q1s304=57600\r";
+	    static const char b1[] = "ate0q1s304=115200\r";
+	    static const StringDescriptor baudstring[2] =
+                { { b0, sizeof(b0)-1 }, { b1, sizeof(b1)-1 } };
+	    set_baud(tty, B19200);
+	    if      (strip_info->user_baud == B57600 ) s = baudstring[0];
+	    else if (strip_info->user_baud == B115200) s = baudstring[1];
+	    else s = baudstring[1]; /* For now */
+	    }
+        else set_baud(tty, strip_info->user_baud);
+        }
+
+    tty->driver.write(tty, 0, s.string, s.length);
+#ifdef EXT_COUNTERS
+    strip_info->tx_ebytes += s.length;
+#endif
 }
 
 /*
@@ -1572,6 +1374,9 @@
         int num_written = tty->driver.write(tty, 0, strip_info->tx_head, strip_info->tx_left);
         strip_info->tx_left -= num_written;
         strip_info->tx_head += num_written;
+#ifdef EXT_COUNTERS
+        strip_info->tx_sbytes += num_written;
+#endif
         RestoreInterrupts(intstat);
     }
     else            /* Else start transmission of another packet */
@@ -1582,12 +1387,21 @@
     }
 }
 
-static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_info, struct sk_buff *skb)
+static __u8 *add_checksum(__u8 *buffer, __u8 *end)
 {
-#if DO_PROC_NET_STRIP_TRACE
-    unsigned char *start_ptr;
-#endif DO_PROC_NET_STRIP_TRACE
+    __u16 sum = 0;
+    __u8 *p = buffer;
+    while (p < end) sum += *p++;
+    end[3] = hextable[sum & 0xF]; sum >>= 4;
+    end[2] = hextable[sum & 0xF]; sum >>= 4;
+    end[1] = hextable[sum & 0xF]; sum >>= 4;
+    end[0] = hextable[sum & 0xF];
+    return(end+4);
+}
 
+static unsigned char *strip_make_packet(unsigned char *buffer, struct strip *strip_info, struct sk_buff *skb)
+{
+    __u8           *ptr = buffer;
     __u8           *stuffstate = NULL;
     STRIP_Header   *header     = (STRIP_Header *)skb->data;
     MetricomAddress haddr      = header->dst_addr;
@@ -1602,7 +1416,6 @@
     {
         printk(KERN_ERR "%s: strip_make_packet: Unknown packet type 0x%04X\n",
             strip_info->dev.name, ntohs(header->protocol));
-        strip_info->tx_dropped++;
         return(NULL);
     }
 
@@ -1610,7 +1423,16 @@
     {
         printk(KERN_ERR "%s: Dropping oversized transmit packet: %d bytes\n",
             strip_info->dev.name, len);
-        strip_info->tx_dropped++;
+        return(NULL);
+    }
+
+    /*
+     * If we're sending to ourselves, discard the packet.
+     * (Metricom radios choke if they try to send a packet to their own address.)
+     */
+    if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr)))
+    {
+        printk(KERN_ERR "%s: Dropping packet addressed to self\n", strip_info->dev.name);
         return(NULL);
     }
 
@@ -1620,22 +1442,21 @@
      */
     if (haddr.c[0] == 0xFF)
     {
-        /*IPaddr a;
-        a.l = strip_info->dev.pa_brdaddr;
-        printk(KERN_INFO "%s: Broadcast packet! Sending to %d.%d.%d.%d\n",
-                strip_info->dev.name, a.b[0], a.b[1], a.b[2], a.b[3]);*/
-
+        /* arp_query returns 1 if it succeeds in looking up the address, 0 if it fails */
         if (!arp_query(haddr.c, strip_info->dev.pa_brdaddr, &strip_info->dev))
         {
-            /*IPaddr a;
-            a.l = strip_info->dev.pa_brdaddr;
-            printk(KERN_INFO "%s: No ARP cache entry for %d.%d.%d.%d\n",
-                strip_info->dev.name, a.b[0], a.b[1], a.b[2], a.b[3]);
-            strip_info->tx_dropped++;*/
+            printk(KERN_ERR "%s: Unable to send packet (no broadcast hub configured)\n",
+                strip_info->dev.name);
             return(NULL);
         }
+	/*
+	 * If we are the broadcast hub, don't bother sending to ourselves.
+	 * (Metricom radios choke if they try to send a packet to their own address.)
+	 */
+        if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) return(NULL);
     }
 
+    *ptr++ = 0x0D;
     *ptr++ = '*';
     *ptr++ = hextable[haddr.c[2] >> 4];
     *ptr++ = hextable[haddr.c[2] & 0xF];
@@ -1652,17 +1473,9 @@
     *ptr++ = key.c[2];
     *ptr++ = key.c[3];
 
-#if DO_PROC_NET_STRIP_TRACE
-    start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
     ptr = StuffData(skb->data + sizeof(STRIP_Header), len, ptr, &stuffstate);
 
-#if DO_PROC_NET_STRIP_TRACE
-    packet_log(strip_info, skb->data + sizeof(STRIP_Header), EntrySend,
-	       header, len, ptr-start_ptr,
-	       slip_len(skb->data + sizeof(STRIP_Header), len)); 
-#endif DO_PROC_NET_STRIP_TRACE
+    if (strip_info->firmware_level >= ChecksummedMessages) ptr = add_checksum(buffer+1, ptr);
 
     *ptr++ = 0x0D;
     return(ptr);
@@ -1670,58 +1483,110 @@
 
 static void strip_send(struct strip *strip_info, struct sk_buff *skb)
 {
+    MetricomAddress haddr;
     unsigned char *ptr = strip_info->tx_buff;
+    int doreset = (long)jiffies - strip_info->watchdog_doreset >= 0;
+    int doprobe = (long)jiffies - strip_info->watchdog_doprobe >= 0 && !doreset;
 
-    /* If we have a packet, encapsulate it and put it in the buffer */
+    /*
+     * 1. If we have a packet, encapsulate it and put it in the buffer
+     */
     if (skb)
     {
-        ptr = strip_make_packet(ptr, strip_info, skb);
-        /* If error, unlock and return */
-        if (!ptr) { strip_unlock(strip_info); return; }
-        strip_info->tx_packets++;        /* Count another successful packet */
-        /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/
-        /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/
-    }
-
-    /* Set up the strip_info ready to send the data */
-    strip_info->tx_head =       strip_info->tx_buff;
-    strip_info->tx_left = ptr - strip_info->tx_buff;
-    strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-
-    /* If watchdog has expired, reset the radio */
-    if ((long)jiffies - strip_info->watchdog_doreset >= 0)
-    {
-        ResetRadio(strip_info);
-        return;
-        /* Note: if there's a packet to send, strip_write_some_more
-                 will do it after the reset has finished */
+        char *newptr = strip_make_packet(ptr, strip_info, skb);
+        strip_info->tx_pps_count++;
+        if (!newptr) strip_info->tx_dropped++;
+        else
+        {
+            ptr = newptr;
+            strip_info->sx_pps_count++;
+            strip_info->tx_packets++;        /* Count another successful packet */
+#ifdef EXT_COUNTERS
+            strip_info->tx_bytes += skb->len;
+            strip_info->tx_rbytes += ptr - strip_info->tx_buff;
+#endif
+            /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/
+            /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/
+        }
     }
 
-    /* No reset.
-     * If it is time for another tickle, tack it on the end of the packet
+    /*
+     * 2. If it is time for another tickle, tack it on, after the packet
      */
-    if ((long)jiffies - strip_info->watchdog_doprobe >= 0)
+    if (doprobe)
     {
-        /* Send tickle to make radio protest */
-        /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/
-        const char *TickleString = TickleString1;
-        int length = sizeof(TickleString1)-1;
-        if (strip_info->structured_messages)
+        StringDescriptor ts = CommandString[strip_info->next_command];
+#if TICKLE_TIMERS
         {
-            TickleString = TickleString2;
-            length = sizeof(TickleString2)-1;
+        struct timeval tv;
+        do_gettimeofday(&tv);
+        printk(KERN_INFO "**** Sending tickle string %d      at %02d.%06d\n",
+            strip_info->next_command, tv.tv_sec % 100, tv.tv_usec);
         }
-        memcpy(ptr, TickleString, length);
-        strip_info->tx_left += length;
+#endif
+        if (ptr == strip_info->tx_buff) *ptr++ = 0x0D;
+
+        *ptr++ = '*'; /* First send "**" to provoke an error message */
+        *ptr++ = '*';
+
+        /* Then add the command */
+        memcpy(ptr, ts.string, ts.length);
+
+        /* Add a checksum ? */
+        if (strip_info->firmware_level < ChecksummedMessages) ptr += ts.length;
+        else ptr = add_checksum(ptr, ptr + ts.length);
+
+        *ptr++ = 0x0D; /* Terminate the command with a <CR> */
+
+        /* Cycle to next periodic command? */
+        if (strip_info->firmware_level >= StructuredMessages)
+                if (++strip_info->next_command >= ELEMENTS_OF(CommandString))
+                        strip_info->next_command = 0;
+#ifdef EXT_COUNTERS
+        strip_info->tx_ebytes += ts.length;
+#endif
         strip_info->watchdog_doprobe = jiffies + 10 * HZ;
         strip_info->watchdog_doreset = jiffies + 1 * HZ;
+        /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/
     }
 
     /*
-     * If it is time for a periodic ARP, queue one up to be sent
+     * 3. Set up the strip_info ready to send the data (if any).
+     */
+    strip_info->tx_head = strip_info->tx_buff;
+    strip_info->tx_left = ptr - strip_info->tx_buff;
+    strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+
+    /*
+     * 4. Debugging check to make sure we're not overflowing the buffer.
+     */
+    if (strip_info->tx_size - strip_info->tx_left < 20)
+        printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name,
+            strip_info->tx_left, strip_info->tx_size - strip_info->tx_left);
+
+    /*
+     * 5. If watchdog has expired, reset the radio. Note: if there's data waiting in
+     * the buffer, strip_write_some_more will send it after the reset has finished
+     */
+    if (doreset) { ResetRadio(strip_info); return; }
+
+    /*
+     * 6. If it is time for a periodic ARP, queue one up to be sent.
+     * We only do this if:
+     *  1. The radio is working
+     *  2. It's time to send another periodic ARP
+     *  3. We really know what our address is (and it is not manually set to zero)
+     *  4. We have a designated broadcast address configured
+     * If we queue up an ARP packet when we don't have a designated broadcast
+     * address configured, then the packet will just have to be discarded in
+     * strip_make_packet. This is not fatal, but it causes misleading information
+     * to be displayed in tcpdump. tcpdump will report that periodic APRs are
+     * being sent, when in fact they are not, because they are all being dropped
+     * in the strip_make_packet routine.
      */
     if (strip_info->working && (long)jiffies - strip_info->gratuitous_arp >= 0 &&
-        memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)))
+        memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
+        arp_query(haddr.c, strip_info->dev.pa_brdaddr, &strip_info->dev))
     {
         /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n",
             strip_info->dev.name, strip_info->arp_interval / HZ);*/
@@ -1729,16 +1594,18 @@
         strip_info->arp_interval *= 2;
         if (strip_info->arp_interval > MaxARPInterval)
             strip_info->arp_interval = MaxARPInterval;
-        arp_send(ARPOP_REPLY, ETH_P_ARP, strip_info->dev.pa_addr,
-                        &strip_info->dev, strip_info->dev.pa_addr,
-                        NULL, strip_info->dev.dev_addr, NULL);
+        arp_send(ARPOP_REPLY, ETH_P_ARP,
+            strip_info->dev.pa_addr,	/* Target address of ARP packet is our address */
+            &strip_info->dev,		/* Device to send packet on */
+            strip_info->dev.pa_addr,	/* Source IP address this ARP packet comes from */
+            NULL,			/* Destination HW address is NULL (broadcast it) */
+            strip_info->dev.dev_addr,	/* Source HW address is our HW address */
+            strip_info->dev.dev_addr);	/* Target HW address is our HW address (redundant) */
     }
 
-    if (strip_info->tx_size - strip_info->tx_left < 20)
-        printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name,
-            strip_info->tx_left, strip_info->tx_size - strip_info->tx_left);
-
-    /* All ready. Start the transmission */
+    /*
+     * 7. All ready. Start the transmission
+     */
     strip_write_some_more(strip_info->tty);
 }
 
@@ -1759,6 +1626,33 @@
     if (strip_info->mtu != strip_info->dev.mtu)
         strip_changedmtu(strip_info);
 
+    if (jiffies - strip_info->pps_timer > HZ)
+    {
+        unsigned long t = jiffies - strip_info->pps_timer;
+        unsigned long rx_pps_count = (strip_info->rx_pps_count * HZ * 8 + t/2) / t;
+        unsigned long tx_pps_count = (strip_info->tx_pps_count * HZ * 8 + t/2) / t;
+        unsigned long sx_pps_count = (strip_info->sx_pps_count * HZ * 8 + t/2) / t;
+
+        strip_info->pps_timer = jiffies;
+        strip_info->rx_pps_count = 0;
+        strip_info->tx_pps_count = 0;
+        strip_info->sx_pps_count = 0;
+
+        strip_info->rx_average_pps = (strip_info->rx_average_pps + rx_pps_count + 1) / 2;
+        strip_info->tx_average_pps = (strip_info->tx_average_pps + tx_pps_count + 1) / 2;
+        strip_info->sx_average_pps = (strip_info->sx_average_pps + sx_pps_count + 1) / 2;
+
+        if (rx_pps_count / 8 >= 10)
+            printk(KERN_INFO "%s: WARNING: Receiving %ld packets per second.\n",
+                strip_info->dev.name, rx_pps_count / 8);
+        if (tx_pps_count / 8 >= 10)
+            printk(KERN_INFO "%s: WARNING: Tx        %ld packets per second.\n",
+                strip_info->dev.name, tx_pps_count / 8);
+        if (sx_pps_count / 8 >= 10)
+            printk(KERN_INFO "%s: WARNING: Sending   %ld packets per second.\n",
+                strip_info->dev.name, sx_pps_count / 8);
+    }
+
     strip_send(strip_info, skb);
 
     if (skb) dev_kfree_skb(skb, FREE_WRITE);
@@ -1766,6 +1660,17 @@
 }
 
 /*
+ * IdleTask periodically calls strip_xmit, so even when we have no IP packets
+ * to send for an extended period of time, the watchdog processing still gets
+ * done to ensure that the radio stays in Starmode
+ */
+
+static void strip_IdleTask(unsigned long parameter)
+{
+    strip_xmit(NULL, (struct device *)parameter);
+}
+
+/*
  * Create the MAC header for an arbitrary protocol layer
  *
  * saddr!=NULL        means use this specific address (n/a for Metricom)
@@ -1779,19 +1684,20 @@
 static int strip_header(struct sk_buff *skb, struct device *dev,
         unsigned short type, void *daddr, void *saddr, unsigned len)
 {
+    struct strip *strip_info = (struct strip *)(dev->priv);
     STRIP_Header *header = (STRIP_Header *)skb_push(skb, sizeof(STRIP_Header));
 
     /*printk(KERN_INFO "%s: strip_header 0x%04X %s\n", dev->name, type,
         type == ETH_P_IP ? "IP" : type == ETH_P_ARP ? "ARP" : "");*/
 
-    memcpy(header->src_addr.c, dev->dev_addr, dev->addr_len);
+    header->src_addr = strip_info->true_dev_addr;
     header->protocol = htons(type);
 
     /*HexDump("strip_header", (struct strip *)(dev->priv), skb->data, skb->data + skb->len);*/
 
     if (!daddr) return(-dev->hard_header_len);
 
-    memcpy(header->dst_addr.c, daddr, dev->addr_len);
+    header->dst_addr = *(MetricomAddress*)daddr;
     return(dev->hard_header_len);
 }
 
@@ -1806,28 +1712,21 @@
 static int strip_rebuild_header(void *buff, struct device *dev,
         unsigned long dst, struct sk_buff *skb)
 {
+    struct strip *strip_info = (struct strip *)(dev->priv);
     STRIP_Header *header = (STRIP_Header *)buff;
-
-    /*printk(KERN_INFO "%s: strip_rebuild_header\n", dev->name);*/
-
-#ifdef CONFIG_INET
     /* Arp find returns zero if if knows the address, */
     /* or if it doesn't know the address it sends an ARP packet and returns non-zero */
-    return arp_find(header->dst_addr.c, dst, dev, dev->pa_addr, skb)? 1 : 0;
-#else
-    return 0;
-#endif
-}
+    int arp_result = arp_find(header->dst_addr.c, dst, dev, dev->pa_addr, skb);
 
-/*
- * IdleTask periodically calls strip_xmit, so even when we have no IP packets
- * to send for an extended period of time, the watchdog processing still gets
- * done to ensure that the radio stays in Starmode
- */
+    if (arp_result == 0 && !memcmp(header->dst_addr.c, strip_info->true_dev_addr.c, sizeof(header->dst_addr.c)))
+    {
+        IPaddr x;
+        x.l = dst;
+        printk(KERN_ERR "%s: ARP lookup %d.%d.%d.%d returned own address\n",
+            strip_info->dev.name, x.b[0], x.b[1], x.b[2], x.b[3]);
+    }
 
-static void strip_IdleTask(unsigned long parameter)
-{
-    strip_xmit(NULL, (struct device *)parameter);
+    return(arp_result);
 }
 
 
@@ -1839,23 +1738,121 @@
     return 0x10000;  /* We can handle an infinite amount of data. :-) */
 }
 
-static void get_radio_address(struct strip *strip_info, __u8 *p)
+/*
+ * This function parses the response to the ATS300? command,
+ * extracting the radio version and serial number.
+ */
+static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end)
+{
+    __u8 *p, *value_begin, *value_end;
+    int len;
+    
+    /* Determine the beginning of the second line of the payload */
+    p = ptr;
+    while (p < end && *p != 10) p++;
+    if (p >= end) return;
+    p++;
+    value_begin = p;
+    
+    /* Determine the end of line */
+    while (p < end && *p != 10) p++;
+    if (p >= end) return;
+    value_end = p;
+    p++;
+     
+    len = value_end - value_begin;
+    len = MIN(len, sizeof(FirmwareVersion) - 1);
+    if (strip_info->firmware_version.c[0] == 0)
+        printk(KERN_INFO "%s: Radio Firmware: %.*s\n",
+            strip_info->dev.name, len, value_begin);
+    sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin);
+    
+    /* Look for the first colon */
+    while (p < end && *p != ':') p++;
+    if (p >= end) return;
+    /* Skip over the space */
+    p += 2;
+    len = sizeof(SerialNumber) - 1;
+    if (p + len <= end) {
+        sprintf(strip_info->serial_number.c, "%.*s", len, p);
+    }
+    else {
+     	printk(KERN_DEBUG "STRIP: radio serial number shorter (%d) than expected (%d)\n",
+     	       end - p, len);
+    }
+}
+
+/*
+ * This function parses the response to the ATS325? command,
+ * extracting the radio battery voltage.
+ */
+static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end)
+{
+    int len;
+
+    len = sizeof(BatteryVoltage) - 1;
+    if (ptr + len <= end) {
+        sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr);
+    }
+    else {
+ 	printk(KERN_DEBUG "STRIP: radio voltage string shorter (%d) than expected (%d)\n",
+ 	       end - ptr, len);
+    }
+}
+
+/*
+ * This function parses the responses to the AT~LA and ATS311 commands,
+ * which list the radio's neighbours.
+ */
+static void get_radio_neighbours(MetricomNodeTable *table, __u8 *ptr, __u8 *end)
+{
+    table->num_nodes = 0;
+    while (ptr < end && table->num_nodes < NODE_TABLE_SIZE)
+        {
+        MetricomNode *node = &table->node[table->num_nodes++];
+        char *dst = node->c, *limit = dst + sizeof(*node) - 1;
+        while (ptr < end && *ptr <= 32) ptr++;
+        while (ptr < end && dst < limit && *ptr != 10) *dst++ = *ptr++;
+        *dst++ = 0;
+        while (ptr < end && ptr[-1] != 10) ptr++;
+        }
+    do_gettimeofday(&table->timestamp);
+}
+
+static int get_radio_address(struct strip *strip_info, __u8 *p)
 {
     MetricomAddress addr;
 
-    string_to_radio_address(&addr, p);
+    if (string_to_radio_address(&addr, p)) return(1);
 
     /* See if our radio address has changed */
-    if (memcmp(strip_info->dev.dev_addr, addr.c, sizeof(addr)))
+    if (memcmp(strip_info->true_dev_addr.c, addr.c, sizeof(addr)))
     {
         MetricomAddressString addr_string;
         radio_address_to_string(&addr, &addr_string);
-        printk(KERN_INFO "%s: My radio address = %s\n", strip_info->dev.name, addr_string.c);
-        memcpy(strip_info->dev.dev_addr, addr.c, sizeof(addr));
+        printk(KERN_INFO "%s: Radio address = %s\n", strip_info->dev.name, addr_string.c);
+        strip_info->true_dev_addr = addr;
+        if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = addr;
         /* Give the radio a few seconds to get its head straight, then send an arp */
-        strip_info->gratuitous_arp = jiffies + 6 * HZ;
+        strip_info->gratuitous_arp = jiffies + 15 * HZ;
         strip_info->arp_interval = 1 * HZ;
     }
+    return(0);
+}
+
+static int verify_checksum(struct strip *strip_info)
+{
+    __u8 *p = strip_info->sx_buff;
+    __u8 *end = strip_info->sx_buff + strip_info->sx_count - 4;
+    u_short sum = (READHEX16(end[0]) << 12) | (READHEX16(end[1]) << 8) |
+                  (READHEX16(end[2]) <<  4) | (READHEX16(end[3]));
+    while (p < end) sum -= *p++;
+    if (sum == 0 && strip_info->firmware_level == StructuredMessages)
+    {
+        strip_info->firmware_level = ChecksummedMessages;
+        printk(KERN_INFO "%s: Radio provides message checksums\n", strip_info->dev.name);
+    }
+    return(sum == 0);
 }
 
 static void RecvErr(char *msg, struct strip *strip_info)
@@ -1866,116 +1863,165 @@
     strip_info->rx_errors++;
 }
 
-static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg)
+static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg, u_long len)
 {
-    static const char ERR_001[] = "001"; /* Not in StarMode! */
-    static const char ERR_002[] = "002"; /* Remap handle */
-    static const char ERR_003[] = "003"; /* Can't resolve name */
-    static const char ERR_004[] = "004"; /* Name too small or missing */
-    static const char ERR_005[] = "005"; /* Bad count specification */
-    static const char ERR_006[] = "006"; /* Header too big */
-    static const char ERR_007[] = "007"; /* Body too big */
-    static const char ERR_008[] = "008"; /* Bad character in name */
-    static const char ERR_009[] = "009"; /* No count or line terminator */
-
-    if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1))
+    if (has_prefix(msg, len, "001")) /* Not in StarMode! */
     {
         RecvErr("Error Msg:", strip_info);
         printk(KERN_INFO "%s: Radio %s is not in StarMode\n",
             strip_info->dev.name, sendername);
     }
-    else if (!strncmp(msg, ERR_002, sizeof(ERR_002)-1))
+
+    else if (has_prefix(msg, len, "002")) /* Remap handle */
     {
-        RecvErr("Error Msg:", strip_info);
-#ifdef notyet        /*Kernel doesn't have scanf!*/
-        int handle;
-        __u8 newname[64];
-        sscanf(msg, "ERR_002 Remap handle &%d to name %s", &handle, newname);
-        printk(KERN_INFO "%s: Radio name %s is handle %d\n",
-            strip_info->dev.name, newname, handle);
-#endif
+	/* We ignore "Remap handle" messages for now */
     }
-    else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1))
+
+    else if (has_prefix(msg, len, "003")) /* Can't resolve name */
     {
         RecvErr("Error Msg:", strip_info);
         printk(KERN_INFO "%s: Destination radio name is unknown\n",
             strip_info->dev.name);
     }
-    else if (!strncmp(msg, ERR_004, sizeof(ERR_004)-1))
+
+    else if (has_prefix(msg, len, "004")) /* Name too small or missing */
     {
         strip_info->watchdog_doreset = jiffies + LongTime;
+#if TICKLE_TIMERS
+        {
+        struct timeval tv;
+        do_gettimeofday(&tv);
+        printk(KERN_INFO "**** Got ERR_004 response         at %02d.%06d\n",
+            tv.tv_sec % 100, tv.tv_usec);
+        }
+#endif
         if (!strip_info->working)
         {
             strip_info->working = TRUE;
-            printk(KERN_INFO "%s: Radio now in starmode\n",
-                strip_info->dev.name);
+            printk(KERN_INFO "%s: Radio now in starmode\n", strip_info->dev.name);
             /*
              * If the radio has just entered a working state, we should do our first
              * probe ASAP, so that we find out our radio address etc. without delay.
              */
             strip_info->watchdog_doprobe = jiffies;
         }
-        if (!strip_info->structured_messages && sendername)
+        if (strip_info->firmware_level == NoStructure && sendername)
         {
-            strip_info->structured_messages = TRUE;
-            printk(KERN_INFO "%s: Radio provides structured messages\n",
-                strip_info->dev.name);
+            strip_info->firmware_level = StructuredMessages;
+            strip_info->next_command   = 0; /* Try to enable checksums ASAP */
+            printk(KERN_INFO "%s: Radio provides structured messages\n", strip_info->dev.name);
+        }
+        if (strip_info->firmware_level >= StructuredMessages)
+        {
+            /*
+             * If this message has a valid checksum on the end, then the call to verify_checksum
+             * will elevate the firmware_level to ChecksummedMessages for us. (The actual return
+             * code from verify_checksum is ignored here.)
+             */
+            verify_checksum(strip_info);
+            /*
+             * If the radio has structured messages but we don't yet have all our information about it,
+             * we should do probes without delay, until we have gathered all the information
+             */
+            if (!GOT_ALL_RADIO_INFO(strip_info)) strip_info->watchdog_doprobe = jiffies;
         }
     }
-    else if (!strncmp(msg, ERR_005, sizeof(ERR_005)-1))
+
+    else if (has_prefix(msg, len, "005")) /* Bad count specification */
         RecvErr("Error Msg:", strip_info);
-    else if (!strncmp(msg, ERR_006, sizeof(ERR_006)-1))
+
+    else if (has_prefix(msg, len, "006")) /* Header too big */
         RecvErr("Error Msg:", strip_info);
-    else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1))
+
+    else if (has_prefix(msg, len, "007")) /* Body too big */
     {
-        /*
-         * Note: This error knocks the radio back into
-         * command mode.
-         */
         RecvErr("Error Msg:", strip_info);
-        printk(KERN_ERR "%s: Error! Packet size too big for radio.",
+        printk(KERN_ERR "%s: Error! Packet size too big for radio.\n",
             strip_info->dev.name);
-        strip_info->watchdog_doreset = jiffies;        /* Do reset ASAP */
     }
-    else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1))
+
+    else if (has_prefix(msg, len, "008")) /* Bad character in name */
     {
         RecvErr("Error Msg:", strip_info);
         printk(KERN_ERR "%s: Radio name contains illegal character\n",
             strip_info->dev.name);
     }
-    else if (!strncmp(msg, ERR_009, sizeof(ERR_009)-1))
+
+    else if (has_prefix(msg, len, "009")) /* No count or line terminator */
+        RecvErr("Error Msg:", strip_info);
+
+    else if (has_prefix(msg, len, "010")) /* Invalid checksum */
+        RecvErr("Error Msg:", strip_info);
+
+    else if (has_prefix(msg, len, "011")) /* Checksum didn't match */
+        RecvErr("Error Msg:", strip_info);
+
+    else if (has_prefix(msg, len, "012")) /* Failed to transmit packet */
         RecvErr("Error Msg:", strip_info);
+
     else
         RecvErr("Error Msg:", strip_info);
 }
 
 static void process_AT_response(struct strip *strip_info, __u8 *ptr, __u8 *end)
 {
-    static const char ATS305[] = "ATS305?";
-    static const char ATS300[] = "ATS300?";
-    static const char ATS325[] = "ATS325?";
-    static const char ATI2[] = "AT~I2 nn";
-
-    /* Skip to the first newline character */
+    u_long len;
     __u8 *p = ptr;
-    while (p < end && *p != 10) p++;
-    if (p >= end) return;
-    p++;
+    while (p < end && p[-1] != 10) p++; /* Skip past first newline character */
+    /* Now ptr points to the AT command, and p points to the text of the response. */
+    len = p-ptr;
 
-    if (!strncmp(ptr, ATS305, sizeof(ATS305)-1))
+#if TICKLE_TIMERS
     {
-        if (IS_RADIO_ADDRESS(p)) get_radio_address(strip_info, p);
-    }
-    else if (!strncmp(ptr, ATS300, sizeof(ATS300)-1)) {
-        get_radio_version(strip_info, p, end);
-    }
-    else if (!strncmp(ptr, ATS325, sizeof(ATS325)-1)) {
-        get_radio_voltage(strip_info, p, end);
+    struct timeval tv;
+    do_gettimeofday(&tv);
+    printk(KERN_INFO "**** Got AT response %.7s      at %02d.%06d\n",
+        ptr, tv.tv_sec % 100, tv.tv_usec);
     }
-    else if (!strncmp(ptr, ATI2, sizeof(ATI2)-1)) {
-        get_radio_neighbors(strip_info, p, end);
+#endif
+
+    if      (has_prefix(ptr, len, "ATS300?" )) get_radio_version(strip_info, p, end);
+    else if (has_prefix(ptr, len, "ATS305?" )) get_radio_address(strip_info, p);
+    else if (has_prefix(ptr, len, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end);
+    else if (has_prefix(ptr, len, "ATS319=7")) verify_checksum(strip_info);
+    else if (has_prefix(ptr, len, "ATS325?" )) get_radio_voltage(strip_info, p, end);
+    else if (has_prefix(ptr, len, "AT~LA"   )) get_radio_neighbours(&strip_info->portables, p, end);
+    else                                       RecvErr("Unknown AT Response:", strip_info);
+}
+
+static void process_ACK(struct strip *strip_info, __u8 *ptr, __u8 *end)
+{
+    /* Currently we don't do anything with ACKs from the radio */
+}
+
+static void process_Info(struct strip *strip_info, __u8 *ptr, __u8 *end)
+{
+    if (ptr+16 > end) RecvErr("Bad Info Msg:", strip_info);
+}
+
+static struct device *get_strip_dev(struct strip *strip_info)
+{
+    /* If our hardware address is *manually set* to zero, and we know our */
+    /* real radio hardware address, try to find another strip device that has been */
+    /* manually set to that address that we can 'transfer ownership' of this packet to  */
+    if (strip_info->manual_dev_addr &&
+        !memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
+        memcmp(&strip_info->true_dev_addr, zero_address.c, sizeof(zero_address)))
+    {
+        struct device *dev = dev_base;
+        while (dev)
+        {
+            if (dev->type == strip_info->dev.type &&
+                !memcmp(dev->dev_addr, &strip_info->true_dev_addr, sizeof(MetricomAddress)))
+            {
+                printk(KERN_INFO "%s: Transferred packet ownership to %s.\n",
+                    strip_info->dev.name, dev->name);
+                return(dev);
+            }
+            dev = dev->next;
+        }
     }
-    else RecvErr("Unknown AT Response:", strip_info);
+    return(&strip_info->dev);
 }
 
 /*
@@ -1987,14 +2033,14 @@
     struct sk_buff *skb = dev_alloc_skb(sizeof(STRIP_Header) + packetlen);
     if (!skb)
     {
-        printk(KERN_INFO "%s: memory squeeze, dropping packet.\n", strip_info->dev.name);
+        printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", strip_info->dev.name);
         strip_info->rx_dropped++;
     }
     else
     {
         memcpy(skb_put(skb, sizeof(STRIP_Header)), header, sizeof(STRIP_Header));
         memcpy(skb_put(skb, packetlen), strip_info->rx_buff, packetlen);
-        skb->dev      = &strip_info->dev;
+        skb->dev      = get_strip_dev(strip_info);
         skb->protocol = header->protocol;
         skb->mac.raw  = skb->data;
 
@@ -2005,6 +2051,10 @@
 
         /* Finally, hand the packet up to the next layer (e.g. IP or ARP, etc.) */
         strip_info->rx_packets++;
+        strip_info->rx_pps_count++;
+#ifdef EXT_COUNTERS
+        strip_info->rx_bytes += packetlen;
+#endif
         netif_rx(skb);
     }
 }
@@ -2013,10 +2063,6 @@
 {
     __u16 packetlen;
 
-#if DO_PROC_NET_STRIP_TRACE
-    __u8 *start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
     /* Decode start of the IP packet header */
     ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4);
     if (!ptr)
@@ -2027,9 +2073,9 @@
 
     packetlen = ((__u16)strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3];
 
-    if (packetlen > MAX_STRIP_MTU)
+    if (packetlen > MAX_RECV_MTU)
     {
-        printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n",
+        printk(KERN_INFO "%s: Dropping oversized received IP packet: %d bytes\n",
             strip_info->dev.name, packetlen);
         strip_info->rx_dropped++;
         return;
@@ -2053,11 +2099,6 @@
 
     header->protocol = htons(ETH_P_IP);
 
-#if DO_PROC_NET_STRIP_TRACE
-    packet_log(strip_info, strip_info->rx_buff, EntryReceive, header,
-	       packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen)); 
-#endif DO_PROC_NET_STRIP_TRACE
-
     deliver_packet(strip_info, header, packetlen);
 }
 
@@ -2066,10 +2107,6 @@
     __u16 packetlen;
     struct arphdr *arphdr = (struct arphdr *)strip_info->rx_buff;
 
-#if DO_PROC_NET_STRIP_TRACE
-    __u8 *start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
     /* Decode start of the ARP packet */
     ptr = UnStuffData(ptr, end, strip_info->rx_buff, 8);
     if (!ptr)
@@ -2080,9 +2117,9 @@
 
     packetlen = 8 + (arphdr->ar_hln + arphdr->ar_pln) * 2;
 
-    if (packetlen > MAX_STRIP_MTU)
+    if (packetlen > MAX_RECV_MTU)
     {
-        printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n",
+        printk(KERN_INFO "%s: Dropping oversized received ARP packet: %d bytes\n",
             strip_info->dev.name, packetlen);
         strip_info->rx_dropped++;
         return;
@@ -2108,15 +2145,47 @@
 
     header->protocol = htons(ETH_P_ARP);
 
-#if DO_PROC_NET_STRIP_TRACE
-    packet_log(strip_info, strip_info->rx_buff, EntryReceive, header,
-	       packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen)); 
-#endif DO_PROC_NET_STRIP_TRACE
-
     deliver_packet(strip_info, header, packetlen);
 }
 
-static void process_packet(struct strip *strip_info)
+/*
+ * process_text_message processes a <CR>-terminated block of data received
+ * from the radio that doesn't begin with a '*' character. All normal
+ * Starmode communication messages with the radio begin with a '*',
+ * so any text that does not indicates a serial port error, a radio that
+ * is in Hayes command mode instead of Starmode, or a radio with really
+ * old firmware that doesn't frame its Starmode responses properly.
+ */
+static void process_text_message(struct strip *strip_info)
+{
+    __u8 *msg = strip_info->sx_buff;
+    int len   = strip_info->sx_count;
+
+    /* Check for anything that looks like it might be our radio name */
+    /* (This is here for backwards compatibility with old firmware)  */
+    if (len == 9 && get_radio_address(strip_info, msg) == 0) return;
+
+    if (text_equal(msg, len, "OK"      )) return; /* Ignore 'OK' responses from prior commands */
+    if (text_equal(msg, len, "ERROR"   )) return; /* Ignore 'ERROR' messages */
+    if (has_prefix(msg, len, "ate0q1"  )) return; /* Ignore character echo back from the radio */
+
+    /* Catch other error messages */
+    /* (This is here for backwards compatibility with old firmware) */
+    if (has_prefix(msg, len, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4], len-4); return; }
+    
+    RecvErr("No initial *", strip_info);
+}
+
+/*
+ * process_message processes a <CR>-terminated block of data received
+ * from the radio. If the radio is not in Starmode or has old firmware,
+ * it may be a line of text in response to an AT command. Ideally, with
+ * a current radio that's properly in Starmode, all data received should
+ * be properly framed and checksummed radio message blocks, containing
+ * either a starmode packet, or a other communication from the radio
+ * firmware, like "INF_" Info messages and &COMMAND responses.
+ */
+static void process_message(struct strip *strip_info)
 {
     STRIP_Header header = { zero_address, zero_address, 0 };
     __u8 *ptr = strip_info->sx_buff;
@@ -2124,29 +2193,11 @@
     __u8 sendername[32], *sptr = sendername;
     MetricomKey key;
 
-    /* Ignore 'OK' responses from prior commands */
-    if (strip_info->sx_count == 2 && ptr[0] == 'O' && ptr[1] == 'K') return;
-
-    /* Check for anything that looks like it might be our radio name: dddd-dddd */
-    /* (This is here for backwards compatibility with old firmware)             */
-    if (strip_info->sx_count == 9 && IS_RADIO_ADDRESS(ptr))
-    {
-        get_radio_address(strip_info, ptr);
-        return;
-    }
-
     /*HexDump("Receiving", strip_info, ptr, end);*/
 
     /* Check for start of address marker, and then skip over it */
-    if (*ptr != '*')
-    {
-        /* Catch other error messages */
-        if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_')
-            RecvErr_Message(strip_info, NULL, &ptr[4]);
-        else RecvErr("No initial *", strip_info);
-        return;
-    }
-    ptr++; /* Skip the initial '*' */
+    if (*ptr == '*') ptr++;
+    else { process_text_message(strip_info); return; }
 
     /* Copy out the return address */
     while (ptr < end && *ptr != '*' && sptr < ARRAY_END(sendername)-1) *sptr++ = *ptr++;
@@ -2164,16 +2215,39 @@
     /* (This is here for backwards compatibility with old firmware) */
     if (!strcmp(sendername, "&COMMAND"))
     {
-        strip_info->structured_messages = FALSE;
+        strip_info->firmware_level = NoStructure;
+        strip_info->next_command   = CompatibilityCommand;
         return;
     }
 
-    if (ptr+4 >= end)
+    if (ptr+4 > end)
     {
         RecvErr("No proto key", strip_info);
         return;
     }
 
+    /* Get the protocol key out of the buffer */
+    key.c[0] = *ptr++;
+    key.c[1] = *ptr++;
+    key.c[2] = *ptr++;
+    key.c[3] = *ptr++;
+
+    /* If we're using checksums, verify the checksum at the end of the packet */
+    if (strip_info->firmware_level >= ChecksummedMessages)
+    {
+        end -= 4;	/* Chop the last four bytes off the packet (they're the checksum) */
+        if (ptr > end)
+        {
+            RecvErr("Missing Checksum", strip_info);
+            return;
+        }
+        if (!verify_checksum(strip_info))
+        {
+            RecvErr("Bad Checksum", strip_info);
+            return;
+        }
+    }
+
     /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev.name, sendername);*/
 
     /*
@@ -2181,38 +2255,45 @@
      * We assume that the destination address was our address (the radio does not
      * tell us this). If the radio supplies a source address, then we use it.
      */
-    memcpy(&header.dst_addr, strip_info->dev.dev_addr, sizeof(MetricomAddress));
-    if (IS_RADIO_ADDRESS(sendername)) string_to_radio_address(&header.src_addr, sendername);
-
-    /* Get the protocol key out of the buffer */
-    key.c[0] = *ptr++;
-    key.c[1] = *ptr++;
-    key.c[2] = *ptr++;
-    key.c[3] = *ptr++;
+    header.dst_addr = strip_info->true_dev_addr;
+    string_to_radio_address(&header.src_addr, sendername);
 
-    if      (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end);
-    else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end);
+#ifdef EXT_COUNTERS
+    if      (key.l == SIP0Key.l) {
+      strip_info->rx_rbytes += (end - ptr);
+      process_IP_packet(strip_info, &header, ptr, end);
+    } else if (key.l == ARP0Key.l) {
+      strip_info->rx_rbytes += (end - ptr);
+      process_ARP_packet(strip_info, &header, ptr, end);
+    } else if (key.l == ATR_Key.l) {
+      strip_info->rx_ebytes += (end - ptr);
+      process_AT_response(strip_info, ptr, end);
+    } else if (key.l == ACK_Key.l) {
+      strip_info->rx_ebytes += (end - ptr);
+      process_ACK(strip_info, ptr, end);
+    } else if (key.l == INF_Key.l) {
+      strip_info->rx_ebytes += (end - ptr);
+      process_Info(strip_info, ptr, end);
+    } else if (key.l == ERR_Key.l) {
+      strip_info->rx_ebytes += (end - ptr);
+      RecvErr_Message(strip_info, sendername, ptr, end-ptr);
+    } else RecvErr("Unrecognized protocol key", strip_info);
+#else
+    if      (key.l == SIP0Key.l) process_IP_packet  (strip_info, &header, ptr, end);
+    else if (key.l == ARP0Key.l) process_ARP_packet (strip_info, &header, ptr, end);
     else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end);
-    else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr);
-    else /* RecvErr("Unrecognized protocol key", strip_info); */
-
-    /* Note, this "else" block is temporary, until Metricom fix their */
-    /* packet corruption bug */
-    {
-        RecvErr("Unrecognized protocol key (retrying)", strip_info);
-        ptr -= 3; /* Back up and try again */
-        key.c[0] = *ptr++;
-        key.c[1] = *ptr++;
-        key.c[2] = *ptr++;
-        key.c[3] = *ptr++;
-        if      (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end);
-        else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end);
-        else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end);
-        else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr);
-        else RecvErr("Unrecognized protocol key", strip_info);
-    }
+    else if (key.l == ACK_Key.l) process_ACK        (strip_info, ptr, end);
+    else if (key.l == INF_Key.l) process_Info       (strip_info, ptr, end);
+    else if (key.l == ERR_Key.l) RecvErr_Message    (strip_info, sendername, ptr, end-ptr);
+    else                         RecvErr("Unrecognized protocol key", strip_info);
+#endif
 }
 
+#define TTYERROR(X) ((X) == TTY_BREAK   ? "Break"            : \
+                     (X) == TTY_FRAME   ? "Framing Error"    : \
+                     (X) == TTY_PARITY  ? "Parity Error"     : \
+                     (X) == TTY_OVERRUN ? "Hardware Overrun" : "Unknown Error")
+
 /*
  * Handle the 'receiver data ready' interrupt.
  * This function is called by the 'tty_io' module in the kernel when
@@ -2237,17 +2318,23 @@
     {
     struct timeval tv;
     do_gettimeofday(&tv);
-    printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %d.%06d\n",
+    printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %02d.%06d\n",
         count, tv.tv_sec % 100, tv.tv_usec);
     }
 #endif
 
+#ifdef EXT_COUNTERS
+    strip_info->rx_sbytes += count;
+#endif
+
     /* Read the characters out of the buffer */
     while (cp < end)
     {
+        if (fp && *fp) printk(KERN_INFO "%s: %s on serial port\n", strip_info->dev.name, TTYERROR(*fp));
         if (fp && *fp++ && !strip_info->discard) /* If there's a serial error, record it */
         {
-            strip_info->discard = 1;
+            /* If we have some characters in the buffer, discard them */
+            strip_info->discard = strip_info->sx_count;
             strip_info->rx_errors++;
         }
 
@@ -2257,21 +2344,23 @@
             if (*cp == 0x0D)                /* If end of packet, decide what to do with it */
             {
                 if (strip_info->sx_count > 3000)
-                    printk(KERN_INFO "Cut a %d byte packet (%d bytes remaining)%s\n",
-                        strip_info->sx_count, end-cp-1,
+                    printk(KERN_INFO "%s: Cut a %d byte packet (%d bytes remaining)%s\n",
+                        strip_info->dev.name, strip_info->sx_count, end-cp-1,
                         strip_info->discard ? " (discarded)" : "");
                 if (strip_info->sx_count > strip_info->sx_size)
                 {
-                    strip_info->discard = 1;
                     strip_info->rx_over_errors++;
                     printk(KERN_INFO "%s: sx_buff overflow (%d bytes total)\n",
                            strip_info->dev.name, strip_info->sx_count);
                 }
-                if (!strip_info->discard) process_packet(strip_info);
+                else if (strip_info->discard)
+                    printk(KERN_INFO "%s: Discarding bad packet (%d/%d)\n",
+                        strip_info->dev.name, strip_info->discard, strip_info->sx_count);
+                else process_message(strip_info);
                 strip_info->discard = 0;
                 strip_info->sx_count = 0;
             }
-            else if (!strip_info->discard) /* If we're not discarding, store the character */
+            else
             {
                 /* Make sure we have space in the buffer */
                 if (strip_info->sx_count < strip_info->sx_size)
@@ -2287,9 +2376,28 @@
 /************************************************************************/
 /* General control routines						*/
 
-static int strip_set_dev_mac_address(struct device *dev, void *addr)
+static int set_mac_address(struct strip *strip_info, MetricomAddress *addr)
+{
+    /*
+     * We're using a manually specified address if the address is set
+     * to anything other than all ones. Setting the address to all ones
+     * disables manual mode and goes back to automatic address determination
+     * (tracking the true address that the radio has).
+     */
+    strip_info->manual_dev_addr = memcmp(addr->c, broadcast_address.c, sizeof(broadcast_address));
+    if (strip_info->manual_dev_addr)
+         *(MetricomAddress*)strip_info->dev.dev_addr = *addr;
+    else *(MetricomAddress*)strip_info->dev.dev_addr = strip_info->true_dev_addr;
+    return 0;
+}
+
+static int dev_set_mac_address(struct device *dev, void *addr)
 {
-    return -1;        /* You cannot override a Metricom radio's address */
+    struct strip *strip_info = (struct strip *)(dev->priv);
+    struct sockaddr *sa = addr;
+    printk(KERN_INFO "%s: strip_set_dev_mac_address called\n", dev->name);
+    set_mac_address(strip_info, (MetricomAddress *)sa->sa_data);
+    return 0;
 }
 
 static struct enet_statistics *strip_get_stats(struct device *dev)
@@ -2347,12 +2455,15 @@
     if (!allocate_buffers(strip_info))
         return(-ENOMEM);
 
-    strip_info->discard  = 0;
-    strip_info->working  = FALSE;
-    strip_info->structured_messages = FALSE;
     strip_info->sx_count = 0;
     strip_info->tx_left  = 0;
 
+    strip_info->discard  = 0;
+    strip_info->working  = FALSE;
+    strip_info->firmware_level = NoStructure;
+    strip_info->next_command   = CompatibilityCommand;
+    strip_info->user_baud      = get_baud(strip_info->tty);
+
     /*
      * Needed because address '0' is special
      */
@@ -2364,7 +2475,7 @@
 
     printk(KERN_INFO "%s: Initializing Radio.\n", strip_info->dev.name);
     ResetRadio(strip_info);
-    strip_info->idle_timer.expires  = jiffies + 2 * HZ;
+    strip_info->idle_timer.expires = jiffies + 1*HZ;
     add_timer(&strip_info->idle_timer);
     return(0);
 }
@@ -2459,7 +2570,7 @@
     dev->rebuild_header     = strip_rebuild_header;
     /*  dev->type_trans            unused */
     /*  dev->set_multicast_list   unused */
-    dev->set_mac_address    = strip_set_dev_mac_address;
+    dev->set_mac_address    = dev_set_mac_address;
     /*  dev->do_ioctl             unused */
     /*  dev->set_config           unused */
     dev->get_stats          = strip_get_stats;
@@ -2472,19 +2583,10 @@
 
 static void strip_free(struct strip *strip_info)
 {
-    MetricomNode *node, *free;
-
     *(strip_info->referrer) = strip_info->next;
     if (strip_info->next)
         strip_info->next->referrer = strip_info->referrer;
     strip_info->magic = 0;
-
-    for (node = strip_info->neighbor_list; node != NULL; )
-    {
-        free = node;
-        node = node->next;
-        kfree(free);
-    }
     kfree(strip_info);
 }
 
@@ -2539,13 +2641,9 @@
     strip_info->idle_timer.data     = (long)&strip_info->dev;
     strip_info->idle_timer.function = strip_IdleTask;
 
-    strip_info->neighbor_list = kmalloc(sizeof(MetricomNode), GFP_KERNEL);
-    strip_info->neighbor_list->type = 0;
-    strip_info->neighbor_list->next = NULL;
-
     /* Note: strip_info->if_name is currently 8 characters long */
-    sprintf(strip_info->if_name, "st%d", channel_id);
-    strip_info->dev.name         = strip_info->if_name;
+    sprintf(strip_info->if_name.c, "st%d", channel_id);
+    strip_info->dev.name         = strip_info->if_name.c;
     strip_info->dev.base_addr    = channel_id;
     strip_info->dev.priv         = (void*)strip_info;
     strip_info->dev.next         = NULL;
@@ -2615,6 +2713,9 @@
 #ifdef MODULE
     MOD_INC_USE_COUNT;
 #endif
+
+    printk(KERN_INFO "STRIP: device \"%s\" activated\n", strip_info->if_name.c);
+
     /*
      * Done.  We have linked the TTY line to a channel.
      */
@@ -2644,6 +2745,7 @@
 
     tty->disc_data = 0;
     strip_info->tty = NULL;
+    printk(KERN_INFO "STRIP: device \"%s\" closed down\n", strip_info->if_name.c);
     strip_free(strip_info);
     tty->disc_data = NULL;
 #ifdef MODULE
@@ -2679,7 +2781,14 @@
             return 0;
 
         case SIOCSIFHWADDR:
-            return -EINVAL;
+            {
+            MetricomAddress addr;
+            printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev.name);
+            err = verify_area(VERIFY_READ, (void*)arg, sizeof(MetricomAddress));
+            if (err) return -err;
+            memcpy_fromfs(&addr, (void*)arg, sizeof(MetricomAddress));
+            return(set_mac_address(strip_info, &addr));
+            }
 
         /*
          * Allow stty to read, but not set, the serial port
@@ -2700,32 +2809,6 @@
 /* Initialization							*/
 
 /*
- *      Registers with the /proc file system to create different /proc/net files.
- */
-
-static int strip_proc_net_register(unsigned short type, char *file_name,
-                                   int (*get_info)(char *, char **, off_t, int, int))
-{
-    struct proc_dir_entry *strip_entry;
-
-    strip_entry = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC);
-
-    memset(strip_entry, 0, sizeof(struct proc_dir_entry));
-    strip_entry->low_ino = type;
-    strip_entry->namelen = strlen(file_name);
-    strip_entry->name = file_name;
-    strip_entry->mode = S_IFREG | S_IRUGO;
-    strip_entry->nlink = 1;
-    strip_entry->uid = 0;
-    strip_entry->gid = 0;
-    strip_entry->size = 0;
-    strip_entry->ops = &proc_net_inode_operations;
-    strip_entry->get_info = get_info;
-
-    return proc_net_register(strip_entry);
-}
-
-/*
  * Initialize the STRIP driver.
  * This routine is called at boot time, to bootstrap the multi-channel
  * STRIP driver
@@ -2739,7 +2822,7 @@
     static struct tty_ldisc strip_ldisc;
     int status;
 
-    printk("STRIP: version %s (unlimited channels)\n", StripVersion);
+    printk(KERN_INFO "STRIP: Version %s (unlimited channels)\n", StripVersion);
 
     /*
      * Fill in our line protocol discipline, and register it
@@ -2764,24 +2847,12 @@
     }
 
     /*
-     * Register the status and trace files with /proc
+     * Register the status file with /proc
      */
-
-#if DO_PROC_NET_STRIP_STATUS
-    if (strip_proc_net_register(PROC_NET_STRIP_STATUS, "strip_status",
-                                               &strip_get_status_info) != 0)
+    if (proc_net_register(&proc_strip_get_status_info) != 0)
     {
-        printk(KERN_ERR "strip: status strip_proc_net_register() failed.\n");
+        printk(KERN_ERR "strip: status proc_net_register() failed.\n");
     }
-#endif
-
-#if DO_PROC_NET_STRIP_TRACE
-    if (strip_proc_net_register(PROC_NET_STRIP_TRACE, "strip_trace",
-                    &strip_get_trace_info) != 0)
-    {
-        printk(KERN_ERR "strip: trace strip_proc_net_register() failed.\n");
-    }
-#endif
 
 #ifdef MODULE
      return status;
@@ -2811,16 +2882,12 @@
     while (struct_strip_list)
         strip_free(struct_strip_list);
 
-    /* Unregister with the /proc/net files here. */
-
-#if DO_PROC_NET_STRIP_TRACE
-    proc_net_unregister(PROC_NET_STRIP_TRACE);
-#endif
-#if DO_PROC_NET_STRIP_STATUS
+    /* Unregister with the /proc/net file here. */
     proc_net_unregister(PROC_NET_STRIP_STATUS);
-#endif
 
     if ((i = tty_register_ldisc(N_STRIP, NULL)))
         printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i);
+
+    printk(KERN_INFO "STRIP: Module Unloaded\n");
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.0.34/linux/drivers/net/tlan.c linux/drivers/net/tlan.c
--- v2.0.34/linux/drivers/net/tlan.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/tlan.c	Mon Jul 13 13:47:32 1998
@@ -3,20 +3,22 @@
  *  Linux ThunderLAN Driver
  *
  *  tlan.c
- *  by James Banks, james.banks@caldera.com
+ *  by James Banks
  *
- *  (C) 1997 Caldera, Inc.
+ *  (C) 1997-1998 Caldera, Inc.
  *
  *  This software may be used and distributed according to the terms
  *  of the GNU Public License, incorporated herein by reference.
  *
- ** This file is best viewed/edited with tabstop=4 and colums>=132.
+ ** This file is best viewed/edited with columns>=132.
  *
  ** Useful (if not required) reading:
  *
  *		Texas Instruments, ThunderLAN Programmer's Guide,
  *			TI Literature Number SPWU013A
  *			available in PDF format from www.ti.com
+ *		Level One, LXT901 and LXT970 Data Sheets
+ *			available in PDF format from www.level1.com
  *		National Semiconductor, DP83840A Data Sheet
  *			available in PDF format from www.national.com
  *		Microchip Technology, 24C01A/02A/04A Data Sheet
@@ -50,48 +52,100 @@
 
 static  int		debug = 0;
 static	int		aui = 0;
+static	int		sa_int = 0;
+static	int		bbuf = 0;
+static	int		duplex = 0;
+static	int		speed = 0;
 static	u8		*TLanPadBuffer;
 static	char		TLanSignature[] = "TLAN";
 static	int		TLanVersionMajor = 0;
-static	int		TLanVersionMinor = 38;
+static	int		TLanVersionMinor = 43;
 
 
-static	TLanPciId TLanDeviceList[] = {
+static	TLanAdapterEntry TLanAdapterList[] = {
 	{ PCI_VENDOR_ID_COMPAQ, 
 	  PCI_DEVICE_ID_NETELLIGENT_10,
-	  "Compaq Netelligent 10"
+	  "Compaq Netelligent 10 T PCI UTP",
+	  TLAN_ADAPTER_ACTIVITY_LED,
+	  0x83
 	},
 	{ PCI_VENDOR_ID_COMPAQ,
 	  PCI_DEVICE_ID_NETELLIGENT_10_100,
-	  "Compaq Netelligent 10/100"
+	  "Compaq Netelligent 10/100 TX PCI UTP",
+	  TLAN_ADAPTER_ACTIVITY_LED,
+	  0x83
 	}, 
 	{ PCI_VENDOR_ID_COMPAQ,
 	  PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED,
-	  "Compaq Integrated NetFlex-3/P"
+	  "Compaq Integrated NetFlex-3/P",
+	  TLAN_ADAPTER_NONE,
+	  0x83
 	}, 
 	{ PCI_VENDOR_ID_COMPAQ,
 	  PCI_DEVICE_ID_NETFLEX_3P,
-	  "Compaq NetFlex-3/P"
+	  "Compaq NetFlex-3/P",
+	  TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY,
+	  0x83
 	},
 	{ PCI_VENDOR_ID_COMPAQ,
 	  PCI_DEVICE_ID_NETFLEX_3P_BNC,
-	  "Compaq NetFlex-3/P"
+	  "Compaq NetFlex-3/P",
+	  TLAN_ADAPTER_NONE,
+	  0x83
 	},
 	{ PCI_VENDOR_ID_COMPAQ,
 	  PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT,
-	  "Compaq ProLiant Netelligent 10/100"
+	  "Compaq Netelligent Integrated 10/100 TX UTP",
+	  TLAN_ADAPTER_NONE,
+	  0x83
 	},
 	{ PCI_VENDOR_ID_COMPAQ,
 	  PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL,
-	  "Compaq Dual Port Netelligent 10/100"
+	  "Compaq Netelligent Dual 10/100 TX PCI UTP",
+	  TLAN_ADAPTER_NONE,
+	  0x83
 	},
 	{ PCI_VENDOR_ID_COMPAQ,
 	  PCI_DEVICE_ID_DESKPRO_4000_5233MMX,
-	  "Compaq Deskpro 4000 5233MMX"
+	  "Compaq Netelligent 10/100 TX Embedded UTP",
+	  TLAN_ADAPTER_NONE,
+	  0x83
+	},
+	{ PCI_VENDOR_ID_OLICOM,
+	  PCI_DEVICE_ID_OLICOM_OC2183,
+	  "Olicom OC-2183/2185",
+	  TLAN_ADAPTER_USE_INTERN_10,
+	  0xF8
+	},
+	{ PCI_VENDOR_ID_OLICOM,
+	  PCI_DEVICE_ID_OLICOM_OC2325,
+	  "Olicom OC-2325",
+	  TLAN_ADAPTER_UNMANAGED_PHY,
+	  0xF8
+	},
+	{ PCI_VENDOR_ID_OLICOM,
+	  PCI_DEVICE_ID_OLICOM_OC2326,
+	  "Olicom OC-2326",
+	  TLAN_ADAPTER_USE_INTERN_10,
+	  0xF8
+	},
+	{ PCI_VENDOR_ID_COMPAQ,
+	  PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100,
+	  "Compaq Netelligent 10/100 TX UTP",
+	  TLAN_ADAPTER_ACTIVITY_LED,
+	  0x83
+	},
+	{ PCI_VENDOR_ID_COMPAQ,
+	  PCI_DEVICE_ID_NETELLIGENT_10_T2,
+	  "Compaq Netelligent 10 T/2 PCI UTP/Coax",
+	  TLAN_ADAPTER_NONE,
+	  0x83
 	},
 	{ 0,
 	  0,
-	  NULL
+	  NULL,
+	  0,
+	  0
 	} /* End of List */
 };
 
@@ -117,28 +171,37 @@
 static void	TLan_Timer( unsigned long );
 
 static void	TLan_ResetLists( struct device * );
+static void	TLan_FreeLists( struct device * );
 static void	TLan_PrintDio( u16 );
 static void	TLan_PrintList( TLanList *, char *, int );
 static void	TLan_ReadAndClearStats( struct device *, int );
-static int	TLan_Reset( struct device * );
+static void	TLan_ResetAdapter( struct device * );
+static void	TLan_FinishReset( struct device * );
 static void	TLan_SetMac( struct device *, int areg, char *mac );
 
-static int	TLan_PhyNop( struct device * );
 static void	TLan_PhyPrint( struct device * );
-static void	TLan_PhySelect( struct device * );
+static void	TLan_PhyDetect( struct device * );
+static void	TLan_PhyPowerDown( struct device * );
+static void	TLan_PhyPowerUp( struct device * );
+static void	TLan_PhyReset( struct device * );
+static void	TLan_PhyStartLink( struct device * );
+static void	TLan_PhyFinishAutoNeg( struct device * );
+/*
+static int	TLan_PhyNop( struct device * );
 static int	TLan_PhyInternalCheck( struct device * );
 static int	TLan_PhyInternalService( struct device * );
 static int	TLan_PhyDp83840aCheck( struct device * );
+*/
 
-static int	TLan_MiiReadReg(u16, u16, u16, u16 *);
+static int	TLan_MiiReadReg( struct device *, u16, u16, u16 * );
 static void	TLan_MiiSendData( u16, u32, unsigned );
-static void	TLan_MiiSync(u16);
-static void	TLan_MiiWriteReg(u16, u16, u16, u16);
+static void	TLan_MiiSync( u16 );
+static void	TLan_MiiWriteReg( struct device *, u16, u16, u16 );
 
 static void	TLan_EeSendStart( u16 );
 static int	TLan_EeSendByte( u16, u8, int );
 static void	TLan_EeReceiveByte( u16, u8 *, int );
-static int	TLan_EeReadByte( u16, u8, u8 * );
+static int	TLan_EeReadByte( struct device *, u8, u8 * );
 
 
 static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
@@ -152,6 +215,25 @@
 	TLan_HandleRxEOC
 };
 
+static inline void
+TLan_SetTimer( struct device *dev, u32 ticks, u32 type )
+{
+	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+	cli();
+	if ( priv->timer.function != NULL ) {
+		return;
+	}
+	priv->timer.function = &TLan_Timer;
+	sti();
+
+	priv->timer.data = (unsigned long) dev;
+	priv->timer.expires = jiffies + ticks;
+	priv->timerSetAt = jiffies;
+	priv->timerType = type;
+	add_timer( &priv->timer );
+
+} /* TLan_SetTimer */
 
 
 /*****************************************************************************
@@ -177,7 +259,7 @@
 	 *
 	 *	This function begins the setup of the driver creating a
 	 *	pad buffer, finding all TLAN devices (matching
-	 *	TLanDeviceList entries), and creating and initializing a
+	 *	TLanAdapterList entries), and creating and initializing a
 	 *	device structure for each adapter.
 	 *
 	 **************************************************************/
@@ -189,14 +271,14 @@
 	struct device	*dev;
 	size_t		dev_size;
 	u8		dfn;
-	u32		dl_ix;
+	u32		index;
 	int		failed;
 	int		found;
 	u32		io_base;
 	u8		irq;
 	u8		rev;
 
-	printk( "TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n",
+	printk( "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n",
 		TLanVersionMajor,
 		TLanVersionMinor
 	      );
@@ -212,7 +294,7 @@
 
 	dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
 
-	while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) {
+	while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ) ) ) {
 		dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
 		if ( dev == NULL ) {
 			printk( "TLAN:  Could not allocate memory for device.\n" );
@@ -227,27 +309,41 @@
 		dev->irq = irq;
 		dev->init = TLan_Init;
 
-		priv->pciBus = bus;
-		priv->pciDeviceFn = dfn;
-		priv->pciRevision = rev;
-		priv->pciEntry = dl_ix;
+		priv->adapter =    &TLanAdapterList[index];
+		priv->adapterRev = rev;
+		priv->aui =        aui;
+		if ( ( duplex != 1 ) && ( duplex != 2 ) ) {
+			duplex = 0;
+		}
+		priv->duplex =     duplex;
+		if ( ( speed != 10 ) && ( speed != 100 ) ) {
+			speed = 0;
+		}
+		priv->speed =      speed;
+		priv->sa_int =     sa_int;
+		priv->debug =      debug;
 
 		ether_setup( dev );
 
 		failed = register_netdev( dev );
 
 		if ( failed ) {
-			printk( "TLAN:  Could not register network device.  Freeing struct.\n" );
+			printk( "TLAN:  Could not register device.\n" );
 			kfree( dev );
 		} else {
 			priv->nextDevice = TLanDevices;
 			TLanDevices = dev;
 			TLanDevicesInstalled++;
-			printk("TLAN:  %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName );
+			printk("TLAN:  %s irq=%2d io=%04x, %s, Rev. %d\n",
+				dev->name,
+				(int) dev->irq,
+				(int) dev->base_addr,
+				priv->adapter->deviceLabel,
+				priv->adapterRev );
 		}
 	}
 	
-	// printk( "TLAN:  Found %d device(s).\n", TLanDevicesInstalled );
+	/* printk( "TLAN:  Found %d device(s).\n", TLanDevicesInstalled ); */
 
     return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
 
@@ -276,11 +372,12 @@
 	struct device	*dev;
 	TLanPrivateInfo	*priv;
 
-	while (TLanDevicesInstalled) {
+	while ( TLanDevicesInstalled ) {
 		dev = TLanDevices;
 		priv = (TLanPrivateInfo *) dev->priv;
-		if ( priv->dmaStorage )
+		if ( priv->dmaStorage ) {
 			kfree( priv->dmaStorage );
+		}
 		release_region( dev->base_addr, 0x10 );
 		unregister_netdev( dev );
 		TLanDevices = priv->nextDevice;
@@ -315,53 +412,79 @@
 	 
 extern int tlan_probe( struct device *dev )
 {
+	TLanPrivateInfo	*priv;
 	static int	pad_allocated = 0;
 	int		found;
-	TLanPrivateInfo	*priv;
 	u8		bus, dfn, irq, rev;
-	u32		io_base, dl_ix;
+	u32		io_base, index;
 
-	found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
-	if ( found ) {
-		dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
-		if ( dev->priv == NULL ) {
-			printk( "TLAN:  Could not allocate memory for device.\n" );
+	found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index );
+
+	if ( ! found ) {
+		return -ENODEV;
+	}
+
+	dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+	
+	if ( dev->priv == NULL ) {
+		printk( "TLAN:  Could not allocate memory for device.\n" );
+		return  -ENOMEM;
+	}
+
+	memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+
+	if ( ! pad_allocated ) {
+		TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, 
+//						( GFP_KERNEL | GFP_DMA )
+						( GFP_KERNEL )
+					      );
+		if ( TLanPadBuffer == NULL ) {
+			printk( "TLAN:  Could not allocate memory for padding.\n" );
+			kfree( dev->priv );
+			return -ENOMEM;
+		} else {
+			pad_allocated = 1;
+			memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
 		}
-		memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
-		priv = (TLanPrivateInfo *) dev->priv;
+	}
 
-		dev->name = priv->devName;
-		strcpy( priv->devName, "    " );
+	priv = (TLanPrivateInfo *) dev->priv;
 
-		dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+	dev->name = priv->devName;
+	strcpy( priv->devName, "    " );
 
-		dev->base_addr = io_base;
-		dev->irq = irq;
+	dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
 
-		priv->pciBus = bus;
-		priv->pciDeviceFn = dfn;
-		priv->pciRevision = rev;
-		priv->pciEntry = dl_ix;
-
-		if ( ! pad_allocated ) {
-			TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
-			if ( TLanPadBuffer == NULL ) {
-				printk( "TLAN:  Could not allocate memory for pad buffer.\n" );
-			} else {
-				pad_allocated = 1;
-				memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
-			}
-		}
-		printk("TLAN %d.%d:  %s irq=%2d io=%04x, %s\n",TLanVersionMajor,
-			TLanVersionMinor,
-			dev->name, 
-			(int) irq, 
-			io_base, 
-			TLanDeviceList[dl_ix].deviceName );
-		TLan_Init( dev );
+	dev->base_addr = io_base;
+	dev->irq = irq;
+
+
+	priv->adapter =    &TLanAdapterList[index];
+	priv->adapterRev = rev;
+	priv->aui =        dev->mem_start & 0x01;
+	priv->duplex =     ( ( dev->mem_start & 0x0C ) == 0x0C ) ? 0 : ( dev->mem_start & 0x0C ) >> 2;
+	priv->speed =      ( ( dev->mem_start & 0x30 ) == 0x30 ) ? 0 : ( dev->mem_start & 0x30 ) >> 4;
+	if ( priv->speed == 0x1 ) {
+		priv->speed = TLAN_SPEED_10;
+	} else if ( priv->speed == 0x2 ) {
+		priv->speed = TLAN_SPEED_100;
 	}
+	priv->sa_int =     dev->mem_start & 0x02;
+	priv->debug =      dev->mem_end;
+
+
+	printk("TLAN %d.%d:  %s irq=%2d io=%04x, %s, Rev. %d\n",
+		TLanVersionMajor,
+		TLanVersionMinor,
+		dev->name, 
+		(int) irq, 
+		io_base,
+		priv->adapter->deviceLabel,
+		priv->adapterRev );
+
+	TLan_Init( dev );
 			
-   	return ( ( found ) ? 0 : -ENODEV );
+   	return 0;
 
 } /* tlan_probe */
 
@@ -389,7 +512,7 @@
 	 *				of the adapter.
 	 *
 	 *	This function searches for an adapter with PCI vendor
-	 *	and device IDs matching those in the TLanDeviceList.
+	 *	and device IDs matching those in the TLanAdapterList.
 	 *	The function 'remembers' the last device it found,
 	 *	and so finds a new device (if anymore are to be found)
 	 *	each time the function is called.  It then looks up
@@ -413,11 +536,11 @@
 		return 0;
 	}
 
-	for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) {
+	for (; TLanAdapterList[dl_index].vendorId != 0; dl_index++) {
 
 		not_found = pcibios_find_device(
-			TLanDeviceList[dl_index].vendorId,
-			TLanDeviceList[dl_index].deviceId,
+			TLanAdapterList[dl_index].vendorId,
+			TLanAdapterList[dl_index].deviceId,
 			pci_index,
 			pci_bus,
 			pci_dfn
@@ -428,8 +551,8 @@
 			TLAN_DBG(
 				TLAN_DEBUG_GNRL,
 				"TLAN:  found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
-				TLanDeviceList[dl_index].vendorId,
-				TLanDeviceList[dl_index].deviceId
+				TLanAdapterList[dl_index].vendorId,
+				TLanAdapterList[dl_index].deviceId
 			);
 
 			pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_REVISION_ID, pci_rev);
@@ -457,8 +580,12 @@
 			if ( *pci_io_base == 0 )
 				printk("TLAN:    IO mapping not available, ignoring device.\n");
 
-			if (pci_command & PCI_COMMAND_MASTER) {
-				TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:    Bus mastering is active.\n");
+			if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) {
+				pcibios_write_config_word ( *pci_bus,  *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER );
+				printk( "TLAN:  Attempting to activate busmastering.\n" );
+				printk( "TLAN:    You may need to set busmastering to on in the CMOS\n" );
+				printk( "TLAN:    before this card will work.\n" );
+				*pci_io_base = 0;
 			}
 
 			pci_index++;
@@ -499,9 +626,9 @@
 
 int TLan_Init( struct device *dev )
 {
-	int				dma_size;
-	int				err;
-	int				i;
+	int		dma_size;
+	int		err;
+	int		i;
 	TLanPrivateInfo	*priv;
 
 	priv = (TLanPrivateInfo *) dev->priv;
@@ -516,8 +643,14 @@
 	}
 	request_region( dev->base_addr, 0x10, TLanSignature );
 
-	dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+	if ( bbuf ) {
+		dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
 	           * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+	} else {
+		dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+	           * ( sizeof(TLanList) );
+	}
+
 	priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
 	if ( priv->dmaStorage == NULL ) {
 		printk( "TLAN:  Could not allocate lists and buffers for %s.\n",
@@ -528,19 +661,24 @@
 	priv->rxList = (TLanList *) 
 		       ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
 	priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
-	priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
-	priv->txBuffer = priv->rxBuffer
-			 + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+
+	if ( bbuf ) {
+		priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+		priv->txBuffer = priv->rxBuffer
+				 + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+	}
 
 	err = 0;
 	for ( i = 0;  i < 6 ; i++ )
-		err |= TLan_EeReadByte( dev->base_addr,
-					(u8) 0x83 + i,
+		err |= TLan_EeReadByte( dev,
+					(u8) priv->adapter->addrOfs + i,
 					(u8 *) &dev->dev_addr[i] );
-	if ( err )
+	if ( err ) {
 		printk( "TLAN:  %s: Error reading MAC from eeprom: %d\n",
 			dev->name,
 			err );
+	}
+
 	dev->addr_len = 6;
 
 	dev->open = &TLan_Open;
@@ -549,12 +687,6 @@
 	dev->get_stats = &TLan_GetStats;
 	dev->set_multicast_list = &TLan_SetMulticastList;
 
-#ifndef MODULE
-
-	aui = dev->mem_start & 0x01;
-	debug = dev->mem_end;
-
-#endif /* MODULE */
 
 	return 0;
 
@@ -582,16 +714,21 @@
 
 int TLan_Open( struct device *dev )
 {
-	int				err;
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
+	int		err;
 
 	priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
-	err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+	if ( priv->sa_int ) {
+		TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:   Using SA_INTERRUPT\n" ); 
+		err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev );
+	} else {
+		err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+	}
 	if ( err ) {
 		printk( "TLAN:  Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
 		return -EAGAIN;
 	}
-
+	
 	MOD_INC_USE_COUNT;
 
 	dev->tbusy = 0;
@@ -603,27 +740,9 @@
 	*/
 	TLan_ResetLists( dev );
 	TLan_ReadAndClearStats( dev, TLAN_IGNORE );
-	TLan_Reset( dev );
-	TLan_Reset( dev );
-	TLan_SetMac( dev, 0, dev->dev_addr );
-	outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
-	if ( debug >= 1 )
-		outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
-
-	init_timer( &priv->timer );
-	priv->timer.data = (unsigned long) dev;
-	priv->timer.function = &TLan_Timer;
-	if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
-		priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
-		priv->timerSetAt = jiffies;
-		priv->timerType = TLAN_TIMER_LINK;
-		add_timer( &priv->timer );
-	} else {
-		outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
-		outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
-	}
+	TLan_ResetAdapter( dev );
 
-	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Device %s opened.  Revision = %x\n", dev->name, priv->tlanRev );
+	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Opened.  TLAN Chip Rev: %x\n", dev->name, priv->tlanRev );
 
 	return 0;
 
@@ -656,9 +775,9 @@
 int TLan_StartTx( struct sk_buff *skb, struct device *dev )
 {
 	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-	TLanList		*tail_list;
-	u8				*tail_buffer;
-	int				pad;
+	TLanList	*tail_list;
+	u8		*tail_buffer;
+	int		pad;
 
 	if ( ! priv->phyOnline ) {
 		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s PHY is not ready\n", dev->name );
@@ -667,16 +786,26 @@
 	}
 
 	tail_list = priv->txList + priv->txTail;
+
 	if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
 		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
 		dev->tbusy = 1;
 		priv->txBusyCount++;
 		return 1;
 	}
+
 	tail_list->forward = 0;
-	tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
-	memcpy( tail_buffer, skb->data, skb->len );
+
+	if ( bbuf ) {
+		tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+		memcpy( tail_buffer, skb->data, skb->len );
+	} else {
+		tail_list->buffer[0].address = virt_to_bus( skb->data );
+		tail_list->buffer[9].address = (u32) skb;
+	}
+
 	pad = TLAN_MIN_FRAME_SIZE - skb->len;
+
 	if ( pad > 0 ) {
 		tail_list->frameSize = (u16) skb->len + pad;
 		tail_list->buffer[0].count = (u32) skb->len;
@@ -688,7 +817,7 @@
 		tail_list->buffer[1].count = 0;
 		tail_list->buffer[1].address = 0;
 	}
-	// are we transferring?
+
 	cli();
 	tail_list->cStat = TLAN_CSTAT_READY;
 	if ( ! priv->txInProgress ) {
@@ -699,17 +828,19 @@
 		outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
 	} else {
 		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Adding buffer %d to TX channel\n", priv->txTail );
-		if ( priv->txTail == 0 )
+		if ( priv->txTail == 0 ) {
 			( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
-		else
+		} else {
 			( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
+		}
 	}
 	sti();
-	priv->txTail++;
-	if ( priv->txTail >= TLAN_NUM_TX_LISTS )
-		priv->txTail = 0;
 
-	dev_kfree_skb( skb, FREE_WRITE );
+	CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS );
+
+	if ( bbuf ) {
+		dev_kfree_skb( skb, FREE_WRITE );
+	}
 		
 	dev->trans_start = jiffies;
 	return 0;
@@ -742,35 +873,34 @@
 
 void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	u32				ack;
+	u32		ack;
 	struct device	*dev;
-	u32				host_cmd;
-	u16				host_int;
-	int				type;
+	u32		host_cmd;
+	u16		host_int;
+	int		type;
 
 	dev = (struct device *) dev_id;
 
-	if ( dev->interrupt )
+	cli();
+	if ( dev->interrupt ) {
 		printk( "TLAN:   Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
+	}
 	dev->interrupt++;
 
-	cli();
-
 	host_int = inw( dev->base_addr + TLAN_HOST_INT );
-	outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints
+	outw( host_int, dev->base_addr + TLAN_HOST_INT );
 
 	type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
 
 	ack = TLanIntVector[type]( dev, host_int );
 
-	sti();
-
 	if ( ack ) {
 		host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
 		outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
 	}
 
 	dev->interrupt--;
+	sti();
 
 } /* TLan_HandleInterrupts */
 
@@ -801,10 +931,11 @@
 
 	TLan_ReadAndClearStats( dev, TLAN_RECORD );
 	outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
-	if ( priv->timerSetAt != 0 )
+	if ( priv->timer.function != NULL )
 		del_timer( &priv->timer );
 	free_irq( dev->irq, dev );
-	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:   Device %s closed.\n", dev->name );
+	TLan_FreeLists( dev );
+	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Device %s closed.\n", dev->name );
 
 	MOD_DEC_USE_COUNT;
 
@@ -881,11 +1012,11 @@
 void TLan_SetMulticastList( struct device *dev )
 {	
 	struct dev_mc_list	*dmi = dev->mc_list;
-	u32					hash1 = 0;
-	u32					hash2 = 0;
-	int					i;
-	u32					offset;
-	u8					tmp;
+	u32			hash1 = 0;
+	u32			hash2 = 0;
+	int			i;
+	u32			offset;
+	u8			tmp;
 
 	if ( dev->flags & IFF_PROMISC ) {
 		tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
@@ -955,7 +1086,7 @@
 u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
 {
 	host_int = 0;
-	// printk( "TLAN:  Invalid interrupt on %s.\n", dev->name );
+	/* printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); */
 	return 0;
 
 } /* TLan_HandleInvalid */
@@ -988,19 +1119,24 @@
 u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
-	int				eoc = 0;
-	TLanList		*head_list;
-	u32				ack = 1;
+	int		eoc = 0;
+	TLanList	*head_list;
+	u32		ack = 1;
 
 	TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
 	host_int = 0;
 	head_list = priv->txList + priv->txHead;
+
+	if ( ! bbuf ) {
+		dev_kfree_skb( (struct sk_buff *) head_list->buffer[9].address, FREE_WRITE );
+		head_list->buffer[9].address = 0;
+	}
+
 	if ( head_list->cStat & TLAN_CSTAT_EOC )
 		eoc = 1;
 	if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
 		printk( "TLAN:  Received interrupt for uncompleted TX frame.\n" );
 	}
-	// printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
 
 #if LINUX_KERNEL_VERSION > 0x20100
 	priv->stats->tx_bytes += head_list->frameSize;
@@ -1008,9 +1144,7 @@
 
 	head_list->cStat = TLAN_CSTAT_UNUSED;
 	dev->tbusy = 0;
-	priv->txHead++;
-	if ( priv->txHead >= TLAN_NUM_TX_LISTS )
-		priv->txHead = 0;
+	CIRC_INC( priv->txHead, TLAN_NUM_TX_LISTS );
 	if ( eoc ) {
 		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
 		head_list = priv->txList + priv->txHead;
@@ -1021,17 +1155,13 @@
 			priv->txInProgress = 0;
 		}
 	}
-	TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
-	if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
-		if ( priv->timerSetAt == 0 ) {
-			// printk("TxEOF Starting timer...\n");
-			priv->timerSetAt = jiffies;
-			priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
-			priv->timerType = TLAN_TIMER_ACT;
-			add_timer( &priv->timer );
-		} else if ( priv->timerType == TLAN_TIMER_ACT ) {
+
+	if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+		TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+		if ( priv->timer.function == NULL ) {
+			TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+		} else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
 			priv->timerSetAt = jiffies;
-			// printk("TxEOF continuing timer...\n");
 		}
 	}
 
@@ -1092,7 +1222,7 @@
 	 *	of the list.  If the frame was the last in the Rx
 	 *	channel (EOC), the function restarts the receive channel
 	 *	by sending an Rx Go command to the adapter.  Then it
-	 *	activates/continues the activity LED.
+	 *	activates/continues the the activity LED.
 	 *
 	 **************************************************************/
 
@@ -1111,11 +1241,14 @@
 	host_int = 0;
 	head_list = priv->rxList + priv->rxHead;
 	tail_list = priv->rxList + priv->rxTail;
-	if ( head_list->cStat & TLAN_CSTAT_EOC )
+
+	if ( head_list->cStat & TLAN_CSTAT_EOC ) {
 		eoc = 1;
+	}
+
 	if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
 		printk( "TLAN:  Received interrupt for uncompleted RX frame.\n" );
-	} else {
+	} else if ( bbuf ) {
 		skb = dev_alloc_skb( head_list->frameSize + 7 );
 		if ( skb == NULL ) { 
 			printk( "TLAN:  Couldn't allocate memory for received data.\n" );
@@ -1124,7 +1257,6 @@
 			skb->dev = dev;
 			skb_reserve( skb, 2 );
 			t = (void *) skb_put( skb, head_list->frameSize );
-			// printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
 
 #if LINUX_KERNEL_VERSION > 0x20100
 			priv->stats->rx_bytes += head_list->frameSize;
@@ -1134,17 +1266,39 @@
 			skb->protocol = eth_type_trans( skb, dev );
 			netif_rx( skb );
 		}
+	} else {
+		skb = (struct sk_buff *) head_list->buffer[9].address;
+		head_list->buffer[9].address = 0;
+		skb_trim( skb, head_list->frameSize );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+			priv->stats->rx_bytes += head_list->frameSize;
+#endif
+
+		skb->protocol = eth_type_trans( skb, dev );
+		netif_rx( skb );
+
+		skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+		if ( skb == NULL ) {
+			printk( "TLAN:  Couldn't allocate memory for received data.\n" );
+			/* If this ever happened it would be a problem */
+		} else {
+			skb->dev = dev;
+			skb_reserve( skb, 2 );
+			t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+			head_list->buffer[0].address = virt_to_bus( t );
+			head_list->buffer[9].address = (u32) skb;
+		}
 	}
+
 	head_list->forward = 0;
 	head_list->frameSize = TLAN_MAX_FRAME_SIZE;
 	head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
 	tail_list->forward = virt_to_bus( head_list );
-	priv->rxHead++;
-	if ( priv->rxHead >= TLAN_NUM_RX_LISTS )
-		priv->rxHead = 0;
-	priv->rxTail++;
-	if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
-		priv->rxTail = 0;
+
+	CIRC_INC( priv->rxHead, TLAN_NUM_RX_LISTS );
+	CIRC_INC( priv->rxTail, TLAN_NUM_RX_LISTS );
+
 	if ( eoc ) { 
 		TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
 		head_list = priv->rxList + priv->rxHead;
@@ -1152,19 +1306,16 @@
 		ack |= TLAN_HC_GO | TLAN_HC_RT;
 		priv->rxEocCount++;
 	}
-	TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
-	if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
-		if ( priv->timerSetAt == 0 )  {
-			// printk("RxEOF Starting timer...\n");
-			priv->timerSetAt = jiffies;
-			priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
-			priv->timerType = TLAN_TIMER_ACT;
-			add_timer( &priv->timer );
-		} else if ( priv->timerType == TLAN_TIMER_ACT ) {
-			// printk("RxEOF tarting continuing timer...\n");
+
+	if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+		TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+		if ( priv->timer.function == NULL )  {
+			TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+		} else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
 			priv->timerSetAt = jiffies;
 		}
 	}
+
 	dev->last_rx = jiffies;
 
 	return ack;
@@ -1194,7 +1345,7 @@
 u32 TLan_HandleDummy( struct device *dev, u16 host_int )
 {
 	host_int = 0;
-	printk( "TLAN:  Dummy interrupt on %s.\n", dev->name );
+	printk( "TLAN:  Test interrupt on %s.\n", dev->name );
 	return 1;
 
 } /* TLan_HandleDummy */
@@ -1269,45 +1420,49 @@
 
 u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
 {	
-	u32				ack;
-	u32				error;
-	u8				net_sts;
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
+	u32		ack;
+	u32		error;
+	u8		net_sts;
+	u32		phy;
+	u16		tlphy_ctl;
+	u16		tlphy_sts;
 	
 	ack = 1;
 	if ( host_int & TLAN_HI_IV_MASK ) {
 		error = inl( dev->base_addr + TLAN_CH_PARM );
-		printk( "TLAN:   Adaptor Check on device %s err = 0x%x\n", dev->name, error );
+		printk( "TLAN:  %s: Adaptor Error = 0x%x\n", dev->name, error );
 		TLan_ReadAndClearStats( dev, TLAN_RECORD );
 		outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+		TLan_FreeLists( dev );
 		TLan_ResetLists( dev );
-		TLan_Reset( dev );
+		TLan_ResetAdapter( dev );
 		dev->tbusy = 0;
-		TLan_SetMac( dev, 0, dev->dev_addr );
-		if ( priv->timerType == 0 ) {
-			if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
-				priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
-				priv->timerSetAt = jiffies;
-				priv->timerType = TLAN_TIMER_LINK;
-				add_timer( &priv->timer );
-			} else {
-				//printk( " RX GO---->\n" );
-				outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
-				outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
-			}
-		}
 		ack = 0;
 	} else {
+		TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Status Check\n", dev->name );
+		phy = priv->phy[priv->phyNum];
+
 		net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
-		if ( net_sts )
+		if ( net_sts ) {
 			TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
-		if ( net_sts & TLAN_NET_STS_MIRQ ) {
-			(*priv->phyService)( dev );
+			TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s:    Net_Sts = %x\n", dev->name, (unsigned) net_sts );
+		}
+		if ( ( net_sts & TLAN_NET_STS_MIRQ ) &&  ( priv->phyNum == 0 ) ) {
+			TLan_MiiReadReg( dev, phy, TLAN_TLPHY_STS, &tlphy_sts );
+			TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+        		if ( ! ( tlphy_sts & TLAN_TS_POLOK ) && ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+                		tlphy_ctl |= TLAN_TC_SWAPOL;
+                		TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+        		} else if ( ( tlphy_sts & TLAN_TS_POLOK ) && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+                		tlphy_ctl &= ~TLAN_TC_SWAPOL;
+                		TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+        		}
+
 			if (debug) {
 				TLan_PhyPrint( dev );
 			}
 		}
-		TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
 	}
 
 	return ack;
@@ -1340,8 +1495,8 @@
 u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
-	TLanList		*head_list;
-	u32				ack = 1;
+	TLanList	*head_list;
+	u32		ack = 1;
 
 	host_int = 0;
 	if (  priv->tlanRev < 0x30 ) {
@@ -1401,34 +1556,44 @@
 void TLan_Timer( unsigned long data )
 {
 	struct device	*dev = (struct device *) data;
-	u16		gen_sts;
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
+	u32		elapsed;
 
-	// printk( "TLAN:  %s Entered Timer, type = %d\n", dev->name, priv->timerType );
+	priv->timer.function = NULL;
 
 	switch ( priv->timerType ) {
-		case TLAN_TIMER_LINK:
-			TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts );
-			if ( gen_sts & MII_GS_LINK ) {
-				priv->phyOnline = 1;
-				outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
-				outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
-				priv->timerSetAt = 0;
-				priv->timerType = 0;
-			} else {
-				priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 );
-				add_timer( &priv->timer );
-			}
+		case TLAN_TIMER_PHY_PDOWN:
+			TLan_PhyPowerDown( dev );
 			break;
-		case TLAN_TIMER_ACT:
-			if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) {
-				TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
-				priv->timerSetAt = 0;
-				priv->timerType = 0;
-			} else {
-				priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
-				add_timer( &priv->timer );
+		case TLAN_TIMER_PHY_PUP:
+			TLan_PhyPowerUp( dev );
+			break;
+		case TLAN_TIMER_PHY_RESET:
+			TLan_PhyReset( dev );
+			break;
+		case TLAN_TIMER_PHY_START_LINK:
+			TLan_PhyStartLink( dev );
+			break;
+		case TLAN_TIMER_PHY_FINISH_AN:
+			TLan_PhyFinishAutoNeg( dev );
+			break;
+		case TLAN_TIMER_FINISH_RESET:
+			TLan_FinishReset( dev );
+			break;
+		case TLAN_TIMER_ACTIVITY:
+			cli();
+			if ( priv->timer.function == NULL ) {
+				elapsed = jiffies - priv->timerSetAt;
+				if ( elapsed >= TLAN_TIMER_ACT_DELAY ) {
+					TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+				} else  {
+					priv->timer.function = &TLan_Timer;
+					priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+					sti();
+					add_timer( &priv->timer );
+				}
 			}
+			sti();
 			break;
 		default:
 			break;
@@ -1467,13 +1632,19 @@
 	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
 	int		i;
 	TLanList	*list;
+	struct sk_buff	*skb;
+	void		*t = NULL;
 
 	priv->txHead = 0;
 	priv->txTail = 0;
 	for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
 		list = priv->txList + i;
 		list->cStat = TLAN_CSTAT_UNUSED;
-		list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+		if ( bbuf ) {
+			list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+		} else {
+			list->buffer[0].address = 0;
+		}
 		list->buffer[2].count = 0;
 		list->buffer[2].address = 0;
 	}
@@ -1485,7 +1656,21 @@
 		list->cStat = TLAN_CSTAT_READY;
 		list->frameSize = TLAN_MAX_FRAME_SIZE;
 		list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
-		list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+		if ( bbuf ) {
+			list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+		} else {
+			skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+			if ( skb == NULL ) {
+				printk( "TLAN:  Couldn't allocate memory for received data.\n" );
+				/* If this ever happened it would be a problem */
+			} else {
+				skb->dev = dev;
+				skb_reserve( skb, 2 );
+				t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+			}
+			list->buffer[0].address = virt_to_bus( t );
+			list->buffer[9].address = (u32) skb;
+		}
 		list->buffer[1].count = 0;
 		list->buffer[1].address = 0;
 		if ( i < TLAN_NUM_RX_LISTS - 1 )
@@ -1497,6 +1682,36 @@
 } /* TLan_ResetLists */
 
 
+void TLan_FreeLists( struct device *dev )
+{
+	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+	int		i;
+	TLanList	*list;
+	struct sk_buff	*skb;
+
+	if ( ! bbuf ) {
+		for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+			list = priv->txList + i;
+			skb = (struct sk_buff *) list->buffer[9].address;
+			if ( skb ) {
+				dev_kfree_skb( skb, FREE_WRITE );
+				list->buffer[9].address = 0;
+			}
+		}
+
+		for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+			list = priv->rxList + i;
+			skb = (struct sk_buff *) list->buffer[9].address;
+			if ( skb ) {
+				dev_kfree_skb( skb, FREE_READ );
+				list->buffer[9].address = 0;
+			}
+		}
+	}
+
+} /* TLan_FreeLists */
+
+
 
 
 	/***************************************************************
@@ -1508,7 +1723,7 @@
 	 *		io_base		Base IO port of the device of
 	 *				which to print DIO registers.
 	 *
-	 *	This function prints out all the internal (DIO)
+	 *	This function prints out all the the internal (DIO)
 	 *	registers of a TLAN chip.
 	 *
 	 **************************************************************/
@@ -1556,7 +1771,7 @@
 	printk( "TLAN:      Forward    = 0x%08x\n",  list->forward );
 	printk( "TLAN:      CSTAT      = 0x%04hx\n", list->cStat );
 	printk( "TLAN:      Frame Size = 0x%04hx\n", list->frameSize );
-	// for ( i = 0; i < 10; i++ ) {
+	/* for ( i = 0; i < 10; i++ ) { */
 	for ( i = 0; i < 2; i++ ) {
 		printk( "TLAN:      Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
 	}
@@ -1587,11 +1802,11 @@
 void TLan_ReadAndClearStats( struct device *dev, int record )
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
-	u32				tx_good, tx_under;
-	u32				rx_good, rx_over;
-	u32				def_tx, crc, code;
-	u32				multi_col, single_col;
-	u32				excess_col, late_col, loss;
+	u32		tx_good, tx_under;
+	u32		rx_good, rx_over;
+	u32		def_tx, crc, code;
+	u32		multi_col, single_col;
+	u32		excess_col, late_col, loss;
 
 	outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
 	tx_good  = inb( dev->base_addr + TLAN_DIO_DATA );
@@ -1659,7 +1874,8 @@
 	 *
 	 **************************************************************/
 
-int TLan_Reset( struct device *dev )
+void
+TLan_ResetAdapter( struct device *dev )
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
 	int		i;
@@ -1667,77 +1883,140 @@
 	u32		data;
 	u8		data8;
 
-//  1.	Assert reset bit.
+	priv->tlanFullDuplex = FALSE;
+/*  1.	Assert reset bit. */
 
 	data = inl(dev->base_addr + TLAN_HOST_CMD);
 	data |= TLAN_HC_AD_RST;
 	outl(data, dev->base_addr + TLAN_HOST_CMD);
+	
+	udelay(1000);
 
-//  2.	Turn off interrupts. ( Probably isn't necessary )
+/*  2.	Turn off interrupts. ( Probably isn't necessary ) */
 
 	data = inl(dev->base_addr + TLAN_HOST_CMD);
 	data |= TLAN_HC_INT_OFF;
 	outl(data, dev->base_addr + TLAN_HOST_CMD);
 
-//  3.	Clear AREGs and HASHs.
+/*  3.	Clear AREGs and HASHs. */
 
  	for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) {
 		TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
 	}
 
-//  4.	Setup NetConfig register.
+/*  4.	Setup NetConfig register. */
 
 	data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
 	TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
 
-//  5.	Load Ld_Tmr and Ld_Thr in HOST_CMD.
+/*  5.	Load Ld_Tmr and Ld_Thr in HOST_CMD. */
 
  	outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
  	outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
 
-//  6.	Unreset the MII by setting NMRST (in NetSio) to 1.
+/*  6.	Unreset the MII by setting NMRST (in NetSio) to 1. */
 
 	outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
 	addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
 	TLan_SetBit( TLAN_NET_SIO_NMRST, addr );
 
-//  7.	Setup the remaining registers.
+/*  7.	Setup the remaining registers. */
 
 	if ( priv->tlanRev >= 0x30 ) {
 		data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
 		TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 );
 	}
-	TLan_PhySelect( dev );
+	TLan_PhyDetect( dev );
 	data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
-	if ( priv->phyFlags & TLAN_PHY_BIT_RATE ) {
+	if ( priv->adapter->flags & TLAN_ADAPTER_BIT_RATE_PHY ) {
 		data |= TLAN_NET_CFG_BIT;
-		if ( aui == 1 ) {
+		if ( priv->aui == 1 ) {
 			TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a );
+		} else if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+			TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x00 );
+			priv->tlanFullDuplex = TRUE;
 		} else {
 			TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 );
 		}
 	}
-	if ( priv->phyFlags & TLAN_PHY_INTERNAL ) {
+	if ( priv->phyNum == 0 ) {
 		data |= TLAN_NET_CFG_PHY_EN;
 	}
 	TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
-	(*priv->phyCheck)( dev ); 
-	data8 = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
-	TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data8 );
-	data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; 
-	if ( priv->phyFlags & TLAN_PHY_INTS ) {
-		data8 |= TLAN_NET_MASK_MASK7; 
+
+	if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+		TLan_FinishReset( dev );
+	} else {
+		TLan_PhyPowerDown( dev );
+	}
+
+} /* TLan_ResetAdapter */
+
+
+
+
+void
+TLan_FinishReset( struct device *dev )
+{
+	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
+	u8		data;
+	u32		phy;
+	u8		sio;
+	u16		status;
+	u16		tlphy_ctl;
+
+	phy = priv->phy[priv->phyNum];
+
+	data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+	if ( priv->tlanFullDuplex ) {
+		data |= TLAN_NET_CMD_DUPLEX;
+	}
+	TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data );
+	data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; 
+	if ( priv->phyNum == 0 ) {
+		data |= TLAN_NET_MASK_MASK7; 
 	}
-	TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 );
+	TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data );
 	TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE );
 
-	if ( priv->phyFlags & TLAN_PHY_UNMANAGED ) {
-		priv->phyOnline = 1;
+	if ( ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) || ( priv->aui ) ) {
+		status = MII_GS_LINK;
+		printk( "TLAN:  %s: Link forced.\n", dev->name );
+	} else {
+		TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+		udelay( 1000 );
+		TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+		if ( status & MII_GS_LINK ) {
+			printk( "TLAN:  %s: Link active.\n", dev->name );
+			TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+		}
 	}
 
-	return 0;
+	if ( priv->phyNum == 0 ) {
+        	TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+        	tlphy_ctl |= TLAN_TC_INTEN;
+        	TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl );
+        	sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
+        	sio |= TLAN_NET_SIO_MINTEN;
+        	TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
+	}
+
+	if ( status & MII_GS_LINK ) {
+		TLan_SetMac( dev, 0, dev->dev_addr );
+		priv->phyOnline = 1;
+		outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+		if ( debug >= 1 ) {
+			outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+		}
+		outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+		outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+	} else {
+		printk( "TLAN:  %s: Link inactive, will retry in 10 secs...\n", dev->name );
+		TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET );
+		return;
+	}
 
-} /* TLan_Reset */
+} /* TLan_FinishReset */
 
 
 
@@ -1787,56 +2066,10 @@
 
 	ThunderLAN Driver PHY Layer Routines
 
-	The TLAN chip can drive any number of PHYs (physical devices).  Rather
-	than having lots of 'if' or '#ifdef' statements, I have created a
-	second driver layer for the PHYs.  Each PHY can be identified from its
-	id in registers 2 and 3, and can be given a Check and Service routine
-	that will be called when the adapter is reset and when the adapter
-	receives a Network Status interrupt, respectively.
-	
 ******************************************************************************
 *****************************************************************************/
 
 
-static TLanPhyIdEntry	TLanPhyIdTable[] = {
-	  { 0x4000,
-	    0x5014,
-	    &TLan_PhyInternalCheck,
-	    &TLan_PhyInternalService,
-	    TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
-	  { 0x4000,
-	    0x5015,
-	    &TLan_PhyInternalCheck,
-	    &TLan_PhyInternalService,
-	    TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
-	  { 0x4000,
-	    0x5016,
-	    &TLan_PhyInternalCheck,
-	    &TLan_PhyInternalService,
-	    TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
-	  { 0x2000,
-	    0x5C00,
-	    &TLan_PhyDp83840aCheck,
-	    &TLan_PhyNop,
-	    TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
-	  { 0x2000,
-	    0x5C01,
-	    &TLan_PhyDp83840aCheck,
-	    &TLan_PhyNop,
-	    TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
-	  { 0x7810,
-	    0x0000,
-	    &TLan_PhyDp83840aCheck,
-	    &TLan_PhyNop,
-	    TLAN_PHY_AUTONEG },
-	  { 0x0000,
-	    0x0000,
-	    NULL,
-	    NULL,
-	    0
-	  }
-	};
-
 
 	/*********************************************************************
 	 *	TLan_PhyPrint
@@ -1844,10 +2077,10 @@
 	 *	Returns:
 	 *		Nothing
 	 *	Parms:
-	 *		dev	A pointer to the device structure of the adapter
-	 *			which the desired PHY is located.
+	 *		dev	A pointer to the device structure of the
+	 *			TLAN device having the PHYs to be detailed.
 	 *				
-	 *	This function prints the registers a PHY.
+	 *	This function prints the registers a PHY (aka tranceiver).
 	 *
 	 ********************************************************************/
 
@@ -1855,30 +2088,27 @@
 {
 	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
 	u16 i, data0, data1, data2, data3, phy;
-	u32 io;
 
-	phy = priv->phyAddr;
-	io = dev->base_addr;
+	phy = priv->phy[priv->phyNum];
 
-	if ( ( phy > 0 ) && ( phy <= TLAN_PHY_MAX_ADDR ) ) {
+	if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+		printk( "TLAN:   Device %s, Unmanaged PHY.\n", dev->name );
+	} else if ( phy <= TLAN_PHY_MAX_ADDR ) {
 		printk( "TLAN:   Device %s, PHY 0x%02x.\n", dev->name, phy );
 		printk( "TLAN:      Off.  +0     +1     +2     +3 \n" );
                 for ( i = 0; i < 0x20; i+= 4 ) {
 			printk( "TLAN:      0x%02x", i );
-			TLan_MiiReadReg( io, phy, i, &data0 );
+			TLan_MiiReadReg( dev, phy, i, &data0 );
 			printk( " 0x%04hx", data0 );
-			TLan_MiiReadReg( io, phy, i + 1, &data1 );
+			TLan_MiiReadReg( dev, phy, i + 1, &data1 );
 			printk( " 0x%04hx", data1 );
-			TLan_MiiReadReg( io, phy, i + 2, &data2 );
+			TLan_MiiReadReg( dev, phy, i + 2, &data2 );
 			printk( " 0x%04hx", data2 );
-			TLan_MiiReadReg( io, phy, i + 3, &data3 );
+			TLan_MiiReadReg( dev, phy, i + 3, &data3 );
 			printk( " 0x%04hx\n", data3 );
 		}
 	} else {
-		printk( "TLAN:   Device %s, PHY 0x%02x (Unmanaged/Unknown).\n",
-			dev->name,
-			phy
-		      );
+		printk( "TLAN:   Device %s, Invalid PHY.\n", dev->name );
 	}
 
 } /* TLan_PhyPrint */
@@ -1887,7 +2117,7 @@
 
 
 	/*********************************************************************
-	 *	TLan_PhySelect
+	 *	TLan_PhyDetect
 	 *
 	 *	Returns:
 	 *		Nothing
@@ -1895,355 +2125,271 @@
 	 *		dev	A pointer to the device structure of the adapter
 	 *			for which the PHY needs determined.
 	 *
-	 *	This function decides which PHY amoung those attached to the
-	 *	TLAN chip is to be used.  The TLAN chip can be attached to
-	 *	multiple PHYs, and the driver needs to decide which one to
-	 *	talk to.  Currently this routine picks the PHY with the lowest
-	 *	address as the internal PHY address is 0x1F, the highest
-	 *	possible.  This strategy assumes that there can be only one
-	 *	other PHY, and, if it exists, it is the one to be used.  If
-	 *	token ring PHYs are ever supported, this routine will become
-	 *	a little more interesting...
+	 *	So far I've found that adapters which have external PHYs
+	 *	may also use the internal PHY for part of the functionality.
+	 *	(eg, AUI/Thinnet).  This function finds out if this TLAN
+	 *	chip has an internal PHY, and then finds the first external
+	 *	PHY (starting from address 0) if it exists).
 	 *
 	 ********************************************************************/
 
-void TLan_PhySelect( struct device *dev )
+void TLan_PhyDetect( struct device *dev )
 {
 	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-	int		 	phy;
-	int			entry;
-	u16			id_hi[TLAN_PHY_MAX_ADDR + 1];
-	u16			id_lo[TLAN_PHY_MAX_ADDR + 1];
-	u16			hi;
-	u16			lo;
-	u16			vendor;
-	u16			device;
-
-	priv->phyCheck = &TLan_PhyNop;	// Make sure these aren't ever NULL
-	priv->phyService = &TLan_PhyNop;
-	
-	vendor = TLanDeviceList[priv->pciEntry].vendorId;
-	device = TLanDeviceList[priv->pciEntry].deviceId;
+	u16		control;
+	u16		hi;
+	u16		lo;
+	u32		phy;
 
-	// This is a bit uglier than I'd like, but the 0xF130 device must
-	// NOT be assigned a valid PHY as it uses an unmanaged, bit-rate
-	// PHY.  It is simplest just to use another goto, rather than
-	// nesting the two for loops in the if statement.
-
-	if ( ( vendor == PCI_VENDOR_ID_COMPAQ ) &&
-	     ( device == PCI_DEVICE_ID_NETFLEX_3P ) ) {
-		entry = 0;
-		phy = 0;
-		goto FINISH;
+	if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+		priv->phyNum = 0xFFFF;
+		return;
 	}
 
-	for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
-		hi = lo = 0;
-		TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &hi );
-		TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &lo );
-		id_hi[phy] = hi;
-		id_lo[phy] = lo;
-		TLAN_DBG( TLAN_DEBUG_GNRL,
-			  "TLAN: Phy %2x, hi = %hx, lo = %hx\n",
-			  phy,
-			  hi,
-			  lo
-			);
+	TLan_MiiReadReg( dev, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi );
+
+	if ( hi != 0xFFFF ) {
+		priv->phy[0] = TLAN_PHY_MAX_ADDR;
+	} else {
+		priv->phy[0] = TLAN_PHY_NONE;
 	}
 
+	priv->phy[1] = TLAN_PHY_NONE;
 	for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
-		if ( ( aui == 1 ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
-			if ( id_hi[phy] != 0xFFFF ) {
-				TLan_MiiSync(dev->base_addr);
-				TLan_MiiWriteReg(dev->base_addr,
-						 phy,
-						 MII_GEN_CTL,
-						 MII_GC_PDOWN | 
-						 MII_GC_LOOPBK | 
-						 MII_GC_ISOLATE );
-
-			}
-			continue;
-		}
-		for ( entry = 0; TLanPhyIdTable[entry].check; entry++) {
-			if ( ( id_hi[phy] == TLanPhyIdTable[entry].idHi ) &&
-			     ( id_lo[phy] == TLanPhyIdTable[entry].idLo ) ) {
-				TLAN_DBG( TLAN_DEBUG_GNRL,
-					  "TLAN: Selected Phy %hx\n",
-					  phy
-					);
-				goto FINISH;
+		TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &control );
+		TLan_MiiReadReg( dev, phy, MII_GEN_ID_HI, &hi );
+		TLan_MiiReadReg( dev, phy, MII_GEN_ID_LO, &lo );
+		if ( ( control != 0xFFFF ) || ( hi != 0xFFFF ) || ( lo != 0xFFFF ) ) {
+			TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: PHY found at %02x %04x %04x %04x\n", phy, control, hi, lo );
+			if ( ( priv->phy[1] == TLAN_PHY_NONE ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
+				priv->phy[1] = phy;
 			}
 		}
 	}
 
-	entry = 0;
-	phy = 0;
-
-FINISH:
-	
-	if ( ( entry == 0 ) && ( phy == 0 ) ) {
-		priv->phyAddr = phy;
-		priv->phyEntry = entry;
-		priv->phyCheck = TLan_PhyNop;
-		priv->phyService = TLan_PhyNop;
-		priv->phyFlags = TLAN_PHY_BIT_RATE | 
-                                 TLAN_PHY_UNMANAGED |
-				 TLAN_PHY_ACTIVITY;
+	if ( priv->phy[1] != TLAN_PHY_NONE ) {
+		priv->phyNum = 1;
+	} else if ( priv->phy[0] != TLAN_PHY_NONE ) {
+		priv->phyNum = 0;
 	} else {
-		priv->phyAddr = phy;
-		priv->phyEntry = entry;
-		priv->phyCheck = TLanPhyIdTable[entry].check;
-		priv->phyService = TLanPhyIdTable[entry].service;
-		priv->phyFlags = TLanPhyIdTable[entry].flags;
+		printk( "TLAN:  Cannot initialize device, no PHY was found!\n" );
 	}
 
-} /* TLan_PhySelect */
-
+} /* TLan_PhyDetect */
 
 
 
-	/***************************************************************
-	 *	TLan_PhyNop
-	 *
-	 *	Returns:
-	 *		Nothing
-	 *	Parms:
-	 *		dev	A pointer to a device structure.
-	 *
-	 *	This function does nothing and is meant as a stand-in
-	 *	for when a Check or Service function would be
-	 *	meaningless.
-	 *
-	 **************************************************************/
 
-int TLan_PhyNop( struct device *dev )
+void TLan_PhyPowerDown( struct device *dev )
 {
-	dev = NULL;
-	return 0;
+	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
+	u16		value;
 
-} /* TLan_PhyNop */
+	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering down PHY(s).\n", dev->name );
+	value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
+	TLan_MiiSync( dev->base_addr );
+	TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+	if ( ( priv->phyNum == 0 ) && ( priv->phy[1] != TLAN_PHY_NONE ) && ( ! ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) {
+		TLan_MiiSync( dev->base_addr );
+		TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value );
+	}
 
+	/* Wait for 5 jiffies (50 ms) and powerup
+	 * This is abitrary.  It is intended to make sure the
+	 * tranceiver settles.
+	 */
+	TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP );
+
+} /* TLan_PhyPowerDown */
 
 
 
-	/***************************************************************
-	 *  TLan_PhyInternalCheck
-	 *
-	 *	Returns:
-	 *		Nothing
-	 *	Parms:
-	 *		dev	A pointer to a device structure of the
-	 *			adapter	holding the PHY to be checked.
-	 *
-	 *	This function resets the internal PHY on a TLAN chip.
-	 *	See Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
-	 *	Programmer's Guide"
-	 *
-	 **************************************************************/
 
-int TLan_PhyInternalCheck( struct device *dev )
+void TLan_PhyPowerUp( struct device *dev )
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
-	u16				gen_ctl;
-	u32				io;
-	u16				phy;
-	u16				value;
-	u8				sio;
-
-	io = dev->base_addr;
-	phy = priv->phyAddr;
-
-	TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
-	if ( gen_ctl & MII_GC_PDOWN ) {
-		TLan_MiiSync( io );
-		TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
-		TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
-		udelay(50000);
-		TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
-		TLan_MiiSync( io );
-	}
-
-	TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-	while ( value & MII_GC_RESET )
-		TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-
-	// TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
-	// TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
-	TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
-
-	udelay(500000);
-
-	TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
-	if ( aui ) 
-		value |= TLAN_TC_AUISEL;
-	else
-		value &= ~TLAN_TC_AUISEL;
-	TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-
-	// Read Possible Latched Link Status
-	TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-	// Read Real Link Status
-       	TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-	if ( ( value & MII_GS_LINK ) || aui ) {
-		priv->phyOnline = 1;
-		TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
-	} else {
-		priv->phyOnline = 0;
-		TLan_DioWrite8( io, TLAN_LED_REG, 0 );
-	}
+	u16		value;
 
-	// Enable Interrupts
-       	TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
-	value |= TLAN_TC_INTEN;
-	TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-
-	sio = TLan_DioRead8( io, TLAN_NET_SIO );
-	sio |= TLAN_NET_SIO_MINTEN;
-	TLan_DioWrite8( io, TLAN_NET_SIO, sio );
-			
-	return 0;
+	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering up PHY.\n", dev->name );
+	TLan_MiiSync( dev->base_addr );
+	value = MII_GC_LOOPBK;
+	TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
 
-} /* TLanPhyInternalCheck */
+	/* Wait for 50 jiffies (500 ms) and reset the
+	 * tranceiver.  The TLAN docs say both 50 ms and
+	 * 500 ms, so do the longer, just in case
+	 */
+	TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET );
 
+} /* TLan_PhyPowerUp */
 
 
 
-	/***************************************************************
-	 *	TLan_PhyInternalService
-	 *
-	 *	Returns:
-	 *		Nothing
-	 *	Parms:
-	 *		dev	A pointer to a device structure of the
-	 *			adapter	holding the PHY to be serviced.
-	 *
-	 *	This function services an interrupt generated by the
-	 *	internal PHY.  It can turn on/off the link LED.  See
-	 *	Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
-	 *	Programmer's Guide".
-	 *
-	 **************************************************************/
 
-int TLan_PhyInternalService( struct device *dev )
+void TLan_PhyReset( struct device *dev )
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
-	u16			tlphy_sts;
-	u16			gen_sts;
-	u16			an_exp;
-	u32			io;
-	u16			phy;
-
-	io = dev->base_addr;
-	phy = priv->phyAddr;
-
-	TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
-	TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); 
-	TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); 
-	if ( ( gen_sts & MII_GS_LINK ) || aui ) {
-		priv->phyOnline = 1;
-		TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
-	} else {
-		priv->phyOnline = 0;
-		TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+	u16		phy;
+	u16		value;
+
+	phy = priv->phy[priv->phyNum];
+
+	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Reseting PHY.\n", dev->name );
+	TLan_MiiSync( dev->base_addr );
+	value = MII_GC_LOOPBK | MII_GC_RESET;
+	TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, value );
+	TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
+	while ( value & MII_GC_RESET ) {
+		TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
 	}
+	TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 );
 
-        if ( ( tlphy_sts & TLAN_TS_POLOK ) == 0) {
-                u16     value;
-                TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value);
-                value |= TLAN_TC_SWAPOL;
-                TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value);
-        }
+	/* Wait for 50 jiffies (500 ms) and initialize.
+	 * I don't remember why I wait this long.
+	 */
+	TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK );
 
-	return 0;
+} /* TLan_PhyReset */
 
-} /* TLan_PhyInternalService */
 
 
 
+void TLan_PhyStartLink( struct device *dev )
+{
+	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
+	u16		ability;
+	u16		control;
+	u16		data;
+	u16		phy;
+	u16		status;
+	u16		tctl;
+
+	phy = priv->phy[priv->phyNum];
+
+	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Trying to activate link.\n", dev->name );
+	TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+	if ( ( status & MII_GS_AUTONEG ) && 
+	     ( priv->duplex == TLAN_DUPLEX_DEFAULT ) && 
+	     ( priv->speed == TLAN_SPEED_DEFAULT ) &&
+	     ( ! priv->aui ) ) {
+		ability = status >> 11;
+
+		if ( priv->speed == TLAN_SPEED_10 ) {
+			ability &= 0x0003;
+		} else if ( priv->speed == TLAN_SPEED_100 ) {
+			ability &= 0x001C;
+		}
+
+		if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+			ability &= 0x000A;
+		} else if ( priv->duplex == TLAN_DUPLEX_HALF ) {
+			ability &= 0x0005;
+		}
+
+		TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
+       		TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
+		TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
+
+		/* Wait for 400 jiffies (4 sec) for autonegotiation
+		 * to complete.  The max spec time is less than this
+		 * but the card need additional time to start AN.
+		 * .5 sec should be plenty extra.
+		 */
+		printk( "TLAN:  %s: Starting autonegotiation.\n", dev->name );
+		TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN );
+		return;
+	}
+
+	if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) {
+		priv->phyNum = 0;
+		data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+		TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+		TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN );
+		return;
+	} else if ( priv->phyNum == 0 ) {
+        	TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl );
+		if ( priv->aui ) {
+                	tctl |= TLAN_TC_AUISEL;
+		} else {
+                	tctl &= ~TLAN_TC_AUISEL;
+			control = 0;
+			if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+				control |= MII_GC_DUPLEX;
+				priv->tlanFullDuplex = TRUE;
+			}
+			if ( priv->speed == TLAN_SPEED_100 ) {
+				control |= MII_GC_SPEEDSEL;
+			}
+       			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, control );
+		}
+        	TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl );
+	}
+
+	/* Wait for 100 jiffies (1 sec) to give the tranceiver time
+	 * to establish link.
+	 */
+	TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET );
+
+} /* TLan_PhyStartLink */
 
-	/***************************************************************
-	 *	TLan_PhyDp83840aCheck
-	 *
-	 *	Returns:
-	 *		Nothing
-	 *	Parms:
-	 *		dev	A pointer to a device structure of the
-	 *			adapter holding the PHY to be reset.
-	 *
-	 *	This function resets a National Semiconductor DP83840A
-	 *	10/100 Mb/s PHY device.  See National Semiconductor's
-	 *	data sheet for more info.  This PHY is used on Compaq
-	 *	Netelligent 10/100 cards.
-	 *
-	 **************************************************************/
 
-static int TLan_PhyDp83840aCheck( struct device *dev )
+
+
+void TLan_PhyFinishAutoNeg( struct device *dev )
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
-	u16			gen_ctl;
-	int			i;
-	u32			io;
-	u16			phy;
-	u16			value;
-	u8			sio;
-
-	io = dev->base_addr;
-	phy = priv->phyAddr;
-
-	TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
-	if ( gen_ctl & MII_GC_PDOWN ) {
-		TLan_MiiSync( io );
-		TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
-		TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
-		for ( i = 0; i < 500000; i++ )
-			SLOW_DOWN_IO;
-		TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
-		TLan_MiiSync( io );
-	}
-
-	TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-	while ( value & MII_GC_RESET )
-		TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-
-	// TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
-	// TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
-	TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
-	TLan_MiiReadReg( io, phy, MII_AN_ADV, &value );
-	value &= ~0x0140;
-	TLan_MiiWriteReg( io, phy, MII_AN_ADV, value );
-       	TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 );
-	TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 );
+	u16		an_adv;
+	u16		an_lpa;
+	u16		data;
+	u16		mode;
+	u16		phy;
+	u16		status;
+	
+	phy = priv->phy[priv->phyNum];
+
+	TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+	if ( ! ( status & MII_GS_AUTOCMPLT ) ) {
+		/* Wait for 800 jiffies (8 sec) to give the process
+		 * more time.  Perhaps we should fail after a while.
+		 */
+		printk( "TLAN:  Giving autonegotiation more time.\n" );
+		TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN );
+		return;
+	}
 
-	for ( i = 0; i < 50000; i++ )
-		SLOW_DOWN_IO;
+	printk( "TLAN:  %s: Autonegotiation complete.\n", dev->name );
+	TLan_MiiReadReg( dev, phy, MII_AN_ADV, &an_adv );
+	TLan_MiiReadReg( dev, phy, MII_AN_LPA, &an_lpa );
+	mode = an_adv & an_lpa & 0x03E0;
+	if ( mode & 0x0100 ) {
+		priv->tlanFullDuplex = TRUE;
+	} else if ( ! ( mode & 0x0080 ) && ( mode & 0x0040 ) ) {
+		priv->tlanFullDuplex = TRUE;
+	}
 
-/*
-	// Read Possible Latched Link Status
-	TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-	// Read Real Link Status
-	TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); 
-	if ( value & MII_GS_LINK ) {
-		priv->phyOnline = 1;
-		TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
-	} else {
-		priv->phyOnline = 0;
-		TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+	if ( ( ! ( mode & 0x0180 ) ) && ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) && ( priv->phyNum != 0 ) ) {
+		priv->phyNum = 0;
+		data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+		TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+		TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN );
+		return;
 	}
 
-	// Enable Interrupts
-	TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
-	value |= TLAN_TC_INTEN;
-	TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-*/
-	sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
-	sio &= ~TLAN_NET_SIO_MINTEN;
-	TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
-//	priv->phyOnline = 1;
-			
-	return 0;
+	if ( priv->phyNum == 0 ) {
+		if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) {
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX );
+			printk( "TLAN:  Starting internal PHY with DUPLEX\n" );
+		} else {
+			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB );
+			printk( "TLAN:  Starting internal PHY with HALF-DUPLEX\n" );
+		}
+	}
 
-} /* TLan_PhyDp83840aCheck */
+	/* Wait for 10 jiffies (100 ms).  No reason in partiticular.
+	 */
+	TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET );
+		
+} /* TLan_PhyFinishAutoNeg */
 
 
 
@@ -2268,9 +2414,10 @@
 	 *		1	otherwise.
 	 *
 	 *	Parms:
-	 *		base_port	The base IO port of the adapter in
-	 *				question.
-	 *		dev		The address of the PHY to be queried.
+	 *		dev		The device structure containing
+	 *				The io address and interrupt count
+	 *				for this device.
+	 *		phy		The address of the PHY to be queried.
 	 *		reg		The register whose contents are to be
 	 *				retreived.
 	 *		val		A pointer to a variable to store the
@@ -2283,30 +2430,32 @@
 	 *
 	 **************************************************************/
 
-int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
+int TLan_MiiReadReg( struct device *dev, u16 phy, u16 reg, u16 *val )
 {
 	u8	nack;
-	u16 sio, tmp;
-	u32 i;
+	u16	sio, tmp;
+	u32	i;
 	int	err;
-	int minten;
+	int	minten;
 
 	err = FALSE;
-	outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
-	sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+	outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+	sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
 
-	cli();
+	if ( dev->interrupt == 0 )
+		cli();
+	dev->interrupt++;
 
-	TLan_MiiSync(base_port);
+	TLan_MiiSync(dev->base_addr);
 
 	minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
 	if ( minten )
 		TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
 
-	TLan_MiiSendData( base_port, 0x1, 2 );		/* Start ( 01b ) */
-	TLan_MiiSendData( base_port, 0x2, 2 );		/* Read  ( 10b ) */
-	TLan_MiiSendData( base_port, dev, 5 );		/* Device #      */
-	TLan_MiiSendData( base_port, reg, 5 );		/* Register #    */
+	TLan_MiiSendData( dev->base_addr, 0x1, 2 );	/* Start ( 01b ) */
+	TLan_MiiSendData( dev->base_addr, 0x2, 2 );	/* Read  ( 10b ) */
+	TLan_MiiSendData( dev->base_addr, phy, 5 );	/* Device #      */
+	TLan_MiiSendData( dev->base_addr, reg, 5 );	/* Register #    */
 
 
 	TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio);		/* Change direction */
@@ -2342,7 +2491,9 @@
 
 	*val = tmp;
 
-	sti();
+	dev->interrupt--;
+	if ( dev->interrupt == 0 )
+		sti();
 
 	return err;
 
@@ -2436,9 +2587,9 @@
 	 *	Returns:
 	 *		Nothing
 	 *	Parms:
-	 *		base_port	The base IO port of the adapter in
-	 *				question.
-	 *		dev		The address of the PHY to be written to.
+	 *		dev		The device structure for the device
+	 *				to write to.
+	 *		phy		The address of the PHY to be written to.
 	 *		reg		The register whose contents are to be
 	 *				written.
 	 *		val		The value to be written to the register.
@@ -2450,29 +2601,31 @@
 	 *
 	 **************************************************************/
 
-void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
+void TLan_MiiWriteReg( struct device *dev, u16 phy, u16 reg, u16 val )
 {
-	u16 sio;
+	u16	sio;
 	int	minten;
 
-	outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
-	sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+	outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+	sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
 
-	cli();
+	if ( dev->interrupt == 0 )
+		cli();
+	dev->interrupt++;
 
-	TLan_MiiSync( base_port );
+	TLan_MiiSync( dev->base_addr );
 
 	minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
 	if ( minten )
 		TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
 
-	TLan_MiiSendData( base_port, 0x1, 2 );		/* Start ( 01b ) */
-	TLan_MiiSendData( base_port, 0x1, 2 );		/* Write ( 01b ) */
-	TLan_MiiSendData( base_port, dev, 5 );		/* Device #      */
-	TLan_MiiSendData( base_port, reg, 5 );		/* Register #    */
+	TLan_MiiSendData( dev->base_addr, 0x1, 2 );	/* Start ( 01b ) */
+	TLan_MiiSendData( dev->base_addr, 0x1, 2 );	/* Write ( 01b ) */
+	TLan_MiiSendData( dev->base_addr, phy, 5 );	/* Device #      */
+	TLan_MiiSendData( dev->base_addr, reg, 5 );	/* Register #    */
 
-	TLan_MiiSendData( base_port, 0x2, 2 );		/* Send ACK */
-	TLan_MiiSendData( base_port, val, 16 );		/* Send Data */
+	TLan_MiiSendData( dev->base_addr, 0x2, 2 );	/* Send ACK */
+	TLan_MiiSendData( dev->base_addr, val, 16 );	/* Send Data */
 
 	TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );	/* Idle cycle */
 	TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
@@ -2480,10 +2633,15 @@
 	if ( minten )
 		TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
 
-	sti();
+	dev->interrupt--;
+	if ( dev->interrupt == 0 )
+		sti();
 
 } /* TLan_MiiWriteReg */
 
+
+
+
 /*****************************************************************************
 ******************************************************************************
 
@@ -2562,7 +2720,7 @@
 	outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
 	sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
 
-	// Assume clock is low, tx is enabled;
+	/* Assume clock is low, tx is enabled; */
 	for ( place = 0x80; place != 0; place >>= 1 ) {
 		if ( place & data )
 			TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
@@ -2578,7 +2736,7 @@
 	TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
 
 	if ( ( ! err ) && stop ) {
-		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	// STOP, raise data while clock is high
+		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	/* STOP, raise data while clock is high */
 		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
 		TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
 	}
@@ -2623,7 +2781,7 @@
 	sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
 	*data = 0;
 
-	// Assume clock is low, tx is enabled;
+	/* Assume clock is low, tx is enabled; */
 	TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
 	for ( place = 0x80; place; place >>= 1 ) {
 		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
@@ -2634,14 +2792,14 @@
 
 	TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
 	if ( ! stop ) {
-		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	// Ack = 0
+		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	/* Ack = 0 */
 		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
 		TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
 	} else {
-		TLan_SetBit( TLAN_NET_SIO_EDATA, sio );		// No ack = 1 (?)
+		TLan_SetBit( TLAN_NET_SIO_EDATA, sio );		/* No ack = 1 (?) */
 		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
 		TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
-		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	// STOP, raise data while clock is high
+		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	/* STOP, raise data while clock is high */
 		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
 		TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
 	}
@@ -2672,26 +2830,30 @@
 	 *
 	 **************************************************************/
 
-int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data )
+int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data )
 {
 	int err;
 
-	cli();
+	if ( dev->interrupt == 0 )
+		cli();
+	dev->interrupt++;
 
-	TLan_EeSendStart( io_base );
-	err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK );
+	TLan_EeSendStart( dev->base_addr );
+	err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK );
 	if (err)
 		return 1;
-	err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK );
+	err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK );
 	if (err)
 		return 2;
-	TLan_EeSendStart( io_base );
-	err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK );
+	TLan_EeSendStart( dev->base_addr );
+	err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK );
 	if (err)
 		return 3;
-	TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP );
+	TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP );
 
-	sti();
+	dev->interrupt--;
+	if ( dev->interrupt == 0 )
+		sti();
 
 	return 0;
 
diff -u --recursive --new-file v2.0.34/linux/drivers/net/tlan.h linux/drivers/net/tlan.h
--- v2.0.34/linux/drivers/net/tlan.h	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/tlan.h	Mon Jul 13 13:47:32 1998
@@ -5,9 +5,9 @@
  *  Linux ThunderLAN Driver
  *
  *  tlan.h
- *  by James Banks, james.banks@caldera.com
+ *  by James Banks
  *
- *  (C) 1997 Caldera, Inc.
+ *  (C) 1997-1998 Caldera, Inc.
  *
  *  This software may be used and distributed according to the terms
  *  of the GNU Public License, incorporated herein by reference.
@@ -33,23 +33,23 @@
 	 *
 	 ****************************************************************/
 
-#define FALSE				0
-#define TRUE				1
+#define FALSE			0
+#define TRUE			1
 
 #define TLAN_MIN_FRAME_SIZE	64
 #define TLAN_MAX_FRAME_SIZE	1600
 
-#define TLAN_NUM_RX_LISTS 4
-#define TLAN_NUM_TX_LISTS 8
+#define TLAN_NUM_RX_LISTS	4
+#define TLAN_NUM_TX_LISTS	8
 
-#define TLAN_IGNORE	0
-#define TLAN_RECORD	1
+#define TLAN_IGNORE		0
+#define TLAN_RECORD		1
 
 #define TLAN_DBG(lvl, format, args...)	if (debug&lvl) printk( format, ##args );
-#define TLAN_DEBUG_GNRL	0x0001
-#define TLAN_DEBUG_TX	0x0002
-#define TLAN_DEBUG_RX	0x0004 
-#define TLAN_DEBUG_LIST	0x0008
+#define TLAN_DEBUG_GNRL		0x0001
+#define TLAN_DEBUG_TX		0x0002
+#define TLAN_DEBUG_RX		0x0004 
+#define TLAN_DEBUG_LIST		0x0008
 
 
 
@@ -59,23 +59,47 @@
 	 *
 	 ****************************************************************/
 		
-	/* NOTE: These have been moved to pci.h, will use them
-	   eventually */
-#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
-#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
-#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
-#define PCI_DEVICE_ID_NETFLEX_3P 0xF130
-#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
-#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
-#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
-#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
-
+#define PCI_DEVICE_ID_NETELLIGENT_10			0xAE34
+#define PCI_DEVICE_ID_NETELLIGENT_10_100		0xAE32
+#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED		0xAE35
+#define PCI_DEVICE_ID_NETFLEX_3P			0xF130
+#define PCI_DEVICE_ID_NETFLEX_3P_BNC			0xF150
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT	0xAE43
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL		0xAE40
+#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX		0xB011
+#define PCI_DEVICE_ID_NETELLIGENT_10_T2			0xB012
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100	0xB030
+#ifndef PCI_DEVICE_ID_OLICOM_OC2183
+#define PCI_DEVICE_ID_OLICOM_OC2183			0x0013
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2325
+#define PCI_DEVICE_ID_OLICOM_OC2325			0x0012
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2326
+#define PCI_DEVICE_ID_OLICOM_OC2326			0x0014
+#endif
 
-typedef struct tlan_pci_id {
+typedef struct tlan_adapter_entry {
 	u16	vendorId;
 	u16	deviceId;
-	char *deviceName;
-} TLanPciId;
+	char	*deviceLabel;
+	u32	flags;
+	u16	addrOfs;
+} TLanAdapterEntry;
+
+#define TLAN_ADAPTER_NONE		0x00000000
+#define TLAN_ADAPTER_UNMANAGED_PHY	0x00000001
+#define TLAN_ADAPTER_BIT_RATE_PHY	0x00000002
+#define TLAN_ADAPTER_USE_INTERN_10	0x00000004
+#define TLAN_ADAPTER_ACTIVITY_LED	0x00000008
+
+#define TLAN_SPEED_DEFAULT	0
+#define TLAN_SPEED_10		10
+#define TLAN_SPEED_100		100
+
+#define TLAN_DUPLEX_DEFAULT	0
+#define TLAN_DUPLEX_HALF	1
+#define TLAN_DUPLEX_FULL	2
 
 
 
@@ -121,25 +145,7 @@
 	 ****************************************************************/
 
 #define TLAN_PHY_MAX_ADDR	0x1F
-
-#define TLAN_PHY_ACTIVITY	0x00000001
-#define TLAN_PHY_AUTONEG	0x00000002
-#define TLAN_PHY_INTS		0x00000004
-#define TLAN_PHY_BIT_RATE	0x00000008
-#define TLAN_PHY_UNMANAGED	0x00000010
-#define TLAN_PHY_INTERNAL	0x00000020
-
-
-typedef int (TLanPhyFunc)( struct device * );
-
-
-typedef struct tlan_phy_id_entry_tag {
-	u16		idHi;
-	u16		idLo;
-	TLanPhyFunc	*check;
-	TLanPhyFunc	*service;
-	u32		flags;
-} TLanPhyIdEntry;
+#define TLAN_PHY_NONE		0x20
 
 
 
@@ -164,21 +170,22 @@
 	u32                     txInProgress;
 	u32                     txTail;
 	u32			txBusyCount;
-	u32                     phyAddr;
-	u32                     phyEntry;
 	u32                     phyOnline;
-	u32                     phyFlags;
-	TLanPhyFunc             *phyCheck;
-	TLanPhyFunc             *phyService;
 	u32			timerSetAt;
 	u32			timerType;
 	struct timer_list	timer;
 	struct net_device_stats	stats;
-	u32                     pciEntry;
-	u8                      pciRevision;
-	u8                      pciBus;
-	u8                      pciDeviceFn;
+	TLanAdapterEntry	*adapter;
+	u32			adapterRev;
+	u32			aui;
+	u32			debug;
+	u32			duplex;
+	u32			phy[2];
+	u32			phyNum;
+	u32			sa_int;
+	u32			speed;
 	u8			tlanRev;
+	u8			tlanFullDuplex;
 	char                    devName[8];
 } TLanPrivateInfo;
 
@@ -191,10 +198,15 @@
 	 ****************************************************************/
 
 #define TLAN_TIMER_LINK			1
-#define TLAN_TIMER_ACT			2
+#define TLAN_TIMER_ACTIVITY		2
+#define TLAN_TIMER_PHY_PDOWN		3
+#define TLAN_TIMER_PHY_PUP		4
+#define TLAN_TIMER_PHY_RESET		5
+#define TLAN_TIMER_PHY_START_LINK	6
+#define TLAN_TIMER_PHY_FINISH_AN	7
+#define TLAN_TIMER_FINISH_RESET		8
 
-#define TLAN_TIMER_LINK_DELAY	230
-#define TLAN_TIMER_ACT_DELAY	10
+#define TLAN_TIMER_ACT_DELAY		10
 
 
 
@@ -215,29 +227,29 @@
 	 *
 	 ****************************************************************/
 
-#define TLAN_HOST_CMD		0x00
+#define TLAN_HOST_CMD			0x00
 #define 	TLAN_HC_GO		0x80000000
-#define		TLAN_HC_STOP	0x40000000
+#define		TLAN_HC_STOP		0x40000000
 #define		TLAN_HC_ACK		0x20000000
-#define		TLAN_HC_CS_MASK 0x1FE00000
+#define		TLAN_HC_CS_MASK		0x1FE00000
 #define		TLAN_HC_EOC		0x00100000
 #define		TLAN_HC_RT		0x00080000
 #define		TLAN_HC_NES		0x00040000
-#define		TLAN_HC_AD_RST	0x00008000
-#define		TLAN_HC_LD_TMR	0x00004000
-#define		TLAN_HC_LD_THR	0x00002000
-#define		TLAN_HC_REQ_INT	0x00001000
-#define		TLAN_HC_INT_OFF	0x00000800
-#define		TLAN_HC_INT_ON	0x00000400
-#define		TLAN_HC_AC_MASK	0x000000FF
-#define TLAN_CH_PARM		0x04
-#define TLAN_DIO_ADR		0x08
-#define		TLAN_DA_ADR_INC	0x8000
-#define		TLAN_DA_RAM_ADR	0x4000
-#define TLAN_HOST_INT		0x0A
-#define		TLAN_HI_IV_MASK	0x1FE0
-#define		TLAN_HI_IT_MASK	0x001C
-#define TLAN_DIO_DATA		0x0C
+#define		TLAN_HC_AD_RST		0x00008000
+#define		TLAN_HC_LD_TMR		0x00004000
+#define		TLAN_HC_LD_THR		0x00002000
+#define		TLAN_HC_REQ_INT		0x00001000
+#define		TLAN_HC_INT_OFF		0x00000800
+#define		TLAN_HC_INT_ON		0x00000400
+#define		TLAN_HC_AC_MASK		0x000000FF
+#define TLAN_CH_PARM			0x04
+#define TLAN_DIO_ADR			0x08
+#define		TLAN_DA_ADR_INC		0x8000
+#define		TLAN_DA_RAM_ADR		0x4000
+#define TLAN_HOST_INT			0x0A
+#define		TLAN_HI_IV_MASK		0x1FE0
+#define		TLAN_HI_IT_MASK		0x001C
+#define TLAN_DIO_DATA			0x0C
 
 
 /* ThunderLAN Internal Register DIO Offsets */
@@ -264,7 +276,7 @@
 #define		TLAN_NET_STS_MIRQ	0x80
 #define		TLAN_NET_STS_HBEAT	0x40
 #define		TLAN_NET_STS_TXSTOP	0x20
-#define		TLAN_NET_STS_RXSTOP 0x10
+#define		TLAN_NET_STS_RXSTOP	0x10
 #define		TLAN_NET_STS_RSRVD	0x0F
 #define TLAN_NET_MASK			0x03
 #define		TLAN_NET_MASK_MASK7	0x80
@@ -283,36 +295,36 @@
 #define		TLAN_NET_CFG_MTEST	0x0100
 #define		TLAN_NET_CFG_PHY_EN	0x0080
 #define		TLAN_NET_CFG_MSMASK	0x007F
-#define TLAN_MAN_TEST		0x06
-#define TLAN_DEF_VENDOR_ID	0x08
-#define TLAN_DEF_DEVICE_ID	0x0A
-#define TLAN_DEF_REVISION	0x0C
-#define TLAN_DEF_SUBCLASS	0x0D
-#define TLAN_DEF_MIN_LAT	0x0E
-#define TLAN_DEF_MAX_LAT	0x0F
+#define TLAN_MAN_TEST			0x06
+#define TLAN_DEF_VENDOR_ID		0x08
+#define TLAN_DEF_DEVICE_ID		0x0A
+#define TLAN_DEF_REVISION		0x0C
+#define TLAN_DEF_SUBCLASS		0x0D
+#define TLAN_DEF_MIN_LAT		0x0E
+#define TLAN_DEF_MAX_LAT		0x0F
 #define TLAN_AREG_0			0x10
 #define TLAN_AREG_1			0x16
 #define TLAN_AREG_2			0x1C
 #define TLAN_AREG_3			0x22
 #define TLAN_HASH_1			0x28
 #define TLAN_HASH_2			0x2C
-#define TLAN_GOOD_TX_FRMS	0x30
-#define TLAN_TX_UNDERUNS	0x33
-#define TLAN_GOOD_RX_FRMS	0x34
-#define TLAN_RX_OVERRUNS	0x37
-#define TLAN_DEFERRED_TX	0x38
-#define TLAN_CRC_ERRORS		0x3A
-#define TLAN_CODE_ERRORS	0x3B
-#define TLAN_MULTICOL_FRMS	0x3C
-#define TLAN_SINGLECOL_FRMS	0x3E
-#define TLAN_EXCESSCOL_FRMS	0x40
-#define TLAN_LATE_COLS		0x41
-#define TLAN_CARRIER_LOSS	0x42
-#define TLAN_ACOMMIT		0x43
-#define TLAN_LED_REG		0x44
-#define		TLAN_LED_ACT	0x10
-#define		TLAN_LED_LINK	0x01
-#define TLAN_BSIZE_REG		0x45
+#define TLAN_GOOD_TX_FRMS		0x30
+#define TLAN_TX_UNDERUNS		0x33
+#define TLAN_GOOD_RX_FRMS		0x34
+#define TLAN_RX_OVERRUNS		0x37
+#define TLAN_DEFERRED_TX		0x38
+#define TLAN_CRC_ERRORS			0x3A
+#define TLAN_CODE_ERRORS		0x3B
+#define TLAN_MULTICOL_FRMS		0x3C
+#define TLAN_SINGLECOL_FRMS		0x3E
+#define TLAN_EXCESSCOL_FRMS		0x40
+#define TLAN_LATE_COLS			0x41
+#define TLAN_CARRIER_LOSS		0x42
+#define TLAN_ACOMMIT			0x43
+#define TLAN_LED_REG			0x44
+#define		TLAN_LED_ACT		0x10
+#define		TLAN_LED_LINK		0x01
+#define TLAN_BSIZE_REG			0x45
 #define TLAN_MAX_RX			0x46
 #define TLAN_INT_DIS			0x48
 #define		TLAN_ID_TX_EOC		0x04
@@ -327,11 +339,11 @@
 
 #define TLAN_INT_NONE			0x0000
 #define TLAN_INT_TX_EOF			0x0001
-#define TLAN_INT_STAT_OVERFLOW	0x0002
+#define TLAN_INT_STAT_OVERFLOW		0x0002
 #define TLAN_INT_RX_EOF			0x0003
 #define TLAN_INT_DUMMY			0x0004
 #define TLAN_INT_TX_EOC			0x0005
-#define TLAN_INT_STATUS_CHECK	0x0006
+#define TLAN_INT_STATUS_CHECK		0x0006
 #define TLAN_INT_RX_EOC			0x0007
 
 
@@ -340,7 +352,7 @@
 
 /* Generic MII/PHY Registers */
 
-#define MII_GEN_CTL				0x00
+#define MII_GEN_CTL			0x00
 #define 	MII_GC_RESET		0x8000
 #define		MII_GC_LOOPBK		0x4000
 #define		MII_GC_SPEEDSEL		0x2000
@@ -351,7 +363,7 @@
 #define		MII_GC_DUPLEX		0x0100
 #define		MII_GC_COLTEST		0x0080
 #define		MII_GC_RESERVED		0x007F
-#define MII_GEN_STS				0x01
+#define MII_GEN_STS			0x01
 #define		MII_GS_100BT4		0x8000
 #define		MII_GS_100BTXFD		0x4000
 #define		MII_GS_100BTXHD		0x2000
@@ -359,19 +371,19 @@
 #define		MII_GS_10BTHD		0x0800
 #define		MII_GS_RESERVED		0x07C0
 #define		MII_GS_AUTOCMPLT	0x0020
-#define		MII_GS_RFLT			0x0010
+#define		MII_GS_RFLT		0x0010
 #define		MII_GS_AUTONEG		0x0008
-#define		MII_GS_LINK			0x0004
+#define		MII_GS_LINK		0x0004
 #define		MII_GS_JABBER		0x0002
 #define		MII_GS_EXTCAP		0x0001
 #define MII_GEN_ID_HI			0x02
 #define MII_GEN_ID_LO			0x03
-#define 	MII_GIL_OUI			0xFC00
+#define 	MII_GIL_OUI		0xFC00
 #define 	MII_GIL_MODEL		0x03F0
 #define 	MII_GIL_REVISION	0x000F
-#define MII_AN_ADV				0x04
-#define MII_AN_LPA				0x05
-#define MII_AN_EXP				0x06
+#define MII_AN_ADV			0x04
+#define MII_AN_LPA			0x05
+#define MII_AN_EXP			0x06
 
 /* ThunderLAN Specific MII/PHY Registers */
 
@@ -394,6 +406,7 @@
 #define		TLAN_TS_RESERVED	0x0FFF
 
 
+#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
 
 /* Routines to access internal registers. */
 
@@ -456,7 +469,7 @@
 
 
 
-
+#if 0
 inline void TLan_ClearBit(u8 bit, u16 port)
 {
 	outb_p(inb_p(port) & ~bit, port);
@@ -477,6 +490,11 @@
 {
 	outb_p(inb_p(port) | bit, port);
 }
+#endif
+
+#define TLan_ClearBit( bit, port )	outb_p(inb_p(port) & ~bit, port)
+#define TLan_GetBit( bit, port )	((int) (inb_p(port) & bit))
+#define TLan_SetBit( bit, port )	outb_p(inb_p(port) | bit, port)
 
 
 inline	u32	xor( u32 a, u32 b )
diff -u --recursive --new-file v2.0.34/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.0.34/linux/drivers/net/wavelan.c	Sun Aug  3 15:17:45 1997
+++ linux/drivers/net/wavelan.c	Mon Jul 13 13:47:32 1998
@@ -1,10 +1,10 @@
 /*
- *	Wavelan ISA driver
+ *	WaveLAN ISA driver
  *
  *		Jean II - HPLB '96
  *
  * Reorganisation and extension of the driver.
- * Original copyrigth follow (see also end of this file).
+ * Original copyright follows (also see the end of this file).
  * See wavelan.p.h for details.
  */
 
@@ -19,7 +19,7 @@
 /************************* MISC SUBROUTINES **************************/
 /*
  * Subroutines which won't fit in one of the following category
- * (wavelan modem or i82586)
+ * (WaveLAN modem or i82586)
  */
 
 /*------------------------------------------------------------------*/
@@ -95,14 +95,14 @@
 #undef	SC
 
   return((char *) NULL);
-} /* wv_structuct_check */
+} /* wv_struct_check */
 #endif	/* STRUCT_CHECK */
 
 /********************* HOST ADAPTER SUBROUTINES *********************/
 /*
- * Usefull subroutines to manage the wavelan ISA interface
+ * Useful subroutines to manage the WaveLAN ISA interface
  *
- * One major difference with the Pcmcia hardware (exept the port mapping)
+ * One major difference with the PCMCIA hardware (except the port mapping)
  * is that we have to keep the state of the Host Control Register
  * because of the interrupt enable & bus size flags.
  */
@@ -190,7 +190,7 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Disable interrupts on the wavelan hardware
+ * Disable interrupts on the WaveLAN hardware
  */
 static inline void
 wv_ints_off(device *	dev)
@@ -209,7 +209,7 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Enable interrupts on the wavelan hardware
+ * Enable interrupts on the WaveLAN hardware
  */
 static inline void
 wv_ints_on(device *	dev)
@@ -228,7 +228,7 @@
 
 /******************* MODEM MANAGEMENT SUBROUTINES *******************/
 /*
- * Usefull subroutines to manage the modem of the wavelan
+ * Useful subroutines to manage the modem of the WaveLAN
  */
 
 /*------------------------------------------------------------------*/
@@ -294,7 +294,7 @@
 /*------------------------------------------------------------------*/
 /*
  * Calculate the PSA CRC (not tested yet)
- * As the Wavelan drivers don't use the CRC, I won't use it either...
+ * As the WaveLAN drivers don't use the CRC, I won't use it either.
  * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
  * NOTE: By specifying a length including the CRC position the
  * returned value should be zero. (i.e. a correct checksum in the PSA)
@@ -416,7 +416,7 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Wait for the frequency EEprom to complete a command...
+ * Wait for the frequency EEPROM to complete a command...
  * I hope this one will be optimally inlined...
  */
 static inline void
@@ -433,7 +433,7 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Read bytes from the Frequency EEprom (frequency select cards).
+ * Read bytes from the frequency EEPROM (frequency select cards).
  */
 static void
 fee_read(u_long		ioaddr,	/* i/o port of the card */
@@ -452,7 +452,7 @@
       /* Write the read command */
       mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
 
-      /* Wait until EEprom is ready (should be quick !) */
+      /* Wait until EEPROM is ready (should be quick!) */
       fee_wait(ioaddr, 10, 100);
 
       /* Read the value */
@@ -465,8 +465,8 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Write bytes from the Frequency EEprom (frequency select cards).
- * This is a bit complicated, because the frequency eeprom has to
+ * Write bytes from the Frequency EEPROM (frequency select cards).
+ * This is a bit complicated, because the frequency EEPROM has to
  * be unprotected and the write enabled.
  * Jean II
  */
@@ -514,7 +514,7 @@
 
   fee_wait(ioaddr, 10, 100);
 
-  /* Write the EEprom address */
+  /* Write the EEPROM address */
   mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
 
   /* Loop on all buffer */
@@ -539,7 +539,7 @@
   fee_wait(ioaddr, 10, 100);
 
 #ifdef EEPROM_IS_PROTECTED	/* disabled */
-  /* Reprotect EEprom */
+  /* Reprotect EEPROM */
   mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
   mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
 
@@ -921,7 +921,7 @@
 static void
 wv_psa_show(psa_t *	p)
 {
-  printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
+  printk(KERN_DEBUG "##### WaveLAN psa contents: #####\n");
   printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
 	 p->psa_io_base_addr_1,
 	 p->psa_io_base_addr_2,
@@ -1033,7 +1033,7 @@
   lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
 #endif	/* WIRELESS_EXT */
 
-  printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
+  printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n");
 #ifdef DEBUG_SHOW_UNUSED
   printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
 	 m.mmr_unused0[0],
@@ -1102,7 +1102,7 @@
 
   obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));   
 
-  printk(KERN_DEBUG "##### wavelan system control block: #####\n");
+  printk(KERN_DEBUG "##### WaveLAN system control block: #####\n");
 
   printk(KERN_DEBUG "status: ");
   printk("stat 0x%x[%s%s%s%s] ",
@@ -1163,7 +1163,7 @@
 {
   /* net_local *lp = (net_local *) dev->priv; */
 
-  printk(KERN_DEBUG "##### wavelan i82586 receiver unit status: #####\n");
+  printk(KERN_DEBUG "##### WaveLAN i82586 receiver unit status: #####\n");
   printk(KERN_DEBUG "ru:");
   /*
    * Not implemented yet...
@@ -1215,7 +1215,7 @@
   unsigned int	i;
   u_short	p;
 
-  printk(KERN_DEBUG "##### wavelan i82586 command unit status: #####\n");
+  printk(KERN_DEBUG "##### WaveLAN i82586 command unit status: #####\n");
 
   printk(KERN_DEBUG);
   for(i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
@@ -1353,7 +1353,7 @@
     {
       unsigned short	freq;
 
-      /* Ask the EEprom to read the frequency from the first area */
+      /* Ask the EEPROM to read the frequency from the first area */
       fee_read(ioaddr, 0x00 /* 1st area - frequency... */,
 	       &freq, 1);
 
@@ -1558,7 +1558,7 @@
 #endif
 
   /* Setting by frequency */
-  /* Theoritically, you may set any frequency between
+  /* Theoretically, you may set any frequency between
    * the two limits with a 0.5 MHz precision. In practice,
    * I don't want you to have trouble with local
    * regulations... */
@@ -1616,7 +1616,7 @@
       unsigned short	area_verify[16];
       unsigned short	dac_verify[2];
       /* Corresponding gain (in the power adjust value table)
-       * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
+       * see AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8
        * & WCIN062D.DOC, page 6.2.9 */
       unsigned short	power_limit[] = { 40, 80, 120, 160, 0 };
       int		power_band = 0;		/* Selected band */
@@ -1645,7 +1645,7 @@
 	power_adjust &= 0xFF;
 
 #ifdef DEBUG_IOCTL_INFO
-      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      printk(KERN_DEBUG "WaveLAN EEPROM Area 1:");
       for(i = 0; i < 16; i++)
 	{
 	  printk(" %04X",
@@ -1653,11 +1653,11 @@
 	}
       printk("\n");
 
-      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+      printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
 	     dac[0], dac[1]);
 #endif
 
-      /* Frequency offset (for info only...) */
+      /* Frequency offset (for info only) */
       area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
 
       /* Receiver Principle main divider coefficient */
@@ -1670,21 +1670,21 @@
 
       /* Others part of the area are flags, bit streams or unused... */
 
-      /* Set the value in the DAC */
+      /* Set the value in the DAC. */
       dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
       dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
 
-      /* Write the first area */
+      /* Write the first area. */
       fee_write(ioaddr, 0x00,
 		area, 16);
 
-      /* Write the DAC */
+      /* Write the DAC. */
       fee_write(ioaddr, 0x60,
 		dac, 2);
 
-      /* We now should verify here that the EEprom writting was ok */
+      /* We now should verify here that the EEPROM writing was OK. */
 
-      /* ReRead the first area */
+      /* Reread the first area. */
       fee_read(ioaddr, 0x00,
 	       area_verify, 16);
 
@@ -1697,14 +1697,14 @@
 	 memcmp(dac, dac_verify, 2 * 2))
 	{
 #ifdef DEBUG_IOCTL_ERROR
-	  printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
+	  printk(KERN_INFO "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n");
 #endif
 	  return -EOPNOTSUPP;
 	}
 
       /* We must download the frequency parameters to the
-       * synthetisers (from the EEprom - area 1)
-       * Note : as the EEprom is auto decremented, we set the end
+       * synthesizers (from the EEPROM - area 1)
+       * Note: as the EEPROM is automatically decremented, we set the end
        * if the area... */
       mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F);
       mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
@@ -1714,7 +1714,7 @@
       fee_wait(ioaddr, 100, 100);
 
       /* We must now download the power adjust value (gain) to
-       * the synthetisers (from the EEprom - area 7 - DAC) */
+       * the synthesizers (from the EEPROM - area 7 - DAC) */
       mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61);
       mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
 	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
@@ -1725,7 +1725,7 @@
 #ifdef DEBUG_IOCTL_INFO
       /* Verification of what we have done... */
 
-      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      printk(KERN_DEBUG "WaveLAN EEPROM Area 1:");
       for(i = 0; i < 16; i++)
 	{
 	  printk(" %04X",
@@ -1733,7 +1733,7 @@
 	}
       printk("\n");
 
-      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+      printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n",
 	     dac_verify[0], dac_verify[1]);
 #endif
 
@@ -1810,12 +1810,12 @@
 #ifdef HISTOGRAM
 /*------------------------------------------------------------------*/
 /*
- * This function calculate an histogram on the signal level.
+ * This function calculates an histogram on the signal level.
  * As the noise is quite constant, it's like doing it on the SNR.
  * We have defined a set of interval (lp->his_range), and each time
  * the level goes in that interval, we increment the count (lp->his_sum).
- * With this histogram you may detect if one wavelan is really weak,
- * or you may also calculate the mean and standard deviation of the level...
+ * With this histogram you may detect if one WaveLAN is really weak,
+ * or you may also calculate the mean and standard deviation of the level.
  */
 static inline void
 wl_his_gather(device *	dev,
@@ -1841,9 +1841,9 @@
  * This is here that are treated the wireless extensions (iwconfig)
  */
 static int
-wavelan_ioctl(struct device *	dev,	/* Device on wich the ioctl apply */
-	      struct ifreq *	rq,	/* Data passed */
-	      int		cmd)	/* Ioctl number */
+wavelan_ioctl(struct device *	dev,	/* device on which the ioctl is applied */
+	      struct ifreq *	rq,	/* data passed */
+	      int		cmd)	/* ioctl number */
 {
   u_long		ioaddr = dev->base_addr;
   net_local *		lp = (net_local *)dev->priv;	/* lp is not unused */
@@ -1870,7 +1870,7 @@
       break;
 
     case SIOCSIWNWID:
-      /* Set NWID in wavelan */
+      /* Set NWID in WaveLAN */
       if(wrq->u.nwid.on)
 	{
 	  /* Set NWID in psa */
@@ -1925,7 +1925,7 @@
 	{
 	  unsigned short	freq;
 
-	  /* Ask the EEprom to read the frequency from the first area */
+	  /* Ask the EEPROM to read the frequency from the first area */
 	  fee_read(ioaddr, 0x00 /* 1st area - frequency... */,
 		   &freq, 1);
 	  wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
@@ -2042,7 +2042,7 @@
        break;
 
     case SIOCGIWRANGE:
-      /* Basic checking... */
+      /* basic checking */
       if(wrq->u.data.pointer != (caddr_t) 0)
 	{
 	  struct iw_range	range;
@@ -2061,7 +2061,7 @@
 	  range.min_nwid = 0x0000;
 	  range.max_nwid = 0xFFFF;
 
-	  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+	  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */
 	  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
 	       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
 	    {
@@ -2281,7 +2281,7 @@
       ret = -EOPNOTSUPP;
     }
 
-  /* ReEnable interrupts & restore flags */
+  /* Enable interrupts, restore flags */
   wv_splx(x);
 
 #ifdef DEBUG_IOCTL_TRACE
@@ -2293,7 +2293,7 @@
 /*------------------------------------------------------------------*/
 /*
  * Get wireless statistics
- * Called by /proc/net/wireless...
+ * Called by /proc/net/wireless
  */
 static iw_stats *
 wavelan_get_wireless_stats(device *	dev)
@@ -2336,7 +2336,7 @@
   wstats->discard.code = 0L;
   wstats->discard.misc = 0L;
 
-  /* ReEnable interrupts & restore flags */
+  /* Enable interrupts & restore flags */
   wv_splx(x);
 
 #ifdef DEBUG_IOCTL_TRACE
@@ -2348,16 +2348,16 @@
 
 /************************* PACKET RECEPTION *************************/
 /*
- * This part deal with receiving the packets.
- * The interrupt handler get an interrupt when a packet has been
- * successfully received and called this part...
+ * This part deals with receiving the packets.
+ * The interrupt handler gets an interrupt when a packet has been
+ * successfully received and calls this part.
  */
 
 /*------------------------------------------------------------------*/
 /*
- * This routine does the actual copy of data (including the ethernet
+ * This routine does the actual copying of data (including the Ethernet
  * header structure) from the WaveLAN card to an sk_buff chain that
- * will be passed up to the network interface layer. NOTE: We
+ * will be passed up to the network interface layer. NOTE: we
  * currently don't handle trailer protocols (neither does the rest of
  * the network interface), so if that is needed, it will (at least in
  * part) be added here.  The contents of the receive ring buffer are
@@ -2413,13 +2413,13 @@
 #endif	/* HISTOGRAM */
      0)
     {
-      u_char	stats[3];	/* Signal level, Noise level, Signal quality */
+      u_char	stats[3];	/* signal level, noise level, signal quality */
 
       /* read signal level, silence level and signal quality bytes */
-      /* Note : in the Pcmcia hardware, these are part of the frame. It seem
+      /* Note: in the PCMCIA hardware, these are part of the frame. It seems
        * that for the ISA hardware, it's nowhere to be found in the frame,
-       * so I'm oblige to do this (it has side effect on /proc/net/wireless)
-       * Any idea ? */
+       * so I'm obliged to do this (it has a side effect on /proc/net/wireless).
+       * Any ideas? */
       mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
       mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3);
       mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
@@ -2506,7 +2506,7 @@
 #endif
 	}
 
-      /* Check is there was problems in the frame processing */
+      /* Were there problems in processing the frame?  Let's check. */
       if((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 |
 			  FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11))
 	 != 0)
@@ -2563,7 +2563,7 @@
 	    }
 	}
 
-      /* Check if frame contain a pointer to the data */
+      /* Does the frame contain a pointer to the data?  Let's check. */
       if(fd.fd_rbd_offset == I82586NULL)
 #ifdef DEBUG_RX_ERROR
 	printk(KERN_INFO "%s: wv_receive(): frame has no data.\n", dev->name);
@@ -2616,7 +2616,7 @@
 
 /*********************** PACKET TRANSMISSION ***********************/
 /*
- * This part deal with sending packet through the wavelan
+ * This part deals with sending packet through the WaveLAN
  *
  */
 
@@ -2630,8 +2630,8 @@
  * Each block contain a transmit command, a nop command,
  * a transmit block descriptor and a buffer.
  * The CU read the transmit block which point to the tbd,
- * read the tbd and the the content of the buffer.
- * When it has finish with it, it goes to the next command
+ * read the tbd and the content of the buffer.
+ * When it has finished with it, it goes to the next command
  * which in our case is the nop. The nop point on itself,
  * so the CU stop here.
  * When we add the next block, we modify the previous nop
@@ -2712,7 +2712,7 @@
 	      sizeof(nop.nop_h.ac_link));
 
   /*
-   * Transmit buffer descriptor. 
+   * Transmit buffer descriptor
    */
   tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen);
   tbd.tbd_next_bd_offset = I82586NULL;
@@ -2721,7 +2721,7 @@
   obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
 
   /*
-   * Data.
+   * Data
    */
   obram_write(ioaddr, buf_addr, buf, clen);
 
@@ -2767,7 +2767,7 @@
 /*------------------------------------------------------------------*/
 /*
  * This routine is called when we want to send a packet (NET3 callback)
- * In this routine, we check if the the harware is ready to accept
+ * In this routine, we check if the hardware is ready to accept
  * the packet. We also prevent reentrance. Then, we call the function
  * to send the packet...
  */
@@ -2783,9 +2783,9 @@
 #endif
 
   /* This flag indicate that the hardware can't perform a transmission.
-   * Theoritically, NET3 check it before sending a packet to the driver,
-   * but in fact it never do that and pool continuously.
-   * As the watchdog will abort too long transmissions, we are quite safe...
+   * Theoretically, NET3 checks it before sending a packet to the driver,
+   * but in fact it never does that and pool continuously.
+   * As the watchdog will abort overly long transmissions, we are quite safe.
    */
   if(dev->tbusy)
     return 1;
@@ -2814,7 +2814,9 @@
 #endif
   else
     {
-      /* If somebody has asked to reconfigure the controler, we can do it now */
+      /* If somebody has asked to reconfigure the controller, 
+       * we can do it now.
+       */
       if(lp->reconfig_82586)
 	{
 	  wv_82586_config(dev);
@@ -2838,12 +2840,12 @@
   return 0;
 }
 
-/********************** HARDWARE CONFIGURATION **********************/
+/*********************** HARDWARE CONFIGURATION ***********************/
 /*
- * This part do the real job of starting and configuring the hardware.
+ * This part does the real job of starting and configuring the hardware.
  */
 
-/*------------------------------------------------------------------*/
+/*--------------------------------------------------------------------*/
 /*
  * Routine to initialize the Modem Management Controller.
  * (called by wv_hw_reset())
@@ -2877,7 +2879,7 @@
       psa.psa_nwid[0] = 0;
       psa.psa_nwid[1] = 0;
 
-      /* As NWID is not set : no NWID checking */
+      /* no NWID checking since NWID is not set */
       psa.psa_nwid_select = 0;
 
       /* Disable encryption */
@@ -2933,7 +2935,7 @@
   m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
   m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
 
-  /* Missing : encryption stuff... */
+  /* Missing: encryption stuff... */
 
   /*
    * Set default modem control parameters.
@@ -2951,29 +2953,29 @@
   m.mmw_decay_prm = 0;
   m.mmw_decay_updat_prm = 0;
 
-  /* Write all info to mmc */
+  /* Write all info to MMC */
   mmc_write(ioaddr, 0, (u_char *)&m, sizeof(m));
 
-  /* The following code start the modem of the 2.00 frequency
+  /* The following code starts the modem of the 2.00 frequency
    * selectable cards at power on. It's not strictly needed for the
-   * following boots...
+   * following boots.
    * The original patch was by Joe Finney for the PCMCIA driver, but
-   * I've cleaned it a bit and add documentation.
+   * I've cleaned it up a bit and added documentation.
    * Thanks to Loeke Brederveld from Lucent for the info.
    */
 
   /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
-   * (does it work for everybody ??? - especially old cards...) */
-  /* Note : WFREQSEL verify that it is able to read from EEprom
-   * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
-   * is 0xA (Xilinx version) or 0xB (Ariadne version).
-   * My test is more crude but do work... */
+   * (does it work for everybody? -- especially old cards?) */
+  /* Note: WFREQSEL verifies that it is able to read a sensible
+   * frequency from from EEPROM (address 0x00) and that
+   * MMR_FEE_STATUS_ID is 0xA (Xilinx version) or 0xB (Ariadne version).
+   * My test is more crude but does work. */
   if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
        (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
     {
       /* We must download the frequency parameters to the
-       * synthetisers (from the EEprom - area 1)
-       * Note : as the EEprom is auto decremented, we set the end
+       * synthesizers (from the EEPROM - area 1)
+       * Note : as the EEPROM is auto decremented, we set the end
        * if the area... */
       m.mmw_fee_addr = 0x0F;
       m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
@@ -2984,19 +2986,19 @@
       fee_wait(ioaddr, 100, 100);
 
 #ifdef DEBUG_CONFIG_INFO
-      /* The frequency was in the last word downloaded... */
+      /* The frequency was in the last word downloaded. */
       mmc_read(ioaddr, (char *)&m.mmw_fee_data_l - (char *)&m,
 	       (unsigned char *)&m.mmw_fee_data_l, 2);
 
-      /* Print some info for the user */
-      printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
+      /* Print some info for the user. */
+      printk(KERN_DEBUG "%s: WaveLAN 2.00 recognised (frequency select) : Current frequency = %ld\n",
 	     dev->name,
 	     ((m.mmw_fee_data_h << 4) |
 	      (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
 #endif
 
       /* We must now download the power adjust value (gain) to
-       * the synthetisers (from the EEprom - area 7 - DAC) */
+       * the synthesizers (from the EEPROM - area 7 - DAC) */
       m.mmw_fee_addr = 0x61;
       m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
       mmc_write(ioaddr, (char *)&m.mmw_fee_ctrl - (char *)&m,
@@ -3100,13 +3102,13 @@
  * Start the command unit executing the NOP
  * self-loop of the first transmit block.
  *
- * Here, we create the list of send buffer used to transmit packets
+ * Here, we create the list of send buffers used to transmit packets
  * between the PC and the command unit. For each buffer, we create a
  * buffer descriptor (pointing on the buffer), a transmit command
- * (pointing to the buffer descriptor) and a nop command.
- * The transmit command is linked to the nop, and the nop to itself.
- * When we will have finish to execute the transmit command, we will
- * then loop on the nop. By releasing the nop link to a new command,
+ * (pointing to the buffer descriptor) and a NOP command.
+ * The transmit command is linked to the NOP, and the NOP to itself.
+ * When we will have finished executing the transmit command, we will
+ * then loop on the NOP. By releasing the NOP link to a new command,
  * we may send another buffer.
  *
  * (called by wv_hw_reset())
@@ -3205,10 +3207,10 @@
 /*
  * This routine does a standard config of the WaveLAN controler (i82586).
  *
- * It initialise the scp, iscp and scb structure
- * The two first are only pointer to the next.
+ * It initialises the scp, iscp and scb structure
+ * The first two are just pointers to the next.
  * The last one is used for basic configuration and for basic
- * communication (interrupt status)
+ * communication (interrupt status).
  *
  * (called by wv_hw_reset())
  */
@@ -3249,7 +3251,7 @@
   iscp.iscp_offset = OFFSET_SCB;
   obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
 
-  /* Our first command is to reset the i82586 */
+  /* Our first command is to reset the i82586. */
   memset(&scb, 0x00, sizeof(scb));
   scb.scb_command = SCB_CMD_RESET;
   scb.scb_cbl_offset = OFFSET_CU;
@@ -3258,7 +3260,7 @@
 
   set_chan_attn(ioaddr, lp->hacr);
 
-  /* Wait for command to finish */
+  /* Wait for command to finish. */
   for(i = 1000; i > 0; i--)
     {
       obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, sizeof(iscp));
@@ -3300,7 +3302,7 @@
 
   wv_ack(dev);
 
-  /* Set the action command header */
+  /* Set the action command header. */
   memset(&cb, 0x00, sizeof(cb));
   cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
   cb.ac_link = OFFSET_CU;
@@ -3331,14 +3333,15 @@
 
 /*------------------------------------------------------------------*/
 /*
- * This routine does a standard config of the WaveLAN controler (i82586).
+ * This routine does a standard configuration of the WaveLAN controller
+ * (i82586).
  *
  * This routine is a violent hack. We use the first free transmit block
  * to make our configuration. In the buffer area, we create the three
- * configure command (linked). We make the previous nop point to the
- * beggining of the buffer instead of the tx command. After, we go as
- * usual to the nop command...
- * Note that only the last command (mc_set) will generate an interrupt...
+ * configuration commands (linked). We make the previous NOP point to
+ * the beginning of the buffer instead of the tx command. After, we go
+ * as usual to the NOP command.
+ * Note that only the last command (mc_set) will generate an interrupt.
  *
  * (called by wv_hw_reset(), wv_82586_reconfig())
  */
@@ -3380,16 +3383,16 @@
 
   lp->tx_n_in_use++;
 
-  /* Calculate addresses of the differents part of the block */
+  /* Calculate addresses of the different parts of the block. */
   tx_addr = txblock;
   nop_addr = tx_addr + sizeof(tx);
   tbd_addr = nop_addr + sizeof(nop);
-  cfg_addr = tbd_addr + sizeof(tbd_t);	/* beggining of the buffer */
+  cfg_addr = tbd_addr + sizeof(tbd_t);	/* beginning of the buffer */
   ias_addr = cfg_addr + sizeof(cfg);
   mcs_addr = ias_addr + sizeof(ias);
 
   /*
-   * Transmit command.
+   * Transmit command
    */
   tx.tx_h.ac_status = 0xFFFF;	/* Fake completion value */
   obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
@@ -3397,7 +3400,7 @@
 	      sizeof(tx.tx_h.ac_status));
 
   /*
-   * NOP command.
+   * NOP command
    */
   nop.nop_h.ac_status = 0;
   obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
@@ -3413,13 +3416,13 @@
 
 #if	0
   /*
-   * The default board configuration.
+   * The default board configuration
    */
   cfg.fifolim_bytecnt 	= 0x080c;
   cfg.addrlen_mode  	= 0x2600;
   cfg.linprio_interframe	= 0x7820;	/* IFS=120, ACS=2 */
   cfg.slot_time      	= 0xf00c;	/* slottime=12    */
-  cfg.hardware	     	= 0x0008;	/* tx even w/o CD */
+  cfg.hardware	     	= 0x0008;	/* tx even without CD */
   cfg.min_frame_len   	= 0x0040;
 #endif	/* 0 */
 
@@ -3535,8 +3538,8 @@
 
 /*------------------------------------------------------------------*/
 /*
- * This routine stop gracefully the WaveLAN controler (i82586).
- * (called by wavelan_close())
+ * This routine, called by wavelan_close(), gracefully stops the 
+ * WaveLAN controller (i82586).
  */
 static inline void
 wv_82586_stop(device *	dev)
@@ -3549,7 +3552,7 @@
   printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name);
 #endif
 
-  /* Suspend both command unit and receive unit */
+  /* Suspend both command unit and receive unit. */
   scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
   obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
 	      (unsigned char *)&scb_cmd, sizeof(scb_cmd));
@@ -3565,7 +3568,7 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Totally reset the wavelan and restart it.
+ * Totally reset the WaveLAN and restart it.
  * Performs the following actions:
  *	1. A power reset (reset DMA)
  *	2. Initialize the radio modem (using wv_mmc_init)
@@ -3584,7 +3587,7 @@
 	 (unsigned int)dev);
 #endif
 
-  /* If watchdog was activated, kill it ! */
+  /* If watchdog was activated, kill it! */
   if(lp->watchdog.prev != (timer_list *) NULL)
     del_timer(&lp->watchdog);
 
@@ -3617,8 +3620,8 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Check if there is a wavelan at the specific base address.
- * As a side effect, it read the MAC address.
+ * Check if there is a WaveLAN at the specific base address.
+ * As a side effect, this reads the MAC address.
  * (called in wavelan_probe() and init_module())
  */
 static int
@@ -3639,10 +3642,10 @@
 	   mac, 6);
 
   /*
-   * Check the first three octets of the addr for the manufacturer's code.
-   * Note: If you can't find your wavelan card, you've got a
-   * non-NCR/AT&T/Lucent ISA cards, see wavelan.p.h for detail on
-   * how to configure your card...
+   * Check the first three octets of the address for the manufacturer's code.
+   * Note: If this can't find your WaveLAN card, you've got a
+   * non-NCR/AT&T/Lucent ISA card.  See wavelan.p.h for details on
+   * how to configure your card.
    */
   for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
     if((mac[0] == MAC_ADDRESSES[i][0]) &&
@@ -3651,7 +3654,7 @@
       return 0;
 
 #ifdef DEBUG_CONFIG_INFO
-  printk(KERN_WARNING "Wavelan (0x%3X) : Your MAC address might be : %02X:%02X:%02X...\n",
+  printk(KERN_WARNING "WaveLAN (0x%3X): your MAC address might be: %02X:%02X:%02X.\n",
 	 ioaddr, mac[0], mac[1], mac[2]);
 #endif
     return ENODEV;
@@ -3691,7 +3694,7 @@
   lp = (net_local *) dev->priv;
   ioaddr = dev->base_addr;
 
-  /* Prevent reentrance. What should we do here ? */
+  /* Prevent reentrance. What should we do here? */
 #ifdef DEBUG_INTERRUPT_ERROR
   if(dev->interrupt)
     printk(KERN_INFO "%s: wavelan_interrupt(): Re-entering the interrupt handler.\n",
@@ -3724,7 +3727,7 @@
       return;
     }
 
-  /* Read interrupt data */
+  /* Read interrupt data. */
   obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
 	     (unsigned char *) &status, sizeof(status));
 
@@ -3771,7 +3774,7 @@
       wv_receive(dev);
     }
 
-  /* Check the state of the command unit */
+  /* Check the state of the command unit. */
   if(((status & SCB_ST_CNA) == SCB_ST_CNA) ||
      (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start))
     {
@@ -3782,7 +3785,7 @@
       wv_hw_reset(dev);
     }
 
-  /* Check the state of the command unit */
+  /* Check the state of the command unit. */
   if(((status & SCB_ST_RNR) == SCB_ST_RNR) ||
      (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start))
     {
@@ -3802,14 +3805,14 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Watchdog : when we start a transmission, we set a timer in the
- * kernel.  If the transmission complete, this timer is disabled. If
- * it expire, it try to unlock the hardware.
+ * Watchdog: when we start a transmission, we set a timer in the
+ * kernel.  If the transmission completes, this timer is disabled. If
+ * the timer expires, we try to unlock the hardware.
  *
- * Note : this watchdog doesn't work on the same principle as the
- * watchdog in the previous version of the ISA driver. I make it this
+ * Note: this watchdog doesn't work on the same principle as the
+ * watchdog in the previous version of the ISA driver. I made it this
  * way because the overhead of add_timer() and del_timer() is nothing
- * and that it avoid calling the watchdog, saving some CPU...
+ * and because it avoids calling the watchdog, saving some CPU time.
  */
 static void
 wavelan_watchdog(u_long		a)
@@ -3876,7 +3879,7 @@
       wv_hw_reset(dev);
     }
   else
-    /* Re-set watchodog for next transmission */
+    /* Reset watchdog for next transmission. */
     if(lp->tx_n_in_use > 0)
       {
 	/* set timer to expire in WATCHDOG_JIFFIES */
@@ -3893,9 +3896,9 @@
 
 /********************* CONFIGURATION CALLBACKS *********************/
 /*
- * Here are the functions called by the linux networking (NET3) for
- * initialization, configuration and deinstallations of the Wavelan
- * ISA Hardware.
+ * Here are the functions called by the Linux networking code (NET3)
+ * for initialization, configuration and deinstallations of the 
+ * WaveLAN ISA hardware.
  */
 
 /*------------------------------------------------------------------*/
@@ -3917,7 +3920,7 @@
   if(dev->irq == 0)
     {
 #ifdef DEBUG_CONFIG_ERRORS
-      printk(KERN_WARNING "%s: wavelan_open(): no irq\n", dev->name);
+      printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n", dev->name);
 #endif
       return -ENXIO;
     }
@@ -3929,7 +3932,7 @@
     {
       irq2dev_map[dev->irq] = (device *) NULL;
 #ifdef DEBUG_CONFIG_ERRORS
-      printk(KERN_WARNING "%s: wavelan_open(): invalid irq\n", dev->name);
+      printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", dev->name);
 #endif
       return -EAGAIN;
     }
@@ -3962,8 +3965,8 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Shutdown the WaveLAN ISA card.
- * Called by NET3 when it "close" the device.
+ * Shut down the WaveLAN ISA card.
+ * Called by NET3 when it "closes" the device.
  */
 static int
 wavelan_close(device *	dev)
@@ -3975,14 +3978,14 @@
 	 (unsigned int) dev);
 #endif
 
-  /* Not do the job twice... */
+  /* Not do the job twice. */
   if(dev->start == 0)
     return 0;
 
   dev->tbusy = 1;
   dev->start = 0;
 
-  /* If watchdog was activated, kill it ! */
+  /* If watchdog was activated, kill it! */
   if(lp->watchdog.prev != (timer_list *) NULL)
     del_timer(&lp->watchdog);
 
@@ -4004,7 +4007,7 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Probe an i/o address, and if the wavelan is there configure the
+ * Probe an I/O address, and if the WaveLAN is there configure the
  * device structure
  * (called by wavelan_probe() & via init_module())
  */
@@ -4071,9 +4074,9 @@
   memset(dev->priv, 0x00, sizeof(net_local));
   lp = (net_local *)dev->priv;
 
-  /* Back link to the device structure */
+  /* Back link to the device structure. */
   lp->dev = dev;
-  /* Add the device at the beggining of the linked list */
+  /* Add the device at the beginning of the linked list. */
   lp->next = wavelan_list;
   wavelan_list = lp;
 
@@ -4086,7 +4089,7 @@
 
   /*
    * Fill in the fields of the device structure
-   * with ethernet-generic values.
+   * with Ethernet-generic values.
    */
   ether_setup(dev);
 
@@ -4115,9 +4118,8 @@
 
 /*------------------------------------------------------------------*/
 /*
- * Check for a network adaptor of this type.
- * Return '0' iff one exists.
- * (There seem to be different interpretations of
+ * Check for a network adaptor of this type.  Return '0' iff one 
+ * exists.  (There seem to be different interpretations of
  * the initial value of dev->base_addr.
  * We follow the example in drivers/net/ne.c.)
  * (called in "Space.c")
@@ -4128,7 +4130,7 @@
 wavelan_probe(device *	dev)
 {
   short		base_addr;
-  mac_addr	mac;		/* Mac address (check wavelan existence) */
+  mac_addr	mac;		/* MAC address (check WaveLAN existence) */
   int		i;
   int		r;
 
@@ -4165,7 +4167,7 @@
       /* Check if the is something at this base address */
       if((r = wv_check_ioaddr(base_addr, mac)) == 0)
 	{
-	  memcpy(dev->dev_addr, mac, 6);	/* Copy mac address */
+	  memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address */
 	  r = wavelan_config(dev);
 	}
 
@@ -4181,14 +4183,14 @@
       return r;
     }
 
-  /* Scan all possible address of the wavelan hardware */
+  /* Scan all possible addresses of the WaveLAN hardware */
   for(i = 0; i < NELS(iobase); i++)
     {
-      /* Check if the is something at this base address */
+      /* Check whether there is something at this base address */
       if(wv_check_ioaddr(iobase[i], mac) == 0)
 	{
-	  dev->base_addr = iobase[i];		/* Copy base address */
-	  memcpy(dev->dev_addr, mac, 6);	/* Copy mac address */
+	  dev->base_addr = iobase[i];		/* Copy base address. */
+	  memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */
 	  if(wavelan_config(dev) == 0)
 	    {
 #ifdef DEBUG_CALLBACK_TRACE
@@ -4199,7 +4201,7 @@
 	}
     }
 
-  /* We may have touch base_addr : another driver may not like it... */
+  /* We may have touch base_addr: another driver may not like it. */
   dev->base_addr = base_addr;
 
 #ifdef DEBUG_CONFIG_INFO
@@ -4212,19 +4214,19 @@
 
 /****************************** MODULE ******************************/
 /*
- * Module entry point : insertion & removal
+ * Module entry point: insertion & removal
  */
 
 #ifdef	MODULE
 /*------------------------------------------------------------------*/
 /*
- * Insertion of the module...
- * I'm now quite proud of the multi-device support...
+ * Insertion of the module.
+ * I'm now quite proud of the multi-device support.
  */
 int
 init_module(void)
 {
-  mac_addr	mac;		/* Mac address (check wavelan existence) */
+  mac_addr	mac;		/* MAC address (check WaveLAN existence) */
   int		ret = 0;
   int		i;
 
@@ -4236,11 +4238,11 @@
   if(io[0] == 0)
     {
 #ifdef DEBUG_CONFIG_ERRORS
-      printk(KERN_WARNING "wavelan init_module(): doing device probing (bad !)\n");
+      printk(KERN_WARNING "WaveLAN init_module(): doing device probing (bad !)\n");
       printk(KERN_WARNING "Specify base addresses while loading module to correct the problem\n");
 #endif
 
-      /* Copy the basic set of address to be probed */
+      /* Copy the basic set of address to be probed. */
       for(i = 0; i < NELS(iobase); i++)
 	io[i] = iobase[i];
     }
@@ -4250,7 +4252,7 @@
   i = -1;
   while((io[++i] != 0) && (i < NELS(io)))
     {
-      /* Check if the is something at this base address */
+      /* Check if there is something at this base address. */
       if(wv_check_ioaddr(io[i], mac) == 0)
 	{
 	  device *	dev;
@@ -4262,7 +4264,7 @@
 	  dev->base_addr = io[i];
 	  dev->irq = irq[i];
 	  dev->init = &wavelan_config;
-	  memcpy(dev->dev_addr, mac, 6);	/* Copy mac address */
+	  memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address */
 
 	  /* Try to create the device */
 	  if(register_netdev(dev) != 0)
@@ -4272,12 +4274,12 @@
 	      kfree_s(dev, sizeof(struct device));
 	      ret = -EIO;
 	    }
-	}	/* If there is something at the address */
-    }		/* Loop on all addresses */
+	}	/* if there is something at the address */
+    }		/* Loop on all addresses. */
 
 #ifdef DEBUG_CONFIG_ERRORS
   if(wavelan_list == (net_local *) NULL)
-    printk(KERN_WARNING "wavelan init_module(): No device found\n");
+    printk(KERN_WARNING "WaveLAN init_module(): no device found\n");
 #endif
 
 #ifdef DEBUG_MODULE_TRACE
@@ -4297,7 +4299,7 @@
   printk(KERN_DEBUG "-> cleanup_module()\n");
 #endif
 
-  /* Loop on all devices and release them */
+  /* Loop on all devices and release them. */
   while(wavelan_list != (net_local *) NULL)
     {
       device *	dev = wavelan_list->dev;
@@ -4310,13 +4312,13 @@
       /* Release the ioport-region. */
       release_region(dev->base_addr, sizeof(ha_t));
 
-      /* Remove definitely the device */
+      /* Definitely remove the device. */
       unregister_netdev(dev);
 
-      /* Unlink the device */
+      /* Unlink the device. */
       wavelan_list = wavelan_list->next;
 
-      /* Free pieces */
+      /* Free pieces. */
       kfree_s(dev->priv, sizeof(struct net_local));
       kfree_s(dev, sizeof(struct device));
     }
diff -u --recursive --new-file v2.0.34/linux/drivers/net/wd.c linux/drivers/net/wd.c
--- v2.0.34/linux/drivers/net/wd.c	Thu Feb 29 21:50:49 1996
+++ linux/drivers/net/wd.c	Mon Jul 13 13:47:32 1998
@@ -54,7 +54,7 @@
 static void wd_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void wd_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, const int start_page);
 static int wd_close_card(struct device *dev);
 
 
diff -u --recursive --new-file v2.0.34/linux/drivers/net/yellowfin.c linux/drivers/net/yellowfin.c
--- v2.0.34/linux/drivers/net/yellowfin.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/yellowfin.c	Mon Jul 13 13:47:32 1998
@@ -38,7 +38,7 @@
 
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1518 effectively disables this feature. */
-static const rx_copybreak = 100;
+static const int rx_copybreak = 100;
 
 /* Keep the ring sizes a power of two for efficiency.
    Making the Tx ring too large decreases the effectiveness of channel
@@ -942,7 +942,7 @@
 	return;
 }
 
-/* This routine is logically part of the interrupt handler, but seperated
+/* This routine is logically part of the interrupt handler, but separated
    for clarity and better register allocation. */
 static int
 yellowfin_rx(struct device *dev)
diff -u --recursive --new-file v2.0.34/linux/drivers/sbus/char/suncons.c linux/drivers/sbus/char/suncons.c
--- v2.0.34/linux/drivers/sbus/char/suncons.c	Thu Apr 25 03:27:42 1996
+++ linux/drivers/sbus/char/suncons.c	Mon Jul 13 13:47:32 1998
@@ -769,7 +769,7 @@
 /*
  * Brooktree is the video dac and is funny to program on the cg6.
  * (it's even funnier on the cg3)
- * The FBC could be the the frame buffer control
+ * The FBC could be the frame buffer control
  * The FHC could be the frame buffer hardware control.
  */
 #define CG6_ROM_OFFSET       0x0
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c
--- v2.0.34/linux/drivers/scsi/AM53C974.c	Thu Feb 27 12:49:41 1997
+++ linux/drivers/scsi/AM53C974.c	Mon Jul 13 13:47:32 1998
@@ -2204,7 +2204,7 @@
 * 
 * Returns : status (SCSI_ABORT_SUCCESS)
 **************************************************************************/
-int AM53C974_reset(Scsi_Cmnd *cmd)
+int AM53C974_reset(Scsi_Cmnd *cmd, unsigned int flags)
 {
 AM53C974_local_declare();
 int                      i;
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/AM53C974.h linux/drivers/scsi/AM53C974.h
--- v2.0.34/linux/drivers/scsi/AM53C974.h	Tue Mar 11 15:40:02 1997
+++ linux/drivers/scsi/AM53C974.h	Mon Jul 13 13:47:32 1998
@@ -290,7 +290,7 @@
 int AM53C974_command(Scsi_Cmnd *SCpnt);
 int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
 int AM53C974_abort(Scsi_Cmnd *cmd);
-int AM53C974_reset (Scsi_Cmnd *cmd);
+int AM53C974_reset (Scsi_Cmnd *cmd, unsigned int flags);
 
 #define AM53C974_local_declare()	unsigned long io_port
 #define AM53C974_setio(instance)	io_port = instance->io_port
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.0.34/linux/drivers/scsi/ChangeLog.ncr53c8xx	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Mon Jul 13 13:47:32 1998
@@ -844,7 +844,7 @@
 	working conditions (3 commands per lun, FAST SCSI, no command queueing).
 	I get the stack overflow problem with the 2 drivers at the same 
 	frequency.
-	With only 2 commands per lun, I dont have the problem with any driver.
+	With only 2 commands per LUN, I don't have the problem with any driver.
 	It seems that the madness of recursion and the recent introduction of
 	the silly generic read function have broken performance and reliability 
 	of scsi drivers.
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/NCR53c406a.c linux/drivers/scsi/NCR53c406a.c
--- v2.0.34/linux/drivers/scsi/NCR53c406a.c	Thu Apr 11 23:49:40 1996
+++ linux/drivers/scsi/NCR53c406a.c	Mon Jul 13 13:47:32 1998
@@ -729,7 +729,7 @@
 }
 
 int 
-NCR53c406a_reset(Scsi_Cmnd *SCpnt){
+NCR53c406a_reset(Scsi_Cmnd *SCpnt, unsigned int flags){
     DEB(printk("NCR53c406a_reset called\n"));
     outb(C4_IMG, CONFIG4);      /* Select reg set 0 */
     outb(CHIP_RESET, CMD_REG);
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/NCR53c406a.h linux/drivers/scsi/NCR53c406a.h
--- v2.0.34/linux/drivers/scsi/NCR53c406a.h	Tue Feb 13 23:26:25 1996
+++ linux/drivers/scsi/NCR53c406a.h	Mon Jul 13 13:47:32 1998
@@ -57,7 +57,7 @@
 int NCR53c406a_command(Scsi_Cmnd *);
 int NCR53c406a_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int NCR53c406a_abort(Scsi_Cmnd *);
-int NCR53c406a_reset(Scsi_Cmnd *);
+int NCR53c406a_reset(Scsi_Cmnd *, unsigned int);
 int NCR53c406a_biosparm(Disk *, kdev_t, int []);
 
 #endif /* _NCR53C406A_H */
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx
--- v2.0.34/linux/drivers/scsi/README.aic7xxx	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/README.aic7xxx	Mon Jul 13 13:47:32 1998
@@ -134,6 +134,17 @@
         both channels to use the IRQ assigned to Channel A, 1 to force both
         channels to use the IRQ assigned to Channel B, and -1 will disable
         this horrible abomination of a hack.  The default is disabled (-1).
+    "aic7xxx=pci_parity:x" - This option controls whether or not the driver
+        enables PCI parity error checking on the PCI bus.  By default, this
+        checking is disabled.  To enable the checks, simply specify pci_parity
+        with no value afterwords.  To reverse the parity from even to odd,
+        supply any number other than 0 or 255.  In short:
+          pci_parity     - Even parity checking (even is the normal PCI parity)
+          pci_parity:x   - Where x > 0, Odd parity checking
+          pci_parity:0   - No check (default)
+        NOTE: In order to get Even PCI parity checking, you must use the
+        version of the option that does not include the : and a number at
+        the end (unless you want to enter exactly 2^32 - 1 as the number).
     "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable
         tagged queueing on specific devices.  As of driver version 5.0.6, we
         now globally enable tagged queueing by default, but we also disable
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c
--- v2.0.34/linux/drivers/scsi/advansys.c	Mon Jul 13 13:46:32 1998
+++ linux/drivers/scsi/advansys.c	Mon Jul 13 13:47:33 1998
@@ -1,5 +1,5 @@
-/* $Id: advansys.c,v 1.49 1998/01/22 20:19:25 bobf Exp bobf $ */
-#define ASC_VERSION "3.1D"    /* AdvanSys Driver Version */
+/* $Id: advansys.c,v 1.50 1998/05/08 23:39:15 bobf Exp bobf $ */
+#define ASC_VERSION "3.1E"    /* AdvanSys Driver Version */
 
 /*
  * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
@@ -571,7 +571,7 @@
             be page aligned.
 
      3.1C (1/10/98):
-         1. Update latest BIOS version to 3.1E.
+         1. Update latest BIOS version checked for from the /proc file.
          2. Don't set microcode SDTR variable at initialization. Instead
             wait until device capabilities have been detected from an Inquiry
             command.
@@ -580,6 +580,18 @@
          1. Improve performance when the driver is compiled as module by
             allowing up to 64 scatter-gather elements instead of 8.
 
+     3.1E (5/1/98):
+         1. Set time delay in AscWaitTixISRDone() to 1000 ms.
+         2. Include SMP locking changes.
+         3. For v2.1.93 and newer kernels use CONFIG_PCI and new PCI BIOS
+            access functions.
+         4. Update board serial number printing.
+         5. Try allocating an IRQ both with and without the SA_INTERRUPT
+            flag set to allow IRQ sharing with drivers that do not set
+            the SA_INTERRUPT flag. Also display a more descriptive error
+            message if request_irq() fails.
+         5. Update to latest Asc and Adv Libraries.
+
   J. Known Problems or Issues
 
          1. Remove conditional constants (ASC_QUEUE_FLOW_CONTROL) around
@@ -639,6 +651,7 @@
  * --- Linux Include Files 
  */
 
+#include <linux/config.h>
 #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
 #ifdef MODULE
 #include <linux/module.h>
@@ -668,10 +681,25 @@
 #include <linux/blk.h>
 #include <linux/stat.h>
 #endif /* version >= v1.3.0 */
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,95)
+#include <asm/spinlock.h>
+#endif /* version >= 2.1.95 */
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
 #include "advansys.h"
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,93)
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif /* CONFIG_PCI */
+#else /* version < v2.1.93 */
+/*
+ * For earlier than v2.1.93 the driver has its own PCI configuration.
+ * If PCI is not needed in a kernel before v2.1.93 this define can be
+ * turned-off to make the driver object smaller.
+ */
+#define ASC_CONFIG_PCI
+#endif /* version < v2.1.93 */
 
 /*
  * If Linux eventually defines a DID_UNDERRUN, the constant here can be
@@ -689,7 +717,7 @@
 #define ADVANSYS_ASSERT
 
 /* Enable driver tracing. */
-/*#define ADVANSYS_DEBUG*/
+/* #define ADVANSYS_DEBUG */
 
 /*
  * Because of no /proc to display them, statistics are disabled
@@ -719,7 +747,7 @@
 
 #define ASC_LIB_VERSION_MAJOR  1
 #define ASC_LIB_VERSION_MINOR  22
-#define ASC_LIB_SERIAL_NUMBER  111
+#define ASC_LIB_SERIAL_NUMBER  113
 
 typedef unsigned char uchar;
 
@@ -1484,7 +1512,7 @@
     ASC_SCSI_BIT_ID_TYPE can_tagged_qng;
     ASC_SCSI_BIT_ID_TYPE cmd_qng_enabled;
     ASC_SCSI_BIT_ID_TYPE disc_enable;
-    uchar               res;
+    ASC_SCSI_BIT_ID_TYPE sdtr_enable;
     uchar               chip_scsi_id:4;
     uchar               isa_dma_speed:4;
     uchar               isa_dma_channel;
@@ -1947,8 +1975,6 @@
 STATIC ushort    AscInitFromEEP(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitFromAscDvcVar(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitMicroCodeVar(ASC_DVC_VAR asc_ptr_type * asc_dvc);
-STATIC void      AscInitPollIsrCallBack(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_QDONE_INFO *);
 STATIC int       AscTestExternalLram(ASC_DVC_VAR asc_ptr_type *);
 STATIC uchar     AscMsgOutSDTR(ASC_DVC_VAR asc_ptr_type *, uchar, uchar);
 STATIC uchar     AscCalSDTRData(ASC_DVC_VAR asc_ptr_type *, uchar, uchar);
@@ -1991,31 +2017,6 @@
 STATIC uchar     _AscCopyLramScsiDoneQ(PortAddr, ushort,
                     ASC_QDONE_INFO *, ulong);
 STATIC int       AscIsrQDone(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscScsiSetupCmdQ(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *, ulong);
-STATIC int       AscScsiInquiry(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *, int);
-STATIC int       AscScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       AscScsiStartStopUnit(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar);
-STATIC int       AscScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *);
-STATIC ulong     *swapfarbuf4(uchar *);
-STATIC int       PollQueueDone(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, int);
-STATIC int       PollScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, ASC_CAP_INFO *);
-STATIC int       PollScsiInquiry(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, uchar *, int);
-STATIC int       PollScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       PollScsiStartUnit(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       InitTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *);
-STATIC int       AscPollQDone(ASC_DVC_VAR asc_ptr_type *,
-                    ASC_SCSI_REQ_Q *, int);
 STATIC int       AscCompareString(uchar *, uchar *, int);
 STATIC ushort    AscGetEisaChipCfg(PortAddr);
 STATIC ulong     AscGetEisaProductID(PortAddr);
@@ -2051,10 +2052,11 @@
 STATIC ushort    AscInitGetConfig(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitSetConfig(ASC_DVC_VAR asc_ptr_type *);
 STATIC ushort    AscInitAsc1000Driver(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscInitPollBegin(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscInitPollEnd(ASC_DVC_VAR asc_ptr_type *);
-STATIC int       AscInitPollTarget(ASC_DVC_VAR asc_ptr_type *,
-                     ASC_SCSI_REQ_Q *, ASC_SCSI_INQUIRY *, ASC_CAP_INFO *);
+STATIC void      AscAsyncFix(ASC_DVC_VAR asc_ptr_type *, uchar,
+                    ASC_SCSI_INQUIRY *);
+STATIC int       AscTagQueuingSafe(ASC_SCSI_INQUIRY *);
+STATIC void      AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *,
+                    uchar, ASC_SCSI_INQUIRY *);
 STATIC int       AscExeScsiQueue(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q *);
 STATIC int       AscISR(ASC_DVC_VAR asc_ptr_type *);
 STATIC uint      AscGetNumOfFreeQueue(ASC_DVC_VAR asc_ptr_type *, uchar,
@@ -2074,7 +2076,7 @@
  */
 
 #define ADV_LIB_VERSION_MAJOR  3
-#define ADV_LIB_VERSION_MINOR  34
+#define ADV_LIB_VERSION_MINOR  45
 
 /* d_os_dep.h */
 #define ADV_OS_LINUX
@@ -3145,10 +3147,6 @@
 #define ADV_IS_DATA_FLAG        0x08 /* 'addr' is data virtual pointer */
 #define ADV_IS_SGLIST_FLAG      0x10 /* 'addr' is sglist virtual pointer */
 
-/* 'IS_SCSIQ_FLAG is now obsolete; Instead use ADV_IS_SCSIQ_FLAG. */
-#define IS_SCSIQ_FLAG           ADV_IS_SCSIQ_FLAQ
-
-
 /* Return the address that is aligned at the next doubleword >= to 'addr'. */
 #define ADV_DWALIGN(addr)       (((ulong) (addr) + 0x3) & ~0x3)
 
@@ -3355,6 +3353,7 @@
 #define PCI_MAX_BUS             0xFF
 #define PCI_IOADDRESS_MASK      0xFFFE
 #define ASC_PCI_VENDORID        0x10CD
+#define ASC_PCI_DEVICE_ID_CNT   4       /* PCI Device ID count. */
 #define ASC_PCI_DEVICE_ID_1100  0x1100
 #define ASC_PCI_DEVICE_ID_1200  0x1200
 #define ASC_PCI_DEVICE_ID_1300  0x1300
@@ -3792,7 +3791,11 @@
     ASC_IS_PCI,
 };
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
 STATIC int pci_scan_method ASC_INITDATA = -1;
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
 
 /*
  * Used with the LILO 'advansys' option to eliminate or
@@ -3854,7 +3857,8 @@
 STATIC int        adv_get_sglist(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *, Scsi_Cmnd *);
 STATIC void       asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *);
 STATIC void       adv_isr_callback(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
-STATIC int        asc_init_dev(ASC_DVC_VAR *, Scsi_Cmnd *);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
 STATIC int        asc_srch_pci_dev(PCI_DEVICE *);
 STATIC uchar      asc_scan_method(void);
 STATIC int        asc_pci_find_dev(PCI_DEVICE *);
@@ -3862,6 +3866,8 @@
 STATIC ushort     asc_get_cfg_word(PCI_DATA *);
 STATIC uchar      asc_get_cfg_byte(PCI_DATA *);
 STATIC void       asc_put_cfg_byte(PCI_DATA *, uchar);
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
 STATIC void       asc_enqueue(asc_queue_t *, REQP, int);
 STATIC REQP       asc_dequeue(asc_queue_t *, int);
 STATIC REQP       asc_dequeue_list(asc_queue_t *, REQP *, int);
@@ -4172,8 +4178,27 @@
     ADV_DVC_VAR         *adv_dvc_varp = NULL;
     int                 ioport = 0;
     int                 share_irq = FALSE;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DEVICE          pciDevice;
     PCI_CONFIG_SPACE    pciConfig;
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+    unsigned long       pci_memory_address;
+#endif /* version >= v1,3,0 */
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    struct pci_dev      *pci_devp = NULL;
+    int                 pci_device_id_cnt = 0;
+    unsigned int        pci_device_id[ASC_PCI_DEVICE_ID_CNT] = {
+                                    ASC_PCI_DEVICE_ID_1100,
+                                    ASC_PCI_DEVICE_ID_1200,
+                                    ASC_PCI_DEVICE_ID_1300,
+                                    ASC_PCI_DEVICE_ID_2300
+                        };
+    unsigned long       pci_memory_address;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
     int                 warn_code, err_code;
     int                 ret;
 
@@ -4217,10 +4242,14 @@
         ioport = 0;
     }
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     memset(&pciDevice, 0, sizeof(PCI_DEVICE));
     memset(&pciConfig, 0, sizeof(PCI_CONFIG_SPACE));
     pciDevice.maxBusNumber = PCI_MAX_BUS;
     pciDevice.endSlot = PCI_MAX_SLOT;
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */ 
 
     for (bus = 0; bus < ASC_NUM_BUS; bus++) {
 
@@ -4301,22 +4330,52 @@
                 iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
                 break;
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+            case ASC_IS_PCI:
+                if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) {
+                    iop = 0;
+                } else {
+                    ASC_DBG2(2,
+                        "advansys_detect: slotFound %d, busNumber %d\n",
+                        pciDevice.slotFound, pciDevice.busNumber);
+                    asc_get_pci_cfg(&pciDevice, &pciConfig);
+                    iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK;
+                    ASC_DBG2(1,
+                        "advansys_detect: vendorID %X, deviceID %X\n",
+                        pciConfig.vendorID, pciConfig.deviceID);
+                    ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
+                        iop, pciConfig.irqLine);
+                }
+                break;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
             case ASC_IS_PCI:
-                    if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) {
-                        iop = 0;
+                while (pci_device_id_cnt < ASC_PCI_DEVICE_ID_CNT) {
+                    if ((pci_devp = pci_find_device(ASC_PCI_VENDORID,
+                         pci_device_id[pci_device_id_cnt], pci_devp)) == NULL) {
+                        pci_device_id_cnt++;
                     } else {
-                        ASC_DBG2(2,
-                            "advansys_detect: slotFound %d, busNumber %d\n",
-                            pciDevice.slotFound, pciDevice.busNumber);
-                        asc_get_pci_cfg(&pciDevice, &pciConfig);
-                        iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK;
-                        ASC_DBG2(1,
-                            "advansys_detect: vendorID %X, deviceID %X\n",
-                            pciConfig.vendorID, pciConfig.deviceID);
-                        ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
-                            iop, pciConfig.irqLine);
+                        break;
                     }
+                }
+                if (pci_devp == NULL) {
+                    iop = 0;
+                } else {
+                    ASC_DBG2(2,
+                        "advansys_detect: devfn %d, bus number %d\n",
+                        pci_devp->devfn, pci_devp->bus->number);
+                    iop = pci_devp->base_address[0] & PCI_IOADDRESS_MASK;
+                    ASC_DBG2(1,
+                        "advansys_detect: vendorID %X, deviceID %X\n",
+                        pci_devp->vendor, pci_devp->device);
+                    ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
+                        iop, pci_devp->irq);
+                }
                 break;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 
             default:
                 ASC_PRINT1("advansys_detect: unknown bus type: %d\n",
@@ -4350,14 +4409,29 @@
             boardp->id = asc_board_count - 1;
 
             /*
-             * Handle both narrow and wide PCI boards.
+             * Handle both narrow and wide boards.
              *
              * If a Wide board was detected, set the board structure
              * wide board flag. Set-up the board structure based on
              * the board type.
              */
-            if ((asc_bus[bus] == ASC_IS_PCI &&
-                 pciConfig.deviceID == ASC_PCI_DEVICE_ID_2300) == 0) {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+            if (asc_bus[bus] == ASC_IS_PCI &&
+                 pciConfig.deviceID == ASC_PCI_DEVICE_ID_2300) {
+                boardp->flags |= ASC_IS_WIDE_BOARD;
+            }
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+            if (asc_bus[bus] == ASC_IS_PCI &&
+                 pci_devp->device == ASC_PCI_DEVICE_ID_2300) {
+                boardp->flags |= ASC_IS_WIDE_BOARD;
+            }
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
+
+            if (ASC_NARROW_BOARD(boardp)) {
                 ASC_DBG(1, "advansys_detect: narrow board\n");
                 asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
                 asc_dvc_varp->bus_type = asc_bus[bus];
@@ -4368,7 +4442,6 @@
                 asc_dvc_varp->isr_callback = (Ptr2Func) asc_isr_callback;
             } else {
                 ASC_DBG(1, "advansys_detect: wide board\n");
-                boardp->flags |= ASC_IS_WIDE_BOARD;
                 adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
                 adv_dvc_varp->drv_ptr = (ulong) boardp;
                 adv_dvc_varp->cfg = &boardp->dvc_cfg.adv_dvc_cfg;
@@ -4388,20 +4461,41 @@
                  * PCI register base address will not cross a page
                  * boundary.
                  */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+                pci_memory_address = pciConfig.baseAddress[1];
+                if ((boardp->ioremap_addr =
+                    ioremap(pci_memory_address & PAGE_MASK,
+                         PAGE_SIZE)) == 0) {
+                   ASC_PRINT3(
+"advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n",
+                   boardp->id, pci_memory_address, ADV_CONDOR_IOLEN);
+                   scsi_unregister(shp);
+                   asc_board_count--;
+                   continue;
+                }
+                adv_dvc_varp->iop_base = (AdvPortAddr)
+                    (boardp->ioremap_addr +
+                     (pci_memory_address - (pci_memory_address & PAGE_MASK)));
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+                pci_memory_address = pci_devp->base_address[1];
                 if ((boardp->ioremap_addr =
-                    ioremap(pciConfig.baseAddress[1] & PAGE_MASK,
+                    ioremap(pci_memory_address & PAGE_MASK,
                          PAGE_SIZE)) == 0) {
                    ASC_PRINT3(
 "advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n",
-                   boardp->id, pciConfig.baseAddress[1], ADV_CONDOR_IOLEN);
+                   boardp->id, pci_memory_address, ADV_CONDOR_IOLEN);
                    scsi_unregister(shp);
                    asc_board_count--;
                    continue;
                 }
                 adv_dvc_varp->iop_base = (AdvPortAddr)
                     (boardp->ioremap_addr +
-                     (pciConfig.baseAddress[1] -
-                      (pciConfig.baseAddress[1] & PAGE_MASK)));
+                     (pci_memory_address - (pci_memory_address & PAGE_MASK)));
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 #endif /* version >= v1,3,0 */
 
                 /*
@@ -4447,16 +4541,33 @@
                     shp->unchecked_isa_dma = FALSE;
                     share_irq = TRUE;
                     break;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
                 case ASC_IS_PCI:
                     shp->irq = asc_dvc_varp->irq_no = pciConfig.irqLine;
-                    shp->unchecked_isa_dma = FALSE;
-                    share_irq = TRUE;
                     asc_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
                     asc_dvc_varp->cfg->pci_slot_info =
                         ASC_PCI_MKID(pciDevice.busNumber,
                             pciDevice.slotFound,
                             pciDevice.devFunc);
+                    shp->unchecked_isa_dma = FALSE;
+                    share_irq = TRUE;
                     break;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+                case ASC_IS_PCI:
+                    shp->irq = asc_dvc_varp->irq_no = pci_devp->irq;
+                    asc_dvc_varp->cfg->pci_device_id = pci_devp->device;
+                    asc_dvc_varp->cfg->pci_slot_info =
+                        ASC_PCI_MKID(pci_devp->bus->number,
+                            PCI_SLOT(pci_devp->devfn),
+                            PCI_FUNC(pci_devp->devfn));
+                    shp->unchecked_isa_dma = FALSE;
+                    share_irq = TRUE;
+                    break;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
                 default:
                     ASC_PRINT2(
 "advansys_detect: board %d: unknown adapter type: %d\n",
@@ -4470,14 +4581,29 @@
                  * For Wide boards set PCI information before calling
                  * AdvInitGetConfig().
                  */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
                 shp->irq = adv_dvc_varp->irq_no = pciConfig.irqLine;
+                adv_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
+                adv_dvc_varp->cfg->pci_slot_info =
+                ASC_PCI_MKID(pciDevice.busNumber,
+                    pciDevice.slotFound,
+                    pciDevice.devFunc);
                 shp->unchecked_isa_dma = FALSE;
                 share_irq = TRUE;
-                adv_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+                shp->irq = adv_dvc_varp->irq_no = pci_devp->irq;
+                adv_dvc_varp->cfg->pci_device_id = pci_devp->device;
                 adv_dvc_varp->cfg->pci_slot_info =
-                    ASC_PCI_MKID(pciDevice.busNumber,
-                        pciDevice.slotFound,
-                        pciDevice.devFunc);
+                ASC_PCI_MKID(pci_devp->bus->number,
+                    PCI_SLOT(pci_devp->devfn),
+                    PCI_FUNC(pci_devp->devfn));
+                shp->unchecked_isa_dma = FALSE;
+                share_irq = TRUE;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
             }
 
             /*
@@ -4572,7 +4698,7 @@
                  */
                 ep = &boardp->eep_config.asc_eep;
 
-                ep->init_sdtr = asc_dvc_varp->init_sdtr;
+                ep->init_sdtr = asc_dvc_varp->cfg->sdtr_enable;
                 ep->disc_enable = asc_dvc_varp->cfg->disc_enable;
                 ep->use_cmd_qng = asc_dvc_varp->cfg->cmd_qng_enabled;
                 ep->isa_dma_speed = asc_dvc_varp->cfg->isa_dma_speed;
@@ -4879,14 +5005,36 @@
             if ((ret = request_irq(shp->irq, advansys_interrupt,
                             SA_INTERRUPT, "advansys")) != 0)
 #else /* version >= v1.3.70 */
-            if ((ret = request_irq(shp->irq, advansys_interrupt,
+           /*
+            * If request_irq() fails with the SA_INTERRUPT flag set,
+            * then try again without the SA_INTERRUPT flag set. This
+            * allows IRQ sharing to work even with other drivers that
+            * do not set the SA_INTERRUPT flag.
+            *
+            * If SA_INTERRUPT is not set, then interrupts are enabled
+            * before the driver interrupt function is called.
+            */
+            if (((ret = request_irq(shp->irq, advansys_interrupt,
                             SA_INTERRUPT | (share_irq == TRUE ? SA_SHIRQ : 0),
-                            "advansys", boardp)) != 0)
+                            "advansys", boardp)) != 0) &&
+                ((ret = request_irq(shp->irq, advansys_interrupt,
+                            (share_irq == TRUE ? SA_SHIRQ : 0),
+                            "advansys", boardp)) != 0))
 #endif /* version >= v1.3.70 */
             {
-                ASC_PRINT2(
-"advansys_detect: board %d: request_irq() failed %d\n",
-                    boardp->id, ret);
+                if (ret == -EBUSY) {
+                    ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ %d already in use.\n",
+                        boardp->id, shp->irq);
+                } else if (ret == -EINVAL) {
+                    ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ %d not valid.\n",
+                        boardp->id, shp->irq);
+                } else {
+                    ASC_PRINT3(
+"advansys_detect: board %d: request_irq(): IRQ %d failed with %d\n",
+                        boardp->id, shp->irq, ret);
+                }
                 release_region(shp->io_port, shp->n_io_port);
 #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
                 iounmap(boardp->ioremap_addr);
@@ -5140,7 +5288,11 @@
                 busname = "ISA";
             }
             sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
 "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u, DMA %u",
+#else /* version >= v2.1.92 */ 
+"AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u, DMA %u",
+#endif /* version >= v2.1.92 */ 
                 ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
                 (unsigned) shp->base,
                 shp->io_port, shp->n_io_port - 1,
@@ -5153,7 +5305,11 @@
                 busname = "PCI";
             }
             sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
                 "AdvanSys SCSI %s: %s %u CDB: IO %X/%X, IRQ %u",
+#else /* version >= v2.1.92 */ 
+                "AdvanSys SCSI %s: %s %u CDB: IO %lX/%X, IRQ %u",
+#endif /* version >= v2.1.92 */ 
                 ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
                 shp->io_port, shp->n_io_port - 1, shp->irq);
         } else {
@@ -5168,7 +5324,11 @@
                     boardp->id, asc_dvc_varp->bus_type);
             }
             sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
                 "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u",
+#else /* version >= v2.1.92 */ 
+                "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u",
+#endif /* version >= v2.1.92 */ 
                 ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
                 (unsigned) shp->base, shp->io_port - 1,
                 shp->n_io_port, shp->irq);
@@ -5718,13 +5878,6 @@
                 status = AscResetDevice(asc_dvc_varp, scp->target);
                 cli();
 
-                /*
-                 * If the device has been reset, try to initialize it.
-                 */
-                if (status == ASC_TRUE) {
-                    status = asc_init_dev(asc_dvc_varp, scp);
-                }
-
                 switch (status) {
                 case ASC_TRUE:
                     ASC_DBG(1, "advansys_reset: AscResetDevice() success\n");
@@ -5945,7 +6098,7 @@
         asc_scsi_done_list(done_scp);
     }
 
-    ASC_DBG1(1, "advansys_reset: ret %d", ret);
+    ASC_DBG1(1, "advansys_reset: ret %d\n", ret);
 
     /* Re-enable interrupts, if they were enabled on entry. */
     restore_flags(flags);
@@ -6109,15 +6262,27 @@
 advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 #endif /* version >= v1.3.70 */
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
     int             flags;
+#else /* version >= v2.1.95 */
+    unsigned long   flags;
+#endif /* version >= v2.1.95 */
     int             i;
     asc_board_t     *boardp;
     Scsi_Cmnd       *done_scp = NULL, *last_scp = NULL;
     Scsi_Cmnd       *new_last_scp;
 
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
     /* Disable interrupts, if they aren't already disabled. */
     save_flags(flags);
     cli();
+#else /* version >= v2.1.95 */
+    /*
+     * Disable interrupts, if they aren't already disabled and acquire
+     * the I/O spinlock.
+     */
+    spin_lock_irqsave(&io_request_lock, flags);
+#endif /* version >= v2.1.95 */
 
     ASC_DBG(1, "advansys_interrupt: begin\n");
 
@@ -6191,13 +6356,26 @@
 
     /*
      * It is possible for the request done function to re-enable
-     * interrupts without confusing the driver. But here interrupts
-     * aren't enabled until all requests have been completed.
+     * interrupts without confusing the driver. But here the
+     * original flags aren't restored until all requests have been
+     * completed.
      */
     asc_scsi_done_list(done_scp);
 
-    /* Re-enable interrupts, if they were enabled on entry. */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
+    /*
+     * Restore the original flags which will enable interrupts
+     * if and only if they were enabled on entry.
+     */
     restore_flags(flags);
+#else /* version >= v2.1.95 */
+    /*
+     * Release the I/O spinlock and restore the original flags
+     * which will enable interrupts if and only if they were
+     * enabled on entry.
+     */
+    spin_unlock_irqrestore(&io_request_lock, flags);
+#endif /* version >= v2.1.95 */
 
     ASC_DBG(1, "advansys_interrupt: end\n");
     return;
@@ -6342,21 +6520,6 @@
         asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
 
         /*
-         * Narrow Board - Asc Library requires special device initialization.
-         *
-         * If this is the first command, then initialize the device. If
-         * no device is found set 'DID_BAD_TARGET' and return.
-         */
-        if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0) {
-            if (asc_init_dev(asc_dvc_varp, scp) == ASC_FALSE) {
-                scp->result = HOST_BYTE(DID_BAD_TARGET);
-                asc_enqueue(&boardp->done, scp, ASC_BACK);
-                return ASC_ERROR;
-            }
-            boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target);
-        }
-
-        /*
          * Build Asc Library request structure using the
          * global structures 'asc_scsi_req' and 'asc_sg_head'.
          *
@@ -6901,7 +7064,7 @@
     asc_board_t         *boardp;
     Scsi_Cmnd           *scp;
     struct Scsi_Host    *shp;
-    int            underrun = ASC_FALSE;
+    int                 underrun = ASC_FALSE;
     int                 i;
 
     ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
@@ -6947,6 +7110,7 @@
      * Display a message and return.
      */
     boardp = ASC_BOARDP(shp);
+    ASC_ASSERT(asc_dvc_varp == &boardp->dvc_var.asc_dvc_var);
     if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
         ASC_PRINT2("asc_isr_callback: board %d: scp %x not on active queue\n",
             boardp->id, (unsigned) scp);
@@ -6957,7 +7121,7 @@
      * Check for an underrun condition.
      */
     if (scp->request_bufflen != 0 && qdonep->remain_bytes != 0 && 
-    qdonep->remain_bytes <= scp->request_bufflen != 0) {
+        qdonep->remain_bytes <= scp->request_bufflen != 0) {
         ASC_DBG1(1, "asc_isr_callback: underrun condition %u bytes\n",
         (unsigned) qdonep->remain_bytes);
         underrun = ASC_TRUE;
@@ -6978,13 +7142,28 @@
             scp->result = HOST_BYTE(DID_ERROR);
             break;
         }
-    /*
-     * If there was an underrun without any other error,
-     * set DID_ERROR to indicate the underrun error.
-     */
-    if (scp->result == 0 && underrun == ASC_TRUE) {
+
+        /*
+         * If an INQUIRY command completed successfully, then call
+         * the AscInquiryHandling() function to set-up the device.
+         */
+        if (scp->cmnd[0] == SCSICMD_Inquiry && scp->lun == 0 &&
+            (scp->request_bufflen - qdonep->remain_bytes) >= 8)
+        {
+            AscInquiryHandling(asc_dvc_varp, scp->target & 0x7,
+                (ASC_SCSI_INQUIRY *) scp->request_buffer);
+        }
+
+        /*
+         * If there was an underrun without any other error,
+         * set DID_ERROR to indicate the underrun error.
+         *
+         * Note: There is no way yet to indicate the number
+         * of underrun bytes.
+         */
+        if (scp->result == 0 && underrun == ASC_TRUE) {
             scp->result = HOST_BYTE(DID_UNDERRUN);
-    }
+        }
         break;
 
     case QD_WITH_ERROR:
@@ -7014,10 +7193,9 @@
 
         default:
             /* QHSTA error occurred */
-            ASC_DBG1(2, "asc_isr_callback: host_stat %x\n",
+            ASC_DBG1(1, "asc_isr_callback: host_stat %x\n",
                 qdonep->d3.host_stat);
-            scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
-                STATUS_BYTE(qdonep->d3.scsi_stat);
+            scp->result = HOST_BYTE(DID_BAD_TARGET);
             break;
         }
         break;
@@ -7029,12 +7207,23 @@
         break;
 
     default:
-        ASC_PRINT1("asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat);
+        ASC_DBG1(1, "asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat);
         scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
                 STATUS_BYTE(qdonep->d3.scsi_stat);
         break;
     }
 
+    /*
+     * If the 'init_tidmask' bit isn't already set for the target and the
+     * current request finished normally, then set the bit for the target
+     * to indicate that a device is present.
+     */
+    if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 &&
+        qdonep->d3.done_stat == QD_NO_ERROR &&
+        qdonep->d3.host_stat == QHSTA_NO_ERROR) {
+        boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target);
+    }
+
     /* 
      * Because interrupts may be enabled by the 'Scsi_Cmnd' done
      * function, add the command to the end of the board's done queue.
@@ -7058,7 +7247,7 @@
     adv_req_t           *reqp;
     Scsi_Cmnd           *scp;
     struct Scsi_Host    *shp;
-    int            underrun = ASC_FALSE;
+    int                 underrun = ASC_FALSE;
     int                 i;
 
     ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
@@ -7126,6 +7315,7 @@
      * to free the adv_req_t and adv_sgblk_t, if any, structures.
      */
     boardp = ASC_BOARDP(shp);
+    ASC_ASSERT(adv_dvc_varp == &boardp->dvc_var.adv_dvc_var);
     if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
         ASC_PRINT2("adv_isr_callback: board %d: scp %x not on active queue\n",
             boardp->id, (unsigned) scp);
@@ -7158,13 +7348,16 @@
             scp->result = HOST_BYTE(DID_ERROR);
             break;
         }
-    /*
-     * If there was an underrun without any other error,
-     * set DID_ERROR to indicate the underrun error.
-     */
-    if (scp->result == 0 && underrun == ASC_TRUE) {
-            scp->result = HOST_BYTE(DID_UNDERRUN);
-    }
+        /*
+         * If there was an underrun without any other error,
+         * set DID_ERROR to indicate the underrun error.
+         *
+         * Note: There is no way yet to indicate the number
+         * of underrun bytes.
+         */
+        if (scp->result == 0 && underrun == ASC_TRUE) {
+                scp->result = HOST_BYTE(DID_UNDERRUN);
+        }
         break;
 
     case QD_WITH_ERROR:
@@ -7194,7 +7387,7 @@
 
         default:
             /* Some other QHSTA error occurred. */
-            ASC_DBG1(2, "adv_isr_callback: host_status %x\n",
+            ASC_DBG1(1, "adv_isr_callback: host_status %x\n",
                 scsiqp->host_status);
             scp->result = HOST_BYTE(DID_BAD_TARGET);
             break;
@@ -7207,15 +7400,15 @@
         break;
 
     default:
-        ASC_PRINT1("adv_isr_callback: done_status %x\n", scsiqp->done_status);
+        ASC_DBG1(1, "adv_isr_callback: done_status %x\n", scsiqp->done_status);
         scp->result = HOST_BYTE(DID_ERROR) | STATUS_BYTE(scsiqp->scsi_status);
         break;
     }
 
     /*
      * If the 'init_tidmask' bit isn't already set for the target and the
-     * current request did not finish with a Selection Timeout, then set
-     * the bit for the target to indicate that a device is present.
+     * current request finished normally, then set the bit for the target
+     * to indicate that a device is present.
      */
     if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 &&
         scsiqp->done_status == QD_NO_ERROR &&
@@ -7252,119 +7445,8 @@
     return;
 }
 
-/*
- * asc_init_dev() - Narrow Board initialization function. 
- *
- * Perform one-time initialization of a device for Asc Library
- */
-STATIC int
-asc_init_dev(ASC_DVC_VAR *asc_dvc_varp, Scsi_Cmnd *scp)
-{
-    asc_board_t             *boardp;
-    ASC_SCSI_REQ_Q          *scsireqq;
-    ASC_CAP_INFO            *cap_info;
-    ASC_SCSI_INQUIRY        *inquiry;
-    int                     found;
-    ASC_SCSI_BIT_ID_TYPE    save_use_tagged_qng;
-    ASC_SCSI_BIT_ID_TYPE    save_can_tagged_qng;
-    int                     ret;
-#ifdef ADVANSYS_DEBUG
-    ASC_SCSI_BIT_ID_TYPE    tidmask; /* target id bit mask: 1 - 128 */
-#endif /* ADVANSYS_DEBUG */
-
-    ASC_DBG1(1, "asc_init_dev: target %d\n", (unsigned) scp->target);
-
-    /* The host's target id is set in init_tidmask during initialization. */
-    ASC_ASSERT(asc_dvc_varp->cfg->chip_scsi_id != scp->target);
-
-    boardp = ASC_BOARDP(scp->host);
-
-    /* Set-up AscInitPollTarget() arguments. */
-    scsireqq = &boardp->scsireqq;
-    memset(scsireqq, 0, sizeof(ASC_SCSI_REQ_Q));
-    cap_info = &boardp->cap_info;
-    memset(cap_info, 0, sizeof(ASC_CAP_INFO));
-    inquiry = &boardp->inquiry;
-    memset(inquiry, 0, sizeof(ASC_SCSI_INQUIRY));
-
-    /*
-     * AscInitPollBegin() re-initializes these bitmask fields to zero.
-     * Save the current bitmask value and 'or' them back in after calling
-     * AscInitPollEnd() below..
-     */
-    save_use_tagged_qng = asc_dvc_varp->use_tagged_qng;
-    save_can_tagged_qng = asc_dvc_varp->cfg->can_tagged_qng;
-
-    ASC_DBG(2, "asc_init_dev: AscInitPollBegin()\n");
-    if (AscInitPollBegin(asc_dvc_varp)) {
-        ASC_PRINT1("asc_init_dev: board %d: AscInitPollBegin() failed\n",
-            boardp->id);
-        return ASC_FALSE;
-    }
-
-    scsireqq->sense_ptr = &scsireqq->sense[0];
-    scsireqq->r1.sense_len = ASC_MIN_SENSE_LEN;
-    scsireqq->r1.target_id = ASC_TID_TO_TARGET_ID(scp->target);
-    scsireqq->r1.target_lun = 0;
-    scsireqq->r2.target_ix = ASC_TIDLUN_TO_IX(scp->target, 0);
-
-    found = ASC_FALSE;
-    ASC_DBG(2, "asc_init_dev: AscInitPollTarget()\n");
-    switch (ret = AscInitPollTarget(asc_dvc_varp, scsireqq, inquiry,
-        cap_info)) {
-    case ASC_TRUE:
-        found = ASC_TRUE;
-#ifdef ADVANSYS_DEBUG
-        tidmask = ADV_TID_TO_TIDMASK(scp->target);
-        ASC_DBG2(1, "asc_init_dev: lba %lu, blk_size %lu\n",
-            cap_info->lba, cap_info->blk_size);
-        ASC_DBG1(1, "asc_init_dev: peri_dvc_type %x\n",
-            inquiry->byte0.peri_dvc_type);
-        if (asc_dvc_varp->use_tagged_qng & tidmask) {
-            ASC_DBG1(1, "asc_init_dev: command queuing enabled: %d\n",
-                asc_dvc_varp->max_dvc_qng[scp->target]);
-        } else {
-            ASC_DBG(1, "asc_init_dev: command queuing disabled\n");
-        }
-        if (asc_dvc_varp->init_sdtr & tidmask) {
-            ASC_DBG(1, "asc_init_dev: synchronous transfers enabled\n");
-        } else {
-            ASC_DBG(1, "asc_init_dev: synchronous transfers disabled\n");
-        }
-        /* Set bit means fix disabled. */
-        if (asc_dvc_varp->pci_fix_asyn_xfer & tidmask) {
-            ASC_DBG(1, "asc_init_dev: synchronous transfer fix disabled\n");
-        } else {
-            ASC_DBG(1, "asc_init_dev: synchronous transfer fix enabled\n");
-        }
-#endif /* ADVANSYS_DEBUG */
-        break;
-    case ASC_FALSE:
-        ASC_DBG(1, "asc_init_dev: no device found\n");
-        break;
-    case ASC_ERROR:
-        ASC_PRINT1("asc_init_dev: board %d: AscInitPollTarget() ASC_ERROR\n",
-                boardp->id);
-        break;
-    default:
-        ASC_PRINT2(
-"asc_init_dev: board %d: AscInitPollTarget() unknown ret %d\n",
-                boardp->id, ret);
-        break;
-    }
-
-    /* Restore previously set bits in the bitmask fields. */
-    asc_dvc_varp->use_tagged_qng |= save_use_tagged_qng;
-    asc_dvc_varp->cfg->can_tagged_qng |= save_can_tagged_qng;
-
-    ASC_DBG(2, "asc_init_dev: AscInitPollEnd()\n");
-    AscInitPollEnd(asc_dvc_varp);
-
-    ASC_DBG1(1, "asc_init_dev: found %d\n", found); 
-
-    return found;
-}
-
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
 /*
  * Search for an AdvanSys PCI device in the PCI configuration space.
  */
@@ -7373,7 +7455,7 @@
 asc_srch_pci_dev(PCI_DEVICE *pciDevice)
 )
 {
-    int ret;
+    int                        ret = PCI_DEVICE_NOT_FOUND;
 
     ASC_DBG(2, "asc_srch_pci_dev: begin\n");
 
@@ -7400,6 +7482,7 @@
             ASC_DBG1(2, "asc_srch_pci_dev: recursive call return %d\n", ret);
         }
     }
+
     ASC_DBG1(2, "asc_srch_pci_dev: return %d\n", ret);
     return ret;
 }
@@ -7500,7 +7583,7 @@
     uchar       counter;
     uchar       *localConfig;
 
-    ASC_DBG1(4, "asc_get_pci_cfg: slot found - %d\n ",
+    ASC_DBG1(4, "asc_get_pci_cfg: slotFound %d\n ",
         pciDevice->slotFound);
 
     pciData.type = pciDevice->type;
@@ -7767,6 +7850,8 @@
     }
     ASC_DBG(4, "asc_put_cfg_byte: end\n");
 }
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
 
 /*
  * Add a 'REQP' to the end of specified queue. Set 'tidmask'
@@ -8176,9 +8261,9 @@
             major, minor, letter >= 26 ? '?' : letter + 'A');
         ASC_PRT_NEXT();
 
-        /* Current available ROM BIOS release is 3.1E. */
+        /* Current available ROM BIOS release is 3.1C. */
         if (major < 3 || (major <= 3 && minor < 1) ||
-            (major <= 3 && minor <= 1 && letter < ('E'- 'A'))) {
+            (major <= 3 && minor <= 1 && letter < ('C'- 'A'))) {
             upgrade = ASC_TRUE;
         }
     }
@@ -8195,18 +8280,18 @@
  * Add serial number to information bar if signature AAh
  * is found in at bit 15-9 (7 bits) of word 1.
  *
- * Serial Number consists 12 alpha-numeric digits.
+ * Serial Number consists fo 12 alpha-numeric digits.
  *
- *       1 - Product type (A,B,C,D..) Word0: 15-13 (3 bits)
- *       2 - MFG Location (A,B,C,D..) Word0: 12-10 (3 bits)
- *     3-4 - Product ID (0-99)        Word0: 10-0 (11 bits)
- *       5 - Product revision         Word0:  "         "
- *
- *         Signature                Word1: 15-9 (7 bits)
- *       6 - Year (4-9)               Word1: 8-6 (3 bits)
- *     7-8 - Week of the year         Word1: 5-0 (6 bits)
+ *       1 - Product type (A,B,C,D..)  Word0: 15-13 (3 bits)
+ *       2 - MFG Location (A,B,C,D..)  Word0: 12-10 (3 bits)
+ *     3-4 - Product ID (0-99)         Word0: 9-0 (10 bits)
+ *       5 - Product revision (A-J)    Word0:  "         "
+ *
+ *           Signature                 Word1: 15-9 (7 bits)
+ *       6 - Year (0-9)                Word1: 8-6 (3 bits) & Word2: 15 (1 bit)
+ *     7-8 - Week of the year (1-52)   Word1: 5-0 (6 bits)
  *
- *    9-12 - Serial Number            Word2: 15-0 (16 bits)
+ *    9-12 - Serial Number (A001-Z999) Word2: 14-0 (15 bits)
  *
  * Note 1: Only production cards will have a serial number.
  *
@@ -8228,7 +8313,11 @@
         w = serialnum[0];
 
         /* Product type - 1st digit. */
-        *cp++ = 'A' + ((w & 0xE000) >> 13);
+        if ((*cp = 'A' + ((w & 0xE000) >> 13)) == 'H') {
+            /* Product type is P=Prototype */
+            *cp += 0x8;
+        }
+        cp++;
 
         /* Manufacturing location - 2nd digit. */
         *cp++ = 'A' + ((w & 0x1C00) >> 10);
@@ -8247,8 +8336,17 @@
          */
         w = serialnum[1];
 
-        /* Year - 6th digit. */
-        *cp++ = '0' + ((w & 0x1C0) >> 6);
+        /*
+         * Year - 6th digit.
+         *
+         * If bit 15 of third word is set, then the
+         * last digit of the year is greater than 7.
+         */
+        if (serialnum[2] & 0x8000) {
+            *cp++ = '8' + ((w & 0x1C0) >> 6);
+        } else {
+            *cp++ = '0' + ((w & 0x1C0) >> 6);
+        }
 
         /* Week of year - 7th, 8th digits. */
         num = w & 0x003F;
@@ -8259,7 +8357,7 @@
         /*
          * Third word
          */
-        w = serialnum[2];
+        w = serialnum[2] & 0x7FFF;
 
         /* Serial number - 9th digit. */
         *cp++ = 'A' + (w / 1000);
@@ -9275,6 +9373,8 @@
         ushort offset)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9283,6 +9383,21 @@
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     return asc_get_cfg_byte(&pciData);
+#else /* ASC_CONFIG_PCI */
+    return 0;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    uchar byte_data;
+    pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, &byte_data);
+    return byte_data;
+#else /* CONFIG_PCI */
+    return 0;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9296,6 +9411,8 @@
         uchar  byte_data)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9304,6 +9421,15 @@
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     asc_put_cfg_byte(&pciData, byte_data);
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, byte_data);
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9400,6 +9526,8 @@
         ushort offset)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9408,6 +9536,21 @@
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     return asc_get_cfg_byte(&pciData);
+#else /* ASC_CONFIG_PCI */
+    return 0;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    uchar byte_data;
+    pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, &byte_data);
+    return byte_data;
+#else /* CONFIG_PCI */
+    return 0;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9421,6 +9564,8 @@
         uchar  byte_data)
 )
 {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
     PCI_DATA    pciData;
 
     pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
@@ -9429,6 +9574,15 @@
     pciData.offset = offset;
     pciData.type = pci_scan_method;
     asc_put_cfg_byte(&pciData, byte_data);
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */ 
+#ifdef CONFIG_PCI
+    pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+        PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+            ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+        offset, byte_data);
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */ 
 }
 
 /*
@@ -9745,8 +9899,9 @@
     printk("ASC_DVC_CFG at addr %x\n", (unsigned) h);
 
     printk(
-" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, res %x,\n",
-            h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable, h->res);
+" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, sdtr_enable %x,\n",
+            h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable,
+            h->sdtr_enable);
 
     printk(
 " chip_scsi_id %d, isa_dma_speed %d, isa_dma_channel %d, chip_version %d,\n",
@@ -10931,200 +11086,156 @@
     return (int_pending);
 }
 
-STATIC int
-AscScsiSetupCmdQ(
-                    REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                    REG ASC_SCSI_REQ_Q * scsiq,
-                    uchar * buf_addr,
-                    ulong buf_len
-)
-{
-    ulong               phy_addr;
-
-    scsiq->r1.cntl = 0;
-    scsiq->r1.sg_queue_cnt = 0;
-    scsiq->r1.q_no = 0;
-    scsiq->r1.extra_bytes = 0;
-    scsiq->r3.scsi_stat = 0;
-    scsiq->r3.scsi_msg = 0;
-    scsiq->r3.host_stat = 0;
-    scsiq->r3.done_stat = 0;
-    scsiq->r2.vm_id = 0;
-    scsiq->r1.data_cnt = buf_len;
-    scsiq->cdbptr = (uchar *) scsiq->cdb;
-    scsiq->sense_ptr = (uchar *) scsiq->sense ;
-    scsiq->r1.sense_len = ASC_MIN_SENSE_LEN ;
-    scsiq->r2.tag_code = (uchar) M2_QTAG_MSG_SIMPLE;
-    scsiq->r2.flag = (uchar) ASC_FLAG_SCSIQ_REQ;
-    scsiq->r2.srb_ptr = (ulong) scsiq;
-    scsiq->r1.status = (uchar) QS_READY;
-    scsiq->r1.data_addr = 0L;
-    if (buf_len != 0L) {
-        if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
-                    (uchar *) buf_addr, scsiq->r1.data_cnt)) == 0L) {
-            return (ERR);
-        }
-        scsiq->r1.data_addr = phy_addr;
-    }
-    if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
-                       (uchar *) scsiq->sense_ptr,
-                       (ulong) scsiq->r1.sense_len)) == 0L) {
-        return (ERR);
-    }
-    scsiq->r1.sense_addr = phy_addr ;
-    return (0);
-}
-
 STATIC uchar _asc_mcode_buf[] ASC_INITDATA =
 {
   0x01,  0x03,  0x01,  0x19,  0x0F,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x0F,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x00,  0x00,  0xDB,  0x0C,  0x0A,  0x05,  0x01,  0x00,  0x00,  0x00,  0x00,  0xFF,  0x00,  0x00,
+  0x00,  0x00,  0x00,  0x00,  0x91,  0x10,  0x0A,  0x05,  0x01,  0x00,  0x00,  0x00,  0x00,  0xFF,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0xFF,  0x80,  0xFF,  0xFF,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x00,  0x23,  0x00,  0x23,  0x00,  0x00,  0x00,  0x07,  0x00,  0xFF,  0x00,  0x00,  0x00,  0x00,
-  0xFF,  0xFF,  0xFF,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0xD4,  0x88,  0x00,  0x00,  0x00,  0x00,
+  0x00,  0x00,  0x00,  0x23,  0x00,  0x24,  0x00,  0x00,  0x00,  0x07,  0x00,  0xFF,  0x00,  0x00,  0x00,  0x00,
+  0xFF,  0xFF,  0xFF,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0xE2,  0x88,  0x00,  0x00,  0x00,  0x00,
   0x80,  0x73,  0x48,  0x04,  0x36,  0x00,  0x00,  0xA2,  0xC2,  0x00,  0x80,  0x73,  0x03,  0x23,  0x36,  0x40,
   0xB6,  0x00,  0x36,  0x00,  0x05,  0xD6,  0x0C,  0xD2,  0x12,  0xDA,  0x00,  0xA2,  0xC2,  0x00,  0x92,  0x80,
-  0x10,  0x98,  0x50,  0x00,  0xF5,  0x00,  0x3A,  0x98,  0xDF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,
-  0x4F,  0x00,  0xF5,  0x00,  0x3A,  0x98,  0xEF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,  0x80,  0x62,
+  0x1E,  0x98,  0x50,  0x00,  0xF5,  0x00,  0x48,  0x98,  0xDF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,
+  0x4F,  0x00,  0xF5,  0x00,  0x48,  0x98,  0xEF,  0x23,  0x36,  0x60,  0xB6,  0x00,  0x92,  0x80,  0x80,  0x62,
   0x92,  0x80,  0x00,  0x46,  0x17,  0xEE,  0x13,  0xEA,  0x02,  0x01,  0x09,  0xD8,  0xCD,  0x04,  0x4D,  0x00,
-  0x00,  0xA3,  0xD6,  0x00,  0x98,  0x97,  0x7F,  0x23,  0x04,  0x61,  0x84,  0x01,  0xE0,  0x84,  0xD2,  0xC1,
-  0x80,  0x73,  0xCD,  0x04,  0x4D,  0x00,  0x00,  0xA3,  0xE2,  0x01,  0x98,  0x97,  0xCE,  0x81,  0x00,  0x33,
-  0x02,  0x00,  0xB2,  0x88,  0x80,  0x73,  0x80,  0x77,  0x00,  0x01,  0x01,  0xA1,  0x02,  0x01,  0x4F,  0x00,
-  0x76,  0x97,  0x07,  0xA6,  0x0C,  0x01,  0x00,  0x33,  0x03,  0x00,  0xB2,  0x88,  0x03,  0x03,  0x03,  0xDE,
-  0x00,  0x33,  0x05,  0x00,  0xB2,  0x88,  0xCE,  0x00,  0x69,  0x60,  0xCE,  0x00,  0x02,  0x03,  0x4A,  0x60,
+  0x00,  0xA3,  0xD6,  0x00,  0xA6,  0x97,  0x7F,  0x23,  0x04,  0x61,  0x84,  0x01,  0xE6,  0x84,  0xD2,  0xC1,
+  0x80,  0x73,  0xCD,  0x04,  0x4D,  0x00,  0x00,  0xA3,  0xE2,  0x01,  0xA6,  0x97,  0xCE,  0x81,  0x00,  0x33,
+  0x02,  0x00,  0xC0,  0x88,  0x80,  0x73,  0x80,  0x77,  0x00,  0x01,  0x01,  0xA1,  0x02,  0x01,  0x4F,  0x00,
+  0x84,  0x97,  0x07,  0xA6,  0x0C,  0x01,  0x00,  0x33,  0x03,  0x00,  0xC0,  0x88,  0x03,  0x03,  0x03,  0xDE,
+  0x00,  0x33,  0x05,  0x00,  0xC0,  0x88,  0xCE,  0x00,  0x69,  0x60,  0xCE,  0x00,  0x02,  0x03,  0x4A,  0x60,
   0x00,  0xA2,  0x80,  0x01,  0x80,  0x63,  0x07,  0xA6,  0x2C,  0x01,  0x80,  0x81,  0x03,  0x03,  0x80,  0x63,
-  0xE2,  0x00,  0x07,  0xA6,  0x3C,  0x01,  0x00,  0x33,  0x04,  0x00,  0xB2,  0x88,  0x03,  0x07,  0x02,  0x01,
-  0x04,  0xCA,  0x0D,  0x23,  0x5A,  0x98,  0x4D,  0x04,  0xFE,  0x84,  0x05,  0xD8,  0x0D,  0x23,  0x5A,  0x98,
-  0xCD,  0x04,  0x15,  0x23,  0xE8,  0x88,  0xFB,  0x23,  0x02,  0x61,  0x82,  0x01,  0x80,  0x63,  0x02,  0x03,
-  0x06,  0xA3,  0x6A,  0x01,  0x00,  0x33,  0x0A,  0x00,  0xB2,  0x88,  0x4E,  0x00,  0x07,  0xA3,  0x76,  0x01,
-  0x00,  0x33,  0x0B,  0x00,  0xB2,  0x88,  0xCD,  0x04,  0x36,  0x2D,  0x00,  0x33,  0x1A,  0x00,  0xB2,  0x88,
+  0xE2,  0x00,  0x07,  0xA6,  0x3C,  0x01,  0x00,  0x33,  0x04,  0x00,  0xC0,  0x88,  0x03,  0x07,  0x02,  0x01,
+  0x04,  0xCA,  0x0D,  0x23,  0x68,  0x98,  0x4D,  0x04,  0x04,  0x85,  0x05,  0xD8,  0x0D,  0x23,  0x68,  0x98,
+  0xCD,  0x04,  0x15,  0x23,  0xF6,  0x88,  0xFB,  0x23,  0x02,  0x61,  0x82,  0x01,  0x80,  0x63,  0x02,  0x03,
+  0x06,  0xA3,  0x6A,  0x01,  0x00,  0x33,  0x0A,  0x00,  0xC0,  0x88,  0x4E,  0x00,  0x07,  0xA3,  0x76,  0x01,
+  0x00,  0x33,  0x0B,  0x00,  0xC0,  0x88,  0xCD,  0x04,  0x36,  0x2D,  0x00,  0x33,  0x1A,  0x00,  0xC0,  0x88,
   0x50,  0x04,  0x90,  0x81,  0x06,  0xAB,  0x8A,  0x01,  0x90,  0x81,  0x4E,  0x00,  0x07,  0xA3,  0x9A,  0x01,
-  0x50,  0x00,  0x00,  0xA3,  0x44,  0x01,  0x00,  0x05,  0x84,  0x81,  0x38,  0x97,  0x02,  0x01,  0x05,  0xC6,
+  0x50,  0x00,  0x00,  0xA3,  0x44,  0x01,  0x00,  0x05,  0x84,  0x81,  0x46,  0x97,  0x02,  0x01,  0x05,  0xC6,
   0x04,  0x23,  0xA0,  0x01,  0x15,  0x23,  0xA1,  0x01,  0xC6,  0x81,  0xFD,  0x23,  0x02,  0x61,  0x82,  0x01,
   0x0A,  0xDA,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA0,  0xBC,  0x01,  0x80,  0x63,  0xCD,  0x04,  0x36,  0x2D,
-  0x00,  0x33,  0x1B,  0x00,  0xB2,  0x88,  0x06,  0x23,  0x5A,  0x98,  0xCD,  0x04,  0xE0,  0x84,  0x06,  0x01,
-  0x00,  0xA2,  0xDC,  0x01,  0x57,  0x60,  0x00,  0xA0,  0xE2,  0x01,  0xE0,  0x84,  0x80,  0x23,  0xA0,  0x01,
-  0xE0,  0x84,  0x80,  0x73,  0x4B,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x08,  0x02,  0x04,  0x01,  0x0C,  0xDE,
-  0x02,  0x01,  0x03,  0xCC,  0x4F,  0x00,  0x76,  0x97,  0x04,  0x82,  0x08,  0x23,  0x02,  0x41,  0x82,  0x01,
-  0x4F,  0x00,  0x54,  0x97,  0x48,  0x04,  0x84,  0x80,  0xE2,  0x97,  0x00,  0x46,  0x56,  0x00,  0x03,  0xC0,
+  0x00,  0x33,  0x1B,  0x00,  0xC0,  0x88,  0x06,  0x23,  0x68,  0x98,  0xCD,  0x04,  0xE6,  0x84,  0x06,  0x01,
+  0x00,  0xA2,  0xDC,  0x01,  0x57,  0x60,  0x00,  0xA0,  0xE2,  0x01,  0xE6,  0x84,  0x80,  0x23,  0xA0,  0x01,
+  0xE6,  0x84,  0x80,  0x73,  0x4B,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x08,  0x02,  0x04,  0x01,  0x0C,  0xDE,
+  0x02,  0x01,  0x03,  0xCC,  0x4F,  0x00,  0x84,  0x97,  0x04,  0x82,  0x08,  0x23,  0x02,  0x41,  0x82,  0x01,
+  0x4F,  0x00,  0x62,  0x97,  0x48,  0x04,  0x84,  0x80,  0xF0,  0x97,  0x00,  0x46,  0x56,  0x00,  0x03,  0xC0,
   0x01,  0x23,  0xE8,  0x00,  0x81,  0x73,  0x06,  0x29,  0x03,  0x42,  0x06,  0xE2,  0x03,  0xEE,  0x67,  0xEB,
-  0x11,  0x23,  0xE8,  0x88,  0xF6,  0x97,  0xF4,  0x80,  0x80,  0x73,  0x80,  0x77,  0x06,  0xA6,  0x36,  0x02,
-  0x00,  0x33,  0x31,  0x00,  0xB2,  0x88,  0x04,  0x01,  0x03,  0xD8,  0xA4,  0x98,  0x4C,  0x96,  0x48,  0x82,
-  0xDC,  0x95,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0xB6,  0x2D,  0x02,  0xA6,  0x72,  0x02,  0x07,  0xA6,
-  0x60,  0x02,  0x06,  0xA6,  0x64,  0x02,  0x03,  0xA6,  0x68,  0x02,  0x00,  0x33,  0x10,  0x00,  0xB2,  0x88,
-  0x76,  0x95,  0x4A,  0x82,  0x42,  0x96,  0x4A,  0x82,  0x04,  0x23,  0xA0,  0x01,  0x14,  0x23,  0xA1,  0x01,
-  0x36,  0x84,  0x04,  0x01,  0x0C,  0xDC,  0xE0,  0x23,  0x25,  0x61,  0xEF,  0x00,  0x14,  0x01,  0x4F,  0x04,
-  0xA8,  0x01,  0x6F,  0x00,  0xA5,  0x01,  0x03,  0x23,  0xA4,  0x01,  0x06,  0x23,  0x9C,  0x01,  0x24,  0x2B,
-  0x1C,  0x01,  0x02,  0xA6,  0xB0,  0x02,  0x07,  0xA6,  0x60,  0x02,  0x06,  0xA6,  0x64,  0x02,  0x03,  0xA6,
-  0x1A,  0x04,  0x01,  0xA6,  0xBA,  0x02,  0x00,  0xA6,  0xBA,  0x02,  0x00,  0x33,  0x12,  0x00,  0xB2,  0x88,
-  0x00,  0x0E,  0x80,  0x63,  0x00,  0x43,  0x00,  0xA0,  0x92,  0x02,  0x4D,  0x04,  0x04,  0x01,  0x0B,  0xDC,
-  0xE7,  0x23,  0x04,  0x61,  0x84,  0x01,  0x10,  0x31,  0x12,  0x35,  0x14,  0x01,  0xEC,  0x00,  0x6C,  0x38,
-  0x00,  0x3F,  0x00,  0x00,  0xF0,  0x82,  0x18,  0x23,  0x04,  0x61,  0x18,  0xA0,  0xE8,  0x02,  0x04,  0x01,
-  0x9C,  0xC8,  0x00,  0x33,  0x1F,  0x00,  0xB2,  0x88,  0x08,  0x31,  0x0A,  0x35,  0x0C,  0x39,  0x0E,  0x3D,
-  0x70,  0x98,  0xB6,  0x2D,  0x01,  0xA6,  0x1A,  0x03,  0x00,  0xA6,  0x1A,  0x03,  0x07,  0xA6,  0x12,  0x03,
-  0x06,  0xA6,  0x16,  0x03,  0x03,  0xA6,  0x1A,  0x04,  0x02,  0xA6,  0x72,  0x02,  0x00,  0x33,  0x33,  0x00,
-  0xB2,  0x88,  0x76,  0x95,  0xF4,  0x82,  0x42,  0x96,  0xF4,  0x82,  0x74,  0x98,  0x80,  0x42,  0x70,  0x98,
-  0x60,  0xE4,  0x04,  0x01,  0x29,  0xC8,  0x31,  0x05,  0x07,  0x01,  0x00,  0xA2,  0x5A,  0x03,  0x00,  0x43,
-  0x87,  0x01,  0x05,  0x05,  0x78,  0x98,  0x70,  0x98,  0x00,  0xA6,  0x1C,  0x03,  0x07,  0xA6,  0x52,  0x03,
-  0x03,  0xA6,  0x36,  0x04,  0x06,  0xA6,  0x56,  0x03,  0x01,  0xA6,  0x1C,  0x03,  0x00,  0x33,  0x25,  0x00,
-  0xB2,  0x88,  0x76,  0x95,  0x38,  0x83,  0x42,  0x96,  0x38,  0x83,  0x04,  0x01,  0x0C,  0xCE,  0x03,  0xC8,
-  0x00,  0x33,  0x42,  0x00,  0xB2,  0x88,  0x00,  0x01,  0x05,  0x05,  0xFF,  0xA2,  0x78,  0x03,  0xB1,  0x01,
-  0x08,  0x23,  0xB2,  0x01,  0x34,  0x83,  0x05,  0x05,  0x15,  0x01,  0x00,  0xA2,  0x98,  0x03,  0xEC,  0x00,
-  0x6E,  0x00,  0x95,  0x01,  0x6C,  0x38,  0x00,  0x3F,  0x00,  0x00,  0x01,  0xA6,  0x94,  0x03,  0x00,  0xA6,
-  0x94,  0x03,  0x0C,  0x84,  0x80,  0x42,  0x70,  0x98,  0x01,  0xA6,  0xA2,  0x03,  0x00,  0xA6,  0xBA,  0x03,
-  0x0C,  0x84,  0x98,  0x98,  0x80,  0x42,  0x01,  0xA6,  0xA2,  0x03,  0x07,  0xA6,  0xB0,  0x03,  0xD2,  0x83,
-  0x76,  0x95,  0xA6,  0x83,  0x00,  0x33,  0x2F,  0x00,  0xB2,  0x88,  0x98,  0x98,  0x80,  0x42,  0x00,  0xA6,
-  0xBA,  0x03,  0x07,  0xA6,  0xC8,  0x03,  0xD2,  0x83,  0x76,  0x95,  0xBE,  0x83,  0x00,  0x33,  0x26,  0x00,
-  0xB2,  0x88,  0x38,  0x2B,  0x80,  0x32,  0x80,  0x36,  0x04,  0x23,  0xA0,  0x01,  0x12,  0x23,  0xA1,  0x01,
-  0x0C,  0x84,  0x06,  0xF0,  0x06,  0xA4,  0xF0,  0x03,  0x80,  0x6B,  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,
-  0x03,  0xA6,  0x0A,  0x04,  0x07,  0xA6,  0x02,  0x04,  0x06,  0xA6,  0x06,  0x04,  0x00,  0x33,  0x17,  0x00,
-  0xB2,  0x88,  0x76,  0x95,  0xF0,  0x83,  0x42,  0x96,  0xF0,  0x83,  0x1A,  0x84,  0x06,  0xF0,  0x06,  0xA4,
-  0x1A,  0x04,  0x80,  0x6B,  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,  0xB6,  0x2D,  0x03,  0xA6,  0x36,  0x04,
-  0x07,  0xA6,  0x2E,  0x04,  0x06,  0xA6,  0x32,  0x04,  0x00,  0x33,  0x30,  0x00,  0xB2,  0x88,  0x76,  0x95,
-  0x1A,  0x84,  0x42,  0x96,  0x1A,  0x84,  0x1D,  0x01,  0x06,  0xCC,  0x00,  0x33,  0x00,  0x84,  0xC0,  0x20,
-  0x00,  0x23,  0xEA,  0x00,  0x81,  0x62,  0xA2,  0x0D,  0x80,  0x63,  0x07,  0xA6,  0x54,  0x04,  0x00,  0x33,
-  0x18,  0x00,  0xB2,  0x88,  0x03,  0x03,  0x80,  0x63,  0xA3,  0x01,  0x07,  0xA4,  0x5E,  0x04,  0x23,  0x01,
-  0x00,  0xA2,  0x80,  0x04,  0x0A,  0xA0,  0x70,  0x04,  0xE0,  0x00,  0x00,  0x33,  0x1D,  0x00,  0xB2,  0x88,
-  0x0B,  0xA0,  0x7C,  0x04,  0xE0,  0x00,  0x00,  0x33,  0x1E,  0x00,  0xB2,  0x88,  0x42,  0x23,  0xE8,  0x88,
-  0x00,  0x23,  0x22,  0xA3,  0xE0,  0x04,  0x08,  0x23,  0x22,  0xA3,  0x9C,  0x04,  0x28,  0x23,  0x22,  0xA3,
-  0xA8,  0x04,  0x02,  0x23,  0x22,  0xA3,  0xBE,  0x04,  0x42,  0x23,  0xE8,  0x88,  0x4A,  0x00,  0x06,  0x61,
-  0x00,  0xA0,  0xA8,  0x04,  0x45,  0x23,  0xE8,  0x88,  0xF6,  0x97,  0x00,  0xA2,  0xBA,  0x04,  0xA4,  0x98,
-  0x00,  0x33,  0x00,  0x82,  0xC0,  0x20,  0x81,  0x62,  0xF0,  0x81,  0x47,  0x23,  0xE8,  0x88,  0x04,  0x01,
-  0x0B,  0xDE,  0xF6,  0x97,  0xA4,  0x98,  0x00,  0x33,  0x00,  0x81,  0xC0,  0x20,  0x81,  0x62,  0x14,  0x01,
-  0x00,  0xA0,  0x08,  0x02,  0x43,  0x23,  0xE8,  0x88,  0x04,  0x23,  0xA0,  0x01,  0x44,  0x23,  0xA1,  0x01,
-  0x80,  0x73,  0x4D,  0x00,  0x03,  0xA3,  0xEE,  0x04,  0x00,  0x33,  0x27,  0x00,  0xB2,  0x88,  0x04,  0x01,
-  0x04,  0xDC,  0x02,  0x23,  0xA2,  0x01,  0x04,  0x23,  0xA0,  0x01,  0xF6,  0x97,  0x20,  0x95,  0x4B,  0x00,
-  0xF6,  0x00,  0x4F,  0x04,  0x4F,  0x00,  0x00,  0xA3,  0x1C,  0x05,  0x00,  0x05,  0x76,  0x00,  0x06,  0x61,
-  0x00,  0xA2,  0x16,  0x05,  0x04,  0x85,  0x38,  0x97,  0xCD,  0x04,  0x1E,  0x85,  0x48,  0x04,  0x84,  0x80,
-  0x02,  0x01,  0x03,  0xDA,  0x80,  0x23,  0x82,  0x01,  0x2E,  0x85,  0x02,  0x23,  0xA0,  0x01,  0x4A,  0x00,
-  0x06,  0x61,  0x00,  0xA2,  0x3A,  0x05,  0x1D,  0x01,  0x04,  0xD6,  0xFF,  0x23,  0x86,  0x41,  0x4B,  0x60,
-  0xCB,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x49,  0x00,  0x81,  0x01,  0x04,  0x01,  0x02,  0xC8,  0x30,  0x01,
-  0x80,  0x01,  0xF7,  0x04,  0x03,  0x01,  0x49,  0x04,  0x80,  0x01,  0xC9,  0x00,  0x00,  0x05,  0x00,  0x01,
-  0xFF,  0xA0,  0x5A,  0x05,  0x77,  0x04,  0x01,  0x23,  0xEA,  0x00,  0x5D,  0x00,  0xFE,  0xC7,  0x00,  0x62,
-  0x00,  0x23,  0xEA,  0x00,  0x00,  0x63,  0x07,  0xA4,  0xDA,  0x05,  0x03,  0x03,  0x02,  0xA0,  0x88,  0x05,
-  0xD6,  0x85,  0x00,  0x33,  0x2D,  0x00,  0xB2,  0x88,  0x04,  0xA0,  0xAE,  0x05,  0x80,  0x63,  0x4A,  0x00,
-  0x06,  0x61,  0x00,  0xA2,  0x9A,  0x05,  0x1D,  0x01,  0x06,  0xD6,  0x02,  0x23,  0x02,  0x41,  0x82,  0x01,
-  0x50,  0x00,  0x54,  0x97,  0xFE,  0x84,  0x04,  0x23,  0x02,  0x41,  0x82,  0x01,  0xFE,  0x84,  0x08,  0xA0,
-  0xB4,  0x05,  0xD6,  0x85,  0x03,  0xA0,  0xBA,  0x05,  0xD6,  0x85,  0x01,  0xA0,  0xC4,  0x05,  0x88,  0x00,
-  0x80,  0x63,  0xB2,  0x86,  0x07,  0xA0,  0xD0,  0x05,  0x06,  0x23,  0x5A,  0x98,  0x48,  0x23,  0xE8,  0x88,
-  0x07,  0x23,  0x80,  0x00,  0xF8,  0x86,  0x80,  0x63,  0x76,  0x85,  0x00,  0x63,  0x4A,  0x00,  0x06,  0x61,
-  0x00,  0xA2,  0x18,  0x06,  0x1D,  0x01,  0x18,  0xD4,  0xC0,  0x23,  0x07,  0x41,  0x83,  0x03,  0x80,  0x63,
-  0x06,  0xA6,  0xFA,  0x05,  0x00,  0x33,  0x37,  0x00,  0xB2,  0x88,  0x1D,  0x01,  0x02,  0xD6,  0x46,  0x23,
-  0xE8,  0x88,  0x63,  0x60,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,  0x12,  0x06,  0x00,  0x33,  0x38,  0x00,
-  0xB2,  0x88,  0xEF,  0x04,  0x6F,  0x00,  0x00,  0x63,  0x4B,  0x00,  0x06,  0x41,  0xCB,  0x00,  0x52,  0x00,
-  0x06,  0x61,  0x00,  0xA2,  0x30,  0x06,  0x1D,  0x01,  0x03,  0xCA,  0xC0,  0x23,  0x07,  0x41,  0x00,  0x63,
-  0x1D,  0x01,  0x04,  0xCC,  0x00,  0x33,  0x00,  0x83,  0xC0,  0x20,  0x81,  0x62,  0x80,  0x23,  0x07,  0x41,
-  0x00,  0x63,  0x80,  0x67,  0x08,  0x23,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,  0x06,  0xA6,  0x5E,  0x06,
-  0x07,  0xA6,  0x76,  0x05,  0x02,  0xA6,  0xEC,  0x06,  0x00,  0x33,  0x39,  0x00,  0xB2,  0x88,  0x00,  0x00,
-  0x01,  0xA0,  0x06,  0x07,  0xDC,  0x95,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,  0x72,  0x06,  0x07,  0xA6,
-  0x76,  0x05,  0x00,  0x00,  0x01,  0xA0,  0x06,  0x07,  0x00,  0x2B,  0x40,  0x0E,  0x80,  0x63,  0x01,  0x00,
-  0x06,  0xA6,  0x8E,  0x06,  0x07,  0xA6,  0x76,  0x05,  0x00,  0x33,  0x3A,  0x00,  0xB2,  0x88,  0x40,  0x0E,
-  0x80,  0x63,  0x00,  0x43,  0x00,  0xA0,  0x80,  0x06,  0x06,  0xA6,  0xA6,  0x06,  0x07,  0xA6,  0x76,  0x05,
-  0x00,  0x33,  0x3B,  0x00,  0xB2,  0x88,  0x80,  0x67,  0x40,  0x0E,  0x80,  0x63,  0x07,  0xA6,  0x76,  0x05,
-  0x00,  0x63,  0x07,  0xA6,  0xBC,  0x06,  0x00,  0x33,  0x2A,  0x00,  0xB2,  0x88,  0x03,  0x03,  0x80,  0x63,
-  0x89,  0x00,  0x0A,  0x2B,  0x07,  0xA6,  0xCE,  0x06,  0x00,  0x33,  0x29,  0x00,  0xB2,  0x88,  0x00,  0x43,
-  0x00,  0xA2,  0xDA,  0x06,  0xC0,  0x0E,  0x80,  0x63,  0xC4,  0x86,  0xC0,  0x0E,  0x00,  0x33,  0x00,  0x80,
-  0xC0,  0x20,  0x81,  0x62,  0x04,  0x01,  0x08,  0xDA,  0x80,  0x63,  0x76,  0x85,  0x80,  0x67,  0x00,  0x33,
-  0x00,  0x40,  0xC0,  0x20,  0x81,  0x62,  0x00,  0x63,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,  0x6A,  0x06,
-  0x00,  0x33,  0x2C,  0x00,  0xB2,  0x88,  0x0C,  0xA2,  0x20,  0x07,  0xDC,  0x95,  0x83,  0x03,  0x80,  0x63,
-  0x06,  0xA6,  0x1E,  0x07,  0x07,  0xA6,  0x76,  0x05,  0x00,  0x33,  0x3D,  0x00,  0xB2,  0x88,  0x00,  0x00,
-  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0x0C,  0xA0,  0x36,  0x07,  0x07,  0xA6,  0x76,  0x05,  0xBF,  0x23,
-  0x04,  0x61,  0x84,  0x01,  0xE0,  0x84,  0x00,  0x63,  0xF0,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x00,  0x01,
-  0xF2,  0x00,  0x01,  0x05,  0x80,  0x01,  0x72,  0x04,  0x71,  0x00,  0x81,  0x01,  0x70,  0x04,  0x80,  0x05,
-  0x81,  0x05,  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x70,  0x00,
-  0x81,  0x01,  0x70,  0x04,  0x71,  0x00,  0x81,  0x01,  0x72,  0x00,  0x80,  0x01,  0x71,  0x04,  0x70,  0x00,
-  0x80,  0x01,  0x70,  0x04,  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x00,  0x01,  0xF1,  0x00,
-  0x70,  0x00,  0x80,  0x01,  0x70,  0x04,  0x71,  0x00,  0x80,  0x01,  0x72,  0x00,  0x81,  0x01,  0x71,  0x04,
-  0x70,  0x00,  0x81,  0x01,  0x70,  0x04,  0x00,  0x63,  0x00,  0x23,  0xB3,  0x01,  0x83,  0x05,  0xA3,  0x01,
-  0xA2,  0x01,  0xA1,  0x01,  0x01,  0x23,  0xA0,  0x01,  0x00,  0x01,  0xC8,  0x00,  0x03,  0xA1,  0xB6,  0x07,
-  0x00,  0x33,  0x07,  0x00,  0xB2,  0x88,  0x80,  0x05,  0x81,  0x05,  0x04,  0x01,  0x11,  0xC8,  0x48,  0x00,
-  0xB0,  0x01,  0xB1,  0x01,  0x08,  0x23,  0xB2,  0x01,  0x05,  0x01,  0x48,  0x04,  0x00,  0x43,  0x00,  0xA2,
-  0xD6,  0x07,  0x00,  0x05,  0xCC,  0x87,  0x00,  0x01,  0xC8,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x05,  0x05,
-  0x00,  0x63,  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,  0x80,  0x43,  0x76,  0x08,
-  0x80,  0x02,  0x77,  0x04,  0x00,  0x63,  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,
-  0x00,  0xA0,  0x06,  0x08,  0x08,  0x88,  0x00,  0x43,  0x76,  0x08,  0x80,  0x02,  0x77,  0x04,  0x00,  0x63,
-  0xF3,  0x04,  0x00,  0x23,  0xF4,  0x00,  0x74,  0x00,  0x80,  0x43,  0xF4,  0x00,  0xCF,  0x40,  0x00,  0xA2,
-  0x36,  0x08,  0x74,  0x04,  0x02,  0x01,  0xF7,  0xC9,  0xF6,  0xD9,  0x00,  0x01,  0x01,  0xA1,  0x16,  0x08,
-  0xF6,  0x97,  0x20,  0x95,  0x16,  0x88,  0x73,  0x04,  0x00,  0x63,  0xF3,  0x04,  0x75,  0x04,  0x4C,  0x88,
-  0x02,  0x01,  0x04,  0xD8,  0x38,  0x97,  0xF6,  0x97,  0x20,  0x95,  0x3C,  0x88,  0x75,  0x00,  0x00,  0xA3,
-  0x56,  0x08,  0x00,  0x05,  0x40,  0x88,  0x73,  0x04,  0x00,  0x63,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,
-  0x68,  0x08,  0x00,  0x33,  0x3E,  0x00,  0xB2,  0x88,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,
-  0x38,  0x2B,  0x8E,  0x88,  0x38,  0x2B,  0x84,  0x88,  0x32,  0x09,  0x31,  0x05,  0x84,  0x98,  0x05,  0x05,
-  0xB2,  0x09,  0x00,  0x63,  0x00,  0x32,  0x00,  0x36,  0x00,  0x3A,  0x00,  0x3E,  0x00,  0x63,  0x80,  0x32,
-  0x80,  0x36,  0x80,  0x3A,  0x80,  0x3E,  0x00,  0x63,  0x38,  0x2B,  0x40,  0x32,  0x40,  0x36,  0x40,  0x3A,
-  0x40,  0x3E,  0x00,  0x63,  0x5A,  0x20,  0xC9,  0x40,  0x00,  0xA0,  0xA4,  0x08,  0x5D,  0x00,  0xFE,  0xC3,
-  0x00,  0x63,  0x80,  0x73,  0xE6,  0x20,  0x02,  0x23,  0xE8,  0x00,  0x82,  0x73,  0xFF,  0xFD,  0x80,  0x73,
-  0x13,  0x23,  0xE8,  0x88,  0x66,  0x20,  0xC0,  0x20,  0x04,  0x23,  0xA0,  0x01,  0xA1,  0x23,  0xA1,  0x01,
-  0x81,  0x62,  0xD2,  0x88,  0x80,  0x73,  0x80,  0x77,  0x68,  0x00,  0x00,  0xA2,  0x80,  0x00,  0x03,  0xC2,
-  0xF1,  0xC7,  0x41,  0x23,  0xE8,  0x88,  0x11,  0x23,  0xA1,  0x01,  0x04,  0x23,  0xA0,  0x01,  0xE0,  0x84,
-
+  0x11,  0x23,  0xF6,  0x88,  0x04,  0x98,  0xF4,  0x80,  0x80,  0x73,  0x80,  0x77,  0x07,  0xA4,  0x32,  0x02,
+  0x7C,  0x95,  0x06,  0xA6,  0x3C,  0x02,  0x03,  0xA6,  0x4C,  0x04,  0xC0,  0x88,  0x04,  0x01,  0x03,  0xD8,
+  0xB2,  0x98,  0x6A,  0x96,  0x4E,  0x82,  0xFE,  0x95,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0xB6,  0x2D,
+  0x02,  0xA6,  0x78,  0x02,  0x07,  0xA6,  0x66,  0x02,  0x06,  0xA6,  0x6A,  0x02,  0x03,  0xA6,  0x6E,  0x02,
+  0x00,  0x33,  0x10,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0x50,  0x82,  0x60,  0x96,  0x50,  0x82,  0x04,  0x23,
+  0xA0,  0x01,  0x14,  0x23,  0xA1,  0x01,  0x3C,  0x84,  0x04,  0x01,  0x0C,  0xDC,  0xE0,  0x23,  0x25,  0x61,
+  0xEF,  0x00,  0x14,  0x01,  0x4F,  0x04,  0xA8,  0x01,  0x6F,  0x00,  0xA5,  0x01,  0x03,  0x23,  0xA4,  0x01,
+  0x06,  0x23,  0x9C,  0x01,  0x24,  0x2B,  0x1C,  0x01,  0x02,  0xA6,  0xB6,  0x02,  0x07,  0xA6,  0x66,  0x02,
+  0x06,  0xA6,  0x6A,  0x02,  0x03,  0xA6,  0x20,  0x04,  0x01,  0xA6,  0xC0,  0x02,  0x00,  0xA6,  0xC0,  0x02,
+  0x00,  0x33,  0x12,  0x00,  0xC0,  0x88,  0x00,  0x0E,  0x80,  0x63,  0x00,  0x43,  0x00,  0xA0,  0x98,  0x02,
+  0x4D,  0x04,  0x04,  0x01,  0x0B,  0xDC,  0xE7,  0x23,  0x04,  0x61,  0x84,  0x01,  0x10,  0x31,  0x12,  0x35,
+  0x14,  0x01,  0xEC,  0x00,  0x6C,  0x38,  0x00,  0x3F,  0x00,  0x00,  0xF6,  0x82,  0x18,  0x23,  0x04,  0x61,
+  0x18,  0xA0,  0xEE,  0x02,  0x04,  0x01,  0x9C,  0xC8,  0x00,  0x33,  0x1F,  0x00,  0xC0,  0x88,  0x08,  0x31,
+  0x0A,  0x35,  0x0C,  0x39,  0x0E,  0x3D,  0x7E,  0x98,  0xB6,  0x2D,  0x01,  0xA6,  0x20,  0x03,  0x00,  0xA6,
+  0x20,  0x03,  0x07,  0xA6,  0x18,  0x03,  0x06,  0xA6,  0x1C,  0x03,  0x03,  0xA6,  0x20,  0x04,  0x02,  0xA6,
+  0x78,  0x02,  0x00,  0x33,  0x33,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0xFA,  0x82,  0x60,  0x96,  0xFA,  0x82,
+  0x82,  0x98,  0x80,  0x42,  0x7E,  0x98,  0x60,  0xE4,  0x04,  0x01,  0x29,  0xC8,  0x31,  0x05,  0x07,  0x01,
+  0x00,  0xA2,  0x60,  0x03,  0x00,  0x43,  0x87,  0x01,  0x05,  0x05,  0x86,  0x98,  0x7E,  0x98,  0x00,  0xA6,
+  0x22,  0x03,  0x07,  0xA6,  0x58,  0x03,  0x03,  0xA6,  0x3C,  0x04,  0x06,  0xA6,  0x5C,  0x03,  0x01,  0xA6,
+  0x22,  0x03,  0x00,  0x33,  0x25,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0x3E,  0x83,  0x60,  0x96,  0x3E,  0x83,
+  0x04,  0x01,  0x0C,  0xCE,  0x03,  0xC8,  0x00,  0x33,  0x42,  0x00,  0xC0,  0x88,  0x00,  0x01,  0x05,  0x05,
+  0xFF,  0xA2,  0x7E,  0x03,  0xB1,  0x01,  0x08,  0x23,  0xB2,  0x01,  0x3A,  0x83,  0x05,  0x05,  0x15,  0x01,
+  0x00,  0xA2,  0x9E,  0x03,  0xEC,  0x00,  0x6E,  0x00,  0x95,  0x01,  0x6C,  0x38,  0x00,  0x3F,  0x00,  0x00,
+  0x01,  0xA6,  0x9A,  0x03,  0x00,  0xA6,  0x9A,  0x03,  0x12,  0x84,  0x80,  0x42,  0x7E,  0x98,  0x01,  0xA6,
+  0xA8,  0x03,  0x00,  0xA6,  0xC0,  0x03,  0x12,  0x84,  0xA6,  0x98,  0x80,  0x42,  0x01,  0xA6,  0xA8,  0x03,
+  0x07,  0xA6,  0xB6,  0x03,  0xD8,  0x83,  0x7C,  0x95,  0xAC,  0x83,  0x00,  0x33,  0x2F,  0x00,  0xC0,  0x88,
+  0xA6,  0x98,  0x80,  0x42,  0x00,  0xA6,  0xC0,  0x03,  0x07,  0xA6,  0xCE,  0x03,  0xD8,  0x83,  0x7C,  0x95,
+  0xC4,  0x83,  0x00,  0x33,  0x26,  0x00,  0xC0,  0x88,  0x38,  0x2B,  0x80,  0x32,  0x80,  0x36,  0x04,  0x23,
+  0xA0,  0x01,  0x12,  0x23,  0xA1,  0x01,  0x12,  0x84,  0x06,  0xF0,  0x06,  0xA4,  0xF6,  0x03,  0x80,  0x6B,
+  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,  0x03,  0xA6,  0x10,  0x04,  0x07,  0xA6,  0x08,  0x04,  0x06,  0xA6,
+  0x0C,  0x04,  0x00,  0x33,  0x17,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0xF6,  0x83,  0x60,  0x96,  0xF6,  0x83,
+  0x20,  0x84,  0x06,  0xF0,  0x06,  0xA4,  0x20,  0x04,  0x80,  0x6B,  0x05,  0x23,  0x83,  0x03,  0x80,  0x63,
+  0xB6,  0x2D,  0x03,  0xA6,  0x3C,  0x04,  0x07,  0xA6,  0x34,  0x04,  0x06,  0xA6,  0x38,  0x04,  0x00,  0x33,
+  0x30,  0x00,  0xC0,  0x88,  0x7C,  0x95,  0x20,  0x84,  0x60,  0x96,  0x20,  0x84,  0x1D,  0x01,  0x06,  0xCC,
+  0x00,  0x33,  0x00,  0x84,  0xC0,  0x20,  0x00,  0x23,  0xEA,  0x00,  0x81,  0x62,  0xA2,  0x0D,  0x80,  0x63,
+  0x07,  0xA6,  0x5A,  0x04,  0x00,  0x33,  0x18,  0x00,  0xC0,  0x88,  0x03,  0x03,  0x80,  0x63,  0xA3,  0x01,
+  0x07,  0xA4,  0x64,  0x04,  0x23,  0x01,  0x00,  0xA2,  0x86,  0x04,  0x0A,  0xA0,  0x76,  0x04,  0xE0,  0x00,
+  0x00,  0x33,  0x1D,  0x00,  0xC0,  0x88,  0x0B,  0xA0,  0x82,  0x04,  0xE0,  0x00,  0x00,  0x33,  0x1E,  0x00,
+  0xC0,  0x88,  0x42,  0x23,  0xF6,  0x88,  0x00,  0x23,  0x22,  0xA3,  0xE6,  0x04,  0x08,  0x23,  0x22,  0xA3,
+  0xA2,  0x04,  0x28,  0x23,  0x22,  0xA3,  0xAE,  0x04,  0x02,  0x23,  0x22,  0xA3,  0xC4,  0x04,  0x42,  0x23,
+  0xF6,  0x88,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA0,  0xAE,  0x04,  0x45,  0x23,  0xF6,  0x88,  0x04,  0x98,
+  0x00,  0xA2,  0xC0,  0x04,  0xB2,  0x98,  0x00,  0x33,  0x00,  0x82,  0xC0,  0x20,  0x81,  0x62,  0xF0,  0x81,
+  0x47,  0x23,  0xF6,  0x88,  0x04,  0x01,  0x0B,  0xDE,  0x04,  0x98,  0xB2,  0x98,  0x00,  0x33,  0x00,  0x81,
+  0xC0,  0x20,  0x81,  0x62,  0x14,  0x01,  0x00,  0xA0,  0x08,  0x02,  0x43,  0x23,  0xF6,  0x88,  0x04,  0x23,
+  0xA0,  0x01,  0x44,  0x23,  0xA1,  0x01,  0x80,  0x73,  0x4D,  0x00,  0x03,  0xA3,  0xF4,  0x04,  0x00,  0x33,
+  0x27,  0x00,  0xC0,  0x88,  0x04,  0x01,  0x04,  0xDC,  0x02,  0x23,  0xA2,  0x01,  0x04,  0x23,  0xA0,  0x01,
+  0x04,  0x98,  0x26,  0x95,  0x4B,  0x00,  0xF6,  0x00,  0x4F,  0x04,  0x4F,  0x00,  0x00,  0xA3,  0x22,  0x05,
+  0x00,  0x05,  0x76,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x1C,  0x05,  0x0A,  0x85,  0x46,  0x97,  0xCD,  0x04,
+  0x24,  0x85,  0x48,  0x04,  0x84,  0x80,  0x02,  0x01,  0x03,  0xDA,  0x80,  0x23,  0x82,  0x01,  0x34,  0x85,
+  0x02,  0x23,  0xA0,  0x01,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA2,  0x40,  0x05,  0x1D,  0x01,  0x04,  0xD6,
+  0xFF,  0x23,  0x86,  0x41,  0x4B,  0x60,  0xCB,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x49,  0x00,  0x81,  0x01,
+  0x04,  0x01,  0x02,  0xC8,  0x30,  0x01,  0x80,  0x01,  0xF7,  0x04,  0x03,  0x01,  0x49,  0x04,  0x80,  0x01,
+  0xC9,  0x00,  0x00,  0x05,  0x00,  0x01,  0xFF,  0xA0,  0x60,  0x05,  0x77,  0x04,  0x01,  0x23,  0xEA,  0x00,
+  0x5D,  0x00,  0xFE,  0xC7,  0x00,  0x62,  0x00,  0x23,  0xEA,  0x00,  0x00,  0x63,  0x07,  0xA4,  0xF8,  0x05,
+  0x03,  0x03,  0x02,  0xA0,  0x8E,  0x05,  0xF4,  0x85,  0x00,  0x33,  0x2D,  0x00,  0xC0,  0x88,  0x04,  0xA0,
+  0xB8,  0x05,  0x80,  0x63,  0x00,  0x23,  0xDF,  0x00,  0x4A,  0x00,  0x06,  0x61,  0x00,  0xA2,  0xA4,  0x05,
+  0x1D,  0x01,  0x06,  0xD6,  0x02,  0x23,  0x02,  0x41,  0x82,  0x01,  0x50,  0x00,  0x62,  0x97,  0x04,  0x85,
+  0x04,  0x23,  0x02,  0x41,  0x82,  0x01,  0x04,  0x85,  0x08,  0xA0,  0xBE,  0x05,  0xF4,  0x85,  0x03,  0xA0,
+  0xC4,  0x05,  0xF4,  0x85,  0x01,  0xA0,  0xCE,  0x05,  0x88,  0x00,  0x80,  0x63,  0xCC,  0x86,  0x07,  0xA0,
+  0xEE,  0x05,  0x5F,  0x00,  0x00,  0x2B,  0xDF,  0x08,  0x00,  0xA2,  0xE6,  0x05,  0x80,  0x67,  0x80,  0x63,
+  0x01,  0xA2,  0x7A,  0x06,  0x7C,  0x85,  0x06,  0x23,  0x68,  0x98,  0x48,  0x23,  0xF6,  0x88,  0x07,  0x23,
+  0x80,  0x00,  0x06,  0x87,  0x80,  0x63,  0x7C,  0x85,  0x00,  0x23,  0xDF,  0x00,  0x00,  0x63,  0x4A,  0x00,
+  0x06,  0x61,  0x00,  0xA2,  0x36,  0x06,  0x1D,  0x01,  0x16,  0xD4,  0xC0,  0x23,  0x07,  0x41,  0x83,  0x03,
+  0x80,  0x63,  0x06,  0xA6,  0x1C,  0x06,  0x00,  0x33,  0x37,  0x00,  0xC0,  0x88,  0x1D,  0x01,  0x01,  0xD6,
+  0x20,  0x23,  0x63,  0x60,  0x83,  0x03,  0x80,  0x63,  0x02,  0x23,  0xDF,  0x00,  0x07,  0xA6,  0x7C,  0x05,
+  0xEF,  0x04,  0x6F,  0x00,  0x00,  0x63,  0x4B,  0x00,  0x06,  0x41,  0xCB,  0x00,  0x52,  0x00,  0x06,  0x61,
+  0x00,  0xA2,  0x4E,  0x06,  0x1D,  0x01,  0x03,  0xCA,  0xC0,  0x23,  0x07,  0x41,  0x00,  0x63,  0x1D,  0x01,
+  0x04,  0xCC,  0x00,  0x33,  0x00,  0x83,  0xC0,  0x20,  0x81,  0x62,  0x80,  0x23,  0x07,  0x41,  0x00,  0x63,
+  0x80,  0x67,  0x08,  0x23,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,  0x01,  0x23,  0xDF,  0x00,  0x06,  0xA6,
+  0x84,  0x06,  0x07,  0xA6,  0x7C,  0x05,  0x80,  0x67,  0x80,  0x63,  0x00,  0x33,  0x00,  0x40,  0xC0,  0x20,
+  0x81,  0x62,  0x00,  0x63,  0x00,  0x00,  0xFE,  0x95,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,  0x94,  0x06,
+  0x07,  0xA6,  0x7C,  0x05,  0x00,  0x00,  0x01,  0xA0,  0x14,  0x07,  0x00,  0x2B,  0x40,  0x0E,  0x80,  0x63,
+  0x01,  0x00,  0x06,  0xA6,  0xAA,  0x06,  0x07,  0xA6,  0x7C,  0x05,  0x40,  0x0E,  0x80,  0x63,  0x00,  0x43,
+  0x00,  0xA0,  0xA2,  0x06,  0x06,  0xA6,  0xBC,  0x06,  0x07,  0xA6,  0x7C,  0x05,  0x80,  0x67,  0x40,  0x0E,
+  0x80,  0x63,  0x07,  0xA6,  0x7C,  0x05,  0x00,  0x23,  0xDF,  0x00,  0x00,  0x63,  0x07,  0xA6,  0xD6,  0x06,
+  0x00,  0x33,  0x2A,  0x00,  0xC0,  0x88,  0x03,  0x03,  0x80,  0x63,  0x89,  0x00,  0x0A,  0x2B,  0x07,  0xA6,
+  0xE8,  0x06,  0x00,  0x33,  0x29,  0x00,  0xC0,  0x88,  0x00,  0x43,  0x00,  0xA2,  0xF4,  0x06,  0xC0,  0x0E,
+  0x80,  0x63,  0xDE,  0x86,  0xC0,  0x0E,  0x00,  0x33,  0x00,  0x80,  0xC0,  0x20,  0x81,  0x62,  0x04,  0x01,
+  0x02,  0xDA,  0x80,  0x63,  0x7C,  0x85,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,  0x8C,  0x06,  0x00,  0x33,
+  0x2C,  0x00,  0xC0,  0x88,  0x0C,  0xA2,  0x2E,  0x07,  0xFE,  0x95,  0x83,  0x03,  0x80,  0x63,  0x06,  0xA6,
+  0x2C,  0x07,  0x07,  0xA6,  0x7C,  0x05,  0x00,  0x33,  0x3D,  0x00,  0xC0,  0x88,  0x00,  0x00,  0x80,  0x67,
+  0x83,  0x03,  0x80,  0x63,  0x0C,  0xA0,  0x44,  0x07,  0x07,  0xA6,  0x7C,  0x05,  0xBF,  0x23,  0x04,  0x61,
+  0x84,  0x01,  0xE6,  0x84,  0x00,  0x63,  0xF0,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x00,  0x01,  0xF2,  0x00,
+  0x01,  0x05,  0x80,  0x01,  0x72,  0x04,  0x71,  0x00,  0x81,  0x01,  0x70,  0x04,  0x80,  0x05,  0x81,  0x05,
+  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x01,  0x01,  0xF1,  0x00,  0x70,  0x00,  0x81,  0x01,
+  0x70,  0x04,  0x71,  0x00,  0x81,  0x01,  0x72,  0x00,  0x80,  0x01,  0x71,  0x04,  0x70,  0x00,  0x80,  0x01,
+  0x70,  0x04,  0x00,  0x63,  0xF0,  0x04,  0xF2,  0x00,  0x72,  0x04,  0x00,  0x01,  0xF1,  0x00,  0x70,  0x00,
+  0x80,  0x01,  0x70,  0x04,  0x71,  0x00,  0x80,  0x01,  0x72,  0x00,  0x81,  0x01,  0x71,  0x04,  0x70,  0x00,
+  0x81,  0x01,  0x70,  0x04,  0x00,  0x63,  0x00,  0x23,  0xB3,  0x01,  0x83,  0x05,  0xA3,  0x01,  0xA2,  0x01,
+  0xA1,  0x01,  0x01,  0x23,  0xA0,  0x01,  0x00,  0x01,  0xC8,  0x00,  0x03,  0xA1,  0xC4,  0x07,  0x00,  0x33,
+  0x07,  0x00,  0xC0,  0x88,  0x80,  0x05,  0x81,  0x05,  0x04,  0x01,  0x11,  0xC8,  0x48,  0x00,  0xB0,  0x01,
+  0xB1,  0x01,  0x08,  0x23,  0xB2,  0x01,  0x05,  0x01,  0x48,  0x04,  0x00,  0x43,  0x00,  0xA2,  0xE4,  0x07,
+  0x00,  0x05,  0xDA,  0x87,  0x00,  0x01,  0xC8,  0x00,  0xFF,  0x23,  0x80,  0x01,  0x05,  0x05,  0x00,  0x63,
+  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,  0x80,  0x43,  0x76,  0x08,  0x80,  0x02,
+  0x77,  0x04,  0x00,  0x63,  0xF7,  0x04,  0x1A,  0x09,  0xF6,  0x08,  0x6E,  0x04,  0x00,  0x02,  0x00,  0xA0,
+  0x14,  0x08,  0x16,  0x88,  0x00,  0x43,  0x76,  0x08,  0x80,  0x02,  0x77,  0x04,  0x00,  0x63,  0xF3,  0x04,
+  0x00,  0x23,  0xF4,  0x00,  0x74,  0x00,  0x80,  0x43,  0xF4,  0x00,  0xCF,  0x40,  0x00,  0xA2,  0x44,  0x08,
+  0x74,  0x04,  0x02,  0x01,  0xF7,  0xC9,  0xF6,  0xD9,  0x00,  0x01,  0x01,  0xA1,  0x24,  0x08,  0x04,  0x98,
+  0x26,  0x95,  0x24,  0x88,  0x73,  0x04,  0x00,  0x63,  0xF3,  0x04,  0x75,  0x04,  0x5A,  0x88,  0x02,  0x01,
+  0x04,  0xD8,  0x46,  0x97,  0x04,  0x98,  0x26,  0x95,  0x4A,  0x88,  0x75,  0x00,  0x00,  0xA3,  0x64,  0x08,
+  0x00,  0x05,  0x4E,  0x88,  0x73,  0x04,  0x00,  0x63,  0x80,  0x7B,  0x80,  0x63,  0x06,  0xA6,  0x76,  0x08,
+  0x00,  0x33,  0x3E,  0x00,  0xC0,  0x88,  0x80,  0x67,  0x83,  0x03,  0x80,  0x63,  0x00,  0x63,  0x38,  0x2B,
+  0x9C,  0x88,  0x38,  0x2B,  0x92,  0x88,  0x32,  0x09,  0x31,  0x05,  0x92,  0x98,  0x05,  0x05,  0xB2,  0x09,
+  0x00,  0x63,  0x00,  0x32,  0x00,  0x36,  0x00,  0x3A,  0x00,  0x3E,  0x00,  0x63,  0x80,  0x32,  0x80,  0x36,
+  0x80,  0x3A,  0x80,  0x3E,  0x00,  0x63,  0x38,  0x2B,  0x40,  0x32,  0x40,  0x36,  0x40,  0x3A,  0x40,  0x3E,
+  0x00,  0x63,  0x5A,  0x20,  0xC9,  0x40,  0x00,  0xA0,  0xB2,  0x08,  0x5D,  0x00,  0xFE,  0xC3,  0x00,  0x63,
+  0x80,  0x73,  0xE6,  0x20,  0x02,  0x23,  0xE8,  0x00,  0x82,  0x73,  0xFF,  0xFD,  0x80,  0x73,  0x13,  0x23,
+  0xF6,  0x88,  0x66,  0x20,  0xC0,  0x20,  0x04,  0x23,  0xA0,  0x01,  0xA1,  0x23,  0xA1,  0x01,  0x81,  0x62,
+  0xE0,  0x88,  0x80,  0x73,  0x80,  0x77,  0x68,  0x00,  0x00,  0xA2,  0x80,  0x00,  0x03,  0xC2,  0xF1,  0xC7,
+  0x41,  0x23,  0xF6,  0x88,  0x11,  0x23,  0xA1,  0x01,  0x04,  0x23,  0xA0,  0x01,  0xE6,  0x84,
 };
 
 STATIC ushort _asc_mcode_size ASC_INITDATA = sizeof(_asc_mcode_buf);
-STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012A727FUL ;
+STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012B5442UL;
 
 #define ASC_SYN_OFFSET_ONE_DISABLE_LIST  16
 STATIC uchar _syn_offset_one_disable_cmd[ASC_SYN_OFFSET_ONE_DISABLE_LIST] =
@@ -12232,7 +12343,7 @@
         if ((cur_req = asc_dvc->cur_dvc_qng[tid_no]) == 0) {
             break;
         }
-        DvcSleepMilliSecond(100L);
+        DvcSleepMilliSecond(1000L);
         if (asc_dvc->cur_dvc_qng[tid_no] == cur_req) {
             break;
         }
@@ -12850,11 +12961,12 @@
     asc_dvc->cfg->can_tagged_qng = 0 ;
     asc_dvc->cfg->cmd_qng_enabled = 0;
     asc_dvc->dvc_cntl = ASC_DEF_DVC_CNTL;
-    asc_dvc->init_sdtr = ASC_SCSI_WIDTH_BIT_SET;
+    asc_dvc->init_sdtr = 0;
     asc_dvc->max_total_qng = ASC_DEF_MAX_TOTAL_QNG;
     asc_dvc->scsi_reset_wait = 3;
     asc_dvc->start_motor = ASC_SCSI_WIDTH_BIT_SET;
     asc_dvc->max_dma_count = AscGetMaxDmaCount(asc_dvc->bus_type);
+    asc_dvc->cfg->sdtr_enable = ASC_SCSI_WIDTH_BIT_SET;
     asc_dvc->cfg->disc_enable = ASC_SCSI_WIDTH_BIT_SET;
     asc_dvc->cfg->chip_scsi_id = ASC_DEF_CHIP_SCSI_ID;
     asc_dvc->cfg->lib_serial_no = ASC_LIB_SERIAL_NUMBER;
@@ -13009,7 +13121,7 @@
                 warn_code |= ASC_WARN_EEPROM_CHKSUM ;
             }
     }
-    asc_dvc->init_sdtr = eep_config->init_sdtr;
+    asc_dvc->cfg->sdtr_enable = eep_config->init_sdtr ;
     asc_dvc->cfg->disc_enable = eep_config->disc_enable;
     asc_dvc->cfg->cmd_qng_enabled = eep_config->use_cmd_qng;
     asc_dvc->cfg->isa_dma_speed = eep_config->isa_dma_speed;
@@ -13127,47 +13239,6 @@
     return (warn_code);
 }
 
-STATIC void
-AscInitPollIsrCallBack(
-                          ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                          ASC_QDONE_INFO * scsi_done_q
-)
-{
-    ASC_SCSI_REQ_Q *scsiq_req;
-    ASC_ISR_CALLBACK    asc_isr_callback;
-    uchar               cp_sen_len;
-    uchar               i;
-
-    ASC_DBG(1, "AscInitPollIsrCallBack: begin\n");
-    if ((scsi_done_q->d2.flag & ASC_FLAG_SCSIQ_REQ) != 0) {
-        scsiq_req = (ASC_SCSI_REQ_Q *) scsi_done_q->d2.srb_ptr;
-        scsiq_req->r3.done_stat = scsi_done_q->d3.done_stat;
-        scsiq_req->r3.host_stat = scsi_done_q->d3.host_stat;
-        scsiq_req->r3.scsi_stat = scsi_done_q->d3.scsi_stat;
-        scsiq_req->r3.scsi_msg = scsi_done_q->d3.scsi_msg;
-        ASC_DBG4(1, "AscInitPollIsrCallBack: done_stat %x, host_stat %x, scsi_stat %x, scsi_msg %x\n",
-            scsi_done_q->d3.done_stat, scsi_done_q->d3.host_stat,
-            scsi_done_q->d3.scsi_stat, scsi_done_q->d3.scsi_msg);
-        if ((scsi_done_q->d3.scsi_stat == SS_CHK_CONDITION) &&
-            (scsi_done_q->d3.host_stat == 0)) {
-            cp_sen_len = (uchar) ASC_MIN_SENSE_LEN;
-            if (scsiq_req->r1.sense_len < ASC_MIN_SENSE_LEN) {
-                cp_sen_len = (uchar) scsiq_req->r1.sense_len;
-            }
-            for (i = 0; i < cp_sen_len; i++) {
-                scsiq_req->sense[i] = scsiq_req->sense_ptr[i];
-            }
-        }
-    } else {
-        if (asc_dvc->isr_callback != 0) {
-            asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
-            (*asc_isr_callback) (asc_dvc, scsi_done_q);
-        }
-    }
-    ASC_DBG(1, "AscInitPollIsrCallBack: end\n");
-    return;
-}
-
 ASC_INITFUNC(
 STATIC int
 AscTestExternalLram(
@@ -13437,413 +13508,124 @@
     return (n_error);
 }
 
-STATIC int
-AscInitPollBegin(
-                    REG ASC_DVC_VAR asc_ptr_type * asc_dvc
-)
-{
-    PortAddr            iop_base;
-
-    iop_base = asc_dvc->iop_base;
-    AscDisableInterrupt(iop_base);
-    asc_dvc->init_state |= ASC_INIT_STATE_BEG_INQUIRY;
-    AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B, 0x00);
-    asc_dvc->use_tagged_qng = 0;
-    asc_dvc->cfg->can_tagged_qng = 0;
-    asc_dvc->saved_ptr2func = (ulong) asc_dvc->isr_callback;
-    asc_dvc->isr_callback = ASC_GET_PTR2FUNC(AscInitPollIsrCallBack);
-    return (0);
-}
-
-STATIC int
-AscInitPollEnd(
-                  REG ASC_DVC_VAR asc_ptr_type * asc_dvc
-)
-{
-    PortAddr            iop_base;
-    rint                i;
-
-    iop_base = asc_dvc->iop_base;
-    asc_dvc->isr_callback = (Ptr2Func) asc_dvc->saved_ptr2func;
-    AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B,
-                     asc_dvc->cfg->disc_enable);
-    AscWriteLramByte(iop_base, ASCV_USE_TAGGED_QNG_B,
-                     asc_dvc->use_tagged_qng);
-    AscWriteLramByte(iop_base, ASCV_CAN_TAGGED_QNG_B,
-                     asc_dvc->cfg->can_tagged_qng);
-    for (i = 0; i <= ASC_MAX_TID; i++) {
-        AscWriteLramByte(iop_base,
-                      (ushort) ((ushort) ASCV_MAX_DVC_QNG_BEG + (ushort) i),
-                         asc_dvc->max_dvc_qng[i]);
-    }
-    AscAckInterrupt(iop_base);
-    AscEnableInterrupt(iop_base);
-    asc_dvc->init_state |= ASC_INIT_STATE_END_INQUIRY;
-    return (0);
-}
-
-STATIC int
-AscInitPollTarget(
-                     REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                     REG ASC_SCSI_REQ_Q * scsiq,
-                     REG ASC_SCSI_INQUIRY * inq,
-                     REG ASC_CAP_INFO * cap_info
-)
+STATIC void
+AscAsyncFix(
+               ASC_DVC_VAR asc_ptr_type *asc_dvc,
+               uchar tid_no,
+               ASC_SCSI_INQUIRY *inq)
 {
-    uchar               tid_no, lun;
-    uchar               dvc_type;
+    uchar                dvc_type;
     ASC_SCSI_BIT_ID_TYPE tid_bits;
-    int                 dvc_found;
-    int                 support_read_cap;
-    int                 tmp_disable_init_sdtr;
-    int                 sta;
 
-    dvc_found = 0;
-    tmp_disable_init_sdtr = FALSE;
-    tid_bits = scsiq->r1.target_id;
-    lun = scsiq->r1.target_lun;
-    tid_no = ASC_TIX_TO_TID(scsiq->r2.target_ix);
-    if (((asc_dvc->init_sdtr & tid_bits) != 0) &&
-        ((asc_dvc->sdtr_done & tid_bits) == 0)) {
-        asc_dvc->init_sdtr &= ~tid_bits;
-        tmp_disable_init_sdtr = TRUE;
-    }
-    ASC_DBG(1, "AscInitPollTarget: before PollScsiInquiry\n");
-    if (PollScsiInquiry(asc_dvc, scsiq, (uchar *) inq,
-        sizeof (ASC_SCSI_INQUIRY)) == 1) {
-        dvc_found = 1;
-        dvc_type = inq->byte0.peri_dvc_type;
-        if (dvc_type != SCSI_TYPE_UNKNOWN) {
-            support_read_cap = TRUE;
-            if ((dvc_type != SCSI_TYPE_DASD)
-                && (dvc_type != SCSI_TYPE_WORM)
-                && (dvc_type != SCSI_TYPE_CDROM)
-                && (dvc_type != SCSI_TYPE_OPTMEM)) {
-                asc_dvc->start_motor &= ~tid_bits;
-                support_read_cap = FALSE;
-            }
-            if (lun == 0) {
-                if ((inq->byte3.rsp_data_fmt >= 2) ||
-                    (inq->byte2.ansi_apr_ver >= 2)) {
-                    if (inq->byte7.CmdQue) {
-                        asc_dvc->cfg->can_tagged_qng |= tid_bits;
-                        if (asc_dvc->cfg->cmd_qng_enabled & tid_bits) {
-                                asc_dvc->use_tagged_qng |= tid_bits;
-                                asc_dvc->max_dvc_qng[tid_no] = asc_dvc->cfg->max_tag_qng[tid_no];
-                        }
-                    }
-                    if (!inq->byte7.Sync) {
-                        asc_dvc->init_sdtr &= ~tid_bits;
-                        asc_dvc->sdtr_done &= ~tid_bits;
-                    } else if (tmp_disable_init_sdtr) {
-                        asc_dvc->init_sdtr |= tid_bits;
-                    }
-                } else {
-                    asc_dvc->init_sdtr &= ~tid_bits;
-                    asc_dvc->sdtr_done &= ~tid_bits;
-                    asc_dvc->use_tagged_qng &= ~tid_bits;
-                }
-            }
-            if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN) {
-                if (!(asc_dvc->init_sdtr & tid_bits)) {
-                    if ((dvc_type == SCSI_TYPE_CDROM) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                            (uchar *) "HP ", 3) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer_always |= tid_bits;
-                    }
-                    asc_dvc->pci_fix_asyn_xfer |= tid_bits;
-                    if ((dvc_type == SCSI_TYPE_PROC) || (dvc_type == SCSI_TYPE_SCANNER)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
-                    }
-                    if ((dvc_type == SCSI_TYPE_SASD) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                            (uchar *) "TANDBERG", 8) == 0) &&
-                        (AscCompareString((uchar *) inq->product_id,
-                         (uchar *) " TDC 36", 7) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
-                    }
-                    if ((dvc_type == SCSI_TYPE_SASD) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                         (uchar *) "WANGTEK ", 8) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
-                    }
+    dvc_type = inq->byte0.peri_dvc_type;
+    tid_bits = ASC_TIX_TO_TARGET_ID(tid_no);
 
-                    if ((dvc_type == SCSI_TYPE_CDROM) &&
-                       (AscCompareString((uchar *)inq->vendor_id,
-                        (uchar *)"NEC     ", 8) == 0) &&
-                        (AscCompareString((uchar *)inq->product_id,
-                         (uchar *)"CD-ROM DRIVE    ", 16) == 0)) {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits ;
-                    }
-                    if ((dvc_type == SCSI_TYPE_CDROM) &&
-                        (AscCompareString((uchar *) inq->vendor_id,
-                         (uchar *) "YAMAHA", 6) == 0)  &&
-                        (AscCompareString((uchar *) inq->product_id,
-                         (uchar *) "CDR400", 6) == 0))
-                    {
-                        asc_dvc->pci_fix_asyn_xfer &= ~tid_bits ;
-                    }
- 
-                    if (asc_dvc->pci_fix_asyn_xfer & tid_bits) {
-                        AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no,
-                                                ASYN_SDTR_DATA_FIX_PCI_REV_AB);
-                    }
-                }
-            }
-            sta = 1;
-            ASC_DBG(1, "AscInitPollTarget: before InitTestUnitReady\n");
-            sta = InitTestUnitReady(asc_dvc, scsiq);
-            if (sta == 1) {
-                if ((cap_info != 0L) && support_read_cap) {
-                    ASC_DBG(1,
-                        "AscInitPollTarget: before PollScsiReadCapacity\n");
-                    if (PollScsiReadCapacity(asc_dvc, scsiq,
-                                             cap_info) != 1) {
-                        cap_info->lba = 0L;
-                        cap_info->blk_size = 0x0000;
-                    } else {
-                    }
-                }
+    if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN) {
+        if (!(asc_dvc->init_sdtr & tid_bits)) {
+            if ((dvc_type == SCSI_TYPE_CDROM) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                    (uchar *) "HP ", 3) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer_always |= tid_bits;
+            }
+            asc_dvc->pci_fix_asyn_xfer |= tid_bits;
+            if ((dvc_type == SCSI_TYPE_PROC) ||
+                (dvc_type == SCSI_TYPE_SCANNER)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+            }
+            if ((dvc_type == SCSI_TYPE_SASD) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "TANDBERG", 8) == 0) &&
+                (AscCompareString((uchar *) inq->product_id,
+                 (uchar *) " TDC 36", 7) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+            }
+            if ((dvc_type == SCSI_TYPE_SASD) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "WANGTEK ", 8) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+            }
+
+            if ((dvc_type == SCSI_TYPE_CDROM) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "NEC     ", 8) == 0) &&
+                (AscCompareString((uchar *) inq->product_id,
+                 (uchar *) "CD-ROM DRIVE    ", 16) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+            }
+
+            if ((dvc_type == SCSI_TYPE_CDROM) &&
+                (AscCompareString((uchar *) inq->vendor_id,
+                 (uchar *) "YAMAHA", 6) == 0) &&
+                (AscCompareString((uchar *) inq->product_id,
+                 (uchar *) "CDR400", 6) == 0)) {
+                asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+            }
+            if (asc_dvc->pci_fix_asyn_xfer & tid_bits) {
+                AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no,
+                                        ASYN_SDTR_DATA_FIX_PCI_REV_AB);
             }
-        } else {
-            asc_dvc->start_motor &= ~tid_bits;
         }
-    } else if (tmp_disable_init_sdtr) {
-        asc_dvc->init_sdtr |= tid_bits;
     }
-    ASC_DBG1(1, "AscInitPollTarget: dvc_found %d\n", dvc_found);
-    return (dvc_found);
-}
-
-STATIC int
-PollQueueDone(
-                 REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                 REG ASC_SCSI_REQ_Q * scsiq,
-                 int timeout_sec
-)
-{
-    int                 status;
-    int                 retry = 0;
-
-    ASC_DBG1(1, "PollQueueDone: timeout_sec %d\n", timeout_sec);
-    do {
-        ASC_DBG(1, "PollQueueDone: before AscExeScsiQueue\n");
-        if ((status = AscExeScsiQueue(asc_dvc,
-                (ASC_SCSI_Q *) scsiq)) == 1) {
-            ASC_DBG(1, "PollQueueDone: before AscPollQDone\n");
-            if ((status = AscPollQDone(asc_dvc, scsiq,
-                                       timeout_sec)) != 1) {
-                ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-                if (status == 0x80) {
-                    if (retry++ > ASC_MAX_INIT_BUSY_RETRY) {
-                        break;
-                    }
-                    scsiq->r3.done_stat = 0;
-                    scsiq->r3.host_stat = 0;
-                    scsiq->r3.scsi_stat = 0;
-                    scsiq->r3.scsi_msg = 0;
-                    DvcSleepMilliSecond(1000);
-                    continue;
-                }
-                scsiq->r3.done_stat = 0;
-                scsiq->r3.host_stat = 0;
-                scsiq->r3.scsi_stat = 0;
-                scsiq->r3.scsi_msg = 0;
-                ASC_DBG(1, "PollQueueDone: before AscAbortSRB()\n");
-                AscAbortSRB(asc_dvc, (ulong) scsiq);
-            }
-            ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-            ASC_DBG1(1, "PollQueueDone: done_stat %x\n", scsiq->r3.done_stat);
-            return (scsiq->r3.done_stat);
-        }
-        ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-        DvcSleepMilliSecond(5);
-    } while (((status == 0) || (status == 0x80)) &&
-              retry++ < ASC_MAX_INIT_BUSY_RETRY);
-    ASC_DBG1(1, "PollQueueDone: status %x\n", status);
-    ASC_DBG(1, "PollQueueDone: done_stat QD_WITH_ERROR\n");
-    return (scsiq->r3.done_stat = QD_WITH_ERROR);
-}
-
-STATIC int
-PollScsiInquiry(
-                   REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                   REG ASC_SCSI_REQ_Q * scsiq,
-                   uchar * buf,
-                   int buf_len
-)
-{
-    if (AscScsiInquiry(asc_dvc, scsiq, buf, buf_len) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 4));
-}
-
-STATIC int
-PollScsiStartUnit(
-                     REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                     REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    if (AscScsiStartStopUnit(asc_dvc, scsiq, 1) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 40));
+    return;
 }
 
 STATIC int
-PollScsiReadCapacity(
-                        REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                        REG ASC_SCSI_REQ_Q * scsiq,
-                        REG ASC_CAP_INFO * cap_info
-)
+AscTagQueuingSafe(ASC_SCSI_INQUIRY *inq)
 {
-    ASC_CAP_INFO        scsi_cap_info;
-    int                 status;
-
-    if (AscScsiReadCapacity(asc_dvc, scsiq,
-                            (uchar *) & scsi_cap_info) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    status = PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 8);
-    if (status == 1) {
-        cap_info->lba = (ulong) * swapfarbuf4((uchar *) & scsi_cap_info.lba);
-        cap_info->blk_size = (ulong) * swapfarbuf4((uchar *) & scsi_cap_info.blk_size);
-        return (scsiq->r3.done_stat);
+    if ((inq->add_len >= 32) &&
+        (AscCompareString((uchar *) inq->vendor_id,
+            (uchar *) "QUANTUM XP34301", 15) == 0) &&
+        (AscCompareString((uchar *) inq->product_rev_level,
+            (uchar *) "1071", 4) == 0))
+    {
+        return 0;
     }
-    return (scsiq->r3.done_stat = QD_WITH_ERROR);
+    return 1;
 }
 
-STATIC ulong *
-swapfarbuf4(
-    uchar *buf
-)
+STATIC void
+AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *asc_dvc,
+                   uchar tid_no, ASC_SCSI_INQUIRY *inq)
 {
-    uchar      tmp;
+    ASC_SCSI_BIT_ID_TYPE tid_bit = ASC_TIX_TO_TARGET_ID(tid_no);
+    ASC_SCSI_BIT_ID_TYPE orig_init_sdtr, orig_use_tagged_qng;
 
-    tmp = buf[3];
-    buf[3] = buf[0];
-    buf[0] = tmp;
-
-    tmp = buf[1];
-    buf[1] = buf[2];
-    buf[2] = tmp;
-
-    return ((ulong *) buf);
-}
+    orig_init_sdtr = asc_dvc->init_sdtr;
+    orig_use_tagged_qng = asc_dvc->use_tagged_qng;
 
-STATIC int
-PollScsiTestUnitReady(
-                         REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                         REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    if (AscScsiTestUnitReady(asc_dvc, scsiq) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q *) scsiq, 12));
-}
-
-STATIC int
-InitTestUnitReady(
-                     REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                     REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    ASC_SCSI_BIT_ID_TYPE tid_bits;
-    int                 retry;
-    ASC_REQ_SENSE *sen;
-
-    retry = 0;
-    tid_bits = scsiq->r1.target_id;
-    while (retry++ < 4) {
-        PollScsiTestUnitReady(asc_dvc, scsiq);
-        if (scsiq->r3.done_stat == 0x01) {
-            return (1);
-        } else if (scsiq->r3.done_stat == QD_WITH_ERROR) {
-            sen = (ASC_REQ_SENSE *) scsiq->sense_ptr;
-            if ((scsiq->r3.scsi_stat == SS_CHK_CONDITION) &&
-                ((sen->err_code & 0x70) != 0)) {
-                if (sen->sense_key == SCSI_SENKEY_NOT_READY) {
-                    if (sen->asc == SCSI_ASC_NOMEDIA)
-                    {
-                        break;
-                    }
-                    if (asc_dvc->start_motor & tid_bits) {
-                        if (PollScsiStartUnit(asc_dvc, scsiq) == 1) {
-                            DvcSleepMilliSecond(250);
-                            continue;
-                        } else {
-                            asc_dvc->start_motor &= ~tid_bits;
-                            break;
-                        }
-                    } else {
-                        DvcSleepMilliSecond(250);
-                    }
-                } else if (sen->sense_key == SCSI_SENKEY_ATTENTION) {
-                    DvcSleepMilliSecond(250);
-                } else {
-                    break;
-                }
-            } else {
-                break;
+    asc_dvc->init_sdtr &= ~tid_bit;
+    asc_dvc->cfg->can_tagged_qng &= ~tid_bit;
+    asc_dvc->use_tagged_qng &= ~tid_bit;
+
+    if (inq->byte3.rsp_data_fmt >= 2 || inq->byte2.ansi_apr_ver >= 2) {
+        if ((asc_dvc->cfg->sdtr_enable & tid_bit) && inq->byte7.Sync) {
+            asc_dvc->init_sdtr |= tid_bit;
+        }
+        if ((asc_dvc->cfg->cmd_qng_enabled & tid_bit) && inq->byte7.CmdQue) {
+            if (AscTagQueuingSafe(inq)) {
+                asc_dvc->use_tagged_qng |= tid_bit;
+                asc_dvc->cfg->can_tagged_qng |= tid_bit;
             }
-        } else if (scsiq->r3.done_stat == QD_ABORTED_BY_HOST) {
-            break;
-        } else {
-            break;
         }
     }
-    return (0);
-}
-
-STATIC int
-AscPollQDone(
-                REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                REG ASC_SCSI_REQ_Q * scsiq,
-                int timeout_sec
-)
-{
-    int                 loop, loop_end;
-    int                 sta;
-    PortAddr            iop_base;
-
-    iop_base = asc_dvc->iop_base;
-    loop = 0;
-    loop_end = timeout_sec * 100;
-    sta = 1;
-    while (TRUE) {
-        if (asc_dvc->err_code != 0) {
-            scsiq->r3.done_stat = QD_WITH_ERROR;
-            ASC_DBG1(1, "AscPollQDone: err_code %x\n", asc_dvc->err_code);
-            sta = ERR;
-            break;
-        }
-        if (scsiq->r3.done_stat != QD_IN_PROGRESS) {
-            if ((scsiq->r3.done_stat == QD_WITH_ERROR) &&
-                (scsiq->r3.scsi_stat == SS_TARGET_BUSY)) {
-                sta = 0x80;
-            }
-            break;
-        }
-        DvcSleepMilliSecond(10);
-        if (loop++ > loop_end) {
-            ASC_DBG(1, "AscPollQDone: loop finished\n");
-            sta = 0;
-            break;
-        }
-        if (AscIsChipHalted(iop_base)) {
-            ASC_DBG(1, "AscPollQDone: AscIsChipHalted()\n");
-            AscISR(asc_dvc);
-            loop = 0;
-        } else {
-            if (AscIsIntPending(iop_base)) {
-                ASC_DBG(1, "AscPollQDone: AscIsIntPending()\n");
-                AscISR(asc_dvc);
-            }
-        }
+    if (orig_use_tagged_qng != asc_dvc->use_tagged_qng) {
+        AscWriteLramByte(asc_dvc->iop_base, ASCV_DISC_ENABLE_B,
+                         asc_dvc->cfg->disc_enable);
+        AscWriteLramByte(asc_dvc->iop_base, ASCV_USE_TAGGED_QNG_B,
+                         asc_dvc->use_tagged_qng);
+        AscWriteLramByte(asc_dvc->iop_base, ASCV_CAN_TAGGED_QNG_B,
+                         asc_dvc->cfg->can_tagged_qng);
+
+        asc_dvc->max_dvc_qng[tid_no] =
+          asc_dvc->cfg->max_tag_qng[tid_no];
+        AscWriteLramByte(asc_dvc->iop_base,
+                         (ushort) (ASCV_MAX_DVC_QNG_BEG + tid_no),
+                         asc_dvc->max_dvc_qng[tid_no]);
     }
-    return (sta);
+    if (orig_init_sdtr != asc_dvc->init_sdtr) {
+        AscAsyncFix(asc_dvc, tid_no, inq);
+    }
+    return;
 }
 
 STATIC int
@@ -14040,105 +13822,16 @@
 }
 
 
-STATIC int
-AscScsiInquiry(
-                  REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                  REG ASC_SCSI_REQ_Q * scsiq,
-                  uchar * buf,
-                  int buf_len
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, buf,
-                         (ulong) buf_len) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->cdb[0] = (uchar) SCSICMD_Inquiry;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = buf_len;
-    scsiq->cdb[5] = 0;
-    scsiq->r2.cdb_len = 6;
-    return (0);
-}
-
-STATIC int
-AscScsiReadCapacity(
-                       REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                       REG ASC_SCSI_REQ_Q * scsiq,
-                       uchar * info
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, info, 8L) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->cdb[0] = (uchar) SCSICMD_ReadCapacity;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = 0;
-    scsiq->cdb[5] = 0;
-    scsiq->cdb[6] = 0;
-    scsiq->cdb[7] = 0;
-    scsiq->cdb[8] = 0;
-    scsiq->cdb[9] = 0;
-    scsiq->r2.cdb_len = 10;
-    return (0);
-}
-
-STATIC int
-AscScsiTestUnitReady(
-                        REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                        REG ASC_SCSI_REQ_Q * scsiq
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR,
-                         (ulong) 0L) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA;
-    scsiq->cdb[0] = (uchar) SCSICMD_TestUnitReady;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = 0;
-    scsiq->cdb[5] = 0;
-    scsiq->r2.cdb_len = 6;
-    return (0);
-}
-
-STATIC int
-AscScsiStartStopUnit(
-                        REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
-                        REG ASC_SCSI_REQ_Q * scsiq,
-                        uchar op_mode
-)
-{
-    if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR, (ulong) 0L) == ERR) {
-        return (scsiq->r3.done_stat = QD_WITH_ERROR);
-    }
-    scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA;
-    scsiq->cdb[0] = (uchar) SCSICMD_StartStopUnit;
-    scsiq->cdb[1] = scsiq->r1.target_lun << 5;
-    scsiq->cdb[2] = 0;
-    scsiq->cdb[3] = 0;
-    scsiq->cdb[4] = op_mode;
-    scsiq->cdb[5] = 0;
-    scsiq->r2.cdb_len = 6;
-    return (0);
-}
-
-
 /*
  * --- Adv Library Functions
  */
 
 /* a_qswap.h */
 STATIC unsigned char _adv_mcode_buf[] ASC_INITDATA = {
-  0x9C,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0x40,  0x0A,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+  0x9C,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0x44,  0x0A,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x72,  0x01,  0xD2,  0x11,  0x00,  0x00,  0x70,  0x01,
-  0x30,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x88,  0x0F,  0x22,  0x03,  0x00,  0x00,  0x00,  0x00,
+  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x72,  0x01,  0xD6,  0x11,  0x00,  0x00,  0x70,  0x01,
+  0x30,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x90,  0x10,  0x2D,  0x03,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
@@ -14158,273 +13851,274 @@
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
   0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
-  0x00,  0x00,  0x04,  0xF7,  0x70,  0x01,  0x0C,  0x1C,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF2,  0xD2,  0x0A,
+  0x00,  0x00,  0x04,  0xF7,  0x70,  0x01,  0x0C,  0x1C,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF2,  0xD6,  0x0A,
   0x04,  0xF7,  0x70,  0x01,  0x06,  0xF7,  0x02,  0x00,  0x3E,  0x57,  0x3C,  0x56,  0x0C,  0x1C,  0x00,  0xFC,
-  0xA6,  0x00,  0x01,  0x58,  0xAA,  0x13,  0x20,  0xF0,  0x9A,  0x03,  0x06,  0xEC,  0xB9,  0x00,  0x0E,  0x47,
+  0xA6,  0x00,  0x01,  0x58,  0xAA,  0x13,  0x20,  0xF0,  0xA6,  0x03,  0x06,  0xEC,  0xB9,  0x00,  0x0E,  0x47,
   0x03,  0xE6,  0x10,  0x00,  0xCE,  0x45,  0x02,  0x13,  0x3E,  0x57,  0x06,  0xEA,  0xB9,  0x00,  0x47,  0x4B,
-  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x4E,  0x12,  0x03,  0xF6,  0xC0,  0x00,
-  0x00,  0xF2,  0x64,  0x0A,  0x41,  0x58,  0x03,  0xF6,  0xD0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x49,  0x44,
-  0x59,  0xF0,  0x0A,  0x02,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x44,  0x58,  0x00,  0xF2,
-  0xDE,  0x0D,  0x02,  0xCC,  0x4A,  0xE4,  0x01,  0x00,  0x55,  0xF0,  0x08,  0x03,  0x45,  0xF4,  0x02,  0x00,
-  0x83,  0x5A,  0x04,  0xCC,  0x01,  0x4A,  0x12,  0x12,  0x00,  0xF2,  0xDE,  0x0D,  0x00,  0xCD,  0x48,  0xE4,
-  0x01,  0x00,  0xE9,  0x13,  0x00,  0xF2,  0xC2,  0x0F,  0xFA,  0x10,  0x0E,  0x47,  0x03,  0xE6,  0x10,  0x00,
+  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x01,  0x48,  0x4E,  0x12,  0x03,  0xF6,  0xC0,  0x00,
+  0x00,  0xF2,  0x68,  0x0A,  0x41,  0x58,  0x03,  0xF6,  0xD0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x49,  0x44,
+  0x59,  0xF0,  0x0A,  0x02,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x44,  0x58,  0x00,  0xF2,
+  0xE2,  0x0D,  0x02,  0xCC,  0x4A,  0xE4,  0x01,  0x00,  0x55,  0xF0,  0x08,  0x03,  0x45,  0xF4,  0x02,  0x00,
+  0x83,  0x5A,  0x04,  0xCC,  0x01,  0x4A,  0x12,  0x12,  0x00,  0xF2,  0xE2,  0x0D,  0x00,  0xCD,  0x48,  0xE4,
+  0x01,  0x00,  0xE9,  0x13,  0x00,  0xF2,  0xC6,  0x0F,  0xFA,  0x10,  0x0E,  0x47,  0x03,  0xE6,  0x10,  0x00,
   0xCE,  0x45,  0x02,  0x13,  0x3E,  0x57,  0xCE,  0x47,  0x97,  0x13,  0x04,  0xEC,  0xB4,  0x00,  0x00,  0xF2,
-  0xDE,  0x0D,  0x00,  0xCD,  0x48,  0xE4,  0x00,  0x00,  0x12,  0x12,  0x3E,  0x57,  0x06,  0xCC,  0x45,  0xF4,
+  0xE2,  0x0D,  0x00,  0xCD,  0x48,  0xE4,  0x00,  0x00,  0x12,  0x12,  0x3E,  0x57,  0x06,  0xCC,  0x45,  0xF4,
   0x02,  0x00,  0x83,  0x5A,  0x00,  0xCC,  0x00,  0xEA,  0xB4,  0x00,  0x92,  0x10,  0x00,  0xF0,  0x8C,  0x01,
   0x43,  0xF0,  0x5C,  0x02,  0x44,  0xF0,  0x60,  0x02,  0x45,  0xF0,  0x64,  0x02,  0x46,  0xF0,  0x68,  0x02,
   0x47,  0xF0,  0x6E,  0x02,  0x48,  0xF0,  0x9E,  0x02,  0xB9,  0x54,  0x62,  0x10,  0x00,  0x1C,  0x5A,  0x10,
-  0x02,  0x1C,  0x56,  0x10,  0x1E,  0x1C,  0x52,  0x10,  0x00,  0xF2,  0x1A,  0x11,  0x50,  0x10,  0x06,  0xFC,
-  0xA8,  0x00,  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x4A,  0x0A,  0x8C,  0x10,  0x01,  0xF6,  0x01,  0x00,
-  0x01,  0xFA,  0xA8,  0x00,  0x00,  0xF2,  0x28,  0x0B,  0x06,  0x10,  0xB9,  0x54,  0x01,  0xFA,  0xA8,  0x00,
-  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x54,  0x0A,  0x01,  0xFC,  0xA8,  0x00,  0x20,  0x10,  0x58,  0x1C,
-  0x00,  0xF2,  0x18,  0x0B,  0x5A,  0x1C,  0x01,  0xF6,  0x01,  0x00,  0x38,  0x54,  0x00,  0xFA,  0xA6,  0x00,
+  0x02,  0x1C,  0x56,  0x10,  0x1E,  0x1C,  0x52,  0x10,  0x00,  0xF2,  0x1E,  0x11,  0x50,  0x10,  0x06,  0xFC,
+  0xA8,  0x00,  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x4E,  0x0A,  0x8C,  0x10,  0x01,  0xF6,  0x01,  0x00,
+  0x01,  0xFA,  0xA8,  0x00,  0x00,  0xF2,  0x2C,  0x0B,  0x06,  0x10,  0xB9,  0x54,  0x01,  0xFA,  0xA8,  0x00,
+  0x03,  0xF6,  0xBE,  0x00,  0x00,  0xF2,  0x58,  0x0A,  0x01,  0xFC,  0xA8,  0x00,  0x20,  0x10,  0x58,  0x1C,
+  0x00,  0xF2,  0x1C,  0x0B,  0x5A,  0x1C,  0x01,  0xF6,  0x01,  0x00,  0x38,  0x54,  0x00,  0xFA,  0xA6,  0x00,
   0x01,  0xFA,  0xA8,  0x00,  0x20,  0x1C,  0x00,  0xF0,  0x72,  0x01,  0x01,  0xF6,  0x01,  0x00,  0x38,  0x54,
   0x00,  0xFA,  0xA6,  0x00,  0x01,  0xFA,  0xA8,  0x00,  0x20,  0x1C,  0x00,  0xF0,  0x80,  0x01,  0x03,  0xF6,
-  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x0A,  0x13,  0x00,  0xF2,  0x34,  0x10,  0x00,  0xF2,
-  0x50,  0x0F,  0x24,  0x10,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x02,  0xF6,  0xD0,  0x00,
-  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x49,  0x44,  0x5B,  0xF0,  0x04,  0x03,  0x00,  0xF2,  0x98,  0x0F,
-  0x00,  0xF0,  0x80,  0x01,  0x00,  0xF2,  0x10,  0x10,  0x0C,  0x1C,  0x02,  0x4B,  0xBF,  0x57,  0x9E,  0x43,
-  0x77,  0x57,  0x07,  0x4B,  0x20,  0xF0,  0x9A,  0x03,  0x40,  0x1C,  0x1E,  0xF0,  0x30,  0x03,  0x26,  0xF0,
-  0x2C,  0x03,  0xA0,  0xF0,  0x1A,  0x03,  0x11,  0xF0,  0x9A,  0x03,  0x12,  0x10,  0x9F,  0xF0,  0x3E,  0x03,
-  0x46,  0x1C,  0x82,  0xE7,  0x05,  0x00,  0x9E,  0xE7,  0x11,  0x00,  0x00,  0xF0,  0x02,  0x0A,  0x0C,  0x1C,
+  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x01,  0x48,  0x0A,  0x13,  0x00,  0xF2,  0x38,  0x10,  0x00,  0xF2,
+  0x54,  0x0F,  0x24,  0x10,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x02,  0xF6,  0xD0,  0x00,
+  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x49,  0x44,  0x5B,  0xF0,  0x04,  0x03,  0x00,  0xF2,  0x9C,  0x0F,
+  0x00,  0xF0,  0x80,  0x01,  0x00,  0xF2,  0x14,  0x10,  0x0C,  0x1C,  0x02,  0x4B,  0xBF,  0x57,  0x9E,  0x43,
+  0x77,  0x57,  0x07,  0x4B,  0x20,  0xF0,  0xA6,  0x03,  0x40,  0x1C,  0x1E,  0xF0,  0x30,  0x03,  0x26,  0xF0,
+  0x2C,  0x03,  0xA0,  0xF0,  0x1A,  0x03,  0x11,  0xF0,  0xA6,  0x03,  0x12,  0x10,  0x9F,  0xF0,  0x3E,  0x03,
+  0x46,  0x1C,  0x82,  0xE7,  0x05,  0x00,  0x9E,  0xE7,  0x11,  0x00,  0x00,  0xF0,  0x06,  0x0A,  0x0C,  0x1C,
   0x48,  0x1C,  0x46,  0x1C,  0x38,  0x54,  0x00,  0xEC,  0xBA,  0x00,  0x08,  0x44,  0x00,  0xEA,  0xBA,  0x00,
-  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x08,  0x44,  0x00,  0x4C,  0x82,  0xE7,  0x02,  0x00,
-  0x00,  0xF2,  0x0E,  0x11,  0x00,  0xF2,  0x0E,  0x11,  0x06,  0xF0,  0x74,  0x03,  0x1E,  0xF0,  0xF8,  0x09,
-  0x00,  0xF0,  0xFE,  0x09,  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0x55,  0xF0,  0xA0,  0x04,  0x01,  0xE6,
-  0x0C,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,
-  0xC4,  0x11,  0x01,  0xF0,  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x46,  0x1C,  0x0C,  0x1C,  0x67,  0x1B,
-  0xBF,  0x57,  0x77,  0x57,  0x02,  0x4B,  0x48,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x8E,  0x0D,  0x30,  0x1C,
-  0x96,  0xF0,  0xB0,  0x03,  0xB1,  0xF0,  0xB4,  0x03,  0x1E,  0xF0,  0xF8,  0x09,  0x85,  0xF0,  0xFE,  0x09,
-  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0x14,  0x12,  0x01,  0xE6,  0x0C,  0x00,  0x00,  0xF2,  0x4A,  0x0D,
-  0x00,  0xF2,  0x0E,  0x11,  0x01,  0xF0,  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x03,  0xF6,  0xE0,  0x00,
-  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x55,  0xF0,  0x8C,  0x04,  0x03,  0x82,  0x03,  0xFC,  0xA0,  0x00,
-  0x9B,  0x57,  0x40,  0x12,  0x69,  0x18,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0x36,  0x04,  0x69,  0x08,
-  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0xFE,  0x09,  0x68,  0x08,  0x4C,  0x44,  0x28,  0x12,  0x44,  0x48,
-  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x45,  0x58,  0x00,  0xF2,  0xF2,  0x0D,  0x00,  0xCC,
-  0x01,  0x48,  0x55,  0xF0,  0x8C,  0x04,  0x4C,  0x44,  0xEF,  0x13,  0x00,  0xF2,  0xC2,  0x0F,  0x00,  0xF2,
-  0x10,  0x10,  0x08,  0x10,  0x68,  0x18,  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,  0x04,  0x80,  0x18,  0xE4,
-  0x10,  0x00,  0x28,  0x12,  0x01,  0xE6,  0x06,  0x00,  0x04,  0x80,  0x18,  0xE4,  0x01,  0x00,  0x04,  0x12,
-  0x01,  0xE6,  0x0D,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x04,  0xE6,  0x02,  0x00,
-  0x9E,  0xE7,  0x15,  0x00,  0x01,  0xF0,  0x18,  0x0A,  0x00,  0xF0,  0xFE,  0x09,  0x69,  0x08,  0x05,  0x80,
-  0x48,  0xE4,  0x00,  0x00,  0x0C,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,  0xB8,  0x00,  0x00,  0xF2,
-  0xB2,  0x10,  0x82,  0xE7,  0x02,  0x00,  0x1C,  0x90,  0x40,  0x5C,  0x00,  0x16,  0x01,  0xE6,  0x06,  0x00,
-  0x00,  0xF2,  0x4A,  0x0D,  0x01,  0xF0,  0x80,  0x01,  0x1E,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0x94,  0x04,
-  0x42,  0x5B,  0x06,  0xF7,  0x03,  0x00,  0x46,  0x59,  0xBF,  0x57,  0x77,  0x57,  0x01,  0xE6,  0x80,  0x00,
-  0x07,  0x80,  0x31,  0x44,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x5E,  0x13,  0x20,  0x80,  0x48,  0xE4,
-  0x03,  0x00,  0x56,  0x12,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x4C,  0x13,  0x00,  0xFC,  0xA2,  0x00,
-  0x98,  0x57,  0x55,  0xF0,  0x18,  0x05,  0x31,  0xE4,  0x40,  0x00,  0x00,  0xFC,  0xA0,  0x00,  0x98,  0x57,
-  0x36,  0x12,  0x4C,  0x1C,  0x00,  0xF2,  0x0E,  0x11,  0x89,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,
-  0x2A,  0x05,  0x82,  0xE7,  0x06,  0x00,  0x1B,  0x80,  0x48,  0xE4,  0x22,  0x00,  0x5B,  0xF0,  0x08,  0x05,
-  0x48,  0xE4,  0x20,  0x00,  0x59,  0xF0,  0x0C,  0x05,  0x00,  0xE6,  0x20,  0x00,  0x09,  0x48,  0x00,  0xF2,
-  0x0E,  0x11,  0x86,  0xF0,  0x2A,  0x05,  0x83,  0x80,  0x04,  0x10,  0x00,  0xF2,  0x9E,  0x0D,  0x00,  0xE6,
-  0x01,  0x00,  0x00,  0xEA,  0x26,  0x01,  0x01,  0xEA,  0x27,  0x01,  0x04,  0x80,  0x18,  0xE4,  0x10,  0x00,
-  0x36,  0x12,  0xB9,  0x54,  0x00,  0xF2,  0xF2,  0x0E,  0x01,  0xE6,  0x06,  0x00,  0x04,  0x80,  0x18,  0xE4,
-  0x01,  0x00,  0x04,  0x12,  0x01,  0xE6,  0x0D,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,
-  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,
-  0x01,  0xF0,  0x18,  0x0A,  0x00,  0xF0,  0xFE,  0x09,  0x00,  0xFC,  0x20,  0x01,  0x98,  0x57,  0x34,  0x12,
-  0x00,  0xFC,  0x24,  0x01,  0x98,  0x57,  0x2C,  0x13,  0xB9,  0x54,  0x00,  0xF2,  0xF2,  0x0E,  0x86,  0xF0,
-  0xA4,  0x05,  0x03,  0xF6,  0x01,  0x00,  0x00,  0xF2,  0x88,  0x0E,  0x85,  0xF0,  0x9A,  0x05,  0x82,  0xE7,
-  0x03,  0x00,  0x00,  0xF2,  0x5C,  0x0B,  0x82,  0xE7,  0x02,  0x00,  0x00,  0xFC,  0x24,  0x01,  0xB0,  0x57,
-  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFC,  0x9E,  0x00,  0x98,  0x57,  0x5A,  0x12,  0x00,  0xFC,  0xB6,  0x00,
-  0x98,  0x57,  0x52,  0x13,  0x03,  0xE6,  0x0C,  0x00,  0x00,  0xFC,  0x9C,  0x00,  0x98,  0x57,  0x04,  0x13,
-  0x03,  0xE6,  0x19,  0x00,  0x05,  0xE6,  0x08,  0x00,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,
-  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x04,  0x13,  0x05,  0xE6,  0x0F,  0x00,  0xB9,  0x54,
-  0x00,  0xF2,  0xF2,  0x0E,  0x86,  0xF0,  0x06,  0x06,  0x00,  0xF2,  0xB6,  0x0E,  0x85,  0xF0,  0xFC,  0x05,
-  0x82,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x5C,  0x0B,  0x82,  0xE7,  0x02,  0x00,  0x00,  0xFC,  0xB6,  0x00,
-  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0xF2,  0xF2,  0x0E,  0x9C,  0x32,
-  0x4E,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x8E,  0x0D,  0x30,  0x1C,  0x82,  0xE7,  0x04,  0x00,  0xB1,  0xF0,
-  0x1E,  0x06,  0x0A,  0xF0,  0x3A,  0x06,  0x05,  0xF0,  0xD2,  0x06,  0x06,  0xF0,  0xD8,  0x06,  0x09,  0xF0,
-  0x20,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0x00,  0xF0,  0xFE,  0x09,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,
-  0x30,  0x12,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x21,  0x80,  0x18,  0xE4,  0xE0,  0x00,
-  0x09,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,
-  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x99,  0xA4,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,
-  0x9A,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x34,  0x12,  0x09,  0xE7,  0x1B,  0x00,  0x00,  0xF2,
-  0x0E,  0x11,  0x21,  0x80,  0x18,  0xE4,  0xE0,  0x00,  0x09,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,
-  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,
-  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF0,  0x08,  0x09,  0xBB,  0x55,
-  0x9A,  0x81,  0x03,  0xF7,  0x20,  0x00,  0x09,  0x6F,  0x93,  0x45,  0x55,  0xF0,  0xDE,  0x06,  0xB1,  0xF0,
-  0xBE,  0x06,  0x0A,  0xF0,  0xB6,  0x06,  0x09,  0xF0,  0x20,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0x00,  0xF0,
-  0xFE,  0x09,  0x00,  0xF2,  0x5C,  0x0B,  0x47,  0x10,  0x09,  0xE7,  0x08,  0x00,  0x41,  0x10,  0x05,  0x80,
-  0x48,  0xE4,  0x00,  0x00,  0x1E,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,  0xB8,  0x00,  0x00,  0xF2,
-  0xB2,  0x10,  0x2C,  0x90,  0xAE,  0x90,  0x08,  0x50,  0x8A,  0x50,  0x38,  0x54,  0x1F,  0x40,  0x00,  0xF2,
-  0xB0,  0x0D,  0x08,  0x10,  0x08,  0x90,  0x8A,  0x90,  0x30,  0x50,  0xB2,  0x50,  0x9C,  0x32,  0x0C,  0x92,
-  0x8E,  0x92,  0x38,  0x54,  0x04,  0x80,  0x30,  0xE4,  0x08,  0x00,  0x04,  0x40,  0x0C,  0x1C,  0x00,  0xF6,
-  0x03,  0x00,  0xB1,  0xF0,  0x22,  0x07,  0x9E,  0xF0,  0x36,  0x07,  0x01,  0x48,  0x55,  0xF0,  0xF8,  0x09,
-  0x0C,  0x1C,  0x10,  0x44,  0xED,  0x10,  0x0B,  0xF0,  0x5A,  0x07,  0x0C,  0xF0,  0x5E,  0x07,  0x05,  0xF0,
-  0x4E,  0x07,  0x06,  0xF0,  0x54,  0x07,  0x09,  0xF0,  0x20,  0x09,  0x00,  0xF0,  0xFE,  0x09,  0x00,  0xF2,
-  0x5C,  0x0B,  0xCF,  0x10,  0x09,  0xE7,  0x08,  0x00,  0xC9,  0x10,  0x2E,  0x1C,  0x02,  0x10,  0x2C,  0x1C,
-  0xAA,  0xF0,  0x60,  0x07,  0xAC,  0xF0,  0x6E,  0x07,  0x40,  0x10,  0x34,  0x1C,  0xF3,  0x10,  0xAD,  0xF0,
-  0x78,  0x07,  0xC8,  0x10,  0x36,  0x1C,  0xE9,  0x10,  0x2B,  0xF0,  0x7E,  0x08,  0x6B,  0x18,  0x18,  0xF4,
-  0x00,  0xFE,  0x20,  0x12,  0x01,  0x58,  0xD2,  0xF0,  0x7E,  0x08,  0x76,  0x18,  0x18,  0xF4,  0x03,  0x00,
-  0xEC,  0x12,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xE2,  0x12,  0x0B,  0xF0,  0x60,  0x07,
-  0x0C,  0xF0,  0x60,  0x07,  0x36,  0x1C,  0x34,  0x1C,  0xB7,  0x10,  0x38,  0x54,  0xB9,  0x54,  0x84,  0x80,
-  0x19,  0xE4,  0x20,  0x00,  0xB2,  0x13,  0x85,  0x80,  0x81,  0x48,  0x66,  0x12,  0x04,  0x80,  0x18,  0xE4,
-  0x08,  0x00,  0x58,  0x13,  0x1F,  0x80,  0x08,  0x44,  0xC8,  0x44,  0x9F,  0x12,  0x1F,  0x40,  0x34,  0x91,
-  0xB6,  0x91,  0x44,  0x55,  0xE5,  0x55,  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,  0xBB,  0x55,  0x82,  0x81,
-  0xC0,  0x55,  0x48,  0xF4,  0x0F,  0x00,  0x5A,  0xF0,  0x16,  0x08,  0x4A,  0xE4,  0x17,  0x00,  0xD5,  0xF0,
-  0xF6,  0x07,  0x02,  0xF6,  0x0F,  0x00,  0x02,  0xF4,  0x02,  0x00,  0x02,  0xEA,  0xB8,  0x00,  0x04,  0x91,
-  0x86,  0x91,  0x02,  0x4B,  0x2C,  0x90,  0x08,  0x50,  0x2E,  0x90,  0x0A,  0x50,  0x2C,  0x51,  0xAE,  0x51,
-  0x00,  0xF2,  0xB2,  0x10,  0x38,  0x54,  0x00,  0xF2,  0xB0,  0x0D,  0x56,  0x10,  0x34,  0x91,  0xB6,  0x91,
-  0x0C,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x08,  0x00,  0x41,  0x12,  0x0C,  0x91,  0x8E,  0x91,  0x04,  0x80,
-  0x18,  0xE4,  0xF7,  0x00,  0x04,  0x40,  0x30,  0x90,  0xB2,  0x90,  0x36,  0x10,  0x02,  0x80,  0x48,  0xE4,
-  0x10,  0x00,  0x31,  0x12,  0x82,  0xE7,  0x10,  0x00,  0x84,  0x80,  0x19,  0xE4,  0x20,  0x00,  0x10,  0x13,
-  0x0C,  0x90,  0x8E,  0x90,  0x5D,  0xF0,  0x74,  0x07,  0x0C,  0x58,  0x8D,  0x58,  0x00,  0xF0,  0x60,  0x07,
-  0x38,  0x54,  0xB9,  0x54,  0x19,  0x80,  0xF1,  0x10,  0x3A,  0x55,  0x19,  0x81,  0xBB,  0x55,  0x10,  0x90,
-  0x92,  0x90,  0x10,  0x58,  0x91,  0x58,  0x14,  0x59,  0x95,  0x59,  0x00,  0xF0,  0x60,  0x07,  0x04,  0x80,
-  0x18,  0xE4,  0x20,  0x00,  0x06,  0x12,  0x6C,  0x19,  0x19,  0x41,  0x7C,  0x10,  0x6C,  0x19,  0x0C,  0x51,
-  0xED,  0x19,  0x8E,  0x51,  0x6B,  0x18,  0x18,  0xF4,  0x00,  0xFF,  0x02,  0x13,  0x6A,  0x10,  0x01,  0x58,
-  0xD2,  0xF0,  0xBC,  0x08,  0x76,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x0A,  0x12,  0x00,  0xFC,  0x22,  0x01,
-  0x18,  0xF4,  0x01,  0x00,  0x06,  0x13,  0x9E,  0xE7,  0x16,  0x00,  0x4C,  0x10,  0xD1,  0xF0,  0xC6,  0x08,
-  0x9E,  0xE7,  0x17,  0x00,  0x42,  0x10,  0xD0,  0xF0,  0xD0,  0x08,  0x9E,  0xE7,  0x19,  0x00,  0x38,  0x10,
-  0xCF,  0xF0,  0xDA,  0x08,  0x9E,  0xE7,  0x20,  0x00,  0x2E,  0x10,  0xCE,  0xF0,  0xE4,  0x08,  0x9E,  0xE7,
-  0x21,  0x00,  0x24,  0x10,  0xCD,  0xF0,  0xEE,  0x08,  0x9E,  0xE7,  0x22,  0x00,  0x1A,  0x10,  0xCC,  0xF0,
-  0x00,  0x09,  0x84,  0x80,  0x19,  0xE4,  0x04,  0x00,  0x06,  0x12,  0x9E,  0xE7,  0x12,  0x00,  0x08,  0x10,
-  0xCB,  0xF0,  0x08,  0x09,  0x9E,  0xE7,  0x24,  0x00,  0xB1,  0xF0,  0x08,  0x09,  0x05,  0xF0,  0x1A,  0x09,
-  0x09,  0xF0,  0x20,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0xE4,  0x10,  0x00,  0xF2,  0x5C,  0x0B,  0xE9,  0x10,
-  0x9C,  0x32,  0x82,  0xE7,  0x20,  0x00,  0x32,  0x1C,  0xE9,  0x09,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,
-  0xFE,  0x09,  0x69,  0x08,  0x01,  0xF0,  0x40,  0x09,  0x1E,  0xF0,  0xF8,  0x09,  0x00,  0xF0,  0x34,  0x09,
-  0x30,  0x44,  0x06,  0x12,  0x9E,  0xE7,  0x42,  0x00,  0xB8,  0x10,  0x04,  0xF6,  0x01,  0x00,  0xB3,  0x45,
-  0x74,  0x12,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x22,  0x13,  0x4B,  0xE4,  0x02,  0x00,  0x36,  0x12,
-  0x4B,  0xE4,  0x28,  0x00,  0xAC,  0x13,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0x03,  0xF6,
-  0xD0,  0x00,  0xFA,  0x14,  0x82,  0xE7,  0x01,  0x00,  0x00,  0xF0,  0x80,  0x01,  0x9E,  0xE7,  0x44,  0x00,
-  0x4B,  0xE4,  0x02,  0x00,  0x06,  0x12,  0x03,  0xE6,  0x02,  0x00,  0x76,  0x10,  0x00,  0xF2,  0x9E,  0x0D,
-  0x03,  0xE6,  0x02,  0x00,  0x6C,  0x10,  0x00,  0xF2,  0x9E,  0x0D,  0x19,  0x82,  0x34,  0x46,  0x0A,  0x13,
-  0x03,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x43,  0x00,  0x68,  0x10,  0x04,  0x80,  0x30,  0xE4,  0x20,  0x00,
-  0x04,  0x40,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0x82,  0xE7,  0x01,  0x00,  0x06,  0xF7,
-  0x02,  0x00,  0x00,  0xF0,  0x08,  0x03,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x06,  0x12,  0x03,  0xE6,
-  0x02,  0x00,  0x3E,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x3A,  0x12,  0x04,  0x80,  0x18,  0xE4,
-  0xFD,  0x00,  0x04,  0x40,  0x1C,  0x1C,  0x9D,  0xF0,  0xE6,  0x09,  0x1C,  0x1C,  0x9D,  0xF0,  0xEC,  0x09,
-  0xC1,  0x10,  0x9E,  0xE7,  0x13,  0x00,  0x0A,  0x10,  0x9E,  0xE7,  0x41,  0x00,  0x04,  0x10,  0x9E,  0xE7,
-  0x24,  0x00,  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0xD5,  0xF0,  0x8A,  0x02,  0x04,  0xE6,  0x04,  0x00,
-  0x06,  0x10,  0x04,  0xE6,  0x04,  0x00,  0x9D,  0x41,  0x1C,  0x42,  0x9F,  0xE7,  0x00,  0x00,  0x06,  0xF7,
-  0x02,  0x00,  0x03,  0xF6,  0xE0,  0x00,  0x3C,  0x14,  0x44,  0x58,  0x45,  0x58,  0x00,  0xF2,  0xF2,  0x0D,
-  0x00,  0xF2,  0x7A,  0x10,  0x00,  0xF2,  0xC2,  0x0F,  0x3C,  0x14,  0x1E,  0x1C,  0x00,  0xF0,  0x80,  0x01,
-  0x12,  0x1C,  0x22,  0x1C,  0xD2,  0x14,  0x00,  0xF0,  0x72,  0x01,  0x83,  0x59,  0x03,  0xDC,  0x73,  0x57,
-  0x80,  0x5D,  0x00,  0x16,  0x83,  0x59,  0x03,  0xDC,  0x38,  0x54,  0x70,  0x57,  0x33,  0x54,  0x3B,  0x54,
-  0x80,  0x5D,  0x00,  0x16,  0x03,  0x57,  0x83,  0x59,  0x38,  0x54,  0x00,  0xCC,  0x00,  0x16,  0x03,  0x57,
-  0x83,  0x59,  0x00,  0x4C,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,  0x01,  0x00,  0x0E,  0x12,  0x48,  0xE4,
-  0x05,  0x00,  0x08,  0x12,  0x00,  0xF2,  0xB8,  0x11,  0x00,  0xF2,  0xC4,  0x11,  0xC1,  0x5A,  0x3A,  0x55,
-  0x02,  0xEC,  0xB5,  0x00,  0x45,  0x59,  0x00,  0xF2,  0xF2,  0x0D,  0x83,  0x58,  0x30,  0xE7,  0x00,  0x00,
-  0x10,  0x4D,  0x30,  0xE7,  0x40,  0x00,  0x10,  0x4F,  0x38,  0x90,  0xBA,  0x90,  0x10,  0x5C,  0x80,  0x5C,
-  0x83,  0x5A,  0x10,  0x4E,  0x04,  0xEA,  0xB5,  0x00,  0x43,  0x5B,  0x03,  0xF4,  0xE0,  0x00,  0x83,  0x59,
-  0x04,  0xCC,  0x01,  0x4A,  0x0A,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,  0x00,  0xF2,  0x34,  0x10,
-  0x00,  0x16,  0x08,  0x1C,  0x00,  0xFC,  0xAC,  0x00,  0x06,  0x58,  0x67,  0x18,  0x18,  0xF4,  0x8F,  0xE1,
-  0x01,  0xFC,  0xAE,  0x00,  0x19,  0xF4,  0x70,  0x1E,  0xB0,  0x54,  0x07,  0x58,  0x00,  0xFC,  0xB0,  0x00,
-  0x08,  0x58,  0x00,  0xFC,  0xB2,  0x00,  0x09,  0x58,  0x0A,  0x1C,  0x00,  0xE6,  0x0F,  0x00,  0x00,  0xEA,
-  0xB9,  0x00,  0x38,  0x54,  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFA,  0xB6,  0x00,  0x18,  0x1C,  0x14,  0x1C,
-  0x10,  0x1C,  0x32,  0x1C,  0x12,  0x1C,  0x00,  0x16,  0x3E,  0x57,  0x0C,  0x14,  0x0E,  0x47,  0x07,  0xE6,
-  0x10,  0x00,  0xCE,  0x47,  0xF5,  0x13,  0x00,  0x16,  0x00,  0xF2,  0x9E,  0x0D,  0x02,  0x4B,  0x03,  0xF6,
-  0xE0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x01,  0x48,  0x20,  0x12,  0x44,  0x58,  0x45,  0x58,  0x9E,  0xE7,
-  0x15,  0x00,  0x9C,  0xE7,  0x04,  0x00,  0x00,  0xF2,  0xF2,  0x0D,  0x00,  0xF2,  0x7A,  0x10,  0x00,  0xF2,
-  0xC2,  0x0F,  0x00,  0xF2,  0x76,  0x0A,  0x1E,  0x1C,  0xD5,  0x10,  0x00,  0x16,  0x69,  0x08,  0x48,  0xE4,
-  0x04,  0x00,  0x64,  0x12,  0x48,  0xE4,  0x02,  0x00,  0x20,  0x12,  0x48,  0xE4,  0x03,  0x00,  0x1A,  0x12,
-  0x48,  0xE4,  0x08,  0x00,  0x14,  0x12,  0x48,  0xE4,  0x01,  0x00,  0xF0,  0x12,  0x48,  0xE4,  0x07,  0x00,
-  0x12,  0x12,  0x01,  0xE6,  0x07,  0x00,  0x00,  0xF2,  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x05,  0xF0,
-  0x5C,  0x0B,  0x00,  0x16,  0x00,  0xE6,  0x01,  0x00,  0x00,  0xEA,  0x99,  0x00,  0x02,  0x80,  0x48,  0xE4,
-  0x03,  0x00,  0xB9,  0x12,  0x48,  0xE4,  0x06,  0x00,  0xB3,  0x12,  0x01,  0xE6,  0x06,  0x00,  0x00,  0xF2,
-  0x4A,  0x0D,  0x00,  0xF2,  0x0E,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,  0x01,  0xF0,
-  0x18,  0x0A,  0x00,  0xF0,  0xFE,  0x09,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,  0x10,  0x00,  0x1C,  0x12,
-  0x82,  0xE7,  0x08,  0x00,  0x3C,  0x56,  0x03,  0x82,  0x00,  0xF2,  0xDE,  0x0D,  0x30,  0xE7,  0x08,  0x00,
-  0x04,  0xF7,  0x70,  0x01,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF0,  0x80,  0x01,  0x6C,  0x19,  0xED,  0x19,
-  0x5D,  0xF0,  0xD0,  0x0B,  0x44,  0x55,  0xE5,  0x55,  0x59,  0xF0,  0x4E,  0x0C,  0x04,  0x55,  0xA5,  0x55,
-  0x1F,  0x80,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,  0x49,  0x44,  0x2E,  0x13,  0x01,  0xEC,
-  0xB8,  0x00,  0x41,  0xE4,  0x02,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x49,  0xE4,  0x11,  0x00,  0x59,  0xF0,
-  0x2A,  0x0C,  0x01,  0xE6,  0x17,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x02,  0x4B,  0x88,  0x90,  0xAC,  0x50,
-  0x8A,  0x90,  0xAE,  0x50,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,  0x10,  0x44,  0x02,  0x4B,
-  0x1F,  0x40,  0xC0,  0x44,  0x00,  0xF2,  0xB0,  0x0D,  0x04,  0x55,  0xA5,  0x55,  0x9F,  0x10,  0x0C,  0x51,
-  0x8E,  0x51,  0x30,  0x90,  0xB2,  0x90,  0x00,  0x56,  0xA1,  0x56,  0x30,  0x50,  0xB2,  0x50,  0x34,  0x90,
-  0xB6,  0x90,  0x40,  0x56,  0xE1,  0x56,  0x34,  0x50,  0xB6,  0x50,  0x65,  0x10,  0xB1,  0xF0,  0x6C,  0x0C,
-  0x85,  0xF0,  0xC6,  0x0B,  0xE9,  0x09,  0x4B,  0xE4,  0x03,  0x00,  0x78,  0x12,  0x4B,  0xE4,  0x02,  0x00,
-  0x01,  0x13,  0xB1,  0xF0,  0x82,  0x0C,  0x85,  0xF0,  0xC6,  0x0B,  0x69,  0x08,  0x48,  0xE4,  0x03,  0x00,
-  0xD5,  0xF0,  0x82,  0x0B,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0xC6,  0x0B,  0xE8,  0x09,  0x3C,  0x56,
-  0x00,  0xFC,  0x20,  0x01,  0x98,  0x57,  0x02,  0x13,  0xBB,  0x45,  0x4B,  0xE4,  0x00,  0x00,  0x08,  0x12,
-  0x03,  0xE6,  0x01,  0x00,  0x04,  0xF6,  0x00,  0x80,  0xA8,  0x14,  0xD2,  0x14,  0x30,  0x1C,  0x02,  0x80,
-  0x48,  0xE4,  0x03,  0x00,  0x10,  0x13,  0x00,  0xFC,  0xB6,  0x00,  0x98,  0x57,  0x02,  0x13,  0x4C,  0x1C,
-  0x3E,  0x1C,  0x00,  0xF0,  0x8A,  0x0B,  0x00,  0xFC,  0x24,  0x01,  0xB0,  0x57,  0x00,  0xFA,  0x24,  0x01,
-  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0x8A,  0x0B,  0x00,  0xF2,  0x88,  0x0E,
-  0x00,  0xF0,  0x8A,  0x0B,  0xB1,  0xF0,  0xF4,  0x0C,  0x85,  0xF0,  0x82,  0x0B,  0x69,  0x08,  0x48,  0xE4,
-  0x01,  0x00,  0xD5,  0xF0,  0x82,  0x0B,  0xFC,  0x14,  0x42,  0x58,  0x6C,  0x14,  0x80,  0x14,  0x30,  0x1C,
-  0x4A,  0xF4,  0x02,  0x00,  0x55,  0xF0,  0x82,  0x0B,  0x4A,  0xF4,  0x01,  0x00,  0x0E,  0x12,  0x02,  0x80,
-  0x48,  0xE4,  0x03,  0x00,  0x06,  0x13,  0x3E,  0x1C,  0x00,  0xF0,  0x8A,  0x0B,  0x00,  0xFC,  0xB6,  0x00,
-  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,
-  0x8A,  0x0B,  0x00,  0xF2,  0xB6,  0x0E,  0x00,  0xF0,  0x8A,  0x0B,  0x4C,  0x1C,  0xB1,  0xF0,  0x4C,  0x0D,
-  0x85,  0xF0,  0x58,  0x0D,  0x69,  0x08,  0xF3,  0x10,  0x86,  0xF0,  0x60,  0x0D,  0x4E,  0x1C,  0x89,  0x48,
-  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,
-  0xFF,  0x7F,  0x30,  0x56,  0x00,  0x5C,  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,
-  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x30,  0x56,  0x00,  0x5C,  0x00,  0x16,  0x00,  0xF6,
-  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x0B,  0x58,  0x00,  0x16,  0x03,  0xF6,
-  0x24,  0x01,  0x00,  0xF2,  0x54,  0x0A,  0x03,  0xF6,  0xB6,  0x00,  0x00,  0xF2,  0x54,  0x0A,  0x00,  0x16,
-  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,  0x18,  0xF4,  0xFF,  0x00,  0x00,  0x54,  0x00,  0x54,  0x00,  0x54,
-  0x00,  0xF4,  0x08,  0x00,  0xE1,  0x18,  0x80,  0x54,  0x03,  0x58,  0x00,  0xDD,  0x01,  0xDD,  0x02,  0xDD,
-  0x03,  0xDC,  0x02,  0x4B,  0x30,  0x50,  0xB2,  0x50,  0x34,  0x51,  0xB6,  0x51,  0x00,  0x16,  0x45,  0x5A,
-  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,  0x05,  0xF4,  0x02,  0x12,  0x83,  0x5A,
-  0x00,  0x16,  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,  0x05,  0xF4,  0x00,  0x12,
-  0x83,  0x5A,  0x00,  0x16,  0x38,  0x54,  0xBB,  0x55,  0x3C,  0x56,  0xBD,  0x56,  0x00,  0xF2,  0x0E,  0x11,
-  0x85,  0xF0,  0x7E,  0x0E,  0xE9,  0x09,  0xC1,  0x59,  0x00,  0xF2,  0x0E,  0x11,  0x85,  0xF0,  0x7E,  0x0E,
-  0xE8,  0x0A,  0x83,  0x55,  0x83,  0x55,  0x4B,  0xF4,  0x90,  0x01,  0x5C,  0xF0,  0x32,  0x0E,  0xBD,  0x56,
-  0x40,  0x10,  0x4B,  0xF4,  0x30,  0x00,  0x59,  0xF0,  0x44,  0x0E,  0x01,  0xF6,  0x0C,  0x00,  0x00,  0xF6,
-  0x01,  0x00,  0x2E,  0x10,  0x02,  0xFC,  0x9C,  0x00,  0x9A,  0x57,  0x14,  0x13,  0x4B,  0xF4,  0x64,  0x00,
-  0x59,  0xF0,  0x60,  0x0E,  0x03,  0xF6,  0x64,  0x00,  0x01,  0xF6,  0x19,  0x00,  0x00,  0xF6,  0x01,  0x00,
-  0x43,  0xF4,  0x33,  0x00,  0x56,  0xF0,  0x72,  0x0E,  0x04,  0xF4,  0x00,  0x01,  0x43,  0xF4,  0x19,  0x00,
-  0xF3,  0x10,  0xB4,  0x56,  0xC3,  0x58,  0x02,  0xFC,  0x9E,  0x00,  0x9A,  0x57,  0x08,  0x13,  0x3C,  0x56,
-  0x00,  0xF6,  0x02,  0x00,  0x00,  0x16,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,
-  0x86,  0xF0,  0xB4,  0x0E,  0x09,  0xE7,  0x02,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xB4,  0x0E,
-  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xB4,  0x0E,  0x4E,  0x1C,  0x89,  0x49,
-  0x00,  0xF2,  0x0E,  0x11,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,
-  0xEE,  0x0E,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xEE,  0x0E,  0x09,  0xE7,
-  0x01,  0x00,  0x00,  0xF2,  0x0E,  0x11,  0x86,  0xF0,  0xEE,  0x0E,  0x89,  0x49,  0x00,  0xF2,  0x0E,  0x11,
-  0x86,  0xF0,  0xEE,  0x0E,  0x4E,  0x1C,  0x89,  0x4A,  0x00,  0xF2,  0x0E,  0x11,  0x00,  0x16,  0x3C,  0x56,
-  0x00,  0x16,  0x00,  0xEC,  0x26,  0x01,  0x48,  0xE4,  0x01,  0x00,  0x1E,  0x13,  0x38,  0x44,  0x00,  0xEA,
-  0x26,  0x01,  0x49,  0xF4,  0x00,  0x00,  0x04,  0x12,  0x4E,  0x1C,  0x02,  0x10,  0x4C,  0x1C,  0x01,  0xEC,
-  0x27,  0x01,  0x89,  0x48,  0x00,  0xF2,  0x0E,  0x11,  0x02,  0x14,  0x00,  0x16,  0x85,  0xF0,  0x4E,  0x0F,
-  0x38,  0x54,  0x00,  0xEA,  0x99,  0x00,  0x00,  0xF2,  0x5C,  0x0B,  0x02,  0x80,  0x48,  0xE4,  0x06,  0x00,
-  0x1C,  0x13,  0x00,  0xEC,  0x99,  0x00,  0x48,  0xE4,  0x01,  0x00,  0x0A,  0x12,  0x04,  0x80,  0x30,  0xE4,
-  0x01,  0x00,  0x04,  0x40,  0x08,  0x10,  0x04,  0x80,  0x18,  0xE4,  0xFE,  0x00,  0x04,  0x40,  0x00,  0x16,
-  0x02,  0xF6,  0xE0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x81,  0x48,  0x22,  0x12,  0x00,  0x4E,
-  0x83,  0x5A,  0x90,  0x4C,  0x20,  0xE7,  0x00,  0x00,  0xC3,  0x58,  0x1B,  0xF4,  0xFF,  0x00,  0x83,  0x55,
-  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,  0x8B,  0x55,  0x83,  0x59,  0x00,  0x4E,  0x00,  0x16,
-  0x00,  0x4E,  0x02,  0xF6,  0xF0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x00,  0x4E,  0x83,  0x5A,  0x30,  0xE7,
-  0x00,  0x00,  0x20,  0xE7,  0x00,  0x00,  0x00,  0x16,  0x02,  0xF6,  0xF0,  0x00,  0x02,  0x57,  0x03,  0x59,
-  0x01,  0xCC,  0x00,  0x4E,  0x83,  0x5A,  0x30,  0xE7,  0x00,  0x00,  0x80,  0x4C,  0xC3,  0x58,  0x1B,  0xF4,
-  0xFF,  0x00,  0x83,  0x55,  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,  0x83,  0x59,  0x00,  0x4E,
-  0x00,  0x16,  0x03,  0xF6,  0xE0,  0x00,  0x03,  0x57,  0x83,  0x59,  0x3A,  0x55,  0x02,  0xCC,  0x45,  0x5A,
-  0x00,  0xF2,  0xF2,  0x0D,  0xC0,  0x5A,  0x40,  0x5C,  0x38,  0x54,  0x00,  0xCD,  0x01,  0xCC,  0x4A,  0x46,
-  0x0A,  0x13,  0x83,  0x59,  0x00,  0x4C,  0x01,  0x48,  0x16,  0x13,  0x0C,  0x10,  0xC5,  0x58,  0x00,  0xF2,
-  0xF2,  0x0D,  0x00,  0x4C,  0x01,  0x48,  0x08,  0x13,  0x05,  0xF6,  0xF0,  0x00,  0x05,  0x57,  0x08,  0x10,
-  0x45,  0x58,  0x00,  0xF2,  0xF2,  0x0D,  0x8D,  0x56,  0x83,  0x5A,  0x80,  0x4C,  0x05,  0x17,  0x00,  0x16,
-  0x02,  0x4B,  0x06,  0xF7,  0x04,  0x00,  0x62,  0x0B,  0x03,  0x82,  0x00,  0xF2,  0xDE,  0x0D,  0x02,  0x80,
-  0x00,  0x4C,  0x45,  0xF4,  0x02,  0x00,  0x52,  0x14,  0x06,  0xF7,  0x02,  0x00,  0x06,  0x14,  0x00,  0xF2,
-  0x50,  0x0F,  0x00,  0x16,  0x02,  0x4B,  0x01,  0xF6,  0xFF,  0x00,  0x38,  0x1C,  0x05,  0xF4,  0x04,  0x00,
-  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x1D,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,  0x4A,  0x10,  0x9C,  0x14,
-  0x01,  0x48,  0x1C,  0x13,  0x0E,  0xF7,  0x3C,  0x00,  0x03,  0xF7,  0x04,  0x00,  0xAF,  0x19,  0x03,  0x42,
-  0x45,  0xF4,  0x02,  0x00,  0x83,  0x5A,  0x02,  0xCC,  0x02,  0x41,  0x45,  0xF4,  0x02,  0x00,  0x00,  0x16,
-  0x91,  0x44,  0xD5,  0xF0,  0x3A,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x01,  0xF6,  0xFF,  0x00,  0x38,  0x1C,
-  0x05,  0xF4,  0x04,  0x00,  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x0E,  0xF7,  0x3C,  0x00,  0x03,  0xF7,
-  0x04,  0x00,  0x0F,  0x79,  0x1C,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,  0x98,  0x10,  0x4E,  0x14,  0x01,  0x48,
-  0x06,  0x13,  0x45,  0xF4,  0x04,  0x00,  0x00,  0x16,  0x91,  0x44,  0xD5,  0xF0,  0x7E,  0x10,  0x00,  0xF0,
-  0x9E,  0x02,  0x02,  0xF6,  0xFF,  0x00,  0x38,  0x1C,  0x2C,  0xBC,  0xAE,  0xBC,  0xE2,  0x08,  0x00,  0xEC,
-  0xB8,  0x00,  0x02,  0x48,  0x1D,  0xF7,  0x80,  0x00,  0xB8,  0xF0,  0xC8,  0x10,  0x1E,  0x14,  0x01,  0x48,
-  0x0E,  0x13,  0x0E,  0xF7,  0x80,  0x00,  0x38,  0x54,  0x03,  0x58,  0xAF,  0x19,  0x82,  0x48,  0x00,  0x16,
-  0x82,  0x48,  0x12,  0x45,  0xD5,  0xF0,  0xB6,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x39,  0xF0,  0xF4,  0x10,
-  0x38,  0x44,  0x00,  0x16,  0x7E,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x04,  0x13,  0x61,  0x18,  0x00,  0x16,
-  0x38,  0x1C,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xF1,  0x12,  0xE3,  0x10,  0x30,  0x44,
-  0x30,  0x44,  0x30,  0x44,  0xB1,  0xF0,  0x14,  0x11,  0x00,  0x16,  0x3E,  0x57,  0x03,  0xF6,  0xE0,  0x00,
-  0x03,  0x57,  0x83,  0x59,  0x04,  0xCC,  0x01,  0x4A,  0x6A,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,
-  0x02,  0x4B,  0x70,  0x14,  0x34,  0x13,  0x02,  0x80,  0x48,  0xE4,  0x08,  0x00,  0x18,  0x12,  0x9C,  0xE7,
-  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,  0x00,  0xF2,  0xC2,  0x0F,  0x00,  0xF2,  0x76,  0x0A,  0x1E,  0x1C,
-  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x30,  0xE4,  0x10,  0x00,  0x04,  0x40,  0x00,  0xF2,  0xDE,  0x0D,
-  0x20,  0xE7,  0x01,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x04,  0xDC,  0x01,  0x4A,  0x24,  0x12,
-  0x45,  0x5A,  0x00,  0xF2,  0xF2,  0x0D,  0x43,  0x5B,  0x06,  0xEC,  0x98,  0x00,  0x00,  0xF2,  0x34,  0x10,
-  0xC6,  0x59,  0x20,  0x14,  0x0A,  0x13,  0x00,  0xF2,  0xC2,  0x0F,  0x00,  0xF2,  0x10,  0x10,  0xA7,  0x10,
-  0x83,  0x5A,  0xD7,  0x10,  0x0E,  0x47,  0x07,  0xE6,  0x10,  0x00,  0xCE,  0x47,  0x5A,  0xF0,  0x1C,  0x11,
-  0xB9,  0x54,  0x00,  0x16,  0x14,  0x90,  0x96,  0x90,  0x02,  0xFC,  0xA8,  0x00,  0x03,  0xFC,  0xAA,  0x00,
-  0x48,  0x55,  0x02,  0x13,  0xC9,  0x55,  0x00,  0x16,  0x00,  0xEC,  0xBA,  0x00,  0x10,  0x44,  0x00,  0xEA,
-  0xBA,  0x00,  0x00,  0x16,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x64,  0x0A,  0x10,  0x44,  0x00,  0x4C,
-  0x00,  0x16 };
+  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x08,  0x44,  0x00,  0x4C,  0x82,  0xE7,  0x02,  0x00,
+  0x00,  0xF2,  0x12,  0x11,  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0x70,  0x03,  0x00,  0xF2,  0x60,  0x0B,
+  0x06,  0xF0,  0x80,  0x03,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,  0xFC,  0x09,  0x00,  0xF0,  0x02,  0x0A,
+  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0x55,  0xF0,  0xAC,  0x04,  0x01,  0xE6,  0x0C,  0x00,  0x00,  0xF2,
+  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,  0x01,  0xF0,
+  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x46,  0x1C,  0x0C,  0x1C,  0x67,  0x1B,  0xBF,  0x57,  0x77,  0x57,
+  0x02,  0x4B,  0x48,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x92,  0x0D,  0x30,  0x1C,  0x96,  0xF0,  0xBC,  0x03,
+  0xB1,  0xF0,  0xC0,  0x03,  0x1E,  0xF0,  0xFC,  0x09,  0x85,  0xF0,  0x02,  0x0A,  0x00,  0xFC,  0xBE,  0x00,
+  0x98,  0x57,  0x14,  0x12,  0x01,  0xE6,  0x0C,  0x00,  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,
+  0x01,  0xF0,  0x7C,  0x02,  0x00,  0xF0,  0x8A,  0x02,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,
+  0x01,  0x48,  0x55,  0xF0,  0x98,  0x04,  0x03,  0x82,  0x03,  0xFC,  0xA0,  0x00,  0x9B,  0x57,  0x40,  0x12,
+  0x69,  0x18,  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0x42,  0x04,  0x69,  0x08,  0x00,  0xF2,  0x12,  0x11,
+  0x85,  0xF0,  0x02,  0x0A,  0x68,  0x08,  0x4C,  0x44,  0x28,  0x12,  0x44,  0x48,  0x03,  0xF6,  0xE0,  0x00,
+  0x00,  0xF2,  0x68,  0x0A,  0x45,  0x58,  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0xCC,  0x01,  0x48,  0x55,  0xF0,
+  0x98,  0x04,  0x4C,  0x44,  0xEF,  0x13,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,  0x14,  0x10,  0x08,  0x10,
+  0x68,  0x18,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,  0x04,  0x80,  0x18,  0xE4,  0x10,  0x00,  0x28,  0x12,
+  0x01,  0xE6,  0x06,  0x00,  0x04,  0x80,  0x18,  0xE4,  0x01,  0x00,  0x04,  0x12,  0x01,  0xE6,  0x0D,  0x00,
+  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,
+  0x01,  0xF0,  0x1C,  0x0A,  0x00,  0xF0,  0x02,  0x0A,  0x69,  0x08,  0x05,  0x80,  0x48,  0xE4,  0x00,  0x00,
+  0x0C,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,  0xB8,  0x00,  0x00,  0xF2,  0xB6,  0x10,  0x82,  0xE7,
+  0x02,  0x00,  0x1C,  0x90,  0x40,  0x5C,  0x00,  0x16,  0x01,  0xE6,  0x06,  0x00,  0x00,  0xF2,  0x4E,  0x0D,
+  0x01,  0xF0,  0x80,  0x01,  0x1E,  0xF0,  0x80,  0x01,  0x00,  0xF0,  0xA0,  0x04,  0x42,  0x5B,  0x06,  0xF7,
+  0x03,  0x00,  0x46,  0x59,  0xBF,  0x57,  0x77,  0x57,  0x01,  0xE6,  0x80,  0x00,  0x07,  0x80,  0x31,  0x44,
+  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x56,  0x13,  0x20,  0x80,  0x48,  0xE4,  0x03,  0x00,  0x4E,  0x12,
+  0x00,  0xFC,  0xA2,  0x00,  0x98,  0x57,  0x55,  0xF0,  0x1C,  0x05,  0x31,  0xE4,  0x40,  0x00,  0x00,  0xFC,
+  0xA0,  0x00,  0x98,  0x57,  0x36,  0x12,  0x4C,  0x1C,  0x00,  0xF2,  0x12,  0x11,  0x89,  0x48,  0x00,  0xF2,
+  0x12,  0x11,  0x86,  0xF0,  0x2E,  0x05,  0x82,  0xE7,  0x06,  0x00,  0x1B,  0x80,  0x48,  0xE4,  0x22,  0x00,
+  0x5B,  0xF0,  0x0C,  0x05,  0x48,  0xE4,  0x20,  0x00,  0x59,  0xF0,  0x10,  0x05,  0x00,  0xE6,  0x20,  0x00,
+  0x09,  0x48,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0x2E,  0x05,  0x83,  0x80,  0x04,  0x10,  0x00,  0xF2,
+  0xA2,  0x0D,  0x00,  0xE6,  0x01,  0x00,  0x00,  0xEA,  0x26,  0x01,  0x01,  0xEA,  0x27,  0x01,  0x04,  0x80,
+  0x18,  0xE4,  0x10,  0x00,  0x36,  0x12,  0xB9,  0x54,  0x00,  0xF2,  0xF6,  0x0E,  0x01,  0xE6,  0x06,  0x00,
+  0x04,  0x80,  0x18,  0xE4,  0x01,  0x00,  0x04,  0x12,  0x01,  0xE6,  0x0D,  0x00,  0x00,  0xF2,  0x4E,  0x0D,
+  0x00,  0xF2,  0x12,  0x11,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,  0x04,  0xE6,  0x02,  0x00,
+  0x9E,  0xE7,  0x15,  0x00,  0x01,  0xF0,  0x1C,  0x0A,  0x00,  0xF0,  0x02,  0x0A,  0x00,  0xFC,  0x20,  0x01,
+  0x98,  0x57,  0x34,  0x12,  0x00,  0xFC,  0x24,  0x01,  0x98,  0x57,  0x2C,  0x13,  0xB9,  0x54,  0x00,  0xF2,
+  0xF6,  0x0E,  0x86,  0xF0,  0xA8,  0x05,  0x03,  0xF6,  0x01,  0x00,  0x00,  0xF2,  0x8C,  0x0E,  0x85,  0xF0,
+  0x9E,  0x05,  0x82,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x60,  0x0B,  0x82,  0xE7,  0x02,  0x00,  0x00,  0xFC,
+  0x24,  0x01,  0xB0,  0x57,  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFC,  0x9E,  0x00,  0x98,  0x57,  0x5A,  0x12,
+  0x00,  0xFC,  0xB6,  0x00,  0x98,  0x57,  0x52,  0x13,  0x03,  0xE6,  0x0C,  0x00,  0x00,  0xFC,  0x9C,  0x00,
+  0x98,  0x57,  0x04,  0x13,  0x03,  0xE6,  0x19,  0x00,  0x05,  0xE6,  0x08,  0x00,  0x00,  0xF6,  0x00,  0x01,
+  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x04,  0x13,  0x05,  0xE6,
+  0x0F,  0x00,  0xB9,  0x54,  0x00,  0xF2,  0xF6,  0x0E,  0x86,  0xF0,  0x0A,  0x06,  0x00,  0xF2,  0xBA,  0x0E,
+  0x85,  0xF0,  0x00,  0x06,  0x82,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x60,  0x0B,  0x82,  0xE7,  0x02,  0x00,
+  0x00,  0xFC,  0xB6,  0x00,  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0xF2,
+  0xF6,  0x0E,  0x9C,  0x32,  0x4E,  0x1C,  0x32,  0x1C,  0x00,  0xF2,  0x92,  0x0D,  0x30,  0x1C,  0x82,  0xE7,
+  0x04,  0x00,  0xB1,  0xF0,  0x22,  0x06,  0x0A,  0xF0,  0x3E,  0x06,  0x05,  0xF0,  0xD6,  0x06,  0x06,  0xF0,
+  0xDC,  0x06,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,  0xFC,  0x09,  0x00,  0xF0,  0x02,  0x0A,  0x04,  0x80,
+  0x18,  0xE4,  0x20,  0x00,  0x30,  0x12,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x21,  0x80,
+  0x18,  0xE4,  0xE0,  0x00,  0x09,  0x48,  0x00,  0xF2,  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,
+  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x99,  0xA4,  0x00,  0xF2,  0x12,  0x11,
+  0x09,  0xE7,  0x00,  0x00,  0x9A,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x34,  0x12,  0x09,  0xE7,
+  0x1B,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x21,  0x80,  0x18,  0xE4,  0xE0,  0x00,  0x09,  0x48,  0x00,  0xF2,
+  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF2,
+  0x12,  0x11,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x09,  0xE7,  0x00,  0x00,  0x00,  0xF0,
+  0x0C,  0x09,  0xBB,  0x55,  0x9A,  0x81,  0x03,  0xF7,  0x20,  0x00,  0x09,  0x6F,  0x93,  0x45,  0x55,  0xF0,
+  0xE2,  0x06,  0xB1,  0xF0,  0xC2,  0x06,  0x0A,  0xF0,  0xBA,  0x06,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,
+  0xFC,  0x09,  0x00,  0xF0,  0x02,  0x0A,  0x00,  0xF2,  0x60,  0x0B,  0x47,  0x10,  0x09,  0xE7,  0x08,  0x00,
+  0x41,  0x10,  0x05,  0x80,  0x48,  0xE4,  0x00,  0x00,  0x1E,  0x12,  0x00,  0xE6,  0x11,  0x00,  0x00,  0xEA,
+  0xB8,  0x00,  0x00,  0xF2,  0xB6,  0x10,  0x2C,  0x90,  0xAE,  0x90,  0x08,  0x50,  0x8A,  0x50,  0x38,  0x54,
+  0x1F,  0x40,  0x00,  0xF2,  0xB4,  0x0D,  0x08,  0x10,  0x08,  0x90,  0x8A,  0x90,  0x30,  0x50,  0xB2,  0x50,
+  0x9C,  0x32,  0x0C,  0x92,  0x8E,  0x92,  0x38,  0x54,  0x04,  0x80,  0x30,  0xE4,  0x08,  0x00,  0x04,  0x40,
+  0x0C,  0x1C,  0x00,  0xF6,  0x03,  0x00,  0xB1,  0xF0,  0x26,  0x07,  0x9E,  0xF0,  0x3A,  0x07,  0x01,  0x48,
+  0x55,  0xF0,  0xFC,  0x09,  0x0C,  0x1C,  0x10,  0x44,  0xED,  0x10,  0x0B,  0xF0,  0x5E,  0x07,  0x0C,  0xF0,
+  0x62,  0x07,  0x05,  0xF0,  0x52,  0x07,  0x06,  0xF0,  0x58,  0x07,  0x09,  0xF0,  0x24,  0x09,  0x00,  0xF0,
+  0x02,  0x0A,  0x00,  0xF2,  0x60,  0x0B,  0xCF,  0x10,  0x09,  0xE7,  0x08,  0x00,  0xC9,  0x10,  0x2E,  0x1C,
+  0x02,  0x10,  0x2C,  0x1C,  0xAA,  0xF0,  0x64,  0x07,  0xAC,  0xF0,  0x72,  0x07,  0x40,  0x10,  0x34,  0x1C,
+  0xF3,  0x10,  0xAD,  0xF0,  0x7C,  0x07,  0xC8,  0x10,  0x36,  0x1C,  0xE9,  0x10,  0x2B,  0xF0,  0x82,  0x08,
+  0x6B,  0x18,  0x18,  0xF4,  0x00,  0xFE,  0x20,  0x12,  0x01,  0x58,  0xD2,  0xF0,  0x82,  0x08,  0x76,  0x18,
+  0x18,  0xF4,  0x03,  0x00,  0xEC,  0x12,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xE2,  0x12,
+  0x0B,  0xF0,  0x64,  0x07,  0x0C,  0xF0,  0x64,  0x07,  0x36,  0x1C,  0x34,  0x1C,  0xB7,  0x10,  0x38,  0x54,
+  0xB9,  0x54,  0x84,  0x80,  0x19,  0xE4,  0x20,  0x00,  0xB2,  0x13,  0x85,  0x80,  0x81,  0x48,  0x66,  0x12,
+  0x04,  0x80,  0x18,  0xE4,  0x08,  0x00,  0x58,  0x13,  0x1F,  0x80,  0x08,  0x44,  0xC8,  0x44,  0x9F,  0x12,
+  0x1F,  0x40,  0x34,  0x91,  0xB6,  0x91,  0x44,  0x55,  0xE5,  0x55,  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,
+  0xBB,  0x55,  0x82,  0x81,  0xC0,  0x55,  0x48,  0xF4,  0x0F,  0x00,  0x5A,  0xF0,  0x1A,  0x08,  0x4A,  0xE4,
+  0x17,  0x00,  0xD5,  0xF0,  0xFA,  0x07,  0x02,  0xF6,  0x0F,  0x00,  0x02,  0xF4,  0x02,  0x00,  0x02,  0xEA,
+  0xB8,  0x00,  0x04,  0x91,  0x86,  0x91,  0x02,  0x4B,  0x2C,  0x90,  0x08,  0x50,  0x2E,  0x90,  0x0A,  0x50,
+  0x2C,  0x51,  0xAE,  0x51,  0x00,  0xF2,  0xB6,  0x10,  0x38,  0x54,  0x00,  0xF2,  0xB4,  0x0D,  0x56,  0x10,
+  0x34,  0x91,  0xB6,  0x91,  0x0C,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x08,  0x00,  0x41,  0x12,  0x0C,  0x91,
+  0x8E,  0x91,  0x04,  0x80,  0x18,  0xE4,  0xF7,  0x00,  0x04,  0x40,  0x30,  0x90,  0xB2,  0x90,  0x36,  0x10,
+  0x02,  0x80,  0x48,  0xE4,  0x10,  0x00,  0x31,  0x12,  0x82,  0xE7,  0x10,  0x00,  0x84,  0x80,  0x19,  0xE4,
+  0x20,  0x00,  0x10,  0x13,  0x0C,  0x90,  0x8E,  0x90,  0x5D,  0xF0,  0x78,  0x07,  0x0C,  0x58,  0x8D,  0x58,
+  0x00,  0xF0,  0x64,  0x07,  0x38,  0x54,  0xB9,  0x54,  0x19,  0x80,  0xF1,  0x10,  0x3A,  0x55,  0x19,  0x81,
+  0xBB,  0x55,  0x10,  0x90,  0x92,  0x90,  0x10,  0x58,  0x91,  0x58,  0x14,  0x59,  0x95,  0x59,  0x00,  0xF0,
+  0x64,  0x07,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x06,  0x12,  0x6C,  0x19,  0x19,  0x41,  0x7C,  0x10,
+  0x6C,  0x19,  0x0C,  0x51,  0xED,  0x19,  0x8E,  0x51,  0x6B,  0x18,  0x18,  0xF4,  0x00,  0xFF,  0x02,  0x13,
+  0x6A,  0x10,  0x01,  0x58,  0xD2,  0xF0,  0xC0,  0x08,  0x76,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x0A,  0x12,
+  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0x06,  0x13,  0x9E,  0xE7,  0x16,  0x00,  0x4C,  0x10,
+  0xD1,  0xF0,  0xCA,  0x08,  0x9E,  0xE7,  0x17,  0x00,  0x42,  0x10,  0xD0,  0xF0,  0xD4,  0x08,  0x9E,  0xE7,
+  0x19,  0x00,  0x38,  0x10,  0xCF,  0xF0,  0xDE,  0x08,  0x9E,  0xE7,  0x20,  0x00,  0x2E,  0x10,  0xCE,  0xF0,
+  0xE8,  0x08,  0x9E,  0xE7,  0x21,  0x00,  0x24,  0x10,  0xCD,  0xF0,  0xF2,  0x08,  0x9E,  0xE7,  0x22,  0x00,
+  0x1A,  0x10,  0xCC,  0xF0,  0x04,  0x09,  0x84,  0x80,  0x19,  0xE4,  0x04,  0x00,  0x06,  0x12,  0x9E,  0xE7,
+  0x12,  0x00,  0x08,  0x10,  0xCB,  0xF0,  0x0C,  0x09,  0x9E,  0xE7,  0x24,  0x00,  0xB1,  0xF0,  0x0C,  0x09,
+  0x05,  0xF0,  0x1E,  0x09,  0x09,  0xF0,  0x24,  0x09,  0x1E,  0xF0,  0xFC,  0x09,  0xE4,  0x10,  0x00,  0xF2,
+  0x60,  0x0B,  0xE9,  0x10,  0x9C,  0x32,  0x82,  0xE7,  0x20,  0x00,  0x32,  0x1C,  0xE9,  0x09,  0x00,  0xF2,
+  0x12,  0x11,  0x85,  0xF0,  0x02,  0x0A,  0x69,  0x08,  0x01,  0xF0,  0x44,  0x09,  0x1E,  0xF0,  0xFC,  0x09,
+  0x00,  0xF0,  0x38,  0x09,  0x30,  0x44,  0x06,  0x12,  0x9E,  0xE7,  0x42,  0x00,  0xB8,  0x10,  0x04,  0xF6,
+  0x01,  0x00,  0xB3,  0x45,  0x74,  0x12,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,  0x22,  0x13,  0x4B,  0xE4,
+  0x02,  0x00,  0x36,  0x12,  0x4B,  0xE4,  0x28,  0x00,  0xAC,  0x13,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,
+  0xC8,  0x11,  0x03,  0xF6,  0xD0,  0x00,  0xFA,  0x14,  0x82,  0xE7,  0x01,  0x00,  0x00,  0xF0,  0x80,  0x01,
+  0x9E,  0xE7,  0x44,  0x00,  0x4B,  0xE4,  0x02,  0x00,  0x06,  0x12,  0x03,  0xE6,  0x02,  0x00,  0x76,  0x10,
+  0x00,  0xF2,  0xA2,  0x0D,  0x03,  0xE6,  0x02,  0x00,  0x6C,  0x10,  0x00,  0xF2,  0xA2,  0x0D,  0x19,  0x82,
+  0x34,  0x46,  0x0A,  0x13,  0x03,  0xE6,  0x02,  0x00,  0x9E,  0xE7,  0x43,  0x00,  0x68,  0x10,  0x04,  0x80,
+  0x30,  0xE4,  0x20,  0x00,  0x04,  0x40,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,  0x82,  0xE7,
+  0x01,  0x00,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF0,  0x08,  0x03,  0x04,  0x80,  0x18,  0xE4,  0x20,  0x00,
+  0x06,  0x12,  0x03,  0xE6,  0x02,  0x00,  0x3E,  0x10,  0x04,  0x80,  0x18,  0xE4,  0x02,  0x00,  0x3A,  0x12,
+  0x04,  0x80,  0x18,  0xE4,  0xFD,  0x00,  0x04,  0x40,  0x1C,  0x1C,  0x9D,  0xF0,  0xEA,  0x09,  0x1C,  0x1C,
+  0x9D,  0xF0,  0xF0,  0x09,  0xC1,  0x10,  0x9E,  0xE7,  0x13,  0x00,  0x0A,  0x10,  0x9E,  0xE7,  0x41,  0x00,
+  0x04,  0x10,  0x9E,  0xE7,  0x24,  0x00,  0x00,  0xFC,  0xBE,  0x00,  0x98,  0x57,  0xD5,  0xF0,  0x8A,  0x02,
+  0x04,  0xE6,  0x04,  0x00,  0x06,  0x10,  0x04,  0xE6,  0x04,  0x00,  0x9D,  0x41,  0x1C,  0x42,  0x9F,  0xE7,
+  0x00,  0x00,  0x06,  0xF7,  0x02,  0x00,  0x03,  0xF6,  0xE0,  0x00,  0x3C,  0x14,  0x44,  0x58,  0x45,  0x58,
+  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0xF2,  0x7E,  0x10,  0x00,  0xF2,  0xC6,  0x0F,  0x3C,  0x14,  0x1E,  0x1C,
+  0x00,  0xF0,  0x80,  0x01,  0x12,  0x1C,  0x22,  0x1C,  0xD2,  0x14,  0x00,  0xF0,  0x72,  0x01,  0x83,  0x59,
+  0x03,  0xDC,  0x73,  0x57,  0x80,  0x5D,  0x00,  0x16,  0x83,  0x59,  0x03,  0xDC,  0x38,  0x54,  0x70,  0x57,
+  0x33,  0x54,  0x3B,  0x54,  0x80,  0x5D,  0x00,  0x16,  0x03,  0x57,  0x83,  0x59,  0x38,  0x54,  0x00,  0xCC,
+  0x00,  0x16,  0x03,  0x57,  0x83,  0x59,  0x00,  0x4C,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,  0x01,  0x00,
+  0x0E,  0x12,  0x48,  0xE4,  0x05,  0x00,  0x08,  0x12,  0x00,  0xF2,  0xBC,  0x11,  0x00,  0xF2,  0xC8,  0x11,
+  0xC1,  0x5A,  0x3A,  0x55,  0x02,  0xEC,  0xB5,  0x00,  0x45,  0x59,  0x00,  0xF2,  0xF6,  0x0D,  0x83,  0x58,
+  0x30,  0xE7,  0x00,  0x00,  0x10,  0x4D,  0x30,  0xE7,  0x40,  0x00,  0x10,  0x4F,  0x38,  0x90,  0xBA,  0x90,
+  0x10,  0x5C,  0x80,  0x5C,  0x83,  0x5A,  0x10,  0x4E,  0x04,  0xEA,  0xB5,  0x00,  0x43,  0x5B,  0x03,  0xF4,
+  0xE0,  0x00,  0x83,  0x59,  0x04,  0xCC,  0x01,  0x4A,  0x0A,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,
+  0x00,  0xF2,  0x38,  0x10,  0x00,  0x16,  0x08,  0x1C,  0x00,  0xFC,  0xAC,  0x00,  0x06,  0x58,  0x67,  0x18,
+  0x18,  0xF4,  0x8F,  0xE1,  0x01,  0xFC,  0xAE,  0x00,  0x19,  0xF4,  0x70,  0x1E,  0xB0,  0x54,  0x07,  0x58,
+  0x00,  0xFC,  0xB0,  0x00,  0x08,  0x58,  0x00,  0xFC,  0xB2,  0x00,  0x09,  0x58,  0x0A,  0x1C,  0x00,  0xE6,
+  0x0F,  0x00,  0x00,  0xEA,  0xB9,  0x00,  0x38,  0x54,  0x00,  0xFA,  0x24,  0x01,  0x00,  0xFA,  0xB6,  0x00,
+  0x18,  0x1C,  0x14,  0x1C,  0x10,  0x1C,  0x32,  0x1C,  0x12,  0x1C,  0x00,  0x16,  0x3E,  0x57,  0x0C,  0x14,
+  0x0E,  0x47,  0x07,  0xE6,  0x10,  0x00,  0xCE,  0x47,  0xF5,  0x13,  0x00,  0x16,  0x00,  0xF2,  0xA2,  0x0D,
+  0x02,  0x4B,  0x03,  0xF6,  0xE0,  0x00,  0x00,  0xF2,  0x68,  0x0A,  0x01,  0x48,  0x20,  0x12,  0x44,  0x58,
+  0x45,  0x58,  0x9E,  0xE7,  0x15,  0x00,  0x9C,  0xE7,  0x04,  0x00,  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0xF2,
+  0x7E,  0x10,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,  0x7A,  0x0A,  0x1E,  0x1C,  0xD5,  0x10,  0x00,  0x16,
+  0x69,  0x08,  0x48,  0xE4,  0x04,  0x00,  0x64,  0x12,  0x48,  0xE4,  0x02,  0x00,  0x20,  0x12,  0x48,  0xE4,
+  0x03,  0x00,  0x1A,  0x12,  0x48,  0xE4,  0x08,  0x00,  0x14,  0x12,  0x48,  0xE4,  0x01,  0x00,  0xF0,  0x12,
+  0x48,  0xE4,  0x07,  0x00,  0x12,  0x12,  0x01,  0xE6,  0x07,  0x00,  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,
+  0x12,  0x11,  0x05,  0xF0,  0x60,  0x0B,  0x00,  0x16,  0x00,  0xE6,  0x01,  0x00,  0x00,  0xEA,  0x99,  0x00,
+  0x02,  0x80,  0x48,  0xE4,  0x03,  0x00,  0xE7,  0x12,  0x48,  0xE4,  0x06,  0x00,  0xE1,  0x12,  0x01,  0xE6,
+  0x06,  0x00,  0x00,  0xF2,  0x4E,  0x0D,  0x00,  0xF2,  0x12,  0x11,  0x04,  0xE6,  0x02,  0x00,  0x9E,  0xE7,
+  0x15,  0x00,  0x01,  0xF0,  0x1C,  0x0A,  0x00,  0xF0,  0x02,  0x0A,  0x00,  0x16,  0x02,  0x80,  0x48,  0xE4,
+  0x10,  0x00,  0x1C,  0x12,  0x82,  0xE7,  0x08,  0x00,  0x3C,  0x56,  0x03,  0x82,  0x00,  0xF2,  0xE2,  0x0D,
+  0x30,  0xE7,  0x08,  0x00,  0x04,  0xF7,  0x70,  0x01,  0x06,  0xF7,  0x02,  0x00,  0x00,  0xF0,  0x80,  0x01,
+  0x6C,  0x19,  0xED,  0x19,  0x5D,  0xF0,  0xD4,  0x0B,  0x44,  0x55,  0xE5,  0x55,  0x59,  0xF0,  0x52,  0x0C,
+  0x04,  0x55,  0xA5,  0x55,  0x1F,  0x80,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,  0x49,  0x44,
+  0x2E,  0x13,  0x01,  0xEC,  0xB8,  0x00,  0x41,  0xE4,  0x02,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x49,  0xE4,
+  0x11,  0x00,  0x59,  0xF0,  0x2E,  0x0C,  0x01,  0xE6,  0x17,  0x00,  0x01,  0xEA,  0xB8,  0x00,  0x02,  0x4B,
+  0x88,  0x90,  0xAC,  0x50,  0x8A,  0x90,  0xAE,  0x50,  0x01,  0xEC,  0xB8,  0x00,  0x82,  0x48,  0x82,  0x80,
+  0x10,  0x44,  0x02,  0x4B,  0x1F,  0x40,  0xC0,  0x44,  0x00,  0xF2,  0xB4,  0x0D,  0x04,  0x55,  0xA5,  0x55,
+  0x9F,  0x10,  0x0C,  0x51,  0x8E,  0x51,  0x30,  0x90,  0xB2,  0x90,  0x00,  0x56,  0xA1,  0x56,  0x30,  0x50,
+  0xB2,  0x50,  0x34,  0x90,  0xB6,  0x90,  0x40,  0x56,  0xE1,  0x56,  0x34,  0x50,  0xB6,  0x50,  0x65,  0x10,
+  0xB1,  0xF0,  0x70,  0x0C,  0x85,  0xF0,  0xCA,  0x0B,  0xE9,  0x09,  0x4B,  0xE4,  0x03,  0x00,  0x78,  0x12,
+  0x4B,  0xE4,  0x02,  0x00,  0x01,  0x13,  0xB1,  0xF0,  0x86,  0x0C,  0x85,  0xF0,  0xCA,  0x0B,  0x69,  0x08,
+  0x48,  0xE4,  0x03,  0x00,  0xD5,  0xF0,  0x86,  0x0B,  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0xCA,  0x0B,
+  0xE8,  0x09,  0x3C,  0x56,  0x00,  0xFC,  0x20,  0x01,  0x98,  0x57,  0x02,  0x13,  0xBB,  0x45,  0x4B,  0xE4,
+  0x00,  0x00,  0x08,  0x12,  0x03,  0xE6,  0x01,  0x00,  0x04,  0xF6,  0x00,  0x80,  0xA8,  0x14,  0xD2,  0x14,
+  0x30,  0x1C,  0x02,  0x80,  0x48,  0xE4,  0x03,  0x00,  0x10,  0x13,  0x00,  0xFC,  0xB6,  0x00,  0x98,  0x57,
+  0x02,  0x13,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF0,  0x8E,  0x0B,  0x00,  0xFC,  0x24,  0x01,  0xB0,  0x57,
+  0x00,  0xFA,  0x24,  0x01,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0x8E,  0x0B,
+  0x00,  0xF2,  0x8C,  0x0E,  0x00,  0xF0,  0x8E,  0x0B,  0xB1,  0xF0,  0xF8,  0x0C,  0x85,  0xF0,  0x86,  0x0B,
+  0x69,  0x08,  0x48,  0xE4,  0x01,  0x00,  0xD5,  0xF0,  0x86,  0x0B,  0xFC,  0x14,  0x42,  0x58,  0x6C,  0x14,
+  0x80,  0x14,  0x30,  0x1C,  0x4A,  0xF4,  0x02,  0x00,  0x55,  0xF0,  0x86,  0x0B,  0x4A,  0xF4,  0x01,  0x00,
+  0x0E,  0x12,  0x02,  0x80,  0x48,  0xE4,  0x03,  0x00,  0x06,  0x13,  0x3E,  0x1C,  0x00,  0xF0,  0x8E,  0x0B,
+  0x00,  0xFC,  0xB6,  0x00,  0xB0,  0x57,  0x00,  0xFA,  0xB6,  0x00,  0x4C,  0x1C,  0x3E,  0x1C,  0x00,  0xF2,
+  0x12,  0x11,  0x86,  0xF0,  0x8E,  0x0B,  0x00,  0xF2,  0xBA,  0x0E,  0x00,  0xF0,  0x8E,  0x0B,  0x4C,  0x1C,
+  0xB1,  0xF0,  0x50,  0x0D,  0x85,  0xF0,  0x5C,  0x0D,  0x69,  0x08,  0xF3,  0x10,  0x86,  0xF0,  0x64,  0x0D,
+  0x4E,  0x1C,  0x89,  0x48,  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,
+  0x00,  0xDC,  0x18,  0xF4,  0xFF,  0x7F,  0x30,  0x56,  0x00,  0x5C,  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,
+  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x18,  0xF4,  0x00,  0x80,  0x30,  0x56,  0x00,  0x5C,
+  0x00,  0x16,  0x00,  0xF6,  0x00,  0x01,  0x00,  0x57,  0x00,  0x57,  0x03,  0x58,  0x00,  0xDC,  0x0B,  0x58,
+  0x00,  0x16,  0x03,  0xF6,  0x24,  0x01,  0x00,  0xF2,  0x58,  0x0A,  0x03,  0xF6,  0xB6,  0x00,  0x00,  0xF2,
+  0x58,  0x0A,  0x00,  0x16,  0x02,  0xEC,  0xB8,  0x00,  0x02,  0x49,  0x18,  0xF4,  0xFF,  0x00,  0x00,  0x54,
+  0x00,  0x54,  0x00,  0x54,  0x00,  0xF4,  0x08,  0x00,  0xE1,  0x18,  0x80,  0x54,  0x03,  0x58,  0x00,  0xDD,
+  0x01,  0xDD,  0x02,  0xDD,  0x03,  0xDC,  0x02,  0x4B,  0x30,  0x50,  0xB2,  0x50,  0x34,  0x51,  0xB6,  0x51,
+  0x00,  0x16,  0x45,  0x5A,  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,  0x05,  0xF4,
+  0x02,  0x12,  0x83,  0x5A,  0x00,  0x16,  0x1D,  0xF4,  0xFF,  0x00,  0x85,  0x56,  0x85,  0x56,  0x85,  0x56,
+  0x05,  0xF4,  0x00,  0x12,  0x83,  0x5A,  0x00,  0x16,  0x38,  0x54,  0xBB,  0x55,  0x3C,  0x56,  0xBD,  0x56,
+  0x00,  0xF2,  0x12,  0x11,  0x85,  0xF0,  0x82,  0x0E,  0xE9,  0x09,  0xC1,  0x59,  0x00,  0xF2,  0x12,  0x11,
+  0x85,  0xF0,  0x82,  0x0E,  0xE8,  0x0A,  0x83,  0x55,  0x83,  0x55,  0x4B,  0xF4,  0x90,  0x01,  0x5C,  0xF0,
+  0x36,  0x0E,  0xBD,  0x56,  0x40,  0x10,  0x4B,  0xF4,  0x30,  0x00,  0x59,  0xF0,  0x48,  0x0E,  0x01,  0xF6,
+  0x0C,  0x00,  0x00,  0xF6,  0x01,  0x00,  0x2E,  0x10,  0x02,  0xFC,  0x9C,  0x00,  0x9A,  0x57,  0x14,  0x13,
+  0x4B,  0xF4,  0x64,  0x00,  0x59,  0xF0,  0x64,  0x0E,  0x03,  0xF6,  0x64,  0x00,  0x01,  0xF6,  0x19,  0x00,
+  0x00,  0xF6,  0x01,  0x00,  0x43,  0xF4,  0x33,  0x00,  0x56,  0xF0,  0x76,  0x0E,  0x04,  0xF4,  0x00,  0x01,
+  0x43,  0xF4,  0x19,  0x00,  0xF3,  0x10,  0xB4,  0x56,  0xC3,  0x58,  0x02,  0xFC,  0x9E,  0x00,  0x9A,  0x57,
+  0x08,  0x13,  0x3C,  0x56,  0x00,  0xF6,  0x02,  0x00,  0x00,  0x16,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,
+  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xB8,  0x0E,  0x09,  0xE7,  0x02,  0x00,  0x00,  0xF2,  0x12,  0x11,
+  0x86,  0xF0,  0xB8,  0x0E,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xB8,  0x0E,
+  0x4E,  0x1C,  0x89,  0x49,  0x00,  0xF2,  0x12,  0x11,  0x00,  0x16,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,
+  0x12,  0x11,  0x86,  0xF0,  0xF2,  0x0E,  0x09,  0xE7,  0x03,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,
+  0xF2,  0x0E,  0x09,  0xE7,  0x01,  0x00,  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xF2,  0x0E,  0x89,  0x49,
+  0x00,  0xF2,  0x12,  0x11,  0x86,  0xF0,  0xF2,  0x0E,  0x4E,  0x1C,  0x89,  0x4A,  0x00,  0xF2,  0x12,  0x11,
+  0x00,  0x16,  0x3C,  0x56,  0x00,  0x16,  0x00,  0xEC,  0x26,  0x01,  0x48,  0xE4,  0x01,  0x00,  0x1E,  0x13,
+  0x38,  0x44,  0x00,  0xEA,  0x26,  0x01,  0x49,  0xF4,  0x00,  0x00,  0x04,  0x12,  0x4E,  0x1C,  0x02,  0x10,
+  0x4C,  0x1C,  0x01,  0xEC,  0x27,  0x01,  0x89,  0x48,  0x00,  0xF2,  0x12,  0x11,  0x02,  0x14,  0x00,  0x16,
+  0x85,  0xF0,  0x52,  0x0F,  0x38,  0x54,  0x00,  0xEA,  0x99,  0x00,  0x00,  0xF2,  0x60,  0x0B,  0x02,  0x80,
+  0x48,  0xE4,  0x06,  0x00,  0x1C,  0x13,  0x00,  0xEC,  0x99,  0x00,  0x48,  0xE4,  0x01,  0x00,  0x0A,  0x12,
+  0x04,  0x80,  0x30,  0xE4,  0x01,  0x00,  0x04,  0x40,  0x08,  0x10,  0x04,  0x80,  0x18,  0xE4,  0xFE,  0x00,
+  0x04,  0x40,  0x00,  0x16,  0x02,  0xF6,  0xE0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x81,  0x48,
+  0x22,  0x12,  0x00,  0x4E,  0x83,  0x5A,  0x90,  0x4C,  0x20,  0xE7,  0x00,  0x00,  0xC3,  0x58,  0x1B,  0xF4,
+  0xFF,  0x00,  0x83,  0x55,  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,  0x8B,  0x55,  0x83,  0x59,
+  0x00,  0x4E,  0x00,  0x16,  0x00,  0x4E,  0x02,  0xF6,  0xF0,  0x00,  0x02,  0x57,  0x03,  0x59,  0x00,  0x4E,
+  0x83,  0x5A,  0x30,  0xE7,  0x00,  0x00,  0x20,  0xE7,  0x00,  0x00,  0x00,  0x16,  0x02,  0xF6,  0xF0,  0x00,
+  0x02,  0x57,  0x03,  0x59,  0x01,  0xCC,  0x00,  0x4E,  0x83,  0x5A,  0x30,  0xE7,  0x00,  0x00,  0x80,  0x4C,
+  0xC3,  0x58,  0x1B,  0xF4,  0xFF,  0x00,  0x83,  0x55,  0x83,  0x55,  0x83,  0x55,  0x03,  0xF4,  0x00,  0x12,
+  0x83,  0x59,  0x00,  0x4E,  0x00,  0x16,  0x03,  0xF6,  0xE0,  0x00,  0x03,  0x57,  0x83,  0x59,  0x3A,  0x55,
+  0x02,  0xCC,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,  0xC0,  0x5A,  0x40,  0x5C,  0x38,  0x54,  0x00,  0xCD,
+  0x01,  0xCC,  0x4A,  0x46,  0x0A,  0x13,  0x83,  0x59,  0x00,  0x4C,  0x01,  0x48,  0x16,  0x13,  0x0C,  0x10,
+  0xC5,  0x58,  0x00,  0xF2,  0xF6,  0x0D,  0x00,  0x4C,  0x01,  0x48,  0x08,  0x13,  0x05,  0xF6,  0xF0,  0x00,
+  0x05,  0x57,  0x08,  0x10,  0x45,  0x58,  0x00,  0xF2,  0xF6,  0x0D,  0x8D,  0x56,  0x83,  0x5A,  0x80,  0x4C,
+  0x05,  0x17,  0x00,  0x16,  0x02,  0x4B,  0x06,  0xF7,  0x04,  0x00,  0x62,  0x0B,  0x03,  0x82,  0x00,  0xF2,
+  0xE2,  0x0D,  0x02,  0x80,  0x00,  0x4C,  0x45,  0xF4,  0x02,  0x00,  0x52,  0x14,  0x06,  0xF7,  0x02,  0x00,
+  0x06,  0x14,  0x00,  0xF2,  0x54,  0x0F,  0x00,  0x16,  0x02,  0x4B,  0x01,  0xF6,  0xFF,  0x00,  0x38,  0x1C,
+  0x05,  0xF4,  0x04,  0x00,  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x1D,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,
+  0x4E,  0x10,  0x9C,  0x14,  0x01,  0x48,  0x1C,  0x13,  0x0E,  0xF7,  0x3C,  0x00,  0x03,  0xF7,  0x04,  0x00,
+  0xAF,  0x19,  0x03,  0x42,  0x45,  0xF4,  0x02,  0x00,  0x83,  0x5A,  0x02,  0xCC,  0x02,  0x41,  0x45,  0xF4,
+  0x02,  0x00,  0x00,  0x16,  0x91,  0x44,  0xD5,  0xF0,  0x3E,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x01,  0xF6,
+  0xFF,  0x00,  0x38,  0x1C,  0x05,  0xF4,  0x04,  0x00,  0x83,  0x5A,  0x18,  0xDF,  0x19,  0xDF,  0x0E,  0xF7,
+  0x3C,  0x00,  0x03,  0xF7,  0x04,  0x00,  0x0F,  0x79,  0x1C,  0xF7,  0x3C,  0x00,  0xB8,  0xF0,  0x9C,  0x10,
+  0x4E,  0x14,  0x01,  0x48,  0x06,  0x13,  0x45,  0xF4,  0x04,  0x00,  0x00,  0x16,  0x91,  0x44,  0xD5,  0xF0,
+  0x82,  0x10,  0x00,  0xF0,  0x9E,  0x02,  0x02,  0xF6,  0xFF,  0x00,  0x38,  0x1C,  0x2C,  0xBC,  0xAE,  0xBC,
+  0xE2,  0x08,  0x00,  0xEC,  0xB8,  0x00,  0x02,  0x48,  0x1D,  0xF7,  0x80,  0x00,  0xB8,  0xF0,  0xCC,  0x10,
+  0x1E,  0x14,  0x01,  0x48,  0x0E,  0x13,  0x0E,  0xF7,  0x80,  0x00,  0x38,  0x54,  0x03,  0x58,  0xAF,  0x19,
+  0x82,  0x48,  0x00,  0x16,  0x82,  0x48,  0x12,  0x45,  0xD5,  0xF0,  0xBA,  0x10,  0x00,  0xF0,  0x9E,  0x02,
+  0x39,  0xF0,  0xF8,  0x10,  0x38,  0x44,  0x00,  0x16,  0x7E,  0x18,  0x18,  0xF4,  0x03,  0x00,  0x04,  0x13,
+  0x61,  0x18,  0x00,  0x16,  0x38,  0x1C,  0x00,  0xFC,  0x22,  0x01,  0x18,  0xF4,  0x01,  0x00,  0xF1,  0x12,
+  0xE3,  0x10,  0x30,  0x44,  0x30,  0x44,  0x30,  0x44,  0xB1,  0xF0,  0x18,  0x11,  0x00,  0x16,  0x3E,  0x57,
+  0x03,  0xF6,  0xE0,  0x00,  0x03,  0x57,  0x83,  0x59,  0x04,  0xCC,  0x01,  0x4A,  0x6A,  0x12,  0x45,  0x5A,
+  0x00,  0xF2,  0xF6,  0x0D,  0x02,  0x4B,  0x70,  0x14,  0x34,  0x13,  0x02,  0x80,  0x48,  0xE4,  0x08,  0x00,
+  0x18,  0x12,  0x9C,  0xE7,  0x02,  0x00,  0x9E,  0xE7,  0x15,  0x00,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,
+  0x7A,  0x0A,  0x1E,  0x1C,  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x30,  0xE4,  0x10,  0x00,  0x04,  0x40,
+  0x00,  0xF2,  0xE2,  0x0D,  0x20,  0xE7,  0x01,  0x00,  0x01,  0xF6,  0x01,  0x00,  0x00,  0x16,  0x04,  0xDC,
+  0x01,  0x4A,  0x24,  0x12,  0x45,  0x5A,  0x00,  0xF2,  0xF6,  0x0D,  0x43,  0x5B,  0x06,  0xEC,  0x98,  0x00,
+  0x00,  0xF2,  0x38,  0x10,  0xC6,  0x59,  0x20,  0x14,  0x0A,  0x13,  0x00,  0xF2,  0xC6,  0x0F,  0x00,  0xF2,
+  0x14,  0x10,  0xA7,  0x10,  0x83,  0x5A,  0xD7,  0x10,  0x0E,  0x47,  0x07,  0xE6,  0x10,  0x00,  0xCE,  0x47,
+  0x5A,  0xF0,  0x20,  0x11,  0xB9,  0x54,  0x00,  0x16,  0x14,  0x90,  0x96,  0x90,  0x02,  0xFC,  0xA8,  0x00,
+  0x03,  0xFC,  0xAA,  0x00,  0x48,  0x55,  0x02,  0x13,  0xC9,  0x55,  0x00,  0x16,  0x00,  0xEC,  0xBA,  0x00,
+  0x10,  0x44,  0x00,  0xEA,  0xBA,  0x00,  0x00,  0x16,  0x03,  0xF6,  0xC0,  0x00,  0x00,  0xF2,  0x68,  0x0A,
+  0x10,  0x44,  0x00,  0x4C,  0x00,  0x16
+};
 
 unsigned short _adv_mcode_size ASC_INITDATA =
-    sizeof(_adv_mcode_buf); /* 0x11D2 */
-unsigned long  _adv_mcode_chksum ASC_INITDATA = 0x0347D07AUL;
+    sizeof(_adv_mcode_buf); /* 0x11D6 */
+unsigned long  _adv_mcode_chksum ASC_INITDATA = 0x03494981UL;
 
 /* a_init.c */
 /*
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v2.0.34/linux/drivers/scsi/aha152x.c	Tue Oct 29 17:42:41 1996
+++ linux/drivers/scsi/aha152x.c	Mon Jul 13 13:47:33 1998
@@ -609,11 +609,12 @@
       prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
     ;
 
-  if(ptr)
+  if(ptr){
     if(prev)
       prev->host_scribble = ptr->host_scribble;
     else
       *SC= (Scsi_Cmnd *) ptr->host_scribble;
+  }
 
   return ptr;
 }
@@ -1724,7 +1725,7 @@
 
   /* we are waiting for the result of a selection attempt */
   if(CURRENT_SC->SCp.phase & in_selection) {
-    if(TESTLO(SSTAT1, SELTO))
+    if(TESTLO(SSTAT1, SELTO)) {
       /* no timeout */
       if(TESTHI(SSTAT0, SELDO)) {
         /* clear BUS FREE interrupt */
@@ -1800,7 +1801,7 @@
         return;
       } else
         aha152x_panic(shpnt, "neither timeout nor selection\007");
-    else {
+    } else {
 #if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
       if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
         printk("SELTO, ");
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v2.0.34/linux/drivers/scsi/aha1542.c	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/aha1542.c	Mon Jul 13 13:47:33 1998
@@ -212,7 +212,7 @@
 
 /* Similar to aha1542_in, except that we wait a very short period of time.
    We use this if we know the board is alive and awake, but we are not sure
-   if the board will respond the the command we are about to send or not */
+   if the board will respond to the command we are about to send or not */
 static int aha1542_in1(unsigned int base, unchar *cmdp, int len)
 {
     unsigned long flags;
@@ -809,7 +809,7 @@
      mbenable_cmd[1]=0;
      mbenable_cmd[2]=mbenable_result[1];
 
-     if(mbenable_result[1] & 0x03) retval = BIOS_TRANSLATION_25563;
+     if((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03)) retval = BIOS_TRANSLATION_25563;
 
      aha1542_out(base,mbenable_cmd,3);
      WAIT(INTRFLAGS(base),INTRMASK,HACC,0);
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.0.34/linux/drivers/scsi/aic7xxx.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/aic7xxx.c	Mon Jul 13 13:47:33 1998
@@ -209,7 +209,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.0.14"
+#define AIC7XXX_C_VERSION  "5.0.19"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -237,12 +237,12 @@
 
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
 #  if defined(__sparc_v9__) || defined(__powerpc__)
-#    error "PPC and Sparc platforms are only support under 2.1.x and above"
+#    error "PPC and Sparc platforms are only support under 2.1.92 and above"
 #  endif
 #  include <linux/bios32.h>
 #endif
 
-#if !defined(__alpha__) && !defined(__sparc__)
+#if defined(__powerpc__) || defined(__i386__)
 #  define MMAPIO
 #endif
 
@@ -728,6 +728,7 @@
         AHC_BIOS_ENABLED          = 0x00800000,
         AHC_ABORT_PENDING         = 0x02000000,
         AHC_RESET_PENDING         = 0x04000000,
+#define AHC_IN_ISR_BIT              28
         AHC_IN_ISR                = 0x10000000,
         AHC_IN_ABORT              = 0x20000000,
         AHC_IN_RESET              = 0x40000000
@@ -866,12 +867,19 @@
 #define  DEVICE_PRINT_WDTR              0x10
 #define  DEVICE_SUCCESS                 0x20
 #define  DEVICE_TAGGED_SUCCESS          0x40
+#define  DEVICE_SCANNED                 0x80
   volatile unsigned char   dev_flags[MAX_TARGETS];
   volatile unsigned char   dev_active_cmds[MAX_TARGETS];
   unsigned char            dev_temp_queue_depth[MAX_TARGETS];
   unsigned char            dev_commands_sent[MAX_TARGETS];
 
   /*
+   * The next 128 (or 256 on 64 bit machines)....
+   */
+  Scsi_Cmnd               *dev_wdtr_cmnd[MAX_TARGETS];
+  Scsi_Cmnd               *dev_sdtr_cmnd[MAX_TARGETS];
+
+  /*
    * The next 64.... 
    */
 
@@ -952,6 +960,9 @@
   unsigned short           ultraenb;         /* Ultra mode target list */
   unsigned short           bios_control;     /* bios control - SEEPROM */
   unsigned short           adapter_control;  /* adapter control - SEEPROM */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  struct pci_dev          *pdev;
+#endif
   unsigned char            pci_bus;
   unsigned char            pci_device_fn;
 
@@ -1070,7 +1081,13 @@
                                               * or reset call into the
                                               * driver.
                                               */
-
+static int aic7xxx_pci_parity = 0;           /*
+                                              * Set this to:
+                                              *   0 - Shut off PCI parity check
+                                              *  -1 - Normal parity check
+                                              *  anything else - reverse pci
+                                              *    pci parity checking
+                                              */
 /*
  * So that insmod can find the variable and make it point to something
  */
@@ -1213,6 +1230,7 @@
     { "7895_irq_hack", &aic7xxx_7895_irq_hack },
     { "override_term", &aic7xxx_override_term },
     { "panic_on_abort", &aic7xxx_panic_on_abort },
+    { "pci_parity", &aic7xxx_pci_parity },
     { "tag_info",    NULL }
   };
 
@@ -1390,8 +1408,8 @@
  * Description:
  *   Find the next patch to download.
  *-F*************************************************************************/
-static struct patch *
-aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+static struct sequencer_patch *
+aic7xxx_next_patch(struct sequencer_patch *cur_patch, int options, int instrptr)
 {
   while (cur_patch != NULL)
   {
@@ -1450,15 +1468,15 @@
     {
       int address_offset;
       unsigned int address;
-      struct patch *patch;
+      struct sequencer_patch *patch;
       int i;
 
       address_offset = 0;
       address = instr.address;
       address |= (instr.opcode_addr & ADDR_HIGH_BIT) << 8;
-      for (i = 0; i < NUMBER(patches); i++)
+      for (i = 0; i < NUMBER(sequencer_patches); i++)
       {
-        patch = &patches[i];
+        patch = &sequencer_patches[i];
         if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
             (((patch->options & options) != 0) && (patch->negative == TRUE)))
         {
@@ -1506,7 +1524,7 @@
 aic7xxx_loadseq(struct aic7xxx_host *p)
 {
   int options;
-  struct patch *cur_patch;
+  struct sequencer_patch *cur_patch;
   int i;
   int downloaded;
 
@@ -1560,7 +1578,7 @@
   }
 
 
-  cur_patch = patches;
+  cur_patch = sequencer_patches;
   aic_outb(p, PERRORDIS | LOADRAM, SEQCTL);
   aic_outb(p, 0, SEQADDR0);
   aic_outb(p, 0, SEQADDR1);
@@ -2266,6 +2284,54 @@
       cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
         (cmd->result & 0xffff);
   }
+  if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+  {
+    if(cmd->cmnd[0] == INQUIRY)
+    {
+      char *buffer;
+      
+      if(cmd->use_sg)
+      {
+        struct scatterlist *sg;
+
+        sg = (struct scatterlist *)cmd->request_buffer;
+        buffer = (char *)sg[0].address;
+      }
+      else
+      {
+        buffer = (char *)cmd->request_buffer;
+      }
+#define WIDE_INQUIRY_BITS 0x60
+#define SYNC_INQUIRY_BITS 0x10
+      if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
+           (p->needwdtr_copy & (1<<tindex)) &&
+           (p->type & AHC_WIDE) )
+      {
+        p->needwdtr |= (1<<tindex);
+        p->needwdtr_copy |= (1<<tindex);
+        p->syncinfo[tindex].offset = MAX_OFFSET_16BIT;
+      }
+      else
+      {
+        p->needwdtr &= ~(1<<tindex);
+        p->needwdtr_copy &= ~(1<<tindex);
+        p->syncinfo[tindex].offset = MAX_OFFSET_8BIT;
+      }
+      if (buffer[7] & SYNC_INQUIRY_BITS)
+      {
+        p->needsdtr |= (1<<tindex);
+        p->needsdtr_copy |= (1<<tindex);
+      }
+      else
+      {
+        p->needsdtr &= ~(1<<tindex);
+        p->needsdtr_copy &= ~(1<<tindex);
+      }
+      p->dev_flags[tindex] |= DEVICE_SCANNED;
+#undef WIDE_INQUIRY_BITS
+#undef SYNC_INQUIRY_BITS
+    }
+  }
   if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
   {
     unsigned short mask;
@@ -2346,13 +2412,17 @@
         scbq_insert_tail(&p->waiting_scbs, scbp);
       }
     }
-    if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp)
+    if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp )
     {
       scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
       if (scbp)
         scbq_insert_tail(&p->waiting_scbs, scbp);
     }
   }
+  if ( !(scb->tag_action) && (p->tagenable & (1<<tindex)) )
+  {
+    p->dev_temp_queue_depth[tindex] = p->dev_max_queue_depth[tindex];
+  }
   p->dev_active_cmds[tindex]--;
   p->activescbs--;
 
@@ -3109,7 +3179,7 @@
   scsiseq = aic_inb(p, SCSISEQ);
   aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ);
 
-  udelay(1000);
+  udelay(5000);
 
   /* Turn off the bus reset. */
   aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ);
@@ -3119,7 +3189,7 @@
   /* Re-enable reset interrupts. */
   aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
 
-  udelay(1000);
+  udelay(2000);
 }
 
 /*+F*************************************************************************
@@ -3274,6 +3344,10 @@
   while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
   {
     tindex = TARGET_INDEX(scb->cmd);
+    if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
+    {
+      p->dev_temp_queue_depth[tindex] = 1;
+    }
     if ( (p->dev_active_cmds[tindex] >=
           p->dev_temp_queue_depth[tindex]) ||
          (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) )
@@ -3301,14 +3375,9 @@
   }
   if (sent)
   {
-    if(p->type & AHC_AIC78x0)
-      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-    else
-    {
-      pause_sequencer(p);
-      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-      unpause_sequencer(p, FALSE);
-    }
+    pause_sequencer(p);
+    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    unpause_sequencer(p, FALSE);
     if (p->activescbs > p->max_activescbs)
       p->max_activescbs = p->activescbs;
   }
@@ -3632,8 +3701,8 @@
         last_msg = aic_inb(p, LAST_MSG);
 
         if ( (last_msg == MSG_IDENTIFYFLAG) &&
-             (scb->tag_action != 0 ) &&
-             !(scb->flags & SCB_MSGOUT_BITS) )
+             (scb->tag_action) &&
+            !(scb->flags & SCB_MSGOUT_BITS) )
         {
           if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
               (p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS))
@@ -3736,14 +3805,8 @@
           }
           scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
           p->syncinfo[scratch_offset].offset = MAX_OFFSET_8BIT;
-          /*
-           * The sequencer should have already cleared the MK_MESSAGE bit in
-           * the SCB, so we simply restart the message out phase and resend
-           * the IDENTIFY and TAG values so that there is no confusion over
-           * what has been rejected.
-           */
-          aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
-          aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+          if (p->needsdtr_copy & target_mask)
+            p->needsdtr |= target_mask;
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
@@ -3763,14 +3826,6 @@
               "asynchronous transfers.\n", p->host_no, CTL_OF_SCB(scb));
             p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_SDTR;
           }
-          /*
-           * The sequencer should have already cleared the MK_MESSAGE bit in
-           * the SCB, so we simply restart the message out phase and resend
-           * the IDENTIFY and TAG values so that there is no confusion over
-           * what has been rejected.
-           */
-          aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
-          aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
         }
         else if (aic7xxx_verbose & VERBOSE_SEQINT)
         {
@@ -3850,7 +3905,7 @@
                    * Send a sense command to the requesting target.
                  * XXX - revisit this and get rid of the memcopys.
                    */
-                memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+                memcpy(&scb->sense_cmd[0], &generic_sense[0],
                        sizeof(generic_sense));
 
                 scb->sense_cmd[1] = (cmd->lun << 5);
@@ -3893,21 +3948,29 @@
                  *
                  * 1998/04/23 - We also don't want to set the flag if the
                  *   original command was a TEST_UNIT_READY since that
-                 *   implies a SEND_SENSE anyway.  Plus, we don't actually
-                 *   start the negotiation here, we just flag it because
-                 *   some devices appear to choke up their message buffer
-                 *   when we mix things together like this.
+                 *   implies a SEND_SENSE anyway.
                  */
-                if ( !(scb->flags & SCB_MSGOUT_BITS) &&
-                      (scb->cmd->cmnd[0] != TEST_UNIT_READY) )
+                if (scb->cmd->cmnd[0] != TEST_UNIT_READY)
                 {
-                  if ( p->needwdtr_copy & target_mask )
+                  if ( (p->needwdtr_copy & target_mask) &&
+                      !(p->wdtr_pending & target_mask) &&
+                      !(p->sdtr_pending & target_mask) )
                   {
                     p->needwdtr |= target_mask;
+                    p->wdtr_pending |= target_mask;
+                    hscb->control |= MK_MESSAGE;
+                    scb->flags |= SCB_MSGOUT_WDTR_16BIT;
                   }
                   if ( p->needsdtr_copy & target_mask )
                   {
                     p->needsdtr |= target_mask;
+                    if ( !(p->wdtr_pending & target_mask) &&
+                         !(p->sdtr_pending & target_mask) )
+                    {
+                      p->sdtr_pending |= target_mask;
+                      hscb->control |= MK_MESSAGE;
+                      scb->flags |= SCB_MSGOUT_SDTR;
+                    }
                   }
                 }
 
@@ -4113,14 +4176,14 @@
         }
         else if (scb->flags & SCB_ABORT)
         {
-          if (scb->hscb->control & TAG_ENB)
+          if (scb->tag_action)
           {
             if (msg_out == MSG_IDENTIFYFLAG)
             {
               p->msg_buf[p->msg_index++] = scb->tag_action;
               p->msg_buf[p->msg_index++] = scb->hscb->tag;
-              p->msg_len += 2;
-            }
+	      p->msg_len += 2;
+	    }
             p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
           }
           else
@@ -4134,13 +4197,6 @@
         }
         else if (scb->flags & SCB_MSGOUT_WDTR)
         {
-          if ( (scb->hscb->control & TAG_ENB) &&
-               (msg_out == MSG_IDENTIFYFLAG) )
-          {
-            p->msg_buf[p->msg_index++] = scb->tag_action;
-            p->msg_buf[p->msg_index++] = scb->hscb->tag;
-            p->msg_len += 2;
-          }
           aic7xxx_construct_wdtr(p, (scb->flags & SCB_WDTR_16BIT));
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
@@ -4157,13 +4213,6 @@
             period = 0;
             offset = 0;
           }
-          if ( (scb->hscb->control & TAG_ENB) &&
-               (msg_out == MSG_IDENTIFYFLAG) )
-          {
-            p->msg_buf[p->msg_index++] = scb->tag_action;
-            p->msg_buf[p->msg_index++] = scb->hscb->tag;
-            p->msg_len += 2;
-          }
           aic7xxx_construct_sdtr(p, period, offset);
         }
         else 
@@ -4442,6 +4491,18 @@
           }
           scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
           p->wdtr_pending &= ~target_mask;
+          /*
+           * By virtue of the SCSI spec, a WDTR message negates any existing
+           * SDTR negotiations.  So, even if needsdtr isn't marked for this
+           * device, we still have to do a new SDTR message if the device
+           * supports SDTR at all.  Therefore, we check needsdtr_copy instead
+           * of needstr.
+           */
+          if ( (p->needsdtr_copy & target_mask) &&
+              !(p->sdtr_pending & target_mask))
+          {
+            p->needsdtr |= target_mask;
+          }
         }
         else
         {
@@ -4960,43 +5021,39 @@
 aic7xxx_pci_intr(struct aic7xxx_host *p)
 {
   unsigned char status1;
-  int error;
 
-  error = 0;
-  error = pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  pci_read_config_byte(p->pdev, PCI_STATUS, &status1);
+#else
+  pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
                                             PCI_STATUS, &status1);
+#endif
 
-  if (error == 0)
-  {
-    if (status1 & DPE)
-      printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
-        "phase.\n", p->host_no, -1, -1, -1);
-    if (status1 & SSE)
-      printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
-        -1, -1, -1);
-    if (status1 & RMA)
-      printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
-        -1, -1, -1);
-    if (status1 & RTA)
-      printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
-        -1, -1, -1);
-    if (status1 & STA)
-      printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
-        -1, -1, -1);
-    if (status1 & DPR)
-      printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
-        "PERR#\n", p->host_no, -1, -1, -1);
-  }
-  else
-  {
-    printk(WARN_LEAD "Error reading PCI config register during PCI ERROR"
-      "interrupt.\n", p->host_no, -1, -1, -1);
-    aic_outb(p,  CLRPARERR, CLRINT);
-    return;
-  }
+  if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+      "phase.\n", p->host_no, -1, -1, -1);
+  if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+      "PERR#\n", p->host_no, -1, -1, -1);
   
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  pci_write_config_byte(p->pdev, PCI_STATUS, status1);
+#else
   pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
                                             PCI_STATUS, status1);
+#endif
   if (status1 & (DPR|RMA|RTA))
     aic_outb(p,  CLRPARERR, CLRINT);
 
@@ -5019,17 +5076,11 @@
   p = (struct aic7xxx_host *)dev_id;
 
   /*
-   * Just a few sanity checks.  Make sure p != NULL, that we have an
-   * interrupt pending, and that we aren't already in our int handler.
+   * Just a few sanity checks.  Make sure that we have an int pending.
    * Also, if PCI, then we are going to check for a PCI bus error status
    * should we get too many spurious interrupts.
    */
-  if (p == NULL)
-  {
-    printk(KERN_WARNING "aic7xxx: ISR routine called with NULL dev_id\n");
-    return;
-  }
-  else if (!(aic_inb(p, INTSTAT) & INT_PEND))
+  if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
   {
 #ifdef CONFIG_PCI
     if ((p->type & AHC_AIC78x0) && (p->spurious_int > 500))
@@ -5047,26 +5098,17 @@
 #endif
     return;
   }
-  else if (p->flags & AHC_IN_ISR)
-  {
-    return;
-  }
 
-  /*
-   * Handle all the interrupt sources - especially for SCSI
-   * interrupts, we won't get a second chance at them.
-   */
-  intstat = aic_inb(p, INTSTAT);
   p->spurious_int = 0;
 
   /*
    * Keep track of interrupts for /proc/scsi
    */
   p->isr_count++;
-  p->flags |= AHC_IN_ISR;
 
   /*
-   * Indicate that we're in the interrupt handler.
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
    */
   if (intstat & CMDCMPLT)
   {
@@ -5122,7 +5164,7 @@
           continue;
         }
         aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel,
-          scb->cmd->lun, scb->cmd->lun);
+          scb->cmd->lun, scb->hscb->tag);
         scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT |
           SCB_QUEUED_ABORT);
         unpause_sequencer(p, FALSE);
@@ -5188,9 +5230,6 @@
   {
     aic7xxx_handle_scsiint(p, intstat);
   }
-  aic7xxx_done_cmds_complete(p);
-  aic7xxx_run_waiting_queues(p);
-  p->flags &= ~AHC_IN_ISR;
 }
 
 /*+F*************************************************************************
@@ -5206,15 +5245,54 @@
 do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
 {
   unsigned long cpu_flags;
+  struct aic7xxx_host *p;
+  static unsigned int re_entry_counter = 0;
+  
+  p = (struct aic7xxx_host *)dev_id;
+  if(!p)
+    return;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95)
-
+  if(test_and_set_bit(AHC_IN_ISR_BIT, &p->flags))
+  {
+    if(re_entry_counter++ > 100000UL)
+    {
+      /*
+       * Hmmm...we seem to be looping here.  This usually means that our
+       * interrupt routine got killed by a NULL pointer deref.  Panic.
+       */
+      sti();
+      panic("aic7xxx: The interrupt routine appears to have seg faulted.\n");
+    }
+    return;
+  }
+  re_entry_counter = 0;
   spin_lock_irqsave(&io_request_lock, cpu_flags);
   aic7xxx_isr(irq, dev_id, regs);
+  aic7xxx_done_cmds_complete(p);
+  aic7xxx_run_waiting_queues(p);
   spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+  clear_bit(AHC_IN_ISR_BIT, &p->flags);
 #else
+  if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags))
+  {
+    if(re_entry_counter++ > 100000UL)
+    {
+      /*
+       * Hmmm...we seem to be looping here.  This usually means that our
+       * interrupt routine got killed by a NULL pointer deref.  Panic.
+       */
+      sti();
+      panic("aic7xxx: The interrupt routine appears to have seg faulted.\n");
+    }
+    return;
+  }
+  re_entry_counter = 0;
   DRIVER_LOCK
   aic7xxx_isr(irq, dev_id, regs);
   DRIVER_UNLOCK
+  aic7xxx_done_cmds_complete(p);
+  aic7xxx_run_waiting_queues(p);
+  clear_bit(AHC_IN_ISR_BIT, (int *)&p->flags);
 #endif
 }
 
@@ -6342,7 +6420,7 @@
       scsi_conf |= p->scsi_id_b;
       aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF + 1);
     }
-    if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+    if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
     {
       /* Reset SCSI bus B. */
       if (aic7xxx_verbose & VERBOSE_PROBE)
@@ -6378,7 +6456,7 @@
   }
 
 
-  if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+  if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
   {
     /* Reset SCSI bus A. */
     if (aic7xxx_verbose & VERBOSE_PROBE)
@@ -6478,7 +6556,12 @@
       }
     }
 
-    aic_outb(p, target_settings, TARG_SCRATCH + i);
+    /*
+     * If we reset the bus, then clear the transfer ssettings, else leave
+     * them be
+     */
+    if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
+      aic_outb(p, target_settings, TARG_SCRATCH + i);
     if (p->needsdtr_copy & (0x01 << i))
     {
       short sxfr, j;
@@ -6516,8 +6599,16 @@
   }
   p->needsdtr = p->needsdtr_copy;
   p->needwdtr = p->needwdtr_copy;
-  aic_outb(p, 0, ULTRA_ENB);
-  aic_outb(p, 0, ULTRA_ENB + 1);
+
+  /*
+   * If we reset the bus, then clear the transfer ssettings, else leave
+   * them be
+   */
+  if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
+  {
+    aic_outb(p, 0, ULTRA_ENB);
+    aic_outb(p, 0, ULTRA_ENB + 1);
+  }
 
   /*
    * Allocate enough hardware scbs to handle the maximum number of
@@ -6770,24 +6861,23 @@
   {
     kfree(p->scb_data->scb_array[i]);
   }
-  /*
-   * Free the SCB data area.
-   */
-  kfree(p->scb_data);
 
   /*
-   * Free the instance of the device structure.
+   * Free any alloced Scsi_Cmnd structures that might be around for
+   * negotiation purposes....
    */
+  for (i = 0; i < MAX_TARGETS; i++)
+  {
+    if(p->dev_wdtr_cmnd[i])
+      kfree(p->dev_wdtr_cmnd[i]);
+    if(p->dev_sdtr_cmnd[i])
+      kfree(p->dev_sdtr_cmnd[i]);
+  }
 
   /*
-   * XXXXXXXX  FIXXXXXMEEEEEE.  How do we unmap the I/O range we have mapped
-   * if we are doing MMAPed I/O ??????????  Our biggest concern is the issue
-   * of possibly calling unmap on an area that *might* be used on another
-   * controller as well (aka, the 4096 byte MMAPed area is back to back
-   * with another controller, and the PAGE_SIZE is greater then 4096, allowing
-   * us to remap in a shared page).
+   * Free the SCB data area.
    */
-  scsi_unregister(p->host);
+  kfree(p->scb_data);
 }
 
 /*+F*************************************************************************
@@ -6960,7 +7050,12 @@
         target_settings &= ~0x70;
         p->ultraenb &= ~(0x01 << i);
       }
-      aic_outb(p, target_settings, TARG_SCRATCH + i);
+      /*
+       * Don't output these settings if we aren't resetting the bus, instead,
+       * leave the devices current settings in place
+       */
+      if (!(aic7xxx_no_reset))
+        aic_outb(p, target_settings, TARG_SCRATCH + i);
     }
     aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
     aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
@@ -7068,7 +7163,13 @@
    */
   if(aic7xxx)
     aic7xxx_setup(aic7xxx, NULL);
-
+  if(dummy_buffer[0] != 'P')
+    printk(KERN_WARNING "aic7xxx: Please read the file /usr/src/linux/drivers"
+      "/scsi/README.aic7xxx\n"
+      "aic7xxx: to see the proper way to specify options to the aic7xxx "
+      "module\n"
+      "aic7xxx: Specifically, don't use any commas when passing arguments to\n"
+      "aic7xxx: insmod or else it might trash certain memory areas.\n");
 #endif
 
   template->proc_dir = &proc_scsi_aic7xxx;
@@ -7344,7 +7445,11 @@
   /*
    * PCI-bus probe.
    */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  if (pci_present())
+#else
   if (pcibios_present())
+#endif
   {
     struct
     {
@@ -7389,7 +7494,7 @@
     };
 
     unsigned short command;
-    unsigned int  devconfig, i;
+    unsigned int  devconfig, i, oldverbose;
 #ifdef MMAPIO
     unsigned long page_offset, base;
 #endif
@@ -7438,14 +7543,43 @@
            */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
           temp_p->irq = pdev->irq;
+          temp_p->pdev = pdev;
           temp_p->pci_bus = pdev->bus->number;
           temp_p->pci_device_fn = pdev->devfn;
           temp_p->base = pdev->base_address[0];
           temp_p->mbase = pdev->base_address[1];
           pci_read_config_word(pdev, PCI_COMMAND, &command);
-          pci_write_config_word(pdev, PCI_COMMAND,
-             command | PCI_COMMAND_MASTER |
-             PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+          {
+            printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
+              (int)command);
+          }
+          command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+            PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
+            PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+          if (aic7xxx_pci_parity == 0)
+            command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+          pci_write_config_word(pdev, PCI_COMMAND, command);
+          pci_read_config_dword(pdev, PCI_COMMAND, &devconfig);
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+          {
+            printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
+          }
+          devconfig |= 0x80000000;
+          if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
+          {
+            devconfig &= ~(0x00000008);
+          }
+          else
+          {
+            devconfig |= 0x00000008;
+          }
+          pci_write_config_dword(pdev, PCI_COMMAND, devconfig);
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic7xxx_pci_devices[i].board_name_index],
+              PCI_SLOT(temp_p->pdev->devfn),
+              PCI_FUNC(temp_p->pdev->devfn));
 #else
           temp_p->pci_bus = pci_bus;
           temp_p->pci_device_fn = pci_devfn;
@@ -7459,16 +7593,38 @@
             &mmapbase);
           temp_p->mbase = mmapbase;
           pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command);
-          pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND,
-            command | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
-            PCI_COMMAND_IO);
-#endif
-
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+          {
+            printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
+              (int)command);
+          }
+          command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+            PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
+            PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+          if (aic7xxx_pci_parity == 0)
+            command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+          pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command);
+          pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig);
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+          {
+            printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
+          }
+          devconfig |= 0x80000000;
+          if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
+          {
+            devconfig &= ~(0x00000008);
+          }
+          else
+          {
+            devconfig |= 0x00000008;
+          }
+          pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig);
           if (aic7xxx_verbose & VERBOSE_PROBE2)
             printk("aic7xxx: <%s> at PCI %d/%d\n", 
               board_names[aic7xxx_pci_devices[i].board_name_index],
               PCI_SLOT(temp_p->pci_device_fn),
               PCI_FUNC(temp_p->pci_device_fn));
+#endif
 
           /*
            * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
@@ -7480,26 +7636,56 @@
           temp_p->pause = temp_p->unpause | PAUSE;
 
 #ifdef MMAPIO
-          base = temp_p->mbase & PAGE_MASK;
-          page_offset = temp_p->mbase - base;
-          /*
-           * replace the next line with this one if you are using 2.1.x:
-           * temp_p->maddr = ioremap(base, page_offset + 256);
-           */
+          if((temp_p->type & AHC_AIC7850) != AHC_AIC7850)
+          {
+            base = temp_p->mbase & PAGE_MASK;
+            page_offset = temp_p->mbase - base;
+            /*
+             * replace the next line with this one if you are using 2.1.x:
+             * temp_p->maddr = ioremap(base, page_offset + 256);
+             */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-          temp_p->maddr = ioremap(base, page_offset + 256);
+            temp_p->maddr = ioremap(base, page_offset + 256);
 #else
-          temp_p->maddr = vremap(base, page_offset + 256);
+            temp_p->maddr = vremap(base, page_offset + 256);
 #endif
-          if(temp_p->maddr)
+            if(temp_p->maddr)
+            {
+              temp_p->maddr += page_offset;
+            }
+          }
+          else
           {
-            temp_p->maddr += page_offset;
+#ifdef __i386__
+            /*
+             * Resort to PIO mode on these controllers and Intel hardware.
+             * For other hardware we need to either disable these controllers
+             * or do without MMAPed IO.  However, for PPC, we can't do
+             * MMAPed IO (according to what I've heard) so we may be forced
+             * to just fail detection on those cards.
+             */
+            temp_p->maddr = NULL;
+#else
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
+#endif /* __i386__ */
           }
 #endif
 
           aic_outb(temp_p, temp_p->pause, HCNTRL);
           while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
 
+          /*
+           * Clear out any pending PCI error status messages.  Also set
+           * verbose to 0 so that we don't emit strange PCI error messages
+           * while cleaning out the current status bits.
+           */
+          oldverbose = aic7xxx_verbose;
+          aic7xxx_verbose = 0;
+          aic7xxx_pci_intr(temp_p);
+          aic7xxx_verbose = oldverbose;
+
           temp_p->bios_address = 0;
 
           /*
@@ -7551,15 +7737,21 @@
 
             case AHC_AIC7895:
               temp_p->flags |= AHC_MULTI_CHANNEL;
-              if (PCI_FUNC(temp_p->pci_device_fn) != 0)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+              if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
                 temp_p->flags |= AHC_CHNLB;
               }
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+              devconfig = le32_to_cpu(devconfig);
               devconfig |= SCBSIZE32;
+              devconfig = cpu_to_le32(devconfig);
               pci_write_config_dword(pdev, DEVCONFIG, devconfig);
 #else
+              if (PCI_FUNC(temp_p->pci_device_fn) != 0)
+              {
+                temp_p->flags |= AHC_CHNLB;
+              }
               pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
                 &devconfig);
               devconfig |= SCBSIZE32;
@@ -7607,7 +7799,6 @@
             temp_p->flags |= AHC_USEDEFAULTS;
             if (sxfrctl1 & STPWEN)
               temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
-            temp_p->scsi_id_b = temp_p->scsi_id;
           }
 
           /*
@@ -7930,6 +8121,7 @@
         {
           found--;
           aic7xxx_free(p);
+          scsi_unregister(p->host);
         }
       }
       current_p = temp_p;
@@ -7948,6 +8140,7 @@
         {
           found--;
           aic7xxx_free(p);
+          scsi_unregister(p->host);
         }
       }
       current_p = temp_p;
@@ -7966,6 +8159,7 @@
         {
           found--;
           aic7xxx_free(p);
+          scsi_unregister(p->host);
         }
       }
       current_p = temp_p;
@@ -7978,6 +8172,89 @@
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_negotiation_complete
+ *
+ * Description:
+ *   Handle completion events for our Negotiation commands.  Clear out the
+ *   struct and get it ready for its next use.
+ *-F*************************************************************************/
+static void
+aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
+{
+  memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_build_negotiation_command
+ *
+ * Description:
+ *   Build a Scsi_Cmnd structure to perform negotiation with or else send
+ *   a pre-built command specifically for this purpose.
+ *-F*************************************************************************/
+static void
+aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *old_cmd,
+  int tindex)
+{
+
+  if ( (p->needwdtr & (1<<tindex)) && !(p->wdtr_pending & (1<<tindex)) )
+  {
+    if(p->dev_wdtr_cmnd[tindex] == NULL)
+    {
+      Scsi_Cmnd *cmd;
+
+      if (!(p->dev_wdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+      {
+        return;
+      }
+      cmd = p->dev_wdtr_cmnd[tindex];
+      memset(cmd, 0, sizeof(Scsi_Cmnd));
+      memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
+      memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
+      memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
+      cmd->lun = 0;
+      cmd->request_bufflen = 0;
+      cmd->request_buffer = NULL;
+      cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
+      cmd->bufflen = 0;
+      cmd->buffer = NULL;
+      cmd->underflow = 0;
+      cmd->cmd_len = 6;
+    }
+    aic7xxx_queue(p->dev_wdtr_cmnd[tindex], 
+                  aic7xxx_negotiation_complete);
+  }
+  else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) )
+  {
+    if(p->dev_sdtr_cmnd[tindex] == NULL)
+    {
+      Scsi_Cmnd *cmd;
+
+      if (!(p->dev_sdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+      {
+        return;
+      }
+      cmd = p->dev_sdtr_cmnd[tindex];
+      memset(cmd, 0, sizeof(Scsi_Cmnd));
+      memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
+      memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
+      memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
+      cmd->lun = 0;
+      cmd->request_bufflen = 0;
+      cmd->request_buffer = NULL;
+      cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
+      cmd->bufflen = 0;
+      cmd->buffer = NULL;
+      cmd->underflow = 0;
+      cmd->cmd_len = 6;
+    }
+    aic7xxx_queue(p->dev_sdtr_cmnd[tindex], 
+                  aic7xxx_negotiation_complete);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_buildscb
  *
  * Description:
@@ -8027,27 +8304,37 @@
       }
     }
   }
-  if ( (p->needwdtr & mask) &&
-      !(p->wdtr_pending & mask) &&
-       (scb->cmd->lun == 0) )
-  {
-    p->wdtr_pending |= mask;
-    hscb->control |= MK_MESSAGE;
-    if (p->needwdtr_copy & mask)
-      scb->flags |= SCB_MSGOUT_WDTR_16BIT;
-    else
-      scb->flags |= SCB_MSGOUT_WDTR_8BIT;
-  }
-  else
+  if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED)
   {
-    if ( (p->needsdtr & mask) &&
-        !(p->sdtr_pending & mask) &&
-        !(p->wdtr_pending & mask) &&
-         (scb->cmd->lun == 0) )
-    {
-      p->sdtr_pending |= mask;
-      hscb->control |= MK_MESSAGE;
-      scb->flags |= SCB_MSGOUT_SDTR;
+    if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) )
+    {
+      if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
+      {
+        p->wdtr_pending |= mask;
+        scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+        hscb->control &= DISCENB;
+        hscb->control |= MK_MESSAGE;
+        scb->tag_action = 0;
+      }
+      else
+      {
+        aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
+      }
+    }
+    if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) )
+    {
+      if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)])
+      {
+        p->sdtr_pending |= mask;
+        scb->flags |= SCB_MSGOUT_SDTR;
+        hscb->control &= DISCENB;
+        hscb->control |= MK_MESSAGE;
+        scb->tag_action = 0;
+      }
+      else if (cmd != p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
+      {
+        aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
+      }
     }
   }
   hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
@@ -8157,7 +8444,7 @@
     }
   }
 
-  if (p->dev_active_cmds[tindex] > cmd->device->queue_depth)
+  if (p->dev_active_cmds[tindex] > (cmd->device->queue_depth + 1))
   {
     printk(WARN_LEAD "Commands queued exceeds queue "
            "depth, active=%d\n",
@@ -8194,7 +8481,7 @@
     aic7xxx_status(cmd) = 0;
     cmd->result = 0;
     cmd->host_scribble = NULL;
-    memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+    memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer));
 
     scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
@@ -8489,6 +8776,7 @@
   {
     aic7xxx_isr(p->irq, p, (void *)NULL);
     pause_sequencer(p);
+    aic7xxx_done_cmds_complete(p);
   }
 
   if ((scb == NULL) || (cmd->serial_number != cmd->serial_number_at_timeout))
@@ -8820,6 +9108,7 @@
   {
     aic7xxx_isr(p->irq, p, (void *)NULL );
     pause_sequencer(p);
+    aic7xxx_done_cmds_complete(p);
   }
 
   if (scb == NULL)
@@ -9069,6 +9358,52 @@
   geom[2] = cylinders;
 
   return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_release
+ *
+ * Description:
+ *   Free the passed in Scsi_Host memory structures prior to unloading the
+ *   module.
+ *-F*************************************************************************/
+int
+aic7xxx_release(struct Scsi_Host *host)
+{
+  struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+  struct aic7xxx_host *next, *prev;
+
+  if(p->irq)
+    free_irq(p->irq, p);
+  release_region(p->base, MAXREG - MINREG);
+  if(p->maddr)
+  {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+    vfree((void *) (((unsigned long) p->maddr) & PAGE_MASK));
+#else
+    iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK));
+#endif
+  }
+  prev = NULL;
+  next = first_aic7xxx;
+  while(next != NULL)
+  {
+    if(next == p)
+    {
+      if(prev == NULL)
+        first_aic7xxx = next->next;
+      else
+        prev->next = next->next;
+    }
+    else
+    {
+      prev = next;
+    }
+    next = next->next;
+  }
+  aic7xxx_free(p);
+  return(0);
 }
 
 #include "aic7xxx_proc.c"
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h
--- v2.0.34/linux/drivers/scsi/aic7xxx.h	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/aic7xxx.h	Mon Jul 13 13:47:33 1998
@@ -51,7 +51,7 @@
 	proc_info: aic7xxx_proc_info,				\
 	name: NULL,						\
 	detect: aic7xxx_detect,					\
-	release: NULL,						\
+	release: aic7xxx_release,				\
 	info: aic7xxx_info,					\
 	command: NULL,						\
 	queuecommand: aic7xxx_queue,				\
@@ -81,7 +81,7 @@
 	proc_info: aic7xxx_proc_info,				\
 	name: NULL,						\
 	detect: aic7xxx_detect,					\
-	release: NULL,						\
+	release: aic7xxx_release,				\
 	info: aic7xxx_info,					\
 	command: NULL,						\
 	queuecommand: aic7xxx_queue,				\
@@ -105,6 +105,7 @@
 extern int aic7xxx_command(Scsi_Cmnd *);
 extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
 extern int aic7xxx_abort(Scsi_Cmnd *);
+extern int aic7xxx_release(struct Scsi_Host *);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
 
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.0.34/linux/drivers/scsi/aic7xxx_proc.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/aic7xxx_proc.c	Mon Jul 13 13:47:33 1998
@@ -243,8 +243,6 @@
             p->adapter_control);
   size += sprintf(BLS, "   Extended Translation: %sabled\n",
       (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
-  size += sprintf(BLS, "         SCSI Bus Reset: %sabled\n",
-      aic7xxx_no_reset ? "Dis" : "En");
   size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
   if (p->type & AHC_ULTRA)
   {
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h
--- v2.0.34/linux/drivers/scsi/aic7xxx_seq.h	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/aic7xxx_seq.h	Mon Jul 13 13:47:33 1998
@@ -496,12 +496,12 @@
 #define	SCB_PAGING      	0x8
 #define	TWIN_CHANNEL    	0x4
 #define	TARGET_MODE     	0x2
-struct patch {
+struct sequencer_patch {
 	int	options;
 	int	negative;
 	int	begin;
 	int	end;
-} patches[] = {
+} sequencer_patches[] = {
 	{ 0x00000002, 0, 0x001, 0x002 },
 	{ 0x00000002, 1, 0x002, 0x003 },
 	{ 0x00000004, 0, 0x009, 0x00d },
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/atari_scsi.c linux/drivers/scsi/atari_scsi.c
--- v2.0.34/linux/drivers/scsi/atari_scsi.c	Mon Jun  3 02:29:12 1996
+++ linux/drivers/scsi/atari_scsi.c	Mon Jul 13 13:47:33 1998
@@ -327,7 +327,7 @@
 		}
 	}
 
-	/* If the DMA is active but not finished, we have the the case
+	/* If the DMA is active but not finished, we have the case
 	 * that some other 5380 interrupt occurred within the DMA transfer.
 	 * This means we have residual bytes, if the desired end address
 	 * is not yet reached. Maybe we have to fetch some bytes from the
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v2.0.34/linux/drivers/scsi/fdomain.c	Tue Dec  2 13:52:32 1997
+++ linux/drivers/scsi/fdomain.c	Mon Jul 13 13:47:33 1998
@@ -1002,7 +1002,7 @@
    Write_FIFO_port       = port_base + Write_FIFO;
    Write_SCSI_Data_port  = port_base + Write_SCSI_Data;
 
-   fdomain_16x0_reset( NULL );
+   fdomain_16x0_reset( NULL, 0 );
 
    if (fdomain_test_loopback()) {
 #if DEBUG_DETECT
@@ -1887,7 +1887,7 @@
    return SCSI_ABORT_SUCCESS;
 }
 
-int fdomain_16x0_reset( Scsi_Cmnd *SCpnt )
+int fdomain_16x0_reset( Scsi_Cmnd *SCpnt, unsigned int flags )
 {
 #if DEBUG_RESET
    static int called_once = 0;
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/fdomain.h linux/drivers/scsi/fdomain.h
--- v2.0.34/linux/drivers/scsi/fdomain.h	Thu Oct 12 22:38:18 1995
+++ linux/drivers/scsi/fdomain.h	Mon Jul 13 13:47:33 1998
@@ -29,7 +29,7 @@
 int        fdomain_16x0_command( Scsi_Cmnd * );
 int        fdomain_16x0_abort( Scsi_Cmnd * );
 const char *fdomain_16x0_info( struct Scsi_Host * );
-int        fdomain_16x0_reset( Scsi_Cmnd * ); 
+int        fdomain_16x0_reset( Scsi_Cmnd *, unsigned int ); 
 int        fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
 int        fdomain_16x0_biosparam( Disk *, kdev_t, int * );
 int        fdomain_16x0_proc_info( char *buffer, char **start, off_t offset,
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.0.34/linux/drivers/scsi/hosts.c	Thu Aug 14 10:31:20 1997
+++ linux/drivers/scsi/hosts.c	Mon Jul 13 13:47:33 1998
@@ -358,7 +358,7 @@
     /* If we are removing the last host registered, it is safe to reuse
      * its host number (this avoids "holes" at boot time) (DB) 
      */
-    if (max_scsi_hosts == next_scsi_host && !scsi_loadable_module_flag)
+    if (max_scsi_hosts == next_scsi_host)
 	max_scsi_hosts--;
     
     next_scsi_host--;
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- v2.0.34/linux/drivers/scsi/ide-scsi.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/ide-scsi.c	Mon Jul 13 13:47:33 1998
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/scsi/ide-scsi.c	Version 0.31 - ALPHA	Apr   3, 1998
+ * linux/drivers/scsi/ide-scsi.c	Version 0.32		May  21, 1998
  *
  * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
  */
@@ -19,6 +19,8 @@
  * Ver 0.3   Jul 24 97   Add support for ATAPI PD/CD drives.
  * Ver 0.31  Apr  3 98   Remove buggy MODE_SENSE6/MODE_SELECT6 translation.
  *                       Add variable irq timeout support.
+ * Ver 0.32  May 21 98   Perform maximal data transfer when the drive wants
+ *                        to send us more data than would fit in our buffer.
  */
 
 #include <linux/module.h>
@@ -276,7 +278,18 @@
 		if ( temp > pc->request_transfer) {
 			if (temp > pc->buffer_size) {
 				printk (KERN_ERR "ide-scsi: The scsi wants to send us more data than expected - discarding data\n");
-				idescsi_discard_data (drive,bcount);
+				temp = pc->buffer_size - pc->actually_transferred;
+				if (temp) {
+					clear_bit(PC_WRITING, &pc->flags);
+					if (pc->sg)
+						idescsi_input_buffers(drive, pc, temp);
+					else
+						atapi_input_bytes(drive, pc->current_position, temp);
+					printk(KERN_ERR "ide-scsi: transferred %d of %d bytes\n", temp, bcount);
+				}
+				pc->actually_transferred += temp;
+				pc->current_position += temp;
+				idescsi_discard_data (drive,bcount - temp);
 				ide_set_handler (drive, &idescsi_pc_intr, get_timeout(pc));
 				return;
 			}
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.0.34/linux/drivers/scsi/ncr53c8xx.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/ncr53c8xx.c	Mon Jul 13 13:47:33 1998
@@ -1861,7 +1861,7 @@
 **	we reach them (for forward jumps).
 **	Therefore we declare a struct here.
 **	If you make changes inside the script,
-**	DONT FORGET TO CHANGE THE LENGTHS HERE!
+**	DON'T FORGET TO CHANGE THE LENGTHS HERE!
 **
 **----------------------------------------------------------
 */
@@ -3769,7 +3769,7 @@
 		case 0x8:
 			/*
 			**	JUMP / CALL
-			**	dont't relocate if relative :-)
+			**	don't relocate if relative :-)
 			*/
 			if (opcode & 0x00800000)
 				relocs = 0;
@@ -6215,7 +6215,7 @@
 	/*
 	**	Why not to try the immediate lower divisor and to choose 
 	**	the one that allows the fastest output speed ?
-	**	We dont want input speed too much greater than output speed.
+	**	We don't want input speed too much greater than output speed.
 	*/
 	if (div >= 1 && fak < 8) {
 		u_long fak2, per2;
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/pas16.c linux/drivers/scsi/pas16.c
--- v2.0.34/linux/drivers/scsi/pas16.c	Mon May  6 02:26:11 1996
+++ linux/drivers/scsi/pas16.c	Mon Jul 13 13:47:33 1998
@@ -510,7 +510,7 @@
     register unsigned char  *d = dst;
     register unsigned short reg = (unsigned short) (instance->io_port + 
 	P_DATA_REG_OFFSET);
-    register i = len;
+    register int i = len;
     int ii = 0;
 
     while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
@@ -546,7 +546,7 @@
     int len) {
     register unsigned char *s = src;
     register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
-    register i = len;
+    register int i = len;
     int ii = 0;
 
     while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v2.0.34/linux/drivers/scsi/ppa.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/ppa.c	Mon Jul 13 13:47:33 1998
@@ -417,7 +417,7 @@
     if (k)
 	return (r & 0xf0);
 
-    /* Counter expired - Time out occured */
+    /* Counter expired - Time out occurred */
     ppa_fail(host_no, DID_TIME_OUT);
     printk("ppa timeout in ppa_wait\n");
     return 0;			/* command timed out */
@@ -1064,7 +1064,7 @@
     unsigned char l = 0, h = 0;
     int retv;
 
-    /* First check for any errors that may of occured
+    /* First check for any errors that may have occurred
      * Here we check for internal errors
      */
     if (tmp->failed)
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h
--- v2.0.34/linux/drivers/scsi/ppa.h	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/ppa.h	Mon Jul 13 13:47:33 1998
@@ -12,11 +12,13 @@
 
 #define   PPA_VERSION   "1.42"
 
+#if 0
 /* Use the following to enable certain chipset support
  * Default is PEDANTIC = 3
  */
 #ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
 #define CONFIG_SCSI_PPA_HAVE_PEDANTIC	3
+#endif
 #endif
 
 /* 
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/qlogicfas.c linux/drivers/scsi/qlogicfas.c
--- v2.0.34/linux/drivers/scsi/qlogicfas.c	Wed Jul 10 21:41:25 1996
+++ linux/drivers/scsi/qlogicfas.c	Mon Jul 13 13:47:34 1998
@@ -656,7 +656,7 @@
 
 /*----------------------------------------------------------------*/
 /* reset SCSI bus */
-int	qlogicfas_reset(Scsi_Cmnd * cmd)
+int	qlogicfas_reset(Scsi_Cmnd * cmd, unsigned int flags)
 {
 	qabort = 2;
 	ql_zap();
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/qlogicfas.h linux/drivers/scsi/qlogicfas.h
--- v2.0.34/linux/drivers/scsi/qlogicfas.h	Wed Jul 10 21:41:25 1996
+++ linux/drivers/scsi/qlogicfas.h	Mon Jul 13 13:47:34 1998
@@ -6,7 +6,7 @@
 int qlogicfas_command(Scsi_Cmnd *);
 int qlogicfas_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
 int qlogicfas_abort(Scsi_Cmnd *);
-int qlogicfas_reset(Scsi_Cmnd *);
+int qlogicfas_reset(Scsi_Cmnd *, unsigned int flags);
 int qlogicfas_biosparam(Disk *, kdev_t, int[]);
 
 #ifndef NULL
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.0.34/linux/drivers/scsi/scsi.c	Mon Jul 13 13:46:36 1998
+++ linux/drivers/scsi/scsi.c	Mon Jul 13 13:47:34 1998
@@ -214,6 +214,7 @@
 #define BLIST_SINGLELUN 0x10
 #define BLIST_NOTQ	0x20
 #define BLIST_SPARSELUN 0x40
+#define BLIST_MAX5LUN	0x80
 
 struct dev_info{
     const char * vendor;
@@ -281,7 +282,9 @@
 {"INSITE","I325VM","*", BLIST_KEY},
 {"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN},
 {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"EMULEX","MD21/S2     ESDI","*", BLIST_SINGLELUN},
@@ -813,6 +816,15 @@
     *max_dev_lun = 8;
     return 1;
   }
+
+  /*
+   * REGAL CDC-4X: avoid hang after LUN 4
+   */
+  if (bflags & BLIST_MAX5LUN) {
+    *max_dev_lun = 5;
+    return 1;
+  }
+
   /*
    * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
    * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
@@ -874,7 +886,8 @@
         scsi_reset (SCpnt,
 		    SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
         return;
-    case (IN_ABORT | IN_RESET | IN_RESET2):
+    case IN_RESET2:
+    case (IN_ABORT | IN_RESET2):
 	/* Obviously the bus reset didn't work.
 	 * Let's try even harder and call for an HBA reset.
          * Maybe the HBA itself crashed and this will shake it loose.
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.0.34/linux/drivers/scsi/scsi.h	Wed Oct 15 15:24:02 1997
+++ linux/drivers/scsi/scsi.h	Mon Jul 13 13:47:34 1998
@@ -521,8 +521,23 @@
 	    req->nr_sectors -= bh->b_size >> 9;
 	    req->sector += bh->b_size >> 9;
 	    bh->b_reqnext = NULL;
-	    mark_buffer_uptodate(bh, uptodate);
-	    unlock_buffer(bh);
+	    /*
+	     * This is our 'MD IO has finished' event handler.
+	     * note that b_state should be cached in a register
+	     * anyways, so the overhead if this checking is almost 
+	     * zero. But anyways .. we never get OO for free :)
+	     */
+	    if (test_bit(BH_MD, &bh->b_state)) {
+		struct md_personality * pers=(struct md_personality *)bh->personality;
+		pers->end_request(bh,uptodate);
+	    }
+	    /*
+	     * the normal (nonmirrored and no RAID5) case:
+	     */
+	    else {
+		mark_buffer_uptodate(bh, uptodate);
+		unlock_buffer(bh);
+	    }
 	    sectors -= bh->b_size >> 9;
 	    if ((bh = req->bh) != NULL) {
 		req->current_nr_sectors = bh->b_size >> 9;
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v2.0.34/linux/drivers/scsi/scsi_proc.c	Mon Aug 11 13:37:24 1997
+++ linux/drivers/scsi/scsi_proc.c	Mon Jul 13 13:47:34 1998
@@ -175,9 +175,9 @@
     
     if (!buf || !cmdList)                           /* bad input ?     */
 	return(NULL);
-    if ((handle = (parseHandle*) kmalloc(sizeof(parseHandle), 1)) == 0)
+    if ((handle = (parseHandle*) kmalloc(sizeof(parseHandle), GFP_KERNEL)) == 0)
 	return(NULL);                               /* out of memory   */
-    if ((handle->cmdPos = (char**) kmalloc(sizeof(int), cmdNum)) == 0) {
+    if ((handle->cmdPos = (char**) kmalloc(sizeof(int) * cmdNum, GFP_KERNEL)) == 0) {
 	kfree(handle);
 	return(NULL);                               /* out of memory   */
     }
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.0.34/linux/drivers/scsi/sd.c	Mon Jul 13 13:46:36 1998
+++ linux/drivers/scsi/sd.c	Mon Jul 13 13:47:34 1998
@@ -76,7 +76,7 @@
 static int check_scsidisk_media_change(kdev_t);
 static int fop_revalidate_scsidisk(kdev_t);
 
-static sd_init_onedisk(int);
+static int sd_init_onedisk(int);
 
 static void requeue_sd_request (Scsi_Cmnd * SCpnt);
 
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/t128.c linux/drivers/scsi/t128.c
--- v2.0.34/linux/drivers/scsi/t128.c	Tue Apr  9 04:25:38 1996
+++ linux/drivers/scsi/t128.c	Mon Jul 13 13:47:34 1998
@@ -324,7 +324,7 @@
     int len) {
     register unsigned char *reg = (unsigned char *) (instance->base + 
 	T_DATA_REG_OFFSET), *d = dst;
-    register i = len;
+    register int i = len;
 
 
 #if 0
@@ -368,7 +368,7 @@
     int len) {
     register unsigned char *reg = (unsigned char *) (instance->base + 
 	T_DATA_REG_OFFSET), *s = src;
-    register i = len;
+    register int i = len;
 
 #if 0
     for (; i; --i) {
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.0.34/linux/drivers/scsi/wd7000.c	Mon Jul 13 13:46:36 1998
+++ linux/drivers/scsi/wd7000.c	Mon Jul 13 13:47:34 1998
@@ -508,7 +508,7 @@
 typedef struct icbUnsMask {	/* I'm totally guessing here */
     unchar op;
     volatile unchar mask[14];	/* mask bits                 */
-#ifdef 0
+#if 0
     unchar rsvd[12];		/* reserved                  */
 #endif
     volatile unchar vue;	/* vendor-unique error code  */
@@ -1686,7 +1686,7 @@
 /*
  *  I also have no idea how to do a reset...
  */
-int wd7000_reset (Scsi_Cmnd *SCpnt)
+int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int flags)
 {
     return (SCSI_RESET_PUNT);
 }
diff -u --recursive --new-file v2.0.34/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h
--- v2.0.34/linux/drivers/scsi/wd7000.h	Mon Aug 11 13:37:24 1997
+++ linux/drivers/scsi/wd7000.h	Mon Jul 13 13:47:34 1998
@@ -20,7 +20,7 @@
 int wd7000_command (Scsi_Cmnd *);
 int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int wd7000_abort (Scsi_Cmnd *);
-int wd7000_reset (Scsi_Cmnd *);
+int wd7000_reset (Scsi_Cmnd *, unsigned int);
 int wd7000_biosparam (Disk *, kdev_t, int *);
 
 #ifndef NULL
diff -u --recursive --new-file v2.0.34/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v2.0.34/linux/drivers/sound/Readme.cards	Sun Aug 31 08:47:02 1997
+++ linux/drivers/sound/Readme.cards	Mon Jul 13 13:47:34 1998
@@ -687,7 +687,7 @@
 DMA numbers. Using the same values than with DOS/Win is a good idea. Don't
 attempt to use the same IRQ or DMA channels twice.
 
-The SB mode of ATP is implemented so the the ATP driver just enables SB
+The SB mode of ATP is implemented so the ATP driver just enables SB
 in the proper address. The SB driver handles the rest. You have to configure
 both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O
 settings.
diff -u --recursive --new-file v2.0.34/linux/drivers/sound/Readme.linux linux/drivers/sound/Readme.linux
--- v2.0.34/linux/drivers/sound/Readme.linux	Sun Aug 31 08:47:09 1997
+++ linux/drivers/sound/Readme.linux	Mon Jul 13 13:47:34 1998
@@ -28,7 +28,7 @@
   sound support during "make config"). Please refer to kernel documentation
   for instructions about configuring and compiling kernel. File Readme.cards
   contains card specific instructions for configuring this driver for
-  use with various soundcards.
+  use with various sound cards.
 
 Boot time configuration (using lilo and insmod) 
 -----------------------------------------------
diff -u --recursive --new-file v2.0.34/linux/fs/Config.in linux/fs/Config.in
--- v2.0.34/linux/fs/Config.in	Mon Jul 13 13:46:37 1998
+++ linux/fs/Config.in	Mon Jul 13 13:47:34 1998
@@ -10,7 +10,7 @@
 tristate 'Second extended fs support' CONFIG_EXT2_FS
 tristate 'xiafs filesystem support' CONFIG_XIA_FS
 
-tristate 'Native language support (Unicode, codepages)' CONFIG_NLS
+tristate 'Native language support (Needed for FAT and ISO9660)' CONFIG_NLS
 if [ "$CONFIG_NLS" = "y" -o "$CONFIG_NLS" = "m" ]; then
   dep_tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS $CONFIG_NLS
 
diff -u --recursive --new-file v2.0.34/linux/fs/buffer.c linux/fs/buffer.c
--- v2.0.34/linux/fs/buffer.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/buffer.c	Mon Jul 13 13:47:34 1998
@@ -53,8 +53,8 @@
 static struct buffer_head * free_list[NR_SIZES] = {NULL, };
 
 static struct buffer_head * unused_list = NULL;
-static struct buffer_head * reuse_list = NULL;
-static struct wait_queue * buffer_wait = NULL;
+static struct buffer_head * reuse_list  = NULL;
+struct wait_queue *         buffer_wait = NULL;
 
 static int nr_buffers = 0;
 static int nr_buffers_type[NR_LIST] = {0,};
@@ -462,6 +462,11 @@
 	return NULL;
 }
 
+struct buffer_head *efind_buffer(kdev_t dev, int block, int size)
+{
+	return find_buffer(dev, block, size);
+}
+
 /*
  * Why like this, I hear you say... The reason is race-conditions.
  * As we don't lock buffers (unless we are reading them, that is),
@@ -650,6 +655,9 @@
 	/* If there are too many dirty buffers, we wake up the update process
 	   now so as to ensure that there are still clean buffers available
 	   for user processes to use (and dirty) */
+
+	if (nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100)
+		wakeup_bdflush(1);
 	
 	/* We are going to try to locate this much memory */
 	needed = bdf_prm.b_un.nrefill * size;  
@@ -1247,7 +1255,9 @@
 	struct buffer_head *tmp;
 	struct page *page;
 
-	clear_bit(BH_Lock, &bh->b_state);
+	if (!clear_bit(BH_Lock, &bh->b_state))
+		printk ("unlock_buffer: already unlocked on %s\n",
+			kdevname(bh->b_dev));
 	wake_up(&bh->b_wait);
 	if (waitqueue_active(&buffer_wait))
 		wake_up(&buffer_wait);
@@ -1594,7 +1604,7 @@
 	if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
 	printk("Wrote %d/%d buffers\n", nwritten, ndirty);
 #endif
-	
+	run_task_queue(&tq_disk); 
 	return 0;
 }
 
diff -u --recursive --new-file v2.0.34/linux/fs/dquot.c linux/fs/dquot.c
--- v2.0.34/linux/fs/dquot.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/dquot.c	Mon Jul 13 13:47:34 1998
@@ -692,7 +692,7 @@
 	short cnt;
 	struct dquot *tmp;
 
-	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
 		for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 			if (type != -1 && cnt != type)
 				continue;
@@ -843,6 +843,15 @@
 		blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE);
 	else
 		blocks = (inode->i_blocks / 2);
+		
+		
+	/*
+	 *	This shouldnt be needed but the goal is to fix 2.0 not
+	 *	do things in best of Torvalds style. Thats for 2.1...
+	 */
+	 
+	if(S_ISFIFO(inode->i_mode)||S_ISSOCK(inode->i_mode))
+		blocks = 0;
 
 	/*
 	 * Build the transfer_from and transfer_to lists and check quotas to see
diff -u --recursive --new-file v2.0.34/linux/fs/exec.c linux/fs/exec.c
--- v2.0.34/linux/fs/exec.c	Tue Mar 10 13:19:09 1998
+++ linux/fs/exec.c	Mon Jul 13 13:47:34 1998
@@ -398,6 +398,8 @@
 		old_mm = current->mm;
 		current->mm = mm;
 		if (new_page_tables(current)) {
+			/* The pgd belongs to the parent ... don't free it! */
+			mm->pgd = NULL;
 			current->mm = old_mm;
 			exit_mmap(mm);
 			kfree(mm);
diff -u --recursive --new-file v2.0.34/linux/fs/ext2/dir.c linux/fs/ext2/dir.c
--- v2.0.34/linux/fs/ext2/dir.c	Wed Aug  6 17:52:01 1997
+++ linux/fs/ext2/dir.c	Mon Jul 13 13:47:34 1998
@@ -41,7 +41,7 @@
 	NULL,			/* mmap */
 	NULL,			/* no special open code */
 	NULL,			/* no special release code */
-	file_fsync,		/* fsync */
+	ext2_sync_file,		/* fsync */
 	NULL,			/* fasync */
 	NULL,			/* check_media_change */
 	NULL			/* revalidate */
diff -u --recursive --new-file v2.0.34/linux/fs/ext2/fsync.c linux/fs/ext2/fsync.c
--- v2.0.34/linux/fs/ext2/fsync.c	Sun Nov 26 09:23:10 1995
+++ linux/fs/ext2/fsync.c	Mon Jul 13 13:47:34 1998
@@ -167,9 +167,6 @@
 {
 	int wait, err = 0;
 
-	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
-	     S_ISLNK(inode->i_mode)))
-		return -EINVAL;
 	if (S_ISLNK(inode->i_mode) && !(inode->i_blocks))
 		/*
 		 * Don't sync fast links!
diff -u --recursive --new-file v2.0.34/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v2.0.34/linux/fs/ext2/ialloc.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/ext2/ialloc.c	Mon Jul 13 13:47:34 1998
@@ -294,7 +294,10 @@
 	struct ext2_super_block * es;
 
 	if (!dir || !(inode = get_empty_inode ()))
+	{
+		*err=-ENOMEM;
 		return NULL;
+	}
 	sb = dir->i_sb;
 	inode->i_sb = sb;
 	inode->i_flags = sb->s_flags;
diff -u --recursive --new-file v2.0.34/linux/fs/fat/dir.c linux/fs/fat/dir.c
--- v2.0.34/linux/fs/fat/dir.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/fat/dir.c	Mon Jul 13 13:47:34 1998
@@ -429,10 +429,10 @@
 	}
 	case VFAT_IOCTL_READDIR_SHORT: {
 		struct dirent *d1 = (struct dirent *)arg;
-		put_user(0, &d1->d_reclen);
 		err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
 		if (err)
 			return err;
+		put_user(0, &d1->d_reclen);
 		return fat_readdirx(inode,filp,(void *)arg,
 				    vfat_ioctl_fill, NULL, 1, 0, 1);
 	}
diff -u --recursive --new-file v2.0.34/linux/fs/fcntl.c linux/fs/fcntl.c
--- v2.0.34/linux/fs/fcntl.c	Tue Sep 16 15:47:57 1997
+++ linux/fs/fcntl.c	Mon Jul 13 13:47:35 1998
@@ -102,7 +102,7 @@
 			/*
 			 * XXX If f_owner is a process group, the
 			 * negative return value will get converted
-			 * into an error.  Oops.  If we keep the the
+			 * into an error.  Oops.  If we keep the
 			 * current syscall conventions, the only way
 			 * to fix this will be in libc.
 			 */
@@ -134,7 +134,7 @@
 			match = -p->pgrp;
 		if (pid != match)
 			continue;
-		if (!euid &&
+		if ((euid != 0) &&
 		    (euid ^ p->suid) && (euid ^ p->uid) &&
 		    (uid ^ p->suid) && (uid ^ p->uid))
 			continue;
diff -u --recursive --new-file v2.0.34/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.0.34/linux/fs/nfs/dir.c	Mon Jul 13 13:46:38 1998
+++ linux/fs/nfs/dir.c	Mon Jul 13 13:47:35 1998
@@ -553,11 +553,13 @@
 	int		error, slen;
 
 	slen = sprintf(silly, ".nfs%ld", inode->i_ino);
-	if ((error = nfs_unlink(dir, silly, slen)) < 0) {
+	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly);
+	nfs_lookup_cache_remove(dir, NULL, silly);
+	if (error < 0)
 		printk("NFS silly_rename cleanup failed (err = %d)\n",
 					-error);
-	}
 	NFS_RENAMED_DIR(inode) = NULL;
+	iput(dir);
 }
 
 static int nfs_unlink(struct inode *dir, const char *name, int len)
diff -u --recursive --new-file v2.0.34/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v2.0.34/linux/fs/nfs/nfsroot.c	Fri May 31 03:46:27 1996
+++ linux/fs/nfs/nfsroot.c	Mon Jul 13 13:47:35 1998
@@ -79,7 +79,7 @@
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>	/* For AX25_P_IP */
 #endif
 #include <linux/skbuff.h>
@@ -306,7 +306,7 @@
 
 	/* If it's not ethernet or AX25, delete it. */
 	if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 	   (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
 #endif
 	    rarp->ar_pln != 4) {
diff -u --recursive --new-file v2.0.34/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.0.34/linux/fs/proc/array.c	Sun Aug  3 13:59:07 1997
+++ linux/fs/proc/array.c	Mon Jul 13 13:47:37 1998
@@ -28,6 +28,8 @@
  *
  * Yves Arrouye      :  remove removal of trailing spaces in get_array.
  *			<Yves.Arrouye@marin.fdn.fr>
+ *
+ * Alan Cox	     :  security fixes. <Alan.Cox@linux.org>a
  */
 
 #include <linux/types.h>
@@ -409,7 +411,7 @@
 	return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer);
 }
 
-static unsigned long get_wchan(struct task_struct *p)
+unsigned long get_wchan(struct task_struct *p)
 {
 	if (!p || p == current || p->state == TASK_RUNNING)
 		return 0;
@@ -1056,6 +1058,28 @@
 	return -EBADF;
 }
 
+static int process_unauthorized(int type, int pid)
+{
+	struct task_struct ** p = get_task(pid);
+
+	if (!p || !*p || !(*p)->mm)
+		return 1;
+
+	switch(type)
+	{
+		case PROC_PID_STATUS:
+		case PROC_PID_STATM:
+		case PROC_PID_STAT:
+		case PROC_PID_MAPS:
+		case PROC_PID_CMDLINE:
+			return 0;	
+	}
+	if(current->fsuid == (*p)->euid)
+		return 0;
+	return 1;
+}
+
+
 static int get_process_array(char * page, int pid, int type)
 {
 	switch (type) {
@@ -1103,6 +1127,13 @@
 	type &= 0x0000ffff;
 	start = NULL;
 	dp = (struct proc_dir_entry *) inode->u.generic_ip;
+	
+	if (pid && process_unauthorized(type, pid))
+	{
+		free_page(page);
+		return -EIO;
+	}
+	
 	if (dp->get_info)
 		length = dp->get_info((char *)page, &start, file->f_pos,
 				      count, 0);
diff -u --recursive --new-file v2.0.34/linux/fs/stat.c linux/fs/stat.c
--- v2.0.34/linux/fs/stat.c	Wed Aug  6 17:52:01 1997
+++ linux/fs/stat.c	Mon Jul 13 13:47:37 1998
@@ -23,9 +23,13 @@
 static void cp_old_stat(struct inode * inode, struct old_stat * statbuf)
 {
 	struct old_stat tmp;
+	static int nagcount = 0;
+
+	if (++nagcount < 5)
+		printk("VFS: Warning: %s using old stat() call. "
+			"Recompile your binary.\n",
+			current->comm);
 
-	printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n",
-		current->comm);
 	tmp.st_dev = kdev_t_to_nr(inode->i_dev);
 	tmp.st_ino = inode->i_ino;
 	tmp.st_mode = inode->i_mode;
diff -u --recursive --new-file v2.0.34/linux/fs/super.c linux/fs/super.c
--- v2.0.34/linux/fs/super.c	Mon Jul 13 13:46:38 1998
+++ linux/fs/super.c	Mon Jul 13 13:47:37 1998
@@ -47,6 +47,7 @@
 extern void wait_for_keypress(void);
 extern struct file_operations * get_blkfops(unsigned int major);
 extern void blkdev_release (struct inode *);
+extern void rd_load_secondary(void);
 
 extern int root_mountflags;
 
diff -u --recursive --new-file v2.0.34/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.0.34/linux/fs/sysv/inode.c	Sat Nov 30 02:21:21 1996
+++ linux/fs/sysv/inode.c	Mon Jul 13 13:47:37 1998
@@ -165,7 +165,6 @@
 	sb->sv_sb_flc_blocks = &sbd1->s_free[0];
 	sb->sv_sb_total_free_blocks = &sbd2->s_tfree;
 	sb->sv_sb_time = &sbd2->s_time;
-	sb->sv_block_base = 0;
 	sb->sv_firstinodezone = 2;
 	sb->sv_firstdatazone = sbd1->s_isize;
 	sb->sv_nzones = sbd1->s_fsize;
@@ -223,7 +222,6 @@
 	sb->sv_sb_total_free_blocks = &sbd->s_tfree;
 	sb->sv_sb_time = &sbd->s_time;
 	sb->sv_sb_state = &sbd->s_state;
-	sb->sv_block_base = 0;
 	sb->sv_firstinodezone = 2;
 	sb->sv_firstdatazone = sbd->s_isize;
 	sb->sv_nzones = sbd->s_fsize;
@@ -281,7 +279,6 @@
 	sb->sv_sb_total_free_blocks = &sbd->s_tfree;
 	sb->sv_sb_time = &sbd->s_time;
 	sb->sv_sb_state = &sbd->s_state;
-	sb->sv_block_base = 0;
 	sb->sv_firstinodezone = 2;
 	sb->sv_firstdatazone = sbd->s_isize;
 	sb->sv_nzones = sbd->s_fsize;
@@ -328,7 +325,6 @@
 	sb->sv_sb_flc_blocks = &sbd->s_free[0];
 	sb->sv_sb_total_free_blocks = &sbd->s_tfree;
 	sb->sv_sb_time = &sbd->s_time;
-	sb->sv_block_base = 0;
 	sb->sv_firstinodezone = 2;
 	sb->sv_firstdatazone = sbd->s_isize;
 	sb->sv_nzones = from_coh_ulong(sbd->s_fsize);
@@ -355,6 +351,7 @@
 	MOD_INC_USE_COUNT;
 	lock_super(sb);
 	set_blocksize(dev,BLOCK_SIZE);
+	sb->sv_block_base = 0;
 
 	/* Try to read Xenix superblock */
 	if ((bh = bread(dev, 1, BLOCK_SIZE)) != NULL) {
diff -u --recursive --new-file v2.0.34/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v2.0.34/linux/fs/umsdos/emd.c	Tue Feb  6 23:39:29 1996
+++ linux/fs/umsdos/emd.c	Mon Jul 13 13:47:38 1998
@@ -104,7 +104,7 @@
 	struct inode *ret = NULL;
 	if (dir->u.umsdos_i.i_emd_dir != 0){
 		ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
-		PRINTK (("deja trouve %d %x [%d] "
+		PRINTK (("deja trouve %d %x [%ld] "
 			,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
 	}else{
 		umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
diff -u --recursive --new-file v2.0.34/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.0.34/linux/fs/umsdos/inode.c	Sat Nov 30 02:21:22 1996
+++ linux/fs/umsdos/inode.c	Mon Jul 13 13:47:38 1998
@@ -223,7 +223,7 @@
 {
 	PRINTK (("read inode %x ino = %d ",inode,inode->i_ino));
 	msdos_read_inode(inode);
-	PRINTK (("ino = %d %d\n",inode->i_ino,inode->i_count));
+	PRINTK (("ino = %d %ld\n",inode->i_ino,inode->i_count));
 	if (S_ISDIR(inode->i_mode)
 		&& (inode->u.umsdos_i.u.dir_info.creating != 0
 			|| inode->u.umsdos_i.u.dir_info.looking != 0
diff -u --recursive --new-file v2.0.34/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v2.0.34/linux/fs/umsdos/namei.c	Sat Nov 30 02:21:23 1996
+++ linux/fs/umsdos/namei.c	Mon Jul 13 13:47:38 1998
@@ -231,7 +231,7 @@
 				if (ret == 0){
 					struct inode *inode = *result;
 					umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
-					PRINTK (("inode %p[%d] ",inode,inode->i_count));
+					PRINTK (("inode %p[%ld] ",inode,inode->i_count));
 					PRINTK (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino
 						,info.fake.fname,current->pid,info.f_pos));
 				}else{
@@ -568,7 +568,7 @@
 	}else if ((ret = umsdos_nevercreat(dir,name,len,-EPERM))==0){
 		struct inode *olddir;
 		ret = umsdos_get_dirowner(oldinode,&olddir);
-		PRINTK (("umsdos_link dir_owner = %d -> %p [%d] "
+		PRINTK (("umsdos_link dir_owner = %d -> %p [%ld] "
 			,oldinode->u.umsdos_i.i_dir_owner,olddir,olddir->i_count));
 		if (ret == 0){
 			struct umsdos_dirent entry;
@@ -594,7 +594,7 @@
 					ret = umsdos_newhidden (olddir,&info);
 					if (ret == 0){
 						olddir->i_count+=2;
-						PRINTK (("olddir[%d] ",olddir->i_count));
+						PRINTK (("olddir[%ld] ",olddir->i_count));
 						ret = umsdos_rename_f (olddir,entry.name
 							,entry.name_len
 							,olddir,info.entry.name,info.entry.name_len
@@ -604,9 +604,9 @@
 							if (path == NULL){
 								ret = -ENOMEM;
 							}else{
-								PRINTK (("olddir[%d] ",olddir->i_count));
+								PRINTK (("olddir[%ld] ",olddir->i_count));
 								ret = umsdos_locate_path (oldinode,path);
-								PRINTK (("olddir[%d] ",olddir->i_count));
+								PRINTK (("olddir[%ld] ",olddir->i_count));
 								if (ret == 0){
 									olddir->i_count++;
 									ret = umsdos_symlink_x (olddir
@@ -875,7 +875,7 @@
 			if (sdir->i_count > 1){
 				ret = -EBUSY;
 			}else if ((empty = umsdos_isempty (sdir)) != 0){
-				PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+				PRINTK (("isempty %d i_count %ld ",empty,sdir->i_count));
 				/* check sticky bit */
 				if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
 				    current->fsuid == sdir->i_uid ||
diff -u --recursive --new-file v2.0.34/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v2.0.34/linux/fs/umsdos/rdir.c	Tue Feb  6 23:39:29 1996
+++ linux/fs/umsdos/rdir.c	Mon Jul 13 13:47:38 1998
@@ -175,7 +175,7 @@
 			if (ret == 0){
 				int empty;
 				if ((empty = umsdos_isempty (sdir)) != 0){
-					PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+					PRINTK (("isempty %d i_count %ld ",empty,sdir->i_count));
 					if (empty == 2){
 						/*
 							Not a Umsdos directory, so the previous msdos_rmdir
diff -u --recursive --new-file v2.0.34/linux/include/asm-alpha/termios.h linux/include/asm-alpha/termios.h
--- v2.0.34/linux/include/asm-alpha/termios.h	Sun Mar 24 02:09:37 1996
+++ linux/include/asm-alpha/termios.h	Mon Jul 13 13:47:38 1998
@@ -71,6 +71,7 @@
 #define N_SLIP		1
 #define N_MOUSE		2
 #define N_PPP		3
+#define N_AX25		5
 
 #ifdef __KERNEL__
 /*	eof=^D		eol=\0		eol2=\0		erase=del
diff -u --recursive --new-file v2.0.34/linux/include/asm-i386/ptrace.h linux/include/asm-i386/ptrace.h
--- v2.0.34/linux/include/asm-i386/ptrace.h	Wed Sep 13 00:08:39 1995
+++ linux/include/asm-i386/ptrace.h	Mon Jul 13 13:47:38 1998
@@ -43,6 +43,12 @@
 	unsigned short ss, __ssu;
 };
 
+/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */
+#define PTRACE_GETREGS            12
+#define PTRACE_SETREGS            13
+#define PTRACE_GETFPREGS          14
+#define PTRACE_SETFPREGS          15
+
 #ifdef __KERNEL__
 #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs))
 #define instruction_pointer(regs) ((regs)->eip)
diff -u --recursive --new-file v2.0.34/linux/include/asm-i386/termios.h linux/include/asm-i386/termios.h
--- v2.0.34/linux/include/asm-i386/termios.h	Mon Aug  4 15:01:06 1997
+++ linux/include/asm-i386/termios.h	Mon Jul 13 13:47:38 1998
@@ -52,6 +52,7 @@
 #define N_MOUSE		2
 #define N_PPP		3
 #define N_STRIP		4
+#define N_AX25		5
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.0.34/linux/include/asm-i386/vm86.h linux/include/asm-i386/vm86.h
--- v2.0.34/linux/include/asm-i386/vm86.h	Wed Dec 11 06:41:01 1996
+++ linux/include/asm-i386/vm86.h	Mon Jul 13 13:47:38 1998
@@ -160,7 +160,7 @@
  * this way. In front of 'return-eip' may be some data, depending on
  * compilation, so we don't rely on this and save the pointer to 'oldregs'
  * in 'regs32' above.
- * However, with GCC-2.7.2 and the the current CFLAGS you see exactly this:
+ * However, with GCC-2.7.2 and the current CFLAGS you see exactly this:
 
 	long return-eip;        from call to vm86()
 	struct pt_regs oldregs;  user space registers as saved by syscall
diff -u --recursive --new-file v2.0.34/linux/include/asm-m68k/termios.h linux/include/asm-m68k/termios.h
--- v2.0.34/linux/include/asm-m68k/termios.h	Fri Apr  5 03:52:09 1996
+++ linux/include/asm-m68k/termios.h	Mon Jul 13 13:47:38 1998
@@ -52,6 +52,7 @@
 #define N_MOUSE		2
 #define N_PPP		3
 #define N_STRIP		4
+#define N_AX25		5
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.0.34/linux/include/asm-ppc/termios.h linux/include/asm-ppc/termios.h
--- v2.0.34/linux/include/asm-ppc/termios.h	Mon May 27 02:00:59 1996
+++ linux/include/asm-ppc/termios.h	Mon Jul 13 13:47:38 1998
@@ -343,6 +343,7 @@
 #define N_SLIP		1
 #define N_MOUSE		2
 #define N_PPP		3
+#define N_AX25		5
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.0.34/linux/include/asm-sparc/termios.h linux/include/asm-sparc/termios.h
--- v2.0.34/linux/include/asm-sparc/termios.h	Sun Apr 21 02:30:34 1996
+++ linux/include/asm-sparc/termios.h	Mon Jul 13 13:47:38 1998
@@ -57,6 +57,7 @@
 #define N_SLIP		1
 #define N_MOUSE		2
 #define N_PPP		3
+#define N_AX25		5
 
 #ifdef __KERNEL__
 /*	intr=^C		quit=^\		erase=del	kill=^U
diff -u --recursive --new-file v2.0.34/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v2.0.34/linux/include/linux/ax25.h	Tue Mar 10 13:19:09 1998
+++ linux/include/linux/ax25.h	Mon Jul 13 13:47:38 1998
@@ -16,57 +16,36 @@
 #define AX25_T2		5
 #define	AX25_BACKOFF	6
 #define	AX25_EXTSEQ	7
-#define	AX25_HDRINCL	8
+#define	AX25_PIDINCL	8
 #define AX25_IDLE	9
 #define AX25_PACLEN	10
-#define AX25_IPMAXQUEUE 11
+#define AX25_IAMDIGI	12
 
 #define AX25_KILL	99
 
-#define SIOCAX25GETUID		(SIOCPROTOPRIVATE)
+#define SIOCAX25GETUID		(SIOCPROTOPRIVATE+0)
 #define SIOCAX25ADDUID		(SIOCPROTOPRIVATE+1)
 #define SIOCAX25DELUID		(SIOCPROTOPRIVATE+2)
 #define SIOCAX25NOUID		(SIOCPROTOPRIVATE+3)
-#define	SIOCAX25BPQADDR		(SIOCPROTOPRIVATE+4)
-#define	SIOCAX25GETPARMS	(SIOCPROTOPRIVATE+5)
-#define	SIOCAX25SETPARMS	(SIOCPROTOPRIVATE+6)
 #define SIOCAX25OPTRT		(SIOCPROTOPRIVATE+7)
 #define SIOCAX25CTLCON		(SIOCPROTOPRIVATE+8)
+#define SIOCAX25GETINFO		(SIOCPROTOPRIVATE+9)
+#define SIOCAX25ADDFWD		(SIOCPROTOPRIVATE+10)
+#define SIOCAX25DELFWD		(SIOCPROTOPRIVATE+11)
 
 #define AX25_SET_RT_IPMODE	2
 
 #define AX25_NOUID_DEFAULT	0
 #define AX25_NOUID_BLOCK	1
 
-#define	AX25_DIGI_INBAND	0x01	/* Allow digipeating within port **/
-#define	AX25_DIGI_XBAND		0x02	/* Allow digipeating across ports **/
-
-#define	AX25_VALUES_IPDEFMODE	0	/* 'D'=DG 'V'=VC */
-#define	AX25_VALUES_AXDEFMODE	1	/* 8=Normal 128=Extended Seq Nos */
-#define	AX25_VALUES_NETROM	2	/* Allow NET/ROM  - 0=No 1=Yes */
-#define	AX25_VALUES_TEXT	3	/* Allow PID=Text - 0=No 1=Yes */
-#define	AX25_VALUES_BACKOFF	4	/* 'E'=Exponential 'L'=Linear */
-#define	AX25_VALUES_CONMODE	5	/* Allow connected modes - 0=No 1=Yes */
-#define	AX25_VALUES_WINDOW	6	/* Default window size for standard AX.25 */
-#define	AX25_VALUES_EWINDOW	7	/* Default window size for extended AX.25 */
-#define	AX25_VALUES_T1		8	/* Default T1 timeout value */
-#define	AX25_VALUES_T2		9	/* Default T2 timeout value */
-#define	AX25_VALUES_T3		10	/* Default T3 timeout value */
-#define	AX25_VALUES_N2		11	/* Default N2 value */
-#define	AX25_VALUES_DIGI	12	/* Digipeat mode */
-#define AX25_VALUES_IDLE	13	/* mode vc idle timer */
-#define AX25_VALUES_PACLEN	14	/* AX.25 MTU */
-#define AX25_VALUES_IPMAXQUEUE  15	/* Maximum number of buffers enqueued */
-#define	AX25_MAX_VALUES		20
-
 typedef struct {
-	char ax25_call[7];	/* 6 call + SSID (shifted ascii!) */
+	char		ax25_call[7];	/* 6 call + SSID (shifted ascii!) */
 } ax25_address;
 
 struct sockaddr_ax25 {
-	short sax25_family;
-	ax25_address sax25_call;
-	int sax25_ndigis;
+	unsigned short	sax25_family;
+	ax25_address	sax25_call;
+	int		sax25_ndigis;
 	/* Digipeater ax25_address sets follow */
 };
 
@@ -74,39 +53,44 @@
 
 struct full_sockaddr_ax25 {
 	struct sockaddr_ax25 fsa_ax25;
-	ax25_address fsa_digipeater[AX25_MAX_DIGIS];
+	ax25_address	fsa_digipeater[AX25_MAX_DIGIS];
 };
 
 struct ax25_routes_struct {
-	ax25_address port_addr;
-	ax25_address dest_addr;
-	unsigned char digi_count;
-	ax25_address digi_addr[AX25_MAX_DIGIS];
+	ax25_address	port_addr;
+	ax25_address	dest_addr;
+	unsigned char	digi_count;
+	ax25_address	digi_addr[AX25_MAX_DIGIS];
 };
 
 struct ax25_route_opt_struct {
-	ax25_address port_addr;
-	ax25_address dest_addr;
-	int cmd;
-	int arg;
+	ax25_address	port_addr;
+	ax25_address	dest_addr;
+	int		cmd;
+	int		arg;
 };
 
 struct ax25_ctl_struct {
-	ax25_address port_addr;
-	ax25_address source_addr;
-	ax25_address dest_addr;
-	unsigned int cmd;
-	unsigned long arg;
-};
-
-struct ax25_bpqaddr_struct {
-	char dev[16];
-	ax25_address addr;
-};
-
-struct ax25_parms_struct {
-	ax25_address port_addr;
-	unsigned short values[AX25_MAX_VALUES];
+	ax25_address	port_addr;
+	ax25_address	source_addr;
+	ax25_address	dest_addr;
+	unsigned int	cmd;
+	unsigned long	arg;
+};
+
+struct ax25_info_struct {
+	unsigned int	n2, n2count;
+	unsigned int	t1, t1timer;
+	unsigned int	t2, t2timer;
+	unsigned int	t3, t3timer;
+	unsigned int	idle, idletimer;
+	unsigned int	state;
+	unsigned int	rcv_q, snd_q;
+};
+
+struct ax25_fwd_struct {
+	ax25_address	port_from;
+	ax25_address	port_to;
 };
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/baycom.h linux/include/linux/baycom.h
--- v2.0.34/linux/include/linux/baycom.h	Mon Jul  8 00:21:46 1996
+++ linux/include/linux/baycom.h	Mon Jul 13 13:47:38 1998
@@ -7,123 +7,36 @@
 #ifndef _BAYCOM_H
 #define _BAYCOM_H
 
-#include <linux/ioctl.h>
-#undef BAYCOM_DEBUG
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
 
 /* -------------------------------------------------------------------- */
-
-struct baycom_statistics {
-	unsigned long rx_packets, tx_packets;
-	unsigned long ptt_keyed;
-	unsigned long rx_bufferoverrun, tx_bufferoverrun;
-};
-
-struct baycom_params {
-	int modem_type;
-	int iobase;
-	int irq;
-	int options;
-	int tx_delay;  /* the transmitter keyup delay in 10ms units */
-	int tx_tail;   /* the transmitter keyoff delay in 10ms units */
-	int slottime;  /* the slottime in 10ms; usually 10 = 100ms */
-	int ppersist;  /* the p-persistence 0..255 */
-	int fulldup;   /* the driver does not support full duplex, setting */
-	               /* this just makes the driver send even if DCD is on */
-};	
-
-/* -------------------------------------------------------------------- */
-
-#define BAYCOM_MAJOR 		51
-
-/* maximum packet length, excluding CRC */
-#define BAYCOM_MAXFLEN 		400	
-
-/* the ioctl type of this driver */
-#define BAYCOM_IOCTL_TYPE       'B'
-
-#define KISS_FEND   ((unsigned char)0300)
-#define KISS_FESC   ((unsigned char)0333)
-#define KISS_TFEND  ((unsigned char)0334)
-#define KISS_TFESC  ((unsigned char)0335)
-
-#define KISS_CMD_DATA       0
-#define KISS_CMD_TXDELAY    1
-#define KISS_CMD_PPERSIST   2
-#define KISS_CMD_SLOTTIME   3
-#define KISS_CMD_TXTAIL     4
-#define KISS_CMD_FULLDUP    5
-
-/*
- * use bottom halves? (HDLC processing done with interrupts on or off)
- */
-#define BAYCOM_USE_BH
-
 /*
- * modem types
+ * structs for the IOCTL commands
  */
 
-#define BAYCOM_MODEM_INVALID 0
-#define BAYCOM_MODEM_SER12   1
-#define BAYCOM_MODEM_PAR96   2
-
-/*
- * modem options; bit mask
- */
-#define BAYCOM_OPTIONS_SOFTDCD  1
-
+struct baycom_debug_data {
+	unsigned long debug1;
+	unsigned long debug2;
+	long debug3;
+};
 
-/*
- * ioctl constants
- */
-#define BAYCOMCTL_GETDCD           _IOR(BAYCOM_IOCTL_TYPE, 0, unsigned char)
-#define BAYCOMCTL_GETPTT           _IOR(BAYCOM_IOCTL_TYPE, 1, unsigned char)
-#define BAYCOMCTL_PARAM_TXDELAY    _IO(BAYCOM_IOCTL_TYPE, 2)
-#define BAYCOMCTL_PARAM_PPERSIST   _IO(BAYCOM_IOCTL_TYPE, 3)
-#define BAYCOMCTL_PARAM_SLOTTIME   _IO(BAYCOM_IOCTL_TYPE, 4)
-#define BAYCOMCTL_PARAM_TXTAIL     _IO(BAYCOM_IOCTL_TYPE, 5)
-#define BAYCOMCTL_PARAM_FULLDUP    _IO(BAYCOM_IOCTL_TYPE, 6)
-
-#define BAYCOMCTL_GETSTAT          _IOR(BAYCOM_IOCTL_TYPE, 7, \
-					struct baycom_statistics)
-
-#define BAYCOMCTL_GETPARAMS        _IOR(BAYCOM_IOCTL_TYPE, 8, \
-					struct baycom_params)
-#define BAYCOMCTL_SETPARAMS        _IOR(BAYCOM_IOCTL_TYPE, 9, \
-					struct baycom_params)
+struct baycom_ioctl {
+	int cmd;
+	union {
+		struct baycom_debug_data dbg;
+	} data;
+};
 
-#define BAYCOMCTL_CALIBRATE        _IO(BAYCOM_IOCTL_TYPE, 10)
+/* -------------------------------------------------------------------- */
 
-#ifdef BAYCOM_DEBUG
 /*
- * these are mainly for debugging purposes
+ * ioctl values change for baycom
  */
-#define BAYCOMCTL_GETSAMPLES       _IOR(BAYCOM_IOCTL_TYPE, 16, unsigned char)
-#define BAYCOMCTL_GETBITS          _IOR(BAYCOM_IOCTL_TYPE, 17, unsigned char)
-
-#define BAYCOMCTL_DEBUG1           _IOR(BAYCOM_IOCTL_TYPE, 18, unsigned long)
-#define BAYCOMCTL_DEBUG2           _IOR(BAYCOM_IOCTL_TYPE, 19, unsigned long)
-#define BAYCOMCTL_DEBUG3           _IOR(BAYCOM_IOCTL_TYPE, 20, unsigned long)
-#endif /* BAYCOM_DEBUG */
+#define BAYCOMCTL_GETDEBUG       0x92
 
 /* -------------------------------------------------------------------- */
 
 #endif /* _BAYCOM_H */
 
 /* --------------------------------------------------------------------- */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file v2.0.34/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.0.34/linux/include/linux/blk.h	Wed Oct 15 15:22:13 1997
+++ linux/include/linux/blk.h	Mon Jul 13 13:47:38 1998
@@ -3,7 +3,9 @@
 
 #include <linux/blkdev.h>
 #include <linux/locks.h>
+#include <linux/malloc.h>
 #include <linux/config.h>
+#include <linux/md.h>
 
 /*
  * NR_REQUEST is the number of entries in the request-queue.
@@ -400,8 +402,24 @@
 	if ((bh = req->bh) != NULL) {
 		req->bh = bh->b_reqnext;
 		bh->b_reqnext = NULL;
-		mark_buffer_uptodate(bh, uptodate);
-		unlock_buffer(bh);
+ 
+ 		/*
+ 		 * This is our 'MD IO has finished' event handler.
+ 		 * note that b_state should be cached in a register
+ 		 * anyways, so the overhead if this checking is almost 
+ 		 * zero. But anyways .. we never get OO for free :)
+ 		 */
+ 		if (test_bit(BH_MD, &bh->b_state)) {
+ 			struct md_personality * pers=(struct md_personality *)bh->personality;
+ 			pers->end_request(bh,uptodate);
+ 		}
+ 		/*
+ 		 * the normal (nonmirrored and no RAID5) case:
+ 		 */
+ 		else {
+ 			mark_buffer_uptodate(bh, uptodate);
+ 			unlock_buffer(bh);
+ 		}
 		if ((bh = req->bh) != NULL) {
 			req->current_nr_sectors = bh->b_size >> 9;
 			if (req->nr_sectors < req->current_nr_sectors) {
diff -u --recursive --new-file v2.0.34/linux/include/linux/blkdev.h linux/include/linux/blkdev.h
--- v2.0.34/linux/include/linux/blkdev.h	Wed Oct 15 15:22:05 1997
+++ linux/include/linux/blkdev.h	Mon Jul 13 13:47:38 1998
@@ -50,9 +50,12 @@
 extern struct wait_queue * wait_for_request;
 extern void resetup_one_dev(struct gendisk *dev, int drive);
 extern void unplug_device(void * data);
+extern void make_request(int major,int rw, struct buffer_head * bh);
 
 /* md needs this function to remap requests */
 extern int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size);
+extern int md_make_request (int minor, int rw, struct buffer_head * bh);
+extern int md_error (kdev_t mddev, kdev_t rdev);
 
 extern int * blk_size[MAX_BLKDEV];
 
diff -u --recursive --new-file v2.0.34/linux/include/linux/bpqether.h linux/include/linux/bpqether.h
--- v2.0.34/linux/include/linux/bpqether.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/bpqether.h	Mon Jul 13 13:47:38 1998
@@ -0,0 +1,41 @@
+#ifndef	__BPQETHER_H
+#define	__BPQETHER_H
+
+/*
+ * 	Defines for the BPQETHER pseudo device driver
+ */
+
+#ifndef __LINUX_IF_ETHER_H
+#include <linux/if_ether.h>
+#endif
+
+#define SIOCSBPQETHOPT		(SIOCDEVPRIVATE+0)	/* reserved */
+#define SIOCSBPQETHADDR		(SIOCDEVPRIVATE+1)
+ 
+struct bpq_ethaddr {
+	unsigned char destination[ETH_ALEN];
+	unsigned char accept[ETH_ALEN];
+};
+
+/* 
+ * For SIOCSBPQETHOPT - this is compatible with PI2/PacketTwin card drivers,
+ * currently not implemented, though. If someone wants to hook a radio
+ * to his ethernet card he may find this useful... ;-)
+ */
+
+#define SIOCGBPQETHPARAM	0x5000  /* get Level 1 parameters */
+#define SIOCSBPQETHPARAM	0x5001  /* set */
+
+struct bpq_req  {
+    int cmd;
+    int speed;			/* unused */
+    int clockmode;		/* unused */
+    int txdelay;
+    unsigned char persist;	/* unused */
+    int slotime;		/* unused */
+    int squeldelay;
+    int dmachan;		/* unused */
+    int irq;			/* unused */
+};
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/cd1400.h linux/include/linux/cd1400.h
--- v2.0.34/linux/include/linux/cd1400.h	Mon Mar 11 01:20:33 1996
+++ linux/include/linux/cd1400.h	Mon Jul 13 13:47:39 1998
@@ -3,6 +3,7 @@
 /*
  *	cd1400.h  -- cd1400 UART hardware info.
  *
+ *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.0.34/linux/include/linux/cdk.h linux/include/linux/cdk.h
--- v2.0.34/linux/include/linux/cdk.h	Thu Apr 11 23:49:46 1996
+++ linux/include/linux/cdk.h	Mon Jul 13 13:47:39 1998
@@ -3,6 +3,7 @@
 /*
  *	cdk.h  -- CDK interface definitions.
  *
+ *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
@@ -302,6 +303,10 @@
 #define	P_LOOPBACK	0x10
 #define	P_DTRFOLLOW	0x20
 #define	P_FAKEDCD	0x40
+
+#define	P_RXIMIN	0x10000
+#define	P_RXITIME	0x20000
+#define	P_RXTHOLD	0x40000
 
 /*
  *	Define a structure to communicate serial port signal and data state
diff -u --recursive --new-file v2.0.34/linux/include/linux/comstats.h linux/include/linux/comstats.h
--- v2.0.34/linux/include/linux/comstats.h	Sat Apr 20 01:25:31 1996
+++ linux/include/linux/comstats.h	Mon Jul 13 13:47:39 1998
@@ -3,6 +3,7 @@
 /*
  *	comstats.h  -- Serial Port Stats.
  *
+ *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.0.34/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.0.34/linux/include/linux/fs.h	Mon Jul 13 13:46:41 1998
+++ linux/include/linux/fs.h	Mon Jul 13 13:47:39 1998
@@ -141,14 +141,15 @@
 typedef char buffer_block[BLOCK_SIZE];
 
 /* bh state bits */
-#define BH_Uptodate	0	/* 1 if the buffer contains valid data */
-#define BH_Dirty	1	/* 1 if the buffer is dirty */
-#define BH_Lock		2	/* 1 if the buffer is locked */
-#define BH_Req		3	/* 0 if the buffer has been invalidated */
-#define BH_Touched	4	/* 1 if the buffer has been touched (aging) */
-#define BH_Has_aged	5	/* 1 if the buffer has been aged (aging) */
-#define BH_Protected	6	/* 1 if the buffer is protected */
-#define BH_FreeOnIO	7	/* 1 to discard the buffer_head after IO */
+#define BH_Uptodate		0	/* 1 if the buffer contains valid data */
+#define BH_Dirty		1	/* 1 if the buffer is dirty */
+#define BH_Lock			2	/* 1 if the buffer is locked */
+#define BH_Req			3	/* 0 if the buffer has been invalidated */
+#define BH_Touched		4	/* 1 if the buffer has been touched (aging) */
+#define BH_Has_aged		5	/* 1 if the buffer has been aged (aging) */
+#define BH_Protected		6	/* 1 if the buffer is protected */
+#define BH_FreeOnIO		7	/* 1 to discard the buffer_head after IO */
+#define BH_MD			8	/* 1 if the buffer is an MD request */
 
 /*
  * Try to keep the most commonly used fields in single cache lines (16
@@ -188,6 +189,13 @@
 	struct buffer_head * b_prev;		/* doubly linked list of hash-queue */
 	struct buffer_head * b_prev_free;	/* doubly linked list of buffers */
 	struct buffer_head * b_reqnext;		/* request queue */
+
+/*
+ * Some MD stuff like RAID5 needs special event handlers and
+ * special private buffer_head fields:
+ */
+	void * personality;
+	void * private_bh;
 };
 
 static inline int buffer_uptodate(struct buffer_head * bh)
diff -u --recursive --new-file v2.0.34/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h
--- v2.0.34/linux/include/linux/hdlcdrv.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/hdlcdrv.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,378 @@
+/*
+ * hdlcdrv.h  -- HDLC packet radio network driver.
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _HDLCDRV_H
+#define _HDLCDRV_H
+
+#include <linux/version.h>
+#include <linux/sockios.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 0x20119
+#include <linux/if_ether.h>
+#endif
+#include <linux/netdevice.h>
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct hdlcdrv_params {
+	int iobase;
+	int irq;
+	int dma;
+	int dma2;
+	int seriobase;
+	int pariobase;
+	int midiiobase;
+};	
+
+struct hdlcdrv_channel_params {
+	int tx_delay;  /* the transmitter keyup delay in 10ms units */
+	int tx_tail;   /* the transmitter keyoff delay in 10ms units */
+	int slottime;  /* the slottime in 10ms; usually 10 = 100ms */
+	int ppersist;  /* the p-persistence 0..255 */
+	int fulldup;   /* some driver do not support full duplex, setting */
+	               /* this just makes them send even if DCD is on */
+};	
+
+struct hdlcdrv_old_channel_state {
+  	int ptt;
+  	int dcd;
+  	int ptt_keyed;
+#if LINUX_VERSION_CODE < 0x20100
+  	struct enet_statistics stats;
+#endif
+};
+
+struct hdlcdrv_channel_state {
+ 	int ptt;
+ 	int dcd;
+ 	int ptt_keyed;
+ 	unsigned long tx_packets;
+ 	unsigned long tx_errors;
+ 	unsigned long rx_packets;
+ 	unsigned long rx_errors;
+};
+
+struct hdlcdrv_ioctl {
+	int cmd;
+	union {
+		struct hdlcdrv_params mp;
+		struct hdlcdrv_channel_params cp;
+		struct hdlcdrv_channel_state cs;
+		struct hdlcdrv_old_channel_state ocs;
+		unsigned int calibrate;
+		unsigned char bits;
+		char modename[128];
+		char drivername[32];
+	} data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * ioctl values
+ */
+#define HDLCDRVCTL_GETMODEMPAR       0
+#define HDLCDRVCTL_SETMODEMPAR       1
+#define HDLCDRVCTL_MODEMPARMASK      2  /* not handled by hdlcdrv */
+#define HDLCDRVCTL_GETCHANNELPAR    10
+#define HDLCDRVCTL_SETCHANNELPAR    11
+#define HDLCDRVCTL_OLDGETSTAT       20
+#define HDLCDRVCTL_CALIBRATE        21
+#define HDLCDRVCTL_GETSTAT          22
+
+/*
+ * these are mainly for debugging purposes
+ */
+#define HDLCDRVCTL_GETSAMPLES       30
+#define HDLCDRVCTL_GETBITS          31
+
+/*
+ * not handled by hdlcdrv, but by its depending drivers
+ */
+#define HDLCDRVCTL_GETMODE          40
+#define HDLCDRVCTL_SETMODE          41
+#define HDLCDRVCTL_MODELIST         42
+#define HDLCDRVCTL_DRIVERNAME       43
+
+/*
+ * mask of needed modem parameters, returned by HDLCDRVCTL_MODEMPARMASK
+ */
+#define HDLCDRV_PARMASK_IOBASE      (1<<0)
+#define HDLCDRV_PARMASK_IRQ         (1<<1)
+#define HDLCDRV_PARMASK_DMA         (1<<2)
+#define HDLCDRV_PARMASK_DMA2        (1<<3)
+#define HDLCDRV_PARMASK_SERIOBASE   (1<<4)
+#define HDLCDRV_PARMASK_PARIOBASE   (1<<5)
+#define HDLCDRV_PARMASK_MIDIIOBASE  (1<<6)
+
+/* -------------------------------------------------------------------- */
+
+#ifdef __KERNEL__
+
+#define HDLCDRV_MAGIC      0x5ac6e778
+#define HDLCDRV_IFNAMELEN    6
+#define HDLCDRV_HDLCBUFFER  32 /* should be a power of 2 for speed reasons */
+#define HDLCDRV_BITBUFFER  256 /* should be a power of 2 for speed reasons */
+#undef HDLCDRV_LOOPBACK  /* define for HDLC debugging purposes */
+#define HDLCDRV_DEBUG
+
+/* maximum packet length, excluding CRC */
+#define HDLCDRV_MAXFLEN             400	
+
+
+struct hdlcdrv_hdlcbuffer {
+	unsigned rd, wr;
+	unsigned short buf[HDLCDRV_HDLCBUFFER];
+};
+
+#ifdef HDLCDRV_DEBUG
+struct hdlcdrv_bitbuffer {
+	unsigned int rd;
+	unsigned int wr;
+	unsigned int shreg;
+	unsigned char buffer[HDLCDRV_BITBUFFER];
+};
+
+extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf, 
+					 unsigned int bit)
+{
+	unsigned char new;
+
+	new = buf->shreg & 1;
+	buf->shreg >>= 1;
+	buf->shreg |= (!!bit) << 7;
+	if (new) {
+		buf->buffer[buf->wr] = buf->shreg;
+		buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+		buf->shreg = 0x80;
+	}
+}
+
+extern inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf, 
+					      unsigned int bits)
+{
+	buf->buffer[buf->wr] = bits & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+	buf->buffer[buf->wr] = (bits >> 8) & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+
+}
+#endif /* HDLCDRV_DEBUG */
+
+/* -------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each driver. 
+ */
+
+struct hdlcdrv_ops {
+	/*
+	 * first some informations needed by the hdlcdrv routines
+	 */
+	const char *drvname;
+	const char *drvinfo;
+	/*
+	 * the routines called by the hdlcdrv routines
+	 */
+	int (*open)(struct device *);
+	int (*close)(struct device *);
+	int (*ioctl)(struct device *, struct ifreq *, 
+		     struct hdlcdrv_ioctl *, int);
+};
+
+struct hdlcdrv_state {
+	int magic;
+
+	char ifname[HDLCDRV_IFNAMELEN];
+
+	const struct hdlcdrv_ops *ops;
+
+	struct {
+		int bitrate;
+	} par;
+
+	struct hdlcdrv_pttoutput {
+		int dma2;
+		int seriobase;
+		int pariobase;
+		int midiiobase;
+		unsigned int flags;
+	} ptt_out;
+
+	struct hdlcdrv_channel_params ch_params;
+
+	struct hdlcdrv_hdlcrx {
+		struct hdlcdrv_hdlcbuffer hbuf;
+		int in_hdlc_rx;
+		/* 0 = sync hunt, != 0 receiving */
+		int rx_state;	
+		unsigned int bitstream;
+		unsigned int bitbuf;
+		int numbits;
+		unsigned char dcd;
+		
+		int len;
+		unsigned char *bp;
+		unsigned char buffer[HDLCDRV_MAXFLEN+2];
+	} hdlcrx;
+
+	struct hdlcdrv_hdlctx {
+		struct hdlcdrv_hdlcbuffer hbuf;
+		int in_hdlc_tx;
+		/*
+		 * 0 = send flags
+		 * 1 = send txtail (flags)
+		 * 2 = send packet
+		 */
+		int tx_state;	
+		int numflags;
+		unsigned int bitstream;
+		unsigned char ptt;
+		int calibrate;
+		int slotcnt;
+
+		unsigned int bitbuf;
+		int numbits;
+		
+		int len;
+		unsigned char *bp;
+		unsigned char buffer[HDLCDRV_MAXFLEN+2];
+	} hdlctx;
+
+#ifdef HDLCDRV_DEBUG
+	struct hdlcdrv_bitbuffer bitbuf_channel;
+	struct hdlcdrv_bitbuffer bitbuf_hdlc;
+#endif /* HDLCDRV_DEBUG */
+
+#if LINUX_VERSION_CODE < 0x20119
+	struct enet_statistics stats;
+#else
+	struct net_device_stats stats;
+#endif
+	int ptt_keyed;
+
+	struct sk_buff_head send_queue;  /* Packets awaiting transmission */
+};
+
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) 
+{
+	return !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) 
+		 % HDLCDRV_HDLCBUFFER);
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
+{
+	return hb->rd == hb->wr;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
+{
+	unsigned newr;
+	unsigned short val;
+	unsigned long flags;
+
+	if (hb->rd == hb->wr)
+		return 0;
+	save_flags(flags);
+	cli();
+	newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER;
+	val = hb->buf[hb->rd];
+	hb->rd = newr;
+	restore_flags(flags);
+	return val;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb, 
+				    unsigned short val)
+{
+	unsigned newp;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER;
+	if (newp != hb->rd) { 
+		hb->buf[hb->wr] = val & 0xffff;
+		hb->wr = newp;
+	}
+	restore_flags(flags);
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
+{
+	hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits);
+}
+
+extern inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
+{
+	unsigned int ret;
+
+	if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) {
+		if (s->hdlctx.calibrate > 0)
+			s->hdlctx.calibrate--;
+		else
+			s->hdlctx.ptt = 0;
+		ret = 0;
+	} else 
+		ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf);
+#ifdef HDLCDRV_LOOPBACK
+	hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret);
+#endif /* HDLCDRV_LOOPBACK */
+	return ret;
+}
+
+extern inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
+{
+#ifdef HDLCDRV_DEBUG
+	hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit);
+#endif /* HDLCDRV_DEBUG */
+}
+
+extern inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
+{
+	s->hdlcrx.dcd = !!dcd;
+}
+
+extern inline int hdlcdrv_ptt(struct hdlcdrv_state *s)
+{
+	return s->hdlctx.ptt || (s->hdlctx.calibrate > 0);
+}
+
+/* -------------------------------------------------------------------- */
+
+void hdlcdrv_receiver(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_transmitter(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_arbitrate(struct device *, struct hdlcdrv_state *);
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+			     unsigned int privsize, char *ifname,
+			     unsigned int baseaddr, unsigned int irq, 
+			     unsigned int dma);
+int hdlcdrv_unregister_hdlcdrv(struct device *dev);
+
+/* -------------------------------------------------------------------- */
+
+
+
+#endif /* __KERNEL__ */
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _HDLCDRV_H */
+
+/* -------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v2.0.34/linux/include/linux/if_arp.h	Wed Oct 15 15:23:06 1997
+++ linux/include/linux/if_arp.h	Mon Jul 13 13:47:39 1998
@@ -45,6 +45,7 @@
 #define ARPHRD_CSLIP6	259
 #define ARPHRD_RSRVD	260		/* Notional KISS type 		*/
 #define ARPHRD_ADAPT	264
+#define	ARPHRD_ROSE	270
 #define ARPHRD_PPP	512
 
 #define ARPHRD_TUNNEL	768		/* IPIP tunnel			*/
diff -u --recursive --new-file v2.0.34/linux/include/linux/istallion.h linux/include/linux/istallion.h
--- v2.0.34/linux/include/linux/istallion.h	Wed Apr 24 02:48:04 1996
+++ linux/include/linux/istallion.h	Mon Jul 13 13:47:39 1998
@@ -3,6 +3,7 @@
 /*
  *	istallion.h  -- stallion intelligent multiport serial driver.
  *
+ *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
@@ -101,6 +102,7 @@
 	int		nrports;
 	int		nrdevs;
 	unsigned int	iobase;
+	int		iosize;
 	unsigned long	memaddr;
 	void		*membase;
 	int		memsize;
diff -u --recursive --new-file v2.0.34/linux/include/linux/major.h linux/include/linux/major.h
--- v2.0.34/linux/include/linux/major.h	Tue Aug 12 13:57:23 1997
+++ linux/include/linux/major.h	Mon Jul 13 13:47:39 1998
@@ -66,6 +66,7 @@
 #define Z2RAM_MAJOR	37
 #define RISCOM8_NORMAL_MAJOR 48
 #define RISCOM8_CALLOUT_MAJOR 49
+#define MKISS_MAJOR	55
 #define APBLOCK_MAJOR   60   /* AP1000 Block device */
 #define DDV_MAJOR       61   /* AP1000 DDV block device */
 
diff -u --recursive --new-file v2.0.34/linux/include/linux/md.h linux/include/linux/md.h
--- v2.0.34/linux/include/linux/md.h	Wed Oct 15 15:22:13 1997
+++ linux/include/linux/md.h	Mon Jul 13 13:47:39 1998
@@ -1,4 +1,3 @@
-
 /*
    md.h : Multiple Devices driver for Linux
           Copyright (C) 1994-96 Marc ZYNGIER
@@ -20,10 +19,17 @@
 
 #include <asm/segment.h>
 #include <linux/major.h>
-#include <linux/mm.h>
 #include <linux/ioctl.h>
+#include <linux/types.h>
 
-#define MD_VERSION "0.35"
+/*
+ * Different major versions are not compatible.
+ * Different minor versions are only downward compatible.
+ * Different patchlevel versions are downward and upward compatible.
+ */
+#define MD_MAJOR_VERSION		0
+#define MD_MINOR_VERSION		36
+#define MD_PATCHLEVEL_VERSION		3
 
 /* ioctls */
 #define REGISTER_DEV _IO (MD_MAJOR, 1)
@@ -43,9 +49,9 @@
 #define FAULT_SHIFT       8
 #define PERSONALITY_SHIFT 16
 
-#define FACTOR_MASK       0xFFUL
-#define FAULT_MASK        0xFF00UL
-#define PERSONALITY_MASK  0xFF0000UL
+#define FACTOR_MASK       0x000000FFUL
+#define FAULT_MASK        0x0000FF00UL
+#define PERSONALITY_MASK  0x00FF0000UL
 
 #define MD_RESERVED       0	/* Not used by now */
 #define LINEAR            (1UL << PERSONALITY_SHIFT)
@@ -55,14 +61,147 @@
 #define RAID5             (4UL << PERSONALITY_SHIFT)
 #define MAX_PERSONALITY   5
 
+/*
+ * MD superblock.
+ *
+ * The MD superblock maintains some statistics on each MD configuration.
+ * Each real device in the MD set contains it near the end of the device.
+ * Some of the ideas are copied from the ext2fs implementation.
+ *
+ * We currently use 4096 bytes as follows:
+ *
+ *	word offset	function
+ *
+ *	   0  -    31	Constant generic MD device information.
+ *        32  -    63   Generic state information.
+ *	  64  -   127	Personality specific information.
+ *	 128  -   511	12 32-words descriptors of the disks in the raid set.
+ *	 512  -   911	Reserved.
+ *	 912  -  1023	Disk specific descriptor.
+ */
+
+/*
+ * If x is the real device size in bytes, we return an apparent size of:
+ *
+ *	y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES
+ *
+ * and place the 4kB superblock at offset y.
+ */
+#define MD_RESERVED_BYTES		(64 * 1024)
+#define MD_RESERVED_SECTORS		(MD_RESERVED_BYTES / 512)
+#define MD_RESERVED_BLOCKS		(MD_RESERVED_BYTES / BLOCK_SIZE)
+
+#define MD_NEW_SIZE_SECTORS(x)		((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS)
+#define MD_NEW_SIZE_BLOCKS(x)		((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS)
+
+#define MD_SB_BYTES			4096
+#define MD_SB_WORDS			(MD_SB_BYTES / 4)
+#define MD_SB_BLOCKS			(MD_SB_BYTES / BLOCK_SIZE)
+#define MD_SB_SECTORS			(MD_SB_BYTES / 512)
+
+/*
+ * The following are counted in 32-bit words
+ */
+#define	MD_SB_GENERIC_OFFSET		0
+#define MD_SB_PERSONALITY_OFFSET	64
+#define MD_SB_DISKS_OFFSET		128
+#define MD_SB_DESCRIPTOR_OFFSET		992
+
+#define MD_SB_GENERIC_CONSTANT_WORDS	32
+#define MD_SB_GENERIC_STATE_WORDS	32
+#define MD_SB_GENERIC_WORDS		(MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS)
+#define MD_SB_PERSONALITY_WORDS		64
+#define MD_SB_DISKS_WORDS		384
+#define MD_SB_DESCRIPTOR_WORDS		32
+#define MD_SB_RESERVED_WORDS		(1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS)
+#define MD_SB_EQUAL_WORDS		(MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS)
+#define MD_SB_DISKS			(MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS)
+
+/*
+ * Device "operational" state bits
+ */
+#define MD_FAULTY_DEVICE		0	/* Device is faulty / operational */
+#define MD_ACTIVE_DEVICE		1	/* Device is a part or the raid set / spare disk */
+#define MD_SYNC_DEVICE			2	/* Device is in sync with the raid set */
+
+typedef struct md_device_descriptor_s {
+	__u32 number;		/* 0 Device number in the entire set */
+	__u32 major;		/* 1 Device major number */
+	__u32 minor;		/* 2 Device minor number */
+	__u32 raid_disk;	/* 3 The role of the device in the raid set */
+	__u32 state;		/* 4 Operational state */
+	__u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5];
+} md_descriptor_t;
+
+#define MD_SB_MAGIC		0xa92b4efc
+
+/*
+ * Superblock state bits
+ */
+#define MD_SB_CLEAN		0
+#define MD_SB_ERRORS		1
+
+typedef struct md_superblock_s {
+
+	/*
+	 * Constant generic information
+	 */
+	__u32 md_magic;		/*  0 MD identifier */
+	__u32 major_version;	/*  1 major version to which the set conforms */
+	__u32 minor_version;	/*  2 minor version to which the set conforms */
+	__u32 patch_version;	/*  3 patchlevel version to which the set conforms */
+	__u32 gvalid_words;	/*  4 Number of non-reserved words in this section */
+	__u32 set_magic;	/*  5 Raid set identifier */
+	__u32 ctime;		/*  6 Creation time */
+	__u32 level;		/*  7 Raid personality (mirroring, raid5, ...) */
+	__u32 size;		/*  8 Apparent size of each individual disk, in kB */
+	__u32 nr_disks;		/*  9 Number of total disks in the raid set */
+	__u32 raid_disks;	/* 10 Number of disks in a fully functional raid set */
+	__u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 11];
+
+	/*
+	 * Generic state information
+	 */
+	__u32 utime;		/*  0 Superblock update time */
+	__u32 state;		/*  1 State bits (clean, ...) */
+	__u32 active_disks;	/*  2 Number of currently active disks (some non-faulty disks might not be in sync) */
+	__u32 working_disks;	/*  3 Number of working disks */
+	__u32 failed_disks;	/*  4 Number of failed disks */
+	__u32 spare_disks;	/*  5 Number of spare disks */
+	__u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 6];
+
+	/*
+	 * Personality information
+	 */
+	__u32 parity_algorithm;
+	__u32 chunk_size;
+	__u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 2];
+
+	/*
+	 * Disks information
+	 */
+	md_descriptor_t disks[MD_SB_DISKS];
+
+	/*
+	 * Reserved
+	 */
+	__u32 reserved[MD_SB_RESERVED_WORDS];
+
+	/*
+	 * Active descriptor
+	 */
+	md_descriptor_t descriptor;
+} md_superblock_t;
+
 #ifdef __KERNEL__
 
-#include <linux/types.h>
+#include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/blkdev.h>
 
 #define MAX_REAL     8		/* Max number of physical dev per md dev */
 #define MAX_MD_DEV   4		/* Max number of md dev */
+#define MAX_MD_THREADS 2	/* Max number of kernel threads */
 
 #define FACTOR(a)         ((a)->repartition & FACTOR_MASK)
 #define MAX_FAULT(a)      (((a)->repartition & FAULT_MASK)>>8)
@@ -77,6 +216,8 @@
   int offset;			/* Real device offset (in blocks) in md dev
 				   (only used in linear mode) */
   struct inode *inode;		/* Lock inode */
+  md_superblock_t *sb;
+  u32 sb_offset;
 };
 
 struct md_dev;
@@ -85,25 +226,39 @@
 {
   char *name;
   int (*map)(struct md_dev *md_dev, kdev_t *rdev,
-	     unsigned long *rsector, unsigned long size);
+	              unsigned long *rsector, unsigned long size);
+  int (*make_request)(struct md_dev *md_dev, int rw, struct buffer_head * bh);
+  void (*end_request)(struct buffer_head * bh, int uptodate);
   int (*run)(int minor, struct md_dev *md_dev);
   int (*stop)(int minor, struct md_dev *md_dev);
   int (*status)(char *page, int minor, struct md_dev *md_dev);
   int (*ioctl)(struct inode *inode, struct file *file,
 	       unsigned int cmd, unsigned long arg);
   int max_invalid_dev;
+  int (*error_handler)(struct md_dev *md_dev, kdev_t dev);
 };
 
 struct md_dev
 {
   struct real_dev devices[MAX_REAL];
   struct md_personality *pers;
+  md_superblock_t *sb;
+  int sb_dirty;
   int repartition;
   int busy;
   int nb_dev;
   void *private;
 };
 
+struct md_thread {
+	void			(*run) (void *data);
+	void			*data;
+	struct wait_queue	*wqueue;
+	__u32			flags;
+};
+
+#define THREAD_WAKEUP	0
+
 extern struct md_dev md_dev[MAX_MD_DEV];
 extern int md_size[MAX_MD_DEV];
 
@@ -111,6 +266,10 @@
 
 extern int register_md_personality (int p_num, struct md_personality *p);
 extern int unregister_md_personality (int p_num);
+extern struct md_thread *md_register_thread (void (*run) (void *data), void *data);
+extern void md_unregister_thread (struct md_thread *thread);
+extern void md_wakeup_thread(struct md_thread *thread);
+extern int md_update_sb (int minor);
 
 #endif __KERNEL__
 #endif _MD_H
diff -u --recursive --new-file v2.0.34/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.0.34/linux/include/linux/netdevice.h	Mon Jul 13 13:46:42 1998
+++ linux/include/linux/netdevice.h	Mon Jul 13 13:47:39 1998
@@ -34,6 +34,7 @@
 #define DEV_NUMBUFFS	3
 #define MAX_ADDR_LEN	7
 #ifndef CONFIG_AX25
+#ifndef CONFIG_AX25_MODULE
 #ifndef CONFIG_TR
 #if !defined(CONFIG_NET_IPIP) && !defined(CONFIG_NET_IPIP_MODULE)
 #define MAX_HEADER	32		/* We really need about 18 worst case .. so 32 is aligned */
@@ -44,8 +45,11 @@
 #define MAX_HEADER	48		/* Token Ring header needs 40 bytes ... 48 is aligned */ 
 #endif /* TR */
 #else
-#define MAX_HEADER	96		/* AX.25 + NetROM */
-#endif /* AX25 */
+#define MAX_HEADER	96		/* AX.25 + NET/ROM module*/
+#endif /* AX.25 module */
+#else
+#define MAX_HEADER	96		/* AX.25 + NET/ROM */
+#endif /* AX.25 */
 
 #define IS_MYADDR	1		/* address is (one of) our own	*/
 #define IS_LOOPBACK	2		/* address is for LOOPBACK	*/
diff -u --recursive --new-file v2.0.34/linux/include/linux/netrom.h linux/include/linux/netrom.h
--- v2.0.34/linux/include/linux/netrom.h	Tue Mar 10 13:19:09 1998
+++ linux/include/linux/netrom.h	Mon Jul 13 13:47:39 1998
@@ -3,7 +3,7 @@
  * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the
  * definition of the ax25_address structure.
  */
- 
+
 #ifndef	NETROM_KERNEL_H
 #define	NETROM_KERNEL_H
 
@@ -12,46 +12,23 @@
 #define NETROM_T1	1
 #define NETROM_T2	2
 #define NETROM_N2	3
-#define	NETROM_HDRINCL	4
-#define	NETROM_PACLEN	5
-
-#define	NETROM_KILL	99
+#define	NETROM_T4	6
+#define	NETROM_IDLE	7
 
-#define	SIOCNRGETPARMS		(SIOCPROTOPRIVATE+0)
-#define	SIOCNRSETPARMS		(SIOCPROTOPRIVATE+1)
 #define	SIOCNRDECOBS		(SIOCPROTOPRIVATE+2)
-#define	SIOCNRRTCTL		(SIOCPROTOPRIVATE+3)
-#define	SIOCNRCTLCON		(SIOCPROTOPRIVATE+4)
 
 struct nr_route_struct {
 #define	NETROM_NEIGH	0
 #define	NETROM_NODE	1
-	int type;
-	ax25_address callsign;
-	char device[16];
-	unsigned int quality;
-	char mnemonic[7];
-	ax25_address neighbour;
-	unsigned int obs_count;
-};
-
-struct nr_parms_struct {
-	unsigned int quality;
-	unsigned int obs_count;
-	unsigned int ttl;
-	unsigned int timeout;
-	unsigned int ack_delay;
-	unsigned int busy_delay;
-	unsigned int tries;
-	unsigned int window;
-	unsigned int paclen;
-};
-
-struct nr_ctl_struct {
-	unsigned char index;
-	unsigned char id;
-	unsigned int  cmd;
-	unsigned long arg;
+	int		type;
+	ax25_address	callsign;
+	char		device[16];
+	unsigned int	quality;
+	char		mnemonic[7];
+	ax25_address	neighbour;
+	unsigned int	obs_count;
+	unsigned int	ndigis;
+	ax25_address	digipeaters[AX25_MAX_DIGIS];
 };
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/pg.h linux/include/linux/pg.h
--- v2.0.34/linux/include/linux/pg.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/pg.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,63 @@
+/* 	pg.h (c) 1998  Grant R. Guenther <grant@torque.net>
+ 		       Under the terms of the GNU public license
+
+
+	pg.h defines the user interface to the generic ATAPI packet
+        command driver for parallel port ATAPI devices (pg). The
+	driver is loosely modelled after the generic SCSI driver, sg,
+	although the actual interface is different.
+
+	The pg driver provides a simple character device interface for
+        sending ATAPI commands to a device.  With the exception of the
+	ATAPI reset operation, all operations are performed by a pair
+        of read and write operations to the appropriate /dev/pgN device.
+	A write operation delivers a command and any outbound data in
+        a single buffer.  Normally, the write will succeed unless the
+        device is offline or malfunctioning, or there is already another
+	command pending.  If the write succeeds, it should be followed
+        immediately by a read operation, to obtain any returned data and
+        status information.  A read will fail if there is no operation
+        in progress.
+
+	As a special case, the device can be reset with a write operation,
+        and in this case, no following read is expected, or permitted.
+
+	There are no ioctl() operations.  Any single operation
+	may transfer at most PG_MAX_DATA bytes.  Note that the driver must
+        copy the data through an internal buffer.  In keeping with all
+	current ATAPI devices, command packets are assumed to be exactly
+	12 bytes in length.
+
+	To permit future changes to this interface, the headers in the
+	read and write buffers contain a single character "magic" flag.
+        Currently this flag must be the character "P".
+
+*/
+
+#define PG_MAGIC	'P'
+#define PG_RESET	'Z'
+#define PG_COMMAND	'C'
+
+#define PG_MAX_DATA	32768
+
+struct pg_write_hdr {
+
+	char	magic;		/* == PG_MAGIC */
+	char	func;		/* PG_RESET or PG_COMMAND */
+	int     dlen;		/* number of bytes expected to transfer */
+	int     timeout;	/* number of seconds before timeout */
+	char	packet[12];	/* packet command */
+
+};
+
+struct pg_read_hdr {
+
+	char	magic;		/* == PG_MAGIC */
+	char	scsi;		/* "scsi" status == sense key */
+	int	dlen;		/* size of device transfer request */
+	int     duration;	/* time in seconds command took */
+	char    pad[12];	/* not used */
+
+};
+
+/* end of pg.h */
diff -u --recursive --new-file v2.0.34/linux/include/linux/pi2.h linux/include/linux/pi2.h
--- v2.0.34/linux/include/linux/pi2.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/pi2.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,133 @@
+
+#define DMA_BUFF_SIZE 2200
+
+/* Network statistics, with the same names as 'struct enet_statistics'. */
+#define netstats enet_statistics
+
+#define ON 1
+#define OFF 0
+
+
+/* Register offset info, specific to the PI
+ * E.g., to read the data port on channel A, use
+ *  inportb(pichan[dev].base + CHANA + DATA)
+ */
+#define CHANB   0   /* Base of channel B regs */
+#define CHANA   2   /* Base of channel A regs */
+
+/* 8530 ports on each channel */
+#define CTL 0
+#define DATA    1
+
+#define DMAEN   0x4 /* Offset off DMA Enable register */
+
+/* Timer chip offsets */
+#define TMR0    0x8 /* Offset of timer 0 register */
+#define TMR1    0x9 /* Offset of timer 1 register */
+#define TMR2    0xA /* Offset of timer 2 register */
+#define TMRCMD  0xB /* Offset of timer command register */
+
+/* Timer chip equates */
+#define SC0 0x00 /* Select counter 0 */
+#define SC1 0x40 /* Select counter 1 */
+#define SC2 0x80 /* Select counter 2 */
+#define CLATCH  0x00 /* Counter latching operation */
+#define MSB 0x20 /* Read/load MSB only */
+#define LSB 0x10 /* Read/load LSB only */
+#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
+#define MODE0   0x00 /* Interrupt on terminal count */
+#define MODE1   0x02 /* Programmable one shot */
+#define MODE2   0x04 /* Rate generator */
+#define MODE3   0x06 /* Square wave rate generator */
+#define MODE4   0x08 /* Software triggered strobe */
+#define MODE5   0x0a /* Hardware triggered strobe */
+#define BCD 0x01 /* BCD counter */
+
+/* DMA controller registers */
+#define DMA_STAT    8   /* DMA controller status register */
+#define DMA_CMD     8   /* DMA controller command register */
+#define DMA_MASK        10  /* DMA controller mask register */
+#define DMA_MODE        11  /* DMA controller mode register */
+#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
+/* DMA data */
+#define DMA_DISABLE (0x04)  /* Disable channel n */
+#define DMA_ENABLE  (0x00)  /* Enable channel n */
+/* Single transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE (0x54)
+/* Single transfers, incr. address, no auto init, reads, ch. n */
+#define DMA_TX_MODE (0x48)
+
+#define SINGLE 3686400
+#define DOUBLE 7372800
+
+#define SIOCGPIPARAM		0x5000	/* get PI parameters */
+#define SIOCSPIPARAM		0x5001	/* set */
+#define SIOCGPIBAUD		0x5002	/* get only baud rate */
+#define SIOCSPIBAUD		0x5003	
+#define SIOCGPIDMA		0x5004	/* get only DMA */
+#define SIOCSPIDMA		0x5005	
+#define SIOCGPIIRQ		0x5006	/* get only IRQ */
+#define SIOCSPIIRQ		0x5007	
+
+struct pi_req  {
+    int cmd;
+    int speed;
+    int clockmode;
+    int txdelay;
+    unsigned char persist;
+    int slotime; 
+    int squeldelay;
+    int dmachan;    
+    int irq;    
+};
+
+#ifdef __KERNEL__
+
+/* Information that needs to be kept for each channel. */
+struct pi_local {
+    struct netstats stats; /* %%%dp*/
+    long open_time;             /* Useless example local info. */
+    unsigned long xtal; 
+
+    struct mbuf *rcvbuf;/* Buffer for current rx packet */
+    struct mbuf *rxdmabuf1; /* DMA rx buffer */
+    struct mbuf *rxdmabuf2; /* DMA rx buffer */
+
+    int bufsiz;         /* Size of rcvbuf */
+    char *rcp;          /* Pointer into rcvbuf */
+
+    struct sk_buff_head sndq;  /* Packets awaiting transmission */
+    int sndcnt;         /* Number of packets on sndq */
+    struct sk_buff *sndbuf;	/* Current buffer being transmitted */
+    char *txdmabuf;     /* Transmit DMA buffer */
+	char *txptr;		/* Used by B port tx */
+	int txcnt;			
+    char tstate;        /* Transmitter state */
+#define IDLE    0       /* Transmitter off, no data pending */
+#define ACTIVE  1       /* Transmitter on, sending data */
+#define UNDERRUN 2      /* Transmitter on, flushing CRC */
+#define FLAGOUT 3       /* CRC sent - attempt to start next frame */
+#define DEFER 4         /* Receive Active - DEFER Transmit */
+#define ST_TXDELAY 5    /* Sending leading flags */
+#define CRCOUT 6
+    char rstate;        /* Set when !DCD goes to 0 (TRUE) */
+/* Normal state is ACTIVE if Receive enabled */
+#define RXERROR 2       /* Error -- Aborting current Frame */
+#define RXABORT 3       /* ABORT sequence detected */
+#define TOOBIG 4        /* too large a frame to store */
+    int dev;            /* Device number */
+    int base;       /* Base of I/O registers */
+    int cardbase;     /* Base address of card */
+    int stata;        /* address of Channel A status regs */
+    int statb;        /* address of Channel B status regs */
+    int speed;        /* Line speed, bps */
+    int clockmode;    /* tapr 9600 modem clocking option */
+    int txdelay;      /* Transmit Delay 10 ms/cnt */
+    unsigned char persist;       /* Persistence (0-255) as a % */
+    int slotime;      /* Delay to wait on persistence hit */
+    int squeldelay;   /* Delay after XMTR OFF for squelch tail */
+    struct iface *iface;    /* Associated interface */
+    int dmachan;           /* DMA channel for this port */
+};
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.0.34/linux/include/linux/proc_fs.h	Wed Oct 15 15:22:08 1997
+++ linux/include/linux/proc_fs.h	Mon Jul 13 13:47:39 1998
@@ -105,6 +105,10 @@
 	PROC_NET_STRIP_STATUS,
 	PROC_NET_STRIP_TRACE,
 	PROC_NET_IPAUTOFW,
+	PROC_NET_RS_NODES,
+	PROC_NET_RS_NEIGH,
+	PROC_NET_RS_ROUTES,
+	PROC_NET_RS,
 	PROC_NET_Z8530,
 	PROC_NET_LAST
 };
diff -u --recursive --new-file v2.0.34/linux/include/linux/pt.h linux/include/linux/pt.h
--- v2.0.34/linux/include/linux/pt.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/pt.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,176 @@
+/*
+ * pt.h: Linux device driver for the Gracilis PackeTwin
+ * Copyright (C) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
+ *
+ * Please read the notice appearing at the top of the file pt.c
+ */
+#define DMA_BUFF_SIZE 2200
+
+/* Network statistics, with the same names as 'struct enet_statistics'. */
+#define netstats enet_statistics
+
+#define ON 1
+#define OFF 0
+
+
+/* Register offset info, specific to the PT
+ * E.g., to read the data port on channel A, use
+ *  inportb(pichan[dev].base + CHANA + DATA)
+ */
+#define CHANB   0   /* Base of channel B regs */
+#define CHANA   2   /* Base of channel A regs */
+
+/* 8530 ports on each channel */
+#define CTL 0
+#define DATA    1
+
+#define DMAEN   0x8 /* Offset off DMA Enable register */
+
+/* Timer chip offsets */
+#define TMR0    0x4 /* Offset of timer 0 register */
+#define TMR1    0x5 /* Offset of timer 1 register */
+#define TMR2    0x6 /* Offset of timer 2 register */
+#define TMRCMD  0x7 /* Offset of timer command register */
+#define INT_REG	0x8
+#define TMR1CLR 0x9
+#define TMR2CLR 0xa
+
+/* Interrupt register equates */
+#define PT_SCC_MSK	0x1
+#define PT_TMR1_MSK	0x2
+#define PT_TMR2_MSK	0x4
+
+/* Serial/interrupt register equates */
+#define PT_DTRA_ON	0x1
+#define PT_DTRB_ON	0x2
+#define PT_EXTCLKA	0x4
+#define PT_EXTCLKB	0x8
+#define PT_LOOPA_ON	0x10
+#define PT_LOOPB_ON	0x20
+#define PT_EI		0x80
+
+/* Timer chip equates */
+#define SC0 0x00 /* Select counter 0 */
+#define SC1 0x40 /* Select counter 1 */
+#define SC2 0x80 /* Select counter 2 */
+#define CLATCH  0x00 /* Counter latching operation */
+#define MSB 0x20 /* Read/load MSB only */
+#define LSB 0x10 /* Read/load LSB only */
+#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
+#define MODE0   0x00 /* Interrupt on terminal count */
+#define MODE1   0x02 /* Programmable one shot */
+#define MODE2   0x04 /* Rate generator */
+#define MODE3   0x06 /* Square wave rate generator */
+#define MODE4   0x08 /* Software triggered strobe */
+#define MODE5   0x0a /* Hardware triggered strobe */
+#define BCD 0x01 /* BCD counter */
+
+/* DMA controller registers */
+#define DMA_STAT    8   /* DMA controller status register */
+#define DMA_CMD     8   /* DMA controller command register */
+#define DMA_MASK        10  /* DMA controller mask register */
+#define DMA_MODE        11  /* DMA controller mode register */
+#define DMA_RESETFF 12  /* DMA controller first/last flip flop  */
+/* DMA data */
+#define DMA_DISABLE (0x04)  /* Disable channel n */
+#define DMA_ENABLE  (0x00)  /* Enable channel n */
+/* Single transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE (0x54)
+/* Single transfers, incr. address, no auto init, reads, ch. n */
+#define DMA_TX_MODE (0x48)
+
+/* Write registers */
+#define DMA_CFG		0x08
+#define SERIAL_CFG	0x09
+#define INT_CFG		0x09	/* shares with serial config */
+#define DMA_CLR_FF	0x0a
+
+#define SINGLE 3686400
+#define DOUBLE 7372800
+#define XTAL   ((long) 6144000L)
+
+#define SIOCGPIPARAM		0x5000	/* get PI parameters */
+#define SIOCSPIPARAM		0x5001	/* set */
+#define SIOCGPIBAUD		0x5002	/* get only baud rate */
+#define SIOCSPIBAUD		0x5003	
+#define SIOCGPIDMA		0x5004	/* get only DMA */
+#define SIOCSPIDMA		0x5005	
+#define SIOCGPIIRQ		0x5006	/* get only IRQ */
+#define SIOCSPIIRQ		0x5007	
+
+struct pt_req  {
+    int cmd;
+    int speed;
+    int clockmode;
+    int txdelay;
+    unsigned char persist;
+    int slotime; 
+    int squeldelay;
+    int dmachan;    
+    int irq;    
+};
+
+/* SCC Interrupt vectors, if we have set 'status low' */
+#define CHBTxIV		0x00
+#define CHBEXTIV	0x02
+#define CHBRxIV		0x04
+#define CHBSRCIV	0x06
+#define CHATxIV		0x08
+#define CHAEXTIV	0x0a
+#define CHARxIV		0x0c
+#define CHASRCIV	0x0e
+
+
+#ifdef __KERNEL__
+
+/* Information that needs to be kept for each channel. */
+struct pt_local {
+    struct netstats stats; /* %%%dp*/
+    long open_time;             /* Useless example local info. */
+    unsigned long xtal; 
+
+    struct mbuf *rcvbuf;/* Buffer for current rx packet */
+    struct mbuf *rxdmabuf1; /* DMA rx buffer */
+    struct mbuf *rxdmabuf2; /* DMA rx buffer */
+
+    int bufsiz;         /* Size of rcvbuf */
+    char *rcp;          /* Pointer into rcvbuf */
+
+    struct sk_buff_head sndq;  /* Packets awaiting transmission */
+    int sndcnt;         /* Number of packets on sndq */
+    struct sk_buff *sndbuf;/* Current buffer being transmitted */
+    char *txdmabuf;     /* Transmit DMA buffer */
+	char *txptr;		/* Used by B port tx */
+	int txcnt;			
+    char tstate;        /* Transmitter state */
+#define IDLE    0       /* Transmitter off, no data pending */
+#define ACTIVE  1       /* Transmitter on, sending data */
+#define UNDERRUN 2      /* Transmitter on, flushing CRC */
+#define FLAGOUT 3       /* CRC sent - attempt to start next frame */
+#define DEFER 4         /* Receive Active - DEFER Transmit */
+#define ST_TXDELAY 5    /* Sending leading flags */
+#define CRCOUT 6
+    char rstate;        /* Set when !DCD goes to 0 (TRUE) */
+/* Normal state is ACTIVE if Receive enabled */
+#define RXERROR 2       /* Error -- Aborting current Frame */
+#define RXABORT 3       /* ABORT sequence detected */
+#define TOOBIG 4        /* too large a frame to store */
+	
+    int dev;            /* Device number */
+    int base;       /* Base of I/O registers */
+    int cardbase;     /* Base address of card */
+    int stata;        /* address of Channel A status regs */
+    int statb;        /* address of Channel B status regs */
+    int speed;        /* Line speed, bps */
+    int clockmode;    /* tapr 9600 modem clocking option */
+    int txdelay;      /* Transmit Delay 10 ms/cnt */
+    unsigned char persist;       /* Persistence (0-255) as a % */
+    int slotime;      /* Delay to wait on persistence hit */
+    int squeldelay;   /* Delay after XMTR OFF for squelch tail */
+    struct iface *iface;    /* Associated interface */
+    int dmachan;           /* DMA channel for this port */
+    char saved_RR0;	/* The saved version of RR) that we compare with */
+    int nrzi;			/* Do we use NRZI (or NRZ) */
+};
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/quota.h linux/include/linux/quota.h
--- v2.0.34/linux/include/linux/quota.h	Thu Jul  4 03:36:52 1996
+++ linux/include/linux/quota.h	Mon Jul 13 13:47:39 1998
@@ -169,6 +169,8 @@
    kdev_t dq_dev;                /* Device this applies to */
    short dq_flags;		/* see DQ_* */
    short dq_count;		/* reference count */
+   short dq_locknest;   	/* lock nesting */	
+   struct task_struct *dq_lockproc;	/* process holding the lock */
    struct vfsmount *dq_mnt;     /* vfsmountpoint this applies to */
    struct dqblk dq_dqb;         /* diskquota usage */
    struct wait_queue *dq_wait;	/* pointer to waitqueue */
diff -u --recursive --new-file v2.0.34/linux/include/linux/raid1.h linux/include/linux/raid1.h
--- v2.0.34/linux/include/linux/raid1.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/raid1.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,41 @@
+#ifndef _RAID1_H
+#define _RAID1_H
+
+#include <linux/md.h>
+
+struct mirror_info {
+	int		number;
+	int		raid_disk;
+	kdev_t		dev;
+	int		operational;
+	int		next;
+	int		sect_limit;
+};
+
+struct raid1_data {
+	struct md_dev *mddev;
+	struct mirror_info mirrors[MD_SB_DISKS];  	/* RAID1 devices, 2 to MD_SB_DISKS */
+	int raid_disks;
+	int working_disks;			/* Number of working disks */
+	int last_used;
+	unsigned long	next_sect;
+	int		sect_count;
+};
+
+/*
+ * this is our 'private' 'collective' RAID1 buffer head.
+ * it contains information about what kind of IO operations were started
+ * for this RAID5 operation, and about their status:
+ */
+
+struct raid1_bh {
+	unsigned int remaining;
+	unsigned int state;
+	int cmd;
+	struct md_dev *mddev;                    /* we could use bh->personality? */
+	struct buffer_head *master_bh;
+	struct buffer_head *mirror_bh [MD_SB_DISKS];
+	struct buffer_head *next_retry;
+};
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/raid5.h linux/include/linux/raid5.h
--- v2.0.34/linux/include/linux/raid5.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/raid5.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,34 @@
+#ifndef _RAID5_H
+#define _RAID5_H
+
+#include <linux/md.h>
+#include <asm/atomic.h>
+
+struct disk_info {
+	kdev_t	dev;
+	int	operational;
+	int	number;
+	int	raid_disk;
+};
+
+struct raid5_data {
+	struct md_dev		*mddev;
+	struct md_thread	*thread;
+	struct disk_info	disks[MD_SB_DISKS];
+	int			buffer_size;
+	int			chunk_size, level, algorithm;
+	int			raid_disks, working_disks, failed_disks;
+	int			sector_count;
+	unsigned long		next_sector;
+	atomic_t		nr_handle;
+};
+
+/*
+ * Our supported algorithms
+ */
+#define ALGORITHM_LEFT_ASYMMETRIC	0
+#define ALGORITHM_RIGHT_ASYMMETRIC	1
+#define ALGORITHM_LEFT_SYMMETRIC	2
+#define ALGORITHM_RIGHT_SYMMETRIC	3
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/rose.h linux/include/linux/rose.h
--- v2.0.34/linux/include/linux/rose.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/rose.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,64 @@
+/*
+ * These are the public elements of the Linux kernel Rose implementation.
+ * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the
+ * definition of the ax25_address structure.
+ */
+
+#ifndef	ROSE_KERNEL_H
+#define	ROSE_KERNEL_H
+
+#define PF_ROSE		AF_ROSE
+#define ROSE_MTU	251
+
+#define	ROSE_DEFER	1
+#define ROSE_T1		2
+#define	ROSE_T2		3
+#define	ROSE_T3		4
+#define	ROSE_IDLE	5
+#define	ROSE_QBITINCL	6
+#define	ROSE_HOLDBACK	7
+
+#define	SIOCRSGCAUSE		(SIOCPROTOPRIVATE+0)
+#define	SIOCRSSCAUSE		(SIOCPROTOPRIVATE+1)
+#define	SIOCRSL2CALL		(SIOCPROTOPRIVATE+2)
+#define	SIOCRSACCEPT		(SIOCPROTOPRIVATE+3)
+#define	SIOCRSCLRRT		(SIOCPROTOPRIVATE+4)
+
+#define	ROSE_DTE_ORIGINATED	0x00
+#define	ROSE_NUMBER_BUSY	0x01
+#define	ROSE_INVALID_FACILITY	0x03
+#define	ROSE_NETWORK_CONGESTION	0x05
+#define	ROSE_OUT_OF_ORDER	0x09
+#define	ROSE_ACCESS_BARRED	0x0B
+#define	ROSE_NOT_OBTAINABLE	0x0D
+#define	ROSE_REMOTE_PROCEDURE	0x11
+#define	ROSE_LOCAL_PROCEDURE	0x13
+#define	ROSE_SHIP_ABSENT	0x39
+
+typedef struct {
+	char		rose_addr[5];
+} rose_address;
+
+struct sockaddr_rose {
+	unsigned short	srose_family;
+	rose_address	srose_addr;
+	ax25_address	srose_call;
+	int		srose_ndigis;
+	ax25_address	srose_digi;
+};
+
+struct rose_route_struct {
+	rose_address	address;
+	unsigned short	mask;
+	ax25_address	neighbour;
+	char		device[16];
+	unsigned char	ndigis;
+	ax25_address	digipeaters[AX25_MAX_DIGIS];
+};
+
+struct rose_cause_struct {
+	unsigned char	cause;
+	unsigned char	diagnostic;
+};
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/sc26198.h linux/include/linux/sc26198.h
--- v2.0.34/linux/include/linux/sc26198.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sc26198.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,533 @@
+/*****************************************************************************/
+
+/*
+ *	sc26198.h  -- SC26198 UART hardware info.
+ *
+ *	Copyright (C) 1995-1998  Stallion Technologies (support@stallion.oz.au).
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef	_SC26198_H
+#define	_SC26198_H
+/*****************************************************************************/
+
+/*
+ *	Define the number of async ports per sc26198 uart device.
+ */
+#define	SC26198_PORTS		8
+
+/*
+ *	Baud rate timing clocks. All derived from a master 14.7456 MHz clock.
+ */
+#define	SC26198_MASTERCLOCK	14745600L
+#define	SC26198_DCLK		(SC26198_MASTERCLOCK)
+#define	SC26198_CCLK		(SC26198_MASTERCLOCK / 2)
+#define	SC26198_BCLK		(SC26198_MASTERCLOCK / 4)
+
+/*
+ *	Define internal FIFO sizes for the 26198 ports.
+ */
+#define	SC26198_TXFIFOSIZE	16
+#define	SC26198_RXFIFOSIZE	16
+
+/*****************************************************************************/
+
+/*
+ *	Global register definitions. These registers are global to each 26198
+ *	device, not specific ports on it.
+ */
+#define	TSTR		0x0d
+#define	GCCR		0x0f
+#define	ICR		0x1b
+#define	WDTRCR		0x1d
+#define	IVR		0x1f
+#define	BRGTRUA		0x84
+#define	GPOSR		0x87
+#define	GPOC		0x8b
+#define	UCIR		0x8c
+#define	CIR		0x8c
+#define	BRGTRUB		0x8d
+#define	GRXFIFO		0x8e
+#define	GTXFIFO		0x8e
+#define	GCCR2		0x8f
+#define	BRGTRLA		0x94
+#define	GPOR		0x97
+#define	GPOD		0x9b
+#define	BRGTCR		0x9c
+#define	GICR		0x9c
+#define	BRGTRLB		0x9d
+#define	GIBCR		0x9d
+#define	GITR		0x9f
+
+/*
+ *	Per port channel registers. These are the register offsets within
+ *	the port address space, so need to have the port address (0 to 7)
+ *	inserted in bit positions 4:6.
+ */
+#define	MR0		0x00
+#define	MR1		0x01
+#define	IOPCR		0x02
+#define	BCRBRK		0x03
+#define	BCRCOS		0x04
+#define	BCRX		0x06
+#define	BCRA		0x07
+#define	XONCR		0x08
+#define	XOFFCR		0x09
+#define	ARCR		0x0a
+#define	RXCSR		0x0c
+#define	TXCSR		0x0e
+#define	MR2		0x80
+#define	SR		0x81
+#define SCCR		0x81
+#define	ISR		0x82
+#define	IMR		0x82
+#define	TXFIFO		0x83
+#define	RXFIFO		0x83
+#define	IPR		0x84
+#define	IOPIOR		0x85
+#define	XISR		0x86
+
+/*
+ *	For any given port calculate the address to use to access a specified
+ *	register. This is only used for unusual access, mostly this is done
+ *	through the assembler access routines.
+ */
+#define	SC26198_PORTREG(port,reg)	((((port) & 0x07) << 4) | (reg))
+
+/*****************************************************************************/
+
+/*
+ *	Global configuration control register bit definitions.
+ */
+#define	GCCR_NOACK		0x00
+#define	GCCR_IVRACK		0x02
+#define	GCCR_IVRCHANACK		0x04
+#define	GCCR_IVRTYPCHANACK	0x06
+#define	GCCR_ASYNCCYCLE		0x00
+#define	GCCR_SYNCCYCLE		0x40
+
+/*****************************************************************************/
+
+/*
+ *	Mode register 0 bit definitions.
+ */
+#define	MR0_ADDRNONE		0x00
+#define	MR0_AUTOWAKE		0x01
+#define	MR0_AUTODOZE		0x02
+#define	MR0_AUTOWAKEDOZE	0x03
+#define	MR0_SWFNONE		0x00
+#define	MR0_SWFTX		0x04
+#define	MR0_SWFRX		0x08
+#define	MR0_SWFRXTX		0x0c
+#define	MR0_TXMASK		0x30
+#define	MR0_TXEMPTY		0x00
+#define	MR0_TXHIGH		0x10
+#define	MR0_TXHALF		0x20
+#define	MR0_TXRDY		0x00
+#define	MR0_ADDRNT		0x00
+#define	MR0_ADDRT		0x40
+#define	MR0_SWFNT		0x00
+#define	MR0_SWFT		0x80
+
+/*
+ *	Mode register 1 bit definitions.
+ */
+#define	MR1_CS5			0x00
+#define	MR1_CS6			0x01
+#define	MR1_CS7			0x02
+#define	MR1_CS8			0x03
+#define	MR1_PAREVEN		0x00
+#define	MR1_PARODD		0x04
+#define	MR1_PARENB		0x00
+#define	MR1_PARFORCE		0x08
+#define	MR1_PARNONE		0x10
+#define	MR1_PARSPECIAL		0x18
+#define	MR1_ERRCHAR		0x00
+#define	MR1_ERRBLOCK		0x20
+#define	MR1_ISRUNMASKED		0x00
+#define	MR1_ISRMASKED		0x40
+#define	MR1_AUTORTS		0x80
+
+/*
+ *	Mode register 2 bit definitions.
+ */
+#define	MR2_STOP1		0x00
+#define	MR2_STOP15		0x01
+#define	MR2_STOP2		0x02
+#define	MR2_STOP916		0x03
+#define	MR2_RXFIFORDY		0x00
+#define	MR2_RXFIFOHALF		0x04
+#define	MR2_RXFIFOHIGH		0x08
+#define	MR2_RXFIFOFULL		0x0c
+#define	MR2_AUTOCTS		0x10
+#define	MR2_TXRTS		0x20
+#define	MR2_MODENORM		0x00
+#define	MR2_MODEAUTOECHO	0x40
+#define	MR2_MODELOOP		0x80
+#define	MR2_MODEREMECHO		0xc0
+
+/*****************************************************************************/
+
+/*
+ *	Baud Rate Generator (BRG) selector values.
+ */
+#define	BRG_50			0x00
+#define	BRG_75			0x01
+#define	BRG_150			0x02
+#define	BRG_200			0x03
+#define	BRG_300			0x04
+#define	BRG_450			0x05
+#define	BRG_600			0x06
+#define	BRG_900			0x07
+#define	BRG_1200		0x08
+#define	BRG_1800		0x09
+#define	BRG_2400		0x0a
+#define	BRG_3600		0x0b
+#define	BRG_4800		0x0c
+#define	BRG_7200		0x0d
+#define	BRG_9600		0x0e
+#define	BRG_14400		0x0f
+#define	BRG_19200		0x10
+#define	BRG_28200		0x11
+#define	BRG_38400		0x12
+#define	BRG_57600		0x13
+#define	BRG_115200		0x14
+#define	BRG_230400		0x15
+#define	BRG_GIN0		0x16
+#define	BRG_GIN1		0x17
+#define	BRG_CT0			0x18
+#define	BRG_CT1			0x19
+#define	BRG_RX2TX316		0x1b
+#define	BRG_RX2TX31		0x1c
+
+#define	SC26198_MAXBAUD		921600
+
+/*****************************************************************************/
+
+/*
+ *	Command register command definitions.
+ */
+#define	CR_NULL			0x04
+#define	CR_ADDRNORMAL		0x0c
+#define	CR_RXRESET		0x14
+#define	CR_TXRESET		0x1c
+#define	CR_CLEARRXERR		0x24
+#define	CR_BREAKRESET		0x2c
+#define	CR_TXSTARTBREAK		0x34
+#define	CR_TXSTOPBREAK		0x3c
+#define	CR_RTSON		0x44
+#define	CR_RTSOFF		0x4c
+#define	CR_ADDRINIT		0x5c
+#define	CR_RXERRBLOCK		0x6c
+#define	CR_TXSENDXON		0x84
+#define	CR_TXSENDXOFF		0x8c
+#define	CR_GANGXONSET		0x94
+#define	CR_GANGXOFFSET		0x9c
+#define	CR_GANGXONINIT		0xa4
+#define	CR_GANGXOFFINIT		0xac
+#define	CR_HOSTXON		0xb4
+#define	CR_HOSTXOFF		0xbc
+#define	CR_CANCELXOFF		0xc4
+#define	CR_ADDRRESET		0xdc
+#define	CR_RESETALLPORTS	0xf4
+#define	CR_RESETALL		0xfc
+
+#define	CR_RXENABLE		0x01
+#define	CR_TXENABLE		0x02
+
+/*****************************************************************************/
+
+/*
+ *	Channel status register.
+ */
+#define	SR_RXRDY		0x01
+#define	SR_RXFULL		0x02
+#define	SR_TXRDY		0x04
+#define	SR_TXEMPTY		0x08
+#define	SR_RXOVERRUN		0x10
+#define	SR_RXPARITY		0x20
+#define	SR_RXFRAMING		0x40
+#define	SR_RXBREAK		0x80
+
+#define	SR_RXERRS		(SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN)
+
+/*****************************************************************************/
+
+/*
+ *	Interrupt status register and interrupt mask register bit definitions.
+ */
+#define	IR_TXRDY		0x01
+#define	IR_RXRDY		0x02
+#define	IR_RXBREAK		0x04
+#define	IR_XONXOFF		0x10
+#define	IR_ADDRRECOG		0x20
+#define	IR_RXWATCHDOG		0x40
+#define	IR_IOPORT		0x80
+
+/*****************************************************************************/
+
+/*
+ *	Interrupt vector register field definitions.
+ */
+#define	IVR_CHANMASK		0x07
+#define	IVR_TYPEMASK		0x18
+#define	IVR_CONSTMASK		0xc0
+
+#define	IVR_RXDATA		0x10
+#define	IVR_RXBADDATA		0x18
+#define	IVR_TXDATA		0x08
+#define	IVR_OTHER		0x00
+
+/*****************************************************************************/
+
+/*
+ *	BRG timer control register bit definitions.
+ */
+#define	BRGCTCR_DISABCLK0	0x00
+#define	BRGCTCR_ENABCLK0	0x08
+#define	BRGCTCR_DISABCLK1	0x00
+#define	BRGCTCR_ENABCLK1	0x80
+
+#define	BRGCTCR_0SCLK16		0x00
+#define	BRGCTCR_0SCLK32		0x01
+#define	BRGCTCR_0SCLK64		0x02
+#define	BRGCTCR_0SCLK128	0x03
+#define	BRGCTCR_0X1		0x04
+#define	BRGCTCR_0X12		0x05
+#define	BRGCTCR_0IO1A		0x06
+#define	BRGCTCR_0GIN0		0x07
+
+#define	BRGCTCR_1SCLK16		0x00
+#define	BRGCTCR_1SCLK32		0x10
+#define	BRGCTCR_1SCLK64		0x20
+#define	BRGCTCR_1SCLK128	0x30
+#define	BRGCTCR_1X1		0x40
+#define	BRGCTCR_1X12		0x50
+#define	BRGCTCR_1IO1B		0x60
+#define	BRGCTCR_1GIN1		0x70
+
+/*****************************************************************************/
+
+/*
+ *	Watch dog timer enable register.
+ */
+#define	WDTRCR_ENABALL		0xff
+
+/*****************************************************************************/
+
+/*
+ *	XON/XOFF interrupt status register.
+ */
+#define	XISR_TXCHARMASK		0x03
+#define	XISR_TXCHARNORMAL	0x00
+#define	XISR_TXWAIT		0x01
+#define	XISR_TXXOFFPEND		0x02
+#define	XISR_TXXONPEND		0x03
+
+#define	XISR_TXFLOWMASK		0x0c
+#define	XISR_TXNORMAL		0x00
+#define	XISR_TXSTOPPEND		0x04
+#define	XISR_TXSTARTED		0x08
+#define	XISR_TXSTOPPED		0x0c
+
+#define	XISR_RXFLOWMASK		0x30
+#define	XISR_RXFLOWNONE		0x00
+#define	XISR_RXXONSENT		0x10
+#define	XISR_RXXOFFSENT		0x20
+
+#define	XISR_RXXONGOT		0x40
+#define	XISR_RXXOFFGOT		0x80
+
+/*****************************************************************************/
+
+/*
+ *	Current interrupt register.
+ */
+#define	CIR_TYPEMASK		0xc0
+#define	CIR_TYPEOTHER		0x00
+#define	CIR_TYPETX		0x40
+#define	CIR_TYPERXGOOD		0x80
+#define	CIR_TYPERXBAD		0xc0
+
+#define	CIR_RXDATA		0x80
+#define	CIR_RXBADDATA		0x40
+#define	CIR_TXDATA		0x40
+
+#define	CIR_CHANMASK		0x07
+#define	CIR_CNTMASK		0x38
+
+#define	CIR_SUBTYPEMASK		0x38
+#define	CIR_SUBNONE		0x00
+#define	CIR_SUBCOS		0x08
+#define	CIR_SUBADDR		0x10
+#define	CIR_SUBXONXOFF		0x18
+#define	CIR_SUBBREAK		0x28
+
+/*****************************************************************************/
+
+/*
+ *	Global interrupting channel register.
+ */
+#define	GICR_CHANMASK		0x07
+
+/*****************************************************************************/
+
+/*
+ *	Global interrupting byte count register.
+ */
+#define	GICR_COUNTMASK		0x0f
+
+/*****************************************************************************/
+
+/*
+ *	Global interrupting type register.
+ */
+#define	GITR_RXMASK		0xc0
+#define	GITR_RXNONE		0x00
+#define	GITR_RXBADDATA		0x80
+#define	GITR_RXGOODDATA		0xc0
+#define	GITR_TXDATA		0x20
+
+#define	GITR_SUBTYPEMASK	0x07
+#define	GITR_SUBNONE		0x00
+#define	GITR_SUBCOS		0x01
+#define	GITR_SUBADDR		0x02
+#define	GITR_SUBXONXOFF		0x03
+#define	GITR_SUBBREAK		0x05
+
+/*****************************************************************************/
+
+/*
+ *	Input port change register.
+ */
+#define	IPR_CTS			0x01
+#define	IPR_DTR			0x02
+#define	IPR_RTS			0x04
+#define	IPR_DCD			0x08
+#define	IPR_CTSCHANGE		0x10
+#define	IPR_DTRCHANGE		0x20
+#define	IPR_RTSCHANGE		0x40
+#define	IPR_DCDCHANGE		0x80
+
+#define	IPR_CHANGEMASK		0xf0
+
+/*****************************************************************************/
+
+/*
+ *	IO port interrupt and output register.
+ */
+#define	IOPR_CTS		0x01
+#define	IOPR_DTR		0x02
+#define	IOPR_RTS		0x04
+#define	IOPR_DCD		0x08
+#define	IOPR_CTSCOS		0x10
+#define	IOPR_DTRCOS		0x20
+#define	IOPR_RTSCOS		0x40
+#define	IOPR_DCDCOS		0x80
+
+/*****************************************************************************/
+
+/*
+ *	IO port configuration register.
+ */
+#define	IOPCR_SETCTS		0x00
+#define	IOPCR_SETDTR		0x04
+#define	IOPCR_SETRTS		0x10
+#define	IOPCR_SETDCD		0x00
+
+#define	IOPCR_SETSIGS		(IOPCR_SETRTS | IOPCR_SETRTS | IOPCR_SETDTR | IOPCR_SETDCD)
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output select register.
+ */
+#define	GPORS_TXC1XA		0x08
+#define	GPORS_TXC16XA		0x09
+#define	GPORS_RXC16XA		0x0a
+#define	GPORS_TXC16XB		0x0b
+#define	GPORS_GPOR3		0x0c
+#define	GPORS_GPOR2		0x0d
+#define	GPORS_GPOR1		0x0e
+#define	GPORS_GPOR0		0x0f
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output register.
+ */
+#define	GPOR_0			0x01
+#define	GPOR_1			0x02
+#define	GPOR_2			0x04
+#define	GPOR_3			0x08
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output clock register.
+ */
+#define	GPORC_0NONE		0x00
+#define	GPORC_0GIN0		0x01
+#define	GPORC_0GIN1		0x02
+#define	GPORC_0IO3A		0x02
+
+#define	GPORC_1NONE		0x00
+#define	GPORC_1GIN0		0x04
+#define	GPORC_1GIN1		0x08
+#define	GPORC_1IO3C		0x0c
+
+#define	GPORC_2NONE		0x00
+#define	GPORC_2GIN0		0x10
+#define	GPORC_2GIN1		0x20
+#define	GPORC_2IO3E		0x20
+
+#define	GPORC_3NONE		0x00
+#define	GPORC_3GIN0		0x40
+#define	GPORC_3GIN1		0x80
+#define	GPORC_3IO3G		0xc0
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output data register.
+ */
+#define	GPOD_0MASK		0x03
+#define	GPOD_0SET1		0x00
+#define	GPOD_0SET0		0x01
+#define	GPOD_0SETR0		0x02
+#define	GPOD_0SETIO3B		0x03
+
+#define	GPOD_1MASK		0x0c
+#define	GPOD_1SET1		0x00
+#define	GPOD_1SET0		0x04
+#define	GPOD_1SETR0		0x08
+#define	GPOD_1SETIO3D		0x0c
+
+#define	GPOD_2MASK		0x30
+#define	GPOD_2SET1		0x00
+#define	GPOD_2SET0		0x10
+#define	GPOD_2SETR0		0x20
+#define	GPOD_2SETIO3F		0x30
+
+#define	GPOD_3MASK		0xc0
+#define	GPOD_3SET1		0x00
+#define	GPOD_3SET0		0x40
+#define	GPOD_3SETR0		0x80
+#define	GPOD_3SETIO3H		0xc0
+
+/*****************************************************************************/
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.0.34/linux/include/linux/socket.h	Tue Aug 12 15:00:14 1997
+++ linux/include/linux/socket.h	Mon Jul 13 13:47:39 1998
@@ -63,7 +63,9 @@
 #ifdef LINUX_2_1_X
 #define AF_INET6	10	/* IP version 6			*/
 #endif
-#define AF_MAX		12	/* For now.. */
+#define AF_ROSE		11	/* Amateur Radio X.25 PLP       */
+#define AF_MAX		13	/* For now.. */
+#define AF_PACKET	17	/* Forward compat hook		*/
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -79,8 +81,9 @@
 #ifdef LINUX_2_1_X
 #define PF_INET6	AF_INET6
 #endif
+#define	PF_ROSE		AF_ROSE
 #define PF_MAX		AF_MAX
-
+#define PF_PACKET	AF_PACKET
 /* Maximum queue length specifiable by listen.  */
 #define SOMAXCONN	128
 
@@ -97,6 +100,7 @@
 #define SOL_AX25	257
 #define SOL_ATALK	258
 #define	SOL_NETROM	259
+#define	SOL_ROSE	260
 #define SOL_TCP		6
 #define SOL_UDP		17
 
diff -u --recursive --new-file v2.0.34/linux/include/linux/soundmodem.h linux/include/linux/soundmodem.h
--- v2.0.34/linux/include/linux/soundmodem.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/soundmodem.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,93 @@
+/*
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _SOUNDMODEM_H
+#define _SOUNDMODEM_H
+
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct sm_debug_data {
+	unsigned int int_rate;
+	unsigned int mod_cycles;
+	unsigned int demod_cycles;
+	unsigned int dma_residue;
+};
+
+struct sm_diag_data {
+	unsigned int mode;
+	unsigned int flags;
+	unsigned int samplesperbit;
+	unsigned int datalen;
+	short *data;
+};
+
+struct sm_mixer_data {
+	unsigned int mixer_type;
+	unsigned int sample_rate;
+	unsigned int bit_rate;
+	unsigned int reg;
+	unsigned int data;
+};
+
+struct sm_config {
+	int hardware;
+	int mode;
+};
+
+struct sm_ioctl {
+	int cmd;
+	union {
+		struct sm_config cfg;
+		struct sm_diag_data diag;	
+		struct sm_mixer_data mix;
+		struct sm_debug_data dbg;
+	} data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * diagnose modes
+ */
+#define SM_DIAGMODE_OFF            0
+#define SM_DIAGMODE_INPUT          1
+#define SM_DIAGMODE_DEMOD          2
+#define SM_DIAGMODE_CONSTELLATION  3
+
+/*
+ * diagnose flags
+ */
+#define SM_DIAGFLAG_DCDGATE    (1<<0)
+#define SM_DIAGFLAG_VALID      (1<<1)
+
+/*
+ * mixer types
+ */
+#define SM_MIXER_INVALID       0
+#define SM_MIXER_AD1848        0x10
+#define SM_MIXER_CRYSTAL       0x11
+#define SM_MIXER_CT1335        0x20
+#define SM_MIXER_CT1345        0x21
+#define SM_MIXER_CT1745        0x22
+
+/*
+ * ioctl values
+ */
+#define SMCTL_DIAGNOSE         0x82
+#define SMCTL_GETMIXER         0x83
+#define SMCTL_SETMIXER         0x84
+#define SMCTL_GETDEBUG         0x85
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _SOUNDMODEM_H */
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.0.34/linux/include/linux/stallion.h linux/include/linux/stallion.h
--- v2.0.34/linux/include/linux/stallion.h	Sat Apr 20 01:25:31 1996
+++ linux/include/linux/stallion.h	Mon Jul 13 13:47:39 1998
@@ -3,6 +3,7 @@
 /*
  *	stallion.h  -- stallion multiport serial driver.
  *
+ *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
@@ -30,6 +31,7 @@
  */
 #define	STL_MAXBRDS		4
 #define	STL_MAXPANELS		4
+#define	STL_MAXBANKS		8
 #define	STL_PORTSPERPANEL	16
 #define	STL_MAXPORTS		64
 #define	STL_MAXDEVS		(STL_MAXBRDS * STL_MAXPORTS)
@@ -65,7 +67,7 @@
  *	is associated with, this makes it (fairly) easy to get back to the
  *	board/panel info for a port.
  */
-typedef struct {
+typedef struct stlport {
 	unsigned long		magic;
 	int			portnr;
 	int			panelnr;
@@ -87,8 +89,11 @@
 	unsigned int		sigs;
 	unsigned int		rxignoremsk;
 	unsigned int		rxmarkmsk;
+	unsigned int		imr;
+	unsigned int		crenable;
 	unsigned long		clk;
 	unsigned long		hwid;
+	void			*uartp;
 	struct tty_struct	*tty;
 	struct wait_queue	*open_wait;
 	struct wait_queue	*close_wait;
@@ -99,34 +104,43 @@
 	stlrq_t			tx;
 } stlport_t;
 
-typedef struct {
+typedef struct stlpanel {
 	unsigned long	magic;
 	int		panelnr;
 	int		brdnr;
 	int		pagenr;
 	int		nrports;
 	int		iobase;
+	void		*uartp;
+	void		(*isr)(struct stlpanel *panelp, unsigned int iobase);
 	unsigned int	hwid;
 	unsigned int	ackmask;
 	stlport_t	*ports[STL_PORTSPERPANEL];
 } stlpanel_t;
 
-typedef struct {
+typedef struct stlbrd {
 	unsigned long	magic;
 	int		brdnr;
 	int		brdtype;
 	int		state;
 	int		nrpanels;
 	int		nrports;
+	int		nrbnks;
 	int		irq;
 	int		irqtype;
+	void		(*isr)(struct stlbrd *brdp);
 	unsigned int	ioaddr1;
 	unsigned int	ioaddr2;
+	unsigned int	iosize1;
+	unsigned int	iosize2;
 	unsigned int	iostatus;
 	unsigned int	ioctrl;
 	unsigned int	ioctrlval;
 	unsigned int	hwid;
 	unsigned long	clk;
+	unsigned int	bnkpageaddr[STL_MAXBANKS];
+	unsigned int	bnkstataddr[STL_MAXBANKS];
+	stlpanel_t	*bnk2panel[STL_MAXBANKS];
 	stlpanel_t	*panels[STL_MAXPANELS];
 } stlbrd_t;
 
diff -u --recursive --new-file v2.0.34/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.0.34/linux/include/linux/sysctl.h	Tue Aug 12 14:06:35 1997
+++ linux/include/linux/sysctl.h	Mon Jul 13 13:47:39 1998
@@ -80,6 +80,7 @@
 #define NET_NETROM      8
 #define NET_AX25        9
 #define NET_BRIDGE	10
+#define	NET_ROSE	11
 
 /* /proc/sys/net/core */
 #define NET_CORE_NET_ALIAS_MAX 1
@@ -106,8 +107,41 @@
 /* /proc/sys/net/appletalk */
 
 /* /proc/sys/net/netrom */
+#define	NET_NETROM_DEFAULT_PATH_QUALITY			1
+#define	NET_NETROM_OBSOLESCENCE_COUNT_INITIALISER	2
+#define	NET_NETROM_NETWORK_TTL_INITIALISER		3
+#define	NET_NETROM_TRANSPORT_TIMEOUT			4
+#define	NET_NETROM_TRANSPORT_MAXIMUM_TRIES		5
+#define	NET_NETROM_TRANSPORT_ACKNOWLEDGE_DELAY		6
+#define	NET_NETROM_TRANSPORT_BUSY_DELAY			7
+#define	NET_NETROM_TRANSPORT_REQUESTED_WINDOW_SIZE	8
+#define	NET_NETROM_ROUTING_CONTROL			9
+#define	NET_NETROM_LINK_FAILS_COUNT			10
 
 /* /proc/sys/net/ax25 */
+#define	NET_AX25_IP_DEFAULT_MODE			1
+#define	NET_AX25_DEFAULT_MODE				2
+#define	NET_AX25_BACKOFF_TYPE				3
+#define	NET_AX25_CONNECT_MODE				4
+#define	NET_AX25_STANDARD_WINDOW			5
+#define	NET_AX25_EXTENDED_WINDOW			6
+#define	NET_AX25_T1_TIMEOUT				7
+#define	NET_AX25_T2_TIMEOUT				8
+#define	NET_AX25_T3_TIMEOUT				9
+#define	NET_AX25_IDLE_TIMEOUT				10
+#define	NET_AX25_N2					11
+#define	NET_AX25_PACLEN					12
+ 
+/* /proc/sys/net/rose */
+#define	NET_ROSE_RESTART_REQUEST_TIMEOUT		1
+#define	NET_ROSE_CALL_REQUEST_TIMEOUT			2
+#define	NET_ROSE_RESET_REQUEST_TIMEOUT			3
+#define	NET_ROSE_CLEAR_REQUEST_TIMEOUT			4
+#define	NET_ROSE_ACK_HOLD_BACK_TIMEOUT			5
+#define	NET_ROSE_ROUTING_CONTROL			6
+#define	NET_ROSE_LINK_FAIL_TIMEOUT			7
+#define	NET_ROSE_MAX_VCS				8
+#define	NET_ROSE_WINDOW_SIZE				9
 
 /* CTL_PROC names: */
 
diff -u --recursive --new-file v2.0.34/linux/include/net/ax25.h linux/include/net/ax25.h
--- v2.0.34/linux/include/net/ax25.h	Wed Oct 15 15:23:06 1997
+++ linux/include/net/ax25.h	Mon Jul 13 13:47:39 1998
@@ -8,44 +8,43 @@
 #define _AX25_H 
 #include <linux/ax25.h>
 
-#define PR_SLOWHZ	10		/*  Run timing at 1/10 second - gives us better resolution for 56kbit links */
+#define AX25_SLOWHZ			10	/* Run timing at 1/10 second - gives us better resolution for 56kbit links */
 
-#define	AX25_T1CLAMPLO  (1 * PR_SLOWHZ)	/* If defined, clamp at 1 second **/
-#define	AX25_T1CLAMPHI (30 * PR_SLOWHZ)	/* If defined, clamp at 30 seconds **/
+#define	AX25_T1CLAMPLO  		(1 * AX25_SLOWHZ)	/* If defined, clamp at 1 second **/
+#define	AX25_T1CLAMPHI 			(30 * AX25_SLOWHZ)	/* If defined, clamp at 30 seconds **/
 
-#define	AX25_BROKEN_NETMAC
+#define	AX25_BPQ_HEADER_LEN		16
+#define	AX25_KISS_HEADER_LEN		1
 
-#define	AX25_BPQ_HEADER_LEN	16
-#define	AX25_KISS_HEADER_LEN	1
-
-#define	AX25_HEADER_LEN		17
-#define	AX25_ADDR_LEN		7
-#define	AX25_DIGI_HEADER_LEN	(AX25_MAX_DIGIS * AX25_ADDR_LEN)
-#define	AX25_MAX_HEADER_LEN	(AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN)
- 
-#define AX25_P_IP	0xCC
-#define AX25_P_ARP	0xCD
-#define AX25_P_TEXT 	0xF0
-#define AX25_P_NETROM 	0xCF
-#define	AX25_P_SEGMENT	0x08
-
-#define	SEG_REM		0x7F
-#define	SEG_FIRST	0x80
-
-#define LAPB_UI		0x03
-#define LAPB_C		0x80
-#define LAPB_E		0x01
-
-#define SSSID_SPARE	0x60	/* Unused bits in SSID for standard AX.25 */
-#define ESSID_SPARE	0x20	/* Unused bits in SSID for extended AX.25 */
-#define DAMA_FLAG	0x20	/* Well, it is *NOT* unused! (dl1bke 951121 */
-
-#define AX25_REPEATED	0x80
-
-#define	ACK_PENDING_CONDITION		0x01
-#define	REJECT_CONDITION		0x02
-#define	PEER_RX_BUSY_CONDITION		0x04
-#define	OWN_RX_BUSY_CONDITION		0x08
+#define	AX25_HEADER_LEN			17
+#define	AX25_ADDR_LEN			7
+#define	AX25_DIGI_HEADER_LEN		(AX25_MAX_DIGIS * AX25_ADDR_LEN)
+#define	AX25_MAX_HEADER_LEN		(AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN)
+
+/* AX.25 Protocol IDs */
+#define AX25_P_ROSE			0x01
+#define AX25_P_IP			0xCC
+#define AX25_P_ARP			0xCD
+#define AX25_P_TEXT 			0xF0
+#define AX25_P_NETROM 			0xCF
+#define	AX25_P_SEGMENT			0x08
+
+/* AX.25 Segment control values */
+#define	AX25_SEG_REM			0x7F
+#define	AX25_SEG_FIRST			0x80
+
+#define AX25_CBIT			0x80	/* Command/Response bit */
+#define AX25_EBIT			0x01	/* HDLC Address Extension bit */
+#define AX25_HBIT			0x80	/* Has been repeated bit */
+
+#define AX25_SSSID_SPARE		0x60	/* Unused bits in SSID for standard AX.25 */
+#define AX25_ESSID_SPARE		0x20	/* Unused bits in SSID for extended AX.25 */
+#define AX25_DAMA_FLAG			0x20	/* Well, it is *NOT* unused! (dl1bke 951121 */
+
+#define	AX25_COND_ACK_PENDING		0x01
+#define	AX25_COND_REJECT		0x02
+#define	AX25_COND_PEER_RX_BUSY		0x04
+#define	AX25_COND_OWN_RX_BUSY		0x08
 
 #ifndef _LINUX_NETDEVICE_H
 #include <linux/netdevice.h>
@@ -75,86 +74,101 @@
 /* Upper sub-layer (LAPB) definitions */
 
 /* Control field templates */
-#define	I	0x00	/* Information frames */
-#define	S	0x01	/* Supervisory frames */
-#define	RR	0x01	/* Receiver ready */
-#define	RNR	0x05	/* Receiver not ready */
-#define	REJ	0x09	/* Reject */
-#define	U	0x03	/* Unnumbered frames */
-#define	SABM	0x2f	/* Set Asynchronous Balanced Mode */
-#define	SABME	0x6f	/* Set Asynchronous Balanced Mode Extended */
-#define	DISC	0x43	/* Disconnect */
-#define	DM	0x0f	/* Disconnected mode */
-#define	UA	0x63	/* Unnumbered acknowledge */
-#define	FRMR	0x87	/* Frame reject */
-#define	UI	0x03	/* Unnumbered information */
-#define	PF	0x10	/* Poll/final bit for standard AX.25 */
-#define	EPF	0x01	/* Poll/final bit for extended AX.25 */
+#define	AX25_I			0x00	/* Information frames */
+#define	AX25_S			0x01	/* Supervisory frames */
+#define	AX25_RR			0x01	/* Receiver ready */
+#define	AX25_RNR		0x05	/* Receiver not ready */
+#define	AX25_REJ		0x09	/* Reject */
+#define	AX25_U			0x03	/* Unnumbered frames */
+#define	AX25_SABM		0x2f	/* Set Asynchronous Balanced Mode */
+#define	AX25_SABME		0x6f	/* Set Asynchronous Balanced Mode Extended */
+#define	AX25_DISC		0x43	/* Disconnect */
+#define	AX25_DM			0x0f	/* Disconnected mode */
+#define	AX25_UA			0x63	/* Unnumbered acknowledge */
+#define	AX25_FRMR		0x87	/* Frame reject */
+#define	AX25_UI			0x03	/* Unnumbered information */
 
-#define ILLEGAL	0x100	/* Impossible to be a real frame type */
+#define	AX25_PF			0x10	/* Poll/final bit for standard AX.25 */
+#define	AX25_EPF		0x01	/* Poll/final bit for extended AX.25 */
 
-#define	POLLOFF		0
-#define	POLLON		1
+#define AX25_ILLEGAL		0x100	/* Impossible to be a real frame type */
 
-/* AX25 L2 C-bit */
+#define	AX25_POLLOFF		0
+#define	AX25_POLLON		1
 
-#define C_COMMAND	1	/* C_ otherwise it clashes with the de600 defines (sigh)) */
-#define C_RESPONSE	2
+/* AX25 L2 C-bit */
+#define AX25_COMMAND		1
+#define AX25_RESPONSE		2
 
 /* Define Link State constants. */
 
-#define AX25_STATE_0	0
-#define AX25_STATE_1	1
-#define AX25_STATE_2	2
-#define AX25_STATE_3	3
-#define AX25_STATE_4	4
-
-#define MODULUS 	8			/*  Standard AX.25 modulus */
-#define	EMODULUS	128			/*  Extended AX.25 modulus */
-
-#define	AX25_DEF_IPDEFMODE	'D'
-#define	AX25_DEF_AXDEFMODE	8
-#define	AX25_DEF_NETROM		1
-#define	AX25_DEF_TEXT		1
-#define	AX25_DEF_BACKOFF	'E'
-#define	AX25_DEF_CONMODE	1
-#define	AX25_DEF_WINDOW		2
-#define	AX25_DEF_EWINDOW	32
-#define	AX25_DEF_T1		10
-#define	AX25_DEF_T2		3
-#define	AX25_DEF_T3		300
-#define	AX25_DEF_N2		10
-#define AX25_DEF_IDLE		20
-#define AX25_DEF_PACLEN		256
-#define AX25_DEF_IPMAXQUEUE	2		/* 1 * ax25->window */
-#define	AX25_DEF_DIGI		(AX25_DIGI_INBAND|AX25_DIGI_XBAND)
+enum { 
+	AX25_STATE_0,
+	AX25_STATE_1,
+	AX25_STATE_2,
+	AX25_STATE_3,
+	AX25_STATE_4
+};
+
+#define	AX25_MAX_DEVICES	20	/* Max No of AX.25 devices */
+
+#define AX25_MODULUS 		8	/*  Standard AX.25 modulus */
+#define	AX25_EMODULUS		128	/*  Extended AX.25 modulus */
+
+enum {
+	AX25_VALUES_IPDEFMODE,	/* 0=DG 1=VC */
+	AX25_VALUES_AXDEFMODE,	/* 0=Normal 1=Extended Seq Nos */
+	AX25_VALUES_BACKOFF,	/* 0=None 1=Linear 2=Exponential */
+	AX25_VALUES_CONMODE,	/* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */
+	AX25_VALUES_WINDOW,	/* Default window size for standard AX.25 */
+	AX25_VALUES_EWINDOW,	/* Default window size for extended AX.25 */
+	AX25_VALUES_T1,		/* Default T1 timeout value */
+	AX25_VALUES_T2,		/* Default T2 timeout value */
+	AX25_VALUES_T3,		/* Default T3 timeout value */
+	AX25_VALUES_IDLE,	/* Connected mode idle timer */
+	AX25_VALUES_N2,		/* Default N2 value */
+	AX25_VALUES_PACLEN,	/* AX.25 MTU */
+	AX25_MAX_VALUES		/* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */
+};
+
+#define	AX25_DEF_IPDEFMODE	0			/* Datagram */
+#define	AX25_DEF_AXDEFMODE	0			/* Normal */
+#define	AX25_DEF_BACKOFF	1			/* Linear backoff */
+#define	AX25_DEF_CONMODE	2			/* Connected mode allowed */
+#define	AX25_DEF_WINDOW		2			/* Window=2 */
+#define	AX25_DEF_EWINDOW	32			/* Module-128 Window=32 */
+#define	AX25_DEF_T1		(10 * AX25_SLOWHZ)	/* T1=10s */
+#define	AX25_DEF_T2		(3 * AX25_SLOWHZ)	/* T2=3s  */
+#define	AX25_DEF_T3		(300 * AX25_SLOWHZ)	/* T3=300s */
+#define	AX25_DEF_N2		10			/* N2=10 */
+#define AX25_DEF_IDLE		(0 * 60 * AX25_SLOWHZ)	/* Idle=None */
+#define AX25_DEF_PACLEN		256			/* Paclen=256 */
 
 typedef struct ax25_uid_assoc {
-	struct ax25_uid_assoc *next;
-	uid_t uid;
-	ax25_address call;
+	struct ax25_uid_assoc	*next;
+	uid_t			uid;
+	ax25_address		call;
 } ax25_uid_assoc;
 
 typedef struct {
-	ax25_address calls[AX25_MAX_DIGIS];
-	unsigned char repeated[AX25_MAX_DIGIS];
-	unsigned char ndigi;
-	char lastrepeat;
+	ax25_address		calls[AX25_MAX_DIGIS];
+	unsigned char		repeated[AX25_MAX_DIGIS];
+	unsigned char		ndigi;
+	char			lastrepeat;
 } ax25_digi;
 
 typedef struct ax25_cb {
 	struct ax25_cb		*next;
 	ax25_address		source_addr, dest_addr;
 	struct device		*device;
-	unsigned char		dama_slave;	/* dl1bke 951121 */
-	unsigned char		state, modulus, hdrincl;
+	unsigned char		dama_slave, iamdigi;
+	unsigned char		state, modulus, pidincl;
 	unsigned short		vs, vr, va;
 	unsigned char		condition, backoff;
 	unsigned char		n2, n2count;
 	unsigned short		t1, t2, t3, idle, rtt;
 	unsigned short		t1timer, t2timer, t3timer, idletimer;
 	unsigned short		paclen;
-	unsigned short		maxqueue;
 	unsigned short		fragno, fraglen;
 	ax25_digi		*digipeat;
 	struct sk_buff_head	write_queue;
@@ -166,16 +180,30 @@
 	struct sock		*sk;		/* Backlink to socket */
 } ax25_cb;
 
+#ifndef _LINUX_SYSCTL_H
+#include <linux/sysctl.h>
+#endif
+
+struct ax25_dev {
+	char			name[20];
+	struct device		*dev;
+	struct device		*forward;
+	struct ctl_table	systable[AX25_MAX_VALUES+1];
+	int			values[AX25_MAX_VALUES];
+};
+
 /* af_ax25.c */
 extern ax25_address null_ax25_address;
 extern char *ax2asc(ax25_address *);
+extern ax25_address *asc2ax(char *);
 extern int  ax25cmp(ax25_address *, ax25_address *);
-extern int  ax25_send_frame(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct device *);
 extern void ax25_destroy_socket(ax25_cb *);
 extern struct device *ax25rtr_get_dev(ax25_address *);
 extern int  ax25_encapsulate(struct sk_buff *, struct device *, unsigned short,
 	void *, void *, unsigned int);
-extern int  ax25_rebuild_header(unsigned char *, struct device *, unsigned long, struct sk_buff *);
+extern int  ax25_rebuild_header(void *, struct device *, unsigned long, struct sk_buff *);
 extern ax25_uid_assoc *ax25_uid_list;
 extern int  ax25_uid_policy;
 extern ax25_address *ax25_findbyuid(uid_t);
@@ -188,7 +216,7 @@
 extern int  ax25_process_rx_frame(ax25_cb *, struct sk_buff *, int, int);
 
 /* ax25_out.c */
-extern void ax25_output(ax25_cb *, struct sk_buff *);
+extern void ax25_output(ax25_cb *, int, struct sk_buff *);
 extern void ax25_kick(ax25_cb *);
 extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int);
 extern void ax25_nr_error_recovery(ax25_cb *);
@@ -197,27 +225,25 @@
 extern void ax25_enquiry_response(ax25_cb *);
 extern void ax25_timeout_response(ax25_cb *);
 extern void ax25_check_iframes_acked(ax25_cb *, unsigned short);
-extern void ax25_check_need_response(ax25_cb *, int, int);
 extern void dama_enquiry_response(ax25_cb *);			/* dl1bke 960114 */
-extern void dama_check_need_response(ax25_cb *, int, int);	/* dl1bke 960114 */
 extern void dama_establish_data_link(ax25_cb *);
 
 /* ax25_route.c */
+extern struct ax25_dev ax25_device[];
 extern int  ax25_rt_get_info(char *, char **, off_t, int, int);
 extern int  ax25_cs_get_info(char *, char **, off_t, int, int);
 extern int  ax25_rt_autobind(ax25_cb *, ax25_address *);
-extern void ax25_rt_build_path(ax25_cb *, ax25_address *, struct device *);
-extern void ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *);
+extern ax25_digi *ax25_rt_find_path(ax25_address *, struct device *);
+extern void ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *);
 extern void ax25_rt_device_down(struct device *);
 extern int  ax25_rt_ioctl(unsigned int, void *);
-extern char ax25_ip_mode_get(ax25_address *, struct device *);
-extern unsigned short ax25_dev_get_value(struct device *, int);
+extern char ax25_rt_mode_get(ax25_address *, struct device *);
+extern int  ax25_dev_get_value(struct device *, int);
 extern void ax25_dev_device_up(struct device *);
 extern void ax25_dev_device_down(struct device *);
-extern int  ax25_dev_ioctl(unsigned int, void *);
-extern int  ax25_bpq_get_info(char *, char **, off_t, int, int);
-extern ax25_address *ax25_bpq_get_addr(struct device *);
-extern int  ax25_bpq_ioctl(unsigned int, void *);
+extern int  ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *);
+extern struct device *ax25_fwd_dev(struct device *);
+extern void ax25_rt_free(void);
 
 /* ax25_subr.c */
 extern void ax25_clear_queues(ax25_cb *);
@@ -235,16 +261,32 @@
 extern int  size_ax25_addr(ax25_digi *);
 extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
 extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
-extern int  ax25_queue_length(ax25_cb *, struct sk_buff *); /* dl1bke 960327 */
 extern void ax25_dama_on(ax25_cb *);	/* dl1bke 951121 */
 extern void ax25_dama_off(ax25_cb *);	/* dl1bke 951121 */
+extern void ax25_disconnect(ax25_cb *, int);
 
-/* ax25_timer */
+/* ax25_timer.c */
 extern void ax25_set_timer(ax25_cb *);
 extern void ax25_t1_timeout(ax25_cb *);
+extern void ax25_link_failed(ax25_cb *, int);
+extern int  (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
+extern int  ax25_listen_mine(ax25_address *, struct device *);
+
+/* sysctl_net_ax25.c */
+extern void ax25_register_sysctl(void);
+extern void ax25_unregister_sysctl(void);
 
 /* ... */
 
-extern ax25_cb * volatile ax25_list;
+extern ax25_cb *volatile ax25_list;
+
+/* support routines for modules that use AX.25, in ax25_timer.c */
+extern int  ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
+extern void ax25_protocol_release(unsigned int);
+extern int  ax25_linkfail_register(void (*)(ax25_cb *, int));
+extern void ax25_linkfail_release(void (*)(ax25_cb *, int));
+extern int  ax25_listen_register(ax25_address *, struct device *);
+extern void ax25_listen_release(ax25_address *, struct device *);
+extern int  ax25_protocol_is_registered(unsigned int);
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/include/net/netrom.h linux/include/net/netrom.h
--- v2.0.34/linux/include/net/netrom.h	Sat Aug 10 00:03:15 1996
+++ linux/include/net/netrom.h	Mon Jul 13 13:47:39 1998
@@ -3,61 +3,68 @@
  *
  *	Jonathan Naylor G4KLX	9/4/95
  */
- 
+
 #ifndef _NETROM_H
 #define _NETROM_H 
 #include <linux/netrom.h>
 
-#define	NR_T1CLAMPLO   (1 * PR_SLOWHZ)	/* If defined, clamp at 1 second **/
-#define	NR_T1CLAMPHI (300 * PR_SLOWHZ)	/* If defined, clamp at 30 seconds **/
+#define	NR_SLOWHZ			10	/* Run timing at 1/10 second */
+
+#define	NR_NETWORK_LEN			15
+#define	NR_TRANSPORT_LEN		5
 
-#define	NR_NETWORK_LEN		15
-#define	NR_TRANSPORT_LEN	5
- 
-#define	NR_PROTO_IP		0x0C
-
-#define	NR_PROTOEXT		0x00
-#define	NR_CONNREQ		0x01
-#define	NR_CONNACK		0x02
-#define	NR_DISCREQ		0x03
-#define	NR_DISCACK		0x04
-#define	NR_INFO			0x05
-#define	NR_INFOACK		0x06
-
-#define	NR_CHOKE_FLAG		0x80
-#define	NR_NAK_FLAG		0x40
-#define	NR_MORE_FLAG		0x20
+#define	NR_PROTO_IP			0x0C
+
+#define	NR_PROTOEXT			0x00
+#define	NR_CONNREQ			0x01
+#define	NR_CONNACK			0x02
+#define	NR_DISCREQ			0x03
+#define	NR_DISCACK			0x04
+#define	NR_INFO				0x05
+#define	NR_INFOACK			0x06
+
+#define	NR_CHOKE_FLAG			0x80
+#define	NR_NAK_FLAG			0x40
+#define	NR_MORE_FLAG			0x20
 
 /* Define Link State constants. */
 
-#define NR_STATE_0		0
-#define NR_STATE_1		1
-#define NR_STATE_2		2
-#define NR_STATE_3		3
-
-#define NR_DEFAULT_T1		(120 * PR_SLOWHZ)	/* Outstanding frames - 120 seconds */
-#define NR_DEFAULT_T2		(5   * PR_SLOWHZ)	/* Response delay     - 5 seconds */
-#define NR_DEFAULT_N2		3			/* Number of Retries */
-#define	NR_DEFAULT_T4		(180 * PR_SLOWHZ)	/* Transport Busy Delay */
-#define	NR_DEFAULT_WINDOW	4			/* Default Window Size	*/
-#define	NR_DEFAULT_OBS		6			/* Default Obsolescence Count */
-#define	NR_DEFAULT_QUAL		10			/* Default Neighbour Quality */
-#define	NR_DEFAULT_TTL		16			/* Default Time To Live */
-#define NR_MODULUS 		256
-#define NR_MAX_WINDOW_SIZE	127			/* Maximum Window Allowable */
-#define	NR_DEFAULT_PACLEN	236			/* Default Packet Length */
+#define NR_STATE_0			0
+#define NR_STATE_1			1
+#define NR_STATE_2			2
+#define NR_STATE_3			3
+
+#define	NR_COND_ACK_PENDING		0x01
+#define	NR_COND_REJECT			0x02
+#define	NR_COND_PEER_RX_BUSY		0x04
+#define	NR_COND_OWN_RX_BUSY		0x08
+
+#define NR_DEFAULT_T1			(120 * NR_SLOWHZ)	/* Outstanding frames - 120 seconds */
+#define NR_DEFAULT_T2			(5   * NR_SLOWHZ)	/* Response delay     - 5 seconds */
+#define NR_DEFAULT_N2			3			/* Number of Retries - 3 */
+#define	NR_DEFAULT_T4			(180 * NR_SLOWHZ)	/* Busy Delay - 180 seconds */
+#define	NR_DEFAULT_WINDOW		4			/* Default Window Size - 4 */
+#define	NR_DEFAULT_OBS			6			/* Default Obsolescence Count - 6 */
+#define	NR_DEFAULT_QUAL			10			/* Default Neighbour Quality - 10 */
+#define	NR_DEFAULT_TTL			16			/* Default Time To Live - 16 */
+#define	NR_DEFAULT_ROUTING		1			/* Is routing enabled ? */
+#define	NR_DEFAULT_FAILS		2			/* Link fails until route fails */
+
+#define NR_MODULUS 			256
+#define NR_MAX_WINDOW_SIZE		127			/* Maximum Window Allowable - 127 */
+#define	NR_MAX_PACKET_SIZE		236			/* Maximum Packet Length - 236 */
 
 typedef struct {
 	ax25_address		user_addr, source_addr, dest_addr;
 	struct device		*device;
 	unsigned char		my_index,   my_id;
 	unsigned char		your_index, your_id;
-	unsigned char		state, condition, bpqext, hdrincl;
+	unsigned char		state, condition, bpqext, window;
 	unsigned short		vs, vr, va, vl;
 	unsigned char		n2, n2count;
-	unsigned short		t1, t2, rtt;
+	unsigned short		t1, t2, t4;
 	unsigned short		t1timer, t2timer, t4timer;
-	unsigned short		fraglen, paclen;
+	unsigned short		fraglen;
 	struct sk_buff_head	ack_queue;
 	struct sk_buff_head	reseq_queue;
 	struct sk_buff_head	frag_queue;
@@ -68,11 +75,13 @@
 	struct nr_neigh *next;
 	ax25_address    callsign;
 	ax25_digi       *digipeat;
+	ax25_cb		*ax25;
 	struct device   *dev;
 	unsigned char   quality;
 	unsigned char   locked;
 	unsigned short  count;
 	unsigned int    number;
+	unsigned char	failed;
 };
 
 struct nr_route {
@@ -84,14 +93,23 @@
 struct nr_node {
 	struct nr_node  *next;
 	ax25_address    callsign;
-	char mnemonic[7];
+	char		mnemonic[7];
 	unsigned char   which;
 	unsigned char   count;
 	struct nr_route routes[3];
 };
 
 /* af_netrom.c */
-extern struct nr_parms_struct nr_default;
+extern int  sysctl_netrom_default_path_quality;
+extern int  sysctl_netrom_obsolescence_count_initialiser;
+extern int  sysctl_netrom_network_ttl_initialiser;
+extern int  sysctl_netrom_transport_timeout;
+extern int  sysctl_netrom_transport_maximum_tries;
+extern int  sysctl_netrom_transport_acknowledge_delay;
+extern int  sysctl_netrom_transport_busy_delay;
+extern int  sysctl_netrom_transport_requested_window_size;
+extern int  sysctl_netrom_routing_control;
+extern int  sysctl_netrom_link_fails_count;
 extern int  nr_rx_frame(struct sk_buff *, struct device *);
 extern void nr_destroy_socket(struct sock *);
 
@@ -118,10 +136,11 @@
 extern struct device *nr_dev_first(void);
 extern struct device *nr_dev_get(ax25_address *);
 extern int  nr_rt_ioctl(unsigned int, void *);
-extern void nr_link_failed(ax25_address *, struct device *);
+extern void nr_link_failed(ax25_cb *, int);
 extern int  nr_route_frame(struct sk_buff *, ax25_cb *);
 extern int  nr_nodes_get_info(char *, char **, off_t, int, int);
 extern int  nr_neigh_get_info(char *, char **, off_t, int, int);
+extern void nr_rt_free(void);
 
 /* nr_subr.c */
 extern void nr_clear_queues(struct sock *);
@@ -130,11 +149,18 @@
 extern int  nr_validate_nr(struct sock *, unsigned short);
 extern int  nr_in_rx_window(struct sock *, unsigned short);
 extern void nr_write_internal(struct sock *, int);
-extern void nr_transmit_dm(struct sk_buff *);
-extern unsigned short nr_calculate_t1(struct sock *);
-extern void nr_calculate_rtt(struct sock *);
+extern void nr_transmit_dm(struct sk_buff *, int);
 
-/* ax25_timer */
+/* nr_timer.c */
 extern void nr_set_timer(struct sock *);
+
+/* sysctl_net_netrom.c */
+extern void nr_register_sysctl(void);
+extern void nr_unregister_sysctl(void);
+
+/* nr_loopback.c */
+extern void nr_loopback_init(void);
+extern void nr_loopback_clear(void);
+extern int nr_loopback_queue(struct sk_buff *);
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/include/net/rose.h linux/include/net/rose.h
--- v2.0.34/linux/include/net/rose.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/rose.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,209 @@
+/*
+ *	Declarations of Rose type objects.
+ *
+ *	Jonathan Naylor G4KLX	25/8/96
+ */
+
+#ifndef _ROSE_H
+#define _ROSE_H 
+#include <linux/rose.h>
+
+#define	ROSE_SLOWHZ			10	/* Run timing at 1/10 second */
+
+#define	ROSE_ADDR_LEN			5
+
+#define	ROSE_MIN_LEN			3
+
+#define	ROSE_GFI			0x10
+#define	ROSE_Q_BIT			0x80
+#define	ROSE_D_BIT			0x40
+#define	ROSE_M_BIT			0x10
+
+#define	ROSE_CALL_REQUEST		0x0B
+#define	ROSE_CALL_ACCEPTED		0x0F
+#define	ROSE_CLEAR_REQUEST		0x13
+#define	ROSE_CLEAR_CONFIRMATION		0x17
+#define	ROSE_DATA			0x00
+#define	ROSE_INTERRUPT			0x23
+#define	ROSE_INTERRUPT_CONFIRMATION	0x27
+#define	ROSE_RR				0x01
+#define	ROSE_RNR			0x05
+#define	ROSE_REJ			0x09
+#define	ROSE_RESET_REQUEST		0x1B
+#define	ROSE_RESET_CONFIRMATION		0x1F
+#define	ROSE_REGISTRATION_REQUEST	0xF3
+#define	ROSE_REGISTRATION_CONFIRMATION	0xF7
+#define	ROSE_RESTART_REQUEST		0xFB
+#define	ROSE_RESTART_CONFIRMATION	0xFF
+#define	ROSE_DIAGNOSTIC			0xF1
+#define	ROSE_ILLEGAL			0xFD
+
+/* Define Link State constants. */
+
+#define ROSE_STATE_0			0		/* Ready */
+#define ROSE_STATE_1			1		/* Awaiting Call Accepted */
+#define ROSE_STATE_2			2		/* Awaiting Clear Confirmation */
+#define ROSE_STATE_3			3		/* Data Transfer */
+#define	ROSE_STATE_4			4		/* Awaiting Reset Confirmation */
+#define	ROSE_STATE_5			5		/* Deferred Call Acceptance */
+
+#define ROSE_DEFAULT_T0			(180 * ROSE_SLOWHZ)	/* Default T10 T20 value */
+#define ROSE_DEFAULT_T1			(200 * ROSE_SLOWHZ)	/* Default T11 T21 value */
+#define ROSE_DEFAULT_T2			(180 * ROSE_SLOWHZ)	/* Default T12 T22 value */
+#define	ROSE_DEFAULT_T3			(180 * ROSE_SLOWHZ)	/* Default T13 T23 value */
+#define	ROSE_DEFAULT_HB			(5 * ROSE_SLOWHZ)	/* Default Holdback value */
+#define	ROSE_DEFAULT_ROUTING		1			/* Default routing flag */
+#define	ROSE_DEFAULT_FAIL_TIMEOUT	(120 * ROSE_SLOWHZ)	/* Time until link considered usable */
+#define	ROSE_DEFAULT_MAXVC		50			/* Maximum number of VCs per neighbour */
+#define ROSE_DEFAULT_WINDOW_SIZE	3			/* Default window value */
+
+#define ROSE_MODULUS 			8
+#define	ROSE_MAX_PACKET_SIZE		251			/* Maximum Packet Size */
+
+#define	ROSE_COND_ACK_PENDING		0x01
+#define	ROSE_COND_PEER_RX_BUSY		0x02
+#define	ROSE_COND_OWN_RX_BUSY		0x04
+
+#define	FAC_NATIONAL			0x00
+#define	FAC_CCITT			0x0F
+
+#define	FAC_NATIONAL_RAND		0x7F
+#define	FAC_NATIONAL_FLAGS		0x3F
+#define	FAC_NATIONAL_DEST_DIGI		0xE9
+#define	FAC_NATIONAL_SRC_DIGI		0xEB
+
+#define	FAC_CCITT_DEST_NSAP		0xC9
+#define	FAC_CCITT_SRC_NSAP		0xCB
+
+struct rose_neigh {
+	struct rose_neigh	*next;
+	ax25_address		callsign;
+	ax25_digi		*digipeat;
+	ax25_cb			*ax25;
+	struct device		*dev;
+	unsigned short		count;
+	unsigned short		use;
+	unsigned int		number;
+	char			restarted;
+	char			dce_mode;
+	struct sk_buff_head	queue;
+	unsigned short		t0timer, ftimer;
+	struct timer_list	timer;
+};
+
+struct rose_node {
+	struct rose_node	*next;
+	rose_address		address;
+	unsigned short		mask;
+	unsigned char		count;
+	struct rose_neigh	*neighbour[3];
+};
+
+struct rose_route {
+	struct rose_route	*next;
+	rose_address		src_addr, dest_addr;
+	ax25_address		src_call, dest_call;
+	unsigned int		lci1, lci2;
+	struct rose_neigh	*neigh1, *neigh2;
+	unsigned int		rand;
+};
+
+struct rose_facilities {
+	rose_address		source_addr,   dest_addr;
+	ax25_address		source_call,   dest_call;
+	unsigned char		source_ndigis, dest_ndigis;
+	ax25_address		source_digi,   dest_digi;
+	unsigned int		rand;
+};
+
+typedef struct {
+	rose_address		source_addr,   dest_addr;
+	ax25_address		source_call,   dest_call;
+	unsigned char		source_ndigis, dest_ndigis;
+	ax25_address		source_digi,   dest_digi;
+	struct rose_neigh	*neighbour;
+	struct device		*device;
+	unsigned int		lci, rand;
+	unsigned char		state, condition, qbitincl, defer;
+	unsigned char		cause, diagnostic;
+	unsigned short		vs, vr, va, vl;
+	unsigned short		timer;
+	unsigned short		t1, t2, t3, hb;
+	struct sock		*sk;		/* Backlink to socket */
+} rose_cb;
+
+/* af_rose.c */
+extern ax25_address rose_callsign;
+extern int  sysctl_rose_restart_request_timeout;
+extern int  sysctl_rose_call_request_timeout;
+extern int  sysctl_rose_reset_request_timeout;
+extern int  sysctl_rose_clear_request_timeout;
+extern int  sysctl_rose_ack_hold_back_timeout;
+extern int  sysctl_rose_routing_control;
+extern int  sysctl_rose_link_fail_timeout;
+extern int  sysctl_rose_maximum_vcs;
+extern int  sysctl_rose_window_size;
+extern int  rosecmp(rose_address *, rose_address *);
+extern int  rosecmpm(rose_address *, rose_address *, unsigned short);
+extern char *rose2asc(rose_address *);
+extern void rose_kill_by_neigh(struct rose_neigh *);
+extern struct sock *rose_find_socket(unsigned int, struct rose_neigh *);
+extern unsigned int rose_new_lci(struct rose_neigh *);
+extern int  rose_rx_call_request(struct sk_buff *, struct device *, struct rose_neigh *, unsigned int);
+extern void rose_destroy_socket(struct sock *);
+
+/* rose_dev.c */
+extern int  rose_rx_ip(struct sk_buff *, struct device *);
+extern int  rose_init(struct device *);
+
+#include <net/rosecall.h>
+
+/* rose_in.c */
+extern int  rose_process_rx_frame(struct sock *, struct sk_buff *);
+
+/* rose_link.c */
+extern void rose_link_set_timer(struct rose_neigh *);
+extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short);
+extern void rose_transmit_restart_request(struct rose_neigh *);
+extern void rose_transmit_restart_confirmation(struct rose_neigh *);
+extern void rose_transmit_diagnostic(struct rose_neigh *, unsigned char);
+extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char);
+extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
+
+/* rose_out.c */
+extern void rose_kick(struct sock *);
+extern void rose_enquiry_response(struct sock *);
+extern void rose_check_iframes_acked(struct sock *, unsigned short);
+
+/* rose_route.c */
+extern void rose_rt_device_down(struct device *);
+extern void rose_link_device_down(struct device *);
+extern struct device *rose_dev_first(void);
+extern struct device *rose_dev_get(rose_address *);
+extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *);
+extern struct device *rose_ax25_dev_get(char *);
+extern struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, unsigned char *);
+extern int  rose_rt_ioctl(unsigned int, void *);
+extern void rose_link_failed(ax25_cb *, int);
+extern int  rose_route_frame(struct sk_buff *, ax25_cb *);
+extern int  rose_nodes_get_info(char *, char **, off_t, int, int);
+extern int  rose_neigh_get_info(char *, char **, off_t, int, int);
+extern int  rose_routes_get_info(char *, char **, off_t, int, int);
+extern void rose_rt_free(void);
+
+/* rose_subr.c */
+extern void rose_clear_queues(struct sock *);
+extern int  rose_validate_nr(struct sock *, unsigned short);
+extern void rose_write_internal(struct sock *, int);
+extern int  rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
+extern int  rose_parse_facilities(struct sk_buff *, struct rose_facilities *);
+extern int  rose_create_facilities(unsigned char *, rose_cb *);
+
+/* rose_timer.c */
+extern void rose_set_timer(struct sock *);
+
+/* sysctl_net_rose.c */
+extern void rose_register_sysctl(void);
+extern void rose_unregister_sysctl(void);
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/include/net/rosecall.h linux/include/net/rosecall.h
--- v2.0.34/linux/include/net/rosecall.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/rosecall.h	Mon Jul 13 13:47:39 1998
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void rose_proto_init(struct net_proto *pro);
diff -u --recursive --new-file v2.0.34/linux/include/net/route.h linux/include/net/route.h
--- v2.0.34/linux/include/net/route.h	Tue Mar 10 13:19:09 1998
+++ linux/include/net/route.h	Mon Jul 13 13:47:39 1998
@@ -131,12 +131,10 @@
 extern __inline__ void ip_rt_put(struct rtable * rt)
 #ifndef MODULE
 {
-	if (rt)
-		atomic_dec(&rt->rt_refcnt);
-
-	/* If this rtable entry is not in the cache, we'd better free it once the
-	 * refcnt goes to zero, because nobody else will... */
-	if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) )
+	/* If this rtable entry is not in the cache, we'd better free
+	 * it once the refcnt goes to zero, because nobody else will.
+	 */
+	if (rt&&atomic_dec_and_test(&rt->rt_refcnt)&&(rt->rt_flags&RTF_NOTCACHED))
 		rt_free(rt);
 }
 #else
diff -u --recursive --new-file v2.0.34/linux/include/net/sock.h linux/include/net/sock.h
--- v2.0.34/linux/include/net/sock.h	Mon Jul 13 13:46:42 1998
+++ linux/include/net/sock.h	Mon Jul 13 13:47:39 1998
@@ -42,11 +42,14 @@
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>	/* struct sk_buff */
 #include <net/protocol.h>		/* struct inet_protocol */
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <net/netrom.h>
 #endif
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <net/rose.h>
+#endif
 #endif
 
 #if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
@@ -267,12 +270,6 @@
 	int			sndbuf;
 	unsigned short		type;
 	unsigned char		localroute;	/* Route locally only */
-#ifdef CONFIG_AX25
-	ax25_cb			*ax25;
-#ifdef CONFIG_NETROM
-	nr_cb			*nr;
-#endif
-#endif
   
 /*
  *	This is where all the private (optional) areas that don't
@@ -282,6 +279,15 @@
 	union
 	{
 	  	struct unix_opt	af_unix;
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+		ax25_cb			*ax25;
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+		nr_cb			*nr;
+#endif
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+		rose_cb			*rose;
+#endif
+#endif
 #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
 		struct atalk_sock	af_at;
 #endif
diff -u --recursive --new-file v2.0.34/linux/init/main.c linux/init/main.c
--- v2.0.34/linux/init/main.c	Mon Jul 13 13:46:42 1998
+++ linux/init/main.c	Mon Jul 13 13:47:39 1998
@@ -182,6 +182,21 @@
 extern void baycom_setup(char *str, int *ints);
 #endif
 
+#ifdef CONFIG_PARIDE_PD
+extern void pd_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PF
+extern void pf_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PT
+extern void pt_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PG
+extern void pg_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PCD
+extern void pcd_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -255,10 +270,12 @@
 #endif
 }
 
-struct {
+struct kernel_param {
 	const char *str;
 	void (*setup_func)(char *, int *);
-} bootsetups[] = {
+} ;
+
+struct kernel_param bootsetups[] = {
 	{ "reserve=", reserve_setup },
 	{ "profile=", profile_setup },
 #ifdef CONFIG_BLK_DEV_RAM
@@ -444,6 +461,27 @@
 	{ 0, 0 }
 };
 
+static struct kernel_param raw_params[] = {
+
+#ifdef CONFIG_PARIDE_PD
+       { "pd.", pd_setup },
+#endif
+#ifdef CONFIG_PARIDE_PCD
+       { "pcd.", pcd_setup },
+#endif
+#ifdef CONFIG_PARIDE_PF
+       { "pf.", pf_setup },
+#endif
+#ifdef CONFIG_PARIDE_PT
+       { "pt.", pt_setup },
+#endif
+#ifdef CONFIG_PARIDE_PG
+       { "pg.", pg_setup },
+#endif
+       { 0, 0 }
+} ;
+
+
 #ifdef CONFIG_BLK_DEV_RAM
 static void ramdisk_start_setup(char *str, int *ints)
 {
@@ -491,6 +529,15 @@
 		}
 		i++;
 	}
+
+        for (i=0; raw_params[i].str; i++) {
+                int n = strlen(raw_params[i].str);
+                if (!strncmp(line,raw_params[i].str,n)) {
+                        raw_params[i].setup_func(line+n, NULL);
+                        return 1;
+                }
+        }
+
 	return 0;
 }
 
@@ -563,6 +610,7 @@
 		const int num;
 	} devices[] = {
 		{ "nfs",     0x00ff },
+		{ "loop",    0x0700 },
 		{ "hda",     0x0300 },
 		{ "hdb",     0x0340 },
 		{ "hdc",     0x1600 },
@@ -599,6 +647,18 @@
 		{ "gscd",    0x1000 },
 		{ "sbpcd",   0x1900 },
 		{ "sonycd",  0x1800 },
+#ifdef CONFIG_PARIDE_PD
+       		{ "pda",     0x2d00 },
+       		{ "pdb",     0x2d10 },
+       		{ "pdc",     0x2d20 },
+       		{ "pdd",     0x2d30 },
+#endif
+#ifdef CONFIG_PARIDE_PCD
+     	  	{ "pcd",     0x2e00 },
+#endif
+#ifdef CONFIG_PARIDE_PF
+	       	{ "pf",      0x2f00 },
+#endif
 		{ NULL, 0 }
 	};
 
diff -u --recursive --new-file v2.0.34/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.0.34/linux/kernel/ksyms.c	Mon Jul 13 13:46:42 1998
+++ linux/kernel/ksyms.c	Mon Jul 13 13:47:40 1998
@@ -203,6 +203,9 @@
 	X(blkdev_release),
 	X(gendisk_head),
 	X(resetup_one_dev),
+	X(unplug_device),
+	X(make_request),
+	X(tq_disk),
 
 #ifdef CONFIG_SERIAL	
 	/* Module creation of serial units */
@@ -236,6 +239,11 @@
 	/* sysctl table registration */
 	X(register_sysctl_table),
 	X(unregister_sysctl_table),
+	X(sysctl_string),
+	X(sysctl_intvec),
+	X(proc_dostring),
+	X(proc_dointvec),
+	X(proc_dointvec_minmax),
 
 	/* interrupt handling */
 	X(request_irq),
diff -u --recursive --new-file v2.0.34/linux/kernel/sched.c linux/kernel/sched.c
--- v2.0.34/linux/kernel/sched.c	Tue Dec  2 13:52:33 1997
+++ linux/kernel/sched.c	Mon Jul 13 13:47:40 1998
@@ -87,6 +87,7 @@
 #define _S(nr) (1<<((nr)-1))
 
 extern void mem_use(void);
+extern unsigned long get_wchan(struct task_struct *);
 
 static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, };
 unsigned long init_user_stack[1024] = { STACK_MAGIC, };
@@ -1681,11 +1682,13 @@
 		printk(" current  ");
 	else
 		printk(" %08lX ", thread_saved_pc(&p->tss));
+	printk("%08lX ", get_wchan(p));
 #else
 	if (p == current)
 		printk("   current task   ");
 	else
 		printk(" %016lx ", thread_saved_pc(&p->tss));
+	printk("%08lX ", get_wchan(p) & 0xffffffffL);
 #endif
 	for (free = 1; free < PAGE_SIZE/sizeof(long) ; free++) {
 		if (((unsigned long *)p->kernel_stack_page)[free])
@@ -1712,12 +1715,12 @@
 
 #if ((~0UL) == 0xffffffff)
 	printk("\n"
-	       "                         free                        sibling\n");
-	printk("  task             PC    stack   pid father child younger older\n");
+	       "                                  free                        sibling\n");
+	printk("  task             PC     wchan   stack   pid father child younger older\n");
 #else
 	printk("\n"
-	       "                                 free                        sibling\n");
-	printk("  task                 PC        stack   pid father child younger older\n");
+	       "                                           free                        sibling\n");
+	printk("  task                 PC         wchan    stack   pid father child younger older\n");
 #endif
 	for (i=0 ; i<NR_TASKS ; i++)
 		if (task[i])
diff -u --recursive --new-file v2.0.34/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.0.34/linux/kernel/sysctl.c	Mon Jul 13 13:46:42 1998
+++ linux/kernel/sysctl.c	Mon Jul 13 13:47:40 1998
@@ -397,9 +397,11 @@
 /* Scan the sysctl entries in table and add them all into /proc */
 static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
 {
-	struct proc_dir_entry *de;
+	struct proc_dir_entry *de, *tmp;
+	int exists;
 	
 	for (; table->ctl_name; table++) {
+		exists = 0;
 		/* Can't do anything without a proc name. */
 		if (!table->procname)
 			continue;
@@ -428,12 +430,24 @@
 		}
 		/* Otherwise it's a subdir */
 		else  {
-			de->ops = &proc_dir_inode_operations;
-			de->nlink++;
-			de->mode |= S_IFDIR;
+			/* First check to see if it already exists */
+			for (tmp = root->subdir; tmp; tmp = tmp->next) {
+				if (tmp->namelen == de->namelen &&
+				    !memcmp(tmp->name,de->name,de->namelen)) {
+					exists = 1;
+					kfree (de);
+					de = tmp;
+				}
+			}
+			if (!exists) {
+				de->ops = &proc_dir_inode_operations;
+				de->nlink++;
+				de->mode |= S_IFDIR;
+			}
 		}
 		table->de = de;
-		proc_register_dynamic(root, de);
+		if (!exists)
+			proc_register_dynamic(root, de);
 		if (de->mode & S_IFDIR )
 			register_proc_table(table->child, de);
 	}
@@ -452,9 +466,13 @@
 			}
 			unregister_proc_table(table->child, de);
 		}
-		proc_unregister(root, de->low_ino);
-		table->de = NULL;
-		kfree(de);			
+		/* Don't unregister proc directories which still have
+		   entries... */
+		if (!((de->mode & S_IFDIR) && de->subdir)) {
+			proc_unregister(root, de->low_ino);
+			table->de = NULL;
+			kfree(de);			
+		}
 	}
 }
 
diff -u --recursive --new-file v2.0.34/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.0.34/linux/mm/page_alloc.c	Tue Mar 10 13:19:09 1998
+++ linux/mm/page_alloc.c	Mon Jul 13 13:47:40 1998
@@ -27,6 +27,8 @@
 int nr_swap_pages = 0;
 int nr_free_pages = 0;
 
+extern struct wait_queue *buffer_wait;
+
 /*
  * Free area management
  *
@@ -120,6 +122,9 @@
 #undef list
 
 	restore_flags(flags);
+	if (!waitqueue_active(&buffer_wait))
+		return;
+	wake_up(&buffer_wait);
 }
 
 void __free_page(struct page *page)
diff -u --recursive --new-file v2.0.34/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.0.34/linux/mm/vmscan.c	Mon Jul 13 13:46:42 1998
+++ linux/mm/vmscan.c	Mon Jul 13 13:47:40 1998
@@ -265,14 +265,15 @@
 	 * Go through process' page directory.
 	 */
 	address = p->swap_address;
-	p->swap_address = 0;
 
 	/*
 	 * Find the proper vm-area
 	 */
 	vma = find_vma(p->mm, address);
-	if (!vma)
+	if (!vma) {
+		p->swap_address = 0;
 		return 0;
+	}
 	if (address < vma->vm_start)
 		address = vma->vm_start;
 
diff -u --recursive --new-file v2.0.34/linux/net/Changes linux/net/Changes
--- v2.0.34/linux/net/Changes	Sat May 11 00:08:34 1996
+++ linux/net/Changes	Mon Jul 13 13:47:40 1998
@@ -23,7 +23,7 @@
 o	eql load balancing driver.			[TESTED]
 o	Token ring drivers.				[TESTED]
 o	IPIP and tunnels				[TESTED]
-o	Fix ethernet/token ring promisc broadcast error [TESTED]
+o	Fix Ethernet/token ring promisc broadcast error [TESTED]
 	(pkt_type set to OTHERHOST in error).
 o	Fixed bug in the routing caches			[TESTED]
 o	Protocol header cache support			[TESTED]
@@ -100,7 +100,7 @@
 
 ------->>>>>  NET3 030  <<<<<----------
 
-o	Long word align ethernet IP headers (64byte align for pentium) [TESTED]
+o	Long word align Ethernet IP headers (64byte align for Pentium) [TESTED]
 		(less helpful than I'd have liked)
 o	Fixed variable length header support to really work	[TESTED]
 o	Mend appletalk/ipx partially			[TESTED]
@@ -132,7 +132,7 @@
 	slows fragmented I/O down, speeds up smaller
 	packets. UDP send ttcp can now touch 7.5Mbyte/sec
 	with nothing else going on. UDP recv is slower 8( [TESTED]
-o	Fixed and enabled ethernet header caches	[TESTED]
+o	Fixed and enabled Ethernet header caches	[TESTED]
 o	Removed junk from igmp				[TESTED]
 o	Obscure UDP/copy&sum bug fix			[TESTED]
 o	Fixed multicast					[TESTED]
@@ -195,7 +195,7 @@
 o	AF_UNIX smarter buffer driving			[TESTED]
 o	AF_UNIX full BSD semantics on STREAM writes	[TESTED]
 o	IOVEC's support repeated calls to copy more	[TESTED]
-o	Zero fragment 'solaris nfs' bug fixed <Werner>  [TESTED]
+o	Zero fragment 'Solaris NFS' bug fixed <Werner>  [TESTED]
 o	NetROM supports sendmsg/recvmsg			[TESTED]
 o	Sendmsg verify_iovec bugfix			[TESTED]
 o	ARP PERM is really permanent now <Craig>	[TESTED]
@@ -415,8 +415,8 @@
 lose one frame per window full of data without measurable speed loss.
 
 4.	RFC1323. These are the extensions for very fast nets.
-RFC1323 will be useful for Linux talking to systems over 100Mb/sec
-ethernet and over ATM as it allows large windows and protects from some
+RFC1323 will be useful for Linux talking to systems over 100 Mb/sec
+Ethernet and over ATM as it allows large windows and protects from some
 potential high speed TCP problems.
 
 6.	Delayed ack. This is mostly supported but not actually set up and
diff -u --recursive --new-file v2.0.34/linux/net/Config.in linux/net/Config.in
--- v2.0.34/linux/net/Config.in	Tue Aug 12 11:30:22 1997
+++ linux/net/Config.in	Mon Jul 13 13:47:40 1998
@@ -12,10 +12,10 @@
 comment ' '
 tristate 'The IPX protocol' CONFIG_IPX
 tristate 'Appletalk DDP' CONFIG_ATALK
-bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
-if [ "$CONFIG_AX25" = "y" ]; then
-  bool 'AX.25 over Ethernet' CONFIG_BPQETHER
-  bool 'Amateur Radio NET/ROM' CONFIG_NETROM
+tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
+if [ "$CONFIG_AX25" != "n" ]; then
+  dep_tristate 'Amateur Radio NET/ROM' CONFIG_NETROM $CONFIG_AX25
+  dep_tristate 'Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
diff -u --recursive --new-file v2.0.34/linux/net/Makefile linux/net/Makefile
--- v2.0.34/linux/net/Makefile	Thu May 16 06:35:55 1996
+++ linux/net/Makefile	Mon Jul 13 13:47:40 1998
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 MOD_SUB_DIRS := ipv4
-ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipx unix appletalk netrom #decnet
+ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipx unix appletalk netrom rose #decnet
 SUB_DIRS     := core ethernet unix
 MOD_LIST_NAME := NET_MISC_MODULES
 
@@ -42,10 +42,26 @@
 
 ifeq ($(CONFIG_NETROM),y)
 SUB_DIRS += netrom
+else
+  ifeq ($(CONFIG_NETROM),m)
+  MOD_SUB_DIRS += netrom
+  endif
+endif
+
+ifeq ($(CONFIG_ROSE),y)
+SUB_DIRS += rose
+else
+  ifeq ($(CONFIG_ROSE),m)
+  MOD_SUB_DIRS += rose
+  endif
 endif
 
 ifeq ($(CONFIG_AX25),y)
 SUB_DIRS += ax25
+else
+  ifeq ($(CONFIG_AX25),m)
+  MOD_SUB_DIRS += ax25
+  endif
 endif
 
 L_TARGET     := network.a
diff -u --recursive --new-file v2.0.34/linux/net/TUNABLE linux/net/TUNABLE
--- v2.0.34/linux/net/TUNABLE	Mon May  6 02:26:16 1996
+++ linux/net/TUNABLE	Mon Jul 13 13:47:40 1998
@@ -11,7 +11,7 @@
 MAX_LINKS		Maximum number of netlink minor devices. (1-32)
 MAX_QBYTES		Size of a netlink device queue (tunable)
 RIF_TABLE_SIZE		Token ring RIF cache size (tunable)
-AARP_HASH_SIZE		Size of appletalk hash table (tunable)
+AARP_HASH_SIZE		Size of Appletalk hash table (tunable)
 AX25_DEF_T1		AX.25 parameters. These are all tunable via
 AX25_DEF_T2		SIOCAX25SETPARMS
 AX25_DEF_T3		T1-T3,N2 have the meanings in the specification
diff -u --recursive --new-file v2.0.34/linux/net/ax25/Makefile linux/net/ax25/Makefile
--- v2.0.34/linux/net/ax25/Makefile	Mon Apr  1 21:43:08 1996
+++ linux/net/ax25/Makefile	Mon Jul 13 13:47:40 1998
@@ -1,5 +1,5 @@
 #
-# Makefile for the Linux TCP/IP (INET) layer.
+# Makefile for the Linux AX.25 layer.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -9,11 +9,10 @@
 
 
 O_TARGET := ax25.o
-O_OBJS	 := af_ax25.o sysctl_net_ax25.o
+O_OBJS	 := sysctl_net_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
+M_OBJS   := $(O_TARGET)
 
-ifdef CONFIG_AX25
-O_OBJS	+= ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
-endif
+OX_OBJS  += af_ax25.o
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.0.34/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.0.34/linux/net/ax25/af_ax25.c	Mon Jul 13 13:46:42 1998
+++ linux/net/ax25/af_ax25.c	Mon Jul 13 13:47:40 1998
@@ -1,8 +1,5 @@
 /*
- *	AX.25 release 032
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	AX.25 release 035
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -81,16 +78,23 @@
  *      AX.25 032	Joerg(DL1BKE)		Fixed DAMA timeout error.
  *						ax25_send_frame() limits the number of enqueued
  *						datagrams per socket.
- *			Jonathan(G4KLX)		Remove auto-router.
+ *	AX.25 033	Jonathan(G4KLX)		Removed auto-router.
+ *			Hans(PE1AYX)		Converted to Module.
+ *			Joerg(DL1BKE)		Moved BPQ Ethernet to seperate driver.
+ *						Fixed 2.0.x specific IP over AX.25 problem.
+ *	AX.25 035	Frederic(F1OAT)		Support for pseudo-digipeating.
+ *			Jonathan(G4KLX)		Support for packet forwarding.
+ *			Jonathan(G4KLX)		Fix wildcard listening parameter setting.
  *
  *	To do:
  *		Restructure the ax25_rcv code to be cleaner/faster and
  *		copy only when needed.
  *		Consider better arbitrary protocol support.
  */
- 
+
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -143,14 +147,14 @@
 
 		if (c != ' ') *s++ = c;
 	}
-	
+
 	*s++ = '-';
 
 	if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
 		*s++ = '1';
 		n -= 10;
 	}
-	
+
 	*s++ = n + '0';
 	*s++ = '\0';
 
@@ -162,6 +166,42 @@
 }
 
 /*
+ *	ascii -> ax25 conversion
+ */
+ax25_address *asc2ax(char *callsign)
+{
+	static ax25_address addr;
+	char *s;
+	int n;
+
+	for (s = callsign, n = 0; n < 6; n++) {
+		if (*s != '\0' && *s != '-')
+			addr.ax25_call[n] = *s++;
+		else
+			addr.ax25_call[n] = ' ';
+		addr.ax25_call[n] <<= 1;
+		addr.ax25_call[n] &= 0xFE;
+	}
+
+	if (*s++ == '\0') {
+		addr.ax25_call[6] = 0x00;
+		return &addr;
+	}
+
+	addr.ax25_call[6] = *s++ - '0';
+
+	if (*s != '\0') {
+		addr.ax25_call[6] *= 10;
+		addr.ax25_call[6] += *s++ - '0';
+	}
+
+	addr.ax25_call[6] <<= 1;
+	addr.ax25_call[6] &= 0x1E;
+
+	return &addr;
+}
+
+/*
  *	Compare two ax.25 addresses
  */
 int ax25cmp(ax25_address *a, ax25_address *b)
@@ -181,13 +221,49 @@
 }
 
 /*
+ *	Compare two AX.25 digipeater paths.
+ */
+static int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2)
+{
+	int i;
+
+	if (digi1->ndigi != digi2->ndigi)
+		return 1;
+
+	if (digi1->lastrepeat != digi2->lastrepeat)
+		return 1;
+
+	for (i = 0; i < digi1->ndigi; i++)
+		if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0)
+			return 1;
+
+	return 0;
+}
+
+/*
+ *	Free an allocated ax25 control block. This is done to centralise
+ *	the MOD count code.
+ */
+static void ax25_free_cb(ax25_cb *ax25)
+{
+	if (ax25->digipeat != NULL) {
+		kfree_s(ax25->digipeat, sizeof(ax25_digi));
+		ax25->digipeat = NULL;
+	}
+
+	kfree_s(ax25, sizeof(ax25_cb));
+
+	MOD_DEC_USE_COUNT;
+}
+
+/*
  *	Socket removal during an interrupt is now safe.
  */
 static void ax25_remove_socket(ax25_cb *ax25)
 {
 	ax25_cb *s;
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
 
@@ -216,18 +292,11 @@
 static void ax25_kill_by_device(struct device *dev)
 {
 	ax25_cb *s;
-	
+
 	for (s = ax25_list; s != NULL; s = s->next) {
 		if (s->device == dev) {
-			s->state  = AX25_STATE_0;
 			s->device = NULL;
-			if (s->sk != NULL) {
-				s->sk->state = TCP_CLOSE;
-				s->sk->err   = ENETUNREACH;
-				if (!s->sk->dead)
-					s->sk->state_change(s->sk);
-				s->sk->dead  = 1;
-			}
+			ax25_disconnect(s, ENETUNREACH);
 		}
 	}
 }
@@ -239,6 +308,10 @@
 {
 	struct device *dev = (struct device *)ptr;
 
+	/* Reject non AX.25 devices */
+	if (dev->type != ARPHRD_AX25)
+		return NOTIFY_DONE;
+
 	switch (event) {
 		case NETDEV_UP:
 			ax25_dev_device_up(dev);
@@ -275,7 +348,7 @@
  *	Find a socket that wants to accept the SABM we have just
  *	received.
  */
-static struct sock *ax25_find_listener(ax25_address *addr, struct device *dev, int type)
+static struct sock *ax25_find_listener(ax25_address *addr, int digi, struct device *dev, int type)
 {
 	unsigned long flags;
 	ax25_cb *s;
@@ -284,6 +357,8 @@
 	cli();
 
 	for (s = ax25_list; s != NULL; s = s->next) {
+		if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
+			continue;
 		if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) {
 			/* If device is null we match any device */
 			if (s->device == NULL || s->device == dev) {
@@ -324,7 +399,7 @@
  *	Find an AX.25 control block given both ends. It will only pick up
  *	floating AX.25 control blocks or non Raw socket bound control blocks.
  */
-static ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, struct device *dev)
+ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev)
 {
 	ax25_cb *s;
 	unsigned long flags;
@@ -335,7 +410,16 @@
 	for (s = ax25_list; s != NULL; s = s->next) {
 		if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
 			continue;
-		if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->device == dev) {
+		if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->device == dev) {
+			if (digi != NULL && digi->ndigi != 0) {
+				if (s->digipeat == NULL)
+					continue;
+				if (ax25digicmp(s->digipeat, digi) != 0)
+					continue;
+			} else {
+				if (s->digipeat != NULL && s->digipeat->ndigi != 0)
+					continue;
+			}
 			restore_flags(flags);
 			return s;
 		}
@@ -372,22 +456,19 @@
 static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
 {
 	struct sk_buff *copy;
-	
+
 	while (sk != NULL) {
 		if (sk->type == SOCK_RAW && sk->protocol == proto && sk->rmem_alloc <= sk->rcvbuf) {
 			if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
 				return;
 
-			copy->sk = sk;
-			atomic_add(copy->truesize, &sk->rmem_alloc);
-			skb_queue_tail(&sk->receive_queue, copy);
-			if (!sk->dead)
-				sk->data_ready(sk, skb->len);
+			if (sock_queue_rcv_skb(sk, copy) != 0)
+				kfree_skb(copy, FREE_READ);
 		}
 
 		sk = sk->next;
 	}
-}	
+}
 
 /*
  *	Deferred destroy.
@@ -412,49 +493,40 @@
 {
 	struct sk_buff *skb;
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
-	
+
 	del_timer(&ax25->timer);
-	
+
 	ax25_remove_socket(ax25);
 	ax25_clear_queues(ax25);	/* Flush the queues */
-	
+
 	if (ax25->sk != NULL) {
 		while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) {
 			if (skb->sk != ax25->sk) {			/* A pending connection */
 				skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
-				ax25_set_timer(skb->sk->ax25);
-				skb->sk->ax25->state = AX25_STATE_0;
+				ax25_set_timer(skb->sk->protinfo.ax25);
+				skb->sk->protinfo.ax25->state = AX25_STATE_0;
 			}
 
 			kfree_skb(skb, FREE_READ);
 		}
 	}
-	
+
 	if (ax25->sk != NULL) {
-		if (ax25->sk->wmem_alloc || ax25->sk->rmem_alloc) { /* Defer: outstanding buffers */
+		if (ax25->sk->wmem_alloc != 0 || ax25->sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
 			init_timer(&ax25->timer);
 			ax25->timer.expires  = jiffies + 10 * HZ;
 			ax25->timer.function = ax25_destroy_timer;
 			ax25->timer.data     = (unsigned long)ax25;
 			add_timer(&ax25->timer);
 		} else {
-			if (ax25->digipeat != NULL) {
-				kfree_s(ax25->digipeat, sizeof(ax25_digi));
-				ax25->digipeat = NULL;
-			}
-		
 			sk_free(ax25->sk);
-			kfree_s(ax25, sizeof(*ax25));
+			ax25_free_cb(ax25);
 		}
 	} else {
-		if (ax25->digipeat != NULL) {
-			kfree_s(ax25->digipeat, sizeof(ax25_digi));
-			ax25->digipeat = NULL;
-		}	
-		kfree_s(ax25, sizeof(*ax25));
+		ax25_free_cb(ax25);
 	}
 
 	restore_flags(flags);
@@ -471,7 +543,7 @@
 ax25_address *ax25_findbyuid(uid_t uid)
 {
 	ax25_uid_assoc *a;
-	
+
 	for (a = ax25_uid_list; a != NULL; a = a->next) {
 		if (a->uid == uid)
 			return &a->call;
@@ -483,7 +555,7 @@
 static int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
 {
 	ax25_uid_assoc *a;
-	
+
 	switch (cmd) {
 		case SIOCAX25GETUID:
 			for (a = ax25_uid_list; a != NULL; a = a->next) {
@@ -493,10 +565,12 @@
 			return -ENOENT;
 
 		case SIOCAX25ADDUID:
-			if(!suser())
+			if (!suser())
 				return -EPERM;
 			if (ax25_findbyuid(sax->sax25_uid))
 				return -EEXIST;
+			if (sax->sax25_uid == 0)
+				return -EINVAL;
 			a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL);
 			if (a == NULL)
 				return -ENOMEM;
@@ -508,8 +582,8 @@
 
 		case SIOCAX25DELUID: {
 			ax25_uid_assoc **l;
-			
-			if(!suser())
+
+			if (!suser())
 				return -EPERM;
 			l = &ax25_uid_list;
 			while ((*l) != NULL) {
@@ -519,12 +593,12 @@
 					kfree_s(a, sizeof(*a));
 					return 0;
 				}
-				
+
 				l = &((*l)->next);
 			}
 			return -ENOENT;
 		}
-		
+
 		default:
 			return -EINVAL;
 	}
@@ -544,55 +618,41 @@
 	ax25_cb *ax25;
 	unsigned long flags;
 	int err;
-	
+
 	if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0)
 		return err;
 
 	memcpy_fromfs(&ax25_ctl, arg, sizeof(ax25_ctl));
-	
+
 	if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL)
 		return -ENODEV;
 
-	if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, dev)) == NULL)
+	if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, NULL, dev)) == NULL)
 		return -ENOTCONN;
 
 	switch (ax25_ctl.cmd) {
 		case AX25_KILL:
-#ifdef CONFIG_NETROM
-			nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
-				
-			ax25->state = AX25_STATE_0;
-			if (ax25->sk != NULL) {
-				ax25->sk->state = TCP_CLOSE;
-				ax25->sk->err   = ENETRESET;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead  = 1;
-			}
-
-			ax25_dama_off(ax25);
+			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+			ax25_disconnect(ax25, ENETRESET);
 			ax25_set_timer(ax25);
 	  		break;
 
 	  	case AX25_WINDOW:
-	  		if (ax25->modulus == MODULUS) {
-	  			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) 
+	  		if (ax25->modulus == AX25_MODULUS) {
+	  			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
 	  				return -EINVAL;
 	  		} else {
-	  			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) 
+	  			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
 	  				return -EINVAL;
 	  		}
 	  		ax25->window = ax25_ctl.arg;
 	  		break;
 
 	  	case AX25_T1:
-  			if (ax25_ctl.arg < 1) 
+  			if (ax25_ctl.arg < 1)
   				return -EINVAL;
-  			ax25->rtt = (ax25_ctl.arg * PR_SLOWHZ) / 2;
-  			ax25->t1 = ax25_ctl.arg * PR_SLOWHZ;
+  			ax25->rtt = (ax25_ctl.arg * AX25_SLOWHZ) / 2;
+  			ax25->t1  = ax25_ctl.arg * AX25_SLOWHZ;
   			save_flags(flags); cli();
   			if (ax25->t1timer > ax25->t1)
   				ax25->t1timer = ax25->t1;
@@ -600,66 +660,101 @@
   			break;
 
 	  	case AX25_T2:
-	  		if (ax25_ctl.arg < 1) 
+	  		if (ax25_ctl.arg < 1)
 	  			return -EINVAL;
 	  		save_flags(flags); cli();
-	  		ax25->t2 = ax25_ctl.arg * PR_SLOWHZ;
+	  		ax25->t2 = ax25_ctl.arg * AX25_SLOWHZ;
 	  		if (ax25->t2timer > ax25->t2)
 	  			ax25->t2timer = ax25->t2;
 	  		restore_flags(flags);
 	  		break;
 
 	  	case AX25_N2:
-	  		if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) 
+	  		if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
 	  			return -EINVAL;
 	  		ax25->n2count = 0;
 	  		ax25->n2 = ax25_ctl.arg;
 	  		break;
 
 	  	case AX25_T3:
-	  		if (ax25_ctl.arg < 0) 
+	  		if (ax25_ctl.arg < 0)
 	  			return -EINVAL;
 	  		save_flags(flags); cli();
-	  		ax25->t3 = ax25_ctl.arg * PR_SLOWHZ;
+	  		ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ;
 	  		if (ax25->t3timer != 0)
 	  			ax25->t3timer = ax25->t3;
 	  		restore_flags(flags);
 	  		break;
 
 	  	case AX25_IDLE:
-	  		if (ax25_ctl.arg < 0) 
+	  		if (ax25_ctl.arg < 0)
 	  			return -EINVAL;
 			save_flags(flags); cli();
-	  		ax25->idle = ax25_ctl.arg * PR_SLOWHZ * 60;
+	  		ax25->idle = ax25_ctl.arg * AX25_SLOWHZ * 60;
 	  		if (ax25->idletimer != 0)
 	  			ax25->idletimer = ax25->idle;
 	  		restore_flags(flags);
 	  		break;
 
 	  	case AX25_PACLEN:
-	  		if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) 
+	  		if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
 	  			return -EINVAL;
-#if 0
-	  		if (ax25_ctl.arg > 256) /* we probably want this */
-	  			printk(KERN_WARNING "ax25_ctl_ioctl: Warning --- huge paclen %d\n", (int)ax25_ctl.arg);
-#endif	  			
 	  		ax25->paclen = ax25_ctl.arg;
 	  		break;
 
-	  	case AX25_IPMAXQUEUE:
-	  		if (ax25_ctl.arg < 1)
-	  			return -EINVAL;
-	  		ax25->maxqueue = ax25_ctl.arg;
-	  		break;
-
 	  	default:
 	  		return -EINVAL;
 	  }
-	  
+
 	  return 0;
 }
 
 /*
+ *	Fill in a created AX.25 created control block with the default
+ *	values for a particular device.
+ */
+static void ax25_fillin_cb(ax25_cb *ax25, struct device *dev)
+{
+	ax25->device = dev;
+
+	if (dev != NULL) {
+		ax25->rtt     = ax25_dev_get_value(dev, AX25_VALUES_T1) / 2;
+		ax25->t1      = ax25_dev_get_value(dev, AX25_VALUES_T1);
+		ax25->t2      = ax25_dev_get_value(dev, AX25_VALUES_T2);
+		ax25->t3      = ax25_dev_get_value(dev, AX25_VALUES_T3);
+		ax25->n2      = ax25_dev_get_value(dev, AX25_VALUES_N2);
+		ax25->paclen  = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
+		ax25->idle    = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
+		ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF);
+
+		if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) {
+			ax25->modulus = AX25_EMODULUS;
+			ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+		} else {
+			ax25->modulus = AX25_MODULUS;
+			ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+		}
+	} else {
+		ax25->rtt     = AX25_DEF_T1 / 2;
+		ax25->t1      = AX25_DEF_T1;
+		ax25->t2      = AX25_DEF_T2;
+		ax25->t3      = AX25_DEF_T3;
+		ax25->n2      = AX25_DEF_N2;
+		ax25->paclen  = AX25_DEF_PACLEN;
+		ax25->idle    = AX25_DEF_IDLE;
+		ax25->backoff = AX25_DEF_BACKOFF;
+
+		if (AX25_DEF_AXDEFMODE) {
+			ax25->modulus = AX25_EMODULUS;
+			ax25->window  = AX25_DEF_EWINDOW;
+		} else {
+			ax25->modulus = AX25_MODULUS;
+			ax25->window  = AX25_DEF_WINDOW;
+		}
+	}
+}
+
+/*
  * Create an empty AX.25 control block.
  */
 static ax25_cb *ax25_create_cb(void)
@@ -669,6 +764,10 @@
 	if ((ax25 = (ax25_cb *)kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
 		return NULL;
 
+	MOD_INC_USE_COUNT;
+
+	memset(ax25, 0x00, sizeof(*ax25));
+
 	skb_queue_head_init(&ax25->write_queue);
 	skb_queue_head_init(&ax25->frag_queue);
 	skb_queue_head_init(&ax25->ack_queue);
@@ -676,47 +775,9 @@
 
 	init_timer(&ax25->timer);
 
-	ax25->dama_slave = 0;
-
-	ax25->rtt     = (AX25_DEF_T1 * PR_SLOWHZ) / 2;
-	ax25->t1      = AX25_DEF_T1 * PR_SLOWHZ;
-	ax25->t2      = AX25_DEF_T2 * PR_SLOWHZ;
-	ax25->t3      = AX25_DEF_T3 * PR_SLOWHZ;
-	ax25->n2      = AX25_DEF_N2;
-	ax25->paclen  = AX25_DEF_PACLEN;
-	ax25->maxqueue= AX25_DEF_IPMAXQUEUE;
-	ax25->idle    = AX25_DEF_IDLE;
-
-	ax25->modulus   = AX25_DEF_AXDEFMODE;
-	ax25->fragno    = 0;
-	ax25->fraglen   = 0;
-	ax25->hdrincl   = 0;
-	ax25->backoff   = AX25_DEF_BACKOFF == 'E';
-	ax25->condition = 0x00;
-	ax25->t1timer   = 0;
-	ax25->t2timer   = 0;
-	ax25->t3timer   = 0;
-	ax25->n2count   = 0;
-	ax25->idletimer = 0;
-
-	ax25->va      = 0;
-	ax25->vr      = 0;
-	ax25->vs      = 0;
-
-	if (AX25_DEF_AXDEFMODE == EMODULUS) {
-		ax25->window = AX25_DEF_EWINDOW;
-	} else {
-		ax25->window = AX25_DEF_WINDOW;
-	}
-
-	ax25->device   = NULL;
-	ax25->digipeat = NULL;
-	ax25->sk       = NULL;
-
-	ax25->state    = AX25_STATE_0;
+	ax25_fillin_cb(ax25, NULL);
 
-	memset(&ax25->dest_addr,   '\0', AX25_ADDR_LEN);
-	memset(&ax25->source_addr, '\0', AX25_ADDR_LEN);
+	ax25->state = AX25_STATE_0;
 
 	return ax25;
 }
@@ -731,75 +792,35 @@
 {
 	ax25_cb *ax25;
 	int count = 0;
-	
+
 	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
 		if (ax25->device == dev && ax25->dama_slave) {
 			count++;
 			break;
 		}
 	}
-		
-	return count;
-}
 
-/*
- *	Fill in a created AX.25 created control block with the default
- *	values for a particular device.
- */
-static void ax25_fillin_cb(ax25_cb *ax25, struct device *dev)
-{
-	ax25->device  = dev;
-
-	ax25->rtt      = ax25_dev_get_value(dev, AX25_VALUES_T1);
-	ax25->t1       = ax25_dev_get_value(dev, AX25_VALUES_T1);
-	ax25->t2       = ax25_dev_get_value(dev, AX25_VALUES_T2);
-	ax25->t3       = ax25_dev_get_value(dev, AX25_VALUES_T3);
-	ax25->n2       = ax25_dev_get_value(dev, AX25_VALUES_N2);
-	ax25->paclen   = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
-	ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_IPMAXQUEUE);
-	ax25->idle     = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
-
-	ax25->dama_slave = 0;
-
-	ax25->modulus = ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE);
-
-	if (ax25->modulus == MODULUS) {
-		ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
-	} else {
-		ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
-	}
-
-	ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF) == 'E';
+	return count;
 }
 
-int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest,
+ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest,
 	ax25_digi *digi, struct device *dev)
 {
 	ax25_cb *ax25;
 
-	if (skb == NULL)
-		return 0;
+	if (paclen == 0)
+		paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
 
 	/*
 	 * Look for an existing connection.
 	 */
-	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
-		if (ax25->sk != NULL && ax25->sk->type != SOCK_SEQPACKET)
-			continue;
-
-		if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev) {
-			if (ax25_queue_length(ax25, skb) > ax25->maxqueue * ax25->window) {
-				kfree_skb(skb, FREE_WRITE);
-			} else {
-				ax25_output(ax25, skb);
-			}
-			ax25->idletimer = ax25->idle;
-			return 1;		/* It already existed */
-		}
+	if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
+		ax25_output(ax25, paclen, skb);
+		return ax25;		/* It already existed */
 	}
 
 	if ((ax25 = ax25_create_cb()) == NULL)
-		return 0;
+		return NULL;
 
 	ax25_fillin_cb(ax25, dev);
 
@@ -808,12 +829,10 @@
 
 	if (digi != NULL) {
 		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-			kfree_s(ax25, sizeof(ax25));
-			return 0;
+			ax25_free_cb(ax25);
+			return NULL;
 		}
 		*ax25->digipeat = *digi;
-	} else {
-		ax25_rt_build_path(ax25, dest, dev);
 	}
 
 	if (ax25_dev_is_dama_slave(ax25->device))
@@ -821,19 +840,15 @@
 	else
 		ax25_establish_data_link(ax25);
 
-	/* idle timeouts only for mode vc connections */
-
-	ax25->idletimer = ax25->idle;
-		
 	ax25_insert_socket(ax25);
 
 	ax25->state = AX25_STATE_1;
 
 	ax25_set_timer(ax25);
 
-	ax25_output(ax25, skb);
-			
-	return 1;			/* We had to create it */	
+	ax25_output(ax25, paclen, skb);
+
+	return ax25;			/* We had to create it */
 }
 
 /*
@@ -842,29 +857,11 @@
 struct device *ax25rtr_get_dev(ax25_address *addr)
 {
 	struct device *dev;
-	
-	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (dev->flags & IFF_UP) {
-			switch (dev->type) {
-				case ARPHRD_AX25: /* Active kiss ax25 mode */ 
-					if (ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
-						return dev;
-					break;
-#ifdef CONFIG_BPQETHER
-				case ARPHRD_ETHER: {
-						ax25_address *dev_addr;
-
-						if ((dev_addr = ax25_bpq_get_addr(dev)) != NULL)
-							if (ax25cmp(addr, dev_addr) == 0)
-								return dev;
-					}
-					break;
-#endif
-				default:
-					break;
-			}
-		}
-	}
+
+	for (dev = dev_base; dev != NULL; dev = dev->next)
+		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25 &&
+		    ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
+		     	return dev;
 
 	return NULL;
 }
@@ -873,7 +870,6 @@
  *	Handling for system calls applied via the various interfaces to an
  *	AX25 socket object
  */
- 
 static int ax25_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	return -EINVAL;
@@ -882,11 +878,9 @@
 static int ax25_setsockopt(struct socket *sock, int level, int optname,
 	char *optval, int optlen)
 {
-	struct sock *sk;
+	struct sock *sk = (struct sock *)sock->data;
 	int err, opt;
 
-	sk = (struct sock *)sock->data;
-	
 	if (level == SOL_SOCKET)
 		return sock_setsockopt(sk, level, optname, optval, optlen);
 
@@ -899,66 +893,73 @@
 	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
 		return err;
 
-	opt = get_fs_long((unsigned long *)optval);
+	opt = get_fs_long((int *)optval);
 
 	switch (optname) {
 		case AX25_WINDOW:
-			if (sk->ax25->modulus == MODULUS) {
+			if (sk->protinfo.ax25->modulus == AX25_MODULUS) {
 				if (opt < 1 || opt > 7)
 					return -EINVAL;
 			} else {
 				if (opt < 1 || opt > 63)
 					return -EINVAL;
 			}
-			sk->ax25->window = opt;
+			sk->protinfo.ax25->window = opt;
 			return 0;
 
 		case AX25_T1:
 			if (opt < 1)
 				return -EINVAL;
-			sk->ax25->rtt = (opt * PR_SLOWHZ) / 2;
+			sk->protinfo.ax25->rtt = (opt * AX25_SLOWHZ) / 2;
+			sk->protinfo.ax25->t1  = opt * AX25_SLOWHZ;
 			return 0;
 
 		case AX25_T2:
 			if (opt < 1)
 				return -EINVAL;
-			sk->ax25->t2 = opt * PR_SLOWHZ;
+			sk->protinfo.ax25->t2 = opt * AX25_SLOWHZ;
 			return 0;
 
 		case AX25_N2:
 			if (opt < 1 || opt > 31)
 				return -EINVAL;
-			sk->ax25->n2 = opt;
+			sk->protinfo.ax25->n2 = opt;
 			return 0;
 
 		case AX25_T3:
 			if (opt < 1)
 				return -EINVAL;
-			sk->ax25->t3 = opt * PR_SLOWHZ;
+			sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ;
 			return 0;
-			
+
 		case AX25_IDLE:
 			if (opt < 0)
 				return -EINVAL;
-			sk->ax25->idle = opt * PR_SLOWHZ * 60;
+			sk->protinfo.ax25->idle = opt * AX25_SLOWHZ * 60;
 			return 0;
 
 		case AX25_BACKOFF:
-			sk->ax25->backoff = opt ? 1 : 0;
+			if (opt < 0 || opt > 2)
+				return -EINVAL;
+			sk->protinfo.ax25->backoff = opt;
 			return 0;
 
 		case AX25_EXTSEQ:
-			sk->ax25->modulus = opt ? EMODULUS : MODULUS;
+			sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
 			return 0;
 
-		case AX25_HDRINCL:
-			sk->ax25->hdrincl = opt ? 1 : 0;
+		case AX25_PIDINCL:
+			sk->protinfo.ax25->pidincl = opt ? 1 : 0;
 			return 0;
-			
+
+		case AX25_IAMDIGI:
+			sk->protinfo.ax25->iamdigi = opt ? 1 : 0;
+			return 0;
+
 		case AX25_PACLEN:
 			if (opt < 16 || opt > 65535)
 				return -EINVAL;
-			sk->ax25->paclen = opt;
+			sk->protinfo.ax25->paclen = opt;
 			return 0;
 
 		default:
@@ -969,57 +970,59 @@
 static int ax25_getsockopt(struct socket *sock, int level, int optname,
 	char *optval, int *optlen)
 {
-	struct sock *sk;
+	struct sock *sk = (struct sock *)sock->data;
 	int val = 0;
-	int err; 
+	int err;
 
-	sk = (struct sock *)sock->data;
-	
 	if (level == SOL_SOCKET)
 		return sock_getsockopt(sk, level, optname, optval, optlen);
-	
+
 	if (level != SOL_AX25)
 		return -EOPNOTSUPP;
 
 	switch (optname) {
 		case AX25_WINDOW:
-			val = sk->ax25->window;
+			val = sk->protinfo.ax25->window;
 			break;
 
 		case AX25_T1:
-			val = (sk->ax25->t1 * 2) / PR_SLOWHZ;
+			val = sk->protinfo.ax25->t1 / AX25_SLOWHZ;
 			break;
 
 		case AX25_T2:
-			val = sk->ax25->t2 / PR_SLOWHZ;
+			val = sk->protinfo.ax25->t2 / AX25_SLOWHZ;
 			break;
 
 		case AX25_N2:
-			val = sk->ax25->n2;
+			val = sk->protinfo.ax25->n2;
 			break;
 
 		case AX25_T3:
-			val = sk->ax25->t3 / PR_SLOWHZ;
+			val = sk->protinfo.ax25->t3 / AX25_SLOWHZ;
 			break;
-			
+
 		case AX25_IDLE:
-			val = sk->ax25->idle / (PR_SLOWHZ * 60);
+			val = sk->protinfo.ax25->idle / (AX25_SLOWHZ * 60);
 			break;
 
 		case AX25_BACKOFF:
-			val = sk->ax25->backoff;
+			val = sk->protinfo.ax25->backoff;
 			break;
 
 		case AX25_EXTSEQ:
-			val = (sk->ax25->modulus == EMODULUS);
+			val = (sk->protinfo.ax25->modulus == AX25_EMODULUS);
 			break;
 
-		case AX25_HDRINCL:
-			val = sk->ax25->hdrincl;
+		case AX25_PIDINCL:
+			val = sk->protinfo.ax25->pidincl;
 			break;
-			
+
+		case AX25_IAMDIGI:
+			val = sk->protinfo.ax25->iamdigi;
+			break;
+
 		case AX25_PACLEN:
-			val = sk->ax25->paclen;
+			val = sk->protinfo.ax25->paclen;
 			break;
 
 		default:
@@ -1088,7 +1091,20 @@
 #ifdef CONFIG_NETROM
 				case AX25_P_NETROM:
 #endif
+#ifdef CONFIG_ROSE
+				case AX25_P_ROSE:
+#endif
 					return -ESOCKTNOSUPPORT;
+#ifdef CONFIG_NETROM_MODULE
+				case AX25_P_NETROM:
+					if (ax25_protocol_is_registered(AX25_P_NETROM))
+						return -ESOCKTNOSUPPORT;
+#endif
+#ifdef CONFIG_ROSE_MODULE
+				case AX25_P_ROSE:
+					if (ax25_protocol_is_registered(AX25_P_ROSE))
+						return -ESOCKTNOSUPPORT;
+#endif
 				default:
 					break;
 			}
@@ -1133,9 +1149,9 @@
 		sk->sleep  = sock->wait;
 	}
 
-	ax25->sk = sk;
-	sk->ax25 = ax25;
-	
+	ax25->sk          = sk;
+	sk->protinfo.ax25 = ax25;
+
 	return 0;
 }
 
@@ -1164,7 +1180,7 @@
 			break;
 		default:
 			sk_free(sk);
-			kfree_s((void *)ax25, sizeof(*ax25));
+			ax25_free_cb(ax25);
 			return NULL;
 	}
 
@@ -1179,7 +1195,6 @@
 	sk->sndbuf      = osk->sndbuf;
 	sk->debug       = osk->debug;
 	sk->state       = TCP_ESTABLISHED;
-	sk->window      = osk->window;
 	sk->mtu         = osk->mtu;
 	sk->sleep       = osk->sleep;
 	sk->zapped      = osk->zapped;
@@ -1189,34 +1204,33 @@
 	sk->write_space  = def_callback1;
 	sk->error_report = def_callback1;
 
-	ax25->modulus = osk->ax25->modulus;
-	ax25->backoff = osk->ax25->backoff;
-	ax25->hdrincl = osk->ax25->hdrincl;
-	ax25->rtt     = osk->ax25->rtt;
-	ax25->t1      = osk->ax25->t1;
-	ax25->t2      = osk->ax25->t2;
-	ax25->t3      = osk->ax25->t3;
-	ax25->n2      = osk->ax25->n2;
-	ax25->idle    = osk->ax25->idle;
-	ax25->paclen  = osk->ax25->paclen;
+	ax25->modulus = osk->protinfo.ax25->modulus;
+	ax25->backoff = osk->protinfo.ax25->backoff;
+	ax25->pidincl = osk->protinfo.ax25->pidincl;
+	ax25->iamdigi = osk->protinfo.ax25->iamdigi;
+	ax25->rtt     = osk->protinfo.ax25->rtt;
+	ax25->t1      = osk->protinfo.ax25->t1;
+	ax25->t2      = osk->protinfo.ax25->t2;
+	ax25->t3      = osk->protinfo.ax25->t3;
+	ax25->n2      = osk->protinfo.ax25->n2;
+	ax25->idle    = osk->protinfo.ax25->idle;
+	ax25->paclen  = osk->protinfo.ax25->paclen;
+	ax25->window  = osk->protinfo.ax25->window;
 
-	ax25->window   = osk->ax25->window;
-	ax25->maxqueue = osk->ax25->maxqueue;
+	ax25->source_addr = osk->protinfo.ax25->source_addr;
 
-	ax25->source_addr = osk->ax25->source_addr;
-	
-	if (osk->ax25->digipeat != NULL) {
+	if (osk->protinfo.ax25->digipeat != NULL) {
 		if ((ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
 			sk_free(sk);
-			kfree_s(ax25, sizeof(*ax25));
+			ax25_free_cb(ax25);
 			return NULL;
 		}
-		
-		*ax25->digipeat = *osk->ax25->digipeat;
+
+		*ax25->digipeat = *osk->protinfo.ax25->digipeat;
 	}
 
-	sk->ax25 = ax25;
-	ax25->sk = sk;
+	sk->protinfo.ax25 = ax25;
+	ax25->sk          = sk;
 
 	return sk;
 }
@@ -1225,6 +1239,9 @@
 {
 	struct sock *sk = (struct sock *)oldsock->data;
 
+	if (sk == NULL || newsock == NULL)
+		return -EINVAL;
+
 	return ax25_create(newsock, sk->protocol);
 }
 
@@ -1235,51 +1252,44 @@
 	if (sk == NULL) return 0;
 
 	if (sk->type == SOCK_SEQPACKET) {
-		switch (sk->ax25->state) {
+		switch (sk->protinfo.ax25->state) {
 			case AX25_STATE_0:
-				sk->state       = TCP_CLOSE;
-				sk->state_change(sk);
-				sk->dead        = 1;
-				ax25_destroy_socket(sk->ax25);
+				ax25_disconnect(sk->protinfo.ax25, 0);
+				ax25_destroy_socket(sk->protinfo.ax25);
 				break;
 
 			case AX25_STATE_1:
-				ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
-				sk->ax25->state = AX25_STATE_0;
-				sk->state       = TCP_CLOSE;
-				sk->state_change(sk);
-				sk->dead        = 1;
-				ax25_destroy_socket(sk->ax25);
+				ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+				ax25_disconnect(sk->protinfo.ax25, 0);
+				ax25_destroy_socket(sk->protinfo.ax25);
 				break;
 
 			case AX25_STATE_2:
-				if (sk->ax25->dama_slave)
-					ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
+				if (sk->protinfo.ax25->dama_slave)
+					ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 				else
-					ax25_send_control(sk->ax25, DM, POLLON, C_RESPONSE);
-				sk->ax25->state = AX25_STATE_0;
-				sk->state       = TCP_CLOSE;
-				sk->state_change(sk);
-				sk->dead        = 1;
-				ax25_destroy_socket(sk->ax25);
-				break;			
+					ax25_send_control(sk->protinfo.ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+				ax25_disconnect(sk->protinfo.ax25, 0);
+				ax25_destroy_socket(sk->protinfo.ax25);
+				break;
 
 			case AX25_STATE_3:
 			case AX25_STATE_4:
-				ax25_clear_queues(sk->ax25);
-				sk->ax25->n2count = 0;
-				if (!sk->ax25->dama_slave) {
-					ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
-					sk->ax25->t3timer = 0;
+				ax25_clear_queues(sk->protinfo.ax25);
+				sk->protinfo.ax25->n2count = 0;
+				if (!sk->protinfo.ax25->dama_slave) {
+					ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+					sk->protinfo.ax25->t3timer = 0;
 				} else {
-					sk->ax25->t3timer = sk->ax25->t3;	/* DAMA slave timeout */
+					sk->protinfo.ax25->t3timer = sk->protinfo.ax25->t3;	/* DAMA slave timeout */
 				}
-				sk->ax25->t1timer = sk->ax25->t1 = ax25_calculate_t1(sk->ax25);
-				sk->ax25->state   = AX25_STATE_2;
-				sk->state         = TCP_CLOSE;
+				sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25);
+				sk->protinfo.ax25->state   = AX25_STATE_2;
+				sk->state                  = TCP_CLOSE;
+				sk->shutdown              |= SEND_SHUTDOWN;
 				sk->state_change(sk);
-				sk->dead          = 1;
-				sk->destroy       = 1;
+				sk->dead                   = 1;
+				sk->destroy                = 1;
 				break;
 
 			default:
@@ -1287,9 +1297,10 @@
 		}
 	} else {
 		sk->state       = TCP_CLOSE;
+		sk->shutdown   |= SEND_SHUTDOWN;
 		sk->state_change(sk);
-		sk->dead = 1;
-		ax25_destroy_socket(sk->ax25);
+		sk->dead        = 1;
+		ax25_destroy_socket(sk->protinfo.ax25);
 	}
 
 	sock->data = NULL;	
@@ -1304,32 +1315,33 @@
  *	BSD 4.4 ADDIFADDR type support. It is however small and trivially backward
  *	compatible 8)
  */
-static int ax25_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
+static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
-	struct sock *sk;
+	struct sock *sk = (struct sock *)sock->data;
 	struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
 	struct device *dev;
 	ax25_address *call;
-	
-	sk = (struct sock *)sock->data;
-	
+
 	if (sk->zapped == 0)
-		return -EIO;
-		
+		return -EINVAL;
+
 	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 		return -EINVAL;
 
+	if (addr->fsa_ax25.sax25_family != AF_AX25)
+		return -EINVAL;
+
 	call = ax25_findbyuid(current->euid);
 	if (call == NULL && ax25_uid_policy && !suser())
-		return -EPERM;
-		
+		return -EACCES;
+
 	if (call == NULL)
-		sk->ax25->source_addr = addr->fsa_ax25.sax25_call;
+		sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call;
 	else
-		sk->ax25->source_addr = *call;
+		sk->protinfo.ax25->source_addr = *call;
 
 	if (sk->debug)
-		printk("AX25: source address set to %s\n", ax2asc(&sk->ax25->source_addr));
+		printk("AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr));
 
 	if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
 		if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) {
@@ -1355,8 +1367,8 @@
 			printk("AX25: bound to device %s\n", dev->name);
 	}
 
-	ax25_fillin_cb(sk->ax25, dev);
-	ax25_insert_socket(sk->ax25);
+	ax25_fillin_cb(sk->protinfo.ax25, dev);
+	ax25_insert_socket(sk->protinfo.ax25);
 
 	sk->zapped = 0;
 
@@ -1370,53 +1382,61 @@
 	int addr_len, int flags)
 {
 	struct sock *sk = (struct sock *)sock->data;
-	struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
-	int err;
-	
+	struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
+	ax25_digi *digi = NULL;
+	int ct = 0, err;
+
 	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
 		return 0;	/* Connect completed during a ERESTARTSYS event */
 	}
-	
+
 	if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
 		sock->state = SS_UNCONNECTED;
 		return -ECONNREFUSED;
 	}
-	
+
 	if (sk->state == TCP_ESTABLISHED && sk->type == SOCK_SEQPACKET)
 		return -EISCONN;	/* No reconnect on a seqpacket socket */
-		
-	sk->state   = TCP_CLOSE;	
+
+	sk->state   = TCP_CLOSE;
 	sock->state = SS_UNCONNECTED;
 
 	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 		return -EINVAL;
 
+	if (fsa->fsa_ax25.sax25_family != AF_AX25)
+		return -EINVAL;
+
+	if (sk->protinfo.ax25->digipeat != NULL) {
+		kfree_s(sk->protinfo.ax25->digipeat, sizeof(ax25_digi));
+		sk->protinfo.ax25->digipeat = NULL;
+	}
+
 	/*
 	 *	Handle digi-peaters to be used.
 	 */
-	if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->sax25_ndigis != 0) {
-		int ct           = 0;
-		struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)addr;
-
+	if (addr_len == sizeof(struct full_sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) {
 		/* Valid number of digipeaters ? */
-		if (addr->sax25_ndigis < 1 || addr->sax25_ndigis > AX25_MAX_DIGIS)
+		if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS)
 			return -EINVAL;
 
-		if (sk->ax25->digipeat == NULL) {
-			if ((sk->ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
-				return -ENOMEM;
-		}
+		if ((digi = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
+			return -ENOBUFS;
 
-		sk->ax25->digipeat->ndigi = addr->sax25_ndigis;
+		digi->ndigi      = fsa->fsa_ax25.sax25_ndigis;
+		digi->lastrepeat = -1;
 
-		while (ct < addr->sax25_ndigis) {
-			sk->ax25->digipeat->repeated[ct] = 0;
-			sk->ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct];
+		while (ct < fsa->fsa_ax25.sax25_ndigis) {
+			if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) {
+				digi->repeated[ct] = 1;
+				digi->lastrepeat   = ct;
+			} else {
+				digi->repeated[ct] = 0;
+			}
+			digi->calls[ct] = fsa->fsa_digipeater[ct];
 			ct++;
 		}
-
-		sk->ax25->digipeat->lastrepeat = 0;
 	}
 
 	/*
@@ -1425,43 +1445,46 @@
 	 *	been filled in, error if it hasn't.
 	 */
 	if (sk->zapped) {
-		if ((err = ax25_rt_autobind(sk->ax25, &addr->sax25_call)) < 0)
+		if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0)
 			return err;
-		ax25_fillin_cb(sk->ax25, sk->ax25->device);
-		ax25_insert_socket(sk->ax25);
+		ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->device);
+		ax25_insert_socket(sk->protinfo.ax25);
 	} else {
-		if (sk->ax25->device == NULL)
+		if (sk->protinfo.ax25->device == NULL)
 			return -EHOSTUNREACH;
 	}
-		
-	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->ax25->source_addr, &addr->sax25_call, sk->ax25->device) != NULL)
-		return -EBUSY;				/* Already such a connection */
 
-	sk->ax25->dest_addr = addr->sax25_call;
-	
+	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->device) != NULL) {
+		if (digi != NULL) kfree_s(digi, sizeof(ax25_digi));
+		return -EADDRINUSE;			/* Already such a connection */
+	}
+
+	sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call;
+	sk->protinfo.ax25->digipeat  = digi;
+
 	/* First the easy one */
 	if (sk->type != SOCK_SEQPACKET) {
 		sock->state = SS_CONNECTED;
 		sk->state   = TCP_ESTABLISHED;
 		return 0;
 	}
-	
-	/* Move to connecting socket, ax.25 lapb WAIT_UA.. */	
+
+	/* Move to connecting socket, ax.25 lapb WAIT_UA.. */
 	sock->state        = SS_CONNECTING;
 	sk->state          = TCP_SYN_SENT;
-	
-	if (ax25_dev_is_dama_slave(sk->ax25->device))
-		dama_establish_data_link(sk->ax25);
+
+	if (ax25_dev_is_dama_slave(sk->protinfo.ax25->device))
+		dama_establish_data_link(sk->protinfo.ax25);
 	else
-		ax25_establish_data_link(sk->ax25);
-		
-	sk->ax25->state     = AX25_STATE_1;
-	ax25_set_timer(sk->ax25);		/* Start going SABM SABM until a UA or a give up and DM */
-	
+		ax25_establish_data_link(sk->protinfo.ax25);
+
+	sk->protinfo.ax25->state = AX25_STATE_1;
+	ax25_set_timer(sk->protinfo.ax25);		/* Start going SABM SABM until a UA or a give up and DM */
+
 	/* Now the loop */
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
 		return -EINPROGRESS;
-		
+
 	cli();	/* To avoid races on the sleep */
 
 	/* A DM or timeout will go to closed, a UA will go to ABM */
@@ -1473,21 +1496,20 @@
 		}
 	}
 
-	if (sk->state != TCP_ESTABLISHED) 
-	{
+	if (sk->state != TCP_ESTABLISHED) {
 		/* Not in ABM, not in WAIT_UA -> failed */
 		sti();
 		sock->state = SS_UNCONNECTED;
 		return sock_error(sk);	/* Always set at this point */
 	}
-	
+
 	sock->state = SS_CONNECTED;
 
 	sti();
-	
+
 	return 0;
 }
-	
+
 static int ax25_socketpair(struct socket *sock1, struct socket *sock2)
 {
 	return -EOPNOTSUPP;
@@ -1499,19 +1521,22 @@
 	struct sock *newsk;
 	struct sk_buff *skb;
 
-	if (newsock->data)
-		sk_free(newsock->data);
+	if (newsock->data != NULL) {
+		sk = (struct sock *)newsock->data;
+		ax25_destroy_socket(sk->protinfo.ax25);
+	}
 
 	newsock->data = NULL;
-	
-	sk = (struct sock *)sock->data;
+
+	if ((sk = (struct sock *)sock->data) == NULL)
+		return -EINVAL;
 
 	if (sk->type != SOCK_SEQPACKET)
 		return -EOPNOTSUPP;
-	
+
 	if (sk->state != TCP_LISTEN)
 		return -EINVAL;
-		
+
 	/*
 	 *	The write queue this time is holding sockets ready to use
 	 *	hooked into the SABM we saved
@@ -1521,7 +1546,7 @@
 		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
 			if (flags & O_NONBLOCK) {
 				sti();
-				return 0;
+				return -EWOULDBLOCK;
 			}
 			interruptible_sleep_on(sk->sleep);
 			if (current->signal & ~current->blocked) {
@@ -1550,49 +1575,49 @@
 	int *uaddr_len, int peer)
 {
 	struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
-	struct sock *sk;
+	struct sock *sk = (struct sock *)sock->data;
 	unsigned char ndigi, i;
-	
-	sk = (struct sock *)sock->data;
-	
+
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 
 		sax->fsa_ax25.sax25_family = AF_AX25;
-		sax->fsa_ax25.sax25_call   = sk->ax25->dest_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.ax25->dest_addr;
 		sax->fsa_ax25.sax25_ndigis = 0;
 		*uaddr_len = sizeof(struct full_sockaddr_ax25);
 
-		if (sk->ax25->digipeat != NULL) {
-			ndigi = sk->ax25->digipeat->ndigi;
+		if (sk->protinfo.ax25->digipeat != NULL) {
+			ndigi = sk->protinfo.ax25->digipeat->ndigi;
 			sax->fsa_ax25.sax25_ndigis = ndigi;
 			for (i = 0; i < ndigi; i++)
-				sax->fsa_digipeater[i] = sk->ax25->digipeat->calls[i];
+				sax->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i];
 		}
 	} else {
 		sax->fsa_ax25.sax25_family = AF_AX25;
-		sax->fsa_ax25.sax25_call   = sk->ax25->source_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.ax25->source_addr;
 		sax->fsa_ax25.sax25_ndigis = 1;
 		*uaddr_len = sizeof(struct full_sockaddr_ax25);
 
-		if (sk->ax25->device != NULL)
-			memcpy(&sax->fsa_digipeater[0], sk->ax25->device->dev_addr, AX25_ADDR_LEN);
+		if (sk->protinfo.ax25->device != NULL)
+			memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN);
 		else
 			sax->fsa_digipeater[0] = null_ax25_address;
 	}
-		
+
 	return 0;
 }
- 
+
 static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype)
 {
 	struct sock *make;
 	struct sock *sk;
 	int type = 0;
-	ax25_digi dp;
+	ax25_digi dp, reverse_dp;
+	struct sk_buff *skbn;
 	ax25_cb *ax25;
 	ax25_address src, dest;
+	ax25_address *next_digi = NULL;
 	struct sock *raw;
 	int mine = 0;
 	int dama;
@@ -1600,20 +1625,20 @@
 	/*
 	 *	Process the AX.25/LAPB frame.
 	 */
-	 
+
 	skb->h.raw = skb->data;
-	
+
 #ifdef CONFIG_FIREWALL
 	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL) != FW_ACCEPT) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-#endif	
+#endif
 
 	/*
 	 *	Parse the address header.
 	 */
-	 
+
 	if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -1622,51 +1647,8 @@
 	/*
 	 *	Ours perhaps ?
 	 */
-	if (dp.lastrepeat + 1 < dp.ndigi) {		/* Not yet digipeated completely */
-		if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) {
-			struct device *dev_out = dev;
-
-			/* We are the digipeater. Mark ourselves as repeated
-			   and throw the packet back out of the same device */
-			dp.lastrepeat++;
-			dp.repeated[(int)dp.lastrepeat] = 1;
-
-			if (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) {
-				while (dp.lastrepeat + 1 < dp.ndigi) {
-					struct device *dev_scan;
-					if ((dev_scan = ax25rtr_get_dev(&dp.calls[dp.lastrepeat + 1])) == NULL)
-						break;
-					dp.lastrepeat++;
-					dp.repeated[(int)dp.lastrepeat] = 1;
-					dev_out = dev_scan;
-				}
-				if (dev != dev_out && (ax25_dev_get_value(dev_out, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) == 0) {
-					kfree_skb(skb, FREE_READ);
-					return 0;
-				}
-			}
-
-			if (dev == dev_out && (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_INBAND) == 0) {
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-
-			build_ax25_addr(skb->data, &src, &dest, &dp, type, MODULUS);
-#ifdef CONFIG_FIREWALL
-			if (call_fw_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-#endif
-
-			skb->arp = 1;
-			ax25_queue_xmit(skb, dev_out, SOPRI_NORMAL);
-		} else {
-			kfree_skb(skb, FREE_READ);
-		}
-
-		return 0;
-	}
+	if (dp.lastrepeat + 1 < dp.ndigi)		/* Not yet digipeated completely */
+		next_digi = &dp.calls[dp.lastrepeat + 1];
 
 	/*
 	 *	Pull of the AX.25 headers leaving the CTRL/PID bytes
@@ -1674,16 +1656,15 @@
 	skb_pull(skb, size_ax25_addr(&dp));
 
 	/* For our port addresses ? */
-	if (ax25cmp(&dest, dev_addr) == 0)
+	if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
 		mine = 1;
 
-#ifdef CONFIG_NETROM
-	/* Also match on any NET/ROM callsign */
-	if (!mine && nr_dev_get(&dest) != NULL)
+	/* Also match on any registered callsign from L3/4 */
+	if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
 		mine = 1;
-#endif	
-	
-	if ((*skb->data & ~0x10) == LAPB_UI) {	/* UI frame - bypass LAPB processing */
+
+	/* UI frame - bypass LAPB processing */
+	if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
 		skb->h.raw = skb->data + 2;		/* skip control and pid */
 
 		if ((raw = ax25_addr_match(&dest)) != NULL)
@@ -1696,8 +1677,12 @@
 
 		/* Now we are pointing at the pid byte */
 		switch (skb->data[1]) {
-#ifdef CONFIG_INET		
+#ifdef CONFIG_INET
 			case AX25_P_IP:
+				if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+					kfree_skb(skb, FREE_READ);
+					skb = skbn;
+				}
 				skb_pull(skb,2);		/* drop PID/CTRL */
 				ip_rcv(skb, dev, ptype);	/* Note ptype here is the wrong one, fix me later */
 				break;
@@ -1706,7 +1691,7 @@
 				skb_pull(skb,2);
 				arp_rcv(skb, dev, ptype);	/* Note ptype here is wrong... */
 				break;
-#endif				
+#endif
 			case AX25_P_TEXT:
 				/* Now find a suitable dgram socket */
 				if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
@@ -1717,16 +1702,13 @@
 						 *	Remove the control and PID.
 						 */
 						skb_pull(skb, 2);
-						skb_queue_tail(&sk->receive_queue, skb);
-						skb->sk = sk;
-						atomic_add(skb->truesize, &sk->rmem_alloc);
-						if (!sk->dead)
-							sk->data_ready(sk, skb->len);
+						if (sock_queue_rcv_skb(sk, skb) != 0)
+							kfree_skb(skb, FREE_READ);
 					}
 				} else {
 					kfree_skb(skb, FREE_READ);
 				}
-				break;	
+				break;
 
 			default:
 				kfree_skb(skb, FREE_READ);	/* Will scan SOCK_AX25 RAW sockets */
@@ -1741,18 +1723,20 @@
 	 *	If not, should we DM the incoming frame (except DMs) or
 	 *	silently ignore them. For now we stay quiet.
 	 */
-	if (!ax25_dev_get_value(dev, AX25_VALUES_CONMODE)) {
+	if (ax25_dev_get_value(dev, AX25_VALUES_CONMODE) == 0) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-	
+
 	/* LAPB */
-	
+
 	/* AX.25 state 1-4 */
-	
-	if ((ax25 = ax25_find_cb(&dest, &src, dev)) != NULL) {
+
+	ax25_digi_invert(&dp, &reverse_dp);
+
+	if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
 		/*
-		 *	Process the frame. If it is queued up internally it returns one otherwise we 
+		 *	Process the frame. If it is queued up internally it returns one otherwise we
 		 *	free it immediately. This routine itself wakes the user context layers so we
 		 *	do no further work
 		 */
@@ -1765,13 +1749,13 @@
 	/* AX.25 state 0 (disconnected) */
 
 	/* a) received not a SABM(E) */
-	
-	if ((*skb->data & ~PF) != SABM && (*skb->data & ~PF) != SABME) {
+
+	if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
 		/*
 		 *	Never reply to a DM. Also ignore any connects for
 		 *	addresses that are not our interfaces and not a socket.
 		 */
-		if ((*skb->data & ~PF) != DM && mine)
+		if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
 			ax25_return_dm(dev, &src, &dest, &dp);
 
 		kfree_skb(skb, FREE_READ);
@@ -1779,8 +1763,13 @@
 	}
 
 	/* b) received SABM(E) */
-	
-	if ((sk = ax25_find_listener(&dest, dev, SOCK_SEQPACKET)) != NULL) {
+
+	if (dp.lastrepeat + 1 == dp.ndigi)
+		sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
+	else
+		sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
+
+	if (sk != NULL) {
 		if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
 			if (mine)
 				ax25_return_dm(dev, &src, &dest, &dp);
@@ -1788,8 +1777,8 @@
 			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
-		
-		ax25 = make->ax25;
+
+		ax25 = make->protinfo.ax25;
 
 		skb_queue_head(&sk->receive_queue, skb);
 
@@ -1799,12 +1788,11 @@
 
 		sk->ack_backlog++;
 	} else {
-#ifdef CONFIG_NETROM
 		if (!mine) {
 			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
-		
+
 		if ((ax25 = ax25_create_cb()) == NULL) {
 			ax25_return_dm(dev, &src, &dest, &dp);
 			kfree_skb(skb, FREE_READ);
@@ -1812,14 +1800,6 @@
 		}
 
 		ax25_fillin_cb(ax25, dev);
-		ax25->idletimer = ax25->idle;
-#else
-		if (mine)
-			ax25_return_dm(dev, &src, &dest, &dp);
-
-		kfree_skb(skb, FREE_READ);
-		return 0;
-#endif
 	}
 
 	ax25->source_addr = dest;
@@ -1841,25 +1821,27 @@
 		}
 	} else {
 		/* Reverse the source SABM's path */
-		ax25_digi_invert(&dp, ax25->digipeat);
+		*ax25->digipeat = reverse_dp;
 	}
 
-	if ((*skb->data & ~PF) == SABME) {
-		ax25->modulus = EMODULUS;
+	if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+		ax25->modulus = AX25_EMODULUS;
 		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
 	} else {
-		ax25->modulus = MODULUS;
+		ax25->modulus = AX25_MODULUS;
 		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
 	}
 
 	ax25->device = dev;
-	
-	ax25_send_control(ax25, UA, POLLON, C_RESPONSE);
+
+	ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
 
 	if (dama) ax25_dama_on(ax25);	/* bke 951121 */
 
 	ax25->dama_slave = dama;
-	ax25->t3timer = ax25->t3;
+	ax25->t3timer    = ax25->t3;
+	ax25->idletimer  = ax25->idle;
+
 	ax25->state   = AX25_STATE_3;
 
 	ax25_insert_socket(ax25);
@@ -1893,30 +1875,6 @@
 	return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
 }
 
-#ifdef CONFIG_BPQETHER
-/*
- *	Receive an AX.25 frame via an Ethernet interface.
- */
-static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
-{
-	ax25_address *port_call;
-	int len;
-
-	skb->sk = NULL;		/* Initially we don't know who it's for */
-
-	if ((port_call = ax25_bpq_get_addr(dev)) == NULL) {
-		kfree_skb(skb, FREE_READ);	/* We have no port callsign */
-		return 0;
-	}
-
-	len = skb->data[0] + skb->data[1] * 256 - 5;
-
-	skb_pull(skb, 2);	/* Remove the length bytes */
-	skb_trim(skb, len);	/* Set the length of the data */
-
-	return ax25_rcv(skb, dev, port_call, ptype);
-}
-#endif
 
 static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
 {
@@ -1931,7 +1889,7 @@
 	ax25_digi dtmp;
 	int lv;
 	int addr_len = msg->msg_namelen;
-	
+
 	if (sk->err)
 		return sock_error(sk);
 
@@ -1940,11 +1898,16 @@
 
 	if (sk->zapped)
 		return -EADDRNOTAVAIL;
-		
-	if (sk->ax25->device == NULL)
+
+	if (sk->shutdown & SEND_SHUTDOWN) {
+		send_sig(SIGPIPE, current, 0);
+		return -EPIPE;
+	}
+
+	if (sk->protinfo.ax25->device == NULL)
 		return -ENETUNREACH;
-		
-	if (usax) {
+
+	if (usax != NULL) {
 		if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 			return -EINVAL;
 		if (usax->sax25_family != AF_AX25)
@@ -1969,7 +1932,7 @@
 		}
 
 		sax = *usax;
-		if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->ax25->dest_addr, &sax.sax25_call) != 0)
+		if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0)
 			return -EISCONN;
 		if (usax->sax25_ndigis == 0)
 			dp = NULL;
@@ -1979,10 +1942,10 @@
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		sax.sax25_family = AF_AX25;
-		sax.sax25_call   = sk->ax25->dest_addr;
-		dp = sk->ax25->digipeat;
+		sax.sax25_call   = sk->protinfo.ax25->dest_addr;
+		dp = sk->protinfo.ax25->digipeat;
 	}
-	
+
 	if (sk->debug)
 		printk("AX.25: sendto: Addresses built.\n");
 
@@ -1998,7 +1961,6 @@
 
 	skb->sk   = sk;
 	skb->free = 1;
-	skb->arp  = 1;
 
 	skb_reserve(skb, size - len);
 
@@ -2008,9 +1970,11 @@
 	/* User data follows immediately after the AX.25 data */
 	memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
 
-	/* Add the PID, usually AX25_TEXT */
-	asmptr  = skb_push(skb, 1);
-	*asmptr = sk->protocol;
+	/* Add the PID if one is not supplied by the user in the skb */
+	if (!sk->protinfo.ax25->pidincl) {
+		asmptr  = skb_push(skb, 1);
+		*asmptr = sk->protocol;
+	}
 
 	if (sk->debug)
 		printk("AX.25: Transmitting buffer\n");
@@ -2022,7 +1986,7 @@
 			return -ENOTCONN;
 		}
 
-		ax25_output(sk->ax25, skb);	/* Shove it onto the queue and kick */
+		ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb);	/* Shove it onto the queue and kick */
 
 		return len;
 	} else {
@@ -2035,38 +1999,36 @@
 		}
 
 		/* Build an AX.25 header */
-		asmptr += (lv = build_ax25_addr(asmptr, &sk->ax25->source_addr, &sax.sax25_call, dp, C_COMMAND, MODULUS));
+		asmptr += (lv = build_ax25_addr(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS));
 
 		if (sk->debug)
 			printk("Built header (%d bytes)\n",lv);
 
 		skb->h.raw = asmptr;
-	
+
 		if (sk->debug)
 			printk("base=%p pos=%p\n", skb->data, asmptr);
 
-		*asmptr = LAPB_UI;
+		*asmptr = AX25_UI;
 
 		/* Datagram frames go straight out of the door as UI */
-		ax25_queue_xmit(skb, sk->ax25->device, SOPRI_NORMAL);
+		ax25_queue_xmit(skb, sk->protinfo.ax25->device, SOPRI_NORMAL);
 
 		return len;
 	}
-		
 }
 
 static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
 {
 	struct sock *sk = (struct sock *)sock->data;
 	struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
-	int copied, length;
+	int copied;
 	struct sk_buff *skb;
 	int er;
-	int dama;
 
 	if (sk->err)
 		return sock_error(sk);
-	
+
 	if (addr_len != NULL)
 		*addr_len = sizeof(*sax);
 
@@ -2081,23 +2043,20 @@
 	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
 		return er;
 
-	if (sk->ax25->hdrincl) {
-		length = skb->len + (skb->data - skb->h.raw);
-	} else {
-		if (sk->type == SOCK_SEQPACKET)
-			skb_pull(skb, 1);		/* Remove PID */
-		length     = skb->len;
-		skb->h.raw = skb->data;
-	}
+	if (!sk->protinfo.ax25->pidincl)
+		skb_pull(skb, 1);		/* Remove PID */
 
-	copied = (size < length) ? size : length;
+	skb->h.raw = skb->data;
+
+	copied = (size < skb->len) ? size : skb->len;
 	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	
-	if (sax) {
+	if (sax != NULL) {
 		ax25_digi digi;
 		ax25_address dest;
+		int dama;
 
-		if (addr_len == (int *)0)
+		if (addr_len == NULL)
 			return -EINVAL;
 		if (*addr_len != sizeof(struct sockaddr_ax25) && *addr_len != sizeof(struct full_sockaddr_ax25))
 			return -EINVAL;
@@ -2129,7 +2088,7 @@
 	skb_free_datagram(sk, skb);
 
 	return copied;
-}		
+}
 
 static int ax25_shutdown(struct socket *sk, int how)
 {
@@ -2148,34 +2107,36 @@
 {
 	struct sock *sk = (struct sock *)sock->data;
 	int err;
-	long amount = 0;
 
 	switch (cmd) {
-		case TIOCOUTQ:
-			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+		case TIOCOUTQ: {
+			long amount;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
 				return err;
 			amount = sk->sndbuf - sk->wmem_alloc;
 			if (amount < 0)
 				amount = 0;
-			put_fs_long(amount, (unsigned long *)arg);
+			put_fs_long(amount, (int *)arg);
 			return 0;
+		}
 
 		case TIOCINQ: {
 			struct sk_buff *skb;
+			long amount = 0L;
 			/* These two are safe on a single CPU system as only user tasks fiddle here */
 			if ((skb = skb_peek(&sk->receive_queue)) != NULL)
 				amount = skb->len;
-			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
 				return err;
-			put_fs_long(amount, (unsigned long *)arg);
+			put_fs_long(amount, (int *)arg);
 			return 0;
 		}
 
 		case SIOCGSTAMP:
 			if (sk != NULL) {
-				if (sk->stamp.tv_sec==0)
+				if (sk->stamp.tv_sec == 0)
 					return -ENOENT;
-				if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
+				if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct timeval))) != 0)
 					return err;
 				memcpy_tofs((void *)arg, &sk->stamp, sizeof(struct timeval));
 				return 0;
@@ -2192,27 +2153,18 @@
 			return ax25_uid_ioctl(cmd, &sax25);
 		}
 
-		case SIOCAX25NOUID:	/* Set the default policy (default/bar) */
+		case SIOCAX25NOUID: {	/* Set the default policy (default/bar) */
+			long amount;
 			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long))) != 0)
 				return err;
-			if(!suser())
+			if (!suser())
 				return -EPERM;
 			amount = get_fs_long((void *)arg);
 			if (amount > AX25_NOUID_BLOCK)
 				return -EINVAL;
 			ax25_uid_policy = amount;
 			return 0;
-
-#ifdef CONFIG_BPQETHER
-		case SIOCAX25BPQADDR:
-			if (!suser())
-				return -EPERM;
-			return ax25_bpq_ioctl(cmd, (void *)arg);
-#endif
-
-		case SIOCAX25GETPARMS:
-		case SIOCAX25SETPARMS:
-			return ax25_dev_ioctl(cmd, (void *)arg);
+		}
 
 		case SIOCADDRT:
 		case SIOCDELRT:
@@ -2220,12 +2172,44 @@
 			if (!suser())
 				return -EPERM;
 			return ax25_rt_ioctl(cmd, (void *)arg);
-			
+
 		case SIOCAX25CTLCON:
 			if (!suser())
 				return -EPERM;
 			return ax25_ctl_ioctl(cmd, (void *)arg);
 
+		case SIOCAX25GETINFO: {
+			struct ax25_info_struct ax25_info;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(ax25_info))) != 0)
+				return err;
+			ax25_info.t1        = sk->protinfo.ax25->t1   / AX25_SLOWHZ;
+			ax25_info.t2        = sk->protinfo.ax25->t2   / AX25_SLOWHZ;
+			ax25_info.t3        = sk->protinfo.ax25->t3   / AX25_SLOWHZ;
+			ax25_info.idle      = sk->protinfo.ax25->idle / (60 * AX25_SLOWHZ);
+			ax25_info.n2        = sk->protinfo.ax25->n2;
+			ax25_info.t1timer   = sk->protinfo.ax25->t1timer   / AX25_SLOWHZ;
+			ax25_info.t2timer   = sk->protinfo.ax25->t2timer   / AX25_SLOWHZ;
+			ax25_info.t3timer   = sk->protinfo.ax25->t3timer   / AX25_SLOWHZ;
+			ax25_info.idletimer = sk->protinfo.ax25->idletimer / (60 * AX25_SLOWHZ);
+			ax25_info.n2count   = sk->protinfo.ax25->n2count;
+			ax25_info.state     = sk->protinfo.ax25->state;
+			ax25_info.rcv_q     = sk->rmem_alloc;
+			ax25_info.snd_q     = sk->wmem_alloc;
+			memcpy_tofs((void *)arg, &ax25_info, sizeof(ax25_info));
+			return 0;
+		}
+
+		case SIOCAX25ADDFWD:
+		case SIOCAX25DELFWD: {
+			struct ax25_fwd_struct ax25_fwd;
+			if (!suser())
+				return -EPERM;
+			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_fwd))) != 0)
+				return err;
+			memcpy_fromfs(&ax25_fwd, (void *)arg, sizeof(ax25_fwd));
+			return ax25_fwd_ioctl(cmd, &ax25_fwd);
+		}
+
 		case SIOCGIFADDR:
 		case SIOCSIFADDR:
 		case SIOCGIFDSTADDR:
@@ -2252,13 +2236,14 @@
 	ax25_cb *ax25;
 	struct device *dev;
 	const char *devname;
+	char callbuf[15];
 	int len = 0;
 	off_t pos = 0;
 	off_t begin = 0;
 
 	cli();
 
-	len += sprintf(buffer, "dest_addr src_addr  dev  st  vs  vr  va    t1     t2     t3      idle   n2  rtt wnd paclen   dama Snd-Q Rcv-Q\n");
+	len += sprintf(buffer, "dest_addr src_addr   dev  st  vs  vr  va    t1     t2     t3      idle   n2  rtt wnd paclen Snd-Q Rcv-Q Inode\n");
 
 	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
 		if ((dev = ax25->device) == NULL)
@@ -2268,40 +2253,46 @@
 
 		len += sprintf(buffer + len, "%-9s ",
 			ax2asc(&ax25->dest_addr));
-		len += sprintf(buffer + len, "%-9s %-4s %2d %3d %3d %3d %3d/%03d %2d/%02d %3d/%03d %3d/%03d %2d/%02d %3d %3d  %5d",
-			ax2asc(&ax25->source_addr), devname,
+
+		sprintf(callbuf, "%s%c", ax2asc(&ax25->source_addr),
+					 (ax25->iamdigi) ? '*' : ' ');
+
+		len += sprintf(buffer + len, "%-10s %-4s %2d %3d %3d %3d %3d/%03d %2d/%02d %3d/%03d %3d/%03d %2d/%02d %3d %3d  %5d",
+			callbuf, devname,
 			ax25->state,
 			ax25->vs, ax25->vr, ax25->va,
-			ax25->t1timer / PR_SLOWHZ,
-			ax25->t1      / PR_SLOWHZ,
-			ax25->t2timer / PR_SLOWHZ,
-			ax25->t2      / PR_SLOWHZ,
-			ax25->t3timer / PR_SLOWHZ,
-			ax25->t3      / PR_SLOWHZ,
-			ax25->idletimer / (PR_SLOWHZ * 60),
-			ax25->idle      / (PR_SLOWHZ * 60),
+			ax25->t1timer / AX25_SLOWHZ,
+			ax25->t1      / AX25_SLOWHZ,
+			ax25->t2timer / AX25_SLOWHZ,
+			ax25->t2      / AX25_SLOWHZ,
+			ax25->t3timer / AX25_SLOWHZ,
+			ax25->t3      / AX25_SLOWHZ,
+			ax25->idletimer / (AX25_SLOWHZ * 60),
+			ax25->idle      / (AX25_SLOWHZ * 60),
 			ax25->n2count, ax25->n2,
-			ax25->rtt     / PR_SLOWHZ,
+			ax25->rtt     / AX25_SLOWHZ,
 			ax25->window,
 			ax25->paclen);
-			
-		len += sprintf(buffer + len, " %s", ax25->dama_slave ? " slave" : "    no");
 
 		if (ax25->sk != NULL) {
-			len += sprintf(buffer + len, " %5d %5d\n",
-				ax25->sk->wmem_alloc,
-				ax25->sk->rmem_alloc);
+			struct sock *s = ax25->sk;
+
+			len += sprintf(buffer + len, " %5d %5d %ld\n",
+				s->wmem_alloc,
+				s->rmem_alloc,
+				s->socket && SOCK_INODE(s->socket) ?
+				SOCK_INODE(s->socket)->i_ino : 0);
 		} else {
 			len += sprintf(buffer + len, "\n");
 		}
-		
+
 		pos = begin + len;
 
 		if (pos < offset) {
 			len   = 0;
 			begin = pos;
 		}
-		
+
 		if (pos > offset + length)
 			break;
 	}
@@ -2318,7 +2309,7 @@
 
 static struct proto_ops ax25_proto_ops = {
 	AF_AX25,
-	
+
 	ax25_create,
 	ax25_dup,
 	ax25_release,
@@ -2341,8 +2332,7 @@
 /*
  *	Called by socket.c on kernel start up
  */
-
-static struct packet_type ax25_packet_type = 
+static struct packet_type ax25_packet_type =
 {
 	0,	/* MUTTER ntohs(ETH_P_AX25),*/
 	0,		/* copy */
@@ -2351,111 +2341,94 @@
 	NULL,
 };
 
-#ifdef CONFIG_BPQETHER
-static struct packet_type bpq_packet_type = 
-{
-	0,	/* MUTTER ntohs(ETH_P_BPQ),*/
-	0,		/* copy */
-	bpq_rcv,
-	NULL,
-	NULL,
-};
-#endif
-
 static struct notifier_block ax25_dev_notifier = {
 	ax25_device_event,
 	0
 };
 
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_ax25_route = {
+	PROC_NET_AX25_ROUTE, 10, "ax25_route",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	ax25_rt_get_info
+};
+static struct proc_dir_entry proc_ax25 = {
+	PROC_NET_AX25, 4, "ax25",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	ax25_get_info
+};
+static struct proc_dir_entry proc_ax25_calls = {
+	PROC_NET_AX25_CALLS, 10, "ax25_calls",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	ax25_cs_get_info
+};
+#endif
+
+static struct symbol_table ax25_syms = {
+#include <linux/symtab_begin.h>
+	X(ax25_encapsulate),
+	X(ax25_rebuild_header),
+	X(ax25_findbyuid),
+	X(ax25_find_cb),
+	X(ax25_linkfail_register),
+	X(ax25_linkfail_release),
+	X(ax25_listen_register),
+	X(ax25_listen_release),
+	X(ax25_protocol_register),
+	X(ax25_protocol_release),
+	X(ax25_send_frame),
+	X(ax25_uid_policy),
+	X(ax25cmp),
+	X(ax2asc),
+	X(asc2ax),
+	X(null_ax25_address),
+#include <linux/symtab_end.h>
+};
+
 void ax25_proto_init(struct net_proto *pro)
 {
 	sock_register(ax25_proto_ops.family, &ax25_proto_ops);
 	ax25_packet_type.type = htons(ETH_P_AX25);
-	dev_add_pack(&ax25_packet_type);	
-#ifdef CONFIG_BPQETHER
-	bpq_packet_type.type  = htons(ETH_P_BPQ);
-	dev_add_pack(&bpq_packet_type);
-#endif
+	dev_add_pack(&ax25_packet_type);
 	register_netdevice_notifier(&ax25_dev_notifier);
-#ifdef CONFIG_PROC_FS			  
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_AX25_ROUTE, 10, "ax25_route",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		ax25_rt_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_AX25, 4, "ax25",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		ax25_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_AX25_CALLS, 10, "ax25_calls",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		ax25_cs_get_info
-	});
-#endif	
-
-	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.32 for Linux NET3.035 (Linux 2.0)\n");
-
-#ifdef CONFIG_BPQETHER
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_AX25_BPQETHER, 13, "ax25_bpqether",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		ax25_bpq_get_info
-	});
+	ax25_register_sysctl();
+	register_symtab(&ax25_syms);
 
-	printk(KERN_INFO "G8BPQ Encapsulation of AX.25 frames enabled\n");
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_ax25_route);
+	proc_net_register(&proc_ax25);
+	proc_net_register(&proc_ax25_calls);
 #endif
+
+	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.35 for Linux NET3.035 (Linux 2.0)\n");
 }
 
 /*
- *	A small shim to dev_queue_xmit to handle the difference between
- *	KISS AX.25 and BPQ AX.25.
+ *	A small shim to dev_queue_xmit to add the KISS control byte, and do
+ *	any packet forwarding in operation.
  */
 void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
 	unsigned char *ptr;
-	
+
 #ifdef CONFIG_FIREWALL
 	if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
 		dev_kfree_skb(skb, FREE_WRITE);
 		return;
 	}
-#endif	
-
-	skb->protocol = htons (ETH_P_AX25);
-
-#ifdef CONFIG_BPQETHER
-	if (dev->type == ARPHRD_ETHER) {
-		static char bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
-		int size;
-
-		if(skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {
-			printk(KERN_CRIT "ax25_queue_xmit: not enough space to add BPQ Ether header\n");
-			dev_kfree_skb(skb, FREE_WRITE);
-			return;
-		}
-
-		size = skb->len;
-	
-		ptr = skb_push(skb, 2);
+#endif
 
-		*ptr++ = (size + 5) % 256;
-		*ptr++ = (size + 5) / 256;
+	skb->protocol = htons(ETH_P_AX25);
+	skb->dev      = ax25_fwd_dev(dev);
+	skb->arp      = 1;
 
-		dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
-		dev_queue_xmit(skb, dev, pri);
-		return;
-	} 
-#endif
+	ptr  = skb_push(skb, 1);
+	*ptr = 0x00;			/* KISS */
 
-	ptr = skb_push(skb, 1);
-	*ptr++ = 0;			/* KISS */
-	dev_queue_xmit(skb, dev, pri);
+	dev_queue_xmit(skb, skb->dev, pri);
 }
 
 /*
@@ -2467,7 +2440,7 @@
  */
 
 #ifdef CONFIG_INET
- 
+
 int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr,
 		void *saddr, unsigned len)
 {
@@ -2475,13 +2448,13 @@
  	unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
 
   	*buff++ = 0;	/* KISS DATA */
-  	
+
 	if (daddr != NULL)
 		memcpy(buff, daddr, dev->addr_len);	/* Address specified */
 
-  	buff[6] &= ~LAPB_C;
-  	buff[6] &= ~LAPB_E;
-  	buff[6] |= SSSID_SPARE;
+  	buff[6] &= ~AX25_CBIT;
+  	buff[6] &= ~AX25_EBIT;
+  	buff[6] |= AX25_SSSID_SPARE;
   	buff += AX25_ADDR_LEN;
 
   	if (saddr != NULL)
@@ -2489,49 +2462,54 @@
   	else
   		memcpy(buff, dev->dev_addr, dev->addr_len);
 
-  	buff[6] &= ~LAPB_C;
-  	buff[6] |= LAPB_E;
-  	buff[6] |= SSSID_SPARE;
+  	buff[6] &= ~AX25_CBIT;
+  	buff[6] |= AX25_EBIT;
+  	buff[6] |= AX25_SSSID_SPARE;
   	buff   += AX25_ADDR_LEN;
 
-  	*buff++ = LAPB_UI;	/* UI */
+  	*buff++ = AX25_UI;	/* UI */
 
   	/* Append a suitable AX.25 PID */
   	switch (type) {
   		case ETH_P_IP:
   			*buff++ = AX25_P_IP;
  			break;
-
   		case ETH_P_ARP:
   			*buff++ = AX25_P_ARP;
   			break;
   		default:
-  			printk(KERN_ERR "wrong protocol type 0x%x2.2\n", type);
+  			printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type);
   			*buff++ = 0;
   			break;
  	}
-	
+
 	if (daddr != NULL)
 	  	return AX25_HEADER_LEN;
 
 	return -AX25_HEADER_LEN;	/* Unfinished header */
 }
 
-int ax25_rebuild_header(unsigned char *bp, struct device *dev, unsigned long dest, struct sk_buff *skb)
+int ax25_rebuild_header(void *buf, struct device *dev, unsigned long dest, struct sk_buff *skb)
 {
 	struct sk_buff *ourskb;
+	unsigned char *bp = (unsigned char *)buf;
+	ax25_address *src, *dst;
+	ax25_digi *digi;
 	int mode;
 
+	dst = (ax25_address *)(bp + 1);
+	src = (ax25_address *)(bp + 8);
+
   	if (arp_find(bp + 1, dest, dev, dev->pa_addr, skb))
   		return 1;
 
+	digi = ax25_rt_find_path(dst, dev);
+
 	if (bp[16] == AX25_P_IP) {
-		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
-		if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) {
+		mode = ax25_rt_mode_get(dst, dev);
+
+		if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE))) {
 			/*
-			 *	This is a workaround to try to keep the device locking
-			 *	straight until skb->free=0 is abolished post 1.4.
-			 *
 			 *	We clone the buffer and release the original thereby
 			 *	keeping it straight
 			 *
@@ -2541,7 +2519,7 @@
 			 *	as we have pulled the frame from the queue by
 			 *	freeing it).
 			 */
-			if ((ourskb = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+			if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
 				dev_kfree_skb(skb, FREE_WRITE);
 				return 1;
 			}
@@ -2555,30 +2533,59 @@
 
 			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
 
-			ax25_send_frame(ourskb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
+			ax25_send_frame(ourskb, ax25_dev_get_value(dev, AX25_VALUES_PACLEN), src, dst, digi, dev);
 
 			return 1;
 		}
 	}
 
-  	bp[7]  &= ~LAPB_C;
-  	bp[7]  &= ~LAPB_E;
-  	bp[7]  |= SSSID_SPARE;
-
-  	bp[14] &= ~LAPB_C;
-  	bp[14] |= LAPB_E;
-  	bp[14] |= SSSID_SPARE;
-  	
-  	/*
-  	 * dl1bke 960317: we use ax25_queue_xmit here to allow mode datagram
-  	 *		  over ethernet. I don't know if this is valid, though.
-  	 */
-	ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
+  	bp[7]  &= ~AX25_CBIT;
+  	bp[7]  &= ~AX25_EBIT;
+  	bp[7]  |= AX25_SSSID_SPARE;
+
+  	bp[14] &= ~AX25_CBIT;
+  	bp[14] |= AX25_EBIT;
+  	bp[14] |= AX25_SSSID_SPARE;
+
+	skb_pull(skb, AX25_KISS_HEADER_LEN);
+
+	if (digi != NULL)
+		ax25_rt_build_path(skb, src, dst, digi);
+
 	ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
 
   	return 1;
-}	
+}
+
+#endif
 
+#ifdef MODULE
+int init_module(void)
+{
+	ax25_proto_init(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_AX25_ROUTE);
+	proc_net_unregister(PROC_NET_AX25);
+	proc_net_unregister(PROC_NET_AX25_CALLS);
+	proc_net_unregister(PROC_NET_AX25_ROUTE);
+#endif
+	ax25_rt_free();
+
+	ax25_unregister_sysctl();
+
+	unregister_netdevice_notifier(&ax25_dev_notifier);
+
+	ax25_packet_type.type = htons(ETH_P_AX25);
+	dev_remove_pack(&ax25_packet_type);
+
+	sock_unregister(AF_AX25);
+}
 #endif
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v2.0.34/linux/net/ax25/ax25_in.c	Sat Aug 10 00:03:16 1996
+++ linux/net/ax25/ax25_in.c	Mon Jul 13 13:47:40 1998
@@ -1,8 +1,5 @@
 /*
- *	AX.25 release 032
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	AX.25 release 035
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -32,12 +29,14 @@
  *			Joerg(DL1BKE)   Renamed it to "IDLE" with a slightly
  *					different behaviour. Fixed defrag
  *					routine (I hope)
- *	AX.25 032	Jonathan(G4KLX)	Remove auto-router.
- *			Darryl(G7LED)	AX.25 segmentation fixed.
+ *	AX.25 032	Darryl(G7LED)	AX.25 segmentation fixed.
+ *	AX.25 033	Jonathan(G4KLX)	Remove auto-router.
+ *					Modularisation changes.
+ *			Joerg(DL1BKE)	Fixed 2.0.x specific IP over AX.25 problem.
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -59,9 +58,6 @@
 #include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
-#ifdef CONFIG_NETROM
-#include <net/netrom.h>
-#endif
 
 static int ax25_rx_iframe(ax25_cb *, struct sk_buff *);
 
@@ -72,13 +68,12 @@
 static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
 {
 	struct sk_buff *skbn, *skbo;
-	int hdrlen, nhdrlen;
-	
+
 	if (ax25->fragno != 0) {
-		if (!(*skb->data & SEG_FIRST)) {
-			if ((ax25->fragno - 1) == (*skb->data & SEG_REM)) {
+		if (!(*skb->data & AX25_SEG_FIRST)) {
+			if ((ax25->fragno - 1) == (*skb->data & AX25_SEG_REM)) {
 				/* Enqueue fragment */
-				ax25->fragno = *skb->data & SEG_REM;
+				ax25->fragno = *skb->data & AX25_SEG_REM;
 				skb_pull(skb, 1);	/* skip fragno */
 				ax25->fraglen += skb->len;
 				skb_queue_tail(&ax25->frag_queue, skb);
@@ -91,52 +86,33 @@
 						return 1;
 					}
 
-					skbn->free = 1;
-					skbn->arp  = 1;
-					skbn->dev  = ax25->device;
-
-					if (ax25->sk != NULL) {
-						skbn->sk = ax25->sk;
-						atomic_add(skbn->truesize, &ax25->sk->rmem_alloc);
-					}
-
 					skb_reserve(skbn, AX25_MAX_HEADER_LEN);
 
-					/* Get first fragment from queue */
-					skbo = skb_dequeue(&ax25->frag_queue);
-					hdrlen  = skbo->data - skbo->h.raw;
-					nhdrlen = hdrlen - 2;
-
-					skb_push(skbo, hdrlen);
-					skb_push(skbn, nhdrlen);
+					skbn->free  = 1;
+					skbn->dev   = ax25->device;
 					skbn->h.raw = skbn->data;
 
-					/* Copy AX.25 headers */
-					memcpy(skbn->data, skbo->data, nhdrlen);
-					skb_pull(skbn, nhdrlen);
-					skb_pull(skbo, hdrlen);
-
 					/* Copy data from the fragments */
-					do {
+					while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) {
 						memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
 						kfree_skb(skbo, FREE_READ);
-					} while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL);
+					}
 
 					ax25->fraglen = 0;
 
 					if (ax25_rx_iframe(ax25, skbn) == 0)
 						kfree_skb(skbn, FREE_READ);
 				}
-				
+
 				return 1;
 			}
 		}
 	} else {
 		/* First fragment received */
-		if (*skb->data & SEG_FIRST) {
+		if (*skb->data & AX25_SEG_FIRST) {
 			while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL)
 				kfree_skb(skbo, FREE_READ);
-			ax25->fragno = *skb->data & SEG_REM;
+			ax25->fragno = *skb->data & AX25_SEG_REM;
 			skb_pull(skb, 1);		/* skip fragno */
 			ax25->fraglen = skb->len;
 			skb_queue_tail(&ax25->frag_queue, skb);
@@ -153,46 +129,46 @@
  */
 static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
 {
+	int (*func)(struct sk_buff *, ax25_cb *);
+	struct sk_buff *skbn;
 	volatile int queued = 0;
 	unsigned char pid;
-	
+
 	if (skb == NULL) return 0;
 
 	ax25->idletimer = ax25->idle;
-	
+
 	pid = *skb->data;
 
-	switch (pid) {
-#ifdef CONFIG_NETROM
-		case AX25_P_NETROM:
-			if (ax25_dev_get_value(ax25->device, AX25_VALUES_NETROM)) {
-				skb_pull(skb, 1);	/* Remove PID */
-				queued = nr_route_frame(skb, ax25);
-			}
-			break;
-#endif
 #ifdef CONFIG_INET
-		case AX25_P_IP:
-			skb_pull(skb, 1);	/* Remove PID */
-			skb->h.raw = skb->data;
-			ip_rcv(skb, ax25->device, NULL);	/* Wrong ptype */
-			queued = 1;
-			break;
+	if (pid == AX25_P_IP) {
+		if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+			kfree_skb(skb, FREE_READ);
+			skb = skbn;
+		}
+		skb_pull(skb, 1);	/* Remove PID */
+		skb->h.raw = skb->data;
+		ip_rcv(skb, ax25->device, NULL);	/* Wrong ptype */
+		return 1;
+	}
 #endif
-		case AX25_P_SEGMENT:
-			skb_pull(skb, 1);	/* Remove PID */
-			queued = ax25_rx_fragment(ax25, skb);
-			break;
+	if (pid == AX25_P_SEGMENT) {
+		skb_pull(skb, 1);	/* Remove PID */
+		return ax25_rx_fragment(ax25, skb);
+	}
 
-		default:
-			if (ax25->sk != NULL && ax25_dev_get_value(ax25->device, AX25_VALUES_TEXT) && ax25->sk->protocol == pid) {
-				if (sock_queue_rcv_skb(ax25->sk, skb) == 0) {
-					queued = 1;
-				} else {
-					ax25->condition |= OWN_RX_BUSY_CONDITION;
-				}
-			}
-			break;
+	if ((func = ax25_protocol_function(pid)) != NULL) {
+		skb_pull(skb, 1);	/* Remove PID */
+		return (*func)(skb, ax25);
+	}
+
+	if (ax25->sk != NULL && ax25_dev_get_value(ax25->device, AX25_VALUES_CONMODE) == 2) {
+		if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) {
+			if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
+				queued = 1;
+			else
+				ax25->condition |= AX25_COND_OWN_RX_BUSY;
+		}
 	}
 
 	return queued;
@@ -206,26 +182,25 @@
 static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type, int dama)
 {
 	switch (frametype) {
-		case SABM:
-			ax25->modulus = MODULUS;
+		case AX25_SABM:
+			ax25->modulus = AX25_MODULUS;
 			ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
 			break;
 
-		case SABME:
-			ax25->modulus = EMODULUS;
+		case AX25_SABME:
+			ax25->modulus = AX25_EMODULUS;
 			ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
 			break;
 
-		case DISC:
-			ax25_send_control(ax25, DM, pf, C_RESPONSE);
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
 			break;
 
-		case UA:
+		case AX25_UA:
 			if (pf || dama) {
 				if (dama) ax25_dama_on(ax25); /* bke */
-					
 				ax25_calculate_rtt(ax25);
 				ax25->t1timer = 0;
 				ax25->t3timer = ax25->t3;
@@ -236,7 +211,6 @@
 				ax25->state   = AX25_STATE_3;
 				ax25->n2count = 0;
 				ax25->dama_slave = dama;	/* bke */
-					
 				if (ax25->sk != NULL) {
 					ax25->sk->state = TCP_ESTABLISHED;
 					/* For WAIT_SABM connections we will produce an accept ready socket here */
@@ -246,20 +220,12 @@
 			}
 			break;
 
-		case DM:
+		case AX25_DM:
 			if (pf) {
-				if (ax25->modulus == MODULUS) {
-					ax25_clear_queues(ax25);
-					ax25->state = AX25_STATE_0;
-					if (ax25->sk != NULL) {
-						ax25->sk->state = TCP_CLOSE;
-						ax25->sk->err   = ECONNREFUSED;
-						if (!ax25->sk->dead)
-							ax25->sk->state_change(ax25->sk);
-						ax25->sk->dead  = 1;
-					}
+				if (ax25->modulus == AX25_MODULUS) {
+					ax25_disconnect(ax25, ECONNREFUSED);
 				} else {
-					ax25->modulus = MODULUS;
+					ax25->modulus = AX25_MODULUS;
 					ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
 				}
 			}
@@ -267,7 +233,7 @@
 
 		default:
 			if (dama && pf)
-				ax25_send_control(ax25, SABM, POLLON, C_COMMAND);
+				ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
 			break;
 	}
 
@@ -282,71 +248,35 @@
 static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
 {
 	switch (frametype) {
-		case SABM:
-		case SABME:
-			ax25_send_control(ax25, DM, pf, C_RESPONSE);
+		case AX25_SABM:
+		case AX25_SABME:
+			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
 			if (ax25->dama_slave)
-				ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 			break;
 
-		case DISC:
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
-			if (ax25->dama_slave) {
-				ax25->state = AX25_STATE_0;
-				ax25_dama_off(ax25);
-
-				if (ax25->sk != NULL) {
-					ax25->sk->state = TCP_CLOSE;
-					ax25->sk->err   = 0;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead  = 1;
-				}
-			}
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			if (ax25->dama_slave) ax25_disconnect(ax25, 0);
 			break;
 
-		case UA:
-			if (pf) {
-				ax25->state = AX25_STATE_0;
-				ax25_dama_off(ax25);
-
-				if (ax25->sk != NULL) {
-					ax25->sk->state = TCP_CLOSE;
-					ax25->sk->err   = 0;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead  = 1;
-				}
-			}
+		case AX25_DM:
+		case AX25_UA:
+			if (pf) ax25_disconnect(ax25, 0);
 			break;
 
-		case DM:
-			if (pf) {
-				ax25->state = AX25_STATE_0;
-				ax25_dama_off(ax25);
-					
-				if (ax25->sk != NULL) {
-					ax25->sk->state = TCP_CLOSE;
-					ax25->sk->err   = 0;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead  = 1;
-				}
-			}
-			break;
-
-		case I:
-		case REJ:
-		case RNR:
-		case RR:
+		case AX25_I:
+		case AX25_REJ:
+		case AX25_RNR:
+		case AX25_RR:
 			if (pf) {
 				if (ax25->dama_slave)
-					ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+					ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 				else
-					ax25_send_control(ax25, DM, POLLON, C_RESPONSE);
+					ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
 			}
 			break;
-				
+
 		default:
 			break;
 	}
@@ -364,28 +294,17 @@
 	int queued = 0;
 
 	switch (frametype) {
-		case SABM:
-			if (dama) ax25_dama_on(ax25);
-				
-			ax25->modulus   = MODULUS;
-			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
-			ax25->condition = 0x00;
-			ax25->t1timer   = 0;
-			ax25->t3timer   = ax25->t3;
-			ax25->idletimer = ax25->idle;
-			ax25->vs        = 0;
-			ax25->va        = 0;
-			ax25->vr        = 0;
-			ax25->dama_slave = dama;
-			break;
-
-		case SABME:
+		case AX25_SABM:
+		case AX25_SABME:
 			if (dama) ax25_dama_on(ax25);
-				
-			ax25->modulus   = EMODULUS;
-			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
+			if (frametype == AX25_SABM) {
+				ax25->modulus = AX25_MODULUS;
+				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+			} else {
+				ax25->modulus = AX25_EMODULUS;
+				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+			}
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
 			ax25->condition = 0x00;
 			ax25->t1timer   = 0;
 			ax25->t3timer   = ax25->t3;
@@ -394,94 +313,66 @@
 			ax25->va        = 0;
 			ax25->vr        = 0;
 			ax25->dama_slave = dama;
+			ax25_requeue_frames(ax25);
 			break;
 
-		case DISC:
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			
-			if (ax25->sk != NULL) {
-				ax25->sk->state = TCP_CLOSE;
-				ax25->sk->err   = 0;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead  = 1;
-			}
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25_disconnect(ax25, 0);
 			break;
 
-		case DM:
-			ax25_clear_queues(ax25);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			if (ax25->sk) {
-				ax25->sk->state = TCP_CLOSE;
-				ax25->sk->err   = ECONNRESET;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead         = 1;
-			}
+		case AX25_DM:
+			ax25_disconnect(ax25, ECONNRESET);
 			break;
 
-		case RNR:
-			ax25->condition |= PEER_RX_BUSY_CONDITION;
-			ax25_check_need_response(ax25, type, pf);
+		case AX25_RR:
+		case AX25_RNR:
+			if (frametype == AX25_RR)
+				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			else
+				ax25->condition |= AX25_COND_PEER_RX_BUSY;
+			if (!ax25->dama_slave && type == AX25_COMMAND && pf)
+				ax25_enquiry_response(ax25);
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_check_iframes_acked(ax25, nr);
-				dama_check_need_response(ax25, type, pf);
+				if (ax25->dama_slave && type == AX25_COMMAND && pf)
+					dama_enquiry_response(ax25);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
 			}
 			break;
-			
-		case RR:
-			ax25->condition &= ~PEER_RX_BUSY_CONDITION;
-			ax25_check_need_response(ax25, type, pf);
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_check_iframes_acked(ax25, nr);
-				dama_check_need_response(ax25, type, pf);
-			} else {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
-				
-		case REJ:
-			ax25->condition &= ~PEER_RX_BUSY_CONDITION;
-			ax25_check_need_response(ax25, type, pf);
+
+		case AX25_REJ:
+			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			if (!ax25->dama_slave && type == AX25_COMMAND && pf)
+				ax25_enquiry_response(ax25);
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_frames_acked(ax25, nr);
 				ax25_calculate_rtt(ax25);
 				ax25->t1timer = 0;
 				ax25->t3timer = ax25->t3;
 				ax25_requeue_frames(ax25);
-				dama_check_need_response(ax25, type, pf);
+				if (ax25->dama_slave && type == AX25_COMMAND && pf)
+					dama_enquiry_response(ax25);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
 			}
 			break;
-			
-		case I:
-#ifndef AX25_BROKEN_NETMAC
-			if (type != C_COMMAND)
-				break;
-#endif
+
+		case AX25_I:
 			if (!ax25_validate_nr(ax25, nr)) {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
 				break;
 			}
-			if (ax25->condition & PEER_RX_BUSY_CONDITION) {
+			if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
 				ax25_frames_acked(ax25, nr);
 			} else {
 				ax25_check_iframes_acked(ax25, nr);
 			}
-			if (ax25->condition & OWN_RX_BUSY_CONDITION) {
+			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
 				if (pf)	{
 					if (ax25->dama_slave)
 						dama_enquiry_response(ax25);
@@ -493,30 +384,22 @@
 			if (ns == ax25->vr) {
 				ax25->vr = (ax25->vr + 1) % ax25->modulus;
 				queued = ax25_rx_iframe(ax25, skb);
-				if (ax25->condition & OWN_RX_BUSY_CONDITION) {
+				if (ax25->condition & AX25_COND_OWN_RX_BUSY)
 					ax25->vr = ns;	/* ax25->vr - 1 */
-					if (pf) {
-						if (ax25->dama_slave)
-							dama_enquiry_response(ax25);
-						else
-							ax25_enquiry_response(ax25);
-					}
-					break;
-				}
-				ax25->condition &= ~REJECT_CONDITION;
+				ax25->condition &= ~AX25_COND_REJECT;
 				if (pf) {
 					if (ax25->dama_slave)
 						dama_enquiry_response(ax25);
 					else
 						ax25_enquiry_response(ax25);
 				} else {
-					if (!(ax25->condition & ACK_PENDING_CONDITION)) {
-						ax25->t2timer = ax25->t2;
-						ax25->condition |= ACK_PENDING_CONDITION;
+					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+						ax25->t2timer    = ax25->t2;
+						ax25->condition |= AX25_COND_ACK_PENDING;
 					}
 				}
 			} else {
-				if (ax25->condition & REJECT_CONDITION) {
+				if (ax25->condition & AX25_COND_REJECT) {
 					if (pf) {
 						if (ax25->dama_slave)
 							dama_enquiry_response(ax25);
@@ -524,18 +407,18 @@
 							ax25_enquiry_response(ax25);
 					}
 				} else {
-					ax25->condition |= REJECT_CONDITION;
+					ax25->condition |= AX25_COND_REJECT;
 					if (ax25->dama_slave)
 						dama_enquiry_response(ax25);
 					else
-						ax25_send_control(ax25, REJ, pf, C_RESPONSE);
-					ax25->condition &= ~ACK_PENDING_CONDITION;
+						ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
 				}
 			}
 			break;
 
-		case FRMR:
-		case ILLEGAL:
+		case AX25_FRMR:
+		case AX25_ILLEGAL:
 			ax25_establish_data_link(ax25);
 			ax25->state = AX25_STATE_1;
 			break;
@@ -557,13 +440,17 @@
 	int queued = 0;
 
 	switch (frametype) {
-		case SABM:
+		case AX25_SABM:
+		case AX25_SABME:
 			if (dama) ax25_dama_on(ax25);
-				
-			ax25->dama_slave = dama;
-			ax25->modulus   = MODULUS;
-			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
+			if (frametype == AX25_SABM) {
+				ax25->modulus = AX25_MODULUS;
+				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+			} else {
+				ax25->modulus = AX25_EMODULUS;
+				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+			}
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
 			ax25->condition = 0x00;
 			ax25->t1timer   = 0;
 			ax25->t3timer   = ax25->t3;
@@ -573,88 +460,26 @@
 			ax25->vr        = 0;
 			ax25->state     = AX25_STATE_3;
 			ax25->n2count   = 0;
-			break;
-
-		case SABME:
-			if (dama) ax25_dama_on(ax25);
-				
 			ax25->dama_slave = dama;
-			ax25->modulus   = EMODULUS;
-			ax25->window    = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
-			ax25->condition = 0x00;
-			ax25->t1timer   = 0;
-			ax25->t3timer   = ax25->t3;
-			ax25->idletimer = ax25->idle;
-			ax25->vs        = 0;
-			ax25->va        = 0;
-			ax25->vr        = 0;
-			ax25->state     = AX25_STATE_3;
-			ax25->n2count   = 0;
+			ax25_requeue_frames(ax25);
 			break;
 
-		case DISC:
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, UA, pf, C_RESPONSE);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			
-			if (ax25->sk != NULL) {
-				ax25->sk->state = TCP_CLOSE;
-				ax25->sk->err   = 0;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead  = 1;
-			}
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25_disconnect(ax25, 0);
 			break;
 
-		case DM:
-			ax25_clear_queues(ax25);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			
-			if (ax25->sk != NULL) {
-				ax25->sk->state = TCP_CLOSE;
-				ax25->sk->err   = ECONNRESET;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead  = 1;
-			}
+		case AX25_DM:
+			ax25_disconnect(ax25, ECONNRESET);
 			break;
 
-		case RNR:
-			ax25->condition |= PEER_RX_BUSY_CONDITION;
-			if (type == C_RESPONSE && pf) {
-				ax25->t1timer = 0;
-				if (ax25_validate_nr(ax25, nr)) {
-					ax25_frames_acked(ax25, nr);
-					if (ax25->vs == ax25->va) {
-						ax25->t3timer = ax25->t3;
-						ax25->n2count = 0;
-						ax25->state   = AX25_STATE_3;
-					}
-				} else {
-					ax25_nr_error_recovery(ax25);
-					ax25->state = AX25_STATE_1;
-				}
-				break;
-			}
-			 
-			ax25_check_need_response(ax25, type, pf);
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_frames_acked(ax25, nr);
-				dama_check_need_response(ax25, type, pf);
-			} else {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
-			
-		case RR:
-			ax25->condition &= ~PEER_RX_BUSY_CONDITION;
-			if (pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND))) {
+		case AX25_RR:
+		case AX25_RNR:
+			if (frametype == AX25_RR)
+				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			else
+				ax25->condition |= AX25_COND_PEER_RX_BUSY;
+			if (type == AX25_RESPONSE && pf) {
 				ax25->t1timer = 0;
 				if (ax25_validate_nr(ax25, nr)) {
 					ax25_frames_acked(ax25, nr);
@@ -665,27 +490,27 @@
 					} else {
 						ax25_requeue_frames(ax25);
 					}
-					dama_check_need_response(ax25, type, pf);
 				} else {
 					ax25_nr_error_recovery(ax25);
 					ax25->state = AX25_STATE_1;
 				}
 				break;
 			}
-
-			ax25_check_need_response(ax25, type, pf);
+			if (!ax25->dama_slave && type == AX25_COMMAND && pf)
+				ax25_enquiry_response(ax25);
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_frames_acked(ax25, nr);
-				dama_check_need_response(ax25, type, pf);
+				if (ax25->dama_slave && type == AX25_COMMAND && pf)
+					dama_enquiry_response(ax25);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
 			}
 			break;
 
-		case REJ:
-			ax25->condition &= ~PEER_RX_BUSY_CONDITION;
-			if (pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND))) {
+		case AX25_REJ:
+			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			if (pf && (type == AX25_RESPONSE || (ax25->dama_slave && type == AX25_COMMAND))) {
 				ax25->t1timer = 0;
 				if (ax25_validate_nr(ax25, nr)) {
 					ax25_frames_acked(ax25, nr);
@@ -696,39 +521,35 @@
 					} else {
 						ax25_requeue_frames(ax25);
 					}
-					dama_check_need_response(ax25, type, pf);
+					if (ax25->dama_slave && type == AX25_COMMAND && pf)
+						dama_enquiry_response(ax25);
 				} else {
 					ax25_nr_error_recovery(ax25);
 					ax25->state = AX25_STATE_1;
 				}
 				break;
 			}
-			
-			ax25_check_need_response(ax25, type, pf);	
+			if (!ax25->dama_slave && type == AX25_COMMAND && pf)
+				ax25_enquiry_response(ax25);
 			if (ax25_validate_nr(ax25, nr)) {
 				ax25_frames_acked(ax25, nr);
-				if(ax25->vs != ax25->va) {
-					ax25_requeue_frames(ax25);
-				}
-				dama_check_need_response(ax25, type, pf);
+				ax25_requeue_frames(ax25);
+				if (ax25->dama_slave && type == AX25_COMMAND && pf)
+					dama_enquiry_response(ax25);
 			} else {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
 			}
 			break;
 
-		case I:
-#ifndef	AX25_BROKEN_NETMAC
-			if (type != C_COMMAND)
-				break;
-#endif
+		case AX25_I:
 			if (!ax25_validate_nr(ax25, nr)) {
 				ax25_nr_error_recovery(ax25);
 				ax25->state = AX25_STATE_1;
 				break;
 			}
 			ax25_frames_acked(ax25, nr);
-			if (ax25->condition & OWN_RX_BUSY_CONDITION) {
+			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
 				if (pf) {
 					if (ax25->dama_slave)
 						ax25_enquiry_response(ax25);
@@ -740,30 +561,22 @@
 			if (ns == ax25->vr) {
 				ax25->vr = (ax25->vr + 1) % ax25->modulus;
 				queued = ax25_rx_iframe(ax25, skb);
-				if (ax25->condition & OWN_RX_BUSY_CONDITION) {
+				if (ax25->condition & AX25_COND_OWN_RX_BUSY)
 					ax25->vr = ns;	/* ax25->vr - 1 */
-					if (pf) {
-						if (ax25->dama_slave)
-							dama_enquiry_response(ax25);
-						else
-							ax25_enquiry_response(ax25);
-					}
-					break;
-				}
-				ax25->condition &= ~REJECT_CONDITION;
+				ax25->condition &= ~AX25_COND_REJECT;
 				if (pf) {
 					if (ax25->dama_slave)
 						dama_enquiry_response(ax25);
 					else
 						ax25_enquiry_response(ax25);
 				} else {
-					if (!(ax25->condition & ACK_PENDING_CONDITION)) {
-						ax25->t2timer = ax25->t2;
-						ax25->condition |= ACK_PENDING_CONDITION;
+					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+						ax25->t2timer    = ax25->t2;
+						ax25->condition |= AX25_COND_ACK_PENDING;
 					}
 				}
 			} else {
-				if (ax25->condition & REJECT_CONDITION) {
+				if (ax25->condition & AX25_COND_REJECT) {
 					if (pf) {
 						if (ax25->dama_slave)
 							dama_enquiry_response(ax25);
@@ -771,18 +584,18 @@
 							ax25_enquiry_response(ax25);
 					}
 				} else {
-					ax25->condition |= REJECT_CONDITION;
+					ax25->condition |= AX25_COND_REJECT;
 					if (ax25->dama_slave)
 						dama_enquiry_response(ax25);
 					else
-						ax25_send_control(ax25, REJ, pf, C_RESPONSE);
-					ax25->condition &= ~ACK_PENDING_CONDITION;
+						ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
 				}
 			}
 			break;
-		
-		case FRMR:
-		case ILLEGAL:
+
+		case AX25_FRMR:
+		case AX25_ILLEGAL:
 			ax25_establish_data_link(ax25);
 			ax25->state = AX25_STATE_1;
 			break;
@@ -800,15 +613,9 @@
 int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
 {
 	int queued = 0, frametype, ns, nr, pf;
-	
-	if (ax25->sk != NULL && ax25->state == AX25_STATE_0 && ax25->sk->dead)
-		return queued;
-
-	if (ax25->state != AX25_STATE_1 && ax25->state != AX25_STATE_2 &&
-	    ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) {
-		printk("ax25_process_rx_frame: frame received - state = %d\n", ax25->state);
-		return queued;
-	}
+
+	if (ax25->state == AX25_STATE_0)
+		return 0;
 
 	del_timer(&ax25->timer);
 
diff -u --recursive --new-file v2.0.34/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.0.34/linux/net/ax25/ax25_out.c	Sat Aug 10 00:03:16 1996
+++ linux/net/ax25/ax25_out.c	Mon Jul 13 13:47:40 1998
@@ -1,8 +1,5 @@
 /*
- *	AX.25 release 032
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	AX.25 release 035
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -32,7 +29,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -55,48 +52,37 @@
 #include <linux/interrupt.h>
 
 /*
- * All outgoing AX.25 I frames pass via this routine. Therefore this is
- * where the fragmentation of frames takes place.
+ *	All outgoing AX.25 I frames pass via this routine. Therefore this is
+ *	where the fragmentation of frames takes place. If fragment is set to
+ *	zero then we are not allowed to do fragmentation, even if the frame
+ *	is too large.
  */
-void ax25_output(ax25_cb *ax25, struct sk_buff *skb)
+void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
 {
 	struct sk_buff *skbn;
 	unsigned char *p;
-	int frontlen, mtu, len, fragno, ka9qfrag, first = 1;
+	int frontlen, len, fragno, ka9qfrag, first = 1;
 	long flags;
-	
-	/*
-	 * dl1bke 960301: We use the new PACLEN parameter as MTU of the AX.25 layer.
-	 *                This will (hopefully) allow user programs to write() data
-	 *                w/o having to think of the maximal amount of data we can
-	 *		  send with one call. It's called PACLEN to (1) avoid confusion
-	 *		  with (IP) MTU and (2) TAPR calls this PACLEN, too ;-)
-	 */
-
-	mtu = ax25->paclen;
-	
-	if ((skb->len - 1) > mtu) {
+
+	if ((skb->len - 1) > paclen) {
 		if (*skb->data == AX25_P_TEXT) {
 			skb_pull(skb, 1); /* skip PID */
 			ka9qfrag = 0;
 		} else {
-			mtu -= 2;	/* Allow for fragment control info */
+			paclen -= 2;	/* Allow for fragment control info */
 			ka9qfrag = 1;
 		}
-		
-		fragno = skb->len / mtu;
-		if (skb->len % mtu == 0) fragno--;
+
+		fragno = skb->len / paclen;
+		if (skb->len % paclen == 0) fragno--;
 
 		frontlen = skb_headroom(skb);	/* Address space + CTRL */
 
 		while (skb->len > 0) {
 			save_flags(flags); 
 			cli();
-			/* 
-			 * do _not_ use sock_alloc_send_skb, our socket may have
-			 * sk->shutdown set...
-			 */
-			if ((skbn = alloc_skb(mtu + 2 + frontlen, GFP_ATOMIC)) == NULL) {
+
+			if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) {
 				restore_flags(flags);
 				printk(KERN_DEBUG "ax25_output: alloc_skb returned NULL\n");
 				if (skb_device_locked(skb))
@@ -105,17 +91,15 @@
 			}
 
 			skbn->sk   = skb->sk;
-			
-			if (skbn->sk)
+			skbn->free = 1;
+
+			if (skbn->sk != NULL)
 				atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
-			
+
 			restore_flags(flags);
-			
-			skbn->free = 1;
-			skbn->arp  = 1;
 
-			len = (mtu > skb->len) ? skb->len : mtu;
-			
+			len = (paclen > skb->len) ? skb->len : paclen;
+
 			if (ka9qfrag == 1) {
 				skb_reserve(skbn, frontlen + 2);
 
@@ -126,7 +110,7 @@
 
 				*p = fragno--;
 				if (first) {
-					*p |= SEG_FIRST;
+					*p |= AX25_SEG_FIRST;
 					first = 0;
 				}
 			} else {
@@ -139,8 +123,7 @@
 			skb_pull(skb, len);
 			skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */
 		}
-		
-		skb->free = 1;
+
 		kfree_skb(skb, FREE_WRITE);
 	} else {
 		skb_queue_tail(&ax25->write_queue, skb);	  /* Throw it on the queue */
@@ -163,23 +146,25 @@
 	if (skb == NULL)
 		return;
 
-	if (ax25->modulus == MODULUS) {
+	if (ax25->modulus == AX25_MODULUS) {
 		frame = skb_push(skb, 1);
 
-		*frame = I;
-		*frame |= (poll_bit) ? PF : 0;
+		*frame = AX25_I;
+		*frame |= (poll_bit) ? AX25_PF : 0;
 		*frame |= (ax25->vr << 5);
 		*frame |= (ax25->vs << 1);
 	} else {
 		frame = skb_push(skb, 2);
 
-		frame[0] = I;
+		frame[0] = AX25_I;
 		frame[0] |= (ax25->vs << 1);
-		frame[1] = (poll_bit) ? EPF : 0;
+		frame[1] = (poll_bit) ? AX25_EPF : 0;
 		frame[1] |= (ax25->vr << 1);
 	}
 
-	ax25_transmit_buffer(ax25, skb, C_COMMAND);	
+	ax25->idletimer = ax25->idle;
+
+	ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
 }
 
 void ax25_kick(ax25_cb *ax25)
@@ -193,8 +178,8 @@
 	start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs;
 	end   = (ax25->va + ax25->window) % ax25->modulus;
 
-	if (!(ax25->condition & PEER_RX_BUSY_CONDITION) &&
-	    start != end                                   &&
+	if (!(ax25->condition & AX25_COND_PEER_RX_BUSY) &&
+	    start != end                                &&
 	    skb_peek(&ax25->write_queue) != NULL) {
 
 		ax25->vs = start;
@@ -216,18 +201,20 @@
 				break;
 			}
 
+			skbn->sk = skb->sk;
+
+			if (skbn->sk != NULL)
+				atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+
 			next = (ax25->vs + 1) % ax25->modulus;
-#ifdef notdef
-			last = (next == end) || skb_peek(&ax25->write_queue) == NULL;
-#else
 			last = (next == end);
-#endif
+
 			/*
 			 * Transmit the frame copy.
 			 * bke 960114: do not set the Poll bit on the last frame
 			 * in DAMA mode.
 			 */
-			ax25_send_iframe(ax25, skbn, (last && !ax25->dama_slave) ? POLLON : POLLOFF);
+			ax25_send_iframe(ax25, skbn, (last && !ax25->dama_slave) ? AX25_POLLON : AX25_POLLOFF);
 
 			ax25->vs = next;
 
@@ -235,12 +222,10 @@
 			 * Requeue the original data frame.
 			 */
 			skb_queue_tail(&ax25->ack_queue, skb);
-#ifdef notdef
-		} while (!last);
-#else
+
 		} while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL);
-#endif
-		ax25->condition &= ~ACK_PENDING_CONDITION;
+
+		ax25->condition &= ~AX25_COND_ACK_PENDING;
 
 		if (ax25->t1timer == 0) {
 			ax25->t3timer = 0;
@@ -256,13 +241,7 @@
 	unsigned char *ptr;
 
 	if (ax25->device == NULL) {
-		if (ax25->sk != NULL) {
-			ax25->sk->state = TCP_CLOSE;
-			ax25->sk->err   = ENETUNREACH;
-			if (!ax25->sk->dead)
-				ax25->sk->state_change(ax25->sk);
-			ax25->sk->dead  = 1;
-		}
+		ax25_disconnect(ax25, ENETUNREACH);
 		return;
 	}
 
@@ -276,8 +255,6 @@
 	ptr = skb_push(skb, size_ax25_addr(ax25->digipeat));
 	build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
 
-	skb->arp = 1;
-
 	ax25_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
 }
 
@@ -296,12 +273,11 @@
 	ax25->condition = 0x00;
 	ax25->n2count   = 0;
 
-	if (ax25->modulus == MODULUS) {
-		ax25_send_control(ax25, SABM, POLLON, C_COMMAND);
-	} else {
-		ax25_send_control(ax25, SABME, POLLON, C_COMMAND);
-	}
-	
+	if (ax25->modulus == AX25_MODULUS)
+		ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+	else
+		ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
+
 	ax25->t3timer = 0;
 	ax25->t2timer = 0;
 	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
@@ -309,34 +285,34 @@
 
 void ax25_transmit_enquiry(ax25_cb *ax25)
 {
-	if (ax25->condition & OWN_RX_BUSY_CONDITION)
-		ax25_send_control(ax25, RNR, POLLON, C_COMMAND);
+	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+		ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND);
 	else
-		ax25_send_control(ax25, RR, POLLON, C_COMMAND);
+		ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND);
 
-	ax25->condition &= ~ACK_PENDING_CONDITION;
+	ax25->condition &= ~AX25_COND_ACK_PENDING;
 
 	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 }
  	
 void ax25_enquiry_response(ax25_cb *ax25)
 {
-	if (ax25->condition & OWN_RX_BUSY_CONDITION)
-		ax25_send_control(ax25, RNR, POLLON, C_RESPONSE);
+	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+		ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE);
 	else
-		ax25_send_control(ax25, RR, POLLON, C_RESPONSE);
+		ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE);
 
-	ax25->condition &= ~ACK_PENDING_CONDITION;
+	ax25->condition &= ~AX25_COND_ACK_PENDING;
 }
 
 void ax25_timeout_response(ax25_cb *ax25)
 {
-	if (ax25->condition & OWN_RX_BUSY_CONDITION)
-		ax25_send_control(ax25, RNR, POLLOFF, C_RESPONSE);
+	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+		ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE);
 	else
-		ax25_send_control(ax25, RR, POLLOFF, C_RESPONSE);
+		ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
 
-	ax25->condition &= ~ACK_PENDING_CONDITION;
+	ax25->condition &= ~AX25_COND_ACK_PENDING;
 }
 
 void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
@@ -355,50 +331,37 @@
 }
 
 /*
- *	dl1bke 960114: shouldn't ax25/dama_check_need_response reside as
- *                static inline void ...() in ax25.h, should it? ;-)
- */
-void ax25_check_need_response(ax25_cb *ax25, int type, int pf)
-{
-	if (!ax25->dama_slave && type == C_COMMAND && pf)
-		ax25_enquiry_response(ax25);
-}
-
-/*
  *	dl1bke 960114: transmit I frames on DAMA poll
  */
 void dama_enquiry_response(ax25_cb *ax25)
 {
 	ax25_cb *ax25o;
-	
-	if (!(ax25->condition & PEER_RX_BUSY_CONDITION)) {
+
+	if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) {
 		ax25_requeue_frames(ax25);
 		ax25_kick(ax25);
 	}
 
-	if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 ||
-	    skb_peek(&ax25->ack_queue) != NULL) {
+	if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL)
 		ax25_t1_timeout(ax25);
-	} else {
+	else
 		ax25->n2count = 0;
-	}
-	
+
 	ax25->t3timer = ax25->t3;
-	
 
 	/* The FLEXNET DAMA master implementation refuses to send us ANY */
 	/* I frame for this connection if we send a REJ here, probably   */
 	/* due to its frame collector scheme? A simple RR or  RNR will   */
 	/* invoke the retransmission, and in fact REJs are superfluous   */
 	/* in DAMA mode anyway...					 */
-	
+
 #if 0
-	if (ax25->condition & REJECT_CONDITION)
-		ax25_send_control(ax25, REJ, POLLOFF, C_RESPONSE);
+	if (ax25->condition & AX25_COND_REJECT)
+		ax25_send_control(ax25, AX25_REJ, AX25_POLLOFF, AX25_RESPONSE);
 	else
-#endif	
+#endif
 		ax25_enquiry_response(ax25);
-		
+
 	/* Note that above response to the poll could be sent behind the  */
 	/* transmissions of the other channels as well... This version    */	
 	/* gives better performance on FLEXNET nodes. (Why, Gunter?)	  */
@@ -417,27 +380,19 @@
 
 		if (!ax25o->dama_slave)
 			continue;
-			
-		if ( !(ax25o->condition & PEER_RX_BUSY_CONDITION) && 
-		     (ax25o->state == AX25_STATE_3 || 
-		     (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) {
+
+		if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && 
+		    (ax25o->state == AX25_STATE_3 || 
+		    (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) {
 			ax25_requeue_frames(ax25o);
 			ax25_kick(ax25o);
 		}
-		
-		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 ||
-		    skb_peek(&ax25o->ack_queue) != NULL) {
+
+		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
 			ax25_t1_timeout(ax25o);
-		}
 
 		ax25o->t3timer = ax25o->t3;
 	}
-}
-
-void dama_check_need_response(ax25_cb *ax25, int type, int pf)
-{
-	if (ax25->dama_slave && type == C_COMMAND && pf)
-		dama_enquiry_response(ax25);
 }
 
 void dama_establish_data_link(ax25_cb *ax25)
diff -u --recursive --new-file v2.0.34/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v2.0.34/linux/net/ax25/ax25_route.c	Sat Aug 10 00:03:16 1996
+++ linux/net/ax25/ax25_route.c	Mon Jul 13 13:47:40 1998
@@ -1,8 +1,5 @@
 /*
- *	AX.25 release 032
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	AX.25 release 035
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -37,11 +34,14 @@
  *			Joerg(DL1BKE)	Fixed AX.25 routing of IP datagram and VC, new ioctl()
  *					"SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
  *					on routes.
- *	AX.25 032	Jonathan(G4KLX)	Remove auto-router.
+ *	AX.25 033	Jonathan(G4KLX)	Remove auto-router.
+ *			Joerg(DL1BKE)	Moved BPQ Ethernet driver to seperate device.
+ *	AX.25 035	Frederic(F1OAT)	Support for pseudo-digipeating.
+ *			Jonathan(G4KLX)	Support for packet forwarding.
  */
- 
+
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -72,11 +72,12 @@
 	char ip_mode;
 } *ax25_route = NULL;
 
-static struct ax25_dev {
-	struct ax25_dev *next;
-	struct device *dev;
-	unsigned short values[AX25_MAX_VALUES];
-} *ax25_device = NULL;
+struct ax25_dev ax25_device[AX25_MAX_DEVICES] = {
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
+	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}
+};
 
 static struct ax25_route *ax25_find_route(ax25_address *, struct device *);
 
@@ -119,7 +120,7 @@
 						kfree_s((void *)s, sizeof(*s));
 						break;
 					}
-				}				
+				}
 			}
 		}
 	}
@@ -152,7 +153,7 @@
 					if (route.digi_count != 0) {
 						if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
 							return -ENOMEM;
-						ax25_rt->digipeat->lastrepeat = 0;
+						ax25_rt->digipeat->lastrepeat = -1;
 						ax25_rt->digipeat->ndigi      = route.digi_count;
 						for (i = 0; i < route.digi_count; i++) {
 							ax25_rt->digipeat->repeated[i] = 0;
@@ -173,7 +174,7 @@
 					kfree_s(ax25_rt, sizeof(struct ax25_route));
 					return -ENOMEM;
 				}
-				ax25_rt->digipeat->lastrepeat = 0;
+				ax25_rt->digipeat->lastrepeat = -1;
 				ax25_rt->digipeat->ndigi      = route.digi_count;
 				for (i = 0; i < route.digi_count; i++) {
 					ax25_rt->digipeat->repeated[i] = 0;
@@ -285,20 +286,20 @@
 				len += sprintf(buffer + len, "    *");
 				break;
 		}
-		
+
 		if (ax25_rt->digipeat != NULL)
 			for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
 				len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
-		
+
 		len += sprintf(buffer + len, "\n");
-				
+
 		pos = begin + len;
 
 		if (pos < offset) {
 			len   = 0;
 			begin = pos;
 		}
-		
+
 		if (pos > offset + length)
 			break;
 	}
@@ -356,7 +357,7 @@
 	struct ax25_route *ax25_spe_rt = NULL;
 	struct ax25_route *ax25_def_rt = NULL;
 	struct ax25_route *ax25_rt;
-	
+
 	/*
 	 *	Bind to the physical interface we heard them on, or the default
 	 *	route if none is found;
@@ -377,7 +378,7 @@
 
 	if (ax25_spe_rt != NULL)
 		return ax25_spe_rt;
-		
+
 	return ax25_def_rt;
 }
 
@@ -389,12 +390,12 @@
 static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
 {
 	int k;
-	
+
 	for (k = 0; k < digipeat->ndigi; k++) {
 		if (ax25cmp(addr, &digipeat->calls[k]) == 0)
 			break;
 	}
-	
+
 	digipeat->ndigi = k;
 }
  
@@ -409,7 +410,7 @@
 
 	if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL)
 		return -EHOSTUNREACH;
-		
+
 	ax25->device = ax25_rt->dev;
 
 	if ((call = ax25_findbyuid(current->euid)) == NULL) {
@@ -437,63 +438,37 @@
  *	dl1bke 960117: build digipeater path
  *	dl1bke 960301: use the default route if it exists
  */
-void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr, struct device *dev)
+ax25_digi *ax25_rt_find_path(ax25_address *addr, struct device *dev)
 {
 	struct ax25_route *ax25_rt;
-	
-	if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
-		return;
-	
-	if (ax25_rt->digipeat == NULL)
-		return;
 
-	if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
-		return;
+	if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
+		return NULL;
 
-	ax25->device    = ax25_rt->dev;
-	*ax25->digipeat = *ax25_rt->digipeat;
-	ax25_adjust_path(addr, ax25->digipeat);
+	return ax25_rt->digipeat;
 }
 
-void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev)
+void ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi)
 {
-	struct ax25_route *ax25_rt;
-	ax25_digi digipeat;
-	ax25_address src, dest;
 	unsigned char *bp;
 	int len;
 
-	skb_pull(skb, 1);	/* skip KISS command */
-
-	if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
-		return;
-
-	if (ax25_rt->digipeat == NULL)
-		return;
-		
-	digipeat = *ax25_rt->digipeat;
-	
-	ax25_adjust_path(addr, &digipeat);
+	len = digi->ndigi * AX25_ADDR_LEN;
 
-	len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN;
-		
 	if (skb_headroom(skb) < len) {
-		printk(KERN_CRIT "ax25_dg_build_path: not enough headroom for digis in skb\n");
+		printk(KERN_CRIT "ax25_rt_build_path: not enough headroom for digis in skb\n");
 		return;
 	}
-	
-	memcpy(&dest, skb->data    , AX25_ADDR_LEN);
-	memcpy(&src,  skb->data + 7, AX25_ADDR_LEN);
 
 	bp = skb_push(skb, len);
 
-	build_ax25_addr(bp, &src, &dest, ax25_rt->digipeat, C_COMMAND, MODULUS);
+	build_ax25_addr(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
 }
 
 /*
  *	Return the IP mode of a given callsign/device pair.
  */
-char ax25_ip_mode_get(ax25_address *callsign, struct device *dev)
+char ax25_rt_mode_get(ax25_address *callsign, struct device *dev)
 {
 	struct ax25_route *ax25_rt;
 
@@ -506,24 +481,24 @@
 
 static struct ax25_dev *ax25_dev_get_dev(struct device *dev)
 {
-	struct ax25_dev *s;
+	int i;
+
+	for (i = 0; i < AX25_MAX_DEVICES; i++)
+		if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev)
+			return ax25_device + i;
 
-	for (s = ax25_device; s != NULL; s = s->next)
-		if (s->dev == dev)
-			return s;
-	
 	return NULL;
 }
 
 /*
  *	Wow, a bit of data hiding. Is this C++ or what ?
  */
-unsigned short ax25_dev_get_value(struct device *dev, int valueno)
+int ax25_dev_get_value(struct device *dev, int valueno)
 {
 	struct ax25_dev *ax25_dev;
 
 	if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) {
-		printk(KERN_WARNING "ax25_dev_get_flag called with invalid device\n");
+		printk(KERN_WARNING "ax25_dev_get_value called with invalid device\n");
 		return 1;
 	}
 
@@ -536,248 +511,120 @@
  */
 void ax25_dev_device_up(struct device *dev)
 {
-	unsigned long flags;
-	struct ax25_dev *ax25_dev;
-	
-	if ((ax25_dev = (struct ax25_dev *)kmalloc(sizeof(struct ax25_dev), GFP_ATOMIC)) == NULL)
-		return;		/* No space */
+	struct ax25_dev *ax25_dev = NULL;
+	int i;
+
+	for (i = 0; i < AX25_MAX_DEVICES; i++) {
+		if (ax25_device[i].dev == NULL) {
+			ax25_dev = ax25_device + i;
+			break;
+		}
+	}
+
+	if (ax25_dev == NULL) {
+		printk(KERN_ERR "ax25_dev_device_up cannot find free AX.25 device\n");
+		return;
+	}
 
-	ax25_dev->dev        = dev;
+	ax25_unregister_sysctl();
+
+	strcpy(ax25_dev->name, dev->name);
+
+	ax25_dev->dev     = dev;
+	ax25_dev->forward = NULL;
 
 	ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
 	ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
-	ax25_dev->values[AX25_VALUES_NETROM]    = AX25_DEF_NETROM;
-	ax25_dev->values[AX25_VALUES_TEXT]      = AX25_DEF_TEXT;
 	ax25_dev->values[AX25_VALUES_BACKOFF]   = AX25_DEF_BACKOFF;
 	ax25_dev->values[AX25_VALUES_CONMODE]   = AX25_DEF_CONMODE;
 	ax25_dev->values[AX25_VALUES_WINDOW]    = AX25_DEF_WINDOW;
 	ax25_dev->values[AX25_VALUES_EWINDOW]   = AX25_DEF_EWINDOW;
-	ax25_dev->values[AX25_VALUES_T1]        = AX25_DEF_T1 * PR_SLOWHZ;
-	ax25_dev->values[AX25_VALUES_T2]        = AX25_DEF_T2 * PR_SLOWHZ;
-	ax25_dev->values[AX25_VALUES_T3]        = AX25_DEF_T3 * PR_SLOWHZ;
-	ax25_dev->values[AX25_VALUES_IDLE]	= AX25_DEF_IDLE * PR_SLOWHZ * 60;
+	ax25_dev->values[AX25_VALUES_T1]        = AX25_DEF_T1;
+	ax25_dev->values[AX25_VALUES_T2]        = AX25_DEF_T2;
+	ax25_dev->values[AX25_VALUES_T3]        = AX25_DEF_T3;
+	ax25_dev->values[AX25_VALUES_IDLE]	= AX25_DEF_IDLE;
 	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
-	ax25_dev->values[AX25_VALUES_DIGI]      = AX25_DEF_DIGI;
 	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
-	ax25_dev->values[AX25_VALUES_IPMAXQUEUE]= AX25_DEF_IPMAXQUEUE;
-
-	save_flags(flags);
-	cli();
-
-	ax25_dev->next = ax25_device;
-	ax25_device    = ax25_dev;
 
-	restore_flags(flags);
+	ax25_register_sysctl();
 }
 
 void ax25_dev_device_down(struct device *dev)
 {
-	struct ax25_dev *s, *t, *ax25_dev = ax25_device;
-	
-	while (ax25_dev != NULL) {
-		s        = ax25_dev;
-		ax25_dev = ax25_dev->next;
+	struct ax25_dev *ax25_dev;
 
-		if (s->dev == dev) {
-			if (ax25_device == s) {
-				ax25_device = s->next;
-				kfree_s((void *)s, (sizeof *s));
-			} else {
-				for (t = ax25_device; t != NULL; t = t->next) {
-					if (t->next == s) {
-						t->next = s->next;
-						kfree_s((void *)s, sizeof(*s));
-						break;
-					}
-				}				
-			}
-		}
-	}
+	ax25_unregister_sysctl();
+
+	if ((ax25_dev = ax25_dev_get_dev(dev)) != NULL)
+		ax25_dev->dev = NULL;
+
+	ax25_register_sysctl();
 }
 
-int ax25_dev_ioctl(unsigned int cmd, void *arg)
+int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
 {
-	struct ax25_parms_struct ax25_parms;
 	struct device *dev;
 	struct ax25_dev *ax25_dev;
-	int err;
+
+	if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL)
+		return -EINVAL;
+
+	if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
+		return -EINVAL;
 
 	switch (cmd) {
-		case SIOCAX25SETPARMS:
-			if (!suser())
-				return -EPERM;
-			if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_parms))) != 0)
-				return err;
-			memcpy_fromfs(&ax25_parms, arg, sizeof(ax25_parms));
-			if ((dev = ax25rtr_get_dev(&ax25_parms.port_addr)) == NULL)
-				return -EINVAL;
-			if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_IPDEFMODE] != 'D' &&
-			    ax25_parms.values[AX25_VALUES_IPDEFMODE] != 'V')
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_AXDEFMODE] != MODULUS &&
-			    ax25_parms.values[AX25_VALUES_AXDEFMODE] != EMODULUS)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_NETROM] != 0 &&
-			    ax25_parms.values[AX25_VALUES_NETROM] != 1)
+		case SIOCAX25ADDFWD:
+			if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL)
 				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_TEXT] != 0 &&
-			    ax25_parms.values[AX25_VALUES_TEXT] != 1)
+			if (ax25_dev->forward != NULL)
 				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_BACKOFF] != 'E' &&
-			    ax25_parms.values[AX25_VALUES_BACKOFF] != 'L')
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_CONMODE] != 0 &&
-			    ax25_parms.values[AX25_VALUES_CONMODE] != 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_WINDOW] < 1 ||
-			    ax25_parms.values[AX25_VALUES_WINDOW] > 7)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_EWINDOW] < 1 ||
-			    ax25_parms.values[AX25_VALUES_EWINDOW] > 63)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_T1] < 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_T2] < 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_T3] < 1)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_IDLE] > 100)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_N2] < 1 ||
-			    ax25_parms.values[AX25_VALUES_N2] > 31)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_PACLEN] < 22)
-				return -EINVAL;
-			if ((ax25_parms.values[AX25_VALUES_DIGI] &
-			    ~(AX25_DIGI_INBAND | AX25_DIGI_XBAND)) != 0)
-				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_IPMAXQUEUE] < 1)
-				return -EINVAL;
-			memcpy(ax25_dev->values, ax25_parms.values, AX25_MAX_VALUES * sizeof(short));
-			ax25_dev->values[AX25_VALUES_T1] *= PR_SLOWHZ;
-			ax25_dev->values[AX25_VALUES_T1] /= 2;
-			ax25_dev->values[AX25_VALUES_T2] *= PR_SLOWHZ;
-			ax25_dev->values[AX25_VALUES_T3] *= PR_SLOWHZ;
-			ax25_dev->values[AX25_VALUES_IDLE] *= PR_SLOWHZ * 60;
+			ax25_dev->forward = dev;
 			break;
 
-		case SIOCAX25GETPARMS:
-			if ((err = verify_area(VERIFY_WRITE, arg, sizeof(struct ax25_parms_struct))) != 0)
-				return err;
-			memcpy_fromfs(&ax25_parms, arg, sizeof(ax25_parms));
-			if ((dev = ax25rtr_get_dev(&ax25_parms.port_addr)) == NULL)
-				return -EINVAL;
-			if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
+		case SIOCAX25DELFWD:
+			if (ax25_dev->forward == NULL)
 				return -EINVAL;
-			memcpy(ax25_parms.values, ax25_dev->values, AX25_MAX_VALUES * sizeof(short));
-			ax25_parms.values[AX25_VALUES_T1] *= 2;
-			ax25_parms.values[AX25_VALUES_T1] /= PR_SLOWHZ;
-			ax25_parms.values[AX25_VALUES_T2] /= PR_SLOWHZ;
-			ax25_parms.values[AX25_VALUES_T3] /= PR_SLOWHZ;
-			ax25_parms.values[AX25_VALUES_IDLE] /= PR_SLOWHZ * 60;
-			memcpy_tofs(arg, &ax25_parms, sizeof(ax25_parms));
+			ax25_dev->forward = NULL;
 			break;
+
+		default:
+			return -EINVAL;
 	}
 
 	return 0;
 }
 
-#ifdef CONFIG_BPQETHER
-static struct ax25_bpqdev {
-	struct ax25_bpqdev *next;
-	struct device *dev;
-	ax25_address callsign;
-} *ax25_bpqdev = NULL;
-
-int ax25_bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+struct device *ax25_fwd_dev(struct device *dev)
 {
-	struct ax25_bpqdev *bpqdev;
-	int len     = 0;
-	off_t pos   = 0;
-	off_t begin = 0;
-  
-	cli();
-
-	len += sprintf(buffer, "dev  callsign\n");
-
-	for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
-		len += sprintf(buffer + len, "%-4s %-9s\n",
-			bpqdev->dev ? bpqdev->dev->name : "???",
-			ax2asc(&bpqdev->callsign));
-
-		pos = begin + len;
-
-		if (pos < offset) {
-			len   = 0;
-			begin = pos;
-		}
-		
-		if (pos > offset + length)
-			break;
-	}
+	struct ax25_dev *ax25_dev;
 
-	sti();
+	if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
+		return dev;
 
-	*start = buffer + (offset - begin);
-	len   -= (offset - begin);
+	if (ax25_dev->forward == NULL)
+		return dev;
 
-	if (len > length) len = length;
+	return ax25_dev->forward;
+}
 
-	return len;
-} 
+#ifdef MODULE
 
-ax25_address *ax25_bpq_get_addr(struct device *dev)
+/*
+ *	Free all memory associated with routing and device structures.
+ */
+void ax25_rt_free(void)
 {
-	struct ax25_bpqdev *bpqdev;
-	
-	for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next)
-		if (bpqdev->dev == dev)
-			return &bpqdev->callsign;
+	struct ax25_route *s, *ax25_rt = ax25_route;
 
-	return NULL;
-}
+	while (ax25_rt != NULL) {
+		s       = ax25_rt;
+		ax25_rt = ax25_rt->next;
 
-int ax25_bpq_ioctl(unsigned int cmd, void *arg)
-{
-	unsigned long flags;
-	struct ax25_bpqdev *bpqdev;
-	struct ax25_bpqaddr_struct bpqaddr;
-	struct device *dev;
-	int err;
+		if (s->digipeat != NULL)
+			kfree_s(s->digipeat, sizeof(ax25_digi));
 
-	switch (cmd) {
-		case SIOCAX25BPQADDR:
-			if ((err = verify_area(VERIFY_READ, arg, sizeof(bpqaddr))) != 0)
-				return err;
-			memcpy_fromfs(&bpqaddr, arg, sizeof(bpqaddr));
-			if ((dev = dev_get(bpqaddr.dev)) == NULL)
-				return -EINVAL;
-			if (dev->type != ARPHRD_ETHER)
-				return -EINVAL;
-			for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
-				if (bpqdev->dev == dev) {
-					bpqdev->callsign = bpqaddr.addr;
-					return 0;
-				}
-			}
-			if ((bpqdev = (struct ax25_bpqdev *)kmalloc(sizeof(struct ax25_bpqdev), GFP_ATOMIC)) == NULL)
-				return -ENOMEM;
-			bpqdev->dev      = dev;
-			bpqdev->callsign = bpqaddr.addr;
-			save_flags(flags);
-			cli();
-			bpqdev->next = ax25_bpqdev;
-			ax25_bpqdev  = bpqdev;
-			restore_flags(flags);
-			break;
-			
-		default:
-			return -EINVAL;
+		kfree_s(s, sizeof(struct ax25_route));
 	}
-
-	return 0;
 }
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v2.0.34/linux/net/ax25/ax25_subr.c	Sat Aug 10 00:03:16 1996
+++ linux/net/ax25/ax25_subr.c	Mon Jul 13 13:47:40 1998
@@ -1,8 +1,5 @@
 /*
- *	AX.25 release 032
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	AX.25 release 035
  *
  *	This code REQUIRES 1.3.61 or higher/ NET3.029
  *
@@ -32,10 +29,11 @@
  *			Joerg(DL1BKE)	Found the real bug in ax25.h, sri.
  *	AX.25 032	Joerg(DL1BKE)	Added ax25_queue_length to count the number of
  *					enqueued buffers of a socket..
+ *	AX.25 035	Frederic(F1OAT)	Support for pseudo-digipeating.
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -64,23 +62,17 @@
 {
 	struct sk_buff *skb;
 
-	while ((skb = skb_dequeue(&ax25->write_queue)) != NULL) {
-		skb->free = 1;
+	while ((skb = skb_dequeue(&ax25->write_queue)) != NULL)
 		kfree_skb(skb, FREE_WRITE);
-	}
 
-	while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
-		skb->free = 1;
+	while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL)
 		kfree_skb(skb, FREE_WRITE);
-	}
 
-	while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL) {
+	while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL)
 		kfree_skb(skb, FREE_READ);
-	}
 
-	while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL) {
+	while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL)
 		kfree_skb(skb, FREE_READ);
-	}
 }
 
 /*
@@ -98,7 +90,6 @@
 	if (ax25->va != nr) {
 		while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) {
 		        skb = skb_dequeue(&ax25->ack_queue);
-			skb->free = 1;
 			kfree_skb(skb, FREE_WRITE);
 			ax25->va = (ax25->va + 1) % ax25->modulus;
 			if (ax25->dama_slave)
@@ -107,7 +98,8 @@
 	}
 }
 
-/* Maybe this should be your ax25_invoke_retransmission(), which appears
+/*
+ * Maybe this should be your ax25_invoke_retransmission(), which appears
  * to be used but not do anything.  ax25_invoke_retransmission() used to
  * be in AX 0.29, but has now gone in 0.30.
  */
@@ -141,7 +133,7 @@
 		if (nr == vc) return 1;
 		vc = (vc + 1) % ax25->modulus;
 	}
-	
+
 	if (nr == ax25->vs) return 1;
 
 	return 0;
@@ -154,41 +146,41 @@
 int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf)
 {
 	unsigned char *frame;
-	int frametype = ILLEGAL;
+	int frametype = AX25_ILLEGAL;
 
 	frame = skb->data;
 	*ns = *nr = *pf = 0;
 
-	if (ax25->modulus == MODULUS) {
-		if ((frame[0] & S) == 0) {
-			frametype = I;			/* I frame - carries NR/NS/PF */
+	if (ax25->modulus == AX25_MODULUS) {
+		if ((frame[0] & AX25_S) == 0) {
+			frametype = AX25_I;			/* I frame - carries NR/NS/PF */
 			*ns = (frame[0] >> 1) & 0x07;
 			*nr = (frame[0] >> 5) & 0x07;
-			*pf = frame[0] & PF;
-		} else if ((frame[0] & U) == 1) { 	/* S frame - take out PF/NR */
+			*pf = frame[0] & AX25_PF;
+		} else if ((frame[0] & AX25_U) == 1) { 	/* S frame - take out PF/NR */
 			frametype = frame[0] & 0x0F;
 			*nr = (frame[0] >> 5) & 0x07;
-			*pf = frame[0] & PF;
-		} else if ((frame[0] & U) == 3) { 	/* U frame - take out PF */
-			frametype = frame[0] & ~PF;
-			*pf = frame[0] & PF;
+			*pf = frame[0] & AX25_PF;
+		} else if ((frame[0] & AX25_U) == 3) { 	/* U frame - take out PF */
+			frametype = frame[0] & ~AX25_PF;
+			*pf = frame[0] & AX25_PF;
 		}
 		skb_pull(skb, 1);
 	} else {
-		if ((frame[0] & S) == 0) {
-			frametype = I;			/* I frame - carries NR/NS/PF */
+		if ((frame[0] & AX25_S) == 0) {
+			frametype = AX25_I;			/* I frame - carries NR/NS/PF */
 			*ns = (frame[0] >> 1) & 0x7F;
 			*nr = (frame[1] >> 1) & 0x7F;
-			*pf = frame[1] & EPF;
+			*pf = frame[1] & AX25_EPF;
 			skb_pull(skb, 2);
-		} else if ((frame[0] & U) == 1) { 	/* S frame - take out PF/NR */
+		} else if ((frame[0] & AX25_U) == 1) { 	/* S frame - take out PF/NR */
 			frametype = frame[0] & 0x0F;
 			*nr = (frame[1] >> 1) & 0x7F;
-			*pf = frame[1] & EPF;
+			*pf = frame[1] & AX25_EPF;
 			skb_pull(skb, 2);
-		} else if ((frame[0] & U) == 3) { 	/* U frame - take out PF */
-			frametype = frame[0] & ~PF;
-			*pf = frame[0] & PF;
+		} else if ((frame[0] & AX25_U) == 3) { 	/* U frame - take out PF */
+			frametype = frame[0] & ~AX25_PF;
+			*pf = frame[0] & AX25_PF;
 			skb_pull(skb, 1);
 		}
 	}
@@ -206,42 +198,37 @@
 	struct sk_buff *skb;
 	unsigned char  *dptr;
 	struct device *dev;
-	
+
 	if ((dev = ax25->device) == NULL)
 		return;	/* Route died */
 
 	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL)
 		return;
 
-	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat));
+	skb->free = 1;
 
-	if (ax25->sk != NULL) {
-		skb->sk = ax25->sk;
-		atomic_add(skb->truesize, &ax25->sk->wmem_alloc);
-	}
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat));
 
 	/* Assume a response - address structure for DTE */
-	if (ax25->modulus == MODULUS) {
+	if (ax25->modulus == AX25_MODULUS) {
 		dptr = skb_put(skb, 1);
 		*dptr = frametype;
-		*dptr |= (poll_bit) ? PF : 0;
-		if ((frametype & U) == S)		/* S frames carry NR */
+		*dptr |= (poll_bit) ? AX25_PF : 0;
+		if ((frametype & AX25_U) == AX25_S)		/* S frames carry NR */
 			*dptr |= (ax25->vr << 5);
 	} else {
-		if ((frametype & U) == U) {
+		if ((frametype & AX25_U) == AX25_U) {
 			dptr = skb_put(skb, 1);
 			*dptr = frametype;
-			*dptr |= (poll_bit) ? PF : 0;
+			*dptr |= (poll_bit) ? AX25_PF : 0;
 		} else {
 			dptr = skb_put(skb, 2);
 			dptr[0] = frametype;
 			dptr[1] = (ax25->vr << 1);
-			dptr[1] |= (poll_bit) ? EPF : 0;
+			dptr[1] |= (poll_bit) ? AX25_EPF : 0;
 		}
 	}
 
-	skb->free = 1;
-
 	ax25_transmit_buffer(ax25, skb, type);
 }
 
@@ -262,24 +249,21 @@
 	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(digi) + 1, GFP_ATOMIC)) == NULL)
 		return;	/* Next SABM will get DM'd */
 
+	skb->free = 1;
+
 	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(digi));
 
 	ax25_digi_invert(digi, &retdigi);
 
 	dptr = skb_put(skb, 1);
-	skb->sk = NULL;
 
-	*dptr = DM | PF;
+	*dptr = AX25_DM | AX25_PF;
 
 	/*
 	 *	Do the address ourselves
 	 */
-
 	dptr  = skb_push(skb, size_ax25_addr(digi));
-	dptr += build_ax25_addr(dptr, dest, src, &retdigi, C_RESPONSE, MODULUS);
-
-	skb->arp  = 1;
-	skb->free = 1;
+	dptr += build_ax25_addr(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS);
 
 	ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
 }
@@ -291,11 +275,19 @@
 {
 	int n, t = 2;
 
-	if (ax25->backoff) {
-		for (n = 0; n < ax25->n2count; n++)
-			t *= 2;
-
-		if (t > 8) t = 8;
+	switch (ax25->backoff) {
+		case 0:
+			break;
+
+		case 1:
+			t += 2 * ax25->n2count;
+			break;
+
+		case 2:
+			for (n = 0; n < ax25->n2count; n++)
+				t *= 2;
+			if (t > 8) t = 8;
+			break;
 	}
 
 	return t * ax25->rtt;
@@ -306,28 +298,29 @@
  */
 void ax25_calculate_rtt(ax25_cb *ax25)
 {
-	if (ax25->t1timer > 0 && ax25->n2count == 0)
-		ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10;
+	switch (ax25->backoff) {
+		case 0:
+			ax25->rtt = ax25->t1 / 2;
+			break;
+
+		case 1:
+		case 2:
+			if (ax25->t1timer > 0 && ax25->n2count == 0)
+				ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10;
+			break;
+	}
+
+	if (ax25->rtt < AX25_T1CLAMPLO)
+		ax25->rtt = AX25_T1CLAMPLO;
 
-#ifdef	AX25_T1CLAMPLO
-	/* Don't go below one tenth of a second */
-	if (ax25->rtt < (AX25_T1CLAMPLO))
-		ax25->rtt = (AX25_T1CLAMPLO);
-#else	/* Failsafe - some people might have sub 1/10th RTTs :-) **/
-	if (ax25->rtt == 0)
-		ax25->rtt = PR_SLOWHZ;
-#endif
-#ifdef	AX25_T1CLAMPHI
-	/* OR above clamped seconds **/
-	if (ax25->rtt > (AX25_T1CLAMPHI))
-		ax25->rtt = (AX25_T1CLAMPHI);
-#endif
+	if (ax25->rtt > AX25_T1CLAMPHI)
+		ax25->rtt = AX25_T1CLAMPHI;
 }
 
 /*
  *	Digipeated address processing
  */
- 
+
 
 /*
  *	Given an AX.25 address pull of to, from, digi list, command/response and the start of data
@@ -336,46 +329,41 @@
 unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
 {
 	int d = 0;
-	
+
 	if (len < 14) return NULL;
-		
-	if (flags != NULL) {
-		*flags = 0;
-	
-		if (buf[6] & LAPB_C) {
-			*flags = C_COMMAND;
-		}
-		if (buf[13] & LAPB_C) {
-			*flags = C_RESPONSE;
-		}
-	}
-		
+
+	*flags = 0;
+
+	if (buf[6] & AX25_CBIT)
+		*flags = AX25_COMMAND;
+	if (buf[13] & AX25_CBIT)
+		*flags = AX25_RESPONSE;
+
 	if (dama != NULL) 
-		*dama = ~buf[13] & DAMA_FLAG;
-		
+		*dama = ~buf[13] & AX25_DAMA_FLAG;
+
 	/* Copy to, from */
-	if (dest != NULL) 
-		memcpy(dest, buf + 0, AX25_ADDR_LEN);
-	if (src != NULL)  
-		memcpy(src,  buf + 7, AX25_ADDR_LEN);
+	memcpy(dest, buf + 0, AX25_ADDR_LEN);
+	memcpy(src,  buf + 7, AX25_ADDR_LEN);
+
 	buf += 2 * AX25_ADDR_LEN;
 	len -= 2 * AX25_ADDR_LEN;
+
 	digi->lastrepeat = -1;
 	digi->ndigi      = 0;
-	
-	while (!(buf[-1] & LAPB_E)) {
+
+	while (!(buf[-1] & AX25_EBIT)) {
 		if (d >= AX25_MAX_DIGIS)  return NULL;	/* Max of 6 digis */
 		if (len < 7) return NULL;	/* Short packet */
 
-		if (digi != NULL) {
-			memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
-			digi->ndigi = d + 1;
-			if (buf[6] & AX25_REPEATED) {
-				digi->repeated[d] = 1;
-				digi->lastrepeat  = d;
-			} else {
-				digi->repeated[d] = 0;
-			}
+		memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
+		digi->ndigi = d + 1;
+
+		if (buf[6] & AX25_HBIT) {
+			digi->repeated[d] = 1;
+			digi->lastrepeat  = d;
+		} else {
+			digi->repeated[d] = 0;
 		}
 
 		buf += AX25_ADDR_LEN;
@@ -395,53 +383,54 @@
 	int ct  = 0;
 
 	memcpy(buf, dest, AX25_ADDR_LEN);
-	buf[6] &= ~(LAPB_E | LAPB_C);
-	buf[6] |= SSSID_SPARE;
+	buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+	buf[6] |= AX25_SSSID_SPARE;
 
-	if (flag == C_COMMAND) buf[6] |= LAPB_C;
+	if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
 
 	buf += AX25_ADDR_LEN;
 	len += AX25_ADDR_LEN;
 
 	memcpy(buf, src, AX25_ADDR_LEN);
-	buf[6] &= ~(LAPB_E | LAPB_C);
-	buf[6] &= ~SSSID_SPARE;
+	buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+	buf[6] &= ~AX25_SSSID_SPARE;
 
-	if (modulus == MODULUS) {
-		buf[6] |= SSSID_SPARE;
-	} else {
-		buf[6] |= ESSID_SPARE;
-	}
+	if (modulus == AX25_MODULUS)
+		buf[6] |= AX25_SSSID_SPARE;
+	else
+		buf[6] |= AX25_ESSID_SPARE;
 
-	if (flag == C_RESPONSE) buf[6] |= LAPB_C;
+	if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT;
 
 	/*
 	 *	Fast path the normal digiless path
 	 */
 	if (d == NULL || d->ndigi == 0) {
-		buf[6] |= LAPB_E;
+		buf[6] |= AX25_EBIT;
 		return 2 * AX25_ADDR_LEN;
 	}	
-	
+
 	buf += AX25_ADDR_LEN;
 	len += AX25_ADDR_LEN;
-	
+
 	while (ct < d->ndigi) {
 		memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
+
 		if (d->repeated[ct])
-			buf[6] |= AX25_REPEATED;
+			buf[6] |= AX25_HBIT;
 		else
-			buf[6] &= ~AX25_REPEATED;
-		buf[6] &= ~LAPB_E;
-		buf[6] |= SSSID_SPARE;
+			buf[6] &= ~AX25_HBIT;
+
+		buf[6] &= ~AX25_EBIT;
+		buf[6] |= AX25_SSSID_SPARE;
 
 		buf += AX25_ADDR_LEN;
 		len += AX25_ADDR_LEN;
 		ct++;
 	}
 
-	buf[-1] |= LAPB_E;
-	
+	buf[-1] |= AX25_EBIT;
+
 	return len;
 }
 
@@ -452,63 +441,30 @@
 
 	return AX25_ADDR_LEN * (2 + dp->ndigi);
 }
-	
+
 /* 
  *	Reverse Digipeat List. May not pass both parameters as same struct
- */	
-void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
-{
-	int ct = 0;
-	
-	/* Invert the digipeaters */
-	
-	while (ct < in->ndigi) {
-		out->calls[ct]    = in->calls[in->ndigi - ct - 1];
-		out->repeated[ct] = 0;
-		ct++;
-	}
-	
-	/* Copy ndigis */
-	out->ndigi = in->ndigi;
-
-	/* Finish off */
-	out->lastrepeat = 0;
-}
-
-/*
- *	count the number of buffers on a list belonging to the same
- *	socket as skb
  */
-
-static int ax25_list_length(struct sk_buff_head *list, struct sk_buff *skb)
+void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
 {
-	int count = 0;
-	long flags;
-	struct sk_buff *skbq;
-
-	save_flags(flags);
-	cli();
+	int ct;
 
-	if (list == NULL) {
-		restore_flags(flags);
-                return 0;
-        }
+	out->ndigi      = in->ndigi;
+	out->lastrepeat = in->ndigi - in->lastrepeat - 2;
 
-	for (skbq = list->next; skbq != (struct sk_buff *)list; skbq = skbq->next)
-		if (skb->sk == skbq->sk)
-			count++;
-
-        restore_flags(flags);
-        return count;
-}
+	/* Invert the digipeaters */
 
-/*
- *	count the number of buffers of one socket on the write/ack-queue
- */
+	for (ct = 0; ct < in->ndigi; ct++) {
+		out->calls[ct] = in->calls[in->ndigi - ct - 1];
 
-int ax25_queue_length(ax25_cb *ax25, struct sk_buff *skb)
-{
-	return ax25_list_length(&ax25->write_queue, skb) + ax25_list_length(&ax25->ack_queue, skb);
+		if (ct <= out->lastrepeat) {
+			out->calls[ct].ax25_call[6] |= AX25_HBIT;
+			out->repeated[ct]            = 1;
+		} else {
+			out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
+			out->repeated[ct]            = 0;
+		}
+	}
 }
 
 /*
@@ -519,7 +475,6 @@
  *
  *	Not to mention this request isn't currently reliable.
  */
- 
 void ax25_kiss_cmd(ax25_cb *ax25, unsigned char cmd, unsigned char param)
 {
 	struct sk_buff *skb;
@@ -532,19 +487,14 @@
 		return;
 
 	skb->free = 1;
-	skb->arp = 1;
-
-	if (ax25->sk != NULL) {
-		skb->sk = ax25->sk;
-		atomic_add(skb->truesize, &ax25->sk->wmem_alloc);
-	}
+	skb->arp  = 1;
 
 	skb->protocol = htons(ETH_P_AX25);
 
 	p = skb_put(skb, 2);
 
-	*p++=cmd;
-	*p  =param;
+	*p++ = cmd;
+	*p++ = param;
 
 	dev_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
 }
@@ -554,6 +504,7 @@
 	if (ax25_dev_is_dama_slave(ax25->device) == 0) {
 		if (ax25->sk != NULL && ax25->sk->debug)
 			printk("ax25_dama_on: DAMA on\n");
+
 		ax25_kiss_cmd(ax25, 5, 1);
 	}
 }
@@ -564,10 +515,37 @@
 		return;
 
 	ax25->dama_slave = 0;
+
 	if (ax25_dev_is_dama_slave(ax25->device) == 0) {
 		if (ax25->sk != NULL && ax25->sk->debug)
 			printk("ax25_dama_off: DAMA off\n");
+
 		ax25_kiss_cmd(ax25, 5, 0);
+	}
+}
+
+void ax25_disconnect(ax25_cb *ax25, int reason)
+{
+	ax25_clear_queues(ax25);
+
+	ax25_dama_off(ax25);
+
+	ax25->t1timer   = 0;
+	ax25->t2timer   = 0;
+	ax25->t3timer   = 0;
+	ax25->idletimer = 0;
+
+	ax25->state = AX25_STATE_0;
+
+	ax25_link_failed(ax25, reason);
+
+	if (ax25->sk != NULL) {
+		ax25->sk->state     = TCP_CLOSE;
+		ax25->sk->err       = reason;
+		ax25->sk->shutdown |= SEND_SHUTDOWN;
+		if (!ax25->sk->dead)
+			ax25->sk->state_change(ax25->sk);
+		ax25->sk->dead      = 1;
 	}
 }
 
diff -u --recursive --new-file v2.0.34/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v2.0.34/linux/net/ax25/ax25_timer.c	Sat Aug 10 00:03:16 1996
+++ linux/net/ax25/ax25_timer.c	Mon Jul 13 13:47:40 1998
@@ -1,8 +1,5 @@
 /*
- *	AX.25 release 032
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	AX.25 release 035
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -19,10 +16,12 @@
  *	AX.25 029	Alan(GW4PTS)	Switched to KA9Q constant names.
  *	AX.25 031	Joerg(DL1BKE)	Added DAMA support
  *	AX.25 032	Joerg(DL1BKE)	Fixed DAMA timeout bug
+ *	AX.25 033	Jonathan(G4KLX)	Modularisation functions.
+ *	AX.25 035	Frederic(F1OAT)	Support for pseudo-digipeating.
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -43,51 +42,31 @@
 #include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
-#ifdef CONFIG_NETROM
-#include <net/netrom.h>
-#endif
 
 static void ax25_timer(unsigned long);
 
 /*
- *	Linux set/reset timer routines
+ *	Linux set timer
  */
 void ax25_set_timer(ax25_cb *ax25)
 {
 	unsigned long flags;	
 
-	save_flags(flags);
-	cli();
-	del_timer(&ax25->timer);
-	restore_flags(flags);
-
-	ax25->timer.next     = ax25->timer.prev = NULL;	
-	ax25->timer.data     = (unsigned long)ax25;
-	ax25->timer.function = &ax25_timer;
-
-	ax25->timer.expires = jiffies + 10;
-	add_timer(&ax25->timer);
-}
-
-static void ax25_reset_timer(ax25_cb *ax25)
-{
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
+	save_flags(flags); cli();
 	del_timer(&ax25->timer);
 	restore_flags(flags);
 
 	ax25->timer.data     = (unsigned long)ax25;
 	ax25->timer.function = &ax25_timer;
 	ax25->timer.expires  = jiffies + 10;
+
 	add_timer(&ax25->timer);
 }
 
 /*
  *	AX.25 TIMER 
  *
- *	This routine is called every 500ms. Decrement timer by this
+ *	This routine is called every 100ms. Decrement timer by this
  *	amount - if expired then process the event.
  */
 static void ax25_timer(unsigned long param)
@@ -99,7 +78,6 @@
 			/* Magic here: If we listen() and a new link dies before it
 			   is accepted() it isn't 'dead' so doesn't get removed. */
 			if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
-				del_timer(&ax25->timer);
 				ax25_destroy_socket(ax25);
 				return;
 			}
@@ -111,11 +89,11 @@
 			 * Check the state of the receive buffer.
 			 */
 			if (ax25->sk != NULL) {
-				if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & OWN_RX_BUSY_CONDITION)) {
-					ax25->condition &= ~OWN_RX_BUSY_CONDITION;
+				if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
+					ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
 					if (!ax25->dama_slave)
-						ax25_send_control(ax25, RR, POLLOFF, C_RESPONSE);
-					ax25->condition &= ~ACK_PENDING_CONDITION;
+						ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
 					break;
 				}
 			}
@@ -132,8 +110,8 @@
 
 	if (ax25->t2timer > 0 && --ax25->t2timer == 0) {
 		if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
-			if (ax25->condition & ACK_PENDING_CONDITION) {
-				ax25->condition &= ~ACK_PENDING_CONDITION;
+			if (ax25->condition & AX25_COND_ACK_PENDING) {
+				ax25->condition &= ~AX25_COND_ACK_PENDING;
 				if (!ax25->dama_slave)
 					ax25_timeout_response(ax25);
 			}
@@ -144,35 +122,21 @@
 		/* dl1bke 960114: T3 expires and we are in DAMA mode:  */
 		/*                send a DISC and abort the connection */
 		if (ax25->dama_slave) {
-#ifdef CONFIG_NETROM
-			nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
-				
-			ax25->state = AX25_STATE_0;
-			if (ax25->sk != NULL) {
-				if (ax25->sk->debug)
-					printk("T3 Timeout\n");
-				ax25->sk->state = TCP_CLOSE;
-				ax25->sk->err   = ETIMEDOUT;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead  = 1;
-			}
-
-			ax25_reset_timer(ax25);
+			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+			ax25_disconnect(ax25, ETIMEDOUT);
+			ax25_set_timer(ax25);
 			return;
 		}
-		
+
 		if (ax25->state == AX25_STATE_3) {
 			ax25->n2count = 0;
 			ax25_transmit_enquiry(ax25);
 			ax25->state   = AX25_STATE_4;
 		}
+
 		ax25->t3timer = ax25->t3;
 	}
-	
+
 	if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
 		/* dl1bke 960228: close the connection when IDLE expires */
 		/* 		  similar to DAMA T3 timeout but with    */
@@ -183,35 +147,35 @@
 		ax25->n2count = 0;
 		if (!ax25->dama_slave) {
 			ax25->t3timer = 0;
-			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 		} else {
 			ax25->t3timer = ax25->t3;
 		}
-		
+
 		/* state 1 or 2 should not happen, but... */
-		
+
 		if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
 			ax25->state = AX25_STATE_0;
 		else
 			ax25->state = AX25_STATE_2;
 
 		ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+		ax25->t2timer = 0;
 
 		if (ax25->sk != NULL) {
-			ax25->sk->state = TCP_CLOSE;
-			ax25->sk->err = 0;
+			ax25->sk->state     = TCP_CLOSE;
+			ax25->sk->err       = 0;
+			ax25->sk->shutdown |= SEND_SHUTDOWN;
 			if (!ax25->sk->dead)
 				ax25->sk->state_change(ax25->sk);
-			ax25->sk->dead = 1;
-			ax25->sk->destroy = 1;
+			ax25->sk->dead      = 1;
 		}
 	}
-		                                                                                                                                                                                                                                                                                                                                        
+
 	/* dl1bke 960114: DAMA T1 timeouts are handled in ax25_dama_slave_transmit */
 	/* 		  nevertheless we have to re-enqueue the timer struct...   */
-	
 	if (ax25->t1timer == 0 || --ax25->t1timer > 0) {
-		ax25_reset_timer(ax25);
+		ax25_set_timer(ax25);
 		return;
 	}
 
@@ -227,64 +191,40 @@
  *                within the poll of any connected channel. Remember 
  *                that we are not allowed to send anything unless we
  *                get polled by the Master.
- *                
+ *
  *                Thus we'll have to do parts of our T1 handling in
  *                ax25_enquiry_response().
  */
-void ax25_t1_timeout(ax25_cb * ax25)
+void ax25_t1_timeout(ax25_cb *ax25)
 {	
 	switch (ax25->state) {
 		case AX25_STATE_1: 
 			if (ax25->n2count == ax25->n2) {
-				if (ax25->modulus == MODULUS) {
-#ifdef CONFIG_NETROM
-					nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
-					ax25_clear_queues(ax25);
-					ax25->state = AX25_STATE_0;
-					if (ax25->sk != NULL) {
-						ax25->sk->state = TCP_CLOSE;
-						ax25->sk->err   = ETIMEDOUT;
-						if (!ax25->sk->dead)
-							ax25->sk->state_change(ax25->sk);
-						ax25->sk->dead  = 1;
-					}
+				if (ax25->modulus == AX25_MODULUS) {
+					ax25_disconnect(ax25, ETIMEDOUT);
 				} else {
-					ax25->modulus = MODULUS;
+					ax25->modulus = AX25_MODULUS;
 					ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
 					ax25->n2count = 0;
-					ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
+					ax25_send_control(ax25, AX25_SABM, ax25_dev_is_dama_slave(ax25->device) ? AX25_POLLOFF : AX25_POLLON, AX25_COMMAND);
 				}
 			} else {
 				ax25->n2count++;
-				if (ax25->modulus == MODULUS) {
-					ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
-				} else {
-					ax25_send_control(ax25, SABME, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
-				}
+				if (ax25->modulus == AX25_MODULUS)
+					ax25_send_control(ax25, AX25_SABM, ax25_dev_is_dama_slave(ax25->device) ? AX25_POLLOFF : AX25_POLLON, AX25_COMMAND);
+				else
+					ax25_send_control(ax25, AX25_SABME, ax25_dev_is_dama_slave(ax25->device) ? AX25_POLLOFF : AX25_POLLON, AX25_COMMAND);
 			}
 			break;
 
 		case AX25_STATE_2:
 			if (ax25->n2count == ax25->n2) {
-#ifdef CONFIG_NETROM
-				nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
-				ax25_clear_queues(ax25);
-				ax25->state = AX25_STATE_0;
-				ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
-				
-				if (ax25->sk != NULL) {
-					ax25->sk->state = TCP_CLOSE;
-					ax25->sk->err   = ETIMEDOUT;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead  = 1;
-				}
+				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+				ax25_disconnect(ax25, ETIMEDOUT);
 			} else {
 				ax25->n2count++;
 				if (!ax25_dev_is_dama_slave(ax25->device))
-					ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+					ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 			}
 			break;
 
@@ -297,21 +237,8 @@
 
 		case AX25_STATE_4:
 			if (ax25->n2count == ax25->n2) {
-#ifdef CONFIG_NETROM
-				nr_link_failed(&ax25->dest_addr, ax25->device);
-#endif
-				ax25_clear_queues(ax25);
-				ax25_send_control(ax25, DM, POLLON, C_RESPONSE);
-				ax25->state = AX25_STATE_0;
-				if (ax25->sk != NULL) {
-					if (ax25->sk->debug)
-						printk("Link Failure\n");
-					ax25->sk->state = TCP_CLOSE;
-					ax25->sk->err   = ETIMEDOUT;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead  = 1;
-				}
+				ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+				ax25_disconnect(ax25, ETIMEDOUT);
 			} else {
 				ax25->n2count++;
 				if (!ax25->dama_slave)
@@ -323,6 +250,241 @@
 	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 
 	ax25_set_timer(ax25);
+}
+
+/************************************************************************/
+/*	Module support functions follow.				*/
+/************************************************************************/
+
+static struct protocol_struct {
+	struct protocol_struct *next;
+	unsigned int pid;
+	int (*func)(struct sk_buff *, ax25_cb *);
+} *protocol_list = NULL;
+
+static struct linkfail_struct {
+	struct linkfail_struct *next;
+	void (*func)(ax25_cb *, int);
+} *linkfail_list = NULL;
+
+static struct listen_struct {
+	struct listen_struct *next;
+	ax25_address  callsign;
+	struct device *dev;
+} *listen_list = NULL;
+
+int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
+{
+	struct protocol_struct *protocol;
+	unsigned long flags;
+
+	if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
+		return 0;
+#ifdef CONFIG_INET
+	if (pid == AX25_P_IP || pid == AX25_P_ARP)
+		return 0;
+#endif
+	if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	protocol->pid  = pid;
+	protocol->func = func;
+
+	save_flags(flags);
+	cli();
+
+	protocol->next = protocol_list;
+	protocol_list  = protocol;
+
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_protocol_release(unsigned int pid)
+{
+	struct protocol_struct *s, *protocol = protocol_list;
+	unsigned long flags;
+
+	if (protocol == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (protocol->pid == pid) {
+		protocol_list = protocol->next;
+		restore_flags(flags);
+		kfree_s(protocol, sizeof(struct protocol_struct));
+		return;
+	}
+
+	while (protocol != NULL && protocol->next != NULL) {
+		if (protocol->next->pid == pid) {
+			s = protocol->next;
+			protocol->next = protocol->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct protocol_struct));
+			return;
+		}
+
+		protocol = protocol->next;
+	}
+
+	restore_flags(flags);
+}
+
+int ax25_linkfail_register(void (*func)(ax25_cb *, int))
+{
+	struct linkfail_struct *linkfail;
+	unsigned long flags;
+
+	if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	linkfail->func = func;
+
+	save_flags(flags);
+	cli();
+
+	linkfail->next = linkfail_list;
+	linkfail_list  = linkfail;
+
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_linkfail_release(void (*func)(ax25_cb *, int))
+{
+	struct linkfail_struct *s, *linkfail = linkfail_list;
+	unsigned long flags;
+
+	if (linkfail == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (linkfail->func == func) {
+		linkfail_list = linkfail->next;
+		restore_flags(flags);
+		kfree_s(linkfail, sizeof(struct linkfail_struct));
+		return;
+	}
+
+	while (linkfail != NULL && linkfail->next != NULL) {
+		if (linkfail->next->func == func) {
+			s = linkfail->next;
+			linkfail->next = linkfail->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct linkfail_struct));
+			return;
+		}
+
+		linkfail = linkfail->next;
+	}
+
+	restore_flags(flags);
+}
+
+int ax25_listen_register(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *listen;
+	unsigned long flags;
+
+	if (ax25_listen_mine(callsign, dev))
+		return 0;
+
+	if ((listen = (struct listen_struct *)kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	listen->callsign = *callsign;
+	listen->dev      = dev;
+
+	save_flags(flags);
+	cli();
+
+	listen->next = listen_list;
+	listen_list  = listen;
+
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_listen_release(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *s, *listen = listen_list;
+	unsigned long flags;
+
+	if (listen == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
+		listen_list = listen->next;
+		restore_flags(flags);
+		kfree_s(listen, sizeof(struct listen_struct));
+		return;
+	}
+
+	while (listen != NULL && listen->next != NULL) {
+		if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
+			s = listen->next;
+			listen->next = listen->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct listen_struct));
+			return;
+		}
+
+		listen = listen->next;
+	}
+
+	restore_flags(flags);
+}
+
+int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
+{
+	struct protocol_struct *protocol;
+
+	for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+		if (protocol->pid == pid)
+			return protocol->func;
+
+	return NULL;
+}
+
+int ax25_listen_mine(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *listen;
+
+	for (listen = listen_list; listen != NULL; listen = listen->next)
+		if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
+			return 1;
+
+	return 0;
+}
+
+void ax25_link_failed(ax25_cb *ax25, int reason)
+{
+	struct linkfail_struct *linkfail;
+
+	for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
+		(linkfail->func)(ax25, reason);
+}
+
+int ax25_protocol_is_registered(unsigned int pid)
+{
+	struct protocol_struct *protocol;
+
+	for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+		if (protocol->pid == pid)
+			return 1;
+
+	return 0;
 }
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/net/ax25/sysctl_net_ax25.c linux/net/ax25/sysctl_net_ax25.c
--- v2.0.34/linux/net/ax25/sysctl_net_ax25.c	Mon Apr  1 22:03:35 1996
+++ linux/net/ax25/sysctl_net_ax25.c	Mon Jul 13 13:47:40 1998
@@ -7,7 +7,118 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <net/ax25.h>
 
-ctl_table ax25_table[] = {
+static int min_ipdefmode[] = {0},	max_ipdefmode[] = {1};
+static int min_axdefmode[] = {0},	max_axdefmode[] = {1};
+static int min_backoff[] = {0},		max_backoff[] = {2};
+static int min_conmode[] = {0},		max_conmode[] = {2};
+static int min_window[] = {1},		max_window[] = {7};
+static int min_ewindow[] = {1},		max_ewindow[] = {63};
+static int min_t1[] = {1},		max_t1[] = {30 * AX25_SLOWHZ};
+static int min_t2[] = {1},		max_t2[] = {20 * AX25_SLOWHZ};
+static int min_t3[] = {0},		max_t3[] = {3600 * AX25_SLOWHZ};
+static int min_idle[] = {0},		max_idle[] = {65535 * AX25_SLOWHZ};
+static int min_n2[] = {1},		max_n2[] = {31};
+static int min_paclen[] = {1},		max_paclen[] = {512};
+
+static struct ctl_table_header *ax25_table_header;
+
+static ctl_table ax25_table[AX25_MAX_DEVICES + 1];
+
+static ctl_table ax25_dir_table[] = {
+	{NET_AX25, "ax25", NULL, 0, 0555, ax25_table},
 	{0}
 };
+
+static ctl_table ax25_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, ax25_dir_table},
+	{0}
+};
+
+static const ctl_table ax25_param_table[] = {
+	{NET_AX25_IP_DEFAULT_MODE, "ip_default_mode",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_ipdefmode, &max_ipdefmode},
+	{NET_AX25_DEFAULT_MODE, "ax25_default_mode",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_axdefmode, &max_axdefmode},
+	{NET_AX25_BACKOFF_TYPE, "backoff_type",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_backoff, &max_backoff},
+	{NET_AX25_CONNECT_MODE, "connect_mode",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_conmode, &max_conmode},
+	{NET_AX25_STANDARD_WINDOW, "standard_window_size",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_window, &max_window},
+	{NET_AX25_EXTENDED_WINDOW, "extended_window_size",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_ewindow, &max_ewindow},
+	{NET_AX25_T1_TIMEOUT, "t1_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_t1, &max_t1},
+	{NET_AX25_T2_TIMEOUT, "t2_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_t2, &max_t2},
+	{NET_AX25_T3_TIMEOUT, "t3_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_t3, &max_t3},
+	{NET_AX25_IDLE_TIMEOUT, "idle_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_idle, &max_idle},
+	{NET_AX25_N2, "maximum_retry_count",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_n2, &max_n2},
+	{NET_AX25_PACLEN, "maximum_packet_length",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_paclen, &max_paclen},
+	{0}	/* that's all, folks! */
+};
+
+void ax25_register_sysctl(void)
+{
+	int i, n, k;
+
+	memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table));
+
+	for (n = 0, i = 0; i < AX25_MAX_DEVICES; i++) {
+		if (ax25_device[i].dev != NULL) {
+			ax25_table[n].ctl_name     = n + 1;
+			ax25_table[n].procname     = ax25_device[i].name;
+			ax25_table[n].data         = NULL;
+			ax25_table[n].maxlen       = 0;
+			ax25_table[n].mode         = 0555;
+			ax25_table[n].child        = ax25_device[i].systable;
+			ax25_table[n].proc_handler = NULL;
+
+			memcpy(ax25_device[i].systable, ax25_param_table, sizeof(ax25_device[i].systable));
+
+			ax25_device[i].systable[AX25_MAX_VALUES].ctl_name = 0;	/* just in case... */
+
+			for (k = 0; k < AX25_MAX_VALUES; k++)
+				ax25_device[i].systable[k].data = &ax25_device[i].values[k];
+
+			n++;
+		}
+	}
+
+	ax25_table_header = register_sysctl_table(ax25_root_table, 1);
+}
+
+void ax25_unregister_sysctl(void)
+{
+	unregister_sysctl_table(ax25_table_header);
+}
diff -u --recursive --new-file v2.0.34/linux/net/core/dev.c linux/net/core/dev.c
--- v2.0.34/linux/net/core/dev.c	Mon Jul 13 13:46:43 1998
+++ linux/net/core/dev.c	Mon Jul 13 13:47:40 1998
@@ -1503,6 +1503,8 @@
  */
 extern int lance_init(void);
 extern int pi_init(void);
+extern int pt_init(void);
+extern int bpq_init(void);
 extern void sdla_setup(void);
 extern int dlci_setup(void);
 
@@ -1534,6 +1536,9 @@
 #endif	
 #if defined(CONFIG_PT)
 	pt_init();
+#endif
+#if defined(CONFIG_BPQETHER)
+	bpq_init();
 #endif
 #if defined(CONFIG_DLCI)
 	dlci_setup();
diff -u --recursive --new-file v2.0.34/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.0.34/linux/net/ipv4/arp.c	Mon Jul 13 13:46:43 1998
+++ linux/net/ipv4/arp.c	Mon Jul 13 13:47:41 1998
@@ -102,9 +102,9 @@
 #include <net/tcp.h>
 #include <net/sock.h>
 #include <net/arp.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <net/netrom.h>
 #endif
 #endif
@@ -1726,8 +1726,8 @@
 	 *	Exceptions everywhere. AX.25 uses the AX.25 PID value not the
 	 *	DIX code for the protocol. Make these device structure fields.
 	 */
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
 #else
 	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
@@ -1837,7 +1837,7 @@
 
   	switch (dev->type)
   	{
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 		case ARPHRD_AX25:
 			if(arp->ar_pro != htons(AX25_P_IP))
 			{
@@ -1846,7 +1846,7 @@
 			}
 			break;
 #endif
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 		case ARPHRD_NETROM:
 			if(arp->ar_pro != htons(AX25_P_IP))
 			{
@@ -2358,8 +2358,8 @@
 /*
  *	Convert hardware address to XX:XX:XX:XX ... form.
  */
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
 			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
 			else {
@@ -2378,7 +2378,7 @@
 			}
 			hbuffer[--k]=0;
 	
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 			}
 #endif
 			size = sprintf(buffer+len,
@@ -2464,3 +2464,39 @@
 	netlink_attach(NETLINK_ARPD, arpd_callback);
 #endif
 }
+
+#ifdef CONFIG_AX25_MODULE
+
+/*
+ *	ax25 -> ascii conversion
+ */
+char *ax2asc(ax25_address *a)
+{
+	static char buf[11];
+	char c, *s;
+	int n;
+
+	for (n = 0, s = buf; n < 6; n++) {
+		c = (a->ax25_call[n] >> 1) & 0x7F;
+
+		if (c != ' ') *s++ = c;
+	}
+	
+	*s++ = '-';
+
+	if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
+		*s++ = '1';
+		n -= 10;
+	}
+	
+	*s++ = n + '0';
+	*s++ = '\0';
+
+	if (*buf == '\0' || *buf == '-')
+	   return "*";
+
+	return buf;
+
+}
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v2.0.34/linux/net/ipv4/rarp.c	Mon Jul 13 13:46:43 1998
+++ linux/net/ipv4/rarp.c	Mon Jul 13 13:47:41 1998
@@ -61,7 +61,7 @@
 #include <net/sock.h>
 #include <net/arp.h>
 #include <net/rarp.h>
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>
 #endif
 #include <linux/proc_fs.h>
@@ -97,7 +97,7 @@
 	NULL
 };
 
-static initflag = 1;
+static int initflag = 1;
 
 
 /*
@@ -268,7 +268,7 @@
  */
 
 	if (
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 		(rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
 #endif
 		(rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
@@ -347,7 +347,7 @@
 			htype = ARPHRD_ETHER;
 			hlen = ETH_ALEN;
 			break;
-#ifdef CONFIG_AX25
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 		case ARPHRD_AX25:
 			htype = ARPHRD_AX25;
 			hlen = 7;
diff -u --recursive --new-file v2.0.34/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.0.34/linux/net/ipv4/route.c	Mon Jul 13 13:46:43 1998
+++ linux/net/ipv4/route.c	Mon Jul 13 13:47:41 1998
@@ -925,6 +925,15 @@
 static __inline__ void rt_kick_free_queue(void)
 {
 	struct rtable *rt, **rtp;
+#if RT_CACHE_DEBUG >= 2
+	static int in = 0;
+
+	if(in) {
+		printk("Attempted multiple entry: rt_kick_free_queue\n");
+		return;
+	}
+	in++;
+#endif
 
 	ip_rt_bh_mask &= ~RT_BH_FREE;
 
@@ -952,6 +961,9 @@
 		}
 		rtp = &rt->rt_next;
 	}
+#if RT_CACHE_DEBUG >= 2
+	in--;
+#endif
 }
 
 void ip_rt_run_bh()
@@ -974,8 +986,11 @@
 			ip_rt_fast_unlock();
 		}
 
-		if (ip_rt_bh_mask & RT_BH_FREE)
+		if (ip_rt_bh_mask & RT_BH_FREE) {
+			ip_rt_fast_lock();
 			rt_kick_free_queue();
+			ip_rt_fast_unlock();
+		}
 	}
 	restore_flags(flags);
 }
@@ -1528,12 +1543,10 @@
 
 void ip_rt_put(struct rtable * rt)
 {
-	if (rt)
-		atomic_dec(&rt->rt_refcnt);
-	
-	/* If this rtable entry is not in the cache, we'd better free it once the
-	 * refcnt goes to zero, because nobody else will... */
-	if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) )
+	/* If this rtable entry is not in the cache, we'd better free
+	 * it once the refcnt goes to zero, because nobody else will.
+	 */
+	if (rt&&atomic_dec_and_test(&rt->rt_refcnt)&&(rt->rt_flags&RTF_NOTCACHED))
 		rt_free(rt);
 }
 
diff -u --recursive --new-file v2.0.34/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.0.34/linux/net/ipv4/tcp_input.c	Mon Jul 13 13:46:43 1998
+++ linux/net/ipv4/tcp_input.c	Mon Jul 13 13:47:41 1998
@@ -36,6 +36,14 @@
  *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *	Willy Konynenberg	:	Transparent proxy adapted to new
  *					socket hash code.
+ *	J Hadi Salim		:	We assumed that some idiot wasnt going
+ *	Alan Cox			to idly redefine bits of ToS in an
+ *					experimental protocol for other things
+ *					(ECN) - wrong!. Mask the bits off. Note
+ *					masking the bits if they dont use ECN
+ *					then use it for ToS is even more 
+ *					broken. 
+ *					</RANT>
  */
 
 #include <linux/config.h>
@@ -44,6 +52,12 @@
 #include <net/tcp.h>
 
 /*
+ *	Do we assume the IP ToS is entirely for its intended purpose
+ */
+ 
+#define TOS_VALID_MASK(x)		((x)&0x3F)
+ 
+/*
  *	Policy code extracted so it's now separate
  */
 
@@ -764,7 +778,7 @@
 	 */
 
 	newsk->ip_ttl=sk->ip_ttl;
-	newsk->ip_tos=skb->ip_hdr->tos;
+	newsk->ip_tos=TOS_VALID_MASK(skb->ip_hdr->tos);
 
 	/*
 	 *	Use 512 or whatever user asked for 
@@ -1024,7 +1038,7 @@
 	 */
 
 	newsk->ip_ttl=sk->ip_ttl;
-	newsk->ip_tos=skb->ip_hdr->tos;
+	newsk->ip_tos=TOS_VALID_MASK(skb->ip_hdr->tos);
 
 	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
 			 sk->bound_device);
@@ -1725,7 +1739,7 @@
 		if(sk->ip_xmit_timeout==TIME_KEEPOPEN)
 			tcp_reset_xmit_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
 	}
-	return 0;
+	return 1;
 }
 
 
@@ -2388,6 +2402,14 @@
 	 
 		if(sk->state==TCP_LISTEN)
 		{
+			/* Don't start connections with illegal address
+			   ranges. Trying to talk TCP to a broken dhcp host
+			   isnt good on a lan with broken SunOS 4.x boxes 
+			   who think its a broadcast */
+			   
+			if ((saddr | daddr) == 0)
+				goto discard_it;
+				
 			if (th->ack) {	/* These use the socket TOS.. might want to be the received TOS */
 #ifdef CONFIG_SYN_COOKIES
 				if (!th->syn && !th->rst) {
diff -u --recursive --new-file v2.0.34/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.0.34/linux/net/ipv4/tcp_output.c	Mon Jul 13 13:46:43 1998
+++ linux/net/ipv4/tcp_output.c	Mon Jul 13 13:47:41 1998
@@ -34,6 +34,8 @@
  *				  refer to the old obsolete destination.
  *              Elliot Poger    : Added support for SO_BINDTODEVICE.
  *	Juan Jose Ciarlante	: Added sock dynamic source address rewriting
+ *		Alan Cox	: Clear reserved fields - bug reported by
+ *				  J Hadi Salim
  */
 
 #include <linux/config.h>
@@ -837,6 +839,8 @@
 	t1->psh = 0;
 	t1->fin = 0;		/* In case someone sent us a SYN|FIN frame! */
 	t1->doff = sizeof(*t1)/4;
+	t1->res1 = 0;	/* RFC requires this, we upset ECN without it */
+	t1->res2 = 0;
 
 	tcp_send_check(t1, saddr, daddr, sizeof(*t1), buff);
 	prot->queue_xmit(NULL, ndev, buff, 1);
@@ -1017,6 +1021,8 @@
 	t1->psh = 0;
 	t1->ack_seq = htonl(newsk->acked_seq);
 	t1->doff = sizeof(*t1)/4+1;
+	t1->res1 = 0;
+	t1->res2 = 0;
 	ptr = skb_put(buff,4);
 	ptr[0] = 2;
 	ptr[1] = 4;
diff -u --recursive --new-file v2.0.34/linux/net/netrom/Makefile linux/net/netrom/Makefile
--- v2.0.34/linux/net/netrom/Makefile	Mon Apr  1 21:43:08 1996
+++ linux/net/netrom/Makefile	Mon Jul 13 13:47:41 1998
@@ -1,5 +1,5 @@
 #
-# Makefile for the Linux TCP/IP (INET) layer.
+# Makefile for the Linux NET/ROM layer.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -8,11 +8,8 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := netrom.o
-O_OBJS	 := af_netrom.o sysctl_net_netrom.o
-
-ifdef CONFIG_NETROM
-O_OBJS	+= nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
-endif
+O_OBJS	 := af_netrom.o sysctl_net_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o nr_loopback.o
+M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.0.34/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.0.34/linux/net/netrom/af_netrom.c	Mon Jul 13 13:46:43 1998
+++ linux/net/netrom/af_netrom.c	Mon Jul 13 13:47:41 1998
@@ -1,8 +1,5 @@
 /*
- *	NET/ROM release 003
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	NET/ROM release 006
  *
  *	This code REQUIRES 1.3.0 or higher/ NET3.029
  *
@@ -26,10 +23,12 @@
  *					a connection.
  *			Alan(GW4PTS)	sendmsg/recvmsg only. Fixed connect clear bug
  *					inherited from AX.25
+ *	NET/ROM 004	Jonathan(G4KLX)	Converted to module.
  */
-  
+
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -60,12 +59,53 @@
 #include <net/arp.h>
 #include <linux/if_arp.h>
 
-struct nr_parms_struct nr_default;
+int sysctl_netrom_default_path_quality            = NR_DEFAULT_QUAL;
+int sysctl_netrom_obsolescence_count_initialiser  = NR_DEFAULT_OBS;
+int sysctl_netrom_network_ttl_initialiser         = NR_DEFAULT_TTL;
+int sysctl_netrom_transport_timeout               = NR_DEFAULT_T1;
+int sysctl_netrom_transport_maximum_tries         = NR_DEFAULT_N2;
+int sysctl_netrom_transport_acknowledge_delay     = NR_DEFAULT_T2;
+int sysctl_netrom_transport_busy_delay            = NR_DEFAULT_T4;
+int sysctl_netrom_transport_requested_window_size = NR_DEFAULT_WINDOW;
+int sysctl_netrom_routing_control                 = NR_DEFAULT_ROUTING;
+int sysctl_netrom_link_fails_count                = NR_DEFAULT_FAILS;
 
 static unsigned short circuit = 0x101;
 
 static struct sock *volatile nr_list = NULL;
 
+static void nr_free_sock(struct sock *sk)
+{
+	kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr));
+
+	sk_free(sk);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static struct sock *nr_alloc_sock(void)
+{
+	struct sock *sk;
+	nr_cb *nr;
+
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	memset(nr, 0x00, sizeof(*nr));
+
+	sk->protinfo.nr = nr;
+	nr->sk = sk;
+
+	return sk;
+}
+
 /*
  *	Socket removal during an interrupt is now safe.
  */
@@ -73,7 +113,7 @@
 {
 	struct sock *s;
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
 
@@ -102,15 +142,16 @@
 static void nr_kill_by_device(struct device *dev)
 {
 	struct sock *s;
-	
+
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (s->nr->device == dev) {
-			s->nr->state  = NR_STATE_0;
-			s->nr->device = NULL;
-			s->state = TCP_CLOSE;
-			s->err   = ENETUNREACH;
+		if (s->protinfo.nr->device == dev) {
+			s->protinfo.nr->state  = NR_STATE_0;
+			s->protinfo.nr->device = NULL;
+			s->state               = TCP_CLOSE;
+			s->err                 = ENETUNREACH;
+			s->shutdown           |= SEND_SHUTDOWN;
 			s->state_change(s);
-			s->dead  = 1;
+			s->dead                = 1;
 		}
 	}
 }
@@ -124,7 +165,7 @@
 
 	if (event != NETDEV_DOWN)
 		return NOTIFY_DONE;
-		
+
 	nr_kill_by_device(dev);
 	nr_rt_device_down(dev);
 	
@@ -160,7 +201,7 @@
 	cli();
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (ax25cmp(&s->nr->source_addr, addr) == 0 && s->state == TCP_LISTEN) {
+		if (ax25cmp(&s->protinfo.nr->source_addr, addr) == 0 && s->state == TCP_LISTEN) {
 			restore_flags(flags);
 			return s;
 		}
@@ -182,7 +223,7 @@
 	cli();
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (s->nr->my_index == index && s->nr->my_id == id) {
+		if (s->protinfo.nr->my_index == index && s->protinfo.nr->my_id == id) {
 			restore_flags(flags);
 			return s;
 		}
@@ -205,7 +246,7 @@
 	cli();
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (s->nr->your_index == index && s->nr->your_id == id) {
+		if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id) {
 			restore_flags(flags);
 			return s;
 		}
@@ -217,6 +258,28 @@
 }
 
 /*
+ *	Find next free circuit ID.
+ */
+static unsigned short nr_find_next_circuit(void)
+{
+	unsigned short id = circuit;
+	unsigned char i, j;
+
+	while (1) {
+		i = id / 256;
+		j = id % 256;
+
+		if (i != 0 && j != 0)
+			if (nr_find_socket(i, j) == NULL)
+				break;
+
+		id++;
+	}
+
+	return id;
+}
+
+/*
  *	Deferred destroy.
  */
 void nr_destroy_socket(struct sock *);
@@ -239,34 +302,33 @@
 {
 	struct sk_buff *skb;
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
-	
+
 	del_timer(&sk->timer);
-	
+
 	nr_remove_socket(sk);
 	nr_clear_queues(sk);		/* Flush the queues */
-	
+
 	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
 		if (skb->sk != sk) {			/* A pending connection */
 			skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
 			nr_set_timer(skb->sk);
-			skb->sk->nr->state = NR_STATE_0;
+			skb->sk->protinfo.nr->state = NR_STATE_0;
 		}
 
 		kfree_skb(skb, FREE_READ);
 	}
-	
-	if (sk->wmem_alloc || sk->rmem_alloc) { /* Defer: outstanding buffers */
+
+	if (sk->wmem_alloc != 0 || sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
 		init_timer(&sk->timer);
 		sk->timer.expires  = jiffies + 10 * HZ;
 		sk->timer.function = nr_destroy_timer;
 		sk->timer.data     = (unsigned long)sk;
 		add_timer(&sk->timer);
 	} else {
-		kfree_s(sk->nr, sizeof(*sk->nr));
-		sk_free(sk);
+		nr_free_sock(sk);
 	}
 
 	restore_flags(flags);
@@ -276,96 +338,17 @@
  *	Handling for system calls applied via the various interfaces to a
  *	NET/ROM socket object.
  */
- 
 static int nr_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	return -EINVAL;
 }
 
-/*
- * dl1bke 960311: set parameters for existing NET/ROM connections,
- *		  includes a KILL command to abort any connection.
- *		  VERY useful for debugging ;-)
- */
-static int nr_ctl_ioctl(const unsigned int cmd, void *arg)
-{
-	struct nr_ctl_struct nr_ctl;
-	struct sock *sk;
-	unsigned long flags;
-	int err;
-	
-	if ((err = verify_area(VERIFY_READ, arg, sizeof(nr_ctl))) != 0)
-		return err;
-
-	memcpy_fromfs(&nr_ctl, arg, sizeof(nr_ctl));
-	
-	if ((sk = nr_find_socket(nr_ctl.index, nr_ctl.id)) == NULL)
-		return -ENOTCONN;
-
-	switch (nr_ctl.cmd) {
-		case NETROM_KILL:
-			nr_clear_queues(sk);
-			nr_write_internal(sk, NR_DISCREQ);
-			sk->nr->state = NR_STATE_0;
-			sk->state = TCP_CLOSE;
-			sk->err   = ENETRESET;
-			if (!sk->dead)
-				sk->state_change(sk);
-			sk->dead  = 1;
-			nr_set_timer(sk);
-	  		break;
-
-	  	case NETROM_T1:
-  			if (nr_ctl.arg < 1) 
-  				return -EINVAL;
-  			sk->nr->rtt = (nr_ctl.arg * PR_SLOWHZ) / 2;
-  			sk->nr->t1 = nr_ctl.arg * PR_SLOWHZ;
-  			save_flags(flags); cli();
-  			if (sk->nr->t1timer > sk->nr->t1)
-  				sk->nr->t1timer = sk->nr->t1;
-  			restore_flags(flags);
-  			break;
-
-	  	case NETROM_T2:
-	  		if (nr_ctl.arg < 1) 
-	  			return -EINVAL;
-	  		save_flags(flags); cli();
-	  		sk->nr->t2 = nr_ctl.arg * PR_SLOWHZ;
-	  		if (sk->nr->t2timer > sk->nr->t2)
-	  			sk->nr->t2timer = sk->nr->t2;
-	  		restore_flags(flags);
-	  		break;
-
-	  	case NETROM_N2:
-	  		if (nr_ctl.arg < 1 || nr_ctl.arg > 10) 
-	  			return -EINVAL;
-	  		sk->nr->n2count = 0;
-	  		sk->nr->n2 = nr_ctl.arg;
-	  		break;
-
-	  	case NETROM_PACLEN:
-	  		if (nr_ctl.arg < 16 || nr_ctl.arg > 65535) 
-	  			return -EINVAL;
-	  		if (nr_ctl.arg > 236) /* we probably want this */
-	  			printk(KERN_WARNING "nr_ctl_ioctl: Warning --- huge paclen %d\n", (int)nr_ctl.arg);
-	  		sk->nr->paclen = nr_ctl.arg;
-	  		break;
-
-	  	default:
-	  		return -EINVAL;
-	  }
-	  
-	  return 0;
-}
-
 static int nr_setsockopt(struct socket *sock, int level, int optname,
 	char *optval, int optlen)
 {
-	struct sock *sk;
+	struct sock *sk = (struct sock *)sock->data;
 	int err, opt;
 
-	sk = (struct sock *)sock->data;
-	
 	if (level == SOL_SOCKET)
 		return sock_setsockopt(sk, level, optname, optval, optlen);
 
@@ -379,36 +362,37 @@
 		return err;
 
 	opt = get_fs_long((unsigned long *)optval);
-	
+
 	switch (optname) {
 		case NETROM_T1:
 			if (opt < 1)
 				return -EINVAL;
-			sk->nr->rtt = (opt * PR_SLOWHZ) / 2;
+			sk->protinfo.nr->t1 = opt * NR_SLOWHZ;
 			return 0;
 
 		case NETROM_T2:
 			if (opt < 1)
 				return -EINVAL;
-			sk->nr->t2 = opt * PR_SLOWHZ;
+			sk->protinfo.nr->t2 = opt * NR_SLOWHZ;
 			return 0;
-			
+
 		case NETROM_N2:
 			if (opt < 1 || opt > 31)
 				return -EINVAL;
-			sk->nr->n2 = opt;
+			sk->protinfo.nr->n2 = opt;
 			return 0;
-			
-		case NETROM_HDRINCL:
-			sk->nr->hdrincl = opt ? 1 : 0;
+
+		case NETROM_T4:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.nr->t4 = opt * NR_SLOWHZ;
 			return 0;
 
-		case NETROM_PACLEN:
-			if (opt < 1 || opt > 65536)
+		case NETROM_IDLE:
+			if (opt < 0)
 				return -EINVAL;
-			sk->nr->paclen = opt;
 			return 0;
-			
+
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -417,37 +401,35 @@
 static int nr_getsockopt(struct socket *sock, int level, int optname,
 	char *optval, int *optlen)
 {
-	struct sock *sk;
+	struct sock *sk = (struct sock *)sock->data;
 	int val = 0;
 	int err; 
 
-	sk = (struct sock *)sock->data;
-	
 	if (level == SOL_SOCKET)
 		return sock_getsockopt(sk, level, optname, optval, optlen);
-	
+
 	if (level != SOL_NETROM)
 		return -EOPNOTSUPP;
-	
+
 	switch (optname) {
 		case NETROM_T1:
-			val = (sk->nr->t1 * 2) / PR_SLOWHZ;
+			val = sk->protinfo.nr->t1 / NR_SLOWHZ;
 			break;
-			
+
 		case NETROM_T2:
-			val = sk->nr->t2 / PR_SLOWHZ;
+			val = sk->protinfo.nr->t2 / NR_SLOWHZ;
 			break;
-			
+
 		case NETROM_N2:
-			val = sk->nr->n2;
+			val = sk->protinfo.nr->n2;
 			break;
-						
-		case NETROM_HDRINCL:
-			val = sk->nr->hdrincl;
+
+		case NETROM_T4:
+			val = sk->protinfo.nr->t4 / NR_SLOWHZ;
 			break;
 
-		case NETROM_PACLEN:
-			val = sk->nr->paclen;
+		case NETROM_IDLE:
+			val = 0;
 			break;
 
 		default:
@@ -472,7 +454,7 @@
 	struct sock *sk = (struct sock *)sock->data;
 
 	if (sk->state != TCP_LISTEN) {
-		memset(&sk->nr->user_addr, '\0', AX25_ADDR_LEN);
+		memset(&sk->protinfo.nr->user_addr, '\0', AX25_ADDR_LEN);
 		sk->max_ack_backlog = backlog;
 		sk->state           = TCP_LISTEN;
 		return 0;
@@ -501,13 +483,10 @@
 	if (sock->type != SOCK_SEQPACKET || protocol != 0)
 		return -ESOCKTNOSUPPORT;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = nr_alloc_sock()) == NULL)
 		return -ENOMEM;
 
-	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
-		sk_free(sk);
-		return -ENOMEM;
-	}
+	nr = sk->protinfo.nr;
 
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -525,7 +504,6 @@
 	sk->priority      = SOPRI_NORMAL;
 	sk->mtu           = NETROM_MTU;	/* 236 */
 	sk->zapped        = 1;
-	sk->window	  = nr_default.window;
 
 	sk->state_change = def_callback1;
 	sk->data_ready   = def_callback2;
@@ -541,42 +519,14 @@
 	skb_queue_head_init(&nr->reseq_queue);
 	skb_queue_head_init(&nr->frag_queue);
 
-	nr->my_index = 0;
-	nr->my_id    = 0;
-	nr->rtt      = nr_default.timeout / 2;
-	nr->t1       = nr_default.timeout;
-	nr->t2       = nr_default.ack_delay;
-	nr->n2       = nr_default.tries;
-	nr->paclen   = nr_default.paclen;
-
-	nr->t1timer  = 0;
-	nr->t2timer  = 0;
-	nr->t4timer  = 0;
-	nr->n2count  = 0;
-
-	nr->va       = 0;
-	nr->vr       = 0;
-	nr->vs       = 0;
-	nr->vl       = 0;
-
-	nr->your_index = 0;
-	nr->your_id    = 0;
-
-	nr->my_index   = 0;
-	nr->my_id      = 0;
-
-	nr->bpqext     = 1;
-	nr->fraglen    = 0;
-	nr->hdrincl    = 0;
-	nr->state      = NR_STATE_0;
-	nr->device     = NULL;
-
-	memset(&nr->source_addr, '\0', AX25_ADDR_LEN);
-	memset(&nr->user_addr,   '\0', AX25_ADDR_LEN);
-	memset(&nr->dest_addr,   '\0', AX25_ADDR_LEN);
+	nr->t1     = sysctl_netrom_transport_timeout;
+	nr->t2     = sysctl_netrom_transport_acknowledge_delay;
+	nr->n2     = sysctl_netrom_transport_maximum_tries;
+	nr->t4     = sysctl_netrom_transport_busy_delay;
+	nr->window = sysctl_netrom_transport_requested_window_size;
 
-	nr->sk = sk;
-	sk->nr = nr;
+	nr->bpqext = 1;
+	nr->state  = NR_STATE_0;
 
 	return 0;
 }
@@ -589,13 +539,10 @@
 	if (osk->type != SOCK_SEQPACKET)
 		return NULL;
 
-	if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = nr_alloc_sock()) == NULL)
 		return NULL;
 
-	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
-		sk_free(sk);
-		return NULL;
-	}
+	nr = sk->protinfo.nr;
 
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -611,7 +558,6 @@
 	sk->sndbuf      = osk->sndbuf;
 	sk->debug       = osk->debug;
 	sk->state       = TCP_ESTABLISHED;
-	sk->window      = osk->window;
 	sk->mtu         = osk->mtu;
 	sk->sleep       = osk->sleep;
 	sk->zapped      = osk->zapped;
@@ -625,29 +571,14 @@
 	skb_queue_head_init(&nr->reseq_queue);
 	skb_queue_head_init(&nr->frag_queue);
 
-	nr->rtt      = osk->nr->rtt;
-	nr->t1       = osk->nr->t1;
-	nr->t2       = osk->nr->t2;
-	nr->n2       = osk->nr->n2;
-	nr->paclen   = osk->nr->paclen;
-
-	nr->device   = osk->nr->device;
-	nr->bpqext   = osk->nr->bpqext;
-	nr->hdrincl  = osk->nr->hdrincl;
-	nr->fraglen  = 0;
-
-	nr->t1timer  = 0;
-	nr->t2timer  = 0;
-	nr->t4timer  = 0;
-	nr->n2count  = 0;
-
-	nr->va       = 0;
-	nr->vr       = 0;
-	nr->vs       = 0;
-	nr->vl       = 0;
-	
-	sk->nr = nr;
-	nr->sk = sk;
+	nr->t1     = osk->protinfo.nr->t1;
+	nr->t2     = osk->protinfo.nr->t2;
+	nr->n2     = osk->protinfo.nr->n2;
+	nr->t4     = osk->protinfo.nr->t4;
+	nr->window = osk->protinfo.nr->window;
+
+	nr->device  = osk->protinfo.nr->device;
+	nr->bpqext  = osk->protinfo.nr->bpqext;
 
 	return sk;
 }
@@ -656,6 +587,9 @@
 {
 	struct sock *sk = (struct sock *)oldsock->data;
 
+	if (sk == NULL || newsock == NULL)
+		return -EINVAL;
+
 	return nr_create(newsock, sk->protocol);
 }
 
@@ -665,44 +599,48 @@
 
 	if (sk == NULL) return 0;
 
-	switch (sk->nr->state) {
+	switch (sk->protinfo.nr->state) {
 
 		case NR_STATE_0:
 			sk->state     = TCP_CLOSE;
+			sk->shutdown |= SEND_SHUTDOWN;
 			sk->state_change(sk);
 			sk->dead      = 1;
 			nr_destroy_socket(sk);
 			break;
 
-		case NR_STATE_1:
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->state_change(sk);
-			sk->dead      = 1;
+                case NR_STATE_1:
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->shutdown          |= SEND_SHUTDOWN;
+                        sk->state_change(sk);
+			sk->dead               = 1;
 			nr_destroy_socket(sk);
 			break;
 
 		case NR_STATE_2:
 			nr_write_internal(sk, NR_DISCACK);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->shutdown          |= SEND_SHUTDOWN;
 			sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			nr_destroy_socket(sk);
 			break;			
 
 		case NR_STATE_3:
 			nr_clear_queues(sk);
-			sk->nr->n2count = 0;
+			sk->protinfo.nr->n2count = 0;
 			nr_write_internal(sk, NR_DISCREQ);
-			sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
-			sk->nr->t2timer = 0;
-			sk->nr->t4timer = 0;
-			sk->nr->state   = NR_STATE_2;
-			sk->state       = TCP_CLOSE;
+			sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
+			sk->protinfo.nr->t2timer = 0;
+			sk->protinfo.nr->t4timer = 0;
+			sk->protinfo.nr->state   = NR_STATE_2;
+			sk->state                = TCP_CLOSE;
+			sk->shutdown            |= SEND_SHUTDOWN;
 			sk->state_change(sk);
-			sk->dead        = 1;
-			sk->destroy     = 1;
+			sk->dead                 = 1;
+			sk->destroy              = 1;
 			break;
 
 		default:
@@ -717,19 +655,20 @@
 
 static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
-	struct sock *sk;
+	struct sock *sk = (struct sock *)sock->data;
 	struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
 	struct device *dev;
 	ax25_address *user, *source;
-	
-	sk = (struct sock *)sock->data;
 
 	if (sk->zapped == 0)
-		return -EIO;
-		
+		return -EINVAL;
+
 	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
 		return -EINVAL;
 
+	if (addr->fsa_ax25.sax25_family != AF_NETROM)
+		return -EINVAL;
+
 	if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) {
 		if (sk->debug)
 			printk("NET/ROM: bind failed: invalid node callsign\n");
@@ -741,9 +680,9 @@
 	 */
 	if (addr->fsa_ax25.sax25_ndigis == 1) {
 		if (!suser())
-			return -EPERM;
-		sk->nr->user_addr   = addr->fsa_digipeater[0];
-		sk->nr->source_addr = addr->fsa_ax25.sax25_call;
+			return -EACCES;
+		sk->protinfo.nr->user_addr   = addr->fsa_digipeater[0];
+		sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call;
 	} else {
 		source = &addr->fsa_ax25.sax25_call;
 
@@ -753,11 +692,11 @@
 			user = source;
 		}
 
-		sk->nr->user_addr   = *user;
-		sk->nr->source_addr = *source;
+		sk->protinfo.nr->user_addr   = *user;
+		sk->protinfo.nr->source_addr = *source;
 	}
 
-	sk->nr->device = dev;
+	sk->protinfo.nr->device = dev;
 	nr_insert_socket(sk);
 
 	sk->zapped = 0;
@@ -775,24 +714,27 @@
 	struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
 	ax25_address *user, *source = NULL;
 	struct device *dev;
-	
+
 	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
 		return 0;	/* Connect completed during a ERESTARTSYS event */
 	}
-	
+
 	if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
 		sock->state = SS_UNCONNECTED;
 		return -ECONNREFUSED;
 	}
-	
+
 	if (sk->state == TCP_ESTABLISHED)
 		return -EISCONN;	/* No reconnect on a seqpacket socket */
-		
+
 	sk->state   = TCP_CLOSE;	
 	sock->state = SS_UNCONNECTED;
 
-	if (addr_len != sizeof(struct sockaddr_ax25))
+	if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
+		return -EINVAL;
+
+	if (addr->sax25_family != AF_NETROM)
 		return -EINVAL;
 
 	if (sk->zapped) {	/* Must bind first - autobinding in this may or may not work */
@@ -809,30 +751,29 @@
 			user = source;
 		}
 
-		sk->nr->user_addr   = *user;
-		sk->nr->source_addr = *source;
-		sk->nr->device      = dev;
+		sk->protinfo.nr->user_addr   = *user;
+		sk->protinfo.nr->source_addr = *source;
+		sk->protinfo.nr->device      = dev;
 
 		nr_insert_socket(sk);		/* Finish the bind */
 	}
 
-	sk->nr->dest_addr = addr->sax25_call;
+	sk->protinfo.nr->dest_addr = addr->sax25_call;
 
-	while (nr_find_socket((unsigned char)circuit / 256, (unsigned char)circuit % 256) != NULL)
-		circuit++;
+	circuit = nr_find_next_circuit();
 
-	sk->nr->my_index = circuit / 256;
-	sk->nr->my_id    = circuit % 256;
+	sk->protinfo.nr->my_index = circuit / 256;
+	sk->protinfo.nr->my_id    = circuit % 256;
 
 	circuit++;
-	
+
 	/* Move to connecting socket, start sending Connect Requests */
-	sock->state   = SS_CONNECTING;
-	sk->state     = TCP_SYN_SENT;
+	sock->state            = SS_CONNECTING;
+	sk->state              = TCP_SYN_SENT;
 	nr_establish_data_link(sk);
-	sk->nr->state = NR_STATE_1;
+	sk->protinfo.nr->state = NR_STATE_1;
 	nr_set_timer(sk);
-	
+
 	/* Now the loop */
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
 		return -EINPROGRESS;
@@ -855,14 +796,14 @@
 		sock->state = SS_UNCONNECTED;
 		return sock_error(sk);	/* Always set at this point */
 	}
-	
+
 	sock->state = SS_CONNECTED;
 
 	sti();
-	
+
 	return 0;
 }
-	
+
 static int nr_socketpair(struct socket *sock1, struct socket *sock2)
 {
 	return -EOPNOTSUPP;
@@ -874,19 +815,20 @@
 	struct sock *newsk;
 	struct sk_buff *skb;
 
-	if (newsock->data)
-		sk_free(newsock->data);
+	if (newsock->data != NULL)
+		nr_destroy_socket((struct sock *)newsock->data);
 
 	newsock->data = NULL;
-	
-	sk = (struct sock *)sock->data;
+
+	if ((sk = (struct sock *)sock->data) == NULL)
+		return -EINVAL;
 
 	if (sk->type != SOCK_SEQPACKET)
 		return -EOPNOTSUPP;
-	
+
 	if (sk->state != TCP_LISTEN)
 		return -EINVAL;
-		
+
 	/*
 	 *	The write queue this time is holding sockets ready to use
 	 *	hooked into the SABM we saved
@@ -896,7 +838,7 @@
 		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
 			if (flags & O_NONBLOCK) {
 				sti();
-				return 0;
+				return -EWOULDBLOCK;
 			}
 			interruptible_sleep_on(sk->sleep);
 			if (current->signal & ~current->blocked) {
@@ -925,35 +867,34 @@
 	int *uaddr_len, int peer)
 {
 	struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
-	struct sock *sk;
-	
-	sk = (struct sock *)sock->data;
-	
+	struct sock *sk = (struct sock *)sock->data;
+
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		sax->fsa_ax25.sax25_family = AF_NETROM;
 		sax->fsa_ax25.sax25_ndigis = 1;
-		sax->fsa_ax25.sax25_call = sk->nr->user_addr;
-		sax->fsa_digipeater[0]   = sk->nr->dest_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.nr->user_addr;
+		sax->fsa_digipeater[0]     = sk->protinfo.nr->dest_addr;
 		*uaddr_len = sizeof(struct full_sockaddr_ax25);
 	} else {
 		sax->fsa_ax25.sax25_family = AF_NETROM;
 		sax->fsa_ax25.sax25_ndigis = 0;
-		sax->fsa_ax25.sax25_call   = sk->nr->source_addr;
+		sax->fsa_ax25.sax25_call   = sk->protinfo.nr->source_addr;
 		*uaddr_len = sizeof(struct sockaddr_ax25);
 	}
 
 	return 0;
 }
- 
+
 int nr_rx_frame(struct sk_buff *skb, struct device *dev)
 {
 	struct sock *sk;
 	struct sock *make;	
 	ax25_address *src, *dest, *user;
 	unsigned short circuit_index, circuit_id;
-	unsigned short frametype, window, timeout;
+	unsigned short peer_circuit_index, peer_circuit_id;
+	unsigned short frametype, flags, window, timeout;
 
 	skb->sk = NULL;		/* Initially we don't know who it's for */
 
@@ -964,47 +905,74 @@
 	src  = (ax25_address *)(skb->data + 0);
 	dest = (ax25_address *)(skb->data + 7);
 
-	circuit_index = skb->data[15];
-	circuit_id    = skb->data[16];
-	frametype     = skb->data[19];
+	circuit_index      = skb->data[15];
+	circuit_id         = skb->data[16];
+	peer_circuit_index = skb->data[17];
+	peer_circuit_id    = skb->data[18];
+	frametype          = skb->data[19] & 0x0F;
+	flags              = skb->data[19] & 0xF0;
 
 #ifdef CONFIG_INET
 	/*
 	 * Check for an incoming IP over NET/ROM frame.
 	 */
-	 if ((frametype & 0x0F) == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
+	if (frametype == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
 		skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
-	 	skb->h.raw = skb->data;
+		skb->h.raw = skb->data;
 
 		return nr_rx_ip(skb, dev);
-	 }
+	}
 #endif
 
 	/*
 	 * Find an existing socket connection, based on circuit ID, if it's
 	 * a Connect Request base it on their circuit ID.
+	 *
+	 * Circuit ID 0/0 is not valid but it could still be a "reset" for
+	 * a circuit that no longer exists at the other end...
 	 */
-	if (((frametype & 0x0F) != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) ||
-	    ((frametype & 0x0F) == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id)) != NULL)) {
+
+	sk = NULL;
+
+	if (circuit_index == 0 && circuit_id == 0) {
+		if (frametype == NR_CONNACK && flags == NR_CHOKE_FLAG)
+			sk = nr_find_peer(peer_circuit_index, peer_circuit_id);
+	} else {
+		if (frametype == NR_CONNREQ)
+			sk = nr_find_peer(circuit_index, circuit_id);
+		else
+			sk = nr_find_socket(circuit_index, circuit_id);
+	}
+
+	if (sk != NULL) {
 		skb->h.raw = skb->data;
 
-		if ((frametype & 0x0F) == NR_CONNACK && skb->len == 22)
-			sk->nr->bpqext = 1;
+		if (frametype == NR_CONNACK && skb->len == 22)
+			sk->protinfo.nr->bpqext = 1;
 		else
-			sk->nr->bpqext = 0;
+			sk->protinfo.nr->bpqext = 0;
 
 		return nr_process_rx_frame(sk, skb);
 	}
 
-	if ((frametype & 0x0F) != NR_CONNREQ)
+	/*
+	 * Now it should be a CONNREQ.
+	 */
+	if (frametype != NR_CONNREQ) {
+		/*
+		 * Never reply to a CONNACK/CHOKE.
+		 */
+		if (frametype != NR_CONNACK || flags != NR_CHOKE_FLAG)
+			nr_transmit_dm(skb, 1);
 		return 0;
-		
+	}
+
 	sk = nr_find_listener(dest);
 
 	user = (ax25_address *)(skb->data + 21);
 
 	if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = nr_make_new(sk)) == NULL) {
-		nr_transmit_dm(skb);
+		nr_transmit_dm(skb, 0);
 		return 0;
 	}
 
@@ -1014,40 +982,42 @@
 	make->state         = TCP_ESTABLISHED;
 
 	/* Fill in his circuit details */
-	make->nr->source_addr = *dest;
-	make->nr->dest_addr   = *src;
-	make->nr->user_addr   = *user;
+	make->protinfo.nr->source_addr = *dest;
+	make->protinfo.nr->dest_addr   = *src;
+	make->protinfo.nr->user_addr   = *user;
 
-	make->nr->your_index = circuit_index;
-	make->nr->your_id    = circuit_id;
+	make->protinfo.nr->your_index  = circuit_index;
+	make->protinfo.nr->your_id     = circuit_id;
+
+	circuit = nr_find_next_circuit();
+
+	make->protinfo.nr->my_index    = circuit / 256;
+	make->protinfo.nr->my_id       = circuit % 256;
 
-	make->nr->my_index   = circuit / 256;
-	make->nr->my_id      = circuit % 256;
-	
 	circuit++;
 
 	/* Window negotiation */
-	if (window < make->window)
-		make->window = window;
+	if (window < make->protinfo.nr->window)
+		make->protinfo.nr->window = window;
 
 	/* L4 timeout negotiation */
 	if (skb->len == 37) {
 		timeout = skb->data[36] * 256 + skb->data[35];
-		if (timeout * PR_SLOWHZ < make->nr->rtt * 2)
-			make->nr->rtt = (timeout * PR_SLOWHZ) / 2;
-		make->nr->bpqext = 1;
+		if (timeout * NR_SLOWHZ < make->protinfo.nr->t1)
+			make->protinfo.nr->t1 = timeout * NR_SLOWHZ;
+		make->protinfo.nr->bpqext = 1;
 	} else {
-		make->nr->bpqext = 0;
+		make->protinfo.nr->bpqext = 0;
 	}
 
 	nr_write_internal(make, NR_CONNACK);
 
-	make->nr->condition = 0x00;
-	make->nr->vs        = 0;
-	make->nr->va        = 0;
-	make->nr->vr        = 0;
-	make->nr->vl        = 0;
-	make->nr->state     = NR_STATE_3;
+	make->protinfo.nr->condition = 0x00;
+	make->protinfo.nr->vs        = 0;
+	make->protinfo.nr->va        = 0;
+	make->protinfo.nr->vr        = 0;
+	make->protinfo.nr->vl        = 0;
+	make->protinfo.nr->state     = NR_STATE_3;
 	sk->ack_backlog++;
 	make->pair = sk;
 
@@ -1072,7 +1042,7 @@
 	struct sk_buff *skb;
 	unsigned char *asmptr;
 	int size;
-	
+
 	if (sk->err)
 		return sock_error(sk);
 
@@ -1082,14 +1052,19 @@
 	if (sk->zapped)
 		return -EADDRNOTAVAIL;
 
-	if (sk->nr->device == NULL)
+	if (sk->shutdown & SEND_SHUTDOWN) {
+		send_sig(SIGPIPE, current, 0);
+		return -EPIPE;
+	}
+
+	if (sk->protinfo.nr->device == NULL)
 		return -ENETUNREACH;
-		
+
 	if (usax) {
 		if (msg->msg_namelen < sizeof(sax))
 			return -EINVAL;
 		sax = *usax;
-		if (ax25cmp(&sk->nr->dest_addr, &sax.sax25_call) != 0)
+		if (ax25cmp(&sk->protinfo.nr->dest_addr, &sax.sax25_call) != 0)
 			return -EISCONN;
 		if (sax.sax25_family != AF_NETROM)
 			return -EINVAL;
@@ -1097,9 +1072,9 @@
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		sax.sax25_family = AF_NETROM;
-		sax.sax25_call   = sk->nr->dest_addr;
+		sax.sax25_call   = sk->protinfo.nr->dest_addr;
 	}
-	
+
 	if (sk->debug)
 		printk("NET/ROM: sendto: Addresses built.\n");
 
@@ -1114,10 +1089,9 @@
 
 	skb->sk   = sk;
 	skb->free = 1;
-	skb->arp  = 1;
 
 	skb_reserve(skb, size - len);
-	
+
 	/*
 	 *	Push down the NET/ROM header
 	 */
@@ -1129,12 +1103,12 @@
 
 	/* Build a NET/ROM Transport header */
 
-	*asmptr++ = sk->nr->your_index;
-	*asmptr++ = sk->nr->your_id;
+	*asmptr++ = sk->protinfo.nr->your_index;
+	*asmptr++ = sk->protinfo.nr->your_id;
 	*asmptr++ = 0;		/* To be filled in later */
 	*asmptr++ = 0;		/*      Ditto            */
 	*asmptr++ = NR_INFO;
-	
+
 	if (sk->debug)
 		printk("Built header.\n");
 
@@ -1145,7 +1119,7 @@
 	skb->h.raw = skb_put(skb, len);
 
 	asmptr = skb->h.raw;
-	
+
 	if (sk->debug)
 		printk("NET/ROM: Appending user data\n");
 
@@ -1185,6 +1159,7 @@
 	 * This works for seqpacket too. The receiver has ordered the queue for
 	 * us! We do one quick check first though
 	 */
+
 	if (sk->state != TCP_ESTABLISHED)
 		return -ENOTCONN;
 
@@ -1192,21 +1167,14 @@
 	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
 		return er;
 
-	if (!sk->nr->hdrincl) {
-		skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
-		skb->h.raw = skb->data;
-	}
+	skb->h.raw = skb->data;
 
 	copied = (size < skb->len) ? size : skb->len;
 	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-	
-	if (sax != NULL) {
-		struct sockaddr_ax25 addr;
-		
-		addr.sax25_family = AF_NETROM;
-		memcpy(&addr.sax25_call, skb->data + 7, AX25_ADDR_LEN);
 
-		*sax = addr;
+	if (sax != NULL) {
+		sax->sax25_family = AF_NETROM;
+		memcpy(&sax->sax25_call, skb->data + 7, AX25_ADDR_LEN);
 
 		*addr_len = sizeof(*sax);
 	}
@@ -1232,34 +1200,36 @@
 {
 	struct sock *sk = (struct sock *)sock->data;
 	int err;
-	long amount = 0;
 
 	switch (cmd) {
-		case TIOCOUTQ:
-			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+		case TIOCOUTQ: {
+			long amount;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
 				return err;
 			amount = sk->sndbuf - sk->wmem_alloc;
 			if (amount < 0)
 				amount = 0;
-			put_fs_long(amount, (unsigned long *)arg);
+			put_fs_long(amount, (int *)arg);
 			return 0;
+		}
 
 		case TIOCINQ: {
 			struct sk_buff *skb;
+			long amount = 0L;
 			/* These two are safe on a single CPU system as only user tasks fiddle here */
 			if ((skb = skb_peek(&sk->receive_queue)) != NULL)
-				amount = skb->len - 20;
-			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+				amount = skb->len;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
 				return err;
-			put_fs_long(amount, (unsigned long *)arg);
+			put_fs_long(amount, (int *)arg);
 			return 0;
 		}
 
 		case SIOCGSTAMP:
 			if (sk != NULL) {
-				if (sk->stamp.tv_sec==0)
+				if (sk->stamp.tv_sec == 0)
 					return -ENOENT;
-				if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
+				if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct timeval))) != 0)
 					return err;
 				memcpy_tofs((void *)arg, &sk->stamp, sizeof(struct timeval));
 				return 0;
@@ -1281,34 +1251,9 @@
 		case SIOCADDRT:
 		case SIOCDELRT:
 		case SIOCNRDECOBS:
-		case SIOCNRRTCTL:
 			if (!suser()) return -EPERM;
 			return nr_rt_ioctl(cmd, (void *)arg);
 
-		case SIOCNRGETPARMS: {
-			struct nr_parms_struct nr_parms;
-			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct nr_parms_struct))) != 0)
-				return err;
-			memcpy_fromfs(&nr_parms, (void *)arg, sizeof(struct nr_parms_struct));
-			nr_parms = nr_default;
-			memcpy_tofs((void *)arg, &nr_parms, sizeof(struct nr_parms_struct));
-			return 0;
-		}
-
-		case SIOCNRSETPARMS: {
-			struct nr_parms_struct nr_parms;
-			if (!suser()) return -EPERM;
-			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(struct nr_parms_struct))) != 0)
-				return err;
-			memcpy_fromfs(&nr_parms, (void *)arg, sizeof(struct nr_parms_struct));
-			nr_default = nr_parms;
-			return 0;
-		}
-		
- 		case SIOCNRCTLCON:
- 			if (!suser()) return -EPERM;
- 			return nr_ctl_ioctl(cmd, (void *)arg);
- 
  		default:
 			return dev_ioctl(cmd, (void *)arg);
 	}
@@ -1325,43 +1270,49 @@
 	int len = 0;
 	off_t pos = 0;
 	off_t begin = 0;
-  
+
 	cli();
 
-	len += sprintf(buffer, "user_addr dest_node src_node  dev    my  your  st  vs  vr  va    t1     t2    n2  rtt wnd paclen Snd-Q Rcv-Q\n");
+	len += sprintf(buffer, "user_addr dest_node src_node  dev    my  your  st  vs  vr  va    t1     t2     t4      idle   n2  wnd Snd-Q Rcv-Q Inode\n");
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if ((dev = s->nr->device) == NULL)
+		if ((dev = s->protinfo.nr->device) == NULL)
 			devname = "???";
 		else
 			devname = dev->name;
-	
+
 		len += sprintf(buffer + len, "%-9s ",
-			ax2asc(&s->nr->user_addr));
+			ax2asc(&s->protinfo.nr->user_addr));
 		len += sprintf(buffer + len, "%-9s ",
-			ax2asc(&s->nr->dest_addr));
-		len += sprintf(buffer + len, "%-9s %-3s  %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %2d/%02d %3d %3d %6d %5d %5d\n",
-			ax2asc(&s->nr->source_addr),
-			devname, s->nr->my_index, s->nr->my_id,
-			s->nr->your_index, s->nr->your_id,
-			s->nr->state,
-			s->nr->vs, s->nr->vr, s->nr->va,
-			s->nr->t1timer / PR_SLOWHZ,
-			s->nr->t1      / PR_SLOWHZ,
-			s->nr->t2timer / PR_SLOWHZ,
-			s->nr->t2      / PR_SLOWHZ,
-			s->nr->n2count, s->nr->n2,
-			s->nr->rtt     / PR_SLOWHZ,
-			s->window, s->nr->paclen,
-			s->wmem_alloc, s->rmem_alloc);
-		
+			ax2asc(&s->protinfo.nr->dest_addr));
+		len += sprintf(buffer + len, "%-9s %-3s  %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %3d/%03d %3d/%03d %2d/%02d %3d %5d %5d %ld\n",
+			ax2asc(&s->protinfo.nr->source_addr),
+			devname, s->protinfo.nr->my_index, s->protinfo.nr->my_id,
+			s->protinfo.nr->your_index, s->protinfo.nr->your_id,
+			s->protinfo.nr->state,
+			s->protinfo.nr->vs, s->protinfo.nr->vr, s->protinfo.nr->va,
+			s->protinfo.nr->t1timer / NR_SLOWHZ,
+			s->protinfo.nr->t1      / NR_SLOWHZ,
+			s->protinfo.nr->t2timer / NR_SLOWHZ,
+			s->protinfo.nr->t2      / NR_SLOWHZ,
+			s->protinfo.nr->t4timer / NR_SLOWHZ,
+			s->protinfo.nr->t4      / NR_SLOWHZ,
+			0,
+			0,
+			s->protinfo.nr->n2count,
+			s->protinfo.nr->n2,
+			s->protinfo.nr->window,
+			s->wmem_alloc, s->rmem_alloc,
+			s->socket && SOCK_INODE(s->socket) ?
+			SOCK_INODE(s->socket)->i_ino : 0);
+
 		pos = begin + len;
 
 		if (pos < offset) {
 			len   = 0;
 			begin = pos;
 		}
-		
+
 		if (pos > offset + length)
 			break;
 	}
@@ -1378,7 +1329,7 @@
 
 static struct proto_ops nr_proto_ops = {
 	AF_NETROM,
-	
+
 	nr_create,
 	nr_dup,
 	nr_release,
@@ -1403,42 +1354,103 @@
 	0
 };
 
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_nr = {
+	PROC_NET_NR, 2, "nr",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	nr_get_info
+};
+static struct proc_dir_entry proc_net_nr_neigh = {
+	PROC_NET_NR_NEIGH, 8, "nr_neigh",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	nr_neigh_get_info
+};
+static struct proc_dir_entry proc_net_nr_nodes = {
+	PROC_NET_NR_NODES, 8, "nr_nodes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	nr_nodes_get_info
+};
+#endif	
+
+static struct device dev_nr[] = {
+	{"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+	{"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}
+};
+
 void nr_proto_init(struct net_proto *pro)
 {
+	int i;
+
 	sock_register(nr_proto_ops.family, &nr_proto_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
-	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.5 for AX25.032 Linux 2.0\n");
+	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.035 Linux 2.0\n");
 
-	nr_default.quality    = NR_DEFAULT_QUAL;
-	nr_default.obs_count  = NR_DEFAULT_OBS;
-	nr_default.ttl        = NR_DEFAULT_TTL;
-	nr_default.timeout    = NR_DEFAULT_T1;
-	nr_default.ack_delay  = NR_DEFAULT_T2;
-	nr_default.busy_delay = NR_DEFAULT_T4;
-	nr_default.tries      = NR_DEFAULT_N2;
-	nr_default.window     = NR_DEFAULT_WINDOW;
-	nr_default.paclen     = NR_DEFAULT_PACLEN;
+	if (!ax25_protocol_register(AX25_P_NETROM, nr_route_frame))
+		printk(KERN_ERR "NET/ROM unable to register protocol with AX.25\n");
+	if (!ax25_linkfail_register(nr_link_failed))
+		printk(KERN_ERR "NET/ROM unable to register linkfail handler with AX.25\n");
+
+	for (i = 0; i < 4; i++)
+		register_netdev(&dev_nr[i]);
+
+	nr_register_sysctl();
+
+	nr_loopback_init();
 
 #ifdef CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_NR, 2, "nr",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		nr_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_NR_NEIGH, 8, "nr_neigh",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		nr_neigh_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_NR_NODES, 8, "nr_nodes",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		nr_nodes_get_info
-	});
+	proc_net_register(&proc_net_nr);
+	proc_net_register(&proc_net_nr_neigh);
+	proc_net_register(&proc_net_nr_nodes);
 #endif	
 }
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	nr_proto_init(NULL);
+
+	register_symtab(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_NR);
+	proc_net_unregister(PROC_NET_NR_NEIGH);
+	proc_net_unregister(PROC_NET_NR_NODES);
+#endif
+	nr_loopback_clear();
+
+	nr_rt_free();
+
+	ax25_protocol_release(AX25_P_NETROM);
+	ax25_linkfail_release(nr_link_failed);
+
+	unregister_netdevice_notifier(&nr_dev_notifier);
+
+	nr_unregister_sysctl();
+
+	sock_unregister(AF_NETROM);
+
+	for (i = 0; i < 4; i++) {
+		if (dev_nr[i].priv != NULL) {
+			kfree(dev_nr[i].priv);
+			dev_nr[i].priv = NULL;
+			unregister_netdev(&dev_nr[i]);
+		}
+	}
+}
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v2.0.34/linux/net/netrom/nr_dev.c	Mon May 13 02:15:24 1996
+++ linux/net/netrom/nr_dev.c	Mon Jul 13 13:47:41 1998
@@ -1,8 +1,5 @@
 /*
- *	NET/ROM release 003
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	NET/ROM release 006
  *
  *	This code REQUIRES 1.3.0 or higher/ NET3.029
  *
@@ -17,10 +14,12 @@
  *	NET/ROM 002	Steve Whitehouse(GW7RRM) fixed the set_mac_address
  *	NET/ROM 003	Jonathan(G4KLX)	Put nr_rebuild_header into line with
  *					ax25_rebuild_header
+ *	NET/ROM 004	Jonathan(G4KLX)	Callsign registration with AX.25.
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <linux/proc_fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
@@ -80,19 +79,19 @@
 	unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 
 	memcpy(buff, (saddr != NULL) ? saddr : dev->dev_addr, dev->addr_len);
-	buff[6] &= ~LAPB_C;
-	buff[6] &= ~LAPB_E;
-	buff[6] |= SSSID_SPARE;
+	buff[6] &= ~AX25_CBIT;
+	buff[6] &= ~AX25_EBIT;
+	buff[6] |= AX25_SSSID_SPARE;
 	buff    += AX25_ADDR_LEN;
 
 	if (daddr != NULL)
 		memcpy(buff, daddr, dev->addr_len);
-	buff[6] &= ~LAPB_C;
-	buff[6] |= LAPB_E;
-	buff[6] |= SSSID_SPARE;
+	buff[6] &= ~AX25_CBIT;
+	buff[6] |= AX25_EBIT;
+	buff[6] |= AX25_SSSID_SPARE;
 	buff    += AX25_ADDR_LEN;
 
-	*buff++ = nr_default.ttl;
+	*buff++ = sysctl_netrom_network_ttl_initialiser;
 
 	*buff++ = NR_PROTO_IP;
 	*buff++ = NR_PROTO_IP;
@@ -102,8 +101,8 @@
 
 	if (daddr != NULL)
 		return 37;
-	
-	return -37;	
+
+	return -37;
 }
 
 static int nr_rebuild_header(void *buff, struct device *dev,
@@ -118,14 +117,14 @@
 		return 1;
 	}
 
-	bp[6] &= ~LAPB_C;
-	bp[6] &= ~LAPB_E;
-	bp[6] |= SSSID_SPARE;
+	bp[6] &= ~AX25_CBIT;
+	bp[6] &= ~AX25_EBIT;
+	bp[6] |= AX25_SSSID_SPARE;
 	bp    += AX25_ADDR_LEN;
-	
-	bp[6] &= ~LAPB_C;
-	bp[6] |= LAPB_E;
-	bp[6] |= SSSID_SPARE;
+
+	bp[6] &= ~AX25_CBIT;
+	bp[6] |= AX25_EBIT;
+	bp[6] |= AX25_SSSID_SPARE;
 
 	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
 		dev_kfree_skb(skb, FREE_WRITE);
@@ -151,9 +150,14 @@
 
 static int nr_set_mac_address(struct device *dev, void *addr)
 {
-	struct sockaddr *sa=addr;
+	struct sockaddr *sa = addr;
+
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
 	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
 
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+
 	return 0;
 }
 
@@ -162,6 +166,8 @@
 	dev->tbusy = 0;
 	dev->start = 1;
 
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+
 	return 0;
 }
 
@@ -170,6 +176,8 @@
 	dev->tbusy = 1;
 	dev->start = 0;
 
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
 	return 0;
 }
 
@@ -217,7 +225,7 @@
 {
 	int i;
 
-	dev->mtu		= 236;		/* MTU			*/
+	dev->mtu		= NR_MAX_PACKET_SIZE;
 	dev->tbusy		= 0;
 	dev->hard_start_xmit	= nr_xmit;
 	dev->open		= nr_open;
@@ -234,13 +242,14 @@
 	dev->flags		= 0;
 	dev->family		= AF_INET;
 
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr		= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= sizeof(unsigned long);
+#ifdef CONFIG_INET
+	dev->pa_addr		= in_aton("192.168.0.1");
+	dev->pa_brdaddr		= in_aton("192.168.0.255");
+	dev->pa_mask		= in_aton("255.255.255.0");
+	dev->pa_alen		= 4;
+#endif
 
-	dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
-	if (dev->priv == NULL)
+	if ((dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL)) == NULL)
 		return -ENOMEM;
 
 	memset(dev->priv, 0, sizeof(struct enet_statistics));
diff -u --recursive --new-file v2.0.34/linux/net/netrom/nr_in.c linux/net/netrom/nr_in.c
--- v2.0.34/linux/net/netrom/nr_in.c	Mon May 13 02:15:24 1996
+++ linux/net/netrom/nr_in.c	Mon Jul 13 13:47:41 1998
@@ -1,8 +1,5 @@
 /*
- *	NET/ROM release 003
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	NET/ROM release 006
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -26,7 +23,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -54,36 +51,30 @@
 {
 	struct sk_buff *skbo, *skbn = skb;
 
+	skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
+
 	if (more) {
-		sk->nr->fraglen += skb->len;
-		skb_queue_tail(&sk->nr->frag_queue, skb);
+		sk->protinfo.nr->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
 		return 0;
 	}
-	
-	if (!more && sk->nr->fraglen > 0) {	/* End of fragment */
-		sk->nr->fraglen += skb->len;
-		skb_queue_tail(&sk->nr->frag_queue, skb);
 
-		if ((skbn = alloc_skb(sk->nr->fraglen, GFP_ATOMIC)) == NULL)
+	if (!more && sk->protinfo.nr->fraglen > 0) {	/* End of fragment */
+		sk->protinfo.nr->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
+
+		if ((skbn = alloc_skb(sk->protinfo.nr->fraglen, GFP_ATOMIC)) == NULL)
 			return 1;
 
-		skbn->free = 1;
-		skbn->arp  = 1;
-		skbn->sk   = sk;
-		sk->rmem_alloc += skbn->truesize;
+		skbn->free  = 1;
 		skbn->h.raw = skbn->data;
 
-		skbo = skb_dequeue(&sk->nr->frag_queue);
-		memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
-		kfree_skb(skbo, FREE_READ);
-
-		while ((skbo = skb_dequeue(&sk->nr->frag_queue)) != NULL) {
-			skb_pull(skbo, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
+		while ((skbo = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL) {
 			memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
 			kfree_skb(skbo, FREE_READ);
 		}
 
-		sk->nr->fraglen = 0;		
+		sk->protinfo.nr->fraglen = 0;		
 	}
 
 	return sock_queue_rcv_skb(sk, skbn);
@@ -99,20 +90,19 @@
 	switch (frametype) {
 
 		case NR_CONNACK:
-			nr_calculate_rtt(sk);
-			sk->window         = skb->data[20];
-			sk->nr->your_index = skb->data[17];
-			sk->nr->your_id    = skb->data[18];
-			sk->nr->t1timer    = 0;
-			sk->nr->t2timer    = 0;
-			sk->nr->t4timer    = 0;
-			sk->nr->vs         = 0;
-			sk->nr->va         = 0;
-			sk->nr->vr         = 0;
-			sk->nr->vl	   = 0;
-			sk->nr->state      = NR_STATE_3;
-			sk->state          = TCP_ESTABLISHED;
-			sk->nr->n2count    = 0;
+			sk->protinfo.nr->your_index = skb->data[17];
+			sk->protinfo.nr->your_id    = skb->data[18];
+			sk->protinfo.nr->t1timer    = 0;
+			sk->protinfo.nr->t2timer    = 0;
+			sk->protinfo.nr->t4timer    = 0;
+			sk->protinfo.nr->vs         = 0;
+			sk->protinfo.nr->va         = 0;
+			sk->protinfo.nr->vr         = 0;
+			sk->protinfo.nr->vl	    = 0;
+			sk->protinfo.nr->state      = NR_STATE_3;
+			sk->protinfo.nr->n2count    = 0;
+			sk->protinfo.nr->window     = skb->data[20];
+			sk->state                   = TCP_ESTABLISHED;
 			/* For WAIT_SABM connections we will produce an accept ready socket here */
 			if (!sk->dead)
 				sk->state_change(sk);
@@ -120,12 +110,13 @@
 
 		case NR_CONNACK | NR_CHOKE_FLAG:
 			nr_clear_queues(sk);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = ECONNREFUSED;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = ECONNREFUSED;
+			sk->shutdown          |= SEND_SHUTDOWN;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
 		default:
@@ -144,16 +135,27 @@
 {
 	switch (frametype) {
 
+		case NR_CONNACK | NR_CHOKE_FLAG:
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = ECONNRESET;
+			sk->shutdown          |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead               = 1;
+			break;
+
 		case NR_DISCREQ:
 			nr_write_internal(sk, NR_DISCACK);
 
 		case NR_DISCACK:
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = 0;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = 0;
+			sk->shutdown          |= SEND_SHUTDOWN;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
 		default:
@@ -188,22 +190,25 @@
 		case NR_DISCREQ:
 			nr_clear_queues(sk);
 			nr_write_internal(sk, NR_DISCACK);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = 0;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = 0;
+			sk->shutdown          |= SEND_SHUTDOWN;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
+		case NR_CONNACK | NR_CHOKE_FLAG:
 		case NR_DISCACK:
 			nr_clear_queues(sk);
-			sk->nr->state = NR_STATE_0;
-			sk->state     = TCP_CLOSE;
-			sk->err       = ECONNRESET;
+			sk->protinfo.nr->state = NR_STATE_0;
+			sk->state              = TCP_CLOSE;
+			sk->err                = ECONNRESET;
+			sk->shutdown          |= SEND_SHUTDOWN;
 			if (!sk->dead)
 				sk->state_change(sk);
-			sk->dead      = 1;
+			sk->dead               = 1;
 			break;
 
 		case NR_INFOACK:
@@ -211,11 +216,11 @@
 		case NR_INFOACK | NR_NAK_FLAG:
 		case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG:
 			if (frametype & NR_CHOKE_FLAG) {
-				sk->nr->condition |= PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = nr_default.busy_delay;
+				sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
+				sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
 			} else {
-				sk->nr->condition &= ~PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = 0;
+				sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
+				sk->protinfo.nr->t4timer = 0;
 			}
 			if (!nr_validate_nr(sk, nr)) {
 				break;
@@ -224,14 +229,14 @@
 				nr_frames_acked(sk, nr);
 				nr_send_nak_frame(sk);
 			} else {
-				if (sk->nr->condition & PEER_RX_BUSY_CONDITION) {
+				if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) {
 					nr_frames_acked(sk, nr);
 				} else {
 					nr_check_iframes_acked(sk, nr);
 				}
 			}
 			break;
-			
+
 		case NR_INFO:
 		case NR_INFO | NR_NAK_FLAG:
 		case NR_INFO | NR_CHOKE_FLAG:
@@ -241,18 +246,18 @@
 		case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG:
 		case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG:
 			if (frametype & NR_CHOKE_FLAG) {
-				sk->nr->condition |= PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = nr_default.busy_delay;
+				sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
+				sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
 			} else {
-				sk->nr->condition &= ~PEER_RX_BUSY_CONDITION;
-				sk->nr->t4timer = 0;
+				sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
+				sk->protinfo.nr->t4timer = 0;
 			}
 			if (nr_validate_nr(sk, nr)) {
 				if (frametype & NR_NAK_FLAG) {
 					nr_frames_acked(sk, nr);
 					nr_send_nak_frame(sk);
 				} else {
-					if (sk->nr->condition & PEER_RX_BUSY_CONDITION) {
+					if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) {
 						nr_frames_acked(sk, nr);
 					} else {
 						nr_check_iframes_acked(sk, nr);
@@ -260,19 +265,19 @@
 				}
 			}
 			queued = 1;
-			skb_queue_head(&sk->nr->reseq_queue, skb);
-			if (sk->nr->condition & OWN_RX_BUSY_CONDITION)
+			skb_queue_head(&sk->protinfo.nr->reseq_queue, skb);
+			if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
 				break;
 			skb_queue_head_init(&temp_queue);
 			do {
-				save_vr = sk->nr->vr;
-				while ((skbn = skb_dequeue(&sk->nr->reseq_queue)) != NULL) {
+				save_vr = sk->protinfo.nr->vr;
+				while ((skbn = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL) {
 					ns = skbn->data[17];
-					if (ns == sk->nr->vr) {
+					if (ns == sk->protinfo.nr->vr) {
 						if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) {
-							sk->nr->vr = (sk->nr->vr + 1) % NR_MODULUS;
+							sk->protinfo.nr->vr = (sk->protinfo.nr->vr + 1) % NR_MODULUS;
 						} else {
-							sk->nr->condition |= OWN_RX_BUSY_CONDITION;
+							sk->protinfo.nr->condition |= NR_COND_OWN_RX_BUSY;
 							skb_queue_tail(&temp_queue, skbn);
 						}
 					} else if (nr_in_rx_window(sk, ns)) {
@@ -283,18 +288,18 @@
 					}
 				}
 				while ((skbn = skb_dequeue(&temp_queue)) != NULL) {
-					skb_queue_tail(&sk->nr->reseq_queue, skbn);
+					skb_queue_tail(&sk->protinfo.nr->reseq_queue, skbn);
 				}
-			} while (save_vr != sk->nr->vr);
+			} while (save_vr != sk->protinfo.nr->vr);
 			/*
 			 * Window is full, ack it immediately.
 			 */
-			if (((sk->nr->vl + sk->window) % NR_MODULUS) == sk->nr->vr) {
+			if (((sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS) == sk->protinfo.nr->vr) {
 				nr_enquiry_response(sk);
 			} else {
-				if (!(sk->nr->condition & ACK_PENDING_CONDITION)) {
-					sk->nr->t2timer = sk->nr->t2;
-					sk->nr->condition |= ACK_PENDING_CONDITION;
+				if (!(sk->protinfo.nr->condition & NR_COND_ACK_PENDING)) {
+					sk->protinfo.nr->t2timer = sk->protinfo.nr->t2;
+					sk->protinfo.nr->condition |= NR_COND_ACK_PENDING;
 				}
 			}
 			break;
@@ -310,22 +315,15 @@
 int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb)
 {
 	int queued = 0, frametype;
-	
-	if (sk->nr->state == NR_STATE_0 && sk->dead)
-		return queued;
-
-	if (sk->nr->state != NR_STATE_1 && sk->nr->state != NR_STATE_2 &&
-	    sk->nr->state != NR_STATE_3) {
-		printk(KERN_ERR "nr_process_rx_frame: frame received - state: %d\n", sk->nr->state);
-		return queued;
-	}
+
+	if (sk->protinfo.nr->state == NR_STATE_0)
+		return 0;
 
 	del_timer(&sk->timer);
 
 	frametype = skb->data[19];
 
-	switch (sk->nr->state)
-	{
+	switch (sk->protinfo.nr->state) {
 		case NR_STATE_1:
 			queued = nr_state1_machine(sk, skb, frametype);
 			break;
diff -u --recursive --new-file v2.0.34/linux/net/netrom/nr_loopback.c linux/net/netrom/nr_loopback.c
--- v2.0.34/linux/net/netrom/nr_loopback.c	Wed Dec 31 16:00:00 1969
+++ linux/net/netrom/nr_loopback.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,101 @@
+/*
+ *	NET/ROM release 006
+ *
+ *	This code REQUIRES 1.2.1 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	NET/ROM 006	Tomi(OH2BNS)	Created this file.
+ *
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <net/ax25.h>
+#include <linux/skbuff.h>
+#include <net/netrom.h>
+
+static struct sk_buff_head loopback_queue;
+static struct timer_list loopback_timer;
+
+static void nr_set_loopback_timer(void);
+
+void nr_loopback_init(void)
+{
+	skb_queue_head_init(&loopback_queue);
+
+	init_timer(&loopback_timer);
+}
+
+void nr_loopback_clear(void)
+{
+	struct sk_buff *skb;
+
+	del_timer(&loopback_timer);
+
+	while ((skb = skb_dequeue(&loopback_queue)) != NULL)
+		kfree_skb(skb, FREE_READ);
+}
+
+static int nr_loopback_running(void)
+{
+	return (loopback_timer.prev != NULL || loopback_timer.next != NULL);
+}
+
+int nr_loopback_queue(struct sk_buff *skb)
+{
+	struct sk_buff *skbn;
+
+	skbn = skb_copy(skb, GFP_ATOMIC);
+
+	kfree_skb(skb, FREE_WRITE);
+
+	if (skbn != NULL)
+		skb_queue_tail(&loopback_queue, skbn);
+
+	if (!nr_loopback_running())
+		nr_set_loopback_timer();
+
+	return 1;
+}
+
+static void nr_loopback_timer(unsigned long);
+
+static void nr_set_loopback_timer(void)
+{
+	del_timer(&loopback_timer);
+
+	loopback_timer.data     = 0;
+	loopback_timer.function = &nr_loopback_timer;
+	loopback_timer.expires  = jiffies + 10;
+
+	add_timer(&loopback_timer);
+}
+
+static void nr_loopback_timer(unsigned long param)
+{
+	struct sk_buff *skb;
+	ax25_address *nr_dest;
+	struct device *dev;
+
+	while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
+		nr_dest = (ax25_address *)(skb->data + 7);
+
+		if ((dev = nr_dev_get(nr_dest)) == NULL) {
+			kfree_skb(skb, FREE_READ);
+			continue;
+		}
+
+		if (nr_rx_frame(skb, dev) == 0)
+			kfree_skb(skb, FREE_READ);
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/netrom/nr_out.c linux/net/netrom/nr_out.c
--- v2.0.34/linux/net/netrom/nr_out.c	Sat Aug 10 00:03:16 1996
+++ linux/net/netrom/nr_out.c	Mon Jul 13 13:47:41 1998
@@ -1,8 +1,5 @@
 /*
- *	NET/ROM release 003
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	NET/ROM release 006
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -19,7 +16,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -50,11 +47,9 @@
 {
 	struct sk_buff *skbn;
 	unsigned char transport[NR_TRANSPORT_LEN];
-	int err, frontlen, len, mtu;
+	int err, frontlen, len;
 
-	mtu = sk->nr->paclen;
-	
-	if (skb->len - NR_TRANSPORT_LEN > mtu) {
+	if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) {
 		/* Save a copy of the Transport Header */
 		memcpy(transport, skb->data, NR_TRANSPORT_LEN);
 		skb_pull(skb, NR_TRANSPORT_LEN);
@@ -62,16 +57,15 @@
 		frontlen = skb_headroom(skb);
 
 		while (skb->len > 0) {
-			if ((skbn = sock_alloc_send_skb(sk, frontlen + mtu, 0, 0, &err)) == NULL)
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, 0, &err)) == NULL)
 				return;
 
 			skbn->sk   = sk;
 			skbn->free = 1;
-			skbn->arp  = 1;
 
 			skb_reserve(skbn, frontlen);
 
-			len = (mtu > skb->len) ? skb->len : mtu;
+			len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE;
 
 			/* Copy the user data */
 			memcpy(skb_put(skbn, len), skb->data, len);
@@ -83,17 +77,16 @@
 
 			if (skb->len > 0)
 				skbn->data[4] |= NR_MORE_FLAG;
-		
+
 			skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
 		}
-		
-		skb->free = 1;
+
 		kfree_skb(skb, FREE_WRITE);
 	} else {
 		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
 	}
 
-	if (sk->nr->state == NR_STATE_3)
+	if (sk->protinfo.nr->state == NR_STATE_3)
 		nr_kick(sk);
 }
 
@@ -106,10 +99,10 @@
 	if (skb == NULL)
 		return;
 
-	skb->data[2] = sk->nr->vs;
-	skb->data[3] = sk->nr->vr;
+	skb->data[2] = sk->protinfo.nr->vs;
+	skb->data[3] = sk->protinfo.nr->vr;
 
-	if (sk->nr->condition & OWN_RX_BUSY_CONDITION)
+	if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
 		skb->data[4] |= NR_CHOKE_FLAG;
 
 	nr_transmit_buffer(sk, skb);	
@@ -118,42 +111,41 @@
 void nr_send_nak_frame(struct sock *sk)
 {
 	struct sk_buff *skb, *skbn;
-	
-	if ((skb = skb_peek(&sk->nr->ack_queue)) == NULL)
+
+	if ((skb = skb_peek(&sk->protinfo.nr->ack_queue)) == NULL)
 		return;
-		
+
 	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL)
 		return;
 
-	skbn->data[2] = sk->nr->va;
-	skbn->data[3] = sk->nr->vr;
+	skbn->data[2] = sk->protinfo.nr->va;
+	skbn->data[3] = sk->protinfo.nr->vr;
 
-	if (sk->nr->condition & OWN_RX_BUSY_CONDITION)
+	if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
 		skbn->data[4] |= NR_CHOKE_FLAG;
 
 	nr_transmit_buffer(sk, skbn);
 
-	sk->nr->condition &= ~ACK_PENDING_CONDITION;
-	sk->nr->vl      = sk->nr->vr;
-	sk->nr->t1timer = 0;
+	sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
+	sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
+	sk->protinfo.nr->t1timer    = 0;
 }
 
 void nr_kick(struct sock *sk)
 {
 	struct sk_buff *skb, *skbn;
-	int last = 1;
-	unsigned short start, end, next;
+	unsigned short start, end;
 
 	del_timer(&sk->timer);
 
-	start = (skb_peek(&sk->nr->ack_queue) == NULL) ? sk->nr->va : sk->nr->vs;
-	end   = (sk->nr->va + sk->window) % NR_MODULUS;
+	start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs;
+	end   = (sk->protinfo.nr->va + sk->protinfo.nr->window) % NR_MODULUS;
 
-	if (!(sk->nr->condition & PEER_RX_BUSY_CONDITION) &&
-	    start != end                                  &&
+	if (!(sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) &&
+	    start != end                                         &&
 	    skb_peek(&sk->write_queue) != NULL) {
 
-		sk->nr->vs = start;
+		sk->protinfo.nr->vs = start;
 
 		/*
 		 * Transmit data until either we're out of data to send or
@@ -171,29 +163,28 @@
 				break;
 			}
 
-			next = (sk->nr->vs + 1) % NR_MODULUS;
-			last = (next == end);
+			skbn->sk = sk;
+			atomic_add(skbn->truesize, &sk->wmem_alloc);
 
 			/*
 			 * Transmit the frame copy.
 			 */
 			nr_send_iframe(sk, skbn);
 
-			sk->nr->vs = next;
+			sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS;
 
 			/*
 			 * Requeue the original data frame.
 			 */
-			skb_queue_tail(&sk->nr->ack_queue, skb);
+			skb_queue_tail(&sk->protinfo.nr->ack_queue, skb);
 
-		} while (!last && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+		} while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
 
-		sk->nr->vl = sk->nr->vr;
-		sk->nr->condition &= ~ACK_PENDING_CONDITION;
+		sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
+		sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
 
-		if (sk->nr->t1timer == 0) {
-			sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
-		}
+		if (sk->protinfo.nr->t1timer == 0)
+			sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
 	}
 
 	nr_set_timer(sk);
@@ -208,31 +199,22 @@
 	 */
 	dptr = skb_push(skb, NR_NETWORK_LEN);
 
-	memcpy(dptr, &sk->nr->source_addr, AX25_ADDR_LEN);
-	dptr[6] &= ~LAPB_C;
-	dptr[6] &= ~LAPB_E;
-	dptr[6] |= SSSID_SPARE;
+	memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
+	dptr[6] &= ~AX25_CBIT;
+	dptr[6] &= ~AX25_EBIT;
+	dptr[6] |= AX25_SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 
-	memcpy(dptr, &sk->nr->dest_addr, AX25_ADDR_LEN);
-	dptr[6] &= ~LAPB_C;
-	dptr[6] |= LAPB_E;
-	dptr[6] |= SSSID_SPARE;
+	memcpy(dptr, &sk->protinfo.nr->dest_addr, AX25_ADDR_LEN);
+	dptr[6] &= ~AX25_CBIT;
+	dptr[6] |= AX25_EBIT;
+	dptr[6] |= AX25_SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 
-	*dptr++ = nr_default.ttl;
+	*dptr++ = sysctl_netrom_network_ttl_initialiser;
 
-	skb->arp = 1;
-
-	if (!nr_route_frame(skb, NULL)) {
+	if (!nr_route_frame(skb, NULL))
 		kfree_skb(skb, FREE_WRITE);
-
-		sk->state = TCP_CLOSE;
-		sk->err   = ENETUNREACH;
-		if (!sk->dead)
-			sk->state_change(sk);
-		sk->dead  = 1;
-	}
 }
 
 /*
@@ -242,13 +224,13 @@
 
 void nr_establish_data_link(struct sock *sk)
 {
-	sk->nr->condition = 0x00;
-	sk->nr->n2count   = 0;
+	sk->protinfo.nr->condition = 0x00;
+	sk->protinfo.nr->n2count   = 0;
 
 	nr_write_internal(sk, NR_CONNREQ);
 
-	sk->nr->t2timer = 0;
-	sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
+	sk->protinfo.nr->t2timer = 0;
+	sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
 }
 
 /*
@@ -257,32 +239,30 @@
 void nr_enquiry_response(struct sock *sk)
 {
 	int frametype = NR_INFOACK;
-	
-	if (sk->nr->condition & OWN_RX_BUSY_CONDITION) {
+
+	if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) {
 		frametype |= NR_CHOKE_FLAG;
 	} else {
-		if (skb_peek(&sk->nr->reseq_queue) != NULL) {
+		if (skb_peek(&sk->protinfo.nr->reseq_queue) != NULL)
 			frametype |= NR_NAK_FLAG;
-		}
 	}
-	
+
 	nr_write_internal(sk, frametype);
 
-	sk->nr->vl = sk->nr->vr;
-	sk->nr->condition &= ~ACK_PENDING_CONDITION;
+	sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
+	sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
 }
 
 void nr_check_iframes_acked(struct sock *sk, unsigned short nr)
 {
-	if (sk->nr->vs == nr) {
+	if (sk->protinfo.nr->vs == nr) {
 		nr_frames_acked(sk, nr);
-		nr_calculate_rtt(sk);
-		sk->nr->t1timer = 0;
-		sk->nr->n2count = 0;
+		sk->protinfo.nr->t1timer = 0;
+		sk->protinfo.nr->n2count = 0;
 	} else {
-		if (sk->nr->va != nr) {
+		if (sk->protinfo.nr->va != nr) {
 			nr_frames_acked(sk, nr);
-			sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
+			sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
 		}
 	}
 }
diff -u --recursive --new-file v2.0.34/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v2.0.34/linux/net/netrom/nr_route.c	Sat Aug 10 00:03:16 1996
+++ linux/net/netrom/nr_route.c	Mon Jul 13 13:47:41 1998
@@ -1,8 +1,5 @@
 /*
- *	NET/ROM release 003
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	NET/ROM release 006
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -20,10 +17,11 @@
  *					Change default quality for new neighbour when same
  *					as node callsign.
  *			Alan Cox(GW4PTS) Added the firewall hooks.
+ *			Tomi(OH2BNS)	Routing quality and link failure changes.
  */
- 
+
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -52,7 +50,6 @@
 #include <net/netrom.h>
 
 static unsigned int nr_neigh_no = 1;
-static int nr_route_on = 1;
 
 static struct nr_node  *nr_node_list  = NULL;
 static struct nr_neigh *nr_neigh_list = NULL;
@@ -83,6 +80,23 @@
 		if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
 			break;
 
+	/*
+	 * If the L2 link to a neighbour has failed in the past
+	 * and now a frame comes from this neighbour then reset the routes.
+	 */
+	if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) {
+		struct nr_node *node;
+
+		for (node = nr_node_list; node != NULL; node = node->next)
+			for (i = 0; i < node->count; i++)
+				if (node->routes[i].neighbour == nr_neigh)
+					if (i < node->which)
+						node->which = i;
+	}
+
+	if (nr_neigh != NULL)
+		nr_neigh->failed = 0;
+
 	if (quality == 0 && nr_neigh != NULL && nr_node != NULL)
 		return 0;
 
@@ -92,23 +106,22 @@
 
 		nr_neigh->callsign = *ax25;
 		nr_neigh->digipeat = NULL;
+		nr_neigh->ax25     = NULL;
 		nr_neigh->dev      = dev;
-		if (ax25cmp(nr, ax25) == 0)
-			nr_neigh->quality = quality;
-		else
-			nr_neigh->quality = nr_default.quality;
+		nr_neigh->quality  = sysctl_netrom_default_path_quality;
 		nr_neigh->locked   = 0;
 		nr_neigh->count    = 0;
 		nr_neigh->number   = nr_neigh_no++;
+		nr_neigh->failed   = 0;
 
-		if (ax25_digi != NULL) {
+		if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
 			if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
 				kfree_s(nr_neigh, sizeof(*nr_neigh));
 				return -ENOMEM;
 			}
 			*nr_neigh->digipeat = *ax25_digi;
 		}
-			
+
 		save_flags(flags);
 		cli();
 
@@ -118,6 +131,9 @@
 		restore_flags(flags);
 	}
 
+	if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
+		nr_neigh->quality = quality;
+
 	if (nr_node == NULL) {
 		if ((nr_node = (struct nr_node *)kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL)
 			return -ENOMEM;
@@ -131,7 +147,7 @@
 		nr_node->routes[0].quality   = quality;
 		nr_node->routes[0].obs_count = obs_count;
 		nr_node->routes[0].neighbour = nr_neigh;
-			
+
 		save_flags(flags);
 		cli();
 
@@ -139,15 +155,15 @@
 		nr_node_list  = nr_node;
 
 		restore_flags(flags);
-		
+
 		nr_neigh->count++;
 
 		return 0;
-	} else {
-		if (nr_node->mnemonic[0] == '\0')
-			strcpy(nr_node->mnemonic, mnemonic);
 	}
 
+	if (quality != 0)
+		strcpy(nr_node->mnemonic, mnemonic);
+
 	for (found = 0, i = 0; i < nr_node->count; i++) {
 		if (nr_node->routes[i].neighbour == nr_neigh) {
 			nr_node->routes[i].quality   = quality;
@@ -166,7 +182,7 @@
 			nr_node->routes[0].quality   = quality;
 			nr_node->routes[0].obs_count = obs_count;
 			nr_node->routes[0].neighbour = nr_neigh;
-				
+
 			nr_node->which++;
 			nr_node->count++;
 			nr_neigh->count++;
@@ -174,10 +190,10 @@
 			/* It must be better than the worst */
 			if (quality > nr_node->routes[2].quality) {
 				nr_node->routes[2].neighbour->count--;
-				
+
 				if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
 					nr_remove_neigh(nr_node->routes[2].neighbour);
-			
+
 				nr_node->routes[2].quality   = quality;
 				nr_node->routes[2].obs_count = obs_count;
 				nr_node->routes[2].neighbour = nr_neigh;
@@ -240,7 +256,7 @@
 {
 	struct nr_node *s;
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
 
@@ -307,7 +323,7 @@
 	struct nr_node  *nr_node;
 	struct nr_neigh *nr_neigh;
 	int i;
-	
+
 	for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
 		if (ax25cmp(callsign, &nr_node->callsign) == 0)
 			break;
@@ -319,16 +335,16 @@
 			break;
 
 	if (nr_neigh == NULL) return -EINVAL;
-	
+
 	for (i = 0; i < nr_node->count; i++) {
 		if (nr_node->routes[i].neighbour == nr_neigh) {
 			nr_neigh->count--;
 
 			if (nr_neigh->count == 0 && !nr_neigh->locked)
 				nr_remove_neigh(nr_neigh);
-				
+
 			nr_node->count--;
-			
+
 			if (nr_node->count == 0) {
 				nr_remove_node(nr_node);
 			} else {
@@ -352,7 +368,7 @@
 /*
  *	Lock a neighbour with a quality.
  */
-static int nr_add_neigh(ax25_address *callsign, struct device *dev, unsigned int quality)
+static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct device *dev, unsigned int quality)
 {
 	struct nr_neigh *nr_neigh;
 	unsigned long flags;
@@ -370,15 +386,25 @@
 
 	nr_neigh->callsign = *callsign;
 	nr_neigh->digipeat = NULL;
+	nr_neigh->ax25     = NULL;
 	nr_neigh->dev      = dev;
 	nr_neigh->quality  = quality;
 	nr_neigh->locked   = 1;
 	nr_neigh->count    = 0;
 	nr_neigh->number   = nr_neigh_no++;
+	nr_neigh->failed   = 0;
+
+	if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
+		if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
+			kfree_s(nr_neigh, sizeof(*nr_neigh));
+			return -ENOMEM;
+		}
+		*nr_neigh->digipeat = *ax25_digi;
+	}
 
 	save_flags(flags);
 	cli();
-			
+
 	nr_neigh->next = nr_neigh_list;
 	nr_neigh_list  = nr_neigh;
 
@@ -435,9 +461,9 @@
 
 			case 1:		/* From 1 -> 0 */
 				nr_neigh = s->routes[i].neighbour;
-				
+
 				nr_neigh->count--;
-						
+
 				if (nr_neigh->count == 0 && !nr_neigh->locked)
 					nr_remove_neigh(nr_neigh);
 
@@ -479,14 +505,14 @@
 	while (nr_neigh != NULL) {
 		s        = nr_neigh;
 		nr_neigh = nr_neigh->next;
-		
+
 		if (s->dev == dev) {
 			nr_node = nr_node_list;
 
 			while (nr_node != NULL) {
 				t       = nr_node;
 				nr_node = nr_node->next;
-				
+
 				for (i = 0; i < t->count; i++) {
 					if (t->routes[i].neighbour == s) {
 						t->count--;
@@ -501,11 +527,11 @@
 						}
 					}
 				}
-				
+
 				if (t->count <= 0)
 					nr_remove_node(t);
 			}
-			
+
 			nr_remove_neigh(s);
 		}
 	}
@@ -525,12 +551,6 @@
 	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
 		return dev;
 
-#ifdef CONFIG_BPQETHER
-	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER)
-		if (ax25_bpq_get_addr(dev) != NULL)
-			return dev;
-#endif
-	
 	return NULL;
 }
 
@@ -559,10 +579,29 @@
 	for (dev = dev_base; dev != NULL; dev = dev->next)
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
 			return dev;
-	
+
 	return NULL;
 }
 
+static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
+{
+	static ax25_digi ax25_digi;
+	int i;
+
+	if (ndigis == 0)
+		return NULL;
+
+	for (i = 0; i < ndigis; i++) {
+		ax25_digi.calls[i]    = digipeaters[i];
+		ax25_digi.repeated[i] = 0;
+	}
+
+	ax25_digi.ndigi      = ndigis;
+	ax25_digi.lastrepeat = -1;
+
+	return &ax25_digi;
+}
+
 /*
  *	Handle the ioctls that control the routing functions.
  */
@@ -571,7 +610,6 @@
 	struct nr_route_struct nr_route;
 	struct device *dev;
 	int err;
-	long opt = 0;
 
 	switch (cmd) {
 
@@ -581,15 +619,19 @@
 			memcpy_fromfs(&nr_route, arg, sizeof(struct nr_route_struct));
 			if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
 				return -EINVAL;
+			if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS)
+				return -EINVAL;
 			switch (nr_route.type) {
 				case NETROM_NODE:
 					return nr_add_node(&nr_route.callsign,
 						nr_route.mnemonic,
 						&nr_route.neighbour,
-						NULL, dev, nr_route.quality,
+						nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
+						dev, nr_route.quality,
 						nr_route.obs_count);
 				case NETROM_NEIGH:
 					return nr_add_neigh(&nr_route.callsign,
+						nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
 						dev, nr_route.quality);
 				default:
 					return -EINVAL;
@@ -615,13 +657,6 @@
 		case SIOCNRDECOBS:
 			return nr_dec_obs();
 
-		case SIOCNRRTCTL:
-			if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0)
-				return err;
-			opt = get_fs_long((void *)arg);
-			nr_route_on = opt ? 1 : 0;
-			return 0;
-
 		default:
 			return -EINVAL;
 	}
@@ -633,17 +668,21 @@
  * 	A level 2 link has timed out, therefore it appears to be a poor link,
  *	then don't use that neighbour until it is reset.
  */
-void nr_link_failed(ax25_address *callsign, struct device *dev)
+void nr_link_failed(ax25_cb *ax25, int reason)
 {
 	struct nr_neigh *nr_neigh;
 	struct nr_node  *nr_node;
 
 	for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
-		if (ax25cmp(&nr_neigh->callsign, callsign) == 0 && nr_neigh->dev == dev)
+		if (nr_neigh->ax25 == ax25)
 			break;
-			
+
 	if (nr_neigh == NULL) return;
-	
+
+	nr_neigh->ax25 = NULL;
+
+	if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return;
+
 	for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
 		if (nr_node->which < nr_node->count && nr_node->routes[nr_node->which].neighbour == nr_neigh)
 			nr_node->which++;
@@ -660,7 +699,7 @@
 	struct nr_node  *nr_node;
 	struct device *dev;
 	unsigned char *dptr;
-	
+
 #ifdef CONFIG_FIREWALL
 	if (ax25 != NULL && call_in_firewall(PF_NETROM, skb->dev, skb->data, NULL) != FW_ACCEPT)
 		return 0;
@@ -672,12 +711,17 @@
 	nr_dest = (ax25_address *)(skb->data + 7);
 
 	if (ax25 != NULL)
-		nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, ax25->device, 0, nr_default.obs_count);
+		nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
+			    ax25->device, 0, sysctl_netrom_obsolescence_count_initialiser);
 
-	if ((dev = nr_dev_get(nr_dest)) != NULL)	/* Its for me */
-		return nr_rx_frame(skb, dev);
+	if ((dev = nr_dev_get(nr_dest)) != NULL) {	/* Its for me */
+		if (ax25 == NULL)			/* Its from me */
+			return nr_loopback_queue(skb);
+		else
+			return nr_rx_frame(skb, dev);
+	}
 
-	if (!nr_route_on && ax25 != NULL)
+	if (!sysctl_netrom_routing_control && ax25 != NULL)
 		return 0;
 
 	/* Its Time-To-Live has expired */
@@ -704,7 +748,9 @@
 	dptr  = skb_push(skb, 1);
 	*dptr = AX25_P_NETROM;
 
-	return ax25_send_frame(skb, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+	nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+
+	return (nr_neigh->ax25 != NULL);
 }
 
 int nr_nodes_get_info(char *buffer, char **start, off_t offset,
@@ -764,19 +810,28 @@
 	int len     = 0;
 	off_t pos   = 0;
 	off_t begin = 0;
+	int i;
 
 	cli();
 
-	len += sprintf(buffer, "addr  callsign  dev  qual lock count\n");
+	len += sprintf(buffer, "addr  callsign  dev  qual lock count failed digipeaters\n");
 
 	for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
-		len += sprintf(buffer + len, "%05d %-9s %-4s  %3d    %d   %3d\n",
+		len += sprintf(buffer + len, "%05d %-9s %-4s  %3d    %d   %3d    %3d",
 			nr_neigh->number,
 			ax2asc(&nr_neigh->callsign),
 			nr_neigh->dev ? nr_neigh->dev->name : "???",
 			nr_neigh->quality,
 			nr_neigh->locked,
-			nr_neigh->count);
+			nr_neigh->count,
+			nr_neigh->failed);
+
+		if (nr_neigh->digipeat != NULL) {
+			for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
+				len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i]));
+		}
+
+		len += sprintf(buffer + len, "\n");
 
 		pos = begin + len;
 
@@ -784,7 +839,7 @@
 			len   = 0;
 			begin = pos;
 		}
-		
+
 		if (pos > offset + length)
 			break;
 	}
@@ -798,5 +853,32 @@
 
 	return len;
 } 
+
+#ifdef MODULE
+
+/*
+ *	Free all memory associated with the nodes and routes lists.
+ */
+void nr_rt_free(void)
+{
+	struct nr_neigh *s, *nr_neigh = nr_neigh_list;
+	struct nr_node  *t, *nr_node  = nr_node_list;
+
+	while (nr_node != NULL) {
+		t       = nr_node;
+		nr_node = nr_node->next;
+
+		nr_remove_node(t);
+	}
+
+	while (nr_neigh != NULL) {
+		s        = nr_neigh;
+		nr_neigh = nr_neigh->next;
+
+		nr_remove_neigh(s);
+	}
+}
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/net/netrom/nr_subr.c linux/net/netrom/nr_subr.c
--- v2.0.34/linux/net/netrom/nr_subr.c	Mon May 13 02:15:24 1996
+++ linux/net/netrom/nr_subr.c	Mon Jul 13 13:47:41 1998
@@ -1,8 +1,5 @@
 /*
- *	NET/ROM release 003
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	NET/ROM release 006
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -16,9 +13,9 @@
  *	NET/ROM 001	Jonathan(G4KLX)	Cloned from ax25_subr.c
  *	NET/ROM	003	Jonathan(G4KLX)	Added G8BPQ NET/ROM extensions.
  */
- 
+
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -48,25 +45,17 @@
 {
 	struct sk_buff *skb;
 
-	while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
-		skb->sk   = sk;
-		skb->free = 1;
+	while ((skb = skb_dequeue(&sk->write_queue)) != NULL)
 		kfree_skb(skb, FREE_WRITE);
-	}
 
-	while ((skb = skb_dequeue(&sk->nr->ack_queue)) != NULL) {
-		skb->sk   = sk;
-		skb->free = 1;
+	while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL)
 		kfree_skb(skb, FREE_WRITE);
-	}
 
-	while ((skb = skb_dequeue(&sk->nr->reseq_queue)) != NULL) {
+	while ((skb = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL)
 		kfree_skb(skb, FREE_READ);
-	}
 
-	while ((skb = skb_dequeue(&sk->nr->frag_queue)) != NULL) {
+	while ((skb = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL)
 		kfree_skb(skb, FREE_READ);
-	}
 }
 
 /*
@@ -81,13 +70,11 @@
 	/*
 	 * Remove all the ack-ed frames from the ack queue.
 	 */
-	if (sk->nr->va != nr) {
-		while (skb_peek(&sk->nr->ack_queue) != NULL && sk->nr->va != nr) {
-		        skb = skb_dequeue(&sk->nr->ack_queue);
-		        skb->sk   = sk;
-			skb->free = 1;
+	if (sk->protinfo.nr->va != nr) {
+		while (skb_peek(&sk->protinfo.nr->ack_queue) != NULL && sk->protinfo.nr->va != nr) {
+		        skb = skb_dequeue(&sk->protinfo.nr->ack_queue);
 			kfree_skb(skb, FREE_WRITE);
-			sk->nr->va = (sk->nr->va + 1) % NR_MODULUS;
+			sk->protinfo.nr->va = (sk->protinfo.nr->va + 1) % NR_MODULUS;
 		}
 	}
 }
@@ -101,7 +88,7 @@
 {
 	struct sk_buff *skb, *skb_prev = NULL;
 
-	while ((skb = skb_dequeue(&sk->nr->ack_queue)) != NULL) {
+	while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL) {
 		if (skb_prev == NULL)
 			skb_queue_head(&sk->write_queue, skb);
 		else
@@ -116,14 +103,14 @@
  */
 int nr_validate_nr(struct sock *sk, unsigned short nr)
 {
-	unsigned short vc = sk->nr->va;
+	unsigned short vc = sk->protinfo.nr->va;
 
-	while (vc != sk->nr->vs) {
+	while (vc != sk->protinfo.nr->vs) {
 		if (nr == vc) return 1;
 		vc = (vc + 1) % NR_MODULUS;
 	}
-	
-	if (nr == sk->nr->vs) return 1;
+
+	if (nr == sk->protinfo.nr->vs) return 1;
 
 	return 0;
 }
@@ -133,8 +120,8 @@
  */
 int nr_in_rx_window(struct sock *sk, unsigned short ns)
 {
-	unsigned short vc = sk->nr->vr;
-	unsigned short vt = (sk->nr->vl + sk->window) % NR_MODULUS;
+	unsigned short vc = sk->protinfo.nr->vr;
+	unsigned short vt = (sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS;
 
 	while (vc != vt) {
 		if (ns == vc) return 1;
@@ -155,13 +142,13 @@
 	int len, timeout;
 
 	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
-	
+
 	switch (frametype & 0x0F) {
 		case NR_CONNREQ:
 			len += 17;
 			break;
 		case NR_CONNACK:
-			len += (sk->nr->bpqext) ? 2 : 1;
+			len += (sk->protinfo.nr->bpqext) ? 2 : 1;
 			break;
 		case NR_DISCREQ:
 		case NR_DISCACK:
@@ -171,10 +158,12 @@
 			printk(KERN_ERR "nr_write_internal: invalid frame type %d\n", frametype);
 			return;
 	}
-	
+
 	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
 		return;
 
+	skb->free = 1;
+
 	/*
 	 *	Space for AX.25 and NET/ROM network header
 	 */
@@ -185,65 +174,64 @@
 	switch (frametype & 0x0F) {
 
 		case NR_CONNREQ:
-			timeout  = (sk->nr->rtt / PR_SLOWHZ) * 2;
-			*dptr++  = sk->nr->my_index;
-			*dptr++  = sk->nr->my_id;
+			timeout  = sk->protinfo.nr->t1 / NR_SLOWHZ;
+			*dptr++  = sk->protinfo.nr->my_index;
+			*dptr++  = sk->protinfo.nr->my_id;
 			*dptr++  = 0;
 			*dptr++  = 0;
 			*dptr++  = frametype;
-			*dptr++  = sk->window;
-			memcpy(dptr, &sk->nr->user_addr, AX25_ADDR_LEN);
-			dptr[6] &= ~LAPB_C;
-			dptr[6] &= ~LAPB_E;
-			dptr[6] |= SSSID_SPARE;
+			*dptr++  = sk->protinfo.nr->window;
+			memcpy(dptr, &sk->protinfo.nr->user_addr, AX25_ADDR_LEN);
+			dptr[6] &= ~AX25_CBIT;
+			dptr[6] &= ~AX25_EBIT;
+			dptr[6] |= AX25_SSSID_SPARE;
 			dptr    += AX25_ADDR_LEN;
-			memcpy(dptr, &sk->nr->source_addr, AX25_ADDR_LEN);
-			dptr[6] &= ~LAPB_C;
-			dptr[6] &= ~LAPB_E;
-			dptr[6] |= SSSID_SPARE;
+			memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
+			dptr[6] &= ~AX25_CBIT;
+			dptr[6] &= ~AX25_EBIT;
+			dptr[6] |= AX25_SSSID_SPARE;
 			dptr    += AX25_ADDR_LEN;
 			*dptr++  = timeout % 256;
 			*dptr++  = timeout / 256;
 			break;
 
 		case NR_CONNACK:
-			*dptr++ = sk->nr->your_index;
-			*dptr++ = sk->nr->your_id;
-			*dptr++ = sk->nr->my_index;
-			*dptr++ = sk->nr->my_id;
+			*dptr++ = sk->protinfo.nr->your_index;
+			*dptr++ = sk->protinfo.nr->your_id;
+			*dptr++ = sk->protinfo.nr->my_index;
+			*dptr++ = sk->protinfo.nr->my_id;
 			*dptr++ = frametype;
-			*dptr++ = sk->window;
-			if (sk->nr->bpqext) *dptr++ = nr_default.ttl;
+			*dptr++ = sk->protinfo.nr->window;
+			if (sk->protinfo.nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser;
 			break;
 
 		case NR_DISCREQ:
 		case NR_DISCACK:
-			*dptr++ = sk->nr->your_index;
-			*dptr++ = sk->nr->your_id;
+			*dptr++ = sk->protinfo.nr->your_index;
+			*dptr++ = sk->protinfo.nr->your_id;
 			*dptr++ = 0;
 			*dptr++ = 0;
 			*dptr++ = frametype;
 			break;
 
 		case NR_INFOACK:
-			*dptr++ = sk->nr->your_index;
-			*dptr++ = sk->nr->your_id;
+			*dptr++ = sk->protinfo.nr->your_index;
+			*dptr++ = sk->protinfo.nr->your_id;
 			*dptr++ = 0;
-			*dptr++ = sk->nr->vr;
+			*dptr++ = sk->protinfo.nr->vr;
 			*dptr++ = frametype;
 			break;
 	}
 
-	skb->free = 1;
-
 	nr_transmit_buffer(sk, skb);
 }
 
 /*
  * This routine is called when a Connect Acknowledge with the Choke Flag
  * set is needed to refuse a connection.
+ * If 'my' is set, send the "reset" using our circuit ID.
  */
-void nr_transmit_dm(struct sk_buff *skb)
+void nr_transmit_dm(struct sk_buff *skb, int my)
 {
 	struct sk_buff *skbn;
 	unsigned char *dptr;
@@ -254,74 +242,43 @@
 	if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL)
 		return;
 
+	skbn->free = 1;
+
 	skb_reserve(skbn, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
 
 	dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
 
 	memcpy(dptr, skb->data + 7, AX25_ADDR_LEN);
-	dptr[6] &= ~LAPB_C;
-	dptr[6] &= ~LAPB_E;
-	dptr[6] |= SSSID_SPARE;
+	dptr[6] &= ~AX25_CBIT;
+	dptr[6] &= ~AX25_EBIT;
+	dptr[6] |= AX25_SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 	
 	memcpy(dptr, skb->data + 0, AX25_ADDR_LEN);
-	dptr[6] &= ~LAPB_C;
-	dptr[6] |= LAPB_E;
-	dptr[6] |= SSSID_SPARE;
+	dptr[6] &= ~AX25_CBIT;
+	dptr[6] |= AX25_EBIT;
+	dptr[6] |= AX25_SSSID_SPARE;
 	dptr += AX25_ADDR_LEN;
 
-	*dptr++ = nr_default.ttl;
+	*dptr++ = sysctl_netrom_network_ttl_initialiser;
+
+	if (my) {
+		*dptr++ = 0;
+		*dptr++ = 0;
+		*dptr++ = skb->data[15];
+		*dptr++ = skb->data[16];
+	} else {
+		*dptr++ = skb->data[15];
+		*dptr++ = skb->data[16];
+		*dptr++ = 0;
+		*dptr++ = 0;
+	}
 
-	*dptr++ = skb->data[15];
-	*dptr++ = skb->data[16];
-	*dptr++ = 0;
-	*dptr++ = 0;
 	*dptr++ = NR_CONNACK | NR_CHOKE_FLAG;
 	*dptr++ = 0;
 
-	skbn->free = 1;
-	skbn->sk   = NULL;
-
 	if (!nr_route_frame(skbn, NULL))
 		kfree_skb(skbn, FREE_WRITE);
-}
-
-/*
- *	Exponential backoff for NET/ROM
- */
-unsigned short nr_calculate_t1(struct sock *sk)
-{
-	int n, t;
-	
-	for (t = 2, n = 0; n < sk->nr->n2count; n++)
-		t *= 2;
-
-	if (t > 8) t = 8;
-
-	return t * sk->nr->rtt;
-}
-
-/*
- *	Calculate the Round Trip Time
- */
-void nr_calculate_rtt(struct sock *sk)
-{
-	if (sk->nr->t1timer > 0 && sk->nr->n2count == 0)
-		sk->nr->rtt = (9 * sk->nr->rtt + sk->nr->t1 - sk->nr->t1timer) / 10;
-
-#ifdef	NR_T1CLAMPLO
-	/* Don't go below one tenth of a second */
-	if (sk->nr->rtt < (NR_T1CLAMPLO))
-		sk->nr->rtt = (NR_T1CLAMPLO);
-#else   /* Failsafe - some people might have sub 1/10th RTTs :-) **/
-        if (sk->nr->rtt == 0)
-                sk->nr->rtt = PR_SLOWHZ;
-#endif
-#ifdef  NR_T1CLAMPHI
-        /* OR above clamped seconds **/
-        if (sk->nr->rtt > (NR_T1CLAMPHI))
-                sk->nr->rtt = (NR_T1CLAMPHI);
-#endif
 }
 
 #endif
diff -u --recursive --new-file v2.0.34/linux/net/netrom/nr_timer.c linux/net/netrom/nr_timer.c
--- v2.0.34/linux/net/netrom/nr_timer.c	Thu Apr 11 23:49:51 1996
+++ linux/net/netrom/nr_timer.c	Mon Jul 13 13:47:41 1998
@@ -1,8 +1,5 @@
 /*
- *	NET/ROM release 003
- *
- *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
- *	releases, misbehave and/or generally screw up. It might even work. 
+ *	NET/ROM release 006
  *
  *	This code REQUIRES 1.2.1 or higher/ NET3.029
  *
@@ -17,7 +14,7 @@
  */
 
 #include <linux/config.h>
-#ifdef CONFIG_NETROM
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -43,51 +40,34 @@
 static void nr_timer(unsigned long);
 
 /*
- *	Linux set/reset timer routines
+ *	Linux set timer
  */
 void nr_set_timer(struct sock *sk)
 {
 	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-	del_timer(&sk->timer);
-	restore_flags(flags);
-
-	sk->timer.next     = sk->timer.prev = NULL;	
-	sk->timer.data     = (unsigned long)sk;
-	sk->timer.function = &nr_timer;
 
-	sk->timer.expires = jiffies+10;
-	add_timer(&sk->timer);
-}
-
-static void nr_reset_timer(struct sock *sk)
-{
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
+	save_flags(flags); cli();
 	del_timer(&sk->timer);
 	restore_flags(flags);
 
 	sk->timer.data     = (unsigned long)sk;
 	sk->timer.function = &nr_timer;
 	sk->timer.expires  = jiffies+10;
+
 	add_timer(&sk->timer);
 }
 
 /*
  *	NET/ROM TIMER 
  *
- *	This routine is called every 500ms. Decrement timer by this
+ *	This routine is called every 100ms. Decrement timer by this
  *	amount - if expired then process the event.
  */
 static void nr_timer(unsigned long param)
 {
 	struct sock *sk = (struct sock *)param;
 
-	switch (sk->nr->state) {
+	switch (sk->protinfo.nr->state) {
 		case NR_STATE_0:
 			/* Magic here: If we listen() and a new link dies before it
 			   is accepted() it isn't 'dead' so doesn't get removed. */
@@ -102,11 +82,11 @@
 			/*
 			 * Check for the state of the receive buffer.
 			 */
-			if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->nr->condition & OWN_RX_BUSY_CONDITION)) {
-				sk->nr->condition &= ~OWN_RX_BUSY_CONDITION;
+			if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)) {
+				sk->protinfo.nr->condition &= ~NR_COND_OWN_RX_BUSY;
+				sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
+				sk->protinfo.nr->vl         = sk->protinfo.nr->vr;
 				nr_write_internal(sk, NR_INFOACK);
-				sk->nr->condition &= ~ACK_PENDING_CONDITION;
-				sk->nr->vl = sk->nr->vr;
 				break;
 			}
 			/*
@@ -119,72 +99,75 @@
 			break;
 	}
 
-	if (sk->nr->t2timer > 0 && --sk->nr->t2timer == 0) {
-		if (sk->nr->state == NR_STATE_3) {
-			if (sk->nr->condition & ACK_PENDING_CONDITION) {
-				sk->nr->condition &= ~ACK_PENDING_CONDITION;
+	if (sk->protinfo.nr->t2timer > 0 && --sk->protinfo.nr->t2timer == 0) {
+		if (sk->protinfo.nr->state == NR_STATE_3) {
+			if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) {
+				sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
 				nr_enquiry_response(sk);
 			}
 		}
 	}
 
-	if (sk->nr->t4timer > 0 && --sk->nr->t4timer == 0) {
-		sk->nr->condition &= ~PEER_RX_BUSY_CONDITION;
+	if (sk->protinfo.nr->t4timer > 0 && --sk->protinfo.nr->t4timer == 0) {
+		sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
 	}
 
-	if (sk->nr->t1timer == 0 || --sk->nr->t1timer > 0) {
-		nr_reset_timer(sk);
+	if (sk->protinfo.nr->t1timer == 0 || --sk->protinfo.nr->t1timer > 0) {
+		nr_set_timer(sk);
 		return;
 	}
 
-	switch (sk->nr->state) {
+	switch (sk->protinfo.nr->state) {
 		case NR_STATE_1: 
-			if (sk->nr->n2count == sk->nr->n2) {
+			if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
 				nr_clear_queues(sk);
-				sk->nr->state = NR_STATE_0;
-				sk->state     = TCP_CLOSE;
-				sk->err       = ETIMEDOUT;
+				sk->protinfo.nr->state = NR_STATE_0;
+				sk->state              = TCP_CLOSE;
+				sk->err                = ETIMEDOUT;
+				sk->shutdown          |= SEND_SHUTDOWN;
 				if (!sk->dead)
 					sk->state_change(sk);
-				sk->dead      = 1;
+				sk->dead               = 1;
 			} else {
-				sk->nr->n2count++;
+				sk->protinfo.nr->n2count++;
 				nr_write_internal(sk, NR_CONNREQ);
 			}
 			break;
 
 		case NR_STATE_2:
-			if (sk->nr->n2count == sk->nr->n2) {
+			if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
 				nr_clear_queues(sk);
-				sk->nr->state = NR_STATE_0;
-				sk->state     = TCP_CLOSE;
-				sk->err       = ETIMEDOUT;
+				sk->protinfo.nr->state = NR_STATE_0;
+				sk->state              = TCP_CLOSE;
+				sk->err                = ETIMEDOUT;
+				sk->shutdown          |= SEND_SHUTDOWN;
 				if (!sk->dead)
 					sk->state_change(sk);
-				sk->dead      = 1;
+				sk->dead               = 1;
 			} else {
-				sk->nr->n2count++;
+				sk->protinfo.nr->n2count++;
 				nr_write_internal(sk, NR_DISCREQ);
 			}
 			break;
 
 		case NR_STATE_3:
-			if (sk->nr->n2count == sk->nr->n2) {
+			if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
 				nr_clear_queues(sk);
-				sk->nr->state = NR_STATE_0;
-				sk->state     = TCP_CLOSE;
-				sk->err       = ETIMEDOUT;
+				sk->protinfo.nr->state = NR_STATE_0;
+				sk->state              = TCP_CLOSE;
+				sk->err                = ETIMEDOUT;
+				sk->shutdown          |= SEND_SHUTDOWN;
 				if (!sk->dead)
 					sk->state_change(sk);
-				sk->dead      = 1;
+				sk->dead               = 1;
 			} else {
-				sk->nr->n2count++;
+				sk->protinfo.nr->n2count++;
 				nr_requeue_frames(sk);
 			}
 			break;
 	}
 
-	sk->nr->t1timer = sk->nr->t1 = nr_calculate_t1(sk);
+	sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
 
 	nr_set_timer(sk);
 }
diff -u --recursive --new-file v2.0.34/linux/net/netrom/sysctl_net_netrom.c linux/net/netrom/sysctl_net_netrom.c
--- v2.0.34/linux/net/netrom/sysctl_net_netrom.c	Mon Apr  1 22:03:35 1996
+++ linux/net/netrom/sysctl_net_netrom.c	Mon Jul 13 13:47:41 1998
@@ -1,5 +1,5 @@
 /* -*- linux-c -*-
- * sysctl_net_netrom.c: sysctl interface to net NETROM subsystem.
+ * sysctl_net_netrom.c: sysctl interface to net NET/ROM subsystem.
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/netrom directory entry (empty =) ). [MS]
@@ -7,7 +7,78 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <net/ax25.h>
+#include <net/netrom.h>
 
-ctl_table netrom_table[] = {
+/*
+ *	Values taken from NET/ROM documentation.
+ */
+static int min_quality[] = {0}, max_quality[] = {255};
+static int min_obs[]     = {0}, max_obs[]     = {255};
+static int min_ttl[]     = {0}, max_ttl[]     = {255};
+static int min_t1[]      = {5 * NR_SLOWHZ};
+static int max_t1[]      = {600 * NR_SLOWHZ};
+static int min_n2[]      = {2}, max_n2[]      = {127};
+static int min_t2[]      = {1 * NR_SLOWHZ};
+static int max_t2[]      = {60 * NR_SLOWHZ};
+static int min_t4[]      = {1 * NR_SLOWHZ};
+static int max_t4[]      = {1000 * NR_SLOWHZ};
+static int min_window[]  = {1}, max_window[]  = {127};
+static int min_route[]   = {0}, max_route[]   = {1};
+static int min_fails[]   = {1}, max_fails[]   = {10};
+
+static struct ctl_table_header *nr_table_header;
+
+static ctl_table nr_table[] = {
+        {NET_NETROM_DEFAULT_PATH_QUALITY, "default_path_quality",
+         &sysctl_netrom_default_path_quality, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_quality, &max_quality},
+        {NET_NETROM_OBSOLESCENCE_COUNT_INITIALISER, "obsolescence_count_initialiser",
+         &sysctl_netrom_obsolescence_count_initialiser, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_obs, &max_obs},
+        {NET_NETROM_NETWORK_TTL_INITIALISER, "network_ttl_initialiser",
+         &sysctl_netrom_network_ttl_initialiser, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_ttl, &max_ttl},
+        {NET_NETROM_TRANSPORT_TIMEOUT, "transport_timeout",
+         &sysctl_netrom_transport_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t1, &max_t1},
+        {NET_NETROM_TRANSPORT_MAXIMUM_TRIES, "transport_maximum_tries",
+         &sysctl_netrom_transport_maximum_tries, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_n2, &max_n2},
+        {NET_NETROM_TRANSPORT_ACKNOWLEDGE_DELAY, "transport_acknowledge_delay",
+         &sysctl_netrom_transport_acknowledge_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t2, &max_t2},
+        {NET_NETROM_TRANSPORT_BUSY_DELAY, "transport_busy_delay",
+         &sysctl_netrom_transport_busy_delay, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_t4, &max_t4},
+        {NET_NETROM_TRANSPORT_REQUESTED_WINDOW_SIZE, "transport_requested_window_size",
+         &sysctl_netrom_transport_requested_window_size, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_window, &max_window},
+        {NET_NETROM_ROUTING_CONTROL, "routing_control",
+         &sysctl_netrom_routing_control, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route},
+        {NET_NETROM_LINK_FAILS_COUNT, "link_fails_count",
+         &sysctl_netrom_link_fails_count, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_fails, &max_fails},
+	{0}
+};
+
+static ctl_table nr_dir_table[] = {
+	{NET_NETROM, "netrom", NULL, 0, 0555, nr_table},
 	{0}
 };
+
+static ctl_table nr_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, nr_dir_table},
+	{0}
+};
+
+void nr_register_sysctl(void)
+{
+	nr_table_header = register_sysctl_table(nr_root_table, 1);
+}
+
+void nr_unregister_sysctl(void)
+{
+	unregister_sysctl_table(nr_table_header);
+}
diff -u --recursive --new-file v2.0.34/linux/net/netsyms.c linux/net/netsyms.c
--- v2.0.34/linux/net/netsyms.c	Mon Jul 13 13:46:43 1998
+++ linux/net/netsyms.c	Mon Jul 13 13:47:41 1998
@@ -15,10 +15,6 @@
 #include <linux/ioport.h>
 #include <net/sock.h>
 
-#ifdef CONFIG_AX25
-#include <net/ax25.h>
-#endif
-
 #ifdef CONFIG_INET
 #include <linux/ip.h>
 #include <linux/etherdevice.h>
@@ -30,6 +26,7 @@
 #include <net/icmp.h>
 #include <net/route.h>
 #include <linux/net_alias.h>
+#include <linux/inet.h>
 #endif
 
 #ifdef CONFIG_NETLINK
@@ -44,7 +41,7 @@
         defined(CONFIG_EL2)     ||      defined(CONFIG_NE2000)          || \
         defined(CONFIG_E2100)   ||      defined(CONFIG_HPLAN_PLUS)      || \
         defined(CONFIG_HPLAN)   ||      defined(CONFIG_AC3200)		|| \
-	defined(CONFIG_ULTRA32)
+	defined(CONFIG_ULTRA32) ||	defined(CONFIG_NE2K_PCI)
 #include "../drivers/net/8390.h"
 #endif
 
@@ -119,7 +116,7 @@
 	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
 	defined(CONFIG_E2100)	||	defined(CONFIG_HPLAN_PLUS)	|| \
 	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		|| \
-	defined(CONFIG_ULTRA32)
+	defined(CONFIG_ULTRA32)	||	defined(CONFIG_NE2K_PCI)
 	/* If 8390 NIC support is built in, we will need these. */
 	X(ei_open),
 	X(ei_close),
@@ -150,10 +147,6 @@
 #endif
 
         /* support for loadable net drivers */
-#ifdef CONFIG_AX25
-	X(ax25_encapsulate),
-	X(ax25_rebuild_header),
-#endif
 #ifdef CONFIG_INET
 	X(register_netdev),
 	X(unregister_netdev),
@@ -164,9 +157,11 @@
 	X(alloc_skb),
 	X(kfree_skb),
 	X(skb_clone),
+	X(skb_copy),
 	X(dev_alloc_skb),
 	X(dev_kfree_skb),
 	X(skb_device_unlock),
+	X(skb_device_locked),
 	X(netif_rx),
 	X(dev_tint),
 	X(irq2dev_map),
@@ -183,6 +178,10 @@
 	X(tty_register_ldisc),
 	X(kill_fasync),
 	X(arp_query),
+	X(ip_rcv),
+	X(arp_rcv),
+	X(in_aton),
+	X(in_ntoa),
 #endif  /* CONFIG_INET */
 
 #ifdef CONFIG_NETLINK
diff -u --recursive --new-file v2.0.34/linux/net/protocols.c linux/net/protocols.c
--- v2.0.34/linux/net/protocols.c	Sat Mar 30 03:20:34 1996
+++ linux/net/protocols.c	Mon Jul 13 13:47:41 1998
@@ -28,6 +28,9 @@
 #ifdef CONFIG_NETROM
 #include <net/nrcall.h>
 #endif
+#ifdef CONFIG_ROSE
+#include <net/rosecall.h>
+#endif
 #endif
 #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
 #if ! ( defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) )
@@ -60,9 +63,12 @@
   { "RIF",	rif_init },				/* RIF for Token ring		*/
 #endif  
 #ifdef CONFIG_AX25  
-  { "AX.25",	ax25_proto_init },
+  { "AX.25",	ax25_proto_init },			/* Amateur Radio AX.25 */
 #ifdef CONFIG_NETROM
-  { "NET/ROM",	nr_proto_init },
+  { "NET/ROM",	nr_proto_init },			/* Amateur Radio NET/ROM */
+#endif
+#ifdef CONFIG_ROSE
+  { "Rose",	rose_proto_init },			/* Amateur Radio X.25 PLP */
 #endif
 #endif  
 #ifdef	CONFIG_INET
diff -u --recursive --new-file v2.0.34/linux/net/rose/Makefile linux/net/rose/Makefile
--- v2.0.34/linux/net/rose/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/Makefile	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux Rose (X.25 PLP) layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := rose.o
+O_OBJS	 := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+tar:
+		tar -cvf /dev/f1 .
diff -u --recursive --new-file v2.0.34/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.0.34/linux/net/rose/af_rose.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/af_rose.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,1548 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from af_netrom.c.
+ *			Terry (VK2KTJ)	Added support for variable length
+ *					address masks.
+ *	ROSE 002	Jonathan(G4KLX)	Changed hdrincl to qbitincl.
+ *					Added random number facilities entry.
+ *	ROSE 003	Jonathan(G4KLX)	Added use count to neighbour.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/stat.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <net/rose.h>
+#include <linux/proc_fs.h>
+#include <net/ip.h>
+#include <net/arp.h>
+#include <linux/if_arp.h>
+
+int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0;
+int sysctl_rose_call_request_timeout    = ROSE_DEFAULT_T1;
+int sysctl_rose_reset_request_timeout   = ROSE_DEFAULT_T2;
+int sysctl_rose_clear_request_timeout   = ROSE_DEFAULT_T3;
+int sysctl_rose_ack_hold_back_timeout   = ROSE_DEFAULT_HB;
+int sysctl_rose_routing_control         = ROSE_DEFAULT_ROUTING;
+int sysctl_rose_link_fail_timeout       = ROSE_DEFAULT_FAIL_TIMEOUT;
+int sysctl_rose_maximum_vcs             = ROSE_DEFAULT_MAXVC;
+int sysctl_rose_window_size             = ROSE_DEFAULT_WINDOW_SIZE;
+
+static struct sock *volatile rose_list = NULL;
+
+ax25_address rose_callsign;
+
+/*
+ *	Convert a ROSE address into text.
+ */
+char *rose2asc(rose_address *addr)
+{
+	static char buffer[11];
+
+	if (addr->rose_addr[0] == 0x00 && addr->rose_addr[1] == 0x00 &&
+	    addr->rose_addr[2] == 0x00 && addr->rose_addr[3] == 0x00 &&
+	    addr->rose_addr[4] == 0x00) {
+		strcpy(buffer, "*");
+	} else {
+		sprintf(buffer, "%02X%02X%02X%02X%02X", addr->rose_addr[0] & 0xFF,
+						addr->rose_addr[1] & 0xFF,
+						addr->rose_addr[2] & 0xFF,
+						addr->rose_addr[3] & 0xFF,
+						addr->rose_addr[4] & 0xFF);
+	}
+
+	return buffer;
+}
+
+/*
+ *	Compare two ROSE addresses, 0 == equal.
+ */
+int rosecmp(rose_address *addr1, rose_address *addr2)
+{
+	int i;
+
+	for (i = 0; i < 5; i++)
+		if (addr1->rose_addr[i] != addr2->rose_addr[i])
+			return 1;
+
+	return 0;
+}
+
+/*
+ *	Compare two ROSE addresses for only mask digits, 0 == equal.
+ */
+int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask)
+{
+	int i, j;
+
+	if (mask > 10)
+		return 1;
+
+	for (i = 0; i < mask; i++) {
+		j = i / 2;
+
+		if ((i % 2) != 0) {
+			if ((addr1->rose_addr[j] & 0x0F) != (addr2->rose_addr[j] & 0x0F))
+				return 1;
+		} else {
+			if ((addr1->rose_addr[j] & 0xF0) != (addr2->rose_addr[j] & 0xF0))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void rose_free_sock(struct sock *sk)
+{
+	kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose));
+
+	sk_free(sk);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static struct sock *rose_alloc_sock(void)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	memset(rose, 0x00, sizeof(*rose));
+
+	sk->protinfo.rose = rose;
+	rose->sk          = sk;
+
+	return sk;
+}
+
+/*
+ *	Socket removal during an interrupt is now safe.
+ */
+static void rose_remove_socket(struct sock *sk)
+{
+	struct sock *s;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_list) == sk) {
+		rose_list = s->next;
+		restore_flags(flags);
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == sk) {
+			s->next = sk->next;
+			restore_flags(flags);
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	Kill all bound sockets on a broken link layer connection to a
+ *	particular neighbour.
+ */
+void rose_kill_by_neigh(struct rose_neigh *neigh)
+{
+	struct sock *s;
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->neighbour == neigh) {
+			s->protinfo.rose->cause      = ROSE_OUT_OF_ORDER;
+			s->protinfo.rose->diagnostic = 0;
+			s->protinfo.rose->state     = ROSE_STATE_0;
+			s->protinfo.rose->neighbour->use--;
+			s->protinfo.rose->neighbour = NULL;
+			s->state                    = TCP_CLOSE;
+			s->err                      = ENETUNREACH;
+			s->shutdown                |= SEND_SHUTDOWN;
+			s->state_change(s);
+			s->dead                     = 1;
+		}
+	}
+}
+
+/*
+ *	Kill all bound sockets on a dropped device.
+ */
+static void rose_kill_by_device(struct device *dev)
+{
+	struct sock *s;
+	
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->device == dev) {
+			s->protinfo.rose->cause  = ROSE_OUT_OF_ORDER;
+			s->protinfo.rose->diagnostic = 0;
+			s->protinfo.rose->state  = ROSE_STATE_0;
+			s->protinfo.rose->neighbour->use--;
+			s->protinfo.rose->device = NULL;
+			s->state                 = TCP_CLOSE;
+			s->err                   = ENETUNREACH;
+			s->shutdown             |= SEND_SHUTDOWN;
+			s->state_change(s);
+			s->dead                  = 1;
+		}
+	}
+}
+
+/*
+ *	Handle device status changes.
+ */
+static int rose_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+
+	if (event != NETDEV_DOWN)
+		return NOTIFY_DONE;
+
+	switch (dev->type) {
+		case ARPHRD_ROSE:
+			rose_kill_by_device(dev);
+			break;
+		case ARPHRD_AX25:
+			rose_link_device_down(dev);
+			rose_rt_device_down(dev);
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+/*
+ *	Add a socket to the bound sockets list.
+ */
+static void rose_insert_socket(struct sock *sk)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	sk->next  = rose_list;
+	rose_list = sk;
+
+	restore_flags(flags);
+}
+
+/*
+ *	Find a socket that wants to accept the Call Request we just
+ *	received.
+ */
+static struct sock *rose_find_listener(rose_address *addr, ax25_address *call)
+{
+	unsigned long flags;
+	struct sock *s;
+
+	save_flags(flags);
+	cli();
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0 && s->state == TCP_LISTEN) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	restore_flags(flags);
+	return NULL;
+}
+
+/*
+ *	Find a connected ROSE socket given my LCI and device.
+ */
+struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh)
+{
+	struct sock *s;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour == neigh) {
+			restore_flags(flags);
+			return s;
+		}
+	}
+
+	restore_flags(flags);
+
+	return NULL;
+}
+
+/*
+ *	Find a unique LCI for a given device.
+ */
+unsigned int rose_new_lci(struct rose_neigh *neigh)
+{
+	int lci;
+
+	if (neigh->dce_mode) {
+		for (lci = 1; lci <= sysctl_rose_maximum_vcs; lci++)
+			if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL)
+				return lci;
+	} else {
+		for (lci = sysctl_rose_maximum_vcs; lci > 0; lci--)
+			if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL)
+				return lci;
+	}
+
+	return 0;
+}
+
+/*
+ *	Deferred destroy.
+ */
+void rose_destroy_socket(struct sock *);
+
+/*
+ *	Handler for deferred kills.
+ */
+static void rose_destroy_timer(unsigned long data)
+{
+	rose_destroy_socket((struct sock *)data);
+}
+
+/*
+ *	This is called from user mode and the timers. Thus it protects itself against
+ *	interrupt users but doesn't worry about being called during work.
+ *	Once it is removed from the queue no interrupt or bottom half will
+ *	touch it and we are (fairly 8-) ) safe.
+ */
+void rose_destroy_socket(struct sock *sk)	/* Not static as it's used by the timer */
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	del_timer(&sk->timer);
+
+	rose_remove_socket(sk);
+	rose_clear_queues(sk);		/* Flush the queues */
+
+	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+		if (skb->sk != sk) {			/* A pending connection */
+			skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
+			rose_set_timer(skb->sk);
+			skb->sk->protinfo.rose->state = ROSE_STATE_0;
+		}
+
+		kfree_skb(skb, FREE_READ);
+	}
+
+	if (sk->wmem_alloc != 0 || sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
+		init_timer(&sk->timer);
+		sk->timer.expires  = jiffies + 10 * HZ;
+		sk->timer.function = rose_destroy_timer;
+		sk->timer.data     = (unsigned long)sk;
+		add_timer(&sk->timer);
+	} else {
+		rose_free_sock(sk);
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	Handling for system calls applied via the various interfaces to a
+ *	ROSE socket object.
+ */
+
+static int rose_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+static int rose_setsockopt(struct socket *sock, int level, int optname,
+	char *optval, int optlen)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	int err, opt;
+
+	if (level == SOL_SOCKET)
+		return sock_setsockopt(sk, level, optname, optval, optlen);
+
+	if (level != SOL_ROSE)
+		return -EOPNOTSUPP;
+
+	if (optval == NULL)
+		return -EINVAL;
+
+	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
+		return err;
+
+	opt = get_fs_long((unsigned long *)optval);
+
+	switch (optname) {
+		case ROSE_DEFER:
+			sk->protinfo.rose->defer = opt ? 1 : 0;
+			return 0;
+
+		case ROSE_T1:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t1 = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_T2:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t2 = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_T3:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->t3 = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_HOLDBACK:
+			if (opt < 1)
+				return -EINVAL;
+			sk->protinfo.rose->hb = opt * ROSE_SLOWHZ;
+			return 0;
+
+		case ROSE_IDLE:
+			if (opt < 0)
+				return -EINVAL;
+			return 0;
+
+		case ROSE_QBITINCL:
+			sk->protinfo.rose->qbitincl = opt ? 1 : 0;
+			return 0;
+
+		default:
+			return -ENOPROTOOPT;
+	}
+}
+
+static int rose_getsockopt(struct socket *sock, int level, int optname,
+	char *optval, int *optlen)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	int val = 0;
+	int err; 
+
+	if (level == SOL_SOCKET)
+		return sock_getsockopt(sk, level, optname, optval, optlen);
+
+	if (level != SOL_ROSE)
+		return -EOPNOTSUPP;
+
+	switch (optname) {
+		case ROSE_DEFER:
+			val = sk->protinfo.rose->defer;
+			break;
+
+		case ROSE_T1:
+			val = sk->protinfo.rose->t1 / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_T2:
+			val = sk->protinfo.rose->t2 / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_T3:
+			val = sk->protinfo.rose->t3 / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_HOLDBACK:
+			val = sk->protinfo.rose->hb / ROSE_SLOWHZ;
+			break;
+
+		case ROSE_IDLE:
+			val = 0;
+			break;
+
+		case ROSE_QBITINCL:
+			val = sk->protinfo.rose->qbitincl;
+			break;
+
+		default:
+			return -ENOPROTOOPT;
+	}
+
+	if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
+		return err;
+
+	put_fs_long(sizeof(int), (unsigned long *)optlen);
+
+	if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
+		return err;
+
+	put_fs_long(val, (unsigned long *)optval);
+
+	return 0;
+}
+
+static int rose_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = (struct sock *)sock->data;
+
+	if (sk->state != TCP_LISTEN) {
+		sk->protinfo.rose->dest_ndigis = 0;
+		memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN);
+		memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN);
+		memset(&sk->protinfo.rose->dest_digi, '\0', AX25_ADDR_LEN);
+		sk->max_ack_backlog = backlog;
+		sk->state           = TCP_LISTEN;
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static void def_callback1(struct sock *sk)
+{
+	if (!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static void def_callback2(struct sock *sk, int len)
+{
+	if (!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static int rose_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if (sock->type != SOCK_SEQPACKET || protocol != 0)
+		return -ESOCKTNOSUPPORT;
+
+	if ((sk = rose_alloc_sock()) == NULL)
+		return -ENOMEM;
+
+	rose = sk->protinfo.rose;
+
+	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->write_queue);
+	skb_queue_head_init(&sk->back_log);
+
+	init_timer(&sk->timer);
+
+	sk->socket        = sock;
+	sk->type          = sock->type;
+	sk->protocol      = protocol;
+	sk->allocation	  = GFP_KERNEL;
+	sk->rcvbuf        = SK_RMEM_MAX;
+	sk->sndbuf        = SK_WMEM_MAX;
+	sk->state         = TCP_CLOSE;
+	sk->priority      = SOPRI_NORMAL;
+	sk->mtu           = ROSE_MTU;	/* 128 */
+	sk->zapped        = 1;
+
+	sk->state_change = def_callback1;
+	sk->data_ready   = def_callback2;
+	sk->write_space  = def_callback1;
+	sk->error_report = def_callback1;
+
+	if (sock != NULL) {
+		sock->data = (void *)sk;
+		sk->sleep  = sock->wait;
+	}
+
+	rose->t1    = sysctl_rose_call_request_timeout;
+	rose->t2    = sysctl_rose_reset_request_timeout;
+	rose->t3    = sysctl_rose_clear_request_timeout;
+	rose->hb    = sysctl_rose_ack_hold_back_timeout;
+
+	rose->state = ROSE_STATE_0;
+
+	return 0;
+}
+
+static struct sock *rose_make_new(struct sock *osk)
+{
+	struct sock *sk;
+	rose_cb *rose;
+
+	if (osk->type != SOCK_SEQPACKET)
+		return NULL;
+
+	if ((sk = rose_alloc_sock()) == NULL)
+		return NULL;
+
+	rose = sk->protinfo.rose;
+
+	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->write_queue);
+	skb_queue_head_init(&sk->back_log);
+
+	init_timer(&sk->timer);
+
+	sk->type        = osk->type;
+	sk->socket      = osk->socket;
+	sk->priority    = osk->priority;
+	sk->protocol    = osk->protocol;
+	sk->rcvbuf      = osk->rcvbuf;
+	sk->sndbuf      = osk->sndbuf;
+	sk->debug       = osk->debug;
+	sk->state       = TCP_ESTABLISHED;
+	sk->mtu         = osk->mtu;
+	sk->sleep       = osk->sleep;
+	sk->zapped      = osk->zapped;
+
+	sk->state_change = def_callback1;
+	sk->data_ready   = def_callback2;
+	sk->write_space  = def_callback1;
+	sk->error_report = def_callback1;
+
+	rose->t1      = osk->protinfo.rose->t1;
+	rose->t2      = osk->protinfo.rose->t2;
+	rose->t3      = osk->protinfo.rose->t3;
+	rose->hb      = osk->protinfo.rose->hb;
+
+	rose->defer    = osk->protinfo.rose->defer;
+	rose->device   = osk->protinfo.rose->device;
+	rose->qbitincl = osk->protinfo.rose->qbitincl;
+
+	return sk;
+}
+
+static int rose_dup(struct socket *newsock, struct socket *oldsock)
+{
+	struct sock *sk = (struct sock *)oldsock->data;
+
+	if (sk == NULL || newsock == NULL)
+		return -EINVAL;
+
+	return rose_create(newsock, sk->protocol);
+}
+
+static int rose_release(struct socket *sock, struct socket *peer)
+{
+	struct sock *sk = (struct sock *)sock->data;
+
+	if (sk == NULL) return 0;
+
+	switch (sk->protinfo.rose->state) {
+
+		case ROSE_STATE_0:
+			sk->state     = TCP_CLOSE;
+			sk->shutdown |= SEND_SHUTDOWN;
+			sk->state_change(sk);
+			sk->dead      = 1;
+			rose_destroy_socket(sk);
+			break;
+
+		case ROSE_STATE_2:
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			sk->state_change(sk);
+			sk->dead                 = 1;
+			rose_destroy_socket(sk);
+			break;			
+
+		case ROSE_STATE_1:
+		case ROSE_STATE_3:
+		case ROSE_STATE_4:
+		case ROSE_STATE_5:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			sk->protinfo.rose->timer = sk->protinfo.rose->t3;
+			sk->protinfo.rose->state = ROSE_STATE_2;
+			sk->state                = TCP_CLOSE;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			sk->state_change(sk);
+			sk->dead                 = 1;
+			sk->destroy              = 1;
+			break;
+
+		default:
+			break;
+	}
+
+	sock->data = NULL;	
+	sk->socket = NULL;	/* Not used, but we should do this. **/
+
+	return 0;
+}
+
+static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
+	struct device *dev;
+	ax25_address *user, *source;
+
+	if (sk->zapped == 0)
+		return -EINVAL;
+
+	if (addr_len != sizeof(struct sockaddr_rose))
+		return -EINVAL;
+
+	if (addr->srose_family != AF_ROSE)
+		return -EINVAL;
+
+	if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
+		if (sk->debug)
+			printk("ROSE: bind failed: invalid address\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	source = &addr->srose_call;
+
+	if ((user = ax25_findbyuid(current->euid)) == NULL) {
+		if (ax25_uid_policy && !suser())
+			return -EACCES;
+		user = source;
+	}
+
+	sk->protinfo.rose->source_addr = addr->srose_addr;
+	sk->protinfo.rose->source_call = *user;
+	sk->protinfo.rose->device      = dev;
+
+	if (addr->srose_ndigis == 1) {
+		sk->protinfo.rose->source_ndigis = 1;
+		sk->protinfo.rose->source_digi   = addr->srose_digi;
+	}
+
+	rose_insert_socket(sk);
+
+	sk->zapped = 0;
+
+	if (sk->debug)
+		printk("ROSE: socket is bound\n");
+
+	return 0;
+}
+
+static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
+	unsigned char cause, diagnostic;
+	ax25_address *user;
+	struct device *dev;
+
+	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
+		sock->state = SS_CONNECTED;
+		return 0;	/* Connect completed during a ERESTARTSYS event */
+	}
+
+	if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
+		sock->state = SS_UNCONNECTED;
+		return -ECONNREFUSED;
+	}
+
+	if (sk->state == TCP_ESTABLISHED)
+		return -EISCONN;	/* No reconnect on a seqpacket socket */
+
+	sk->state   = TCP_CLOSE;
+	sock->state = SS_UNCONNECTED;
+
+	if (addr_len != sizeof(struct sockaddr_rose))
+		return -EINVAL;
+
+	if (addr->srose_family != AF_ROSE)
+		return -EINVAL;
+
+	if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL)
+		return -ENETUNREACH;
+
+	if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0)
+		return -ENETUNREACH;
+
+	if (sk->zapped) {	/* Must bind first - autobinding in this may or may not work */
+		sk->zapped = 0;
+
+		if ((dev = rose_dev_first()) == NULL)
+			return -ENETUNREACH;
+
+		if ((user = ax25_findbyuid(current->euid)) == NULL)
+			return -EINVAL;
+
+		memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN);
+		sk->protinfo.rose->source_call = *user;
+		sk->protinfo.rose->device      = dev;
+
+		rose_insert_socket(sk);		/* Finish the bind */
+	}
+
+	sk->protinfo.rose->dest_addr = addr->srose_addr;
+	sk->protinfo.rose->dest_call = addr->srose_call;
+	sk->protinfo.rose->rand      = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci;
+
+	if (addr->srose_ndigis == 1) {
+		sk->protinfo.rose->dest_ndigis = 1;
+		sk->protinfo.rose->dest_digi   = addr->srose_digi;
+	}
+
+	/* Move to connecting socket, start sending Connect Requests */
+	sock->state   = SS_CONNECTING;
+	sk->state     = TCP_SYN_SENT;
+
+	sk->protinfo.rose->state = ROSE_STATE_1;
+
+	sk->protinfo.rose->neighbour->use++;
+
+	sk->protinfo.rose->timer = sk->protinfo.rose->t1;
+	rose_write_internal(sk, ROSE_CALL_REQUEST);
+
+	rose_set_timer(sk);
+
+	/* Now the loop */
+	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+		return -EINPROGRESS;
+
+	cli();	/* To avoid races on the sleep */
+
+	/*
+	 * A Connect Ack with Choke or timeout or failed routing will go to closed.
+	 */
+	while (sk->state == TCP_SYN_SENT) {
+		interruptible_sleep_on(sk->sleep);
+		if (current->signal & ~current->blocked) {
+			sti();
+			return -ERESTARTSYS;
+		}
+	}
+
+	if (sk->state != TCP_ESTABLISHED) {
+		sti();
+		sock->state = SS_UNCONNECTED;
+		return sock_error(sk);	/* Always set at this point */
+	}
+
+	sock->state = SS_CONNECTED;
+
+	sti();
+
+	return 0;
+}
+
+static int rose_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct sock *sk;
+	struct sock *newsk;
+	struct sk_buff *skb;
+
+	if (newsock->data != NULL)
+		rose_destroy_socket((struct sock *)newsock->data);
+
+	newsock->data = NULL;
+
+	if ((sk = (struct sock *)sock->data) == NULL)
+		return -EINVAL;
+
+	if (sk->type != SOCK_SEQPACKET)
+		return -EOPNOTSUPP;
+
+	if (sk->state != TCP_LISTEN)
+		return -EINVAL;
+
+	/*
+	 *	The write queue this time is holding sockets ready to use
+	 *	hooked into the SABM we saved
+	 */
+	do {
+		cli();
+		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
+			if (flags & O_NONBLOCK) {
+				sti();
+				return -EWOULDBLOCK;
+			}
+			interruptible_sleep_on(sk->sleep);
+			if (current->signal & ~current->blocked) {
+				sti();
+				return -ERESTARTSYS;
+			}
+		}
+	} while (skb == NULL);
+
+	newsk = skb->sk;
+	newsk->pair = NULL;
+	sti();
+
+	/* Now attach up the new socket */
+	skb->sk = NULL;
+	kfree_skb(skb, FREE_READ);
+	sk->ack_backlog--;
+	newsock->data = newsk;
+
+	return 0;
+}
+
+static int rose_getname(struct socket *sock, struct sockaddr *uaddr,
+	int *uaddr_len, int peer)
+{
+	struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr;
+	struct sock *sk = (struct sock *)sock->data;
+
+	if (peer != 0) {
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+		srose->srose_family = AF_ROSE;
+		srose->srose_ndigis = 0;
+		srose->srose_addr   = sk->protinfo.rose->dest_addr;
+		srose->srose_call   = sk->protinfo.rose->dest_call;
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+		*uaddr_len = sizeof(struct sockaddr_rose);
+	} else {
+		srose->srose_family = AF_ROSE;
+		srose->srose_ndigis = 0;
+		srose->srose_addr   = sk->protinfo.rose->source_addr;
+		srose->srose_call   = sk->protinfo.rose->source_call;
+		if (sk->protinfo.rose->source_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->source_digi;
+		}
+		*uaddr_len = sizeof(struct sockaddr_rose);
+	}
+
+	return 0;
+}
+
+int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_neigh *neigh, unsigned int lci)
+{
+	struct sock *sk;
+	struct sock *make;
+	struct rose_facilities facilities;
+
+	skb->sk = NULL;		/* Initially we don't know who it's for */
+
+	/*
+	 *	skb->data points to the rose frame start
+	 */
+	if (!rose_parse_facilities(skb, &facilities)) {
+		rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76);
+		return 0;
+	}
+
+	sk = rose_find_listener(&facilities.source_addr, &facilities.source_call);
+
+	/*
+	 * We can't accept the Call Request.
+	 */
+	if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = rose_make_new(sk)) == NULL) {
+		rose_transmit_clear_request(neigh, lci, ROSE_NETWORK_CONGESTION, 120);
+		return 0;
+	}
+
+	skb->sk     = make;
+	make->state = TCP_ESTABLISHED;
+
+	make->protinfo.rose->lci           = lci;
+	make->protinfo.rose->dest_addr     = facilities.dest_addr;
+	make->protinfo.rose->dest_call     = facilities.dest_call;
+	make->protinfo.rose->dest_ndigis   = facilities.dest_ndigis;
+	make->protinfo.rose->dest_digi     = facilities.dest_digi;
+	make->protinfo.rose->source_addr   = facilities.source_addr;
+	make->protinfo.rose->source_call   = facilities.source_call;
+	make->protinfo.rose->source_ndigis = facilities.source_ndigis;
+	make->protinfo.rose->source_digi   = facilities.source_digi;
+	make->protinfo.rose->neighbour     = neigh;
+	make->protinfo.rose->device        = dev;
+
+	make->protinfo.rose->neighbour->use++;
+
+	if (sk->protinfo.rose->defer) {
+		make->protinfo.rose->state = ROSE_STATE_5;
+	} else {
+		rose_write_internal(make, ROSE_CALL_ACCEPTED);
+		make->protinfo.rose->state = ROSE_STATE_3;
+	}
+
+	make->protinfo.rose->condition = 0x00;
+	make->protinfo.rose->vs        = 0;
+	make->protinfo.rose->va        = 0;
+	make->protinfo.rose->vr        = 0;
+	make->protinfo.rose->vl        = 0;
+	sk->ack_backlog++;
+	make->pair = sk;
+
+	rose_insert_socket(make);
+
+	skb_queue_head(&sk->receive_queue, skb);
+
+	rose_set_timer(make);
+
+	if (!sk->dead)
+		sk->data_ready(sk, skb->len);
+
+	return 1;
+}
+
+static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name;
+	int err;
+	struct sockaddr_rose srose;
+	struct sk_buff *skb;
+	unsigned char *asmptr;
+	int size, qbit = 0;
+
+	if (sk->err)
+		return sock_error(sk);
+
+	if (flags)
+		return -EINVAL;
+
+	if (sk->zapped)
+		return -EADDRNOTAVAIL;
+
+	if (sk->shutdown & SEND_SHUTDOWN) {
+		send_sig(SIGPIPE, current, 0);
+		return -EPIPE;
+	}
+
+	if (sk->protinfo.rose->neighbour == NULL || sk->protinfo.rose->device == NULL)
+		return -ENETUNREACH;
+
+	if (usrose != NULL) {
+		if (msg->msg_namelen < sizeof(srose))
+			return -EINVAL;
+		srose = *usrose;
+		if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 ||
+		    ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0)
+			return -EISCONN;
+		if (srose.srose_ndigis == 1 && sk->protinfo.rose->dest_ndigis == 1) {
+			if (ax25cmp(&sk->protinfo.rose->dest_digi, &srose.srose_digi) != 0)
+				return -EISCONN;
+		}
+		if (srose.srose_family != AF_ROSE)
+			return -EINVAL;
+	} else {
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+
+		srose.srose_family = AF_ROSE;
+		srose.srose_addr   = sk->protinfo.rose->dest_addr;
+		srose.srose_call   = sk->protinfo.rose->dest_call;
+		srose.srose_ndigis = 0;
+
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose.srose_ndigis = 1;
+			srose.srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+	}
+
+	if (sk->debug)
+		printk("ROSE: sendto: Addresses built.\n");
+
+	/* Build a packet */
+	if (sk->debug)
+		printk("ROSE: sendto: building packet.\n");
+
+	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
+
+	if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+		return err;
+
+	skb->sk   = sk;
+	skb->free = 1;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN);
+
+	/*
+	 *	Put the data on the end
+	 */
+	asmptr = skb->h.raw = skb_put(skb, len);
+
+	if (sk->debug)
+		printk("ROSE: Appending user data\n");
+
+	/* User data follows immediately after the ROSE transport header */
+	memcpy_fromiovec(asmptr, msg->msg_iov, len);
+
+	/*
+	 *	If the Q BIT Include socket option is in force, the first
+	 *	byte of the user data is the logical value of the Q Bit.
+	 */
+	if (sk->protinfo.rose->qbitincl) {
+		qbit = skb->data[0];
+		skb_pull(skb, 1);
+	}
+
+	/*
+	 *	Push down the ROSE header
+	 */
+	asmptr = skb_push(skb, ROSE_MIN_LEN);
+
+	if (sk->debug)
+		printk("Building ROSE Header.\n");
+
+	/* Build a ROSE Network header */
+	asmptr[0] = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI;
+	asmptr[1] = (sk->protinfo.rose->lci >> 0) & 0xFF;
+	asmptr[2] = ROSE_DATA;
+
+	if (qbit)
+		asmptr[0] |= ROSE_Q_BIT;
+
+	if (sk->debug)
+		printk("Built header.\n");
+
+	if (sk->debug)
+		printk("ROSE: Transmitting buffer\n");
+
+	if (sk->state != TCP_ESTABLISHED) {
+		kfree_skb(skb, FREE_WRITE);
+		return -ENOTCONN;
+	}
+
+	skb_queue_tail(&sk->write_queue, skb);	/* Shove it onto the queue */
+
+	if (sk->protinfo.rose->state == ROSE_STATE_3)
+		rose_kick(sk);
+
+	return len;
+}
+
+
+static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
+		   int flags, int *addr_len)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name;
+	int copied, qbit;
+	unsigned char *asmptr;
+	struct sk_buff *skb;
+	int er;
+
+	if (sk->err)
+		return sock_error(sk);
+	
+	if (addr_len != NULL)
+		*addr_len = sizeof(*srose);
+
+	/*
+	 * This works for seqpacket too. The receiver has ordered the queue for
+	 * us! We do one quick check first though
+	 */
+	if (sk->state != TCP_ESTABLISHED)
+		return -ENOTCONN;
+
+	/* Now we can treat all alike */
+	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+		return er;
+
+	qbit = (skb->data[0] & ROSE_Q_BIT) == ROSE_Q_BIT;
+
+	skb_pull(skb, ROSE_MIN_LEN);
+
+	if (sk->protinfo.rose->qbitincl) {
+		asmptr  = skb_push(skb, 1);
+		*asmptr = qbit;
+	}
+
+	skb->h.raw = skb->data;
+
+	copied = (size < skb->len) ? size : skb->len;
+	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+	if (srose != NULL) {
+		srose->srose_family = AF_ROSE;
+		srose->srose_addr   = sk->protinfo.rose->dest_addr;
+		srose->srose_call   = sk->protinfo.rose->dest_call;
+		srose->srose_ndigis = 0;
+
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			srose->srose_ndigis = 1;
+			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		}
+
+		*addr_len = sizeof(*srose);
+	}
+
+	skb_free_datagram(sk, skb);
+
+	return copied;
+}
+
+static int rose_shutdown(struct socket *sk, int how)
+{
+	return -EOPNOTSUPP;
+}
+
+static int rose_select(struct socket *sock , int sel_type, select_table *wait)
+{
+	struct sock *sk = (struct sock *)sock->data;
+
+	return datagram_select(sk, sel_type, wait);
+}
+
+static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = (struct sock *)sock->data;
+	int err;
+
+	switch (cmd) {
+		case TIOCOUTQ: {
+			long amount;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0)
+				return err;
+			amount = sk->sndbuf - sk->wmem_alloc;
+			if (amount < 0)
+				amount = 0;
+			put_fs_long(amount, (unsigned int *)arg);
+			return 0;
+		}
+
+		case TIOCINQ: {
+			struct sk_buff *skb;
+			long amount = 0L;
+			/* These two are safe on a single CPU system as only user tasks fiddle here */
+			if ((skb = skb_peek(&sk->receive_queue)) != NULL)
+				amount = skb->len;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0)
+				return err;
+			put_fs_long(amount, (unsigned int *)arg);
+			return 0;
+		}
+
+		case SIOCGSTAMP:
+			if (sk != NULL) {
+				if (sk->stamp.tv_sec == 0)
+					return -ENOENT;
+				if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct timeval))) != 0)
+					return err;
+				memcpy_tofs((void *)arg, &sk->stamp, sizeof(struct timeval));
+				return 0;
+			}
+			return -EINVAL;
+
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+		case SIOCGIFBRDADDR:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFMETRIC:
+		case SIOCSIFMETRIC:
+			return -EINVAL;
+
+		case SIOCADDRT:
+		case SIOCDELRT:
+		case SIOCRSCLRRT:
+			if (!suser()) return -EPERM;
+			return rose_rt_ioctl(cmd, (void *)arg);
+
+		case SIOCRSGCAUSE: {
+			struct rose_cause_struct rose_cause;
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct rose_cause_struct))) != 0)
+				return err;
+			rose_cause.cause      = sk->protinfo.rose->cause;
+			rose_cause.diagnostic = sk->protinfo.rose->diagnostic;
+			memcpy_tofs((void *)arg, &rose_cause, sizeof(struct rose_cause_struct));
+			return 0;
+		}
+
+		case SIOCRSSCAUSE: {
+			struct rose_cause_struct rose_cause;
+			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(struct rose_cause_struct))) != 0)
+				return err;
+			memcpy_fromfs(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct));
+			sk->protinfo.rose->cause      = rose_cause.cause;
+			sk->protinfo.rose->diagnostic = rose_cause.diagnostic;
+			return 0;
+		}
+
+		case SIOCRSL2CALL:
+			if (!suser()) return -EPERM;
+			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0)
+				return err;
+			if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+				ax25_listen_release(&rose_callsign, NULL);
+			memcpy_fromfs(&rose_callsign, (void *)arg, sizeof(ax25_address));
+			if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+				ax25_listen_register(&rose_callsign, NULL);
+			return 0;
+
+		case SIOCRSACCEPT:
+			if (sk->protinfo.rose->state == ROSE_STATE_5) {
+				rose_write_internal(sk, ROSE_CALL_ACCEPTED);
+				sk->protinfo.rose->condition = 0x00;
+				sk->protinfo.rose->vs        = 0;
+				sk->protinfo.rose->va        = 0;
+				sk->protinfo.rose->vr        = 0;
+				sk->protinfo.rose->vl        = 0;
+				sk->protinfo.rose->state     = ROSE_STATE_3;
+			}
+			return 0;
+
+		default:
+			return dev_ioctl(cmd, (void *)arg);
+	}
+
+	/*NOTREACHED*/
+	return 0;
+}
+
+static int rose_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct sock *s;
+	struct device *dev;
+	const char *devname, *callsign;
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "dest_addr  dest_call src_addr   src_call  dev   lci st vs vr va   t  t1  t2  t3  hb    idle Snd-Q Rcv-Q Inode\n");
+
+	for (s = rose_list; s != NULL; s = s->next) {
+		if ((dev = s->protinfo.rose->device) == NULL)
+			devname = "???";
+		else
+			devname = dev->name;
+
+		len += sprintf(buffer + len, "%-10s %-9s ",
+			rose2asc(&s->protinfo.rose->dest_addr),
+			ax2asc(&s->protinfo.rose->dest_call));
+
+		if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0)
+			callsign = "??????-?";
+		else
+			callsign = ax2asc(&s->protinfo.rose->source_call);
+
+		len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X  %d  %d  %d  %d %3d %3d %3d %3d %3d %3d/%03d %5d %5d %ld\n",
+			rose2asc(&s->protinfo.rose->source_addr), callsign,
+			devname,  s->protinfo.rose->lci & 0x0FFF,
+			s->protinfo.rose->state,
+			s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va,
+			s->protinfo.rose->timer / ROSE_SLOWHZ,
+			s->protinfo.rose->t1    / ROSE_SLOWHZ,
+			s->protinfo.rose->t2    / ROSE_SLOWHZ,
+			s->protinfo.rose->t3    / ROSE_SLOWHZ,
+			s->protinfo.rose->hb    / ROSE_SLOWHZ,
+			0, 0,
+			s->wmem_alloc, s->rmem_alloc,
+			s->socket && SOCK_INODE(s->socket) ?
+			SOCK_INODE(s->socket)->i_ino : 0);
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return(len);
+}
+
+static struct proto_ops rose_proto_ops = {
+	AF_ROSE,
+
+	rose_create,
+	rose_dup,
+	rose_release,
+	rose_bind,
+	rose_connect,
+	rose_socketpair,
+	rose_accept,
+	rose_getname,
+	rose_select,
+	rose_ioctl,
+	rose_listen,
+	rose_shutdown,
+	rose_setsockopt,
+	rose_getsockopt,
+	rose_fcntl,
+	rose_sendmsg,
+	rose_recvmsg
+};
+
+static struct notifier_block rose_dev_notifier = {
+	rose_device_event,
+	0
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_rose = {
+	PROC_NET_RS, 4, "rose",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_get_info
+};
+static struct proc_dir_entry proc_net_rose_neigh = {
+	PROC_NET_RS_NEIGH, 10, "rose_neigh",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_neigh_get_info
+};
+static struct proc_dir_entry proc_net_rose_nodes = {
+	PROC_NET_RS_NODES, 10, "rose_nodes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_nodes_get_info
+};
+static struct proc_dir_entry proc_net_rose_routes = {
+	PROC_NET_RS_ROUTES, 11, "rose_routes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	rose_routes_get_info
+};
+#endif	
+
+static struct device dev_rose[] = {
+	{"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
+};
+
+void rose_proto_init(struct net_proto *pro)
+{
+	int i;
+
+	rose_callsign = null_ax25_address;
+
+	sock_register(rose_proto_ops.family, &rose_proto_ops);
+	register_netdevice_notifier(&rose_dev_notifier);
+	printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.3 for AX25.035 Linux 2.0\n");
+
+	if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame))
+		printk(KERN_ERR "ROSE: unable to register protocol with AX.25\n");
+	if (!ax25_linkfail_register(rose_link_failed))
+		printk(KERN_ERR "ROSE: unable to register linkfail handler with AX.25\n");
+
+	for (i = 0; i < 6; i++)
+		register_netdev(&dev_rose[i]);
+
+	rose_register_sysctl();
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_rose);
+	proc_net_register(&proc_net_rose_neigh);
+	proc_net_register(&proc_net_rose_nodes);
+	proc_net_register(&proc_net_rose_routes);
+#endif
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	rose_proto_init(NULL);
+	
+	register_symtab(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_RS);
+	proc_net_unregister(PROC_NET_RS_NEIGH);
+	proc_net_unregister(PROC_NET_RS_NODES);
+	proc_net_unregister(PROC_NET_RS_ROUTES);
+#endif
+	rose_rt_free();
+
+	ax25_protocol_release(AX25_P_ROSE);
+	ax25_linkfail_release(rose_link_failed);
+
+	if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+		ax25_listen_release(&rose_callsign, NULL);
+
+	rose_unregister_sysctl();
+
+	unregister_netdevice_notifier(&rose_dev_notifier);
+
+	sock_unregister(AF_ROSE);
+
+	for (i = 0; i < 6; i++) {
+		if (dev_rose[i].priv != NULL) {
+			kfree(dev_rose[i].priv);
+			dev_rose[i].priv = NULL;
+			unregister_netdev(&dev_rose[i]);
+		}
+	}
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- v2.0.34/linux/net/rose/rose_dev.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_dev.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,238 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_dev.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/proc_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>	/* For the statistics structure. */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <net/ax25.h>
+#include <net/rose.h>
+
+/*
+ *	Only allow IP over ROSE frames through if the netrom device is up.
+ */
+
+int rose_rx_ip(struct sk_buff *skb, struct device *dev)
+{
+	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+
+	if (!dev->start) {
+		stats->rx_errors++;
+		return 0;
+	}
+
+	stats->rx_packets++;
+	skb->protocol = htons(ETH_P_IP);
+
+	/* Spoof incoming device */
+	skb->dev = dev;
+
+	skb->h.raw = skb->data;
+	ip_rcv(skb, skb->dev, NULL);
+
+	return 1;
+}
+
+static int rose_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+	void *daddr, void *saddr, unsigned len)
+{
+	unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2);
+
+	*buff++ = ROSE_GFI | ROSE_Q_BIT;
+	*buff++ = 0x00;
+	*buff++ = ROSE_DATA;
+	*buff++ = 0x7F;
+	*buff++ = AX25_P_IP;
+
+	if (daddr != NULL)
+		return 37;
+
+	return -37;
+}
+
+static int rose_rebuild_header(void *buff, struct device *dev,
+	unsigned long raddr, struct sk_buff *skb)
+{
+	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+	unsigned char *bp = (unsigned char *)buff;
+	struct sk_buff *skbn;
+
+	if (!arp_query(bp + 7, raddr, dev)) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 1;
+	}
+
+	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 1;
+	}
+
+	skbn->sk = skb->sk;
+
+	if (skbn->sk != NULL)
+		atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	if (!rose_route_frame(skbn, NULL)) {
+		dev_kfree_skb(skbn, FREE_WRITE);
+		stats->tx_errors++;
+	}
+
+	stats->tx_packets++;
+
+	return 1;
+}
+
+static int rose_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+
+	return 0;
+}
+
+static int rose_open(struct device *dev)
+{
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+
+	return 0;
+}
+
+static int rose_close(struct device *dev)
+{
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
+	return 0;
+}
+
+static int rose_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+
+	if (skb == NULL || dev == NULL)
+		return 0;
+
+	if (!dev->start) {
+		printk(KERN_ERR "rose: xmit call when iface is down\n");
+		return 1;
+	}
+
+	cli();
+
+	if (dev->tbusy != 0) {
+		sti();
+		stats->tx_errors++;
+		return 1;
+	}
+
+	dev->tbusy = 1;
+
+	sti();
+
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	stats->tx_errors++;
+
+	dev->tbusy = 0;
+
+	mark_bh(NET_BH);
+
+	return 0;
+}
+
+static struct enet_statistics *rose_get_stats(struct device *dev)
+{
+	return (struct enet_statistics *)dev->priv;
+}
+
+int rose_init(struct device *dev)
+{
+	int i;
+
+	dev->mtu		= ROSE_MAX_PACKET_SIZE - 2;
+	dev->tbusy		= 0;
+	dev->hard_start_xmit	= rose_xmit;
+	dev->open		= rose_open;
+	dev->stop		= rose_close;
+
+	dev->hard_header	= rose_header;
+	dev->hard_header_len	= AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
+	dev->addr_len		= ROSE_ADDR_LEN;
+	dev->type		= ARPHRD_ROSE;
+	dev->rebuild_header	= rose_rebuild_header;
+	dev->set_mac_address    = rose_set_mac_address;
+
+	/* New-style flags. */
+	dev->flags		= 0;
+	dev->family		= AF_INET;
+
+#ifdef CONFIG_INET
+	dev->pa_addr		= in_aton("192.168.0.1");
+	dev->pa_brdaddr		= in_aton("192.168.0.255");
+	dev->pa_mask		= in_aton("255.255.255.0");
+	dev->pa_alen		= 4;
+#endif
+
+	if ((dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	memset(dev->priv, 0, sizeof(struct enet_statistics));
+
+	dev->get_stats = rose_get_stats;
+
+	/* Fill in the generic fields of the device structure. */
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	return 0;
+};
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/rose_in.c linux/net/rose/rose_in.c
--- v2.0.34/linux/net/rose/rose_in.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_in.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,343 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	Most of this code is based on the SDL diagrams published in the 7th
+ *	ARRL Computer Networking Conference papers. The diagrams have mistakes
+ *	in them, but are mostly correct. Before you modify the code could you
+ *	read the SDL diagrams as the code is not obvious and probably very
+ *	easy to break;
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_in.c
+ *	ROSE 003	Jonathan(G4KLX)	Removed M bit processing.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h>			/* For ip_rcv */
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+/*
+ * State machine for state 1, Awaiting Call Accepted State.
+ * The handling of the timer(s) is in file rose_timer.c.
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+	switch (frametype) {
+
+		case ROSE_CALL_ACCEPTED:
+			sk->protinfo.rose->condition = 0x00;
+			sk->protinfo.rose->timer     = 0;
+			sk->protinfo.rose->vs        = 0;
+			sk->protinfo.rose->va        = 0;
+			sk->protinfo.rose->vr        = 0;
+			sk->protinfo.rose->vl        = 0;
+			sk->protinfo.rose->state     = ROSE_STATE_3;
+			sk->state                    = TCP_ESTABLISHED;
+			if (!sk->dead)
+				sk->state_change(sk);
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->cause      = skb->data[3];
+			sk->protinfo.rose->diagnostic = skb->data[4];
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = ECONNREFUSED;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Clear Confirmation State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+	switch (frametype) {
+
+		case ROSE_CLEAR_REQUEST:
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+			sk->protinfo.rose->cause      = skb->data[3];
+			sk->protinfo.rose->diagnostic = skb->data[4];
+		case ROSE_CLEAR_CONFIRMATION:
+			rose_clear_queues(sk);
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m)
+{
+	int queued = 0;
+
+	switch (frametype) {
+
+		case ROSE_RESET_REQUEST:
+			rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
+			sk->protinfo.rose->condition = 0x00;
+			sk->protinfo.rose->timer     = 0;
+			sk->protinfo.rose->vs        = 0;
+			sk->protinfo.rose->vr        = 0;
+			sk->protinfo.rose->va        = 0;
+			sk->protinfo.rose->vl        = 0;
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->cause      = skb->data[3];
+			sk->protinfo.rose->diagnostic = skb->data[4];
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		case ROSE_RR:
+		case ROSE_RNR:
+			if (frametype == ROSE_RNR)
+				sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY;
+			else
+				sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
+			if (!rose_validate_nr(sk, nr)) {
+				rose_clear_queues(sk);
+				rose_write_internal(sk, ROSE_RESET_REQUEST);
+				sk->protinfo.rose->condition = 0x00;
+				sk->protinfo.rose->vs        = 0;
+				sk->protinfo.rose->vr        = 0;
+				sk->protinfo.rose->va        = 0;
+				sk->protinfo.rose->vl        = 0;
+				sk->protinfo.rose->state     = ROSE_STATE_4;
+				sk->protinfo.rose->timer     = sk->protinfo.rose->t2;
+			} else {
+				if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
+					sk->protinfo.rose->va = nr;
+				} else {
+					rose_check_iframes_acked(sk, nr);
+				}
+			}
+			break;
+
+		case ROSE_DATA:	/* XXX */
+			sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
+			if (!rose_validate_nr(sk, nr)) {
+				rose_clear_queues(sk);
+				rose_write_internal(sk, ROSE_RESET_REQUEST);
+				sk->protinfo.rose->condition = 0x00;
+				sk->protinfo.rose->vs        = 0;
+				sk->protinfo.rose->vr        = 0;
+				sk->protinfo.rose->va        = 0;
+				sk->protinfo.rose->vl        = 0;
+				sk->protinfo.rose->state     = ROSE_STATE_4;
+				sk->protinfo.rose->timer     = sk->protinfo.rose->t2;
+				break;
+			}
+			if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
+				sk->protinfo.rose->va = nr;
+			} else {
+				rose_check_iframes_acked(sk, nr);
+			}
+			if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)
+				break;
+			if (ns == sk->protinfo.rose->vr) {
+				if (sock_queue_rcv_skb(sk, skb) == 0) {
+					sk->protinfo.rose->vr = (sk->protinfo.rose->vr + 1) % ROSE_MODULUS;
+					queued = 1;
+				} else {
+					sk->protinfo.rose->condition |= ROSE_COND_OWN_RX_BUSY;
+				}
+			}
+			/*
+			 * If the window is full, ack the frame, else start the
+			 * acknowledge hold back timer.
+			 */
+			if (((sk->protinfo.rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == sk->protinfo.rose->vr) {
+				sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+				sk->protinfo.rose->timer      = 0;
+				rose_enquiry_response(sk);
+			} else {
+				sk->protinfo.rose->condition |= ROSE_COND_ACK_PENDING;
+				sk->protinfo.rose->timer      = sk->protinfo.rose->hb;
+			}
+			break;
+
+		default:
+			printk(KERN_WARNING "rose: unknown %02X in state 3\n", frametype);
+			break;
+	}
+
+	return queued;
+}
+
+/*
+ * State machine for state 4, Awaiting Reset Confirmation State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+	switch (frametype) {
+
+		case ROSE_RESET_REQUEST:
+			rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
+		case ROSE_RESET_CONFIRMATION:
+			sk->protinfo.rose->timer     = 0;
+			sk->protinfo.rose->condition = 0x00;
+			sk->protinfo.rose->va        = 0;
+			sk->protinfo.rose->vr        = 0;
+			sk->protinfo.rose->vs        = 0;
+			sk->protinfo.rose->vl        = 0;
+			sk->protinfo.rose->state     = ROSE_STATE_3;
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->cause      = skb->data[3];
+			sk->protinfo.rose->diagnostic = skb->data[4];
+			sk->protinfo.rose->timer = 0;
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * State machine for state 5, Awaiting Call Acceptance State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+	switch (frametype) {
+
+		case ROSE_CLEAR_REQUEST:
+			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->cause      = skb->data[3];
+			sk->protinfo.rose->diagnostic = skb->data[4];
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+	}
+
+	return 0;
+}
+
+/* Higher level upcall for a LAPB frame */
+int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
+{
+	int queued = 0, frametype, ns, nr, q, d, m;
+
+	if (sk->protinfo.rose->state == ROSE_STATE_0)
+		return 0;
+
+	del_timer(&sk->timer);
+
+	frametype = rose_decode(skb, &ns, &nr, &q, &d, &m);
+
+	switch (sk->protinfo.rose->state) {
+		case ROSE_STATE_1:
+			queued = rose_state1_machine(sk, skb, frametype);
+			break;
+		case ROSE_STATE_2:
+			queued = rose_state2_machine(sk, skb, frametype);
+			break;
+		case ROSE_STATE_3:
+			queued = rose_state3_machine(sk, skb, frametype, ns, nr, q, d, m);
+			break;
+		case ROSE_STATE_4:
+			queued = rose_state4_machine(sk, skb, frametype);
+			break;
+		case ROSE_STATE_5:
+			queued = rose_state5_machine(sk, skb, frametype);
+			break;
+	}
+
+	rose_set_timer(sk);
+
+	return queued;
+}
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/rose_link.c linux/net/rose/rose_link.c
--- v2.0.34/linux/net/rose/rose_link.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_link.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,318 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from rose_timer.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/firewall.h>
+#include <net/rose.h>
+
+static void rose_link_timer(unsigned long);
+
+/*
+ *	Linux set timer
+ */
+void rose_link_set_timer(struct rose_neigh *neigh)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	del_timer(&neigh->timer);
+	restore_flags(flags);
+
+	neigh->timer.data     = (unsigned long)neigh;
+	neigh->timer.function = &rose_link_timer;
+	neigh->timer.expires  = jiffies + 10;
+
+	add_timer(&neigh->timer);
+}
+
+/*
+ *	ROSE Link Timer
+ *
+ *	This routine is called every 100ms. Decrement timer by this
+ *	amount - if expired then process the event.
+ */
+static void rose_link_timer(unsigned long param)
+{
+	struct rose_neigh *neigh = (struct rose_neigh *)param;
+
+	if (neigh->ftimer > 0)
+		neigh->ftimer--;
+
+	if (neigh->t0timer > 0) {
+		neigh->t0timer--;
+
+		if (neigh->t0timer == 0) {
+			rose_transmit_restart_request(neigh);
+			neigh->dce_mode = 0;
+			neigh->t0timer  = sysctl_rose_restart_request_timeout;
+		}
+	}
+
+	if (neigh->ftimer > 0 || neigh->t0timer > 0)
+		rose_link_set_timer(neigh);
+	else
+		del_timer(&neigh->timer);
+}
+
+/*
+ *	Interface to ax25_send_frame. Changes my level 2 callsign depending
+ *	on whether we have a global ROSE callsign or use the default port
+ *	callsign.
+ */
+static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
+{
+	ax25_address *rose_call;
+
+	if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
+		rose_call = (ax25_address *)neigh->dev->dev_addr;
+	else
+		rose_call = &rose_callsign;
+
+	neigh->ax25 = ax25_send_frame(skb, 0, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+
+	return (neigh->ax25 != NULL);
+}
+
+/*
+ *	Interface to ax25_link_up. Changes my level 2 callsign depending
+ *	on whether we have a global ROSE callsign or use the default port
+ *	callsign.
+ */
+static int rose_link_up(struct rose_neigh *neigh)
+{
+	ax25_address *rose_call;
+
+	if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
+		rose_call = (ax25_address *)neigh->dev->dev_addr;
+	else
+		rose_call = &rose_callsign;
+
+	neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+
+	return (neigh->ax25 != NULL);
+}
+
+/*
+ *	This handles all restart and diagnostic frames.
+ */
+void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigned short frametype)
+{
+	struct sk_buff *skbn;
+
+	switch (frametype) {
+		case ROSE_RESTART_REQUEST:
+			neigh->t0timer   = 0;
+			neigh->restarted = 1;
+			neigh->dce_mode  = (skb->data[3] == ROSE_DTE_ORIGINATED);
+			del_timer(&neigh->timer);
+			rose_transmit_restart_confirmation(neigh);
+			break;
+
+		case ROSE_RESTART_CONFIRMATION:
+			neigh->t0timer   = 0;
+			neigh->restarted = 1;
+			del_timer(&neigh->timer);
+			break;
+
+		case ROSE_DIAGNOSTIC:
+			printk(KERN_WARNING "rose: diagnostic #%d - %02X %02X %02X\n", skb->data[3], skb->data[4], skb->data[5], skb->data[6]);
+			break;
+
+		default:
+			printk(KERN_WARNING "rose: received unknown %02X with LCI 000\n", frametype);
+			break;
+	}
+
+	if (neigh->restarted) {
+		while ((skbn = skb_dequeue(&neigh->queue)) != NULL)
+			if (!rose_send_frame(skbn, neigh))
+				kfree_skb(skbn, FREE_WRITE);
+	}
+}
+
+/*
+ *	This routine is called when a Restart Request is needed
+ */
+void rose_transmit_restart_request(struct rose_neigh *neigh)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb->free = 1;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 3);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = ROSE_GFI;
+	*dptr++ = 0x00;
+	*dptr++ = ROSE_RESTART_REQUEST;
+	*dptr++ = ROSE_DTE_ORIGINATED;
+	*dptr++ = 0;
+
+	if (!rose_send_frame(skb, neigh))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine is called when a Restart Confirmation is needed
+ */
+void rose_transmit_restart_confirmation(struct rose_neigh *neigh)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb->free = 1;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 1);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = ROSE_GFI;
+	*dptr++ = 0x00;
+	*dptr++ = ROSE_RESTART_CONFIRMATION;
+
+	if (!rose_send_frame(skb, neigh))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine is called when a Diagnostic is required.
+ */
+void rose_transmit_diagnostic(struct rose_neigh *neigh, unsigned char diag)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 2;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb->free = 1;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 2);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = ROSE_GFI;
+	*dptr++ = 0x00;
+	*dptr++ = ROSE_DIAGNOSTIC;
+	*dptr++ = diag;
+
+	if (!rose_send_frame(skb, neigh))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine is called when a Clear Request is needed outside of the context
+ * of a connected socket.
+ */
+void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause, unsigned char diagnostic)
+{
+	struct sk_buff *skb;
+	unsigned char *dptr;
+	int len;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb->free = 1;
+
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
+
+	dptr = skb_put(skb, ROSE_MIN_LEN + 3);
+
+	*dptr++ = AX25_P_ROSE;
+	*dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI;
+	*dptr++ = ((lci >> 0) & 0xFF);
+	*dptr++ = ROSE_CLEAR_REQUEST;
+	*dptr++ = cause;
+	*dptr++ = diagnostic;
+
+	if (!rose_send_frame(skb, neigh))
+		kfree_skb(skb, FREE_WRITE);
+}
+
+void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
+{
+	unsigned char *dptr;
+
+#ifdef CONFIG_FIREWALL
+	if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+		return;
+#endif
+
+	if (!rose_link_up(neigh))
+		neigh->restarted = 0;
+
+	dptr = skb_push(skb, 1);
+	*dptr++ = AX25_P_ROSE;
+
+	if (neigh->restarted) {
+		if (!rose_send_frame(skb, neigh))
+			kfree_skb(skb, FREE_WRITE);
+	} else {
+		skb_queue_tail(&neigh->queue, skb);
+
+		if (neigh->t0timer == 0) {
+			rose_transmit_restart_request(neigh);
+			neigh->dce_mode = 0;
+			neigh->t0timer  = sysctl_rose_restart_request_timeout;
+			rose_link_set_timer(neigh);
+		}
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/rose_out.c linux/net/rose/rose_out.c
--- v2.0.34/linux/net/rose/rose_out.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_out.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,120 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_out.c
+ *	ROSE 003	Jonathan(G4KLX)	Removed M bit processing.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+/* 
+ *	This procedure is passed a buffer descriptor for an iframe. It builds
+ *	the rest of the control part of the frame and then writes it out.
+ */
+static void rose_send_iframe(struct sock *sk, struct sk_buff *skb)
+{
+	if (skb == NULL)
+		return;
+
+	skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0;
+	skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E;
+
+	rose_transmit_link(skb, sk->protinfo.rose->neighbour);	
+}
+
+void rose_kick(struct sock *sk)
+{
+	struct sk_buff *skb;
+	unsigned short end;
+
+	del_timer(&sk->timer);
+
+	end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS;
+
+	if (!(sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) &&
+	    sk->protinfo.rose->vs != end                             &&
+	    skb_peek(&sk->write_queue) != NULL) {
+		/*
+		 * Transmit data until either we're out of data to send or
+		 * the window is full.
+		 */
+
+		skb  = skb_dequeue(&sk->write_queue);
+
+		do {
+			/*
+			 * Transmit the frame.
+			 */
+			rose_send_iframe(sk, skb);
+
+			sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
+
+		} while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+
+		sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
+		sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+		sk->protinfo.rose->timer      = 0;
+	}
+
+	rose_set_timer(sk);
+}
+
+/*
+ * The following routines are taken from page 170 of the 7th ARRL Computer
+ * Networking Conference paper, as is the whole state machine.
+ */
+
+void rose_enquiry_response(struct sock *sk)
+{
+	if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)
+		rose_write_internal(sk, ROSE_RNR);
+	else
+		rose_write_internal(sk, ROSE_RR);
+
+	sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
+	sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+	sk->protinfo.rose->timer      = 0;
+}
+
+void rose_check_iframes_acked(struct sock *sk, unsigned short nr)
+{
+	if (sk->protinfo.rose->vs == nr) {
+		sk->protinfo.rose->va = nr;
+	} else {
+		if (sk->protinfo.rose->va != nr)
+			sk->protinfo.rose->va = nr;
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.0.34/linux/net/rose/rose_route.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_route.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,999 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_route.c.
+ *			Terry(VK2KTJ)	Added support for variable length
+ *					address masks.
+ *	ROSE 002	Jonathan(G4KLX)	Uprated through routing of packets.
+ *					Routing loop detection.
+ *	ROSE 003	Jonathan(G4KLX)	Added use count to neighbours.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/arp.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/firewall.h>
+#include <net/rose.h>
+
+static unsigned int rose_neigh_no = 1;
+
+static struct rose_node  *rose_node_list  = NULL;
+static struct rose_neigh *rose_neigh_list = NULL;
+static struct rose_route *rose_route_list = NULL;
+
+static void rose_remove_neigh(struct rose_neigh *);
+
+/*
+ *	Add a new route to a node, and in the process add the node and the
+ *	neighbour if it is new.
+ */
+static int rose_add_node(struct rose_route_struct *rose_route, struct device *dev)
+{
+	struct rose_node  *rose_node, *rose_tmpn, *rose_tmpp;
+	struct rose_neigh *rose_neigh;
+	unsigned long flags;
+	int i;
+
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
+		if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0))
+			break;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev)
+			break;
+
+	if (rose_neigh == NULL) {
+		if ((rose_neigh = (struct rose_neigh *)kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL)
+			return -ENOMEM;
+
+		rose_neigh->callsign  = rose_route->neighbour;
+		rose_neigh->digipeat  = NULL;
+		rose_neigh->ax25      = NULL;
+		rose_neigh->dev       = dev;
+		rose_neigh->count     = 0;
+		rose_neigh->use       = 0;
+		rose_neigh->dce_mode  = 0;
+		rose_neigh->number    = rose_neigh_no++;
+		rose_neigh->restarted = 0;
+		skb_queue_head_init(&rose_neigh->queue);
+		rose_neigh->t0timer   = 0;
+		rose_neigh->ftimer    = 0;
+		init_timer(&rose_neigh->timer);
+
+		if (rose_route->ndigis != 0) {
+			if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
+				kfree_s(rose_neigh, sizeof(*rose_neigh));
+				return -ENOMEM;
+			}
+
+			rose_neigh->digipeat->ndigi      = rose_route->ndigis;
+			rose_neigh->digipeat->lastrepeat = -1;
+
+			for (i = 0; i < rose_route->ndigis; i++) {
+				rose_neigh->digipeat->calls[i]    = rose_route->digipeaters[i];
+				rose_neigh->digipeat->repeated[i] = 0;
+			}
+		}
+
+		save_flags(flags); cli();
+		rose_neigh->next = rose_neigh_list;
+		rose_neigh_list  = rose_neigh;
+		restore_flags(flags);
+	}
+
+	/*
+	 * This is a new node to be inserted into the list. Find where it needs
+	 * to be inserted into the list, and insert it. We want to be sure
+	 * to order the list in descending order of mask size to ensure that
+	 * later when we are searching this list the first match will be the
+	 * best match.
+	 */
+	if (rose_node == NULL) {
+		rose_tmpn = rose_node_list;
+		rose_tmpp = NULL;
+
+		while (rose_tmpn != NULL) {
+			if (rose_tmpn->mask > rose_route->mask) {
+				rose_tmpp = rose_tmpn;
+				rose_tmpn = rose_tmpn->next;
+			} else {
+				break;
+			}
+		}
+
+		/* create new node */
+		if ((rose_node = (struct rose_node *)kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL)
+			return -ENOMEM;
+
+		rose_node->address = rose_route->address;
+		rose_node->mask    = rose_route->mask;
+		rose_node->count   = 1;
+		rose_node->neighbour[0] = rose_neigh;
+
+		save_flags(flags); cli();
+
+		if (rose_tmpn == NULL) {
+			if (rose_tmpp == NULL) {	/* Empty list */
+				rose_node_list  = rose_node;
+				rose_node->next = NULL;
+			} else {
+				rose_tmpp->next = rose_node;
+				rose_node->next = NULL;
+			}
+		} else {
+			if (rose_tmpp == NULL) {	/* 1st node */
+				rose_node->next = rose_node_list;
+				rose_node_list  = rose_node;
+			} else {
+				rose_tmpp->next = rose_node;
+				rose_node->next = rose_tmpn;
+			}
+		}
+
+		restore_flags(flags);
+
+		rose_neigh->count++;
+
+		return 0;
+	}
+
+	/* We have space, slot it in */
+	if (rose_node->count < 3) {
+		rose_node->neighbour[rose_node->count] = rose_neigh;
+		rose_node->count++;
+		rose_neigh->count++;
+	}
+
+	return 0;
+}
+
+static void rose_remove_node(struct rose_node *rose_node)
+{
+	struct rose_node *s;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_node_list) == rose_node) {
+		rose_node_list = rose_node->next;
+		restore_flags(flags);
+		kfree_s(rose_node, sizeof(struct rose_node));
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == rose_node) {
+			s->next = rose_node->next;
+			restore_flags(flags);
+			kfree_s(rose_node, sizeof(struct rose_node));
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+static void rose_remove_neigh(struct rose_neigh *rose_neigh)
+{
+	struct rose_neigh *s;
+	unsigned long flags;
+	struct sk_buff *skb;
+
+	del_timer(&rose_neigh->timer);
+
+	while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_neigh_list) == rose_neigh) {
+		rose_neigh_list = rose_neigh->next;
+		restore_flags(flags);
+		if (rose_neigh->digipeat != NULL)
+			kfree_s(rose_neigh->digipeat, sizeof(ax25_digi));
+		kfree_s(rose_neigh, sizeof(struct rose_neigh));
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == rose_neigh) {
+			s->next = rose_neigh->next;
+			restore_flags(flags);
+			if (rose_neigh->digipeat != NULL)
+				kfree_s(rose_neigh->digipeat, sizeof(ax25_digi));
+			kfree_s(rose_neigh, sizeof(struct rose_neigh));
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+static void rose_remove_route(struct rose_route *rose_route)
+{
+	struct rose_route *s;
+	unsigned long flags;
+
+	if (rose_route->neigh1 != NULL)
+		rose_route->neigh1->use--;
+
+	if (rose_route->neigh2 != NULL)
+		rose_route->neigh2->use--;
+
+	save_flags(flags);
+	cli();
+
+	if ((s = rose_route_list) == rose_route) {
+		rose_route_list = rose_route->next;
+		restore_flags(flags);
+		kfree_s(rose_route, sizeof(struct rose_route));
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == rose_route) {
+			s->next = rose_route->next;
+			restore_flags(flags);
+			kfree_s(rose_route, sizeof(struct rose_route));
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ *	"Delete" a node. Strictly speaking remove a route to a node. The node
+ *	is only deleted if no routes are left to it.
+ */
+static int rose_del_node(struct rose_route_struct *rose_route, struct device *dev)
+{
+	struct rose_node  *rose_node;
+	struct rose_neigh *rose_neigh;
+	int i;
+
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
+		if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0))
+			break;
+
+	if (rose_node == NULL) return -EINVAL;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev)
+			break;
+
+	if (rose_neigh == NULL) return -EINVAL;
+
+	for (i = 0; i < rose_node->count; i++) {
+		if (rose_node->neighbour[i] == rose_neigh) {
+			rose_neigh->count--;
+
+			if (rose_neigh->count == 0 && rose_neigh->use == 0)
+				rose_remove_neigh(rose_neigh);
+
+			rose_node->count--;
+
+			if (rose_node->count == 0) {
+				rose_remove_node(rose_node);
+			} else {
+				switch (i) {
+					case 0:
+						rose_node->neighbour[0] = rose_node->neighbour[1];
+					case 1:
+						rose_node->neighbour[1] = rose_node->neighbour[2];
+					case 2:
+						break;
+				}
+			}
+
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/*
+ *	A device has been removed. Remove its routes and neighbours.
+ */
+void rose_rt_device_down(struct device *dev)
+{
+	struct rose_neigh *s, *rose_neigh = rose_neigh_list;
+	struct rose_node  *t, *rose_node;
+	int i;
+
+	while (rose_neigh != NULL) {
+		s          = rose_neigh;
+		rose_neigh = rose_neigh->next;
+
+		if (s->dev == dev) {
+			rose_node = rose_node_list;
+
+			while (rose_node != NULL) {
+				t         = rose_node;
+				rose_node = rose_node->next;
+
+				for (i = 0; i < t->count; i++) {
+					if (t->neighbour[i] == s) {
+						t->count--;
+
+						switch (i) {
+							case 0:
+								t->neighbour[0] = t->neighbour[1];
+							case 1:
+								t->neighbour[1] = t->neighbour[2];
+							case 2:
+								break;
+						}
+					}
+				}
+
+				if (t->count <= 0)
+					rose_remove_node(t);
+			}
+
+			rose_remove_neigh(s);
+		}
+	}
+}
+
+/*
+ *	A device has been removed. Remove its links.
+ */
+void rose_route_device_down(struct device *dev)
+{
+	struct rose_route *s, *rose_route = rose_route_list;
+
+	while (rose_route != NULL) {
+		s          = rose_route;
+		rose_route = rose_route->next;
+
+		if (s->neigh1->dev == dev || s->neigh2->dev == dev)
+			rose_remove_route(s);
+	}
+}
+
+/*
+ *	Clear all nodes and neighbours out, except for neighbours with
+ *	active connections going through them.
+ */
+static int rose_clear_routes(void)
+{
+	struct rose_neigh *s, *rose_neigh = rose_neigh_list;
+	struct rose_node  *t, *rose_node  = rose_node_list;
+
+	while (rose_node != NULL) {
+		t         = rose_node;
+		rose_node = rose_node->next;
+
+		rose_remove_node(t);
+	}
+
+	while (rose_neigh != NULL) {
+		s          = rose_neigh;
+		rose_neigh = rose_neigh->next;
+
+		s->count = 0;
+
+		if (s->use == 0)
+			rose_remove_neigh(s);
+	}
+
+	return 0;
+}
+
+/*
+ *	Check that the device given is a valid AX.25 interface that is "up".
+ */
+struct device *rose_ax25_dev_get(char *devname)
+{
+	struct device *dev;
+
+	if ((dev = dev_get(devname)) == NULL)
+		return NULL;
+
+	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
+		return dev;
+
+	return NULL;
+}
+
+/*
+ *	Find the first active ROSE device, usually "rose0".
+ */
+struct device *rose_dev_first(void)
+{
+	struct device *dev, *first = NULL;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next)
+		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
+			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
+				first = dev;
+
+	return first;
+}
+
+/*
+ *	Find the ROSE device for the given address.
+ */
+struct device *rose_dev_get(rose_address *addr)
+{
+	struct device *dev;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next)
+		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
+			return dev;
+
+	return NULL;
+}
+
+struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neigh)
+{
+	struct rose_route *rose_route;
+
+	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next)
+		if ((rose_route->neigh1 == neigh && rose_route->lci1 == lci) ||
+		    (rose_route->neigh2 == neigh && rose_route->lci2 == lci))
+			return rose_route;
+
+	return NULL;
+}
+
+/*
+ *	Find a neighbour given a ROSE address.
+ */
+struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic)
+{
+	struct rose_node *node;
+	int failed = 0;
+	int i;
+
+	for (node = rose_node_list; node != NULL; node = node->next) {
+		if (rosecmpm(addr, &node->address, node->mask) == 0) {
+			for (i = 0; i < node->count; i++) {
+				if (node->neighbour[i]->ftimer == 0)
+					return node->neighbour[i];
+				else
+					failed = 1;
+			}
+			/* F6FBB : All nodes for this route are out of order */
+			break;
+		}
+	}
+
+	if (failed) {
+		*cause      = ROSE_OUT_OF_ORDER;
+		*diagnostic = 0;
+	} else {
+		*cause      = ROSE_NOT_OBTAINABLE;
+		*diagnostic = 0;
+	}
+
+	return NULL;
+}
+
+/*
+ *	Handle the ioctls that control the routing functions.
+ */
+int rose_rt_ioctl(unsigned int cmd, void *arg)
+{
+	struct rose_route_struct rose_route;
+	struct device *dev;
+	int err;
+
+	switch (cmd) {
+
+		case SIOCADDRT:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0)
+				return err;
+			memcpy_fromfs(&rose_route, arg, sizeof(struct rose_route_struct));
+			if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
+				return -EINVAL;
+			if (rose_dev_get(&rose_route.address) != NULL)	/* Can't add routes to ourself */
+				return -EINVAL;
+			if (rose_route.mask > 10) /* Mask can't be more than 10 digits */
+				return -EINVAL;
+
+			return rose_add_node(&rose_route, dev);
+
+		case SIOCDELRT:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0)
+				return err;
+			memcpy_fromfs(&rose_route, arg, sizeof(struct rose_route_struct));
+			if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
+				return -EINVAL;
+			return rose_del_node(&rose_route, dev);
+
+		case SIOCRSCLRRT:
+			return rose_clear_routes();
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
+{
+	struct rose_route *rose_route, *s;
+	struct sk_buff    *skb;
+
+	rose_neigh->restarted = 0;
+	rose_neigh->t0timer   = 0;
+	rose_neigh->ftimer    = sysctl_rose_link_fail_timeout;
+
+	rose_link_set_timer(rose_neigh);
+
+	while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+
+	rose_route = rose_route_list;
+
+	while (rose_route != NULL) {
+		if ((rose_route->neigh1 == rose_neigh && rose_route->neigh2 == rose_neigh) ||
+		    (rose_route->neigh1 == rose_neigh && rose_route->neigh2 == NULL)       ||
+		    (rose_route->neigh2 == rose_neigh && rose_route->neigh1 == NULL)) {
+			s = rose_route->next;
+			rose_remove_route(rose_route);
+			rose_route = s;
+			continue;
+		}
+
+		if (rose_route->neigh1 == rose_neigh) {
+			rose_route->neigh1->use--;
+			rose_route->neigh1 = NULL;
+			rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0);
+		}
+
+		if (rose_route->neigh2 == rose_neigh) {
+			rose_route->neigh2->use--;
+			rose_route->neigh2 = NULL;
+			rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0);
+		}
+
+		rose_route = rose_route->next;
+	}
+}
+
+/*
+ * 	A level 2 link has timed out, therefore it appears to be a poor link,
+ *	then don't use that neighbour until it is reset. Blow away all through
+ *	routes and connections using this route.
+ */
+void rose_link_failed(ax25_cb *ax25, int reason)
+{
+	struct rose_neigh *rose_neigh;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (rose_neigh->ax25 == ax25)
+			break;
+
+	if (rose_neigh == NULL) return;
+
+	rose_neigh->ax25 = NULL;
+
+	rose_del_route_by_neigh(rose_neigh);
+	rose_kill_by_neigh(rose_neigh);
+}
+
+/*
+ *	A device has been "downed" remove its link status. Blow away all
+ *	through routes and connections that use this device.
+ */
+void rose_link_device_down(struct device *dev)
+{
+	struct rose_neigh *rose_neigh;
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
+		if (rose_neigh->dev == dev) {
+			rose_del_route_by_neigh(rose_neigh);
+			rose_kill_by_neigh(rose_neigh);
+		}
+	}
+}
+
+/*
+ *	Route a frame to an appropriate AX.25 connection.
+ */
+int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
+{
+	struct rose_neigh *rose_neigh, *new_neigh;
+	struct rose_route *rose_route;
+	struct rose_facilities facilities;
+	rose_address *src_addr, *dest_addr;
+	struct sock *sk;
+	unsigned short frametype;
+	unsigned int lci, new_lci;
+	unsigned char cause, diagnostic;
+	struct device *dev;
+	unsigned long flags;
+
+#ifdef CONFIG_FIREWALL
+	if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+		return 0;
+#endif
+
+	frametype = skb->data[2];
+	lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
+	src_addr  = (rose_address *)(skb->data + 9);
+	dest_addr = (rose_address *)(skb->data + 4);
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
+		if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->device == rose_neigh->dev)
+			break;
+
+	if (rose_neigh == NULL)
+		return 0;
+
+	/*
+	 *	Obviously the link is working, halt the ftimer.
+	 */
+	rose_neigh->ftimer = 0;
+
+	/*
+	 *	LCI of zero is always for us, and its always a restart
+	 * 	frame.
+	 */
+	if (lci == 0) {
+		rose_link_rx_restart(skb, rose_neigh, frametype);
+		return 0;
+	}
+
+	/*
+	 *	Find an existing socket.
+	 */
+	if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) {
+		skb->h.raw = skb->data;
+		return rose_process_rx_frame(sk, skb);
+	}
+
+	/*
+	 *	Is is a Call Request and is it for us ?
+	 */
+	if (frametype == ROSE_CALL_REQUEST)
+		if ((dev = rose_dev_get(dest_addr)) != NULL)
+			return rose_rx_call_request(skb, dev, rose_neigh, lci);
+
+	if (!sysctl_rose_routing_control) {
+		rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0);
+		return 0;
+	}
+
+	/*
+	 *	Route it to the next in line if we have an entry for it.
+	 */
+	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
+		if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) {
+			if (rose_route->neigh2 != NULL) {
+				skb->data[0] &= 0xF0;
+				skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
+				skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
+				rose_transmit_link(skb, rose_route->neigh2);
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 1;
+			} else {
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 0;
+			}
+		}
+		if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) {
+			if (rose_route->neigh1 != NULL) {
+				skb->data[0] &= 0xF0;
+				skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F;
+				skb->data[1]  = (rose_route->lci1 >> 0) & 0xFF;
+				rose_transmit_link(skb, rose_route->neigh1);
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 1;
+			} else {
+				if (frametype == ROSE_CLEAR_CONFIRMATION)
+					rose_remove_route(rose_route);
+				return 0;
+			}
+		}
+	}
+
+	/*
+	 *	We know that:
+	 *	1. The frame isn't for us,
+	 *	2. It isn't "owned" by any existing route.
+	 */
+	if (frametype != ROSE_CALL_REQUEST)	/* XXX */
+		return 0;
+
+	if (!rose_parse_facilities(skb, &facilities)) {
+		rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
+		return 0;
+	}
+
+	/*
+	 *	Check for routing loops.
+	 */
+	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
+		if (rose_route->rand == facilities.rand &&
+		    rosecmp(src_addr, &rose_route->src_addr) == 0 &&
+		    ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 &&
+		    ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) {
+			printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr));
+			printk(KERN_DEBUG "ROSE:                to %s\n", rose2asc(dest_addr));
+			rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120);
+			return 0;
+		}
+	}
+
+	if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) {
+		if (cause == ROSE_NOT_OBTAINABLE)
+			printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr));
+		rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic);
+		return 0;
+	}
+
+	if ((new_lci = rose_new_lci(new_neigh)) == 0) {
+		rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71);
+		return 0;
+	}
+
+	if ((rose_route = (struct rose_route *)kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
+		rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120);
+		return 0;
+	}
+
+	rose_route->lci1      = lci;
+	rose_route->src_addr  = *src_addr;
+	rose_route->dest_addr = *dest_addr;
+	rose_route->src_call  = facilities.dest_call;
+	rose_route->dest_call = facilities.source_call;
+	rose_route->rand      = facilities.rand;
+	rose_route->neigh1    = rose_neigh;
+	rose_route->lci2      = new_lci;
+	rose_route->neigh2    = new_neigh;
+
+	rose_route->neigh1->use++;
+	rose_route->neigh2->use++;
+
+	save_flags(flags); cli();
+	rose_route->next = rose_route_list;
+	rose_route_list  = rose_route;
+	restore_flags(flags);
+
+	skb->data[0] &= 0xF0;
+	skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
+	skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
+
+	rose_transmit_link(skb, rose_route->neigh2);
+
+	return 1;
+}
+
+int rose_nodes_get_info(char *buffer, char **start, off_t offset,
+		      int length, int dummy)
+{
+	struct rose_node *rose_node;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+	int i;
+
+	cli();
+
+	len += sprintf(buffer, "address    mask n neigh neigh neigh\n");
+
+	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) {
+		len += sprintf(buffer + len, "%-10s %04d %d",
+			rose2asc(&rose_node->address),
+			rose_node->mask,
+			rose_node->count);			
+
+		for (i = 0; i < rose_node->count; i++)
+			len += sprintf(buffer + len, " %05d",
+				rose_node->neighbour[i]->number);
+
+		len += sprintf(buffer + len, "\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+int rose_neigh_get_info(char *buffer, char **start, off_t offset,
+		      int length, int dummy)
+{
+	struct rose_neigh *rose_neigh;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+	int i;
+
+	cli();
+
+	len += sprintf(buffer, "addr  callsign  dev  count use mode restart  t0  tf digipeaters\n");
+
+	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
+		len += sprintf(buffer + len, "%05d %-9s %-4s   %3d %3d  %3s     %3s %3d %3d",
+			rose_neigh->number,
+			ax2asc(&rose_neigh->callsign),
+			rose_neigh->dev ? rose_neigh->dev->name : "???",
+			rose_neigh->count,
+			rose_neigh->use,
+			(rose_neigh->dce_mode) ? "DCE" : "DTE",
+			(rose_neigh->restarted) ? "yes" : "no",
+			rose_neigh->t0timer / ROSE_SLOWHZ,
+			rose_neigh->ftimer  / ROSE_SLOWHZ);
+
+		if (rose_neigh->digipeat != NULL) {
+			for (i = 0; i < rose_neigh->digipeat->ndigi; i++)
+				len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i]));
+		}
+
+		len += sprintf(buffer + len, "\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+int rose_routes_get_info(char *buffer, char **start, off_t offset,
+		      int length, int dummy)
+{
+	struct rose_route *rose_route;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "lci  address     callsign   neigh  <-> lci  address     callsign   neigh\n");
+
+	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
+		if (rose_route->neigh1 != NULL) {
+			len += sprintf(buffer + len, "%3.3X  %-10s  %-9s  %05d      ",
+				rose_route->lci1,
+				rose2asc(&rose_route->src_addr),
+				ax2asc(&rose_route->src_call),
+				rose_route->neigh1->number);
+		} else {
+			len += sprintf(buffer + len, "000  *           *          00000      ");
+		}
+
+		if (rose_route->neigh2 != NULL) {
+			len += sprintf(buffer + len, "%3.3X  %-10s  %-9s  %05d\n",
+				rose_route->lci2,
+				rose2asc(&rose_route->dest_addr),
+				ax2asc(&rose_route->dest_call),
+				rose_route->neigh2->number);
+		} else {
+			len += sprintf(buffer + len, "000  *           *          00000\n");
+		}
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+#ifdef MODULE
+
+/*
+ *	Release all memory associated with ROSE routing structures.
+ */
+void rose_rt_free(void)
+{
+	struct rose_neigh *s, *rose_neigh = rose_neigh_list;
+	struct rose_node  *t, *rose_node  = rose_node_list;
+	struct rose_route *u, *rose_route = rose_route_list;
+
+	while (rose_neigh != NULL) {
+		s          = rose_neigh;
+		rose_neigh = rose_neigh->next;
+
+		rose_remove_neigh(s);
+	}
+
+	while (rose_node != NULL) {
+		t         = rose_node;
+		rose_node = rose_node->next;
+
+		rose_remove_node(t);
+	}
+
+	while (rose_route != NULL) {
+		u          = rose_route;
+		rose_route = rose_route->next;
+
+		rose_remove_route(u);
+	}
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- v2.0.34/linux/net/rose/rose_subr.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_subr.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,425 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_subr.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+/*
+ *	This routine purges all of the queues of frames.
+ */
+void rose_clear_queues(struct sock *sk)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&sk->write_queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ *	Validate that the value of nr is between va and vs. Return true or
+ *	false for testing.
+ */
+int rose_validate_nr(struct sock *sk, unsigned short nr)
+{
+	unsigned short vc = sk->protinfo.rose->va;
+
+	while (vc != sk->protinfo.rose->vs) {
+		if (nr == vc) return 1;
+		vc = (vc + 1) % ROSE_MODULUS;
+	}
+
+	if (nr == sk->protinfo.rose->vs) return 1;
+
+	return 0;
+}
+
+/* 
+ *  This routine is called when the packet layer internally generates a
+ *  control frame.
+ */
+void rose_write_internal(struct sock *sk, int frametype)
+{
+	struct sk_buff *skb;
+	unsigned char  *dptr;
+	unsigned char  lci1, lci2;
+	char buffer[100];
+	int len, faclen = 0;
+
+	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
+
+	switch (frametype) {
+		case ROSE_CALL_REQUEST:
+			len   += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN;
+			faclen = rose_create_facilities(buffer, sk->protinfo.rose);
+			len   += faclen;
+			break;
+		case ROSE_CALL_ACCEPTED:
+		case ROSE_CLEAR_REQUEST:
+		case ROSE_RESET_REQUEST:
+			len   += 2;
+			break;
+	}
+
+	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb->free = 1;
+
+	/*
+	 *	Space for AX.25 header and PID.
+	 */
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1);
+	
+	dptr = skb_put(skb, skb_tailroom(skb));
+
+	lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F;
+	lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF;
+
+	switch (frametype) {
+
+		case ROSE_CALL_REQUEST:
+			*dptr++ = ROSE_GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = 0xAA;
+			memcpy(dptr, &sk->protinfo.rose->dest_addr,  ROSE_ADDR_LEN);
+			dptr   += ROSE_ADDR_LEN;
+			memcpy(dptr, &sk->protinfo.rose->source_addr, ROSE_ADDR_LEN);
+			dptr   += ROSE_ADDR_LEN;
+			memcpy(dptr, buffer, faclen);
+			dptr   += faclen;
+			break;
+
+		case ROSE_CALL_ACCEPTED:
+			*dptr++ = ROSE_GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = 0x00;		/* Address length */
+			*dptr++ = 0;		/* Facilities length */
+			break;
+
+		case ROSE_CLEAR_REQUEST:
+			*dptr++ = ROSE_GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = sk->protinfo.rose->cause;
+			*dptr++ = sk->protinfo.rose->diagnostic;
+			break;
+
+		case ROSE_RESET_REQUEST:
+			*dptr++ = ROSE_GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++ = frametype;
+			*dptr++ = ROSE_DTE_ORIGINATED;
+			*dptr++ = 0;
+			break;
+
+		case ROSE_RR:
+		case ROSE_RNR:
+			*dptr++ = ROSE_GFI | lci1;
+			*dptr++ = lci2;
+			*dptr   = frametype;
+			*dptr++ |= (sk->protinfo.rose->vr << 5) & 0xE0;
+			break;
+
+		case ROSE_CLEAR_CONFIRMATION:
+		case ROSE_RESET_CONFIRMATION:
+			*dptr++ = ROSE_GFI | lci1;
+			*dptr++ = lci2;
+			*dptr++  = frametype;
+			break;
+
+		default:
+			printk(KERN_ERR "rose_write_internal: invalid frametype %02X\n", frametype);
+			kfree_skb(skb, FREE_WRITE);
+			return;
+	}
+
+	rose_transmit_link(skb, sk->protinfo.rose->neighbour);
+}
+
+int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m)
+{
+	unsigned char *frame;
+
+	frame = skb->data;
+
+	*ns = *nr = *q = *d = *m = 0;
+
+	switch (frame[2]) {
+		case ROSE_CALL_REQUEST:
+		case ROSE_CALL_ACCEPTED:
+		case ROSE_CLEAR_REQUEST:
+		case ROSE_CLEAR_CONFIRMATION:
+		case ROSE_RESET_REQUEST:
+		case ROSE_RESET_CONFIRMATION:
+			return frame[2];
+		default:
+			break;
+	}
+
+	if ((frame[2] & 0x1F) == ROSE_RR  ||
+	    (frame[2] & 0x1F) == ROSE_RNR) {
+		*nr = (frame[2] >> 5) & 0x07;
+		return frame[2] & 0x1F;
+	}
+
+	if ((frame[2] & 0x01) == ROSE_DATA) {
+		*q  = (frame[0] & ROSE_Q_BIT) == ROSE_Q_BIT;
+		*d  = (frame[0] & ROSE_D_BIT) == ROSE_D_BIT;
+		*m  = (frame[2] & ROSE_M_BIT) == ROSE_M_BIT;
+		*nr = (frame[2] >> 5) & 0x07;
+		*ns = (frame[2] >> 1) & 0x07;
+		return ROSE_DATA;
+	}
+
+	return ROSE_ILLEGAL;
+}
+
+static int rose_parse_national(unsigned char *p, struct rose_facilities *facilities, int len)
+{
+	unsigned char l, n = 0;
+
+	do {
+		switch (*p & 0xC0) {
+			case 0x00:
+				p   += 2;
+				n   += 2;
+				len -= 2;
+				break;
+
+			case 0x40:
+				if (*p == FAC_NATIONAL_RAND)
+					facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);
+				p   += 3;
+				n   += 3;
+				len -= 3;
+				break;
+
+			case 0x80:
+				p   += 4;
+				n   += 4;
+				len -= 4;
+				break;
+
+			case 0xC0:
+				l = p[1];
+				if (*p == FAC_NATIONAL_DEST_DIGI) {
+					memcpy(&facilities->source_digi, p + 2, AX25_ADDR_LEN);
+					facilities->source_ndigis = 1;
+				}
+				if (*p == FAC_NATIONAL_SRC_DIGI) {
+					memcpy(&facilities->dest_digi,   p + 2, AX25_ADDR_LEN);
+					facilities->dest_ndigis = 1;
+				}
+				p   += l + 2;
+				n   += l + 2;
+				len -= l + 2;
+				break;
+		}
+	} while (*p != 0x00 && len > 0);
+
+	return n;
+}
+
+static int rose_parse_ccitt(unsigned char *p, struct rose_facilities *facilities, int len)
+{
+	unsigned char l, n = 0;
+	char callsign[11];
+
+	do {
+		switch (*p & 0xC0) {
+			case 0x00:
+				p   += 2;
+				n   += 2;
+				len -= 2;
+				break;
+
+			case 0x40:
+				p   += 3;
+				n   += 3;
+				len -= 3;
+				break;
+
+			case 0x80:
+				p   += 4;
+				n   += 4;
+				len -= 4;
+				break;
+
+			case 0xC0:
+				l = p[1];
+				if (*p == FAC_CCITT_DEST_NSAP) {
+					memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN);
+					memcpy(callsign, p + 12,   l - 10);
+					callsign[l - 10] = '\0';
+					facilities->source_call = *asc2ax(callsign);
+				}
+				if (*p == FAC_CCITT_SRC_NSAP) {
+					memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN);
+					memcpy(callsign, p + 12, l - 10);
+					callsign[l - 10] = '\0';
+					facilities->dest_call = *asc2ax(callsign);
+				}
+				p   += l + 2;
+				n   += l + 2;
+				len -= l + 2;
+				break;
+		}
+	} while (*p != 0x00 && len > 0);
+
+	return n;
+}
+
+int rose_parse_facilities(struct sk_buff *skb, struct rose_facilities *facilities)
+{
+	int facilities_len, len;
+	unsigned char *p;
+
+	memset(facilities, 0x00, sizeof(struct rose_facilities));
+
+	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
+	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
+
+	p = skb->data + len + 4;
+
+	facilities_len = *p++;
+
+	if (facilities_len == 0)
+		return 0;
+
+	while (facilities_len > 0) {
+		if (*p == 0x00) {
+			facilities_len--;
+			p++;
+
+			switch (*p) {
+				case FAC_NATIONAL:		/* National */
+					len = rose_parse_national(p + 1, facilities, facilities_len - 1);
+					facilities_len -= len + 1;
+					p += len + 1;
+					break;
+
+				case FAC_CCITT:		/* CCITT */
+					len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1);
+					facilities_len -= len + 1;
+					p += len + 1;
+					break;
+
+				default:
+					printk(KERN_DEBUG "rose_parse_facilities: unknown facilities family %02X\n", *p);
+					facilities_len--;
+					p++;
+					break;
+			}
+		}
+	}
+
+	return 1;
+}
+
+int rose_create_facilities(unsigned char *buffer, rose_cb *rose)
+{
+	unsigned char *p = buffer + 1;
+	char *callsign;
+	int len;
+
+	/* National Facilities */
+	if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) {
+		*p++ = 0x00;
+		*p++ = FAC_NATIONAL;
+
+		if (rose->rand != 0) {
+			*p++ = FAC_NATIONAL_RAND;
+			*p++ = (rose->rand >> 8) & 0xFF;
+			*p++ = (rose->rand >> 0) & 0xFF;
+		}
+
+		if (rose->source_ndigis == 1) {
+			*p++ = FAC_NATIONAL_SRC_DIGI;
+			*p++ = AX25_ADDR_LEN;
+			memcpy(p, &rose->source_digi, AX25_ADDR_LEN);
+			p   += AX25_ADDR_LEN;
+		}
+
+		if (rose->dest_ndigis == 1) {
+			*p++ = FAC_NATIONAL_DEST_DIGI;
+			*p++ = AX25_ADDR_LEN;
+			memcpy(p, &rose->dest_digi, AX25_ADDR_LEN);
+			p   += AX25_ADDR_LEN;
+		}
+	}
+
+	*p++ = 0x00;
+	*p++ = FAC_CCITT;
+
+	*p++ = FAC_CCITT_DEST_NSAP;
+
+	callsign = ax2asc(&rose->dest_call);
+
+	*p++ = strlen(callsign) + 10;
+	*p++ = (strlen(callsign) + 9) * 2;		/* ??? */
+
+	*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
+	*p++ = ROSE_ADDR_LEN * 2;
+	memcpy(p, &rose->dest_addr, ROSE_ADDR_LEN);
+	p   += ROSE_ADDR_LEN;
+
+	memcpy(p, callsign, strlen(callsign));
+	p   += strlen(callsign);
+
+	*p++ = FAC_CCITT_SRC_NSAP;
+
+	callsign = ax2asc(&rose->source_call);
+
+	*p++ = strlen(callsign) + 10;
+	*p++ = (strlen(callsign) + 9) * 2;		/* ??? */
+
+	*p++ = 0x47; *p++ = 0x00; *p++ = 0x11;
+	*p++ = ROSE_ADDR_LEN * 2;
+	memcpy(p, &rose->source_addr, ROSE_ADDR_LEN);
+	p   += ROSE_ADDR_LEN;
+
+	memcpy(p, callsign, strlen(callsign));
+	p   += strlen(callsign);
+
+	len       = p - buffer;
+	buffer[0] = len - 1;
+
+	return len;
+}
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/rose_timer.c linux/net/rose/rose_timer.c
--- v2.0.34/linux/net/rose/rose_timer.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_timer.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,143 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_timer.c
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/rose.h>
+
+static void rose_timer(unsigned long);
+
+/*
+ *	Linux set timer
+ */
+void rose_set_timer(struct sock *sk)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	del_timer(&sk->timer);
+	restore_flags(flags);
+
+	sk->timer.data     = (unsigned long)sk;
+	sk->timer.function = &rose_timer;
+	sk->timer.expires  = jiffies + 10;
+
+	add_timer(&sk->timer);
+}
+
+/*
+ *	ROSE Timer
+ *
+ *	This routine is called every 100ms. Decrement timer by this
+ *	amount - if expired then process the event.
+ */
+static void rose_timer(unsigned long param)
+{
+	struct sock *sk = (struct sock *)param;
+
+	switch (sk->protinfo.rose->state) {
+		case ROSE_STATE_0:
+			/* Magic here: If we listen() and a new link dies before it
+			   is accepted() it isn't 'dead' so doesn't get removed. */
+			if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) {
+				del_timer(&sk->timer);
+				rose_destroy_socket(sk);
+				return;
+			}
+			break;
+
+		case ROSE_STATE_3:
+			/*
+			 * Check for the state of the receive buffer.
+			 */
+			if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) {
+				sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY;
+				sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+				sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
+				sk->protinfo.rose->timer      = 0;
+				rose_write_internal(sk, ROSE_RR);
+				break;
+			}
+			/*
+			 * Check for frames to transmit.
+			 */
+			rose_kick(sk);
+			break;
+
+		default:
+			break;
+	}
+
+	if (sk->protinfo.rose->timer == 0 || --sk->protinfo.rose->timer > 0) {
+		rose_set_timer(sk);
+		return;
+	}
+
+	/*
+	 * Timer has expired, it may have been T1, T2, T3 or HB. We can tell
+	 * by the socket state.
+	 */
+	switch (sk->protinfo.rose->state) {
+		case ROSE_STATE_3:	/* HB */
+			if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) {
+				sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+				rose_enquiry_response(sk);
+			}
+			break;
+
+		case ROSE_STATE_1:	/* T1 */
+		case ROSE_STATE_4:	/* T2 */
+			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			sk->protinfo.rose->state = ROSE_STATE_2;
+			sk->protinfo.rose->timer = sk->protinfo.rose->t3;
+			break;
+
+		case ROSE_STATE_2:	/* T3 */
+			rose_clear_queues(sk);
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = ETIMEDOUT;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+			break;
+	}
+
+	rose_set_timer(sk);
+}
+
+#endif
diff -u --recursive --new-file v2.0.34/linux/net/rose/sysctl_net_rose.c linux/net/rose/sysctl_net_rose.c
--- v2.0.34/linux/net/rose/sysctl_net_rose.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/sysctl_net_rose.c	Mon Jul 13 13:47:41 1998
@@ -0,0 +1,72 @@
+/* -*- linux-c -*-
+ * sysctl_net_rose.c: sysctl interface to net ROSE subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/rose directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <net/ax25.h>
+#include <net/rose.h>
+
+static int min_timer[]  = {1 * ROSE_SLOWHZ};
+static int max_timer[]  = {300 * ROSE_SLOWHZ};
+static int min_route[]  = {0}, max_route[] = {1};
+static int min_ftimer[] = {60 * ROSE_SLOWHZ};
+static int max_ftimer[] = {600 * ROSE_SLOWHZ};
+static int min_maxvcs[] = {1}, max_maxvcs[] = {254};
+static int min_window[] = {1}, max_window[] = {7};
+
+static struct ctl_table_header *rose_table_header;
+
+static ctl_table rose_table[] = {
+        {NET_ROSE_RESTART_REQUEST_TIMEOUT, "restart_request_timeout",
+         &sysctl_rose_restart_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_CALL_REQUEST_TIMEOUT, "call_request_timeout",
+         &sysctl_rose_call_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_RESET_REQUEST_TIMEOUT, "reset_request_timeout",
+         &sysctl_rose_reset_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_CLEAR_REQUEST_TIMEOUT, "clear_request_timeout",
+         &sysctl_rose_clear_request_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_ACK_HOLD_BACK_TIMEOUT, "acknowledge_hold_back_timeout",
+         &sysctl_rose_ack_hold_back_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+        {NET_ROSE_ROUTING_CONTROL, "routing_control",
+         &sysctl_rose_routing_control, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route},
+        {NET_ROSE_LINK_FAIL_TIMEOUT, "link_fail_timeout",
+         &sysctl_rose_link_fail_timeout, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_ftimer, &max_ftimer},
+        {NET_ROSE_MAX_VCS, "maximum_virtual_circuits",
+         &sysctl_rose_maximum_vcs, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_maxvcs, &max_maxvcs},
+        {NET_ROSE_WINDOW_SIZE, "window_size",
+         &sysctl_rose_window_size, sizeof(int), 0644, NULL,
+         &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_window, &max_window},
+	{0}
+};
+
+static ctl_table rose_dir_table[] = {
+	{NET_ROSE, "rose", NULL, 0, 0555, rose_table},
+	{0}
+};
+
+static ctl_table rose_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, rose_dir_table},
+	{0}
+};
+
+void rose_register_sysctl(void)
+{
+	rose_table_header = register_sysctl_table(rose_root_table, 1);
+}
+
+void rose_unregister_sysctl(void)
+{
+	unregister_sysctl_table(rose_table_header);
+}
diff -u --recursive --new-file v2.0.34/linux/net/sysctl_net.c linux/net/sysctl_net.c
--- v2.0.34/linux/net/sysctl_net.c	Mon Jun  3 02:42:42 1996
+++ linux/net/sysctl_net.c	Mon Jul 13 13:47:41 1998
@@ -28,14 +28,6 @@
 extern ctl_table atalk_table[];
 #endif
 
-#ifdef CONFIG_NETROM
-extern ctl_table netrom_table[];
-#endif
-
-#ifdef CONFIG_AX25
-extern ctl_table ax25_table[];
-#endif
-
 extern ctl_table core_table[], unix_table[];
 
 #ifdef CONFIG_NET
@@ -61,12 +53,6 @@
 #endif
 #ifdef CONFIG_ATALK
         {NET_ATALK,  "appletalk", NULL, 0, 0555, atalk_table},
-#endif
-#ifdef CONFIG_NETROM
-	{NET_NETROM, "netrom",    NULL, 0, 0555, netrom_table},
-#endif
-#ifdef CONFIG_AX25
-	{NET_AX25,   "ax25",      NULL, 0, 0555, ax25_table},
 #endif
 #ifdef CONFIG_BRIDGE
         {NET_BRIDGE, "bridge",    NULL, 0, 0555, bridge_table},
