## Automatically generated incremental diff
## From:   linux-2.4.4-pre2
## To:     linux-2.4.4-pre3
## Robot:  $Id: make-incremental-diff,v 1.8 2001/12/04 06:05:04 hpa Exp $

diff -urN linux-2.4.4-pre2/CREDITS linux/CREDITS
--- linux-2.4.4-pre2/CREDITS	Mon Dec  3 22:34:41 2001
+++ linux/CREDITS	Mon Dec  3 22:34:49 2001
@@ -328,6 +328,9 @@
 D: AUN network protocols
 D: Co-architect of the parallel port sharing system
 D: IPv6 netfilter
+S: FutureTV Labs Ltd
+S: Brunswick House, 61-69 Newmarket Rd, Cambridge CB5 8EG
+S: United Kingdom
 
 N: Thomas Bogendörfer
 E: tsbogend@alpha.franken.de
diff -urN linux-2.4.4-pre2/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.4.4-pre2/Documentation/Configure.help	Sun Mar 25 18:24:31 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 22:34:49 2001
@@ -13,8 +13,10 @@
 #     http://www.traduc.org/kernelfr
 #   - Spanish, by Carlos Perelló Marín (fperllo@ehome.encis.es), at
 #     http://visar.csustan.edu/~carlos/
+#     XXX: Site has moved, new location has no Configure.help trans.
 #   - Italian, by Alessandro Rubini (rubini@linux.it), at
 #     ftp://ftp-pavia1.linux.it/pub/linux/Configure.help
+#     XXX: ftp-pavia1.linux.it: Non-existent host/domain
 #   - Polish, by Cezar Cichocki (cezar@cs.net.pl), at
 #     http://www.cs.net.pl/~cezar/Kernel
 #   - German, by SuSE, at http://www.suse.de/~ke/kernel . This patch
@@ -268,6 +270,11 @@
   Most normal users won't need the RAM disk functionality, and can
   thus say N here.
 
+Default RAM disk size
+CONFIG_BLK_DEV_RAM_SIZE
+  The default value is 4096. Only change this if you know what are
+  you doing. If you are using IBM S/390, then set this to 8192.
+
 Initial RAM disk (initrd) support
 CONFIG_BLK_DEV_INITRD
   The initial RAM disk is a RAM disk that is loaded by the boot loader
@@ -415,7 +422,7 @@
 
   To fine-tune ATA/IDE drive/interface parameters for improved
   performance, look for the hdparm package at
-  ftp://metalab.unc.edu/pub/Linux/kernel/patches/diskdrives/ .
+  http://www.ibiblio.org/pub/Linux/system/hardware .
 
   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),
@@ -711,7 +718,7 @@
 IGNORE word93 Validation BITS
 CONFIG_IDEDMA_IVB
   Since various rules were applied and created ... et al. as it relates
-  the detection of vaild cable signals.  This is a result of unclear terms
+  the detection of valid cable signals.  This is a result of unclear terms
   in ATA-4 and ATA-5 standards.
 
   It is normally safe to answer Y; however, the default is N.
@@ -1299,14 +1306,37 @@
   have a high-level driver for the type of device that you want to
   support.
 
-MicroSolutions backpack protocol
+Micro Solutions BACKPACK Series 5 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.
+  This option enables support for the Micro Solutions BACKPACK parallel
+  port Series 5 IDE protocol. (Most BACKPACK drives made before 1999 were 
+  Series 5) Series 5 drives will NOT always have the Series noted on the
+  bottom of the drive. Series 6 drivers will. 
+
+  In other words, if your BACKPACK drive dosen't say "Series 6" on the
+  bottom, enable this option.
+
+  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.
+
+Micro Solutions BACKPACK Series 6 protocol
+CONFIG_PARIDE_BPCK6
+  This option enables support for the Micro Solutions BACKPACK parallel
+  port Series 6 IDE protocol. (Most BACKPACK drives made after 1999 were 
+  Series 6) Series 6 drives will have the Series noted on the bottom of
+  the drive. Series 5 drivers don't always have it noted. 
+
+  In other words, if your BACKPACK drive says "Series 6" on the bottom,
+  enable this option.
+
+  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 bpck6.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
@@ -1616,12 +1646,12 @@
 CONFIG_SGI_SN0_N_MODE
   The nodes of Origin 200, Origin 2000 and Onyx 2 systems can be
   configured in either N-Modes which allows for more nodes or M-Mode
-  which allows for more more memory.  Your system is most probably
+  which allows for more memory.  Your system is most probably
   running in M-Mode, so you should say N here.
 
 MIPS JAZZ onboard SONIC Ethernet support
 CONFIG_MIPS_JAZZ_SONIC
-  This is the driver for the onboard card of of MIPS Magnum 4000,
+  This is the driver for the onboard card of MIPS Magnum 4000,
   Acer PICA, Olivetti M700-10 and a few other identical OEM systems.
 
 MIPS JAZZ FAS216 SCSI support
@@ -2110,7 +2140,6 @@
   If you want to compile it as a module, say M here and read
   Documentation/modules.txt.  If unsure, say `N'.
 
-
 TCP Explicit Congestion Notification support
 CONFIG_INET_ECN
   Explicit Congestion Notification (ECN) allows routers to notify
@@ -2821,14 +2850,6 @@
   section 6.4 of the Linux Programmer's Guide, available from
   http://www.linuxdoc.org/docs.html#guide .
 
-  Shared memory is now implemented using a new (minimal) virtual file
-  system. To mount it automatically at system startup just add the
-  following line to your /etc/fstab:
-
-  none	/dev/shm	shm	defaults	0 0
-
-  Saying Y here enlarges your kernel by about 18 KB. Just say Y.
-
 BSD Process Accounting
 CONFIG_BSD_PROCESS_ACCT
   If you say Y here, a user level program will be able to instruct the
@@ -2994,14 +3015,16 @@
    - "Pentium-Classic" for the Intel Pentium.
    - "Pentium-MMX" for the Intel Pentium MMX.
    - "Pentium-Pro" for the Intel Pentium Pro/Celeron/Pentium II.
-   - "Pentium-III" for the Intel Pentium III.
-   - "Pentium-4" for the Intel Pentium 4
+   - "Pentium-III" for the Intel Pentium III
+     and Celerons based on the coppermine core.
+   - "Pentium-4" for the Intel Pentium 4.
    - "K6" for the AMD K6, K6-II and K6-III (aka K6-3D).
-   - "Athlon" for the AMD Athlon (K7).
+   - "Athlon" for the AMD K7 family (Athlon/Duron/Thunderbird).
    - "Crusoe" for the Transmeta Crusoe series.
    - "Winchip-C6" for original IDT Winchip.
    - "Winchip-2" for IDT Winchip 2.
    - "Winchip-2A" for IDT Winchips with 3dNow! capabilities.
+   - "CyrixIII" for VIA Cyrix III or VIA C3.
 
   If you don't know what to do, choose "386".
 
@@ -3509,7 +3532,7 @@
 Virtual Frame Buffer support (ONLY FOR TESTING!)
 CONFIG_FB_VIRTUAL
   This is a `virtual' frame buffer device. It operates on a chunk of
-  unswapable kernel memory instead of on the memory of a graphics
+  unswappable kernel memory instead of on the memory of a graphics
   board. This means you cannot see any output sent to this frame
   buffer device, while it does consume precious memory. The main use
   of this frame buffer device is testing and debugging the frame
@@ -4301,7 +4324,7 @@
   packets with different FWMARK ("firewalling mark") values
   (see ipchains(8), "-m" argument).
 
-Appletalk interfaces support
+AppleTalk interfaces support
 CONFIG_APPLETALK
   AppleTalk is the way Apple computers speak to each other on a
   network. If your Linux box is connected to such a network and you
@@ -4314,11 +4337,10 @@
   want to join the conversation, say Y. You will need to use the
   netatalk package so that your Linux box can act as a print and file
   server for Macs as well as access AppleTalk printers. Check out
-  http://threepio.hitchcock.org/cgi-bin/faq/netatalk/faq.pl on the WWW
-  for details. EtherTalk is the name used for AppleTalk over Ethernet
-  and the cheaper and slower LocalTalk is AppleTalk over a proprietary
-  Apple network using serial links. EtherTalk and LocalTalk are fully
-  supported by Linux.
+  http://www.zettabyte.net/netatalk/ on the WWW for details. EtherTalk 
+  is the name used for AppleTalk over Ethernet and the cheaper and 
+  slower LocalTalk is AppleTalk over a proprietary Apple network using
+  serial links. EtherTalk and LocalTalk are fully supported by Linux.
 
   General information about how to connect Linux, Windows machines and
   Macs is on the WWW at http://www.eats.com/linux_mac_win.html . The
@@ -4329,8 +4351,10 @@
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
   The module is called appletalk.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt. I hear that
-  the GNU boycott of Apple is over, so even politically correct people
+  module, say M here and read Documentation/modules.txt.  You almost
+  certainly want to compile it as a module so you can restart your 
+  AppleTalk stack without rebooting your machine.  I hear that the 
+  GNU boycott of Apple is over, so even politically correct people
   are allowed to say Y here.
 
 AppleTalk-IP driver support
@@ -4831,7 +4855,7 @@
   with the Frames Diverter on, can do some *really* transparent www 
   caching using a Squid proxy for example.
 
-  This is very usefull when you don't want to change your router's
+  This is very useful when you don't want to change your router's
   config (or if you simply don't have access to it).
 
   The other possible usages of diverting Ethernet Frames are numberous:
@@ -4916,7 +4940,7 @@
 
 Routing messages
 CONFIG_RTNETLINK
-  If you say Y here, userspace programs can receive some network
+  If you say Y here, user space programs can receive some network
   related routing information over the netlink. 'rtmon', supplied
   with the iproute2 package (ftp://ftp.inr.ac.ru), can read and
   interpret this data.  Information sent to the kernel over this link
@@ -5084,6 +5108,15 @@
   Note that extended debugging may create certain race conditions
   itself. Enable this ONLY if you suspect problems with the driver.
 
+Fujitsu FireStream (FS50/FS155)
+CONFIG_ATM_FIRESTREAM
+  Driver for the Fujitsu FireStream 155 (MB86697) and 
+  FireStream 50 (MB86695) ATM PCI chips.
+
+  This driver is also available as a module. If you want to compile
+  it as a module, say M here and read Documentation/modules.txt. The
+  module will be called firestream.o.
+
 Enable usec resolution timestamps
 CONFIG_ATM_ZATM_EXACT_TS
   The uPD98401 SAR chip supports a high-resolution timer (approx. 30
@@ -8078,6 +8111,18 @@
   The module will be called cosa.o. For general information about
   modules read Documentation/modules.txt.
 
+Etinc PCISYNC serial boards support
+CONFIG_DSCC4
+  This is a driver for Etinc PCISYNC boards based on the Infineon
+  (ex. Siemens) DSCC4 chipset. It is supposed to work with the four
+  ports card. Take a look at http://www.cogenit.fr/dscc4
+  for further informations about the driver and his configuration.
+
+  The driver will be compiled as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called dscc4.o. For general information about
+  modules read Documentation/modules.txt.
+
 Lan Media sync serial boards support
 CONFIG_LANMEDIA
   This is a driver for the following Lan Media family of serial boards.
@@ -8164,13 +8209,26 @@
 
 Sangoma WANPIPE(tm) multiprotocol cards
 CONFIG_VENDOR_SANGOMA
-  WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com ) is
-  a family of intelligent multiprotocol WAN adapters with data
-  transfer rates up to T1 (1.544 Mbps). They are also known as
-  Synchronous Data Link Adapters (SDLA) and designated S502E(A), S503
-  or S508. These cards support the X.25, Frame Relay, and PPP
-  protocols. If you have one or more of these cards, say Y to this
-  option; you may then also want to read the file
+
+  WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com)
+  is a family of intelligent multiprotocol WAN adapters with data 
+  transfer rates up to 4Mbps. They are also known as Synchronous 
+  Data Link Adapters (SDLA) and are designated as S514-PCI or S508-ISA. 
+  These cards support
+  
+    - X.25, Frame Relay, PPP, Cisco HDLC protocols. 
+  
+    - API support for protocols like HDLC (LAPB), 
+      HDLC Streaming, X.25, Frame Relay and BiSync. 
+  
+    - Ethernet Bridging over Frame Relay protocol.
+
+    - MULTILINK PPP
+
+    - Async PPP (Modem Dialup)
+
+  If you have one or more of these cards, say M to this option; you
+  may then also want to read the file  
   Documentation/networking/wanpipe.txt. The next questions will ask
   you about the protocols you want the driver to support.
 
@@ -8179,44 +8237,58 @@
   The module will be called wanpipe.o. For general information about
   modules read Documentation/modules.txt.
 
-Maximum number of cards
-CONFIG_WANPIPE_CARDS
-  Enter number of WANPIPE adapters installed in your machine. The
-  driver can support up to 8 cards. You may enter more than you
-  actually have if you plan to add more cards in the future without
-  re-compiling the driver, but remember that in this case you'll waste
-  some kernel memory (about 1K per card).
-
-WANPIPE Cisco HDLC support
-CONFIG_WANPIPE_CHDLC
-  Say Y to this option if you are planning to connect a WANPIPE card
-  to a connection which uses the synchronous Cisco HDLC (High-level
-  Data Link Control) protocol. This protocol is often used on
-  high-speed leased lines like T1/E1.
-
 WANPIPE X.25 support
 CONFIG_WANPIPE_X25
   Say Y to this option if you are planning to connect a WANPIPE card
-  to an X.25 network. You should then also have said Y to "CCITT X.25
-  Packet Layer" and "LAPB Data Link Driver", above. If you say N, the
-  X.25 support will not be included in the driver (saves about 16 KB
-  of kernel memory).
+  to an X.25 network.  Note, this feature also includes the X.25 API
+  support used to develope custom applications over the X.25 protocol.
+  If you say N, the X.25 support will not be included in the driver. 
+  The X.25 option is supported on S514-PCI and S508-ISA cards.
 
 WANPIPE Frame Relay support
 CONFIG_WANPIPE_FR
   Say Y to this option if you are planning to connect a WANPIPE card
-  to a frame relay network. You should then also have said Y to "Frame
-  Relay (DLCI) support", above. If you say N, the frame relay
-  support will not be included in the driver (saves about 16 KB of
-  kernel memory).
+  to a frame relay network, or use frame relay API to develope 
+  custom applications over the Frame  Relay protocol.  
+  This feature also contains the Ethernet Bridging over Frame Relay,
+  where a WANPIPE frame relay link can be directly connected to the Linux
+  kernel bridge. If you say N, the frame relay support will
+  not be included in the driver. The Frame Relay option is 
+  supported on S514-PCI and S508-ISA cards.
 
 WANPIPE PPP support
 CONFIG_WANPIPE_PPP
   Say Y to this option if you are planning to connect a WANPIPE card
-  to a leased line using Point-to-Point protocol (PPP). You should
-  then also have said Y to "PPP (point-to-point) support", above. If
-  you say N, the PPP support will not be included in the driver (saves
-  about 16 KB of kernel memory).
+  to a leased line using Point-to-Point protocol (PPP). If you say N,
+  the PPP support will not be included in the driver. The PPP option
+  is supported on S514-PCI/S508-ISA cards.
+
+WANPIPE MultiPort PPP support
+CONFIG_WANPIPE_MULTPPP
+  Say Y to this option if you are planning to connect a WANPIPE card
+  to a leased line using Point-to-Point protocol (PPP). Note, the
+  MultiPort PPP uses the Linux Kernel SyncPPP protocol over the Sangoma
+  HDLC Streaming adapter.  In this case each Sangoma adapter port 
+  can support an independent PPP connection.  For example, a single
+  Quad-Port PCI adapter can support up to four independent 
+  PPP links. If you say N,the PPP support will not be included 
+  in the driver. The PPP option is supported on S514-PCI/S508-ISA cards.
+ 	
+WANPIPE Cisco HDLC support
+CONFIG_WANPIPE_CHDLC
+  Say Y to this option if you are planning to connect a WANPIPE card
+  to a leased line using the Cisco HDLC protocol. This now supports
+  Dual Port Cisco HDLC on the S514-PCI/S508-ISA cards. 
+  This support also allows user to build applications using the 
+  HDLC streaming API. 
+  
+  CHDLC Streaming driver also supports MULTILINK PPP
+  support that can bind multiple WANPIPE T1 cards into
+  a single logical channel.
+  
+  If you say  N, the Cisco HDLC support and 
+  HDLC streaming API and MULTILINK PPP will not be 
+  included in the driver.
 
 MultiGate/COMX support
 CONFIG_COMX
@@ -8288,7 +8360,7 @@
   If you want to compile this as a module, say M and read
   Documentation/modules.txt. The module will be called
   i810-tco.o.
-  
+
 MultiGate Cisco-HDLC and synchronous PPP protocol support
 CONFIG_COMX_PROTO_PPP
   Cisco-HDLC and synchronous PPP protocol driver for all MultiGate
@@ -8351,6 +8423,49 @@
   If you say N, the X.25 support will not be included in the driver
   (saves about 11 KB of kernel memory).
 
+Generic HDLC driver
+CONFIG_HDLC
+  Say Y to this option if your Linux box contains a WAN card supported
+  by this driver and you are planning to connect the box to a WAN
+  ( = Wide Area Network). You will need supporting software from
+  http://hq.pm.waw.pl/hdlc/.
+  Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame
+  Relay, synchronous Point-to-Point Protocol (PPP) and X.25.
+
+  If unsure, say N here.
+
+Synchronous Point-to-Point Protocol (PPP)
+CONFIG_HDLC_PPP
+  Say Y to this option if you want generic HDLC driver to support
+  PPP over WAN (Wide Area Network) connections.
+
+  If unsure, say N here.
+
+CCITT X.25 protocol
+CONFIG_HDLC_X25
+  Say Y to this option if you want generic HDLC driver to support
+  X.25 protocol over WAN (Wide Area Network) connections.
+
+  If unsure, say N here.
+
+SDL RISCom/N2 driver
+CONFIG_N2
+  This driver is for RISCom/N2 single or dual channel ISA cards
+  made by SDL Communications Inc. If you have such a card,
+  say Y here and see http://hq.pm.waw.pl/pub/hdlc/
+
+  Note that N2csu and N2dds cards are not supported by this driver.
+
+  If unsure, say N here.
+
+Moxa C101 driver
+CONFIG_C101
+  This driver is for C101 SuperSync ISA cards made by Moxa
+  Technologies Co., Ltd. If you have such a card,
+  say Y here and see http://hq.pm.waw.pl/pub/hdlc/
+
+  If unsure, say N here.
+
 Ethernet (10 or 100Mbit)
 CONFIG_NET_ETHERNET
   Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common
@@ -10055,62 +10170,100 @@
 Memory Technology Device (MTD) support
 CONFIG_MTD
   Memory Technology Devices are flash, RAM and similar chips, often
-  used for solid state filesystems on embedded devices. This option
+  used for solid state file systems on embedded devices. This option
   will provide the generic support for MTD drivers to register
   themselves with the kernel and for potential users of MTD devices
   to enumerate the devices which are present and obtain a handle on
   them. It will also allow you to select individual drivers for 
   particular hardware and users of MTD device. If unsure, say N.
 
+MTD debugging support
+CONFIG_MTD_DEBUG
+  This turns on low-level debugging for the entire MTD sub-system.
+
+MTD debugging verbosity
+CONFIG_MTD_DEBUG_VERBOSE
+  Determines the verbosity level of the MTD debugging messages.
+
 M-Systems Disk-On-Chip 1000 support
 CONFIG_MTD_DOC1000
   This provides an MTD device driver for the M-Systems DiskOnChip
   1000 devices, which are obsolete so you probably want to say 'N'.
 
-M-Systems Disk-On-Chip 2000 support
+M-Systems Disk-On-Chip 2000 and Millennium support
 CONFIG_MTD_DOC2000
   This provides an MTD device driver for the M-Systems DiskOnChip
-  2000 devices. If you use this, you probably also want the NFTL
-  'NAND Flash Translation Layer' below, which is used to emulate
+  2000 and Millennium devices. Originally designed for the DiskOnChip
+  2000, it also now includes support for the DiskOnChip Millennium.
+  If you have problems with this driver and the DiskOnChip Millennium,
+  you may wish to try the alternative Millennium driver below. To use
+  the alternative driver, you will need to undefine DOC_SINGLE_DRIVER
+  in the drivers/mtd/docprobe.c source code.
+
+  If you use this device, you probably also want to enable the NFTL
+  'NAND Flash Translation Layer' option below, which is used to emulate
   a block device by using a kind of filesystem on the flash chips.
 
-M-Systems Disk-On-Chip Millennium support
+Alternative Disk-On-Chip Millennium support
 CONFIG_MTD_DOC2001
-  This provides an MTD device driver for the M-Systems DiskOnChip
-  Millennium devices. If you use this, you probably also want the
-  NFTL 'NAND Flash Translation Layer' below, which is used to emulate
+  This provides an alternative MTD device driver for the M-Systems
+  DiskOnChip Millennium devices. Use this if you have problems with
+  the combined DiskOnChip 2000 and Millennium driver above. To get
+  the DiskOnChip probe code to load and use this driver instead of
+  the other one, you will need to undefine DOC_SINGLE_DRIVER near
+  the beginning of drivers/mtd/docprobe.c
+
+  If you use this device, you probably also want to enable the NFTL
+  'NAND Flash Translation Layer' option below, which is used to emulate
   a block device by using a kind of filesystem on the flash chips.
 
+Ramix PMC551 PCI Mezzanine ram card support
+CONFIG_MTD_PMC551
+  This provides a MTD device driver for the Ramix PMC551 RAM PCI card
+  from Ramix Inc. (http://www.ramix.com/products/memory/pmc551.html).
+  These devices come in memory configurations from 32M - 1G.  If you
+  have one, you probably want to enable this.
+
+  If this driver is compiled as a module you get the ability to select the
+  size of the aperture window pointing into the devices memory.  What this
+  means is that if you have a 1G card, normally the kernel will use a 1G
+  memory map as it's view of the device.  As a module, you can select a
+  1M window into the memory and the driver will "slide" the window around
+  the PMC551's memory.  This was particularly useful on the 2.2 kernels
+  on PPC architectures as there was limited kernel space to deal with.
+
 Use extra onboard system memory as MTD device
 CONFIG_MTD_SLRAM
   If your CPU cannot cache all of the physical memory in your machine,
   you can still use it for storage or swap by using this driver to
   present it to the system as a Memory Technology Device.
 
-Ramix PMC551 PCI Mezzanine ram card support
-CONFIG_MTD_PMC551
-  This provides an MTD device driver for the Ramix PMC551 RAM card.
-  If you have one, you probably want to enable this.
-
-PMC551 256M DRAM Bugfix.
+PMC551 256M DRAM Bugfix
 CONFIG_MTD_PMC551_BUGFIX
-  Some PMC551 boards hacve invalid column and row mux values. This 
-  option will fix them, but will break other memory configurations.
+  Some of Ramix's PMC551 boards with 256M configurations have invalid
+  column and row mux values. This option will fix them, but will break
+  other memory configurations.  If unsure say N.
+
+PMC551 Debugging
+CONFIG_MTD_PMC551_DEBUG
+  This option makes the PMC551 more verbose during it's operation and is
+  only really useful if you are developing on this driver or suspect a
+  possible hardware or driver bug.  If unsure say N.
 
 Debugging RAM test driver
 CONFIG_MTD_MTDRAM
-  This enables a test MTD device driver which uses vmalloc() to 
-  provide storage. You probably want to say 'N' unless you're 
-  testing stuff, or unless you want to use it in place of a ramdisk
-  when I've eventually got round to making the CONFIG_BLK_DEV option
-  and you've turned it off.
+  This enables a test MTD device driver which uses vmalloc() to
+  provide storage. You probably want to say 'N' unless you're
+  testing stuff.
 
 Common Flash Interface (CFI) support
 CONFIG_MTD_CFI
-  Intel's Common Flash Interface specification provides a universal
-  method for probing the capabilities of flash devices. If you wish
-  to support any device which uses CFI-compliant devices, you need
-  to enable this option.
+  The Common Flash Interface specification was developed by Intel,
+  AMD and other flash manufactures that provides a universal method
+  for probing the capabilities of flash devices. If you wish to
+  support any device that is CFI-compliant, you need to enable this
+  option. Visit (http://www.amd.com/products/nvd/overview/cfi.html)
+  for more information on CFI.
 
 CFI support for Intel/Sharp Extended Command Set chips
 CONFIG_MTD_CFI_INTELEXT
@@ -10122,10 +10275,10 @@
 Flash chip mapping in physical memory
 CONFIG_MTD_PHYSMAP
   This provides a 'mapping' driver which allows the CFI probe and
-  command set driver code to communicate with flash chips which 
+  command set driver code to communicate with flash chips which
   are mapped physically into the CPU's memory. You will need to
   configure the physical address and size of the flash chips on
-  your particular board.
+  your particular board as well as the bus width.
 
 Physical start location of flash chip mapping
 CONFIG_MTD_PHYSMAP_START
@@ -10143,38 +10296,51 @@
   map which should hopefully be in the documentation for your
   board. 
 
+CONFIG_MTD_PHYSMAP_BUSWIDTH
+  This is the total width of the data bus of the flash devices
+  in octets. For example, if you have a data bus width of 32
+  bits, you would set the bus width octect value to 4. This is
+  used internally by the CFI drivers.
+
 Flash chip mapping on Mixcom piggyback card
 CONFIG_MTD_MIXMEM
   This supports the paging arrangement for access to flash chips
-  on the Mixcom piggyback card, allowing the flash chip drivers 
-  to get on with their job of driving the flash chips without 
+  on the MixCOM piggyback card, allowing the flash chip drivers
+  to get on with their job of driving the flash chips without
   having to know about the paging. If you have one of these boards,
-  you probably want to enable this mapping driver.
+  you probably want to enable this mapping driver. More info is at
+  (http://www.itc.hu/).
 
 Flash chip mapping on Nora
 CONFIG_MTD_NORA
   If you had to ask, you don't have one. Say 'N'.
 
+Flash chip mapping on PNC2000
+CONFIG_MTD_PNC2000
+  PNC-2000 is the name of Network Camera product from PHOTRON
+  Ltd. in Japan. It uses CFI-compliant flash.
+
 Flash chip mapping on Octagon 5066 SBC
 CONFIG_MTD_OCTAGON
   This provides a 'mapping' driver which supports the way in which
   the flash chips are connected in the Octagon-5066 Single Board
-  Computer. You will also need to complete and enable the driver
-  for JEDEC flash chips.
+  Computer. More information on the board is available at
+  (http://www.octagonsystems.com/Products/5066/5066.html).
 
 Flash chip mapping on RPXlite PPC board
 CONFIG_MTD_RPXLITE
-  The RPXLite PowerPC board has CFI-compliant chips mapped in 
+  The RPXLite PowerPC board has CFI-compliant chips mapped in
   a strange sparse mapping. This 'mapping' driver supports that
   arrangement, allowing the CFI probe and command set driver code
-  to communicate with the chips on the RPXLite board.
+  to communicate with the chips on the RPXLite board. More at
+  (http://www.embeddedplanet.com/rpx_lite_specification_sheet.htm).
 
 Flash chip mapping on Tempustech VMAX SBC301
 CONFIG_MTD_VMAX
   This provides a 'mapping' driver which supports the way in which
   the flash chips are connected in the Tempustech VMAX SBC301 Single
-  Board Computer. You will also need to complete and enable the driver
-  for JEDEC flash chips.
+  Board Computer. More information on the board is available at
+  (http://www.tempustech.com/tt301.htm).
 
 Direct chardevice access to MTD devices
 CONFIG_MTD_CHAR
@@ -10195,15 +10361,15 @@
 
   Later, it may be extended to perform read/erase/modify/write cycles
   on flash chips to emulate a smaller block size. Needless to say,
-  this is very unsafe, but could be useful for filesystems which are
+  this is very unsafe, but could be useful for file systems which are
   almost never written to.
 
 FTL (Flash Translation Layer) support
 CONFIG_FTL
   This provides support for the original Flash Translation Layer which
   is part of the PCMCIA specification. It uses a kind of pseudo-
-  filesystem on a flash device to emulate a block device with 512-byte
-  sectors, on top of which you put a 'normal' filesystem. You may find
+  file system on a flash device to emulate a block device with 512-byte
+  sectors, on top of which you put a 'normal' file system. You may find
   that the algorithms used in this code are patented unless you live
   in the Free World where software patents aren't legal - in the USA
   you are only permitted to use this on PCMCIA hardware, although 
@@ -10214,8 +10380,8 @@
 CONFIG_NFTL
   This provides support for the NAND Flash Translation Layer which is
   used on M-Systems' DiskOnChip devices. It uses a kind of pseudo-
-  filesystem on a flash device to emulate a block device with 512-byte
-  sectors, on top of which you put a 'normal' filesystem. You may find
+  file system on a flash device to emulate a block device with 512-byte
+  sectors, on top of which you put a 'normal' file system. You may find
   that the algorithms used in this code are patented unless you live
   in the Free World where software patents aren't legal - in the USA
   you are only permitted to use this on DiskOnChip hardware, although 
@@ -10224,8 +10390,8 @@
 
 Write support for NFTL (EXPERIMENTAL)
 CONFIG_NFTL_RW
-  If you're lucky, this will actually work. Don't whinge if it doesn't.
-  Contact dwmw2@infradead.org if you want to help to make it more 
+  If you're lucky, this will actually work. Don't whine if it doesn't.
+  Contact (dwmw2@infradead.org) if you want to help to make it more
   reliable.
 
 Support for USB
@@ -10399,6 +10565,16 @@
 
   If unsure, say Y.
 
+Input core support
+CONFIG_INPUT
+  Say Y here if you want to enable any of the following options for
+  USB Human Interface Device (HID) support.
+
+  Say Y here if you want to enable any of the USB HID options in the
+  USB support section which require Input core support.
+
+  Otherwise, say N.
+
 Keyboard support
 CONFIG_INPUT_KEYBDEV
   Say Y here if you want your USB HID keyboard (or an ADB keyboard
@@ -11072,23 +11248,44 @@
 
   If unsure, say N.
    
+Virtual memory file system support
+CONFIG_TMPFS
+  Tmpfs is a file system which keeps all files in virtual memory.
+
+  In contrast to RAM disks, which get allocated a fixed amount of
+  physical RAM, tmpfs grows and shrinks to accommodate the files it
+  contains and is able to swap unneeded pages out to swap space.
+
+  Everything is "virtual" in the sense that no files will be created
+  on your hard drive; if you reboot, everything in tmpfs will be
+  lost.
+  
+  You should mount the filesystem somewhere to be able to use
+  POSIX shared memory. Adding the following line to /etc/fstab should
+  take care of things:
+
+  tmpfs		/dev/shm	tmpfs		defaults	0 0
+
+  Remember to create the directory that you intend to mount tmpfs on
+  if necessary (/dev/shm is automagically created if you use devfs).
+
+  You can set limits for the number of blocks and inodes used by the
+  filesystem with the mount options "size", "nr_blocks" and
+  "nr_inodes". These parameters accept a suffix k, m or g for kilo,
+  mega and giga and can be changed on remount.
+  
+  The initial permissions of the root directory can be set with the
+  mount option "mode".
+
 Simple RAM-based file system support
 CONFIG_RAMFS
   Ramfs is a file system which keeps all files in RAM. It allows
   read and write access.
 
-  In contrast to RAM disks, which get allocated a fixed amount of RAM,
-  ramfs grows and shrinks to accommodate the files it contains.
+  It is more of an programming example than a useable filesystem. If
+  you need a file system which lives in RAM with limit checking use
+  tmpfs.
 
-  Before you can use this RAM-based file system, it has to be mounted,
-  meaning it has to be given a location in the directory hierarchy. If
-  you want to use the location /ramfiles for example, you would have
-  to create that directory first and then mount the file system by
-  saying "mount -t ramfs ramfs /ramfiles" or the equivalent line in
-  /etc/fstab. Everything is "virtual" in the sense that no files will
-  be created on your hard drive; if you reboot, everything in
-  /ramfiles will be lost.
-  
   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. The module will be
@@ -11634,21 +11831,16 @@
   whenever you want), say M here and read Documentation/modules.txt.
   The module will be called efs.o. 
 
-Journalling Flash File System (JFFS) support (EXPERIMENTAL)
+Support for the Journalling Flash Filesystem
 CONFIG_JFFS_FS
-  JFFS is a new file system designed for use on flash memory devices
-  rather than on block devices. It was developed on the 2.0 kernel
-  by Axis Communications AB for use on their Linux-based products,
-  and released under GPL, then 'borrowed' and ported to work with 
-  the 2.4 kernel and the new Memory Technology Device system. 
-
-  The 2.4 port is experimental and not yet supported by Axis. Basically,
-  the good bits are probably theirs, and if it's broken in 2.4 it's 
-  probably our fault. See http://www.developer.axis.com/software/jffs/
-  for more information about JFFS.
-
-  Any potential patches or queries should be sent to Axis' mailing 
-  list for JFFS: <jffs-dev@axis.com>
+  JFFS is the Journaling Flash File System developed by Axis
+  Communications in Sweden, aimed at providing a crash/powerdown-safe
+  filesystem for disk-less embedded devices. Further information is
+  available at (http://developer.axis.com/software/jffs/).
+
+JFFS debugging verbosity
+CONFIG_JFFS_FS_VERBOSE
+  Determines the verbosity level of the JFFS debugging messages.
 
 UFS file system support (read-only)
 CONFIG_UFS_FS
@@ -11723,6 +11915,12 @@
   file system support", above. If you don't know what all this is
   about, say N.
 
+Minix subpartition support
+CONFIG_MINIX_SUBPARTITION
+  Minix 2.0.0/2.0.2 subpartition table support for Linux.
+  Say Y here if you want to mount and use Minix 2.0.0/2.0.2
+  subpartitions.
+
 Sun partition tables support
 CONFIG_SUN_PARTITION
   Like most systems, SunOS uses its own hard disk partition table
@@ -11749,6 +11947,12 @@
   Say Y here if you would like to be able to read the hard disk
   partition table format used by SGI machines.
 
+Ultrix partition support
+CONFIG_ULTRIX_PARTITION
+  Say Y here if you would like to be able to read the hard disk
+  partition table format used by DEC (now Compaq) Ultrix machines.
+  Otherwise, say N.
+
 ADFS file system support (EXPERIMENTAL)
 CONFIG_ADFS_FS
   The Acorn Disc Filing System is the standard file system of the
@@ -11998,7 +12202,7 @@
 
 nls default codepage
 CONFIG_NLS_DEFAULT
-  The default NLS used when mounting filesystem. Currently, the valid
+  The default NLS used when mounting file system. Currently, the valid
   values are:
   big5, cp437, cp737, cp775, cp850, cp852, cp855, cp857, cp860, cp861,
   cp862, cp863, cp864, cp865, cp866, cp869, cp874, cp932, cp936,
@@ -12187,7 +12391,7 @@
 
 nls codepage 932
 CONFIG_NLS_CODEPAGE_932
-  The Microsoft fat filesystem family can deal with filenames in
+  The Microsoft FAT file system family can deal with filenames in
   native language character sets. These character sets are stored in
   so-called DOS codepages. You need to include the appropriate
   codepage if you want to be able to read/write these filenames on
@@ -12199,7 +12403,7 @@
 
 nls codepage 936
 CONFIG_NLS_CODEPAGE_936
-  The Microsoft fat filesystem family can deal with filenames in
+  The Microsoft FAT file system family can deal with filenames in
   native language character sets. These character sets are stored in
   so-called DOS codepages. You need to include the appropriate
   codepage if you want to be able to read/write these filenames on
@@ -12210,7 +12414,7 @@
 
 nls codepage 949
 CONFIG_NLS_CODEPAGE_949
-  The Microsoft fat filesystem family can deal with filenames in
+  The Microsoft FAT file system family can deal with filenames in
   native language character sets. These character sets are stored in
   so-called DOS codepages. You need to include the appropriate
   codepage if you want to be able to read/write these filenames on
@@ -12220,7 +12424,7 @@
 
 nls codepage 950
 CONFIG_NLS_CODEPAGE_950
-  The Microsoft fat filesystem family can deal with filenames in
+  The Microsoft FAT file system family can deal with filenames in
   native language character sets. These character sets are stored in
   so-called DOS codepages. You need to include the appropriate
   codepage if you want to be able to read/write these filenames on
@@ -13742,17 +13946,32 @@
   module, say M here and read Documentation/modules.txt. Most people
   will say N.
 
+ZF MachZ Watchdog
+CONFIG_MACHZ_WDT
+  If you are using a ZF Micro MachZ processor, say Y here, otherwise N.
+  This is the driver for the watchdog timer builtin on that processor
+  using ZF-Logic interface. This watchdog simply watches your kernel to
+  make sure it doesn't freeze, and if it does, it reboots your computer
+  after a certain amount of time.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module is called machzwd.o. If you want to compile it as a module,
+  say M here and read Documentation/modules.txt.
+
 Toshiba Laptop support
 CONFIG_TOSHIBA
-  If you intend to run this the kernel on a Toshiba portable say yes
-  here. This adds a driver to safely access the System Management
-  Mode of the CPU on Toshiba portables. The System Management Mode
+  This adds a driver to safely access the System Management Mode
+  of the CPU on Toshiba portables. The System Management Mode
   is used to set the BIOS and power saving options on Toshiba portables.
 
   For information on utilities to make use of this driver see the
-  Toshiba Linux utilities website at:
+  Toshiba Linux utilities web site at:
   http://www.buzzard.org.uk/toshiba/
 
+  Say Y if you intend to run this kernel on a Toshiba portable.
+  Say N otherwise.
+
 /dev/cpu/microcode - Intel IA32 CPU microcode support
 CONFIG_MICROCODE
   If you say Y here and also to "/dev file system support" in the
@@ -13787,6 +14006,18 @@
   with major 203 and minors 0 to 31 for /dev/cpu/0/cpuid to
   /dev/cpu/31/cpuid.
 
+SBC-60XX Watchdog Timer
+CONFIG_60XX_WDT
+ This driver can be used with the watchdog timer found on some
+ single board computers, namely the 6010 PII based computer.
+ It may well work with other cards.  It reads port 0x443 to enable
+ and re-set the watchdog timer, and reads port 0x45 to disable
+ the watchdog.  If you have a card that behave in similar ways,
+ you can probably make this driver work with your card as well.
+
+ You can compile this driver directly into the kernel, or use
+ it as a module.  The module will be called sbc60xxwdt.o.
+
 Enhanced Real Time Clock Support
 CONFIG_RTC
   If you say Y here and create a character special file /dev/rtc with
@@ -14253,6 +14484,8 @@
   16 or Logitech SoundMan 16 sound card. Answer N if you have some
   other card made by Media Vision or Logitech since those are not
   PAS16 compatible. Please read Documentation/sound/PAS16.
+  It is not necessary to add Sound Blaster support separately; it
+  is included in PAS support.
 
   If you compile the driver into the kernel, you have to add
   "pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2>
@@ -14459,13 +14692,15 @@
   See Documentation/sound/CS4232 for more information on configuring
   this card.
 
-Support for Yamaha OPL3-SA2, SA3, and SAx based PnP cards
+Support for Yamaha OPL3-SA2 and SA3 based PnP cards
 CONFIG_SOUND_OPL3SA2
   Say Y or M if you have a card based on one of these Yamaha
-  sound chipsets. Read Documentation/sound/OPL3-SA2 for more
-  information on configuring these cards.
+  sound chipsets or the "SAx", which is actually a SA3. Read
+  Documentation/sound/OPL3-SA2 for more information on configuring
+  these cards.
 
-  If you compile the driver into the kernel, you have to add
+  If you compile the driver into the kernel and do not also
+  configure in the optional ISA PnP support, you will have to add
   "opl3sa2=<io>,<irq>,<dma>,<dma2>,<mssio>,<mpuio>" to the kernel
   command line.
 
@@ -14647,7 +14882,7 @@
 
 SC-6600 CDROM Interface
 CONFIG_SC6600_CDROM
-  This is used to activate the the CDROM interface of the Audio Excel
+  This is used to activate the CDROM interface of the Audio Excel
   DSP 16 card. Enter: 0 for Sony, 1 for Panasonic, 2 for IDE, 4 for no
   CDROM present.
 
@@ -14661,6 +14896,11 @@
   driver as a module you have to specify the MPU I/O base address with
   the parameter 'mpu_base=0xNNN'.
 
+C-Media PCI (CMI8338/8378)
+CONFIG_SOUND_CMPCI
+  Say Y or M if you have a PCI sound card using the CMI8338
+  or the CMI8378 chip.set.
+
 Creative EMU10K1 based PCI sound cards
 CONFIG_SOUND_EMU10K1
   Say Y or M if you have a PCI sound card using the EMU10K1
@@ -16523,6 +16763,68 @@
   from the running kernel whenever you want). If you want to compile
   it as a module, say M here and read Documentation/modules.txt.
 
+IBM's S/390 architecture
+CONFIG_ARCH_S390
+  Select this option, if you want to run the Kernel on one of IBM's
+  mainframes of the S/390 generation. You should have installed the
+  s390-compiler released by IBM (based on gcc-2.95.1) before.
+
+Merge some code into the kernel to make the image IPLable
+CONFIG_IPLABLE
+  If you want to use the produced kernel to IPL directly from a
+  device, you have to merge a bootsector specific to the device
+  into the first bytes of the kernel. You will have to select the
+  IPL device on another question, that pops up, when you select
+  CONFIG_IPLABE.
+
+IPL from a /390 tape unit
+CONFIG_IPL_TAPE
+  Select this option if you want to IPL the image from a Tape.
+
+IPL from a virtual card reader emulated by VM/ESA
+CONFIG_IPL_RDR_VM
+  Select this option if you are running under VM/ESA and want 
+  to IPL the image from the emulated card reader.
+
+IPL from a real card reader
+CONFIG_IPL_RDR
+  Select this option if you want to IPL the image from a real
+  card reader. Maybe you still got one and want to try. We didn't
+  test.
+
+IBMs S/390 Harddisks (DASDs)
+CONFIG_DASD
+  Enable this option if you want to access DASDs directly utilizing
+  S/390s channel subsystem commands. This is necessary for running
+  natively on a single image or an LPAR.
+
+Enable DASD fast write
+CONFIG_DASD_FAST_IO
+  Enable fast I/O for DASDs. That means that the next I/O command
+  is already issued at interrupt time, if an I/O request is pending.
+  This option gives significant speedup of I/O, because we don't
+  schedule the bottom-halves as often as Intel.
+
+Support for IBM-style disk-labels (S/390)
+CONFIG_S390_PARTITION
+  Enable this option to assure standard IBM labels on the DASDs.
+  You must enable it, if you are planning to access DASDs also
+  attached to another IBM mainframe operation system (OS/390, 
+  VM/ESA, VSE/ESA).
+
+ECKD devices
+CONFIG_DASD_ECKD
+  ECKD devices are the most commonly used devices. you should enable
+  this option unless you are very sure to have no ECKD device.
+
+CKD devices
+CONFIG_DASD_CKD
+  CKD devices are currently unsupported.
+
+FBA devices
+CONFIG_DASD_FBA
+  FBA devices are currently unsupported.
+
 SAB3036 tuner support
 CONFIG_TUNER_3036
   Say Y here to include support for Philips SAB3036 compatible tuners.
@@ -16559,7 +16861,7 @@
   Saying N will reduce the size of the Footbridge kernel.
 
 Include support for the EBSA285
-CONFIG_ARCH_EBSA285
+CONFIG_ARCH_EBSA285_HOST
   Say Y here if you intend to run this kernel on the EBSA285 card
   in host ("central function") mode.
 
@@ -16645,50 +16947,14 @@
   http://www.visuaide.com/pagevictor.en.html for information on
   this system.
 
-Support ARM610 processor
-CONFIG_CPU_ARM6
-  Say Y here if you wish to include support for the ARM610 processor.
-
-Support ARM710 processor
-CONFIG_CPU_ARM7
-  Say Y here if you wish to include support for the ARM710 processor.
-
-Support StrongARM(R) SA-110 processor
-CONFIG_CPU_SA110
-  Say Y here if you wish to include support for the Intel(R)
-  StrongARM(R) SA-110 processor.
-
-Support ARM720 processor
-CONFIG_CPU_ARM720
-  Say Y here if you wish to include support for the ARM720 processor.
-
-Support ARM920
-CONFIG_CPU_ARM920
-  Say Y here if you wish to include support for the ARM920 processor.
-
-Support ARM610 processor
-CONFIG_CPU_ARM6
-  Say Y here if you wish to include support for the ARM610 processor.
-
-Support ARM710 processor
-CONFIG_CPU_ARM7
-  Say Y here if you wish to include support for the ARM710 processor.
-
-Support StrongARM(R) SA-110 processor
-CONFIG_CPU_SA110
-  Say Y here if you wish to include support for the Intel(R)
-  StrongARM(R) SA-110 processor.
-
-Support ARM720 processor
-CONFIG_CPU_ARM720
-  Say Y here if you wish to include support for the ARM720 processor.
-
-Support ARM920
-CONFIG_CPU_ARM920
-  Say Y here if you wish to include support for the ARM920 processor.
+Load kernel using Angel Debug Monitor
+CONFIG_ANGELBOOT
+  Say Y if you plan to load the kernel using Angel, ARM Ltd's target
+  debug stub.  If you are not using Angel, you must say N.  It is
+  important to get this setting correct.
 
 Math emulation
-CONFIG_NWFPE
+CONFIG_FPE_NWFPE
   Say Y to include the NWFPE floating point emulator in the kernel.
   This is necessary to run most binaries. Linux does not currently
   support floating point hardware so you need to say Y here even if
@@ -16702,6 +16968,23 @@
   You may say N here if you are going to load the Acorn FPEmulator
   early in the bootup.
 
+CONFIG_FPE_FASTFPE
+  Say Y here to include the FAST floating point emulator in the kernel.
+  This is an experimental much faster emulator which has only 32 bit
+  precision for the mantissa. It does not support any exceptions. This
+  makes it very simple, it is approximately 4-8 times faster than NWFPE.
+
+  It should be sufficient for most programs. It is definitely not
+  suitable if you do scientific calculations that need double precision
+  for iteration formulas that sum up lots of very small numbers. If you
+  do not feel you need a faster FP emulation you should better choose
+  NWFPE.
+
+  It is also possible to say M to build the emulator as a module
+  (fastfpe.o). But keep in mind that you should only load the FP emulator
+  early in the bootup. You should never change from NWFPE to FASTFPE or
+  vice versa in an active system!
+
 DS1620 Thermometer support
 CONFIG_DS1620
   Say Y here to include support for the thermal management hardware
@@ -16721,11 +17004,12 @@
   you are concerned with the code size or don't want to see these
   messages.
 
-Compile kernel with frame pointer
-CONFIG_FRAME_POINTER
-  If you say Y here, the resulting kernel will be slightly larger and
-  slower, but it will give useful debugging information. If you don't
-  debug the kernel, you can say N.
+Compile kernel without frame pointer
+CONFIG_NO_FRAME_POINTER
+  If you say Y here, the resulting kernel will be slightly smaller and
+  faster. However, when a problem occurs with the kernel, the
+  information that is reported is severely limited. Most people
+  should say N here.
 
 User fault debugging
 CONFIG_DEBUG_USER
@@ -16833,6 +17117,21 @@
   you can make the first serial port the console by answering Y to
   this option.
 
+L7200 SDB keyboard support
+CONFIG_KEYBOARD_L7200
+  Enable this option if you would like to be able to use a keyboard
+  on a LinkUp Systems L7200 board.
+
+L7200 SDB Fujitsu keyboard support
+CONFIG_KEYBOARD_L7200_NORM
+  Select the Fujitsu keyboard if you want a normal QWERTY style
+  keyboard on the LinkUp SDB.
+
+L7200 SDB Prototype keyboard support
+CONFIG_KEYBOARD_L7200_DEMO
+  Select the prototype keyboard if you want to play with the
+  LCD/keyboard combination on the LinkUp SDB.
+
 Footbridge Mode
 CONFIG_HOST_FOOTBRIDGE
   The 21285 Footbridge chip can operate in either `host mode' or
@@ -16903,8 +17202,7 @@
   infrared communication and is supported by most laptops and PDA's.
 
   To use Linux support for the IrDA (tm) protocols, you will also need
-  some user-space utilities like the irmanager and probably irattach
-  as well. For more information, see the file
+  some user-space utilities like irattach. For more information, see the file
   Documentation/networking/irda.txt. You also want to read the
   IR-HOWTO, available at http://www.linuxdoc.org/docs.html#howto .
 
@@ -16912,6 +17210,10 @@
   want to compile it as a module, say M here and read
   Documentation/modules.txt.
 
+IrDA protocol options
+CONFIG_IRDA_OPTIONS
+  Say Y here if you want to configure any of the following IrDA options.
+
 IrDA Cache last LSAP
 CONFIG_IRDA_CACHE_LAST_LSAP
   Say Y here if you want IrLMP to cache the last LSAP used. This makes
@@ -16976,6 +17278,17 @@
   to another Linux machine running the IrLAN protocol for ad-hoc 
   networking!
 
+IrNET Protocol 
+CONFIG_IRNET
+  Say Y here if you want to build support for the IrNET protocol. If
+  you want to compile it as a module (irnet.o), say M here and read
+  Documentation/modules.txt. IrNET is a PPP driver, so you will also
+  need a working PPP subsystem (driver, daemon and config)...
+
+  IrNET is an alternate way to tranfer TCP/IP traffic over IrDA. It
+  uses synchronous PPP over a set of point to point IrDA sockets. You
+  can use it between Linux machine or with W2k.
+
 IrCOMM Protocol
 CONFIG_IRCOMM
   Say Y here if you want to build support for the IrCOMM protocol. If
@@ -17350,7 +17663,7 @@
 # LocalWords:  howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip
 # LocalWords:  proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS
 # LocalWords:  telnetting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl
-# LocalWords:  Mb SKB IPX Novell dosemu Appletalk DDP ATALK vmalloc visar ehome
+# LocalWords:  Mb SKB IPX Novell dosemu DDP ATALK vmalloc visar ehome
 # LocalWords:  SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA
 # LocalWords:  buslogic DMA DPT ATT eata dma PIO UltraStor fdomain umsdos ext
 # LocalWords:  QLOGIC qlogic TMC seagate Trantor ultrastor FASST wd NETDEVICES
@@ -17378,7 +17691,7 @@
 # LocalWords:  Brumby pci TNC cis ohio faq usenet NETLINK dev hydra ca Tyne mem
 # LocalWords:  carleton DECstation SUNFD JENSEN Noname XXXM SLiRP LILO's amifb
 # LocalWords:  pppd Zilog ZS SRM bootloader ez mainmenu rarp ipfwadm paride pcd
-# LocalWords:  RTNETLINK mknod xos MTU lwared Macs mac netatalk macs cs Wolff
+# LocalWords:  RTNETLINK mknod xos MTU lwared Macs netatalk macs cs Wolff
 # LocalWords:  dartmouth flowerpt MultiMaster FlashPoint tudelft etherexpress
 # LocalWords:  ICL EtherTeam ETH IDESCSI TXC SmartRAID SmartCache httpd sjc dlp
 # LocalWords:  thesphere TwoServers BOOTP DHCP ncpfs BPQETHER BPQ MG HIPPI cern
@@ -17395,7 +17708,7 @@
 # LocalWords:  Keepalive linefill RELCOM keepalive analogue CDR conf CDI INIT
 # LocalWords:  OPTi isp irq noisp VFAT vfat NTFS losetup dmsdosfs dosfs ISDN MP
 # LocalWords:  NOWAYOUT behaviour dialin isdn callback BTX Teles  XXXX LVM lvm
-ICN EDSS Cisco
+# LocalWords:  ICN EDSS Cisco
 # 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
@@ -17522,7 +17835,7 @@
 # LocalWords:  AudioPCI lspci SonicVibes sonicvibes SPARCs roadrunner CLgen UPA
 # LocalWords:  swansea shtml Zoltrix zoltrix BINUTILS EGCS binutils VIDC DACs
 # LocalWords:  CyberVision Cirrus PowerBooks Topcat SBUS CGsix TurboGX BWtwo SS
-# LocalWords:  CGthree TCX unswapable vfb fbcon hicolor truecolor AFB ILBM SOC
+# LocalWords:  CGthree TCX unswappable vfb fbcon hicolor truecolor AFB ILBM SOC
 # LocalWords:  IPLAN gracilis Fibre SBus SparcSTORAGE SV jnewbigin swin QNX qnx
 # LocalWords:  PTY PTYS ptyxx ttyxx PTYs ssh sb Avance ALS pss pvv kerneli hd
 # LocalWords:  synth WaveFront MSND NONPNP AudioExcelDSP STRAM APUS CHRP MBX Nx
diff -urN linux-2.4.4-pre2/Documentation/DocBook/parportbook.tmpl linux/Documentation/DocBook/parportbook.tmpl
--- linux-2.4.4-pre2/Documentation/DocBook/parportbook.tmpl	Wed Jul 12 16:24:33 2000
+++ linux/Documentation/DocBook/parportbook.tmpl	Mon Dec  3 22:34:49 2001
@@ -2079,7 +2079,7 @@
                               | PARPORT_STATUS_BUSY);
         unsigned char val = (PARPORT_STATUS_ERROR
                               | PARPORT_STATUS_BUSY);
-        struct parport_frob_struct frob;
+        struct ppdev_frob_struct frob;
         struct timespec ts;
 
         /* Wait for printer to be ready */
diff -urN linux-2.4.4-pre2/Documentation/networking/wan-router.txt linux/Documentation/networking/wan-router.txt
--- linux-2.4.4-pre2/Documentation/networking/wan-router.txt	Fri Jul 28 12:50:52 2000
+++ linux/Documentation/networking/wan-router.txt	Mon Dec  3 22:34:49 2001
@@ -1,27 +1,12 @@
 ------------------------------------------------------------------------------
-WAN Router for Linux Operating System
+Linux WAN Router Utilities Package
 ------------------------------------------------------------------------------
-Version 2.1.1 - Nov 08, 1999
-Version 2.0.8 - Nov 02, 1999
-Version 2.0.7 - Aug 26, 1999
-Version 2.0.6 - Aug 17, 1999
-Version 2.0.5 - Aug 12, 1999
-Version 2.0.4 - Nov 26, 1998
-Version 2.0.3 - Aug 25, 1998
-Version 2.0.2 - Dec 09, 1997
-Version 2.0.1 - Nov 28, 1997
-Version 2.0.0 - Nov 06, 1997
-Version 1.0.3 - June 3, 1997
-Version 1.0.1 - January 30, 1997
+Version 2.2.1 
+Mar 28, 2001
 Author: Nenad Corbic <ncorbic@sangoma.com>
-Copyright (c) 1995-1999 Sangoma Technologies Inc.
+Copyright (c) 1995-2001 Sangoma Technologies Inc.
 ------------------------------------------------------------------------------
 
-
-WARNING:  This Version of WANPIPE supports only the S508 and S508/FT1 cards.  
-IF YOU OWN A S502E OR A S508 CARD THEN PLEASE CONTACT SANGOMA TECHNOLOGIES FOR 
-AN UPGRADE.  ONLY THE BiSYNC STREAMING CODE IS SUPPORTED ON S502E/S503 cards. 
-
 INTRODUCTION
 
 Wide Area Networks (WANs) are used to interconnect Local Area Networks (LANs)
@@ -41,7 +26,7 @@
 Unix-like operating system anyway). With a number of relatively inexpensive WAN
 interface cards available on the market, a perfectly usable router can be
 built for less than half a price of an external router.  Yet a Linux box
-acting as a router can still be used for other purposes, such as firewalling,
+acting as a router can still be used for other purposes, such as fire-walling,
 running FTP, WWW or DNS server, etc.
 
 This kernel module introduces the notion of a WAN Link Driver (WLD) to Linux
@@ -87,15 +72,28 @@
 To ba able to use the Linux WAN Router you will also need a WAN Tools package
 available from
 
-	ftp.sangoma.com/pub/linux/vX.Y.Z/wantools-X.Y.Z.tgz
-			or
-	ftp.sangoma.com/pub/linux/vX.Y.Z/wanpipe-X.Y.Z.tgz
+	ftp.sangoma.com/pub/linux/current_wanpipe/wanpipe-X.Y.Z.tgz
+
+where vX.Y.Z represent the wanpipe version number.
+
+For technical questions and/or comments please e-mail to ncorbic@sangoma.com.
+For general inquiries please contact Sangoma Technologies Inc. by
 
-where vX.Y.Z represent the Linux kernel version number.
+	Hotline:	1-800-388-2475	(USA and Canada, toll free)
+	Phone:		(905) 474-1990  ext: 106
+	Fax:		(905) 474-9223
+	E-mail:		dm@sangoma.com	(David Mandelstam)
+	WWW:		http://www.sangoma.com
 
-For technical questions and/or comments regarding this product please e-mail
-to jaspreet@sangoma.com or dm@sangoma.com.
 
+INSTALLATION
+
+Please read the WanpipeForLinux.pdf manual on how to 
+install the WANPIPE tools and drivers properly. 
+
+
+After installing wanpipe package: /usr/local/wanrouter/doc. 
+On the ftp.sangoma.com : /linux/current_wanpipe/doc
 
 
 COPYRIGHT AND LICENSING INFORMATION
@@ -114,120 +112,511 @@
 
 
 
-ACKNOWLEDGMENTS
+ACKNOWLEDGEMENTS
 
 This product is based on the WANPIPE(tm) Multiprotocol WAN Router developed
-by Sangoma Technologies Inc. for Linux 1.2.x.  Release of Linux 2.0 in summer
-1996 commanded adequate changes to the WANPIPE code to take full advantage of
-new Linux features.  Instead of continuing developing proprietary interface
-specific to Sangoma WAN cards, we decided to put all hardware-independent code
-into a separate module and define two levels of interfaces - one for user-
-level applications and another for kernel-level WAN drivers.
+by Sangoma Technologies Inc. for Linux 2.0.x and 2.2.x.  Success of the WANPIPE
+together with the next major release of Linux kernel in summer 1996 commanded
+adequate changes to the WANPIPE code to take full advantage of new Linux
+features.
+
+Instead of continuing developing proprietary interface tied to Sangoma WAN
+cards, we decided to separate all hardware-independent code into a separate
+module and defined two levels of interfaces - one for user-level applications
+and another for kernel-level WAN drivers.  WANPIPE is now implemented as a
+WAN driver compliant with the WAN Link Driver interface.  Also a general
+purpose WAN configuration utility and a set of shell scripts was developed to 
+support WAN router at the user level.
 
 Many useful ideas concerning hardware-independent interface implementation
 were given by Mike McLagan <mike.mclagan@linux.org> and his implementation
 of the Frame Relay router and drivers for Sangoma cards (dlci/sdla).
 
+With the new implementation of the APIs being incorporated into the WANPIPE,
+a special thank goes to Alan Cox in providing insight into BSD sockets.
+
 Special thanks to all the WANPIPE users who performed field-testing, reported
 bugs and made valuable comments and suggestions that help us to improve this
 product.
 
 
 
+NEW IN THIS RELEASE
+
+	o Updated the WANCFG utility
+		Calls the pppconfig to configure the PPPD
+		for async connections.
+
+	o Added the PPPCONFIG utility
+		Used to configure the PPPD dameon for the
+		WANPIPE Async PPP and standard serial port.
+		The wancfg calls the pppconfig to configure
+		the pppd.
+
+	o Fixed the PCI autodetect feature.  
+		The SLOT 0 was used as an autodetect option
+		however, some high end PC's slot numbers start
+		from 0. 
+
+	o This release has been tested with the new backupd
+	  daemon release.
+	
+
+PRODUCT COMPONENTS AND RELATED FILES
+
+/etc: (or user defined)
+	wanpipe1.conf	default router configuration file
+
+/lib/modules/X.Y.Z/misc:
+	wanrouter.o	router kernel loadable module
+	af_wanpipe.o	wanpipe api socket module
+
+/lib/modules/X.Y.Z/net:
+	sdladrv.o	Sangoma SDLA support module
+	wanpipe.o	Sangoma WANPIPE(tm) driver module
+
+/proc/net/wanrouter
+	Config		reads current router configuration
+	Status		reads current router status
+	{name}		reads WAN driver statistics
+
+/usr/sbin:
+	wanrouter	wanrouter start-up script
+	wanconfig	wanrouter configuration utility
+	sdladump	WANPIPE adapter memory dump utility
+        fpipemon        Monitor for Frame Relay
+        cpipemon        Monitor for Cisco HDLC
+	ppipemon 	Monitor for PPP
+	xpipemon 	Monitor for X25
+	wpkbdmon        WANPIPE keyboard led monitor/debugger
+
+/usr/local/wanrouter:
+	README		this file
+	COPYING		GNU General Public License
+	Setup		installation script
+	Filelist	distribution definition file
+	wanrouter.rc	meta-configuration file 
+			(used by the Setup and wanrouter script)
+
+/usr/local/wanrouter/doc:
+	wanpipeForLinux.pdf 	WAN Router User's Manual
+
+/usr/local/wanrouter/patches:
+	wanrouter-v2213.gz  	patch for Linux kernels 2.2.11 up to 2.2.13.
+	wanrouter-v2214.gz	patch for Linux kernel 2.2.14. 
+	wanrouter-v2215.gz	patch for Linux kernels 2.2.15 to 2.2.17.
+	wanrouter-v2218.gz	patch for Linux kernels 2.2.18 and up.
+	wanrouter-v240.gz	patch for Linux kernel 2.4.0.  
+	wanrouter-v242.gz	patch for Linux kernel 2.4.2 and up.
+	wanrouter-v2034.gz	patch for Linux kernel 2.0.34
+	wanrouter-v2036.gz 	patch for Linux kernel 2.0.36 and up. 
+
+/usr/local/wanrouter/patches/kdrivers:
+	Sources of the latest WANPIPE device drivers.
+	These are used to UPGRADE the linux kernel to the newest
+	version if the kernel source has already been pathced with
+	WANPIPE drivers.
+
+/usr/local/wanrouter/samples:
+	interface	sample interface configuration file
+	wanpipe1.cpri 	CHDLC primary port
+     	wanpipe2.csec 	CHDLC secondary port
+     	wanpipe1.fr   	Frame Relay protocol
+     	wanpipe1.ppp  	PPP protocol ) 
+	wanpipe1.asy	CHDLC ASYNC protocol
+	wanpipe1.x25	X25 protocol
+	wanpipe1.stty	Sync TTY driver (Used by Kernel PPPD daemon)
+	wanpipe1.atty	Async TTY driver (Used by Kernel PPPD daemon)
+	wanrouter.rc	sample meta-configuration file
+
+/usr/local/wanrouter/util:
+	*		wan-tools utilities source code
+
+/usr/local/wanrouter/api/x25:
+	*		x25 api sample programs.
+/usr/local/wanrouter/api/chdlc:
+	*		chdlc api sample programs.
+/usr/local/wanrouter/api/fr:
+	*		fr api sample programs.
+/usr/local/wanrouter/config/wancfg:
+	wancfg		WANPIPE GUI configuration program.
+                        Creates wanpipe#.conf files. 
+/usr/local/wanrouter/config/cfgft1:
+	cfgft1		GUI CSU/DSU configuration program.
+
+/usr/include/linux:
+	wanrouter.h	router API definitions
+	wanpipe.h	WANPIPE API definitions
+	sdladrv.h	SDLA support module API definitions
+	sdlasfm.h	SDLA firmware module definitions
+	if_wanpipe.h	WANPIPE Socket definitions
+	if_wanpipe_common.h	WANPIPE Socket/Driver common definitions.
+	sdlapci.h	WANPIPE PCI definitions
+	
+
+/usr/src/linux/net/wanrouter:
+	*		wanrouter source code
+
+/var/log:
+	wanrouter	wanrouter start-up log (created by the Setup script)
+
+/var/lock:  (or /var/lock/subsys for RedHat)
+	wanrouter	wanrouter lock file (created by the Setup script)
+
+/usr/local/wanrouter/firmware:
+	fr514.sfm	Frame relay firmware for Sangoma S508/S514 card
+	cdual514.sfm	Dual Port Cisco HDLC firmware for Sangoma S508/S514 card
+	ppp514.sfm      PPP Firmware for Sangoma S508 and S514 cards
+	x25_508.sfm	X25 Firmware for Sangoma S508 card.
+
+
 REVISION HISTORY
 
-2.1.1   Nov 09, 1999 		- New code for S514PCI card
-                                - Completely redesigned drivers
-                                  fully tested and optimized.
-
-2.0.8   Nov 02, 1999            - Fixed up the X25API code.
-                                - Clear call bug fixed.i
-                                - Enabled driver for multi-card
-                                  operation.                                
-
-2.0.7   Aug 26, 1999		- Merged X25API code into WANPIPE.
-				- Fixed a memory leak for X25API
-				- Updated the X25API code for 2.2.X kernels.
-				- Improved NEM handling.
-
-2.0.6   Aug 17, 1999            - Kernel patch works for both 2.2.10 and 2.2.11 kernels
-                                - Fixed up 2.0.5 installation bugs
-                                - No functional difference between 2.0.6 and 2.0.5         
-
-2.0.5   Aug 12, 1999            - NEW PPP, interrupt drive code
-				- NEW X25 Xpipmon debugger
-				- Comments added to setup scripts
-				- Numerous bug fixes
-
-2.0.4	Nov 26, 1998		- NEW Cisco Dual Port support. 
-				- NEW support for BiSync Streaming API.
-				- NEW support for HDLC (LAPB) API.
-				- WANPIPE provides an API for application 
-				  development using the BSD socket interface.
-
-2.0.3 	Aug 25, 1998		- NEW support for Cisco HDLC, with cpipemon 
-				  utility for monitoring
-				- CIR support for Frame-relay
-				- Support for PAP and CHAP for ppp has been 
-				  implemented
-				- Dynamic IP assignment for PPP
-				- Multiple channel IPX support for Frame-relay 
-				  and X25
-				- Inverse Arp support for Frame-relay
-				- FT1 Configuration utility for linux
-				- Man Pages for router.conf, router, sdladump,
-				  cfgft1, fpipemon, ppipemon and cpipemon
-
-2.0.2	Dev 09, 1997	      - Implemented PAP and CHAP for ppp.
-
-2.0.1	Nov 28, 1997	      -	Protection of "enable_irq()" while
-                                "disable_irq()" has been enabled from any other
-                                routine (for Frame Relay, PPP and X25).
-			      - Added additional Stats for Fpipemon and Ppipemon
-                              - Improved Load Sharing for multiple boards.
-
-2.0.0   Nov 07, 1997          - Implemented protection of RACE conditions by
-                                critical flags for FRAME RELAY and PPP.
-                              - DLCI List interrupt mode implemented.
-                              - IPX support in FRAME RELAY and PPP.
-                              - IPX Server Support (MARS)
-                              - More driver specific stats included in FPIPEMON
-                                and PIPEMON.
-
-1.0.5   July 28, 1997         - Configurable T391,T392,N391,N392,N393 for Frame
-                                Relay in router.conf.
-                              - Configurable Memory Address through router.conf
-                                for Frame Relay, PPP and X.25. (commenting this
-                                out enables auto-detection).
-                              - Fixed freeing up received buffers using kfree()
-                                for Frame Relay and X.25.
-                              - Protect sdla_peek() by calling save_flags(),
-                                cli() and restore_flags().
-                              - Changed number of Trace elements from 32 to 20
-                              - Added DLCI specific data monitoring in FPIPEMON.
-
-1.0.4   July 10, 1997         - S508/FT1 monitoring capability in fpipemon and
-                                ppipemon utilities.
-                              - Configurable TTL for UDP packets.
-                              - Multicast and Broadcast IP source addresses are
-                                silently discarded.
-
-1.0.3	June 3, 1997
-	 		      -	UDP port for multiple boards (Frame relay, PPP)
-	 			Continuous Transmission of Configure Request 
-			      -	Packet for PPP (this support is only added for 
-			   	508 cards)
-	 		      -	Connection Timeout for PPP changed from 900 to 0
-	 	              -	Flow Control for multiple boards and multiple 
-				channels (Frame Relay)
-
-1.0.1	January 30, 1997
+1.0.0	December 31, 1996	Initial version
+
+1.0.1	January 30, 1997	Status and statistics can be read via /proc
+				filesystem entries.
+
+1.0.2   April 30, 1997          Added UDP management via monitors.
+
+1.0.3	June 3, 1997		UDP management for multiple boards using Frame
+				Relay and PPP
+				Enabled continuous transmission of Configure 
+				Request Packet for PPP (for 508 only)
+				Connection Timeout for PPP changed from 900 to 0
+				Flow Control Problem fixed for Frame Relay
+
+1.0.4	July 10, 1997		S508/FT1 monitoring capability in fpipemon and
+				ppipemon utilities.
+				Configurable TTL for UDP packets.
+				Multicast and Broadcast IP source addresses are
+				silently discarded.
+
+1.0.5	July 28, 1997		Configurable T391,T392,N391,N392,N393 for Frame
+				Relay in router.conf.
+				Configurable Memory Address through router.conf 
+				for Frame Relay, PPP and X.25. (commenting this
+ 				out enables auto-detection).
+				Fixed freeing up received buffers using kfree()
+ 				for Frame Relay and X.25.
+				Protect sdla_peek() by calling save_flags(),
+				cli() and restore_flags().
+				Changed number of Trace elements from 32 to 20
+				Added DLCI specific data monitoring in FPIPEMON. 
+2.0.0	Nov 07, 1997		Implemented protection of RACE conditions by 
+				critical flags for FRAME RELAY and PPP.
+				DLCI List interrupt mode implemented.
+				IPX support in FRAME RELAY and PPP.
+				IPX Server Support (MARS)
+				More driver specific stats included in FPIPEMON
+				and PIPEMON.
+
+2.0.1	Nov 28, 1997		Bug Fixes for version 2.0.0.
+				Protection of "enable_irq()" while 
+				"disable_irq()" has been enabled from any other
+				routine (for Frame Relay, PPP and X25).
+				Added additional Stats for Fpipemon and Ppipemon
+				Improved Load Sharing for multiple boards
+
+2.0.2	Dec 09, 1997		Support for PAP and CHAP for ppp has been
+				implemented.
+
+2.0.3	Aug 15, 1998		New release supporting Cisco HDLC, CIR for Frame
+				relay, Dynamic IP assignment for PPP and Inverse
+				Arp support for Frame-relay.  Man Pages are 
+				included for better support and a new utility
+				for configuring FT1 cards.
+
+2.0.4	Dec 09, 1998	        Dual Port support for Cisco HDLC.
+				Support for HDLC (LAPB) API.
+				Supports BiSync Streaming code for S502E 
+				and S503 cards.
+				Support for Streaming HDLC API.
+				Provides a BSD socket interface for 
+				creating applications using BiSync
+   				streaming.        
+
+2.0.5   Aug 04, 1999 		CHDLC initializatin bug fix.
+				PPP interrupt driven driver: 
+  				Fix to the PPP line hangup problem.
+				New PPP firmware
+				Added comments to the startup SYSTEM ERROR messages
+				Xpipemon debugging application for the X25 protocol
+				New USER_MANUAL.txt
+				Fixed the odd boundary 4byte writes to the board.
+				BiSync Streaming code has been taken out.  
+				 Available as a patch.
+				Streaming HDLC API has been taken out.  
+				 Available as a patch.                 
+
+2.0.6   Aug 17, 1999		Increased debugging in statup scripts
+				Fixed insallation bugs from 2.0.5
+				Kernel patch works for both 2.2.10 and 2.2.11 kernels.
+				There is no functional difference between the two packages         
+
+2.0.7   Aug 26, 1999		o  Merged X25API code into WANPIPE.
+				o  Fixed a memeory leak for X25API
+				o  Updated the X25API code for 2.2.X kernels.
+				o  Improved NEM handling.   
+
+2.1.0	Oct 25, 1999		o New code for S514 PCI Card
+				o New CHDLC and Frame Relay drivers
+				o PPP and X25 are not supported in this release    
+
+2.1.1	Nov 30, 1999		o PPP support for S514 PCI Cards
+
+2.1.3   Apr 06, 2000		o Socket based x25api 
+				o Socket based chdlc api
+				o Socket based fr api
+				o Dual Port Receive only CHDLC support.
+				o Asynchronous CHDLC support (Secondary Port)
+				o cfgft1 GUI csu/dsu configurator
+				o wancfg GUI configuration file 
+				  configurator.
+				o Architectual directory changes.
+
+beta-2.1.4 Jul 2000		o Dynamic interface configuration:
+					Network interfaces reflect the state
+					of protocol layer.  If the protocol becomes
+					disconnected, driver will bring down
+					the interface.  Once the protocol reconnects
+					the interface will be brought up. 
+					
+					Note: This option is turned off by default.
+
+				o Dynamic wanrouter setup using 'wanconfig':
+					wanconfig utility can be used to
+					shutdown,restart,start or reconfigure 
+					a virtual circuit dynamically.
+				     
+					Frame Relay:  Each DLCI can be: 
+						      created,stopped,restarted and reconfigured
+						      dynamically using wanconfig.
+					
+						      ex: wanconfig card wanpipe1 dev wp1_fr16 up
+				  
+				o Wanrouter startup via command line arguments:
+					wanconfig also supports wanrouter startup via command line
+					arguments.  Thus, there is no need to create a wanpipe#.conf
+					configuration file.  
+
+				o Socket based x25api update/bug fixes.
+					Added support for LCN numbers greater than 255.
+					Option to pass up modem messages.
+					Provided a PCI IRQ check, so a single S514
+					card is guaranteed to have a non-sharing interrupt.
+
+				o Fixes to the wancfg utility.
+				o New FT1 debugging support via *pipemon utilities.
+				o Frame Relay ARP support Enabled.
+
+beta3-2.1.4 Jul 2000		o X25 M_BIT Problem fix.
+				o Added the Multi-Port PPP
+				  Updated utilites for the Multi-Port PPP.
+
+2.1.4	Aut 2000
+				o In X25API:
+					Maximum packet an application can send
+					to the driver has been extended to 4096 bytes.
+
+					Fixed the x25 startup bug. Enable 
+					communications only after all interfaces
+					come up.  HIGH SVC/PVC is used to calculate
+					the number of channels.
+					Enable protocol only after all interfaces
+					are enabled.
+
+				o Added an extra state to the FT1 config, kernel module.
+				o Updated the pipemon debuggers.
+
+				o Blocked the Multi-Port PPP from running on kernels
+				  2.2.16 or greater, due to syncppp kernel module
+				  change. 
+	  
+beta1-2.1.5 	Nov 15 2000
+				o Fixed the MulitPort PPP Support for kernels 2.2.16 and above.
+				  2.2.X kernels only
+
+				o Secured the driver UDP debugging calls
+					- All illegal netowrk debugging calls are reported to
+					  the log.
+					- Defined a set of allowed commands, all other denied.
+					
+				o Cpipemon
+					- Added set FT1 commands to the cpipemon. Thus CSU/DSU
+					  configuraiton can be performed using cpipemon.
+					  All systems that cannot run cfgft1 GUI utility should
+					  use cpipemon to configure the on board CSU/DSU.
+
+
+				o Keyboard Led Monitor/Debugger
+					- A new utilty /usr/sbin/wpkbdmon uses keyboard leds
+					  to convey operatinal statistic information of the 
+					  Sangoma WANPIPE cards.
+					NUM_LOCK    = Line State  (On=connected,    Off=disconnected)
+					CAPS_LOCK   = Tx data     (On=transmitting, Off=no tx data)
+					SCROLL_LOCK = Rx data     (On=receiving,    Off=no rx data
+					
+				o Hardware probe on module load and dynamic device allocation
+					- During WANPIPE module load, all Sangoma cards are probed
+					  and found information is printed in the /var/log/messages.
+					- If no cards are found, the module load fails.
+					- Appropriate number of devices are dynamically loaded 
+					  based on the number of Sangoma cards found.
+
+					  Note: The kernel configuraiton option 
+						CONFIG_WANPIPE_CARDS has been taken out.
+					
+				o Fixed the Frame Relay and Chdlc network interfaces so they are
+				  compatible with libpcap libraries.  Meaning, tcpdump, snort,
+				  ethereal, and all other packet sniffers and debuggers work on
+				  all WANPIPE netowrk interfaces.
+					- Set the network interface encoding type to ARPHRD_PPP.
+					  This tell the sniffers that data obtained from the
+					  network interface is in pure IP format.
+				  Fix for 2.2.X kernels only.
+				
+				o True interface encoding option for Frame Relay and CHDLC
+					- The above fix sets the network interface encoding
+					  type to ARPHRD_PPP, however some customers use
+					  the encoding interface type to determine the
+					  protocol running.  Therefore, the TURE ENCODING
+					  option will set the interface type back to the
+					  original value.  
+
+					  NOTE: If this option is used with Frame Relay and CHDLC
+						libpcap library support will be broken.  
+						i.e. tcpdump will not work.
+					Fix for 2.2.x Kernels only.
+						
+				o Ethernet Bridgind over Frame Relay
+					- The Frame Relay bridging has been developed by 
+					  Kristian Hoffmann and Mark Wells.  
+					- The Linux kernel bridge is used to send ethernet 
+					  data over the frame relay links.
+					For 2.2.X Kernels only.
+
+				o Added extensive 2.0.X support. Most new features of
+				  2.1.5 for protocols Frame Relay, PPP and CHDLC are
+				  supported under 2.0.X kernels. 
+
+beta1-2.2.0 	Dec 30 2000
+				o Updated drivers for 2.4.X kernels.
+				o Updated drivers for SMP support.
+				o X25API is now able to share PCI interrupts.
+				o Took out a general polling routine that was used
+				  only by X25API. 
+				o Added appropriate locks to the dynamic reconfiguration
+				  code.
+				o Fixed a bug in the keyboard debug monitor.
+
+beta2-2.2.0	Jan 8 2001
+				o Patches for 2.4.0 kernel
+				o Patches for 2.2.18 kernel
+				o Minor updates to PPP and CHLDC drivers.
+				  Note: No functinal difference. 
+
+beta3-2.2.9	Jan 10 2001
+				o I missed the 2.2.18 kernel patches in beta2-2.2.0
+				  release.  They are included in this release.
+
+Stable Release
+2.2.0		Feb 01 2001
+				o Bug fix in wancfg GUI configurator.
+					The edit function didn't work properly.
+
+
+bata1-2.2.1	Feb 09 2001
+			o WANPIPE TTY Driver emulation. 
+			  Two modes of operation Sync and Async.
+				Sync: Using the PPPD daemon, kernel SyncPPP layer
+				      and the Wanpipe sync TTY driver: a PPP protocol 
+				      connection can be established via Sangoma adapter, over
+				      a T1 leased line.
+			
+				      The 2.4.0 kernel PPP layer supports MULTILINK
+				      protocol, that can be used to bundle any number of Sangoma
+				      adapters (T1 lines) into one, under a single IP address.
+				      Thus, efficiently obtaining multiple T1 throughput. 
+
+				      NOTE: The remote side must also implement MULTILINK PPP
+					    protocol.
+
+				Async:Using the PPPD daemon, kernel AsyncPPP layer
+				      and the WANPIPE async TTY driver: a PPP protocol
+				      connection can be established via Sangoma adapter and
+				      a modem, over a telephone line.
+
+				      Thus, the WANPIPE async TTY driver simulates a serial
+				      TTY driver that would normally be used to interface the 
+				      MODEM to the linux kernel.
+				
+			o WANPIPE PPP Backup Utility
+				This utility will monitor the state of the PPP T1 line.
+				In case of failure, a dial up connection will be established
+				via pppd daemon, ether via a serial tty driver (serial port), 
+				or a WANPIPE async TTY driver (in case serial port is unavailable).
+				
+				Furthermore, while in dial up mode, the primary PPP T1 link
+				will be monitored for signs of life.  
+
+				If the PPP T1 link comes back to life, the dial up connection
+				will be shutdown and T1 line re-established.
+			
+
+			o New Setup installation script.
+				Option to UPGRADE device drivers if the kernel source has
+				already been patched with WANPIPE.
+
+				Option to COMPILE WANPIPE modules against the currently 
+				running kernel, thus no need for manual kernel and module
+				re-compilatin.
+			
+			o Updates and Bug Fixes to wancfg utility.
+
+bata2-2.2.1	Feb 20 2001
+
+			o Bug fixes to the CHDLC device drivers.
+				The driver had compilation problmes under kernels
+				2.2.14 or lower.
+
+			o Bug fixes to the Setup installation script.
+				The device drivers compilation options didn't work
+				properly.
+
+			o Update to the wpbackupd daemon.  
+				Optimized the cross-over times, between the primary
+				link and the backup dialup.
+
+beta3-2.2.1	Mar 02 2001
+			o Patches for 2.4.2 kernel.
+
+			o Bug fixes to util/ make files.
+			o Bug fixes to the Setup installation script.
+
+			o Took out the backupd support and made it into
+			  as separate package.
+			  
+beta4-2.2.1     Mar 12 2001
+
+		o Fix to the Frame Relay Device driver.
+			IPSAC sends a packet of zero length
+			header to the frame relay driver.  The
+			driver tries to push its own 2 byte header
+			into the packet, which causes the driver to
+			crash.
+
+		o Fix the WANPIPE re-configuration code.
+			Bug was found by trying to run  the cfgft1 while the
+			interface was already running.  
+
+		o Updates to cfgft1.
+			Writes a wanpipe#.cfgft1 configuration file
+			once the CSU/DSU is configured. This file can
+			holds the current CSU/DSU configuration.
+
 
-			      -	Implemented user-readable status and statistics
-				via /proc filesystem
 
-1.0.0	December 31, 1996
+>>>>>> END OF README <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 
-			      -	Initial version
 
->>>>>> END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
diff -urN linux-2.4.4-pre2/Documentation/networking/wanpipe.txt linux/Documentation/networking/wanpipe.txt
--- linux-2.4.4-pre2/Documentation/networking/wanpipe.txt	Fri Jul 28 12:50:52 2000
+++ linux/Documentation/networking/wanpipe.txt	Mon Dec  3 22:34:49 2001
@@ -1,32 +1,100 @@
 ------------------------------------------------------------------------------
 Linux WAN Router Utilities Package
 ------------------------------------------------------------------------------
-Version 2.1.1
-Nov 08, 1999
+Version 2.2.1 
+Mar 28, 2001
 Author: Nenad Corbic <ncorbic@sangoma.com>
-Copyright (c) 1995-1999 Sangoma Technologies Inc.
+Copyright (c) 1995-2001 Sangoma Technologies Inc.
 ------------------------------------------------------------------------------
 
 INTRODUCTION
 
-This is a set of utilities and shell scripts you need in order to be able to
-use Linux kernel-level WAN Router.  Please read WAN Router User's manual
-(router.txt) and WANPIPE driver documentation found in /usr/lib/router/doc
-directory for installation and configuration instructions.
+Wide Area Networks (WANs) are used to interconnect Local Area Networks (LANs)
+and/or stand-alone hosts over vast distances with data transfer rates
+significantly higher than those achievable with commonly used dial-up
+connections.
+
+Usually an external device called `WAN router' sitting on your local network
+or connected to your machine's serial port provides physical connection to
+WAN.  Although router's job may be as simple as taking your local network
+traffic, converting it to WAN format and piping it through the WAN link, these
+devices are notoriously expensive, with prices as much as 2 - 5 times higher
+then the price of a typical PC box.
+
+Alternatively, considering robustness and multitasking capabilities of Linux,
+an internal router can be built (most routers use some sort of stripped down
+Unix-like operating system anyway). With a number of relatively inexpensive WAN
+interface cards available on the market, a perfectly usable router can be
+built for less than half a price of an external router.  Yet a Linux box
+acting as a router can still be used for other purposes, such as fire-walling,
+running FTP, WWW or DNS server, etc.
+
+This kernel module introduces the notion of a WAN Link Driver (WLD) to Linux
+operating system and provides generic hardware-independent services for such
+drivers.  Why can existing Linux network device interface not be used for
+this purpose?  Well, it can.  However, there are a few key differences between
+a typical network interface (e.g. Ethernet) and a WAN link.
+
+Many WAN protocols, such as X.25 and frame relay, allow for multiple logical
+connections (known as `virtual circuits' in X.25 terminology) over a single
+physical link.  Each such virtual circuit may (and almost always does) lead
+to a different geographical location and, therefore, different network.  As a
+result, it is the virtual circuit, not the physical link, that represents a
+route and, therefore, a network interface in Linux terms.
+
+To further complicate things, virtual circuits are usually volatile in nature
+(excluding so called `permanent' virtual circuits or PVCs).  With almost no
+time required to set up and tear down a virtual circuit, it is highly desirable
+to implement on-demand connections in order to minimize network charges.  So
+unlike a typical network driver, the WAN driver must be able to handle multiple
+network interfaces and cope as multiple virtual circuits come into existence
+and go away dynamically.
+ 
+Last, but not least, WAN configuration is much more complex than that of say
+Ethernet and may well amount to several dozens of parameters.  Some of them
+are "link-wide"  while others are virtual circuit-specific.  The same holds
+true for WAN statistics which is by far more extensive and extremely useful
+when troubleshooting WAN connections.  Extending the ifconfig utility to suit
+these needs may be possible, but does not seem quite reasonable.  Therefore, a
+WAN configuration utility and corresponding application programmer's interface
+is needed for this purpose.
+
+Most of these problems are taken care of by this module.  Its goal is to
+provide a user with more-or-less standard look and feel for all WAN devices and
+assist a WAN device driver writer by providing common services, such as:
+
+ o User-level interface via /proc file system
+ o Centralized configuration
+ o Device management (setup, shutdown, etc.)
+ o Network interface management (dynamic creation/destruction)
+ o Protocol encapsulation/decapsulation
 
-You can find the latest version of this software in /pub/linux directory on
-Sangoma Technologies' anonymous FTP server (ftp.sangoma.com). 
+To ba able to use the Linux WAN Router you will also need a WAN Tools package
+available from
+
+	ftp.sangoma.com/pub/linux/current_wanpipe/wanpipe-X.Y.Z.tgz
+
+where vX.Y.Z represent the wanpipe version number.
 
 For technical questions and/or comments please e-mail to ncorbic@sangoma.com.
 For general inquiries please contact Sangoma Technologies Inc. by
 
 	Hotline:	1-800-388-2475	(USA and Canada, toll free)
-	Phone:		(905) 474-1990
+	Phone:		(905) 474-1990  ext: 106
 	Fax:		(905) 474-9223
 	E-mail:		dm@sangoma.com	(David Mandelstam)
 	WWW:		http://www.sangoma.com
 
 
+INSTALLATION
+
+Please read the WanpipeForLinux.pdf manual on how to 
+install the WANPIPE tools and drivers properly. 
+
+
+After installing wanpipe package: /usr/local/wanrouter/doc. 
+On the ftp.sangoma.com : /linux/current_wanpipe/doc
+
 
 COPYRIGHT AND LICENSING INFORMATION
 
@@ -47,7 +115,7 @@
 ACKNOWLEDGEMENTS
 
 This product is based on the WANPIPE(tm) Multiprotocol WAN Router developed
-by Sangoma Technologies Inc. for Linux 2.2.x.  Success of the WANPIPE
+by Sangoma Technologies Inc. for Linux 2.0.x and 2.2.x.  Success of the WANPIPE
 together with the next major release of Linux kernel in summer 1996 commanded
 adequate changes to the WANPIPE code to take full advantage of new Linux
 features.
@@ -75,22 +143,33 @@
 
 NEW IN THIS RELEASE
 
-o Renamed startup script to wanrouter
-o Option to turn off/on each router 
-  separately
-o New source directory /usr/lib/wanrouter
-o New PPP driver 
-o X25 is not supported in this release 
+	o Updated the WANCFG utility
+		Calls the pppconfig to configure the PPPD
+		for async connections.
+
+	o Added the PPPCONFIG utility
+		Used to configure the PPPD dameon for the
+		WANPIPE Async PPP and standard serial port.
+		The wancfg calls the pppconfig to configure
+		the pppd.
+
+	o Fixed the PCI autodetect feature.  
+		The SLOT 0 was used as an autodetect option
+		however, some high end PC's slot numbers start
+		from 0. 
 
+	o This release has been tested with the new backupd
+	  daemon release.
 	
+
 PRODUCT COMPONENTS AND RELATED FILES
 
-/etc:
+/etc: (or user defined)
 	wanpipe1.conf	default router configuration file
-	wanrouter.rc	meta-configuration file (used by the Setup script)
 
 /lib/modules/X.Y.Z/misc:
 	wanrouter.o	router kernel loadable module
+	af_wanpipe.o	wanpipe api socket module
 
 /lib/modules/X.Y.Z/net:
 	sdladrv.o	Sangoma SDLA support module
@@ -102,64 +181,93 @@
 	{name}		reads WAN driver statistics
 
 /usr/sbin:
-	wanrouter	router start-up script
-	wanconfig	router configuration utility
+	wanrouter	wanrouter start-up script
+	wanconfig	wanrouter configuration utility
 	sdladump	WANPIPE adapter memory dump utility
         fpipemon        Monitor for Frame Relay
         cpipemon        Monitor for Cisco HDLC
+	ppipemon 	Monitor for PPP
+	xpipemon 	Monitor for X25
+	wpkbdmon        WANPIPE keyboard led monitor/debugger
 
-/usr/lib/wanrouter:
+/usr/local/wanrouter:
 	README		this file
 	COPYING		GNU General Public License
 	Setup		installation script
-	Configure	configuration script
 	Filelist	distribution definition file
+	wanrouter.rc	meta-configuration file 
+			(used by the Setup and wanrouter script)
 
-/usr/lib/wanrouter/doc:
-	WANPIPE_USER_MANUAL.txt WAN Router User's Manual
-	WANPIPE_CONFIG.txt	WAN Configuration Manual
-
-/usr/lib/wanrouter/interfaces:
-	*		interface configuration files (TCP/IP configuration)
-
-/usr/lib/wanrouter/patches:
-	wanrouter-22.gz	patch for Linux kernel 2.2.10 and 2.2.11 
-			(compatible for all 2.2.X kernels)
-	wanrouter-20.gz patch for Linux kernel 2.0.36 
+/usr/local/wanrouter/doc:
+	wanpipeForLinux.pdf 	WAN Router User's Manual
 
-	Fix_2.2.11.gz   patch to fix the 2.2.11 kernel so other patches
-                        can be applied properly.
+/usr/local/wanrouter/patches:
+	wanrouter-v2213.gz  	patch for Linux kernels 2.2.11 up to 2.2.13.
+	wanrouter-v2214.gz	patch for Linux kernel 2.2.14. 
+	wanrouter-v2215.gz	patch for Linux kernels 2.2.15 to 2.2.17.
+	wanrouter-v2218.gz	patch for Linux kernels 2.2.18 and up.
+	wanrouter-v240.gz	patch for Linux kernel 2.4.0.  
+	wanrouter-v242.gz	patch for Linux kernel 2.4.2 and up.
+	wanrouter-v2034.gz	patch for Linux kernel 2.0.34
+	wanrouter-v2036.gz 	patch for Linux kernel 2.0.36 and up. 
+
+/usr/local/wanrouter/patches/kdrivers:
+	Sources of the latest WANPIPE device drivers.
+	These are used to UPGRADE the linux kernel to the newest
+	version if the kernel source has already been pathced with
+	WANPIPE drivers.
 
-/usr/lib/wanrouter/samples:
+/usr/local/wanrouter/samples:
 	interface	sample interface configuration file
 	wanpipe1.cpri 	CHDLC primary port
      	wanpipe2.csec 	CHDLC secondary port
      	wanpipe1.fr   	Frame Relay protocol
      	wanpipe1.ppp  	PPP protocol ) 
+	wanpipe1.asy	CHDLC ASYNC protocol
+	wanpipe1.x25	X25 protocol
+	wanpipe1.stty	Sync TTY driver (Used by Kernel PPPD daemon)
+	wanpipe1.atty	Async TTY driver (Used by Kernel PPPD daemon)
 	wanrouter.rc	sample meta-configuration file
 
-/usr/lib/wanrouter/src:
-	*		wan-tools source code
+/usr/local/wanrouter/util:
+	*		wan-tools utilities source code
+
+/usr/local/wanrouter/api/x25:
+	*		x25 api sample programs.
+/usr/local/wanrouter/api/chdlc:
+	*		chdlc api sample programs.
+/usr/local/wanrouter/api/fr:
+	*		fr api sample programs.
+/usr/local/wanrouter/config/wancfg:
+	wancfg		WANPIPE GUI configuration program.
+                        Creates wanpipe#.conf files. 
+/usr/local/wanrouter/config/cfgft1:
+	cfgft1		GUI CSU/DSU configuration program.
 
 /usr/include/linux:
 	wanrouter.h	router API definitions
 	wanpipe.h	WANPIPE API definitions
 	sdladrv.h	SDLA support module API definitions
 	sdlasfm.h	SDLA firmware module definitions
+	if_wanpipe.h	WANPIPE Socket definitions
+	if_wanpipe_common.h	WANPIPE Socket/Driver common definitions.
+	sdlapci.h	WANPIPE PCI definitions
+	
 
-/usr/src/linux/net/router:
-	*		router source code
+/usr/src/linux/net/wanrouter:
+	*		wanrouter source code
 
 /var/log:
-	wanrouter	router start-up log (created by the Setup script)
+	wanrouter	wanrouter start-up log (created by the Setup script)
 
-/var/lock:
-	wanrouter	router lock file (created by the Setup script)
+/var/lock:  (or /var/lock/subsys for RedHat)
+	wanrouter	wanrouter lock file (created by the Setup script)
 
-/usr/lib/wanrouter/wanpipe:
+/usr/local/wanrouter/firmware:
 	fr514.sfm	Frame relay firmware for Sangoma S508/S514 card
 	cdual514.sfm	Dual Port Cisco HDLC firmware for Sangoma S508/S514 card
 	ppp514.sfm      PPP Firmware for Sangoma S508 and S514 cards
+	x25_508.sfm	X25 Firmware for Sangoma S508 card.
 
 
 REVISION HISTORY
@@ -228,7 +336,7 @@
 				creating applications using BiSync
    				streaming.        
 
-2.0.5   Aug 04, 1999 		CHDLC initialization bug fix.
+2.0.5   Aug 04, 1999 		CHDLC initializatin bug fix.
 				PPP interrupt driven driver: 
   				Fix to the PPP line hangup problem.
 				New PPP firmware
@@ -241,17 +349,274 @@
 				Streaming HDLC API has been taken out.  
 				 Available as a patch.                 
 
-2.0.6   Aug 17, 1999		Increased debugging in startup scripts
-				Fixed installation bugs from 2.0.5
+2.0.6   Aug 17, 1999		Increased debugging in statup scripts
+				Fixed insallation bugs from 2.0.5
 				Kernel patch works for both 2.2.10 and 2.2.11 kernels.
 				There is no functional difference between the two packages         
 
 2.0.7   Aug 26, 1999		o  Merged X25API code into WANPIPE.
-				o  Fixed a memory leak for X25API
+				o  Fixed a memeory leak for X25API
 				o  Updated the X25API code for 2.2.X kernels.
 				o  Improved NEM handling.   
 
 2.1.0	Oct 25, 1999		o New code for S514 PCI Card
 				o New CHDLC and Frame Relay drivers
 				o PPP and X25 are not supported in this release    
+
+2.1.1	Nov 30, 1999		o PPP support for S514 PCI Cards
+
+2.1.3   Apr 06, 2000		o Socket based x25api 
+				o Socket based chdlc api
+				o Socket based fr api
+				o Dual Port Receive only CHDLC support.
+				o Asynchronous CHDLC support (Secondary Port)
+				o cfgft1 GUI csu/dsu configurator
+				o wancfg GUI configuration file 
+				  configurator.
+				o Architectual directory changes.
+
+beta-2.1.4 Jul 2000		o Dynamic interface configuration:
+					Network interfaces reflect the state
+					of protocol layer.  If the protocol becomes
+					disconnected, driver will bring down
+					the interface.  Once the protocol reconnects
+					the interface will be brought up. 
+					
+					Note: This option is turned off by default.
+
+				o Dynamic wanrouter setup using 'wanconfig':
+					wanconfig utility can be used to
+					shutdown,restart,start or reconfigure 
+					a virtual circuit dynamically.
+				     
+					Frame Relay:  Each DLCI can be: 
+						      created,stopped,restarted and reconfigured
+						      dynamically using wanconfig.
+					
+						      ex: wanconfig card wanpipe1 dev wp1_fr16 up
+				  
+				o Wanrouter startup via command line arguments:
+					wanconfig also supports wanrouter startup via command line
+					arguments.  Thus, there is no need to create a wanpipe#.conf
+					configuration file.  
+
+				o Socket based x25api update/bug fixes.
+					Added support for LCN numbers greater than 255.
+					Option to pass up modem messages.
+					Provided a PCI IRQ check, so a single S514
+					card is guaranteed to have a non-sharing interrupt.
+
+				o Fixes to the wancfg utility.
+				o New FT1 debugging support via *pipemon utilities.
+				o Frame Relay ARP support Enabled.
+
+beta3-2.1.4 Jul 2000		o X25 M_BIT Problem fix.
+				o Added the Multi-Port PPP
+				  Updated utilites for the Multi-Port PPP.
+
+2.1.4	Aut 2000
+				o In X25API:
+					Maximum packet an application can send
+					to the driver has been extended to 4096 bytes.
+
+					Fixed the x25 startup bug. Enable 
+					communications only after all interfaces
+					come up.  HIGH SVC/PVC is used to calculate
+					the number of channels.
+					Enable protocol only after all interfaces
+					are enabled.
+
+				o Added an extra state to the FT1 config, kernel module.
+				o Updated the pipemon debuggers.
+
+				o Blocked the Multi-Port PPP from running on kernels
+				  2.2.16 or greater, due to syncppp kernel module
+				  change. 
+	  
+beta1-2.1.5 	Nov 15 2000
+				o Fixed the MulitPort PPP Support for kernels 2.2.16 and above.
+				  2.2.X kernels only
+
+				o Secured the driver UDP debugging calls
+					- All illegal netowrk debugging calls are reported to
+					  the log.
+					- Defined a set of allowed commands, all other denied.
+					
+				o Cpipemon
+					- Added set FT1 commands to the cpipemon. Thus CSU/DSU
+					  configuraiton can be performed using cpipemon.
+					  All systems that cannot run cfgft1 GUI utility should
+					  use cpipemon to configure the on board CSU/DSU.
+
+
+				o Keyboard Led Monitor/Debugger
+					- A new utilty /usr/sbin/wpkbdmon uses keyboard leds
+					  to convey operatinal statistic information of the 
+					  Sangoma WANPIPE cards.
+					NUM_LOCK    = Line State  (On=connected,    Off=disconnected)
+					CAPS_LOCK   = Tx data     (On=transmitting, Off=no tx data)
+					SCROLL_LOCK = Rx data     (On=receiving,    Off=no rx data
+					
+				o Hardware probe on module load and dynamic device allocation
+					- During WANPIPE module load, all Sangoma cards are probed
+					  and found information is printed in the /var/log/messages.
+					- If no cards are found, the module load fails.
+					- Appropriate number of devices are dynamically loaded 
+					  based on the number of Sangoma cards found.
+
+					  Note: The kernel configuraiton option 
+						CONFIG_WANPIPE_CARDS has been taken out.
+					
+				o Fixed the Frame Relay and Chdlc network interfaces so they are
+				  compatible with libpcap libraries.  Meaning, tcpdump, snort,
+				  ethereal, and all other packet sniffers and debuggers work on
+				  all WANPIPE netowrk interfaces.
+					- Set the network interface encoding type to ARPHRD_PPP.
+					  This tell the sniffers that data obtained from the
+					  network interface is in pure IP format.
+				  Fix for 2.2.X kernels only.
+				
+				o True interface encoding option for Frame Relay and CHDLC
+					- The above fix sets the network interface encoding
+					  type to ARPHRD_PPP, however some customers use
+					  the encoding interface type to determine the
+					  protocol running.  Therefore, the TURE ENCODING
+					  option will set the interface type back to the
+					  original value.  
+
+					  NOTE: If this option is used with Frame Relay and CHDLC
+						libpcap library support will be broken.  
+						i.e. tcpdump will not work.
+					Fix for 2.2.x Kernels only.
+						
+				o Ethernet Bridgind over Frame Relay
+					- The Frame Relay bridging has been developed by 
+					  Kristian Hoffmann and Mark Wells.  
+					- The Linux kernel bridge is used to send ethernet 
+					  data over the frame relay links.
+					For 2.2.X Kernels only.
+
+				o Added extensive 2.0.X support. Most new features of
+				  2.1.5 for protocols Frame Relay, PPP and CHDLC are
+				  supported under 2.0.X kernels. 
+
+beta1-2.2.0 	Dec 30 2000
+				o Updated drivers for 2.4.X kernels.
+				o Updated drivers for SMP support.
+				o X25API is now able to share PCI interrupts.
+				o Took out a general polling routine that was used
+				  only by X25API. 
+				o Added appropriate locks to the dynamic reconfiguration
+				  code.
+				o Fixed a bug in the keyboard debug monitor.
+
+beta2-2.2.0	Jan 8 2001
+				o Patches for 2.4.0 kernel
+				o Patches for 2.2.18 kernel
+				o Minor updates to PPP and CHLDC drivers.
+				  Note: No functinal difference. 
+
+beta3-2.2.9	Jan 10 2001
+				o I missed the 2.2.18 kernel patches in beta2-2.2.0
+				  release.  They are included in this release.
+
+Stable Release
+2.2.0		Feb 01 2001
+				o Bug fix in wancfg GUI configurator.
+					The edit function didn't work properly.
+
+
+bata1-2.2.1	Feb 09 2001
+			o WANPIPE TTY Driver emulation. 
+			  Two modes of operation Sync and Async.
+				Sync: Using the PPPD daemon, kernel SyncPPP layer
+				      and the Wanpipe sync TTY driver: a PPP protocol 
+				      connection can be established via Sangoma adapter, over
+				      a T1 leased line.
+			
+				      The 2.4.0 kernel PPP layer supports MULTILINK
+				      protocol, that can be used to bundle any number of Sangoma
+				      adapters (T1 lines) into one, under a single IP address.
+				      Thus, efficiently obtaining multiple T1 throughput. 
+
+				      NOTE: The remote side must also implement MULTILINK PPP
+					    protocol.
+
+				Async:Using the PPPD daemon, kernel AsyncPPP layer
+				      and the WANPIPE async TTY driver: a PPP protocol
+				      connection can be established via Sangoma adapter and
+				      a modem, over a telephone line.
+
+				      Thus, the WANPIPE async TTY driver simulates a serial
+				      TTY driver that would normally be used to interface the 
+				      MODEM to the linux kernel.
+				
+			o WANPIPE PPP Backup Utility
+				This utility will monitor the state of the PPP T1 line.
+				In case of failure, a dial up connection will be established
+				via pppd daemon, ether via a serial tty driver (serial port), 
+				or a WANPIPE async TTY driver (in case serial port is unavailable).
+				
+				Furthermore, while in dial up mode, the primary PPP T1 link
+				will be monitored for signs of life.  
+
+				If the PPP T1 link comes back to life, the dial up connection
+				will be shutdown and T1 line re-established.
+			
+
+			o New Setup installation script.
+				Option to UPGRADE device drivers if the kernel source has
+				already been patched with WANPIPE.
+
+				Option to COMPILE WANPIPE modules against the currently 
+				running kernel, thus no need for manual kernel and module
+				re-compilatin.
+			
+			o Updates and Bug Fixes to wancfg utility.
+
+bata2-2.2.1	Feb 20 2001
+
+			o Bug fixes to the CHDLC device drivers.
+				The driver had compilation problmes under kernels
+				2.2.14 or lower.
+
+			o Bug fixes to the Setup installation script.
+				The device drivers compilation options didn't work
+				properly.
+
+			o Update to the wpbackupd daemon.  
+				Optimized the cross-over times, between the primary
+				link and the backup dialup.
+
+beta3-2.2.1	Mar 02 2001
+			o Patches for 2.4.2 kernel.
+
+			o Bug fixes to util/ make files.
+			o Bug fixes to the Setup installation script.
+
+			o Took out the backupd support and made it into
+			  as separate package.
+			  
+beta4-2.2.1     Mar 12 2001
+
+		o Fix to the Frame Relay Device driver.
+			IPSAC sends a packet of zero length
+			header to the frame relay driver.  The
+			driver tries to push its own 2 byte header
+			into the packet, which causes the driver to
+			crash.
+
+		o Fix the WANPIPE re-configuration code.
+			Bug was found by trying to run  the cfgft1 while the
+			interface was already running.  
+
+		o Updates to cfgft1.
+			Writes a wanpipe#.cfgft1 configuration file
+			once the CSU/DSU is configured. This file can
+			holds the current CSU/DSU configuration.
+
+
+
 >>>>>> END OF README <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+
diff -urN linux-2.4.4-pre2/Documentation/s390/3270.ChangeLog linux/Documentation/s390/3270.ChangeLog
--- linux-2.4.4-pre2/Documentation/s390/3270.ChangeLog	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/s390/3270.ChangeLog	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,15 @@
+ChangeLog for the UTS Global 3270-support patch
+
+Feb 6, 2001:
+	* This changelog is new
+	* tub3270 now supports 3270 console:
+		Specify y for CONFIG_3270 and y for CONFIG_3270_CONSOLE.
+		Support for 3215 will not appear if 3270 console support
+		is chosen.
+		NOTE:  The default is 3270 console support, NOT 3215.
+	* the components are remodularized: added source modules are
+	  tubttybld.c and tubttyscl.c, for screen-building code and
+	  scroll-timeout code.
+	* tub3270 source for this (2.4.0) version is #ifdeffed to
+	  build with both 2.4.0 and 2.2.16.2.
+	* color support and minimal other ESC-sequence support is added.
diff -urN linux-2.4.4-pre2/Documentation/s390/3270.txt linux/Documentation/s390/3270.txt
--- linux-2.4.4-pre2/Documentation/s390/3270.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/s390/3270.txt	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,280 @@
+IBM 3270 Display System support
+
+This file describes the driver that supports local channel attachment
+of IBM 3270 devices.  It consists of three sections:
+	* Introduction
+	* Installation
+	* Operation
+
+
+INTRODUCTION.
+
+This paper describes installing and operating 3270 devices under
+Linux/390.  A 3270 device is a block-mode rows-and-columns terminal of
+which I'm sure hundreds of millions were sold by IBM and clonemakers
+twenty and thirty years ago.
+
+You may have 3270s in-house and not know it.  If you're using the
+VM-ESA operating system, define a 3270 to your virtual machine by using
+the command "DEF GRAF <hex-address>"  This paper presumes you will be
+defining four 3270s with the CP/CMS commands
+
+	DEF GRAF 620
+	DEF GRAF 621
+	DEF GRAF 622
+	DEF GRAF 623
+
+Your network connection from VM-ESA allows you to use x3270, tn3270, or
+another 3270 emulator, started from an xterm window on your PC or
+workstation.  With the DEF GRAF command, an application such as xterm,
+and this Linux-390 3270 driver, you have another way of talking to your
+Linux box.
+
+This paper covers installation of the driver and operation of a
+dialed-in x3270.
+
+
+HELP !!!
+
+The device name of e.g. /dev/3270/tty620 noted below is at variance
+with "standard" Linux device names.  What should it be?  The portion
+"/dev/3270" was recommended by H. Peter Anvin, maintainer of the
+official Linux major-numbers list; the portion "tty620" was recommended
+by me.  Please send your thoughts on this issue at least to me at
+rbh00@utsglobal.com.  Even if you think it's okay as is, please let me
+know.  Thanks.
+
+
+INSTALLATION.
+
+You install the driver by installing a patch, doing a kernel build, and
+running the configuration script (config3270.sh, in this directory).
+
+WARNING:  If you are using 3270 console support, you must rerun the
+configuration script every time you change the console's address (perhaps
+by using the condev= parameter in silo's /boot/parmfile).  More precisely,
+you should rerun the configuration script every time your set of 3270s,
+including the console 3270, changes subchannel identifier relative to
+one another.  ReIPL as soon as possible after running the configuration
+script and the resulting /tmp/mkdev3270.
+
+If you have chosen to make tub3270 a module, you add a line to
+/etc/modules.conf.  If you are working on a VM virtual machine, you
+can use DEF GRAF to define virtual 3270 devices.  If you generate 3270
+console support, the driver automatically converts your console at boot
+time to a 3270 if it is a 3215.
+
+In brief, these are the steps:
+	1. Install the tub3270 patch
+	2. (If a module) add a line to /etc/modules.conf
+	3. (If VM) define devices with DEF GRAF
+	4. Reboot
+	5. Configure
+
+To test that everything works, assuming VM and x3270,
+	1. Bring up an x3270 window.
+	2. Use the DIAL command in that window.
+	3. You should immediately see a Linux login screen.
+
+Here are the installation steps in detail:
+
+	0.  Retrieve the patch file via anonymous ftp from
+	ftp://ftp.utsglobal.com/pub/tub3270.  The patch is designed
+	to apply smoothly to an IBM 2.4.0 system with no other
+	UTS-Global patches applied.  We know of some easily resolvable
+	conflicts between this and other of our patches.
+
+	1. Apply the patch.  Then do
+		make oldconfig
+			(Reply "y" or "m" for CONFIG_3270; if "y",
+			reply "y" or "n" for CONFIG_3270_CONSOLE)
+		make dep
+		make image
+		make modules
+		make modules_install
+		<run silo in the usual way>
+
+	2. (Perform this step only if you have configured tub3270 as a
+	module.)  Add a line to /etc/modules.conf to automatically
+	load the driver when it's needed.  With this line added,
+	you will see login prompts appear on your 3270s as soon as
+	boot is complete (or with emulated 3270s, as soon as you dial
+	into your vm guest using the command "DIAL <vmguestname>").
+	Since the line-mode major number is 212, the line to add to
+	/etc/modules.conf should be:
+		alias char-major-212 tub3270
+
+	3. Define graphic devices to your vm guest machine, if you
+	haven't already.  Define them before you reboot (reipl):
+		DEFINE GRAF 620
+		DEFINE GRAF 621
+		DEFINE GRAF 622
+		DEFINE GRAF 623
+
+	4. Reboot.  The reboot process scans hardware devices, including
+	3270s, and this enables the tub3270 driver once loaded to respond
+	correctly to the configuration requests of the next step.  If
+	you have chosen 3270 console support, your console now behaves
+	as a 3270, not a 3215.
+
+	5. Run the 3270 configuration script config3270.  It is
+	distributed in this same directory, Documentation/s390, as
+	config3270.sh.	Inspect the output script it produces,
+	/tmp/mkdev3270, and then run that script.  This will create the
+	necessary character special device files and make the necessary
+	changes to /etc/inittab.  Then notify /sbin/init that /etc/inittab
+	has changed, by issuing the telinit command with the q operand:
+		cd /usr/src/linux/Documentation/s390
+		sh config3270.sh
+		sh /tmp/mkdev3270
+		telinit q
+	This should be sufficient for your first time.	If your 3270
+	configuration has changed and you're reusing config3270, you
+	should follow these steps:
+		Change 3270 configuration
+		Reboot
+		Run config3270 and /tmp/mkdev3270
+		Reboot
+
+Here are the testing steps in detail:
+
+	1. Bring up an x3270 window, or use an actual hardware 3278 or
+	3279, or use the 3270 emulator of your choice.  You would be
+	running the emulator on your PC or workstation.  You would use
+	the command, for example,
+		x3270 vm-esa-domain-name &
+	if you wanted a 3278 Model 4 with 43 rows of 80 columns, the
+	default model number.  The driver does not take advantage of
+	extended attributes.
+
+	The screen you should now see contains a VM logo with input
+	lines near the bottom.  Use TAB to move to the bottom line,
+	probably labeled "COMMAND  ===>".
+
+	2. Use the DIAL command instead of the LOGIN command to connect
+	to one of the virtual 3270s you defined with the DEF GRAF
+	commands:
+		dial my-vm-guest-name
+
+	3. You should immediately see a login prompt from your
+	Linux-390 operating system.  If that does not happen, you would
+	see instead the line "DIALED TO my-vm-guest-name   0620".
+
+	To troubleshoot:  do these things.
+
+	A. Is the driver loaded?  Use the lsmod command (no operands)
+	to find out.  Probably it isn't.  Try loading it manually, with
+	the command "insmod tub3270".  Does that command give error
+	messages?  Ha!  There's your problem.
+
+	B. Is the /etc/inittab file modified as in installation step 3
+	above?  Use the grep command to find out; for instance, issue
+	"grep 3270 /etc/inittab".  Nothing found?  There's your
+	problem!
+
+	C. Are the device special files created, as in installation
+	step 2 above?  Use the ls -l command to find out; for instance,
+	issue "ls -l /dev/3270/tty620".  The output should start with the
+	letter "c" meaning character device and should contain "212, 1"
+	just to the left of the device name.  No such file?  no "c"?
+	Wrong major number?  Wrong minor number?  There's your
+	problem!
+
+	D. Do you get the message
+		 "HCPDIA047E my-vm-guest-name 0620 does not exist"?
+	If so, you must issue the command "DEF GRAF 620" from your VM
+	3215 console and then reboot the system.
+
+
+
+OPERATION.
+
+The driver defines three areas on the 3270 screen:  the log area, the
+input area, and the status area.
+
+The log area takes up all but the bottom two lines of the screen.  The
+driver writes terminal output to it, starting at the top line and going
+down.  When it fills, the status area changes from "Linux Running" to
+"Linux More...".  After a scrolling timeout of (default) 5 sec, the
+screen clears and more output is written, from the top down.
+
+The input area extends from the beginning of the second-to-last screen
+line to the start of the status area.  You type commands in this area
+and hit ENTER to execute them.
+
+The status area initializes to "Linux Running" to give you a warm
+fuzzy feeling.  When the log area fills up and output awaits, it
+changes to "Linux More...".  At this time you can do several things or
+nothing.  If you do nothing, the screen will clear in (default) 5 sec
+and more output will appear.  You may hit ENTER with nothing typed in
+the input area to toggle between "Linux More..." and "Linux Holding",
+which indicates no scrolling will occur.  (If you hit ENTER with "Linux
+Running" and nothing typed, the application receives a newline.)
+
+You may change the scrolling timeout value.  For example, the following
+command line:
+	echo scrolltime=60 > /proc/tty/driver/tty3270
+changes the scrolling timeout value to 60 sec.  Set scrolltime to 0 if
+you wish to prevent scrolling entirely.
+
+Other things you may do when the log area fills up are:  hit PA2 to
+clear the log area and write more output to it, or hit CLEAR to clear
+the log area and the input area and write more output to the log area.
+
+Some of the Program Function (PF) and Program Attention (PA) keys are
+preassigned special functions.  The ones that are not yield an alarm
+when pressed.
+
+PA1 causes a SIGINT to the currently running application.  You may do
+the same thing from the input area, by typing "^C" and hitting ENTER.
+
+PA2 causes the log area to be cleared.  If output awaits, it is then
+written to the log area.
+
+PF3 causes an EOF to be received as input by the application.  You may
+cause an EOF also by typing "^D" and hitting ENTER.
+
+No PF key is preassigned to cause a job suspension, but you may cause a
+job suspension by typing "^Z" and hitting ENTER.  You may wish to
+assign this function to a PF key.  To make PF7 cause job suspension,
+execute the command:
+	echo pf7=^z > /proc/tty/driver/tty3270
+
+If the input you type does not end with the two characters "^n", the
+driver appends a newline character and sends it to the tty driver;
+otherwise the driver strips the "^n" and does not append a newline.
+The IBM 3215 driver behaves similarly.
+
+Pf10 causes the most recent command to be retrieved from the tube's
+command stack (default depth 20) and displayed in the input area.  You
+may hit PF10 again for the next-most-recent command, and so on.  A
+command is entered into the stack only when the input area is not made
+invisible (such as for password entry) and it is not identical to the
+current top entry.  PF10 rotates backward through the command stack;
+PF11 rotates forward.  You may assign the backward function to any PF
+key (or PA key, for that matter), say, PA3, with the command:
+	echo -e pa3=\\033k > /proc/tty/driver/tty3270
+This assigns the string ESC-k to PA3.  Similarly, the string ESC-j
+performs the forward function.  (Rationale:  In bash with vi-mode line
+editing, ESC-k and ESC-j retrieve backward and forward history.
+Suggestions welcome.)
+
+Is a stack size of twenty commands not to your liking?  Change it on
+the fly.  To change to saving the last 100 commands, execute the
+command:
+	echo recallsize=100 > /proc/tty/driver/tty3270
+
+Have a command you issue frequently?  Assign it to a PF or PA key!  Use
+the command
+	echo pf24="mkdir foobar; cd foobar" > /proc/tty/driver/tty3270 
+to execute the commands mkdir foobar and cd foobar immediately when you
+hit PF24.  Want to see the command line first, before you execute it?
+Use the -n option of the echo command:
+	echo -n pf24="mkdir foo; cd foo" > /proc/tty/driver/tty3270
+
+
+
+Happy testing!  I welcome any and all comments about this document, the
+driver, etc etc.
+
+Dick Hitt <rbh00@utsglobal.com>
diff -urN linux-2.4.4-pre2/Documentation/scsi-generic.txt linux/Documentation/scsi-generic.txt
--- linux-2.4.4-pre2/Documentation/scsi-generic.txt	Thu Feb  8 16:32:44 2001
+++ linux/Documentation/scsi-generic.txt	Mon Dec  3 22:34:49 2001
@@ -1,6 +1,6 @@
-            Notes on Linux's SG driver version 2.1.36
+            Notes on Linux's SG driver version 2.1.39
             -----------------------------------------
-                                                        20000110
+                                                        20010329
 
 Introduction
 ============
@@ -12,7 +12,7 @@
 amongst other things.
 
 These are notes on the Linux SCSI generic packet device driver (sg)
-describing version 2.1.36 . The original driver was written by Lawrence
+describing version 2.1.39 . The original driver was written by Lawrence
 Foard and remained in place with minimal changes since circa 1992.
 Version 2 of this driver remains backward compatible (binary and
 source **) with the original. It adds scatter gather, command queuing,
@@ -23,6 +23,10 @@
 at the linux/Documentation directory. The full document can be found
 at http://www.torque.net/sg/p/scsi-generic_long.txt .
 
+The Linux 2.4 series kernels have now been released. Lk 2.4 contains
+an upgraded "version 3" sg driver which is described in a supplementary
+document at http://www.torque.net/sg/p/scsi-generic_v3.txt .
+
 The interface and usage of the original sg driver have been documented
 by Heiko Eissfeldt in a HOWTO called SCSI-Programming-HOWTO. My copy
 of the document is version 1.5 dated 7th May 1996. It can found at
@@ -41,7 +45,8 @@
 The SCSI generic packet device driver (sg) is a character based device.
 It is one of the four high level device driver in the SCSI sub-system;
 the others are sd (for direct-access devices - disks), st (for tapes)
-and sr (for data cdroms). The other three devices are block devices.
+and sr (for data cdroms). Sd and sr are block devices while st (like sg)
+is a character device.
 
 The unifying layer of the SCSI sub-system is the so-called mid-level.
 Below that are the "low level" drivers which are the drivers for the
@@ -236,10 +241,11 @@
 /dev/sg[a-z]
 /dev/sg[0,1,2,...]
 or a symbolic link to one of these. [Devfs has its own sub-directory for
-sg devices with entries like: /dev/sg/c1b2t3u4 .] It seems as though SCSI
-devices are allocated to sg minor numbers in the same order as they appear
-in 'cat /proc/scsi/scsi'. Sg is a "character" based Linux device driver.
-This means it has an open/close/read/write/ioctl type interface.
+sg devices with entries like: /dev/scsi/host1/bus2/target3/lun4/generic .]
+It seems as though SCSI devices are allocated to sg minor numbers in the
+same order as they appear in 'cat /proc/scsi/scsi'. Sg is a "character"
+based Linux device driver.  This means it has an open/close/read/write/ioctl
+type interface.
 
 Flags can be either O_RDONLY or O_RDWR or-ed with either
 O_EXCL          waits for other opens on sg device to be closed before
@@ -396,7 +402,7 @@
 (which is called 'sg_release()' in the version 2 driver) to facilitate
 the cleanup mentioned above.
 
-A problem persists in version 2.1.36 if the sg driver is a module and is
+A problem persists in version 2.1.39 if the sg driver is a module and is
 removed while packets are still "in flight".
 
 Returns 0 if successful, otherwise -1 implies an error.
@@ -595,6 +601,8 @@
 do much (may in the future after other issues are resolved). Yields an
 EBUSY error if the SCSI bus or the associated device is being reset
 when this ioctl() is called, otherwise returns 0.
+N.B. In some recent distributions there is a patch to the SCSI mid level
+code that activates this ioctl. Check your distribution.
 
 SG_SET_DEBUG +:
 Assumes 3rd argument is pointing to an int. 0 (default) turns debugging
@@ -640,7 +648,7 @@
 found, switching to normal blocked io. A working example of this logic
 is in the sg_scan utility program.
 
-open("/dev/sga", O_RDONLY | O_NONBLOCK)
+open("/dev/sg0", O_RDONLY | O_NONBLOCK)
 /* check device, EBUSY means some other process has O_EXCL lock on it */
 /* when the device you want is found then ... */
 flags = fcntl(sg_fd, F_GETFL)
diff -urN linux-2.4.4-pre2/MAINTAINERS linux/MAINTAINERS
--- linux-2.4.4-pre2/MAINTAINERS	Mon Dec  3 22:34:41 2001
+++ linux/MAINTAINERS	Mon Dec  3 22:34:49 2001
@@ -1494,8 +1494,8 @@
 S:	Maintained for 2.2 only
 
 WAN ROUTER & SANGOMA WANPIPE DRIVERS & API (X.25, FRAME RELAY, PPP, CISCO HDLC)
-P:     Jaspreet Singh
-M:     jaspreet@sangoma.com
+P:     Nenad Corbic
+M:     ncorbic@sangoma.com
 M:     dm@sangoma.com
 W:     http://www.sangoma.com
 S:     Supported
diff -urN linux-2.4.4-pre2/Makefile linux/Makefile
--- linux-2.4.4-pre2/Makefile	Mon Dec  3 22:34:41 2001
+++ linux/Makefile	Mon Dec  3 22:34:49 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 4
-EXTRAVERSION =-pre2
+EXTRAVERSION =-pre3
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.4-pre2/arch/arm/Makefile linux/arch/arm/Makefile
--- linux-2.4.4-pre2/arch/arm/Makefile	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/Makefile	Mon Dec  3 22:34:49 2001
@@ -1,52 +1,36 @@
 #
 # arch/arm/Makefile
 #
-# This file is included by the global makefile so that you can add your own
-# architecture-specific flags and dependencies. Remember to do have actions
-# for "archclean" and "archdep" for cleaning up and making dependencies for
-# this architecture
-#
 # This file is subject to the terms and conditions of the GNU General Public
 # License.  See the file "COPYING" in the main directory of this archive
 # for more details.
 #
-# Copyright (C) 1995-2000 by Russell King
+# Copyright (C) 1995-2001 by Russell King
 
 LINKFLAGS	:=-p -X -T arch/arm/vmlinux.lds
 GZFLAGS		:=-9
 CFLAGS		+=-fno-common -pipe
 
-ifdef CONFIG_FRAME_POINTER
+ifneq ($(CONFIG_NO_FRAME_POINTER),y)
 CFLAGS		:=$(CFLAGS:-fomit-frame-pointer=)
 endif
 
-ifdef CONFIG_DEBUG_INFO
+ifeq ($(CONFIG_DEBUG_INFO),y)
 CFLAGS		+=-g
 endif
 
-# Ensure this is ld "2.9.4" or later
-NEW_LINKER	:= $(shell $(LD) --gc-sections --version >/dev/null 2>&1; echo $$?)
-
-ifneq ($(NEW_LINKER),0)
-dummy:;		@echo '*** ${VERSION}.${PATCHLEVEL} kernels no longer build correctly with old versions of binutils.'
-		@echo '*** Please upgrade your binutils to 2.9.5.'
-		@false
-endif
-
 # Select CPU dependent flags.  Note that order of declaration is important;
 # the options further down the list override previous items.
 #
-apcs-$(CONFIG_CPU_26)		:=-mapcs-26 -mcpu=arm3 -Os
-apcs-$(CONFIG_CPU_32)		:=-mapcs-32
+apcs-y				:=
+apcs-$(CONFIG_CPU_26)		:=-mcpu=arm3 -Os
 
-arch-$(CONFIG_CPU_32v3)		:=-march=armv3
+arch-y				:=
+arch-$(CONFIG_CPU_32v3)		:=-march=armv3m
 arch-$(CONFIG_CPU_32v4)		:=-march=armv4
 arch-$(CONFIG_CPU_32v5)		:=-march=armv5
 
-proc-$(CONFIG_CPU_32v3)		:=-marmv3m
-proc-$(CONFIG_CPU_32v4)		:=-marmv4
-proc-$(CONFIG_CPU_32v5)		:=-marmv5
-
+tune-y				:=
 tune-$(CONFIG_CPU_ARM610)	:=-mtune=arm610
 tune-$(CONFIG_CPU_ARM710)	:=-mtune=arm710
 tune-$(CONFIG_CPU_ARM720T)	:=-mtune=arm7tdmi
@@ -54,19 +38,27 @@
 tune-$(CONFIG_CPU_SA110)	:=-mtune=strongarm110
 tune-$(CONFIG_CPU_SA1100)	:=-mtune=strongarm1100
 
-CFLAGS		+= $(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float
-AFLAGS		+= $(apcs-y) $(proc-y) -mno-fpu
+CFLAGS		+= -mapcs-32 $(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float
+AFLAGS		+= -mapcs-32 $(apcs-y) $(arch-y) -mno-fpu
 
 LIBGCC		:= $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
 
 ifeq ($(CONFIG_CPU_26),y)
 PROCESSOR	 = armo
-TEXTADDR	 = 0x02080000
+  ifeq ($(CONFIG_ROM_KERNEL),y)
+    DATAADDR	 = 0x02080000
+    TEXTADDR	 = 0x03800000
+    LDSCRIPT	 = arch/arm/vmlinux-armo-rom.lds.in
+  else
+    TEXTADDR	 = 0x02080000
+    LDSCRIPT	 = arch/arm/vmlinux-armo.lds.in
+  endif
 endif
 
 ifeq ($(CONFIG_CPU_32),y)
 PROCESSOR	 = armv
 TEXTADDR	 = 0xC0008000
+LDSCRIPT	 = arch/arm/vmlinux-armv.lds.in
 endif
 
 ifeq ($(CONFIG_ARCH_ARCA5K),y)
@@ -97,8 +89,13 @@
 INCDIR		 = ebsa285
 endif
 
-ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
-MACHINE		 = nexuspci
+ifeq ($(CONFIG_ARCH_FTVPCI),y)
+MACHINE		 = ftvpci
+INCDIR		 = nexuspci
+endif
+
+ifeq ($(CONFIG_ARCH_TBOX),y)
+MACHINE		 = tbox
 endif
 
 ifeq ($(CONFIG_ARCH_SHARK),y)
@@ -134,6 +131,10 @@
 INCDIR		:= $(MACHINE)
 endif
 
+ifeq ($(origin DATAADDR), undefined)
+DATAADDR	:= .
+endif
+
 # If we have a machine-specific directory, then include it in the build.
 MACHDIR		:= arch/arm/mach-$(MACHINE)
 ifeq ($(MACHDIR),$(wildcard $(MACHDIR)))
@@ -143,13 +144,17 @@
 
 HEAD		:= arch/arm/kernel/head-$(PROCESSOR).o \
 		   arch/arm/kernel/init_task.o
-SUBDIRS		+= arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe
+SUBDIRS		+= arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe \
+		   arch/arm/fastfpe
 CORE_FILES	:= arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
-LIBS		:= arch/arm/lib/lib.o arch/arm/lib/lib.a $(LIBS) $(LIBGCC)
+LIBS		:= arch/arm/lib/lib.a $(LIBS) $(LIBGCC)
 
-ifeq ($(CONFIG_NWFPE),y)
+ifeq ($(CONFIG_FPE_NWFPE),y)
 LIBS		:= arch/arm/nwfpe/math-emu.o $(LIBS)
 endif
+ifeq ($(CONFIG_FPE_FASTFPE),y)
+LIBS		:= arch/arm/fastfpe/fast-math-emu.o $(LIBS)
+endif
 
 ifeq ($(CONFIG_ARCH_CLPS7500),y)
 SUBDIRS		+= drivers/acorn/char
@@ -157,21 +162,13 @@
 endif
 
 MAKEBOOT	 = $(MAKE) -C arch/$(ARCH)/boot
+MAKETOOLS	 = $(MAKE) -C arch/$(ARCH)/tools
 
 # The following is a hack to get 'constants.h' up
 # to date before starting compilation
 
-$(patsubst %, _dir_%, $(SUBDIRS)) init/main.o init/version.o : \
-	include/asm-arm/mach-types.h
-
-$(patsubst %, _dir_%, $(SUBDIRS)) : constants
-
-include/asm-arm/mach-types.h: arch/arm/tools/mach-types  \
-			      arch/arm/tools/gen-mach-types
-	@awk -f arch/arm/tools/gen-mach-types arch/arm/tools/mach-types > $@
-
-constants: dummy
-	@$(MAKE) -C arch/arm/lib constants.h
+$(patsubst %,_dir_%, $(SUBDIRS)): maketools
+$(patsubst %,_modsubdir_%,$(MOD_DIRS)): maketools
 
 symlinks: archsymlinks
 
@@ -181,8 +178,8 @@
 
 vmlinux: arch/arm/vmlinux.lds
 
-arch/arm/vmlinux.lds: arch/arm/vmlinux-$(PROCESSOR).lds.in dummy
-	@sed 's/TEXTADDR/$(TEXTADDR)/' <$< >$@
+arch/arm/vmlinux.lds: $(LDSCRIPT) dummy
+	@sed 's/TEXTADDR/$(TEXTADDR)/;s/DATAADDR/$(DATAADDR)/' $(LDSCRIPT) >$@
 
 arch/arm/kernel arch/arm/mm arch/arm/lib: dummy
 	$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
@@ -190,17 +187,42 @@
 bzImage zImage zinstall Image bootpImage install: vmlinux
 	@$(MAKEBOOT) $@
 
+CLEAN_FILES	+= \
+	arch/arm/vmlinux.lds
+
+MRPROPER_FILES	+= \
+	include/asm-arm/arch \
+	include/asm-arm/proc \
+	include/asm-arm/constants.h* \
+	include/asm-arm/mach-types.h
+
+# We use MRPROPER_FILES and CLEAN_FILES now
 archmrproper:
-	$(RM) include/asm-arm/arch include/asm-arm/proc
+	@/bin/true
 
 archclean:
 	@$(MAKEBOOT) clean
-	$(RM) arch/arm/lib/constants.h arch/arm/vmlinux.lds
-	$(RM) include/asm-arm/mach-types.h
 
-archdep: archsymlinks
+archdep: scripts/mkdep archsymlinks
+	@$(MAKETOOLS) dep
 	@$(MAKEBOOT) dep
 
+maketools: checkbin
+	@$(MAKETOOLS) all
+
+# Ensure this is ld "2.9.4" or later
+NEW_LINKER	:= $(shell $(LD) --gc-sections --version >/dev/null 2>&1; echo $$?)
+
+ifneq ($(NEW_LINKER),0)
+checkbin:
+	@echo '*** ${VERSION}.${PATCHLEVEL} kernels no longer build correctly with old versions of binutils.'
+	@echo '*** Please upgrade your binutils to 2.9.5.'
+	@false
+else
+checkbin:
+	@true
+endif
+
 # My testing targets (that short circuit a few dependencies)
 zImg:;	@$(MAKEBOOT) zImage
 Img:;	@$(MAKEBOOT) Image
@@ -211,15 +233,7 @@
 #
 # Configuration targets.  Use these to select a
 # configuration for your architecture
-CFGS=	a5k_config		ebsa110_config		\
-	footbridge_config	rpc_config		\
-	brutus_config		victor_config		\
-	empeg_config		graphicsclient_config	\
-	assabet_config		lart_config		\
-	cerf_config		lusl7200_config 	\
-	sherman_config		pangolin_config
-
-$(CFGS):
+%_config:
 	@( \
 	CFG=$(@:_config=); \
 	if [ -f arch/arm/def-configs/$$CFG ]; then \
diff -urN linux-2.4.4-pre2/arch/arm/boot/Makefile linux/arch/arm/boot/Makefile
--- linux-2.4.4-pre2/arch/arm/boot/Makefile	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/boot/Makefile	Mon Dec  3 22:34:49 2001
@@ -55,8 +55,7 @@
 endif
 
 ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
-ZTEXTADDR	 = 0x40200000
-ZRELADDR	 = 0x40008000
+ZTEXTADDR	 = 0x40008000
 endif
 
 ifeq ($(CONFIG_ARCH_L7200),y)
@@ -72,7 +71,6 @@
 
 ifeq ($(CONFIG_ARCH_P720T),y)
 ZTEXTADDR	 = 0xc0018000
-ZRELADDR	 = 0xc0018000
 PARAMS_PHYS	 = 0xc0000100
 INITRD_PHYS	 = 0xc0400000
 INITRD_VIRT	 = 0xc0400000
diff -urN linux-2.4.4-pre2/arch/arm/boot/compressed/Makefile linux/arch/arm/boot/compressed/Makefile
--- linux-2.4.4-pre2/arch/arm/boot/compressed/Makefile	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/boot/compressed/Makefile	Mon Dec  3 22:34:49 2001
@@ -9,7 +9,7 @@
 
 HEAD		 = head.o
 OBJS		 = misc.o
-CFLAGS		 = $(CPPFLAGS) -O2 -DSTDC_HEADERS $(CFLAGS_PROC)
+CFLAGS		 = $(CPPFLAGS) -O2 -DSTDC_HEADERS $(CFLAGS_PROC) -msoft-float
 FONTC		 = $(TOPDIR)/drivers/video/font_acorn_8x8.c
 ZLDFLAGS	 = -p -X -T vmlinux.lds
 
@@ -25,16 +25,24 @@
 OBJS		+= head-netwinder.o
 endif
 
+ifeq ($(CONFIG_ARCH_SHARK),y)
+OBJS		+= head-shark.o ofw-shark.o
+endif
+
 ifeq ($(CONFIG_ARCH_INTEGRATOR),y)
 OBJS		+= head-netwinder.o
 endif
 
-ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
-HEAD		 = head-nexuspci.o
+ifeq ($(CONFIG_ARCH_FTVPCI),y)
+OBJS		+= head-ftvpci.o
 endif
 
 ifeq ($(CONFIG_ARCH_L7200),y)
 OBJS		+= head-l7200.o
+endif
+
+ifeq ($(CONFIG_ARCH_CLPS7500),y)
+HEAD		= head-clps7500.o
 endif
 
 ifeq ($(CONFIG_ARCH_P720T),y)
diff -urN linux-2.4.4-pre2/arch/arm/boot/compressed/head-clps7500.S linux/arch/arm/boot/compressed/head-clps7500.S
--- linux-2.4.4-pre2/arch/arm/boot/compressed/head-clps7500.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/boot/compressed/head-clps7500.S	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,87 @@
+/*
+ * linux/arch/arm/boot/compressed/head.S
+ *
+ * Copyright (C) 1999, 2000, 2001 Nexus Electronics Ltd
+ */
+
+#include <linux/config.h>
+
+		/* There are three different ways the kernel can be
+		   booted on a 7500 system: from Angel (loaded in RAM), from
+		   16-bit ROM or from 32-bit Flash.  Luckily, a single kernel
+		   image does for them all. */
+		/* This branch is taken if the CPU memory width matches the
+		   actual device in use.  The default at power on is 16 bits
+		   so we must be prepared for a mismatch. */
+		.section ".start", #alloc, #execinstr
+2:
+		b	1f
+		.word	0xffff
+		.word	0xb632		@ mov r11, #0x03200000
+		.word	0xe3a0
+		.word	0x0000		@ mov r0, #0
+		.word	0xe3a0
+		.word	0x0080		@ strb r0, [r11, #0x80]
+		.word	0xe5cb
+		.word	0xf000		@ mov pc, #0
+		.word	0xe3a0
+1:
+		adr	r1, 2b
+		teq	r1, #0
+		bne	.Langel
+		/* This is a direct-from-ROM boot.  Copy the kernel into 
+		   RAM and run it there. */
+		mov	r0, #0x30
+		mcr	p15, 0, r0, c1, c0, 0
+		mov	r0, #0x13
+		msr	cpsr, r0
+		mov	r12, #0x03000000	@ point to LEDs
+		orr	r12, r12, #0x00020000
+		orr	r12, r12, #0xba00
+		mov	r0, #0x5500
+		str	r0, [r12]
+		mov	r0, #0x10000000
+		orr	r0, r0, #0x8000
+		mov	r4, r0
+		ldr	r2, =_end
+2:
+		ldr	r3, [r1], #4
+		str	r3, [r0], #4
+		teq	r0, r2
+		bne	2b
+		mov	r0, #0xff00
+		str	r0, [r12]
+1:	
+		mov	r12, #0x03000000	@ point to LEDs
+		orr	r12, r12, #0x00020000
+		orr	r12, r12, #0xba00
+		mov	r0, #0xfe00
+		str	r0, [r12]
+
+		adr	lr, 1f
+		mov	r0, #0
+		mov	r1, #14		/* MACH_TYPE_CLPS7500 */
+		mov	pc, lr
+.Langel:
+#ifdef CONFIG_ANGELBOOT
+		/* Call Angel to switch into SVC mode. */
+		mov	r0, #0x17
+		swi	0x123456
+#endif
+		/* Ensure all interrupts are off and MMU disabled */
+		mrs	r0, cpsr
+		orr	r0, r0, #0xc0
+		msr	cpsr, r0
+
+		adr	lr, 1b
+		orr	lr, lr, #0x10000000
+		mov	r0, #0x30		@ MMU off
+		mcr	p15, 0, r0, c1, c0, 0
+		mov	r0, r0
+	 	mov	pc, lr
+
+		.ltorg
+
+1:
+/* And the rest */
+#include "head.S"
diff -urN linux-2.4.4-pre2/arch/arm/boot/compressed/head-shark.S linux/arch/arm/boot/compressed/head-shark.S
--- linux-2.4.4-pre2/arch/arm/boot/compressed/head-shark.S	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/boot/compressed/head-shark.S	Mon Dec  3 22:34:49 2001
@@ -1,5 +1,5 @@
 /* The head-file for the Shark
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * Does the following:
  * - get the memory layout from firmware. This can only be done as long as the mmu
@@ -20,8 +20,6 @@
 
 		b	__beginning
 	
-__serial_addr:	.long	0xf7eff3f8
-		.long	0				@ space
 __ofw_data:	.long	0				@ the number of memory blocks
 		.space  128				@ (startaddr,size) ...
 		.space  128				@ bootargs
@@ -31,14 +29,10 @@
 
 		mov	r0, #0xC0			@ disable irq and fiq
 		mov	r1, r0
-		mrs	r3, cpsr_all
+		mrs	r3, cpsr
 		bic	r2, r3, r0
 		eor	r2, r2, r1
-		msr	cpsr_all, r2
-
-		ldr	r0, __serial_addr		@ disable serial interrupt
-		mov	r1, #0				@ hangs the machine, I don t know why.
-		strb	r1, [r0, #0x01]
+		msr	cpsr_c, r2
 
 		mov	r0, r4				@ get the Memory layout from firmware
 		adr	r1, __ofw_data
diff -urN linux-2.4.4-pre2/arch/arm/boot/compressed/head.S linux/arch/arm/boot/compressed/head.S
--- linux-2.4.4-pre2/arch/arm/boot/compressed/head.S	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/boot/compressed/head.S	Mon Dec  3 22:34:49 2001
@@ -87,7 +87,8 @@
 
 		b	1f
 		.word	0x016f2818		@ Magic numbers to help the loader
-		.word	start	
+		.word	start			@ absolute load/run zImage address
+		.word	_edata			@ zImage end address
 1:		mov	r7, r1			@ save architecture ID
 		mov	r8, #0			@ save r0
 #ifdef CONFIG_ANGELBOOT
@@ -210,7 +211,7 @@
 		mov	r0, r3
 		mov	r8, r0, lsr #18
 		mov	r8, r8, lsl #18		@ start of RAM
-		add	r9, r8, #0x20000000	@ the maximum RAM size
+		add	r9, r8, #0x10000000	@ a reasonable RAM size
 		mov	r1, #0x12
 		orr	r1, r1, #3 << 10
 		add	r2, r3, #16384
diff -urN linux-2.4.4-pre2/arch/arm/boot/compressed/ll_char_wr.S linux/arch/arm/boot/compressed/ll_char_wr.S
--- linux-2.4.4-pre2/arch/arm/boot/compressed/ll_char_wr.S	Mon Sep 18 15:15:24 2000
+++ linux/arch/arm/boot/compressed/ll_char_wr.S	Mon Dec  3 22:34:49 2001
@@ -16,7 +16,7 @@
 @ Regs: [] = corruptible
 @       {} = used
 @       () = do not use
-#define __ASSEMBLY__
+
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 		.text
diff -urN linux-2.4.4-pre2/arch/arm/boot/compressed/ofw-shark.c linux/arch/arm/boot/compressed/ofw-shark.c
--- linux-2.4.4-pre2/arch/arm/boot/compressed/ofw-shark.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/boot/compressed/ofw-shark.c	Mon Dec  3 22:34:49 2001
@@ -1,7 +1,7 @@
 /*
  * linux/arch/arm/boot/compressed/ofw-shark.c
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * This file is used to get some basic information
  * about the memory layout of the shark we are running
@@ -11,6 +11,7 @@
 
 
 #include <linux/kernel.h>
+#include <linux/types.h>
 #include <asm/setup.h>
 #include <asm/page.h>
 
@@ -18,7 +19,7 @@
 asmlinkage void
 create_params (unsigned long *buffer)
 {
-	/* Is there a better address? Also change in kernel/arch.c */
+	/* Is there a better address? Also change in mach-shark/arch.c */
 	struct param_struct *params = (struct param_struct *) 0x08003000;
 	int j,i,m,k,nr_banks,size;
 
diff -urN linux-2.4.4-pre2/arch/arm/boot/compressed/setup-sa1100.S linux/arch/arm/boot/compressed/setup-sa1100.S
--- linux-2.4.4-pre2/arch/arm/boot/compressed/setup-sa1100.S	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/boot/compressed/setup-sa1100.S	Mon Dec  3 22:34:49 2001
@@ -24,6 +24,9 @@
 PPC_BASE:	.long	0x90060000
 #define PPAR	0x08
 
+IC_BASE:	.long	0x90050000
+#define ICMR	0x04
+
 UART1_BASE:	.long	0x80010000
 UART3_BASE:	.long	0x80050000
 #define UTCR0           0x00
@@ -52,6 +55,11 @@
 ENTRY(sa1100_setup)
 		mov	r3, r0			@ keep machine type in r3
 
+		@ Clear all interrupt sources
+		ldr	r0, IC_BASE
+		mov	r1, #0
+		str	r1, [r0, #ICMR]
+
 @ Read System Configuration "Register" for Assabet.
 @ (taken from "Intel StrongARM SA-1110 Microprocessor Development Board
 @ User's Guide," p.4-9)
@@ -87,6 +95,7 @@
 		@ Initialize UART (if bootloader has not done it yet)...
 		teq	r3, #MACH_TYPE_BRUTUS
 		teqne	r3, #MACH_TYPE_ASSABET
+		teqne	r3, #MACH_TYPE_GRAPHICSCLIENT
 		bne	skip_uart
 
 		@ UART3 if Assabet is used with Neponset
@@ -95,6 +104,11 @@
 		ldreq	r0, UART3_BASE
 		beq	uart_init
 
+		@ UART3 on GraphicsClient
+		teq	r3, #MACH_TYPE_GRAPHICSCLIENT
+		ldreq	r0, UART3_BASE
+		beq	uart_init
+
 		@ At least for Brutus, the UART1 is used through
 		@ the alternate GPIO function...
 		teq	r3, #MACH_TYPE_BRUTUS
@@ -115,7 +129,8 @@
 
 uart1:		ldr	r0, UART1_BASE
 
-uart_init:	ldr	r1, [r0, #UTSR1]
+uart_init:
+1:		ldr	r1, [r0, #UTSR1]
 		tst	r1, #1<<0		@ TBY
 		bne	1b
 		mov	r1, #0
diff -urN linux-2.4.4-pre2/arch/arm/config.in linux/arch/arm/config.in
--- linux-2.4.4-pre2/arch/arm/config.in	Fri Feb  9 11:38:27 2001
+++ linux/arch/arm/config.in	Mon Dec  3 22:34:49 2001
@@ -21,10 +21,8 @@
 mainmenu_option next_comment
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
-if [ "$CONFIG_MODULES" = "y" ]; then
-   bool '  Set version information on all module symbols' CONFIG_MODVERSIONS
-   bool '  Kernel module loader' CONFIG_KMOD
-fi
+dep_bool '  Set version information on all module symbols' CONFIG_MODVERSIONS $CONFIG_MODULES
+dep_bool '  Kernel module loader' CONFIG_KMOD $CONFIG_MODULES
 endmenu
 
 
@@ -36,71 +34,62 @@
 	 Cirrus-CL-PS7500FE     CONFIG_ARCH_CLPS7500 \
 	 Co-EBSA285		CONFIG_ARCH_CO285 \
 	 EBSA-110		CONFIG_ARCH_EBSA110 \
+	 LinkUp-L7200		CONFIG_ARCH_L7200 \
 	 FootBridge		CONFIG_ARCH_FOOTBRIDGE \
 	 Integrator		CONFIG_ARCH_INTEGRATOR \
 	 RiscPC			CONFIG_ARCH_RPC \
- 	 SA1100-based		CONFIG_ARCH_SA1100" RiscPC
-
-# the following are placeholders for when they are fully integrated
-#	 LinkUp-L7200		CONFIG_ARCH_L7200
+ 	 SA1100-based		CONFIG_ARCH_SA1100 \
+ 	 CLPS711x/EP721x-based	CONFIG_ARCH_CLPS711X" RiscPC
 
 mainmenu_option next_comment
 comment 'Archimedes/A5000 Implementations'
-if [ "$CONFIG_ARCH_ARCA5K" = "y" ]; then
-   # These architectures will be combined.  However, until this
-   # is complete...  Note that the ARC will take precidence over
-   # A5K
-   comment 'Archimedes/A5000 Implementations (select only ONE)'
-
-   bool '  Archimedes' CONFIG_ARCH_ARC
-   bool '  A5000' CONFIG_ARCH_A5K
-fi
+# These architectures will be combined.  However, until this
+# is complete...  Note that the ARC will take precidence over
+# A5K
+comment 'Archimedes/A5000 Implementations (select only ONE)'
+dep_bool '  Archimedes' CONFIG_ARCH_ARC $CONFIG_ARCH_ARCA5K
+dep_bool '  A5000' CONFIG_ARCH_A5K $CONFIG_ARCH_ARCA5K
 endmenu
 
 mainmenu_option next_comment
 comment 'Footbridge Implementations'
-if [ "$CONFIG_ARCH_FOOTBRIDGE" = "y" ]; then
-   bool '  CATS' CONFIG_ARCH_CATS
-   bool '  Compaq Personal Server' CONFIG_ARCH_PERSONAL_SERVER
-   bool '  EBSA285 (addin mode)' CONFIG_ARCH_EBSA285_ADDIN
-   bool '  EBSA285 (host mode)' CONFIG_ARCH_EBSA285_HOST
-   bool '  NetWinder' CONFIG_ARCH_NETWINDER
-fi
+dep_bool '  CATS' CONFIG_ARCH_CATS $CONFIG_ARCH_FOOTBRIDGE
+dep_bool '  Compaq Personal Server' CONFIG_ARCH_PERSONAL_SERVER $CONFIG_ARCH_FOOTBRIDGE
+dep_bool '  EBSA285 (addin mode)' CONFIG_ARCH_EBSA285_ADDIN $CONFIG_ARCH_FOOTBRIDGE
+dep_bool '  EBSA285 (host mode)' CONFIG_ARCH_EBSA285_HOST $CONFIG_ARCH_FOOTBRIDGE
+dep_bool '  NetWinder' CONFIG_ARCH_NETWINDER $CONFIG_ARCH_FOOTBRIDGE
 endmenu
 
 mainmenu_option next_comment
 comment 'SA11x0 Implementations'
-if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
-
-   bool '  Assabet' CONFIG_SA1100_ASSABET
-   if [ "$CONFIG_SA1100_ASSABET" = "y" ]; then
-      bool '    Include support for Neponset' CONFIG_ASSABET_NEPONSET
-   fi
-   bool '  Brutus' CONFIG_SA1100_BRUTUS
-   bool '  CerfBoard' CONFIG_SA1100_CERF
-   bool '  Compaq iPAQ H3600 (Bitsy)' CONFIG_SA1100_BITSY
-#   bool '  Empeg' CONFIG_SA1100_EMPEG
-#   bool '  Itsy' CONFIG_SA1100_ITSY
-   bool '  LART' CONFIG_SA1100_LART
-#   bool '  PLEB' CONFIG_SA1100_PLEB
-   bool '  ThinClient' CONFIG_SA1100_THINCLIENT
-   bool '  GraphicsClient' CONFIG_SA1100_GRAPHICSCLIENT
-   bool '  nanoEngine' CONFIG_SA1100_NANOENGINE
-   bool '  Victor' CONFIG_SA1100_VICTOR
-#   bool '  Tifon' CONFIG_SA1100_TIFON
-   bool '  XP860' CONFIG_SA1100_XP860
-
-   # Someday, we'll support this as a general option.
-   bool '  Load kernel using Angel Debug Monitor' CONFIG_ANGELBOOT
-
-   # Determine if SA1111 support is required
-   if [ "$CONFIG_ASSABET_NEPONSET" = "y" -o \
-	"$CONFIG_SA1100_XP860" = "y" ]; then
-      define_bool CONFIG_SA1111 y
-   fi
+dep_bool '  Assabet' CONFIG_SA1100_ASSABET $CONFIG_ARCH_SA1100
+dep_bool '    Include support for Neponset' CONFIG_ASSABET_NEPONSET $CONFIG_SA1100_ASSABET
+dep_bool '  Brutus' CONFIG_SA1100_BRUTUS $CONFIG_ARCH_SA1100
+dep_bool '  CerfBoard' CONFIG_SA1100_CERF $CONFIG_ARCH_SA1100
+dep_bool '  Compaq iPAQ H3600 (Bitsy)' CONFIG_SA1100_BITSY $CONFIG_ARCH_SA1100
+#dep_bool '  Empeg' CONFIG_SA1100_EMPEG $CONFIG_ARCH_SA1100
+#dep_bool '  Itsy' CONFIG_SA1100_ITSY $CONFIG_ARCH_SA1100
+dep_bool '  LART' CONFIG_SA1100_LART $CONFIG_ARCH_SA1100
+#dep_bool '  PLEB' CONFIG_SA1100_PLEB $CONFIG_ARCH_SA1100
+dep_bool '  GraphicsClient' CONFIG_SA1100_GRAPHICSCLIENT $CONFIG_ARCH_SA1100
+dep_bool '  nanoEngine' CONFIG_SA1100_NANOENGINE $CONFIG_ARCH_SA1100
+dep_bool '  Victor' CONFIG_SA1100_VICTOR $CONFIG_ARCH_SA1100
+dep_bool '  Sherman' CONFIG_SA1100_SHERMAN $CONFIG_ARCH_SA1100
+dep_bool '  XP860' CONFIG_SA1100_XP860 $CONFIG_ARCH_SA1100
+dep_bool '  Pangolin' CONFIG_SA1100_PANGOLIN $CONFIG_ARCH_SA1100
+
+# Determine if SA1111 support is required
+if [ "$CONFIG_ASSABET_NEPONSET" = "y" -o \
+     "$CONFIG_SA1100_XP860" = "y" ]; then
+   define_bool CONFIG_SA1111 y
 fi
 endmenu
 
+mainmenu_option next_comment
+comment 'CLPS711X/EP721X Implementations'
+dep_bool '  P720T' CONFIG_ARCH_P720T $CONFIG_ARCH_CLPS711X
+endmenu
+
 # Definitions to make life easier
 if [ "$CONFIG_ARCH_ARCA5K" = "y" -o \
      "$CONFIG_ARCH_RPC" = "y" ]; then
@@ -147,49 +136,77 @@
 
 comment 'Processor Type'
 
-# Select CPU and optimisation dependent on architecture
-if [ "$CONFIG_ARCH_RPC" = "y" ]; then
+# Firstly, figure out what processor architecture version we should be using.
+if [ "$CONFIG_ARCH_RPC"        = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" ]; then
    define_bool CONFIG_CPU_32v3 y
-   bool 'Support ARM610 processor' CONFIG_CPU_ARM6
-   bool 'Support ARM710 processor' CONFIG_CPU_ARM7
-   bool 'Support StrongARM(R) SA-110 processor' CONFIG_CPU_SA110
+else
+   define_bool CONFIG_CPU_32v3 n
 fi
-if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
-     "$CONFIG_FOOTBRIDGE" = "y" -o \
-     "$CONFIG_ARCH_TBOX" = "y" -o \
-     "$CONFIG_ARCH_SHARK" = "y" -o \
-     "$CONFIG_ARCH_NEXUSPCI" = "y" ]; then
+if [ "$CONFIG_ARCH_EBSA110"    = "y" -o "$CONFIG_FOOTBRIDGE"    = "y" -o \
+     "$CONFIG_ARCH_TBOX"       = "y" -o "$CONFIG_ARCH_SHARK"    = "y" -o \
+     "$CONFIG_ARCH_NEXUSPCI"   = "y" -o "$CONFIG_ARCH_CLPS711X" = "y" -o \
+     "$CONFIG_ARCH_INTEGRATOR" = "y" -o "$CONFIG_ARCH_SA1100"   = "y" -o \
+     "$CONFIG_ARCH_L7200"      = "y" ]; then
    define_bool CONFIG_CPU_32v4 y
-   define_bool CONFIG_CPU_SA110 y
+else
+   define_bool CONFIG_CPU_32v4 n
 fi
+
+# Select CPU types depending on the architecture selected.
+# We use this to select which CPUs are supported, and to select
+# the compiler tuning options.
+
+# ARM610
+if [ "$CONFIG_ARCH_RPC" = "y" ]; then
+   bool 'Support ARM610 processor' CONFIG_CPU_ARM610
+else
+   define_bool CONFIG_CPU_ARM610 n
+fi
+
+# ARM710
 if [ "$CONFIG_ARCH_CLPS7500" = "y" ]; then
-   define_bool CONFIG_CPU_32v3 y
-   define_bool CONFIG_CPU_ARM7 y
+   define_bool CONFIG_CPU_ARM710 y
+else
+   if [ "$CONFIG_ARCH_RPC" = "y" ]; then
+      bool 'Support ARM710 processor' CONFIG_CPU_ARM710
+   else
+      define_bool CONFIG_CPU_ARM710 n
+   fi
 fi
-if [ "$CONFIG_ARCH_L7200" = "y" ]; then
-   define_bool CONFIG_CPU_32v4 y
-   define_bool CONFIG_CPU_ARM720 y
+
+# ARM720T
+if [ "$CONFIG_ARCH_CLPS711X" = "y" -o "$CONFIG_ARCH_L7200" = "y" ]; then
+   define_bool CONFIG_CPU_ARM720T y
+else
+   if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+      bool 'Support ARM720T processor' CONFIG_CPU_ARM720T
+   else
+      define_bool CONFIG_CPU_ARM720T n
+   fi
 fi
+
+# ARM920T
 if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
-   define_bool CONFIG_CPU_32v4 y
-   bool 'Support ARM720 processor' CONFIG_CPU_ARM720
-   bool 'Support ARM920 processor' CONFIG_CPU_ARM920
-#   bool 'Support ARM10 processor' CONFIG_CPU_ARM10
-fi
-if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
-   define_bool CONFIG_CPU_32v4 y
-   define_bool CONFIG_CPU_SA1100 y
+   bool 'Support ARM920T processor' CONFIG_CPU_ARM920T
+else
+   define_bool CONFIG_CPU_ARM920T n
 fi
-
-if [ "$CONFIG_CPU_ARM920" = "y" ]; then
-   bool '  ARM920 CPU idle' CONFIG_CPU_ARM920_CPU_IDLE
-   bool '  ARM920 I-Cache on' CONFIG_CPU_ARM920_I_CACHE_ON
-   bool '  ARM920 D-Cache on' CONFIG_CPU_ARM920_D_CACHE_ON
+if [ "$CONFIG_CPU_ARM920T" = "y" ]; then
+   bool '  ARM920T CPU idle' CONFIG_CPU_ARM920_CPU_IDLE
+   bool '  ARM920T I-Cache on' CONFIG_CPU_ARM920_I_CACHE_ON
+   bool '  ARM920T D-Cache on' CONFIG_CPU_ARM920_D_CACHE_ON
    if [ "$CONFIG_CPU_ARM920_D_CACHE_ON" = "y" ] ; then
-      bool '  Force write through caches on ARM920' CONFIG_CPU_ARM920_WRITETHROUGH
+      bool '  Force write through caches on ARM920T' CONFIG_CPU_ARM920_WRITETHROUGH
    fi
 fi
-#if [ "$CONFIG_CPU_ARM10" = "y" ]; then
+
+# ARM1020
+#if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+#   bool 'Support ARM1020 processor' CONFIG_CPU_ARM1020
+#else
+    define_bool CONFIG_CPU_ARM1020 n
+#fi
+#if [ "$CONFIG_CPU_ARM1020" = "y" ]; then
 #   bool '  ARM10 I-Cache on' CONFIG_CPU_ARM10_I_CACHE_ON
 #   bool '  ARM10 D-Cache on' CONFIG_CPU_ARM10_D_CACHE_ON
 #   if [ "$CONFIG_CPU_ARM10_D_CACHE_ON" = "y" ] ; then
@@ -197,6 +214,30 @@
 #   fi
 #fi
 
+# SA110
+if [ "$CONFIG_ARCH_EBSA110"  = "y" -o "$CONFIG_FOOTBRIDGE" = "y" -o \
+     "$CONFIG_ARCH_TBOX"     = "y" -o "$CONFIG_ARCH_SHARK" = "y" -o \
+     "$CONFIG_ARCH_NEXUSPCI" = "y" ]; then
+   define_bool CONFIG_CPU_SA110 y
+else
+   if [ "$CONFIG_ARCH_RPC" = "y" ]; then
+      bool 'Support StrongARM(R) SA-110 processor' CONFIG_CPU_SA110
+   else
+      define_bool CONFIG_CPU_SA110 n
+   fi
+fi
+
+# SA1100
+if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+   define_bool CONFIG_CPU_SA1100 y
+else
+   define_bool CONFIG_CPU_SA1100 n
+fi
+
+#if [ "$CONFIG_CPU_32" = "y" ]; then
+#   bool 'Support Thumb instructions' CONFIG_ARM_THUMB
+#fi
+
 # Select various configuration options depending on the machine type
 if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
    define_bool CONFIG_DISCONTIGMEM y
@@ -209,8 +250,12 @@
 mainmenu_option next_comment
 comment 'General setup'
 
+comment 'Please ensure that you have read the help on the next option'
+bool 'Load kernel using Angel Debug Monitor' CONFIG_ANGELBOOT
+
 # Now handle the bus types
-if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \
+if [ "$CONFIG_ARCH_FTVPCI" = "y" -o \
+     "$CONFIG_ARCH_SHARK" = "y" -o \
      "$CONFIG_FOOTBRIDGE_HOST" = "y" ]; then
    define_bool CONFIG_PCI y
 else
@@ -223,21 +268,19 @@
 fi
 
 if [ "$CONFIG_FOOTBRIDGE_HOST" = "y" -o \
-     "$CONFIG_ARCH_SHARK" = "y" ]; then
+     "$CONFIG_ARCH_SHARK" = "y" -o \
+     "$CONFIG_ARCH_CLPS7500" = "y" -o \
+     "$CONFIG_ARCH_EBSA110" = "y" ]; then
    define_bool CONFIG_ISA y
-   define_bool CONFIG_ISA_DMA y
 else
    define_bool CONFIG_ISA n
-   define_bool CONFIG_ISA_DMA n
 fi
 
-# Do we have a PC-type keyboard in this architecture?
-if [ "$CONFIG_FOOTBRIDGE_HOST" = "y" ]; then
-   define_bool CONFIG_PC_KEYB y
-   define_bool CONFIG_PC_KEYMAP y
-fi
-if [ "$CONFIG_SA1100_ASSABET" = "y" ]; then
-   define_bool CONFIG_PC_KEYMAP y
+if [ "$CONFIG_FOOTBRIDGE_HOST" = "y" -o \
+     "$CONFIG_ARCH_SHARK" = "y" ]; then
+   define_bool CONFIG_ISA_DMA y
+else
+   define_bool CONFIG_ISA_DMA n
 fi
 
 source drivers/pci/Config.in
@@ -251,42 +294,44 @@
 bool 'System V IPC' CONFIG_SYSVIPC
 bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
-tristate 'NWFPE math emulation' CONFIG_NWFPE
+tristate 'NWFPE math emulation' CONFIG_FPE_NWFPE
+dep_tristate 'FastFPE math emulation (experimental)' CONFIG_FPE_FASTFPE $CONFIG_EXPERIMENTAL
 choice 'Kernel core (/proc/kcore) format' \
 	"ELF		CONFIG_KCORE_ELF	\
 	 A.OUT		CONFIG_KCORE_AOUT" ELF
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
+dep_bool 'Power Management support (experimental)' CONFIG_PM $CONFIG_EXPERIMENTAL
+dep_tristate 'RISC OS personality' CONFIG_ARTHUR $CONFIG_CPU_32
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   bool 'Power Management support' CONFIG_PM
-fi
-
-if [ "$CONFIG_CPU_32" = "y" ]; then
-   tristate 'RISC OS personality' CONFIG_ARTHUR
-fi
-if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
-     "$CONFIG_ARCH_SA1100" = "y" -o \
-     "$CONFIG_ARCH_CLPS7500" = "y" -o \
+if [ "$CONFIG_ARCH_EBSA110" = "y" -o         \
+     "$CONFIG_ARCH_SA1100" = "y" -o          \
+     "$CONFIG_ARCH_CLPS7500" = "y" -o        \
      "$CONFIG_ARCH_PERSONAL_SERVER" = "y" -o \
-     "$CONFIG_ARCH_CATS" = "y" -o \
-     "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+     "$CONFIG_ARCH_CATS" = "y" -o            \
+     "$CONFIG_ARCH_INTEGRATOR" = "y" -o      \
+     "$CONFIG_ARCH_P720T" = "y" ]; then
    string 'Default kernel command string' CONFIG_CMDLINE ""
 fi
-if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
-     "$CONFIG_ARCH_EBSA110" = "y" -o   \
-     "$CONFIG_ARCH_EBSA285" = "y" -o   \
-     "$CONFIG_ARCH_CO285" = "y" -o     \
-     "$CONFIG_ARCH_SA1100" = "y" -o    \
-     "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+if [ "$CONFIG_ARCH_NETWINDER" = "y" -o  \
+     "$CONFIG_ARCH_EBSA110" = "y" -o    \
+     "$CONFIG_ARCH_EBSA285" = "y" -o    \
+     "$CONFIG_ARCH_FTVPCI" = "y" -o     \
+     "$CONFIG_ARCH_SHARK" = "y" -o      \
+     "$CONFIG_ARCH_CO285" = "y" -o      \
+     "$CONFIG_ARCH_SA1100" = "y" -o     \
+     "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+     "$CONFIG_ARCH_P720T" = "y" ]; then
    bool 'Timer and CPU usage LEDs' CONFIG_LEDS
    if [ "$CONFIG_LEDS" = "y" ]; then
-      if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
-	   "$CONFIG_ARCH_EBSA285" = "y" -o   \
-	   "$CONFIG_ARCH_CO285" = "y" -o     \
-	   "$CONFIG_ARCH_SA1100" = "y" -o    \
-           "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+      if [ "$CONFIG_ARCH_NETWINDER" = "y" -o  \
+	   "$CONFIG_ARCH_EBSA285" = "y" -o    \
+	   "$CONFIG_ARCH_SHARK" = "y" -o      \
+	   "$CONFIG_ARCH_CO285" = "y" -o      \
+	   "$CONFIG_ARCH_SA1100" = "y" -o     \
+           "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+	   "$CONFIG_ARCH_P720T" = "y" ]; then
          bool '  Timer LED' CONFIG_LEDS_TIMER
          bool '  CPU usage LED' CONFIG_LEDS_CPU
       fi
@@ -350,9 +395,13 @@
 fi
 endmenu
 
+if [ "$CONFIG_ARCH_CLPS711X" = "y" ]; then
+   source drivers/ssi/Config.in
+fi
+
 source drivers/ieee1394/Config.in
 
-source drivers/i2o/Config.in
+source drivers/message/i2o/Config.in
 
 mainmenu_option next_comment
 comment 'ISDN subsystem'
@@ -363,6 +412,11 @@
 fi
 endmenu
 
+#
+# input before char - char/joystick depends on it. As does USB.
+#
+source drivers/input/Config.in
+
 source drivers/char/Config.in
 if [ "$CONFIG_ARCH_ACORN" = "y" -a \
      "$CONFIG_BUSMOUSE" = "y" ]; then
@@ -373,11 +427,34 @@
    fi
 fi
 
+source drivers/media/Config.in
+
 source fs/Config.in
 
 if [ "$CONFIG_VT" = "y" ]; then
    mainmenu_option next_comment
    comment 'Console drivers'
+   # Select the keyboard type for this architecture.
+   if [ "$CONFIG_FOOTBRIDGE_HOST" = "y" -o \
+        "$CONFIG_ARCH_CLPS7500" = "y" -o   \
+        "$CONFIG_ARCH_SHARK" = "y" ]; then
+      define_bool CONFIG_PC_KEYB y
+   fi
+   if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+      define_bool CONFIG_KMI_KEYB y
+      define_bool CONFIG_KMI_MOUSE y
+   fi
+
+   # Do we use the PC-type keyboard map?
+   if [ "$CONFIG_FOOTBRIDGE_HOST" = "y" -o \
+        "$CONFIG_ARCH_SHARK" = "y" -o      \
+        "$CONFIG_ARCH_SA1100" = "y" -o     \
+        "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+        "$CONFIG_ARCH_TBOX" = "y" -o       \
+        "$CONFIG_ARCH_CLPS7500" = "y" -o   \
+        "$CONFIG_ARCH_P720T" = "y" ]; then
+      define_bool CONFIG_PC_KEYMAP y
+   fi
    if [ "$CONFIG_ARCH_ACORN" != "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
       bool 'VGA text console' CONFIG_VGA_CONSOLE
    fi
@@ -388,6 +465,7 @@
 
 if [ "$CONFIG_ARCH_ACORN" = "y" -o \
      "$CONFIG_ARCH_CLPS7500" = "y" -o \
+     "$CONFIG_ARCH_TBOX" = "y" -o \
      "$CONFIG_ARCH_SHARK" = "y" -o \
      "$CONFIG_PCI" = "y" ]; then
    mainmenu_option next_comment
@@ -408,23 +486,14 @@
 
 # Always compile kernel with framepointer (until 2.4 real comes out)
 # Bug reports aren't much use without this.
-#bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
-define_bool CONFIG_FRAME_POINTER y
+bool 'Compile kernel without frame pointer' CONFIG_NO_FRAME_POINTER
 bool 'Verbose kernel error messages' CONFIG_DEBUG_ERRORS
 bool 'Verbose user fault messages' CONFIG_DEBUG_USER
 bool 'Include debugging information in kernel binary' CONFIG_DEBUG_INFO
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
-if [ "$CONFIG_CPU_26" = "y" ]; then
-   bool 'Disable pgtable cache' CONFIG_NO_PGT_CACHE
-fi
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   # These options are only for real kernel hackers
-   # who want to get their hands dirty. 
-   bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL
-   if [ "$CONFIG_DEBUG_LL" = "y" ]; then
-      if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
-         bool 'Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT
-      fi
-   fi
-fi
+dep_bool 'Disable pgtable cache' CONFIG_NO_PGT_CACHE $CONFIG_CPU_26
+# These options are only for real kernel hackers who want to get their hands dirty. 
+dep_bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL $CONFIG_EXPERIMENTAL
+dep_bool '  Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT $CONFIG_DEBUG_LL $CONFIG_FOOTBRIDGE
+dep_bool '  kernel low-level debugging messages via UART2' CONFIG_DEBUG_CLPS711X_UART2 $CONFIG_DEBUG_LL $CONFIG_ARCH_CLPS711X
 endmenu
diff -urN linux-2.4.4-pre2/arch/arm/kernel/Makefile linux/arch/arm/kernel/Makefile
--- linux-2.4.4-pre2/arch/arm/kernel/Makefile	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/kernel/Makefile	Mon Dec  3 22:34:49 2001
@@ -13,16 +13,18 @@
 AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR)
 AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR)
 
+# This is depreciated.
 O_OBJS_arc        = dma-arc.o oldlatches.o
 O_OBJS_rpc        = dma-rpc.o
 O_OBJS_footbridge = dma-footbridge.o isa.o
 O_OBJS_l7200      = fiq.o
 
-pci-nexuspci	  = plx90x0.o
+pci-ftvpci	  = plx90x0.o
 pci-footbridge	  = dec21285.o
 pci-shark	  = via82c505.o
 
-pci-$(CONFIG_ARCH_NEXUSPCI) += ftv-pci.o
+# this is here to allow us to eventually move it out to mach-ftvpci
+pci-$(CONFIG_ARCH_FTVPCI) += ftv-pci.o
 
 
 O_TARGET := kernel.o
@@ -36,7 +38,7 @@
 obj-n		:=
 obj-		:=
 
-export-objs	:= armksyms.o dma.o ecard.o fiq.o oldlatches.o time.o
+export-objs	:= armksyms.o dma.o ecard.o fiq.o io.o oldlatches.o time.o
 
 no-irq-arch	:= $(CONFIG_ARCH_INTEGRATOR) $(CONFIG_ARCH_CLPS711X) \
 		   $(CONFIG_ARCH_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110)
@@ -46,16 +48,21 @@
 endif
 
 obj-$(CONFIG_ARCH_ACORN) += ecard.o fiq.o time-acorn.o
+obj-$(CONFIG_ARCH_CLPS7500) += time-acorn.o
 obj-$(CONFIG_DEBUG_LL)	+= debug-$(PROCESSOR).o
 obj-$(CONFIG_MODULES)	+= armksyms.o
 obj-$(CONFIG_ARTHUR)	+= arthur.o
 obj-$(CONFIG_ISA_DMA)	+= dma-isa.o
 obj-$(CONFIG_PCI)	+= bios32.o $(pci-$(MACHINE)) $(pci-y)
 
+ifneq ($(MACHINE),ebsa110)
+  obj-y		+= io.o
+endif
+
 all: kernel.o $(HEAD_OBJ) init_task.o
 
 include $(TOPDIR)/Rules.make
 
 # Spell out some dependencies that `make dep' doesn't spot
-entry-armv.o: calls.S ../lib/constants.h
-entry-armo.o: calls.S ../lib/constants.h
+entry-armv.o: calls.S $(TOPDIR)/include/asm-arm/constants.h
+entry-armo.o: calls.S $(TOPDIR)/include/asm-arm/constants.h
diff -urN linux-2.4.4-pre2/arch/arm/kernel/arch.c linux/arch/arm/kernel/arch.c
--- linux-2.4.4-pre2/arch/arm/kernel/arch.c	Mon Sep 18 15:15:24 2000
+++ linux/arch/arm/kernel/arch.c	Mon Dec  3 22:34:49 2001
@@ -19,10 +19,9 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/dec21285.h>
 
-unsigned int vram_size;
+extern void genarch_init_irq(void);
 
-extern void setup_initrd(unsigned int start, unsigned int size);
-extern void setup_ramdisk(int doload, int prompt, int start, unsigned int rd_sz);
+unsigned int vram_size;
 
 #ifdef CONFIG_ARCH_ACORN
 
@@ -79,6 +78,7 @@
 	DISABLE_PARPORT(1)
 	FIXUP(fixup_acorn)
 	MAPIO(rpc_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_ARC
@@ -86,6 +86,7 @@
 	MAINTAINER("Dave Gilbert")
 	BOOT_PARAMS(0x0207c000)
 	FIXUP(fixup_acorn)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_A5K
@@ -93,11 +94,13 @@
 	MAINTAINER("Russell King")
 	BOOT_PARAMS(0x0207c000)
 	FIXUP(fixup_acorn)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #endif
 
 #ifdef CONFIG_ARCH_L7200
+extern void __init l7200_map_io(void);
 
 static void __init
 fixup_l7200(struct machine_desc *desc, struct param_struct *params,
@@ -109,34 +112,28 @@
         mi->bank[0].node  = 0;
 
         ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
-        setup_ramdisk( 1, 0, 0, 8192 );
-        setup_initrd( __phys_to_virt(0xf1000000), 0x00162b0d);
-}
+        setup_ramdisk( 1, 0, 0, CONFIG_BLK_DEV_RAM_SIZE);
+        setup_initrd( __phys_to_virt(0xf1000000), 0x005dac7b);
 
-extern void __init l7200_map_io(void);
+        /* Serial Console COM2 and LCD */
+	strcpy( *cmdline, "console=tty0 console=ttyLU1,115200");
+
+        /* Serial Console COM1 and LCD */
+	//strcpy( *cmdline, "console=tty0 console=ttyLU0,115200");
+
+        /* Console on LCD */
+	//strcpy( *cmdline, "console=tty0");
+}
 
-MACHINE_START(L7200, "LinkUp Systems L7200SDB")
-	MAINTAINER("Steve Hill")
+MACHINE_START(L7200, "LinkUp Systems L7200")
+	MAINTAINER("Steve Hill / Scott McConnell")
 	BOOT_MEM(0xf0000000, 0x80040000, 0xd0000000)
 	FIXUP(fixup_l7200)
 	MAPIO(l7200_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 
-#ifdef CONFIG_ARCH_EBSA110
-
-extern void __init ebsa110_map_io(void);
-
-MACHINE_START(EBSA110, "EBSA110")
-	MAINTAINER("Russell King")
-	BOOT_MEM(0x00000000, 0xe0000000, 0xe0000000)
-	BOOT_PARAMS(0x00000400)
-	DISABLE_PARPORT(0)
-	DISABLE_PARPORT(2)
-	SOFT_REBOOT
-	MAPIO(ebsa110_map_io)
-MACHINE_END
-#endif
 #ifdef CONFIG_ARCH_NEXUSPCI
 
 extern void __init nexuspci_map_io(void);
@@ -145,6 +142,7 @@
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x40000000, 0x10000000, 0xe0000000)
 	MAPIO(nexuspci_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_TBOX
@@ -155,21 +153,25 @@
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x80000000, 0x00400000, 0xe0000000)
 	MAPIO(tbox_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_CLPS7110
 MACHINE_START(CLPS7110, "CL-PS7110")
 	MAINTAINER("Werner Almesberger")
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_ETOILE
 MACHINE_START(ETOILE, "Etoile")
 	MAINTAINER("Alex de Vries")
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_LACIE_NAS
 MACHINE_START(LACIE_NAS, "LaCie_NAS")
 	MAINTAINER("Benjamin Herrenschmidt")
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_CLPS7500
@@ -180,5 +182,6 @@
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x10000000, 0x03000000, 0xe0000000)
 	MAPIO(clps7500_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
diff -urN linux-2.4.4-pre2/arch/arm/kernel/armksyms.c linux/arch/arm/kernel/armksyms.c
--- linux-2.4.4-pre2/arch/arm/kernel/armksyms.c	Mon Sep 18 15:15:24 2000
+++ linux/arch/arm/kernel/armksyms.c	Mon Dec  3 22:34:49 2001
@@ -48,7 +48,6 @@
 extern int sys_read(int, char *, int);
 extern int sys_lseek(int, off_t, int);
 extern int sys_exit(int);
-extern int sys_wait4(int, int *, int, struct rusage *);
 
 /*
  * libgcc functions - functions that are used internally by the
@@ -77,15 +76,18 @@
 extern void fpundefinstr(void);
 extern void fp_enter(void);
 
-#define EXPORT_SYMBOL_ALIAS(sym,orig) \
- const char __kstrtab_##sym##[] __attribute__((section(".kstrtab"))) = \
-    __MODULE_STRING(##sym##); \
- const struct module_symbol __ksymtab_##sym __attribute__((section("__ksymtab"))) = \
+#define EXPORT_SYMBOL_ALIAS(sym,orig)		\
+ const char __kstrtab_##sym##[]			\
+  __attribute__((section(".kstrtab"))) =	\
+    __MODULE_STRING(sym);			\
+ const struct module_symbol __ksymtab_##sym	\
+  __attribute__((section("__ksymtab"))) =	\
     { (unsigned long)&##orig, __kstrtab_##sym };
-	/*
-	 * floating point math emulator support.
-	 * These symbols will never change their calling convention...
-	 */
+
+/*
+ * floating point math emulator support.
+ * These symbols will never change their calling convention...
+ */
 EXPORT_SYMBOL_ALIAS(kern_fp_enter,fp_enter);
 EXPORT_SYMBOL_ALIAS(fp_printk,printk);
 EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig);
@@ -95,7 +97,9 @@
 EXPORT_SYMBOL(ret_from_exception);
 #endif
 
+#ifdef CONFIG_VT
 EXPORT_SYMBOL(kd_mksound);
+#endif
 
 	/* platform dependent support */
 EXPORT_SYMBOL(dump_thread);
@@ -150,8 +154,6 @@
 #ifndef CONFIG_NO_PGT_CACHE
 EXPORT_SYMBOL(quicklists);
 #endif
-EXPORT_SYMBOL(__handle_bad_pmd);
-EXPORT_SYMBOL(__handle_bad_pmd_kernel);
 
 	/* string / mem functions */
 EXPORT_SYMBOL_NOVERS(strcpy);
diff -urN linux-2.4.4-pre2/arch/arm/kernel/arthur.c linux/arch/arm/kernel/arthur.c
--- linux-2.4.4-pre2/arch/arm/kernel/arthur.c	Mon Sep 18 15:15:24 2000
+++ linux/arch/arm/kernel/arthur.c	Mon Dec  3 22:34:49 2001
@@ -1,7 +1,9 @@
 /*
- * Arthur personality
+ *  linux/arch/arm/kernel/arthur.c
+ *
+ *  Copyright (C) 1998, 1999, 2000 Philip Blundell
  *
- * Copyright (C) 1998, 1999, 2000 Philip Blundell
+ * Arthur personality
  */
 
 /*
diff -urN linux-2.4.4-pre2/arch/arm/kernel/bios32.c linux/arch/arm/kernel/bios32.c
--- linux-2.4.4-pre2/arch/arm/kernel/bios32.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/kernel/bios32.c	Mon Dec  3 22:34:49 2001
@@ -307,7 +307,8 @@
 		 * parity line correctly.
 		 */
 		if (dev->vendor == PCI_VENDOR_ID_INTERG &&
-		    dev->device == PCI_DEVICE_ID_INTERG_2000)
+		    (dev->device == PCI_DEVICE_ID_INTERG_2000 ||
+		     dev->device == PCI_DEVICE_ID_INTERG_2010))
 			busdata->features &= ~(PCI_COMMAND_SERR |
 					       PCI_COMMAND_PARITY);
 
@@ -394,6 +395,7 @@
 extern struct hw_pci netwinder_pci;
 extern struct hw_pci personal_server_pci;
 extern struct hw_pci ftv_pci;
+extern struct hw_pci shark_pci;
 extern struct hw_pci integrator_pci;
 
 void __init pcibios_init(void)
@@ -409,6 +411,12 @@
 			break;
 		}
 #endif
+#ifdef CONFIG_ARCH_SHARK
+		if (machine_is_shark()) {
+			hw_pci = &shark_pci;
+			break;
+		}
+#endif
 #ifdef CONFIG_ARCH_CATS
 		if (machine_is_cats()) {
 			hw_pci = &cats_pci;
@@ -427,8 +435,8 @@
 			break;
 		}
 #endif
-#ifdef CONFIG_ARCH_NEXUSPCI
-		if (machine_is_nexuspci()) {
+#ifdef CONFIG_ARCH_FTVPCI
+		if (machine_is_ftvpci()) {
 			hw_pci = &ftv_pci;
 			break;
 		}
diff -urN linux-2.4.4-pre2/arch/arm/kernel/debug-armv.S linux/arch/arm/kernel/debug-armv.S
--- linux-2.4.4-pre2/arch/arm/kernel/debug-armv.S	Mon Sep 18 15:15:24 2000
+++ linux/arch/arm/kernel/debug-armv.S	Mon Dec  3 22:34:49 2001
@@ -12,7 +12,6 @@
 #include <linux/config.h>
 #include <linux/linkage.h>
 #include <asm/hardware.h>
-#include <asm/hardware/dec21285.h>
 
 		.text
 
@@ -67,8 +66,31 @@
 		tst	\rd, #0x10
 		beq	1001b
 		.endm
+ 	
+#elif defined(CONFIG_ARCH_SHARK)
+		.macro	addruart,rx
+		mov	\rx, #0xe0000000
+		orr	\rx, \rx, #0x000003f8
+		.endm
+
+		.macro	senduart,rd,rx
+		strb	\rd, [\rx]
+		.endm
+
+		.macro	busyuart,rd,rx
+		mov	\rd, #0
+1001:		add	\rd, \rd, #1
+		teq	\rd, #0x10000
+		bne	1001b
+		.endm
+
+		.macro	waituart,rd,rx
+		.endm
 
 #elif defined(CONFIG_FOOTBRIDGE)
+
+#include <asm/hardware/dec21285.h>
+
 #ifndef CONFIG_DEBUG_DC21285_PORT
 	/* For NetWinder debugging */
 		.macro	addruart,rx
@@ -120,9 +142,12 @@
 		.macro	waituart,rd,rx
 		.endm
 #endif
-#elif defined(CONFIG_ARCH_NEXUSPCI)
+#elif defined(CONFIG_ARCH_FTVPCI)
 		.macro	addruart,rx
-		ldr	\rx, =0xfff00000
+		mrc	p15, 0, \rx, c1, c0
+		tst	\rx, #1			@ MMU enabled?
+		movne	\rx, #0xe0000000
+		moveq	\rx, #0x10000000
 		.endm
 
 		.macro	senduart,rd,rx
@@ -131,8 +156,8 @@
 
 		.macro	busyuart,rd,rx
 1001:		ldr	\rd, [\rx, #0x4]
-		tst	\rd, #1 << 0
-		bne	1001b
+		tst	\rd, #1 << 2
+		beq	1001b
 		.endm
 
 		.macro	waituart,rd,rx
@@ -164,6 +189,26 @@
 		bne	1001b
 		.endm
 
+#elif defined(CONFIG_ARCH_CLPS7500)
+		.macro	addruart,rx
+		mov	\rx, #0xe0000000
+		orr	\rx, \rx, #0x00010000
+		orr	\rx, \rx, #0x00000be0
+		.endm
+
+		.macro	senduart,rd,rx
+		strb	\rd, [\rx]
+		.endm
+
+		.macro	busyuart,rd,rx
+		.endm
+
+		.macro	waituart,rd,rx
+1001:		ldrb	\rd, [\rx, #0x14]
+		tst	\rd, #0x20
+		beq	1001b
+		.endm
+
 #elif defined(CONFIG_ARCH_L7200)
 
 		.equ	io_virt, IO_BASE
@@ -174,12 +219,12 @@
 		tst	\rx, #1			@ MMU enabled?
 		moveq	\rx, #io_phys		@ physical base address
 		movne	\rx, #io_virt		@ virtual address
-		add	\rx, \rx, #0x00044000	@ Ser1
-@		add	\rx, \rx, #0x00045000	@ Ser2
+		add	\rx, \rx, #0x00044000	@ UART1
+@		add	\rx, \rx, #0x00045000	@ UART2
 		.endm
 
 		.macro	senduart,rd,rx
-		str	\rd, [\rx, #0x0]	@ UARTDR1
+		str	\rd, [\rx, #0x0]	@ UARTDR
 		.endm
 
 		.macro	waituart,rd,rx
@@ -198,9 +243,6 @@
 
 #include <asm/hardware/serial_amba.h>
 
-		.equ	io_virt, 0xf0000000 + (0x16000000 >> 4)
-		.equ	io_phys, 0x16000000
-
 		.macro	addruart,rx
 		mrc	p15, 0, \rx, c1, c0
 		tst	\rx, #1			@ MMU enabled?
@@ -223,6 +265,41 @@
 1001:		ldr	\rd, [\rx, #0x18]	@ UARTFLG
 		tst	\rd, #1 << 3		@ UARTFLGUBUSY - 1 when busy
 		bne	1001b
+		.endm
+
+#elif defined(CONFIG_ARCH_CLPS711X)
+
+#include <asm/hardware/clps7111.h>
+
+		.macro	addruart,rx
+		mrc	p15, 0, \rx, c1, c0
+		tst	\rx, #1			@ MMU enabled?
+		moveq	\rx, #CLPS7111_PHYS_BASE
+		movne	\rx, #CLPS7111_VIRT_BASE
+#ifndef CONFIG_DEBUG_CLPS711X_UART2
+		add	\rx, \rx, #0x0000	@ UART1
+#else
+		add	\rx, \rx, #0x1000	@ UART2
+#endif
+		.endm
+
+		.macro	senduart,rd,rx
+		str	\rd, [\rx, #0x0480]	@ UARTDR
+		.endm
+
+		.macro	waituart,rd,rx
+1001:		ldr	\rd, [\rx, #0x0140]	@ SYSFLGx
+		tst	\rd, #1 << 11		@ UBUSYx
+		bne	1001b
+		.endm
+
+		.macro	busyuart,rd,rx
+		tst	\rx, #0x1000		@ UART2 does not have CTS here
+		bne	1002f
+1001:		ldr	\rd, [\rx, #0x0140]	@ SYSFLGx
+		tst	\rd, #1 << 8		@ CTS
+		bne	1001b
+1002:
 		.endm
 
 #else
diff -urN linux-2.4.4-pre2/arch/arm/kernel/ecard.c linux/arch/arm/kernel/ecard.c
--- linux-2.4.4-pre2/arch/arm/kernel/ecard.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/arm/kernel/ecard.c	Mon Dec  3 22:34:49 2001
@@ -37,6 +37,7 @@
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/proc_fs.h>
+#include <linux/notifier.h>
 #include <linux/init.h>
 
 #include <asm/dma.h>
@@ -46,6 +47,7 @@
 #include <asm/irq.h>
 #include <asm/pgalloc.h>
 #include <asm/mmu_context.h>
+#include <asm/mach/irq.h>
 
 #ifndef CONFIG_ARCH_RPC
 #define HAVE_EXPMASK
@@ -53,7 +55,7 @@
 
 enum req {
 	req_readbytes,
-	req_reset
+	req_reset_all
 };
 
 struct ecard_request {
@@ -134,19 +136,14 @@
 #define POD_INT_ADDR(x)	((volatile unsigned char *)\
 			 ((BUS_ADDR((x)) - IO_BASE) + IO_START))
 
-static void
-ecard_task_reset(struct ecard_request *req)
+static inline void ecard_task_reset(void)
 {
-	if (req->ec == NULL) {
-		ecard_t *ec;
+	ecard_t *ec;
 
-		for (ec = cards; ec; ec = ec->next)
-			if (ec->loader)
-				ecard_loader_reset(POD_INT_ADDR(ec->podaddr),
-						   ec->loader);
-	} else if (req->ec->loader)
-		ecard_loader_reset(POD_INT_ADDR(req->ec->podaddr),
-				   req->ec->loader);
+	for (ec = cards; ec; ec = ec->next)
+		if (ec->loader)
+			ecard_loader_reset(POD_INT_ADDR(ec->podaddr),
+					   ec->loader);
 }
 
 static void
@@ -156,48 +153,47 @@
 	volatile unsigned char *base_addr =
 		(volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr);
 	unsigned int len = req->length;
+	unsigned int off = req->address;
 
 	if (req->ec->slot_no == 8) {
 		/*
-		 * The card maintains an index which
-		 * increments the address into a 4096-byte
-		 * page on each access.  We need to keep
+		 * The card maintains an index which increments the address
+		 * into a 4096-byte page on each access.  We need to keep
 		 * track of the counter.
 		 */
 		static unsigned int index;
-		unsigned int offset, page;
-		unsigned char byte = 0; /* keep gcc quiet */
-
-		offset = req->address & 4095;
-		page   = req->address >> 12;
+		unsigned int page;
 
-		if (page > 256)
+		page = (off >> 12) * 4;
+		if (page > 256 * 4)
 			return;
 
-		page *= 4;
+		off &= 4095;
 
-		if (offset == 0 || index > offset) {
-			/*
-			 * We need to reset the index counter.
-			 */
+		/*
+		 * If we are reading offset 0, or our current index is
+		 * greater than the offset, reset the hardware index counter.
+		 */
+		if (off == 0 || index > off) {
 			*base_addr = 0;
 			index = 0;
 		}
 
-		while (index <= offset) {
+		/*
+		 * Increment the hardware index counter until we get to the
+		 * required offset.  The read bytes are discarded.
+		 */
+		while (index < off) {
+			unsigned char byte;
 			byte = base_addr[page];
 			index += 1;
 		}
 
 		while (len--) {
-			*buf++ = byte;
-			if (len) {
-				byte = base_addr[page];
-				index += 1;
-			}
+			*buf++ = base_addr[page];
+			index += 1;
 		}
 	} else {
-		unsigned int off = req->address;
 
 		if (!req->use_loader || !req->ec->loader) {
 			off *= 4;
@@ -220,50 +216,30 @@
 
 }
 
+static void ecard_do_request(struct ecard_request *req)
+{
+	switch (req->req) {
+	case req_readbytes:
+		ecard_task_readbytes(req);
+		break;
+
+	case req_reset_all:
+		ecard_task_reset();
+		break;
+	}
+}
+
 #ifdef CONFIG_CPU_32
 static pid_t ecard_pid;
 static wait_queue_head_t ecard_wait;
-static wait_queue_head_t ecard_done;
 static struct ecard_request *ecard_req;
 
-/* to be removed when exec_mmap becomes extern */
-static int exec_mmap(void)
-{
-	struct mm_struct * mm, * old_mm;
-
-	old_mm = current->mm;
-	if (old_mm && atomic_read(&old_mm->mm_users) == 1) {
-		flush_cache_mm(old_mm);
-		mm_release();
-		exit_mmap(old_mm);
-		flush_tlb_mm(old_mm);
-		return 0;
-	}
-
-	mm = mm_alloc();
-	if (mm) {
-		struct mm_struct *active_mm = current->active_mm;
-
-		current->mm = mm;
-		current->active_mm = mm;
-		activate_mm(active_mm, mm);
-		mm_release();
-		if (old_mm) {
-			if (active_mm != old_mm) BUG();
-			mmput(old_mm);
-			return 0;
-		}
-		mmdrop(active_mm);
-		return 0;
-	}
-	return -ENOMEM;
-}
+static DECLARE_MUTEX_LOCKED(ecard_done_sem);
 
 /*
- * Set up the expansion card
- * daemon's environment.
+ * Set up the expansion card daemon's page tables.
  */
-static void ecard_init_task(int force)
+static void ecard_init_pgtables(struct mm_struct *mm)
 {
 	/* We want to set up the page tables for the following mapping:
 	 *  Virtual	Physical
@@ -279,29 +255,41 @@
 	pgd_t *src_pgd, *dst_pgd;
 	unsigned int dst_addr = IO_START;
 
-	if (!force)
-		exec_mmap();
-
-	src_pgd = pgd_offset(current->mm, IO_BASE);
-	dst_pgd = pgd_offset(current->mm, dst_addr);
+	src_pgd = pgd_offset(mm, IO_BASE);
+	dst_pgd = pgd_offset(mm, dst_addr);
 
 	while (dst_addr < IO_START + IO_SIZE) {
 		*dst_pgd++ = *src_pgd++;
 		dst_addr += PGDIR_SIZE;
 	}
 
-	flush_tlb_range(current->mm, IO_START, IO_START + IO_SIZE);
-
 	dst_addr = EASI_START;
-	src_pgd = pgd_offset(current->mm, EASI_BASE);
-	dst_pgd = pgd_offset(current->mm, dst_addr);
+	src_pgd = pgd_offset(mm, EASI_BASE);
+	dst_pgd = pgd_offset(mm, dst_addr);
 
 	while (dst_addr < EASI_START + EASI_SIZE) {
 		*dst_pgd++ = *src_pgd++;
 		dst_addr += PGDIR_SIZE;
 	}
 
-	flush_tlb_range(current->mm, EASI_START, EASI_START + EASI_SIZE);
+	flush_tlb_range(mm, IO_START, IO_START + IO_SIZE);
+	flush_tlb_range(mm, EASI_START, EASI_START + EASI_SIZE);
+}
+
+static int ecard_init_mm(void)
+{
+	struct mm_struct * mm = mm_alloc();
+	struct mm_struct *active_mm = current->active_mm;
+
+	if (!mm)
+		return -ENOMEM;
+
+	current->mm = mm;
+	current->active_mm = mm;
+	activate_mm(active_mm, mm);
+	mmdrop(active_mm);
+	ecard_init_pgtables(mm);
+	return 0;
 }
 
 static int
@@ -309,22 +297,23 @@
 {
 	struct task_struct *tsk = current;
 
-	tsk->session = 1;
-	tsk->pgrp = 1;
-
 	/*
 	 * We don't want /any/ signals, not even SIGKILL
 	 */
 	sigfillset(&tsk->blocked);
 	sigemptyset(&tsk->pending.signal);
 	recalc_sigpending(tsk);
-
 	strcpy(tsk->comm, "kecardd");
+	daemonize();
 
 	/*
-	 * Set up the environment
+	 * Allocate a mm.  We're not a lazy-TLB kernel task since we need
+	 * to set page table entries where the user space would be.  Note
+	 * that this also creates the page tables.  Failure is not an
+	 * option here.
 	 */
-	ecard_init_task(0);
+	if (ecard_init_mm())
+		panic("kecardd: unable to alloc mm\n");
 
 	while (1) {
 		struct ecard_request *req;
@@ -338,16 +327,8 @@
 			}
 		} while (req == NULL);
 
-		switch (req->req) {
-		case req_readbytes:
-			ecard_task_readbytes(req);
-			break;
-
-		case req_reset:
-			ecard_task_reset(req);
-			break;
-		}
-		wake_up(&ecard_done);
+		ecard_do_request(req);
+		up(&ecard_done_sem);
 	}
 }
 
@@ -357,76 +338,90 @@
  * FIXME: The test here is not sufficient to detect if the
  * kcardd is running.
  */
-static inline void
+static void
 ecard_call(struct ecard_request *req)
 {
 	/*
-	 * If we're called from task 0, or from an
-	 * interrupt (will be keyboard interrupt),
-	 * we forcefully set up the memory map, and
-	 * call the loader.  We can't schedule, or
-	 * sleep for this call.
+	 * Make sure we have a context that is able to sleep.
 	 */
-	if ((current == &init_task || in_interrupt()) &&
-	    req->req == req_reset && req->ec == NULL) {
-		ecard_init_task(1);
-		ecard_task_reset(req);
-	} else {
-		if (ecard_pid <= 0)
-			ecard_pid = kernel_thread(ecard_task, NULL,
-					CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	if (current == &init_task || in_interrupt())
+		BUG();
 
-		ecard_req = req;
+	if (ecard_pid <= 0)
+		ecard_pid = kernel_thread(ecard_task, NULL,
+				CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 
-		wake_up(&ecard_wait);
+	ecard_req = req;
+	wake_up(&ecard_wait);
 
-		sleep_on(&ecard_done);
-	}
+	/*
+	 * Now wait for kecardd to run.
+	 */
+	down(&ecard_done_sem);
 }
 #else
 /*
  * On 26-bit processors, we don't need the kcardd thread to access the
  * expansion card loaders.  We do it directly.
  */
-static inline void
-ecard_call(struct ecard_request *req)
-{
-	if (req->req == req_reset)
-		ecard_task_reset(req);
-	else
-		ecard_task_readbytes(req);
-}
+#define ecard_call(req)	ecard_do_request(req)
 #endif
 
 /* ======================= Mid-level card control ===================== */
+
 /*
- * This is called to reset the loaders for each expansion card on reboot.
+ * This function is responsible for resetting the expansion cards to a
+ * sensible state immediately prior to rebooting the system.  This function
+ * has process state (keventd), so we can sleep.
+ *
+ * Possible "val" values here:
+ *  SYS_RESTART   -  restarting system
+ *  SYS_HALT      - halting system
+ *  SYS_POWER_OFF - powering down system
  *
- * This is required to make sure that the card is in the correct state
- * that RiscOS expects it to be.
+ * We ignore all calls, unless it is a SYS_RESTART call - power down/halts
+ * will be followed by a SYS_RESTART if ctrl-alt-del is pressed again.
  */
-void
-ecard_reset(int slot)
+static void ecard_reboot(struct notifier_block *me, unsigned long val, void *v)
 {
 	struct ecard_request req;
 
-	req.req = req_reset;
+	if (val != SYS_RESTART)
+		return;
 
-	if (slot < 0)
-		req.ec = NULL;
-	else
-		req.ec = slot_to_ecard(slot);
+	/*
+	 * Disable the expansion card interrupt
+	 */
+	disable_irq(IRQ_EXPANSIONCARD);
 
+	/*
+	 * If we have any expansion card loader code which will handle
+	 * the reset for us, call it now.
+	 */
+	req.req = req_reset_all;
 	ecard_call(&req);
 
+	/*
+	 * Disable the expansion card interrupt again, just to be sure.
+	 */
+	disable_irq(IRQ_EXPANSIONCARD);
+
+	/*
+	 * Finally, reset the expansion card interrupt mask to
+	 * all enable (RISC OS doesn't set this)
+	 */
 #ifdef HAS_EXPMASK
-	if (have_expmask && slot < 0) {
-		have_expmask |= ~0;
-		EXPMASK_ENABLE = have_expmask;
-	}
+	have_expmask = ~0;
+	__raw_writeb(have_expmask, EXPMASK_ENABLE);
 #endif
 }
 
+static struct notifier_block ecard_reboot_notifier = {
+	notifier_call:	ecard_reboot,
+};
+
+
+
 static void
 ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
 {
@@ -506,7 +501,7 @@
 #ifdef HAS_EXPMASK
 	if (irqnr < 4 && have_expmask) {
 		have_expmask |= 1 << irqnr;
-		EXPMASK_ENABLE = have_expmask;
+		__raw_writeb(have_expmask, EXPMASK_ENABLE);
 	}
 #endif
 }
@@ -516,7 +511,7 @@
 #ifdef HAS_EXPMASK
 	if (irqnr < 4 && have_expmask) {
 		have_expmask &= ~(1 << irqnr);
-		EXPMASK_ENABLE = have_expmask;
+		__raw_writeb(have_expmask, EXPMASK_ENABLE);
 	}
 #endif
 }
@@ -556,7 +551,7 @@
  *
  * They are not meant to be called directly, but via enable/disable_irq.
  */
-void ecard_enableirq(unsigned int irqnr)
+static void ecard_enableirq(unsigned int irqnr)
 {
 	ecard_t *ec = slot_to_ecard(irqnr - 32);
 
@@ -572,7 +567,7 @@
 	}
 }
 
-void ecard_disableirq(unsigned int irqnr)
+static void ecard_disableirq(unsigned int irqnr)
 {
 	ecard_t *ec = slot_to_ecard(irqnr - 32);
 
@@ -717,7 +712,7 @@
 	const unsigned int statusmask = 15;
 	unsigned int status;
 
-	status = EXPMASK_STATUS & statusmask;
+	status = __raw_readb(EXPMASK_STATUS) & statusmask;
 	if (status) {
 		unsigned int slot;
 		ecard_t *ec;
@@ -739,18 +734,21 @@
 			 * otherwise you will lose serial data at high speeds!
 			 */
 			oldexpmask = have_expmask;
-			EXPMASK_ENABLE = (have_expmask &= priority_masks[slot]);
+			have_expmask &= priority_masks[slot];
+			__raw_writeb(have_expmask, EXPMASK_ENABLE);
 			sti();
 			do_ecard_IRQ(ec->irq, regs);
 			cli();
-			EXPMASK_ENABLE = have_expmask = oldexpmask;
-			status = EXPMASK_STATUS & statusmask;
+			have_expmask = oldexpmask;
+			__raw_writeb(have_expmask, EXPMASK_ENABLE);
+			status = __raw_readb(EXPMASK_STATUS) & statusmask;
 			if (status)
 				goto again;
 		} else {
 			printk(KERN_WARNING "card%d: interrupt from unclaimed "
 			       "card???\n", slot);
-			EXPMASK_ENABLE = (have_expmask &= ~(1 << slot));
+			have_expmask &= ~(1 << slot);
+			__raw_writeb(have_expmask, EXPMASK_ENABLE);
 		}
 	} else
 		printk(KERN_WARNING "Wild interrupt from backplane (masks)\n");
@@ -762,10 +760,10 @@
 	ecard_t *ec;
 	int found;
 
-	EXPMASK_ENABLE = 0x00;
-	EXPMASK_STATUS = 0xff;
-	found = ((EXPMASK_STATUS & 15) == 0);
-	EXPMASK_ENABLE = 0xff;
+	__raw_writeb(0x00, EXPMASK_ENABLE);
+	__raw_writeb(0xff, EXPMASK_STATUS);
+	found = (__raw_readb(EXPMASK_STATUS) & 15) == 0;
+	__raw_writeb(0xff, EXPMASK_ENABLE);
 
 	if (!found)
 		return;
@@ -781,7 +779,7 @@
 	for (ec = cards; ec; ec = ec->next)
 		have_expmask |= 1 << ec->slot_no;
 
-	EXPMASK_ENABLE = have_expmask;
+	__raw_writeb(have_expmask, EXPMASK_ENABLE);
 }
 #else
 #define ecard_probeirqhw()
@@ -830,7 +828,7 @@
 	}
 
 #ifdef IOMD_ECTCR
-	outb(ectcr, IOMD_ECTCR);
+	iomd_writeb(ectcr, IOMD_ECTCR);
 #endif
 	return address;
 }
@@ -910,9 +908,8 @@
 	int i, rc = -ENOMEM;
 
 	ec = kmalloc(sizeof(ecard_t), GFP_KERNEL);
-
 	if (!ec)
-		goto nodev;
+		goto nomem;
 
 	memset(ec, 0, sizeof(ecard_t));
 
@@ -969,27 +966,31 @@
 	if (slot == 8)
 		ec->irq = 11;
 #endif
+	/*
+	 * hook the interrupt handlers
+	 */
+	if (ec->irq != 0 && ec->irq >= 32) {
+		irq_desc[ec->irq].mask_ack = ecard_disableirq;
+		irq_desc[ec->irq].mask     = ecard_disableirq;
+		irq_desc[ec->irq].unmask   = ecard_enableirq;
+		irq_desc[ec->irq].valid    = 1;
+	}
+
 #ifdef CONFIG_ARCH_RPC
 	/* On RiscPC, only first two slots have DMA capability */
 	if (slot < 2)
 		ec->dma = 2 + slot;
 #endif
-#if 0	/* We don't support FIQs on expansion cards at the moment */
-	ec->fiq = 96 + slot;
-#endif
-
-	rc = 0;
 
 	for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
 
 	*ecp = ec;
+	slot_to_expcard[slot] = ec;
+	return 0;
 
 nodev:
-	if (rc && ec)
-		kfree(ec);
-	else
-		slot_to_expcard[slot] = ec;
-
+	kfree(ec);
+nomem:
 	return rc;
 }
 
@@ -1058,9 +1059,13 @@
 {
 	int slot;
 
+	/*
+	 * Register our reboot notifier
+	 */
+	register_reboot_notifier(&ecard_reboot_notifier);
+
 #ifdef CONFIG_CPU_32
 	init_waitqueue_head(&ecard_wait);
-	init_waitqueue_head(&ecard_done);
 #endif
 
 	printk("Probing expansion cards\n");
diff -urN linux-2.4.4-pre2/arch/arm/kernel/entry-armv.S linux/arch/arm/kernel/entry-armv.S
--- linux-2.4.4-pre2/arch/arm/kernel/entry-armv.S	Tue Mar  6 19:44:35 2001
+++ linux/arch/arm/kernel/entry-armv.S	Mon Dec  3 22:34:49 2001
@@ -89,41 +89,52 @@
 		.if	ioc_base_low
 		orr	r4, r4, #ioc_base_low
 		.endif
-		ldrb	\irqstat, [r4, #0x24]		@ get high priority first
-		adr	\base, irq_prio_h
+		ldrb	\irqstat, [r4, #IOMD_IRQREQB]	@ get high priority first
+		ldr	\base, =irq_prio_h
 		teq	\irqstat, #0
 #ifdef IOMD_BASE
-		ldreqb	\irqstat, [r4, #0x1f4]		@ get dma
-		adreq	\base, irq_prio_d
+		ldreqb	\irqstat, [r4, #IOMD_DMAREQ]	@ get dma
+		addeq	\base, \base, #256		@ irq_prio_h table size
 		teqeq	\irqstat, #0
+		bne	2406f
 #endif
-		ldreqb	\irqstat, [r4, #0x14]		@ get low priority
-		adreq	\base, irq_prio_l
-
-		teq	\irqstat, #0
-		ldrneb	\irqnr, [\base, \irqstat]	@ get IRQ number
+		ldreqb	\irqstat, [r4, #IOMD_IRQREQA]	@ get low priority
+		addeq	\base, \base, #256		@ irq_prio_d table size
+		teqeq	\irqstat, #0
+#ifdef IOMD_IRQREQC
+		ldreqb	\irqstat, [r4, #IOMD_IRQREQC]
+		addeq	\base, \base, #256		@ irq_prio_l table size
+		teqeq	\irqstat, #0
+#endif
+#ifdef IOMD_IRQREQD
+		ldreqb	\irqstat, [r4, #IOMD_IRQREQD]
+		addeq	\base, \base, #256		@ irq_prio_lc table size
+		teqeq	\irqstat, #0
+#endif
+2406:		ldrneb	\irqnr, [\base, \irqstat]	@ get IRQ number
 		.endm
 
 /*
- * Interrupt table (incorporates priority)
+ * Interrupt table (incorporates priority).  Please note that we
+ * rely on the order of these tables (see above code).
  */
 		.macro	irq_prio_table
-irq_prio_l:	.byte	 0, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
-		.byte	 4, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
-		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
-		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
-		.byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
-		.byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
-		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
-		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+irq_prio_h:	.byte	 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	12, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
 #ifdef IOMD_BASE
 irq_prio_d:	.byte	 0,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
 		.byte	20,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
@@ -142,26 +153,64 @@
 		.byte	21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
 		.byte	21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
 #endif
-irq_prio_h:	.byte	 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	12, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
-		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+irq_prio_l:	.byte	 0, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 4, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+#ifdef IOMD_IRQREQC
+irq_prio_lc:	.byte	24,24,25,24,26,26,26,26,27,27,27,27,27,27,27,27
+		.byte	28,24,25,24,26,26,26,26,27,27,27,27,27,27,27,27
+		.byte	29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+		.byte	29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+		.byte	30,30,30,30,30,30,30,30,27,27,27,27,27,27,27,27
+		.byte	30,30,30,30,30,30,30,30,27,27,27,27,27,27,27,27
+		.byte	29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+		.byte	29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+		.byte	31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+#endif
+#ifdef IOMD_IRQREQD
+irq_prio_ld:	.byte	40,40,41,40,42,42,42,42,43,43,43,43,43,43,43,43
+		.byte	44,40,41,40,42,42,42,42,43,43,43,43,43,43,43,43
+		.byte	45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+		.byte	45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+		.byte	46,46,46,46,46,46,46,46,43,43,43,43,43,43,43,43
+		.byte	46,46,46,46,46,46,46,46,43,43,43,43,43,43,43,43
+		.byte	45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+		.byte	45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+		.byte	47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+#endif
 		.endm
 
 #elif defined(CONFIG_ARCH_EBSA110)
 
+#define IRQ_STAT		0xff000000	/* read */
+
 		.macro	disable_fiq
 		.endm
 
@@ -457,7 +506,9 @@
 		.macro	irq_prio_table
 		.endm
 
-#elif defined(CONFIG_ARCH_P720T)
+#elif defined(CONFIG_ARCH_CLPS711X)
+
+#include <asm/hardware/clps7111.h>
 
 		.macro	disable_fiq
 		.endm
diff -urN linux-2.4.4-pre2/arch/arm/kernel/entry-common.S linux/arch/arm/kernel/entry-common.S
--- linux-2.4.4-pre2/arch/arm/kernel/entry-common.S	Tue Mar  6 19:44:35 2001
+++ linux/arch/arm/kernel/entry-common.S	Mon Dec  3 22:34:49 2001
@@ -35,9 +35,7 @@
 
 		.align	5
 fast_syscall_return:
-		str	r0, [sp, #S_R0 + S_OFF]	@ returned r0
-slow_syscall_return:
-		add	sp, sp, #S_OFF
+		str	r0, [sp, #S_R0+S_OFF]!	@ returned r0
 ret_from_sys_call:				@ external entry
 		get_softirq r0
 		get_current_task r5
@@ -52,6 +50,7 @@
 		bne	ret_reschedule
 		teq	r1, #0			@ check for signals
 		blne	ret_signal
+
 ret_from_all:	restore_user_regs		@ internal
 
 ret_signal:	mov	r1, sp			@ internal
@@ -132,7 +131,8 @@
 		str	ip, [sp, #S_IP + S_OFF]	@ trace exit [IP = 1]
 		bl	SYMBOL_NAME(syscall_trace)
 		str	tip, [sp, #S_IP + S_OFF]
-		b	slow_syscall_return
+		add	sp, sp, #S_OFF
+		b	ret_from_sys_call
 
 2:		add	r1, sp, #S_OFF
 		tst	scno, #0x00f00000	@ is it a Unix SWI?
@@ -142,8 +142,9 @@
 		b	SYMBOL_NAME(sys_ni_syscall) @ not private func
 
 3:		eor	r0, scno, #OS_NUMBER <<20	@ Put OS number back
-		adrsvc	al, lr, slow_syscall_return
-		b	SYMBOL_NAME(deferred)
+		bl	SYMBOL_NAME(deferred)
+		add	sp, sp, #S_OFF
+		b	ret_from_sys_call
 
 		.align	5
 		.type	__irq_stat, #object
diff -urN linux-2.4.4-pre2/arch/arm/kernel/fiq.c linux/arch/arm/kernel/fiq.c
--- linux-2.4.4-pre2/arch/arm/kernel/fiq.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/kernel/fiq.c	Mon Dec  3 22:34:49 2001
@@ -124,7 +124,7 @@
 #ifdef CONFIG_CPU_26
 	"mov	%0, pc
 	bic	%1, %0, #0x3
-	orr	%1, %1, #0x0c000001
+	orr	%1, %1, %3
 	teqp	%1, #0		@ select FIQ mode
 	mov	r0, r0
 	ldmia	%2, {r8 - r14}
@@ -133,7 +133,7 @@
 #endif
 #ifdef CONFIG_CPU_32
 	"mrs	%0, cpsr
-	mov	%1, #0xc1
+	mov	%1, %3
 	msr	cpsr_c, %1	@ select FIQ mode
 	mov	r0, r0
 	ldmia	%2, {r8 - r14}
@@ -141,7 +141,7 @@
 	mov	r0, r0"
 #endif
 	: "=&r" (tmp), "=&r" (tmp2)
-	: "r" (&regs->ARM_r8)
+	: "r" (&regs->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE)
 	/* These registers aren't modified by the above code in a way
 	   visible to the compiler, but we mark them as clobbers anyway
 	   so that GCC won't put any of the input or output operands in
@@ -156,7 +156,7 @@
 #ifdef CONFIG_CPU_26
 	"mov	%0, pc
 	bic	%1, %0, #0x3
-	orr	%1, %1, #0x0c000001
+	orr	%1, %1, %3
 	teqp	%1, #0		@ select FIQ mode
 	mov	r0, r0
 	stmia	%2, {r8 - r14}
@@ -165,7 +165,7 @@
 #endif
 #ifdef CONFIG_CPU_32
 	"mrs	%0, cpsr
-	mov	%1, #0xc1
+	mov	%1, %3
 	msr	cpsr_c, %1	@ select FIQ mode
 	mov	r0, r0
 	stmia	%2, {r8 - r14}
@@ -173,7 +173,7 @@
 	mov	r0, r0"
 #endif
 	: "=&r" (tmp), "=&r" (tmp2)
-	: "r" (&regs->ARM_r8)
+	: "r" (&regs->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE)
 	/* These registers aren't modified by the above code in a way
 	   visible to the compiler, but we mark them as clobbers anyway
 	   so that GCC won't put any of the input or output operands in
diff -urN linux-2.4.4-pre2/arch/arm/kernel/head-armo.S linux/arch/arm/kernel/head-armo.S
--- linux-2.4.4-pre2/arch/arm/kernel/head-armo.S	Mon Nov 27 17:07:59 2000
+++ linux/arch/arm/kernel/head-armo.S	Mon Dec  3 22:34:49 2001
@@ -82,9 +82,9 @@
 		mov	r1, #0
 		str	r0, [r1, #4]
 		ldr	r0, arm2_id
-		swp	r2, r2, [r1]		@ check for swp (ARM2 can't)
+		swp	r2, r2, [r1]		@ check for swp (ARM2 cant)
 		ldr	r0, arm250_id
-		mrc	15, 0, r3, c0, c0	@ check for CP#15 (ARM250 can't)
+		mrc	15, 0, r3, c0, c0	@ check for CP#15 (ARM250 cant)
 		mov	r0, r3
 continue:	mov	r2, #0xeb000000		@ Make undef vector loop
 		sub	r2, r2, #2
diff -urN linux-2.4.4-pre2/arch/arm/kernel/head-armv.S linux/arch/arm/kernel/head-armv.S
--- linux-2.4.4-pre2/arch/arm/kernel/head-armv.S	Mon Nov 27 17:07:59 2000
+++ linux/arch/arm/kernel/head-armv.S	Mon Dec  3 22:34:49 2001
@@ -26,7 +26,9 @@
  *
  * Note that swapper_pg_dir is the virtual address of the page tables, and
  * pgtbl gives us a position-independent reference to these tables.  We can
- * do this because stext == TEXT_ADDR
+ * do this because stext == TEXTADDR
+ *
+ * swapper_pg_dir, pgtbl and krnladr are all closely related.
  */
 #if (TEXTADDR & 0xffff) != 0x8000
 #error TEXTADDR must start at 0xXXXX8000
@@ -35,24 +37,36 @@
 		.globl	SYMBOL_NAME(swapper_pg_dir)
 		.equ	SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000
 
-		.macro	pgtbl, reg
+		.macro	pgtbl, reg, rambase
 		adr	\reg, stext
 		sub	\reg, \reg, #0x4000
 		.endm
 
+/*
+ * Since the page table is closely related to the kernel start address, we
+ * can convert the page table base address to the base address of the section
+ * containing both.
+ */
+		.macro	krnladr, rd, pgtable, rambase
+		bic	\rd, \pgtable, #0x000ff000
+		.endm
+
+/*
+ *  Kernel startup entry point.
+ *
+ * The rules are:
+ *  r0      - should be 0
+ *  r1      - unique architecture number
+ *  MMU     - off
+ *  I-cache - on or off
+ *  D-cache - off
+ *
+ * See linux/arch/arm/tools/mach-types for the complete list of numbers
+ * for r1.
+ */
 		.section ".text.init",#alloc,#execinstr
 		.type	stext, #function
 ENTRY(stext)
-/*
- * Entry point.  The general rules are:
- *  should be called with r0 == 0
- *  r1 contains the unique architecture number
- *  with MMU is off, I-cache may be on or off, D-cache should be off.
- * See linux/arch/arm/kernel/arch.c and linux/include/asm-arm/system.h
- * for the complete list of numbers for r1.  If you require a new number,
- * please follow the instructions given towards the end of
- * linux/Documentation/arm/README.
- */
 		mov	r12, r0
 /*
  * NOTE!  Any code which is placed here should be done for one of
@@ -115,7 +129,7 @@
 #endif
 
 		mov	r0, #F_BIT | I_BIT | MODE_SVC	@ make sure svc mode
-		msr	cpsr_c, r0			@ and all irqs diabled
+		msr	cpsr_c, r0			@ and all irqs disabled
 		bl	__lookup_processor_type
 		teq	r10, #0				@ invalid processor?
 		moveq	r0, #'p'			@ yes, error 'p'
@@ -129,6 +143,7 @@
 		add	pc, r10, #12			@ initialise processor
 							@ (return control reg)
 
+		.type	__switch_data, %object
 __switch_data:	.long	__mmap_switched
 		.long	SYMBOL_NAME(compat)
 		.long	SYMBOL_NAME(__bss_start)
@@ -138,6 +153,7 @@
 		.long	SYMBOL_NAME(cr_alignment)
 		.long	SYMBOL_NAME(init_task_union)+8192
 
+		.type	__ret, %function
 __ret:		ldr	lr, __switch_data
 		mcr	p15, 0, r0, c1, c0
 		mov	r0, r0
@@ -190,31 +206,37 @@
  * r8 = page table flags
  */
 __create_page_tables:
-		pgtbl	r4
+		pgtbl	r4, r5				@ page table address
+
+		/*
+		 * Clear the 16K level 1 swapper page table
+		 */
 		mov	r0, r4
 		mov	r3, #0
-		add	r2, r0, #0x4000			@ 16k of page table
-1:		str	r3, [r0], #4			@ Clear page table
+		add	r2, r0, #0x4000
+1:		str	r3, [r0], #4
 		str	r3, [r0], #4
 		str	r3, [r0], #4
 		str	r3, [r0], #4
 		teq	r0, r2
 		bne	1b
+
 		/*
-		 * Create identity mapping for first MB of kernel.
-		 * This is marked cacheable and bufferable.
-		 *
-		 * The identity mapping will be removed by  paging_init()
+		 * Create identity mapping for first MB of kernel to
+		 * cater for the MMU enable.  This identity mapping
+		 * will be removed by paging_init()
 		 */
-		add	r3, r8, r5			@ mmuflags + start of RAM
-		add	r0, r4, r5, lsr #18
-		str	r3, [r0]			@ identity mapping
+		krnladr	r2, r4, r5			@ start of kernel
+		add	r3, r8, r2			@ flags + kernel base
+		str	r3, [r4, r2, lsr #18]		@ identity mapping
+
 		/*
 		 * Now setup the pagetables for our kernel direct
 		 * mapped region.  We round TEXTADDR down to the
 		 * nearest megabyte boundary.
 		 */
-		add	r0, r4, #(TEXTADDR & 0xfff00000) >> 18 @ start of kernel
+		add	r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ start of kernel
+		add	r0, r0, #(TEXTADDR & 0x00f00000) >> 18
 		str	r3, [r0], #4			@ PAGE_OFFSET + 0MB
 		add	r3, r3, #1 << 20
 		str	r3, [r0], #4			@ PAGE_OFFSET + 1MB
@@ -223,13 +245,25 @@
 		add	r3, r3, #1 << 20
 		str	r3, [r0], #4			@ PAGE_OFFSET + 3MB
 
+		/*
+		 * Ensure that the first section of RAM is present.
+		 * we assume that:
+		 *  1. the RAM is aligned to a 256MB boundary
+		 *  2. the kernel is executing in the same 256MB chunk
+		 *     as the start of RAM.
+		 */
+		bic	r0, r0, #0x0ff00000 >> 18	@ round down
+		and	r2, r5, #0xf0000000		@ round down
+		add	r3, r8, r2			@ flags + rambase
+		str	r3, [r0]
+
 		bic	r8, r8, #0x0c			@ turn off cacheable
 							@ and bufferable bits
 #ifdef CONFIG_DEBUG_LL
 		/*
 		 * Map in IO space for serial debugging.
 		 * This allows debug messages to be output
-		 * via a serial before paging_init.
+		 * via a serial console before paging_init.
 		 */
 		add	r0, r4, r7
 		rsb	r3, r7, #0x4000	@ PTRS_PER_PGD*sizeof(long)
@@ -241,12 +275,13 @@
 		add	r3, r3, #1 << 20
 		teq	r0, r2
 		bne	1b
-#ifdef CONFIG_ARCH_NETWINDER
+#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
 		/*
 		 * If we're using the NetWinder, we need to map in
 		 * the 16550-type serial port for the debug messages
 		 */
-		teq	r1, #5
+		teq	r1, #MACH_TYPE_NETWINDER
+		teqne	r1, #MACH_TYPE_CATS
 		bne	1f
 		add	r0, r4, #0x3fc0
 		mov	r3, #0x7c000000
@@ -263,13 +298,12 @@
 		 * Similar reasons here - for debug.  This is
 		 * only for Acorn RiscPC architectures.
 		 */
-		teq	r5, #0
-		addne	r0, r4, #0x80			@ 02000000
-		movne	r3, #0x02000000
-		orrne	r3, r3, r8
-		strne	r3, [r0]
-		addne	r0, r4, #0x3600			@ d8000000
-		strne	r3, [r0]
+		add	r0, r4, #0x80			@ 02000000
+		mov	r3, #0x02000000
+		orr	r3, r3, r8
+		str	r3, [r0]
+		add	r0, r4, #0x3600			@ d8000000
+		str	r3, [r0]
 #endif
 		mov	pc, lr
 
diff -urN linux-2.4.4-pre2/arch/arm/kernel/io.c linux/arch/arm/kernel/io.c
--- linux-2.4.4-pre2/arch/arm/kernel/io.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/io.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,49 @@
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+
+/*
+ * Copy data from IO memory space to "real" memory space.
+ * This needs to be optimized.
+ */
+void _memcpy_fromio(void * to, unsigned long from, size_t count)
+{
+	while (count) {
+		count--;
+		*(char *) to = readb(from);
+		((char *) to)++;
+		from++;
+	}
+}
+
+/*
+ * Copy data from "real" memory space to IO memory space.
+ * This needs to be optimized.
+ */
+void _memcpy_toio(unsigned long to, const void * from, size_t count)
+{
+	while (count) {
+		count--;
+		writeb(*(char *) from, to);
+		((char *) from)++;
+		to++;
+	}
+}
+
+/*
+ * "memset" on IO memory space.
+ * This needs to be optimized.
+ */
+void _memset_io(unsigned long dst, int c, size_t count)
+{
+	while (count) {
+		count--;
+		writeb(c, dst);
+		dst++;
+	}
+}
+
+EXPORT_SYMBOL(_memcpy_fromio);
+EXPORT_SYMBOL(_memcpy_toio);
+EXPORT_SYMBOL(_memset_io);
diff -urN linux-2.4.4-pre2/arch/arm/kernel/irq-arch.c linux/arch/arm/kernel/irq-arch.c
--- linux-2.4.4-pre2/arch/arm/kernel/irq-arch.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/kernel/irq-arch.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,41 @@
+/*
+ *  linux/arch/arm/kernel/irq-arch.c
+ *
+ *  Copyright (C) 1995-2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  We contain the architecture-specific parts of interrupt handling
+ *  in this file.  In 2.5, it will move into the various arch/arm/mach-*
+ *  directories.
+ */
+#include <linux/ptrace.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <asm/mach/irq.h>
+
+/*
+ * Get architecture specific interrupt handlers
+ * and interrupt initialisation.
+ */
+#include <asm/arch/irq.h>
+
+void __init genarch_init_irq(void)
+{
+	irq_init_irq();
+}
+
diff -urN linux-2.4.4-pre2/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c
--- linux-2.4.4-pre2/arch/arm/kernel/irq.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/arm/kernel/irq.c	Mon Dec  3 22:34:49 2001
@@ -2,7 +2,7 @@
  *  linux/arch/arm/kernel/irq.c
  *
  *  Copyright (C) 1992 Linus Torvalds
- *  Modifications for ARM processor Copyright (C) 1995-1998 Russell King.
+ *  Modifications for ARM processor Copyright (C) 1995-2000 Russell King.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -29,9 +29,11 @@
 #include <linux/smp.h>
 #include <linux/init.h>
 
-#include <asm/hardware.h>
-#include <asm/io.h>
+#include <asm/irq.h>
 #include <asm/system.h>
+#include <asm/mach/irq.h>
+
+#include <asm/arch/irq.h>	/* pick up fixup_irq definition */
 
 /*
  * Maximum IRQ count.  Currently, this is arbitary.  However, it should
@@ -42,41 +44,11 @@
  */
 #define MAX_IRQ_CNT	100000
 
-spinlock_t irq_controller_lock;
-
-int setup_arm_irq(int, struct irqaction *);
-extern int get_fiq_list(char *);
-extern void init_FIQ(void);
-
-struct irqdesc {
-	unsigned int	 nomask   : 1;		/* IRQ does not mask in IRQ   */
-	unsigned int	 enabled  : 1;		/* IRQ is currently enabled   */
-	unsigned int	 triggered: 1;		/* IRQ has occurred	      */
-	unsigned int	 probing  : 1;		/* IRQ in use for a probe     */
-	unsigned int	 probe_ok : 1;		/* IRQ can be used for probe  */
-	unsigned int	 valid    : 1;		/* IRQ claimable	      */
-	unsigned int	 noautoenable : 1;	/* don't automatically enable IRQ */
-	unsigned int	 unused   :25;
-	void (*mask_ack)(unsigned int irq);	/* Mask and acknowledge IRQ   */
-	void (*mask)(unsigned int irq);		/* Mask IRQ		      */
-	void (*unmask)(unsigned int irq);	/* Unmask IRQ		      */
-	struct irqaction *action;
-	/*
-	 * IRQ lock detection
-	 */
-	unsigned int	 lck_cnt;
-	unsigned int	 lck_pc;
-	unsigned int	 lck_jif;
-};
-
-static struct irqdesc irq_desc[NR_IRQS];
 static volatile unsigned long irq_err_count;
+static spinlock_t irq_controller_lock;
 
-/*
- * Get architecture specific interrupt handlers
- * and interrupt initialisation.
- */
-#include <asm/arch/irq.h>
+struct irqdesc irq_desc[NR_IRQS];
+void (*init_arch_irq)(void) __initdata = NULL;
 
 /*
  * Dummy mask/unmask handler
@@ -85,6 +57,14 @@
 {
 }
 
+/**
+ *	disable_irq - disable an irq and wait for completion
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line.
+ *
+ *	This function may be called - with care - from IRQ context.
+ */
 void disable_irq(unsigned int irq)
 {
 	unsigned long flags;
@@ -95,6 +75,14 @@
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
 
+/**
+ *	enable_irq - enable interrupt handling on an irq
+ *	@irq: Interrupt to enable
+ *
+ *	Re-enables the processing of interrupts on this IRQ line
+ *
+ *	This function may be called from IRQ context.
+ */
 void enable_irq(unsigned int irq)
 {
 	unsigned long flags;
@@ -271,6 +259,7 @@
 	int shared = 0;
 	struct irqaction *old, **p;
 	unsigned long flags;
+	struct irqdesc *desc;
 
 	/*
 	 * Some drivers like serial.c use request_irq() heavily,
@@ -292,8 +281,9 @@
 	/*
 	 * The following block of code has to be executed atomically
 	 */
+	desc = irq_desc + irq;
 	spin_lock_irqsave(&irq_controller_lock, flags);
-	p = &irq_desc[irq].action;
+	p = &desc->action;
 	if ((old = *p) != NULL) {
 		/* Can't share interrupts unless both agree to */
 		if (!(old->flags & new->flags & SA_SHIRQ)) {
@@ -312,11 +302,11 @@
 	*p = new;
 
 	if (!shared) {
-		irq_desc[irq].nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
-		irq_desc[irq].probing = 0;
-		if (!irq_desc[irq].noautoenable) {
-			irq_desc[irq].enabled = 1;
-			irq_desc[irq].unmask(irq);
+		desc->nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
+		desc->probing = 0;
+		if (!desc->noautoenable) {
+			desc->enabled = 1;
+			desc->unmask(irq);
 		}
 	}
 
@@ -324,13 +314,45 @@
 	return 0;
 }
 
+/**
+ *	request_irq - allocate an interrupt line
+ *	@irq: Interrupt line to allocate
+ *	@handler: Function to be called when the IRQ occurs
+ *	@irqflags: Interrupt type flags
+ *	@devname: An ascii name for the claiming device
+ *	@dev_id: A cookie passed back to the handler function
+ *
+ *	This call allocates interrupt resources and enables the
+ *	interrupt line and IRQ handling. From the point this
+ *	call is made your handler function may be invoked. Since
+ *	your handler function must clear any interrupt the board
+ *	raises, you must take care both to initialise your hardware
+ *	and to set up the interrupt handler in the right order.
+ *
+ *	Dev_id must be globally unique. Normally the address of the
+ *	device data structure is used as the cookie. Since the handler
+ *	receives this value it makes sense to use it.
+ *
+ *	If your interrupt is shared you must pass a non NULL dev_id
+ *	as this is required when freeing the interrupt.
+ *
+ *	Flags:
+ *
+ *	SA_SHIRQ		Interrupt is shared
+ *
+ *	SA_INTERRUPT		Disable local interrupts while processing
+ *
+ *	SA_SAMPLE_RANDOM	The interrupt can be used for entropy
+ *
+ */
 int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 		 unsigned long irq_flags, const char * devname, void *dev_id)
 {
 	unsigned long retval;
 	struct irqaction *action;
 
-	if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler)
+	if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||
+	    (irq_flags & SA_SHIRQ && !dev_id))
 		return -EINVAL;
 
 	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
@@ -351,6 +373,18 @@
 	return retval;
 }
 
+/**
+ *	free_irq - free an interrupt
+ *	@irq: Interrupt line to free
+ *	@dev_id: Device identity to free
+ *
+ *	Remove an interrupt handler. The handler is removed and if the
+ *	interrupt line is no longer in use by any driver it is disabled.
+ *	On a shared IRQ the caller must ensure the interrupt is disabled
+ *	on the card it drives before calling this function.
+ *
+ *	This function may be called from interrupt context.
+ */
 void free_irq(unsigned int irq, void *dev_id)
 {
 	struct irqaction * action, **p;
@@ -486,6 +520,6 @@
 		irq_desc[irq].unmask   = dummy_mask_unmask_irq;
 	}
 
-	irq_init_irq();
+	init_arch_irq();
 	init_dma();
 }
diff -urN linux-2.4.4-pre2/arch/arm/kernel/leds-ebsa110.c linux/arch/arm/kernel/leds-ebsa110.c
--- linux-2.4.4-pre2/arch/arm/kernel/leds-ebsa110.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/kernel/leds-ebsa110.c	Wed Dec 31 16:00:00 1969
@@ -1,51 +0,0 @@
-/*
- *  linux/arch/arm/kernel/leds-ebsa110.c
- *
- *  Copyright (C) 1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- *  EBSA-110 LED control routines.  We use the led as follows:
- *
- *   - Red - toggles state every 50 timer interrupts
- */
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-
-#include <asm/hardware.h>
-#include <asm/leds.h>
-#include <asm/system.h>
-#include <asm/mach-types.h>
-
-static spinlock_t leds_lock;
-
-static void ebsa110_leds_event(led_event_t ledevt)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&leds_lock, flags);
-
-	switch(ledevt) {
-	case led_timer:
-		*(volatile unsigned char *)0xf2400000 ^= 128;
-		break;
-
-	default:
-		break;
-	}
-
-	spin_unlock_irqrestore(&leds_lock, flags);
-}
-
-static int __init leds_init(void)
-{
-	if (machine_is_ebsa110())
-		leds_event = ebsa110_leds_event;
-
-	return 0;
-}
-
-__initcall(leds_init);
diff -urN linux-2.4.4-pre2/arch/arm/kernel/oldlatches.c linux/arch/arm/kernel/oldlatches.c
--- linux-2.4.4-pre2/arch/arm/kernel/oldlatches.c	Mon Nov 27 17:07:59 2000
+++ linux/arch/arm/kernel/oldlatches.c	Mon Dec  3 22:34:49 2001
@@ -59,7 +59,7 @@
 	}
 }
 
-initcall(oldlatch_init);
+__initcall(oldlatch_init);
 
 EXPORT_SYMBOL(oldlatch_aupdate);
 EXPORT_SYMBOL(oldlatch_bupdate);
diff -urN linux-2.4.4-pre2/arch/arm/kernel/setup.c linux/arch/arm/kernel/setup.c
--- linux-2.4.4-pre2/arch/arm/kernel/setup.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/kernel/setup.c	Mon Dec  3 22:34:49 2001
@@ -26,6 +26,7 @@
 #include <asm/mach-types.h>
 
 #include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
 
 #ifndef MEM_SIZE
 #define MEM_SIZE	(16*1024*1024)
@@ -35,10 +36,21 @@
 #define CONFIG_CMDLINE ""
 #endif
 
+#if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE)
+char fpe_type[8];
+
+static int __init fpe_setup(char *line)
+{
+	memcpy(fpe_type, line, 8);
+	return 1;
+}
+
+__setup("fpe=", fpe_setup);
+#endif
+
 extern void paging_init(struct meminfo *, struct machine_desc *desc);
 extern void bootmem_init(struct meminfo *);
 extern void reboot_setup(char *str);
-extern void disable_hlt(void);
 extern unsigned long memparse(char *ptr, char **retptr);
 extern int root_mountflags;
 extern int _stext, _text, _etext, _edata, _end;
@@ -71,9 +83,10 @@
 char elf_platform[ELF_PLATFORM_SIZE];
 char saved_command_line[COMMAND_LINE_SIZE];
 
+static struct meminfo meminfo __initdata = { 0, };
 static struct proc_info_item proc_info;
 static const char *machine_name;
-static char command_line[COMMAND_LINE_SIZE] = { 0, };
+static char command_line[COMMAND_LINE_SIZE];
 
 static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
 static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
@@ -306,71 +319,222 @@
 		request_resource(&ioport_resource, &lp2);
 }
 
+/*
+ *  Tag parsing.
+ *
+ * This is the new way of passing data to the kernel at boot time.  Rather
+ * than passing a fixed inflexible structure to the kernel, we pass a list
+ * of variable-sized tags to the kernel.  The first tag must be a ATAG_CORE
+ * tag for the list to be recognised (to distinguish the tagged list from
+ * a param_struct).  The list is terminated with a zero-length tag (this tag
+ * is not parsed in any way).
+ */
+static int __init parse_tag_core(const struct tag *tag)
+{
+	if ((tag->u.core.flags & 1) == 0)
+		root_mountflags &= ~MS_RDONLY;
+	ROOT_DEV = to_kdev_t(tag->u.core.rootdev);
+	return 0;
+}
+
+static int __init parse_tag_mem32(const struct tag *tag)
+{
+	if (meminfo.nr_banks >= NR_BANKS) {
+		printk(KERN_WARNING
+		       "Ignoring memory bank 0x%08x size %dKB\n",
+			tag->u.mem.start, tag->u.mem.size / 1024);
+		return -EINVAL;
+	}
+	meminfo.bank[meminfo.nr_banks].start = tag->u.mem.start;
+	meminfo.bank[meminfo.nr_banks].size  = tag->u.mem.size;
+	meminfo.bank[meminfo.nr_banks].node  = 0;
+	meminfo.nr_banks += 1;
+
+	return 0;
+}
+
+static int __init parse_tag_videotext(const struct tag *tag)
+{
+	screen_info.orig_x            = tag->u.videotext.x;
+	screen_info.orig_y            = tag->u.videotext.y;
+	screen_info.orig_video_page   = tag->u.videotext.video_page;
+	screen_info.orig_video_mode   = tag->u.videotext.video_mode;
+	screen_info.orig_video_cols   = tag->u.videotext.video_cols;
+	screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx;
+	screen_info.orig_video_lines  = tag->u.videotext.video_lines;
+	screen_info.orig_video_isVGA  = tag->u.videotext.video_isvga;
+	screen_info.orig_video_points = tag->u.videotext.video_points;
+	return 0;
+}
+
+static int __init parse_tag_ramdisk(const struct tag *tag)
+{
+	setup_ramdisk((tag->u.ramdisk.flags & 1) == 0,
+		      (tag->u.ramdisk.flags & 2) == 0,
+		      tag->u.ramdisk.start, tag->u.ramdisk.size);
+	return 0;
+}
+
+static int __init parse_tag_initrd(const struct tag *tag)
+{
+	setup_initrd(tag->u.initrd.start, tag->u.initrd.size);
+	return 0;
+}
+
+static int __init parse_tag_serialnr(const struct tag *tag)
+{
+	system_serial_low = tag->u.serialnr.low;
+	system_serial_high = tag->u.serialnr.high;
+	return 0;
+}
+
+static int __init parse_tag_revision(const struct tag *tag)
+{
+	system_rev = tag->u.revision.rev;
+	return 0;
+}
+
+static int __init parse_tag_cmdline(const struct tag *tag)
+{
+	strncpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
+	default_command_line[COMMAND_LINE_SIZE - 1] = '\0';
+	return 0;
+}
+
+/*
+ * This is the core tag table; these are the tags
+ * that we recognise for any machine type.
+ */
+static const struct tagtable core_tagtable[] __init = {
+	{ ATAG_CORE,      parse_tag_core      },
+	{ ATAG_MEM,       parse_tag_mem32     },
+	{ ATAG_VIDEOTEXT, parse_tag_videotext },
+	{ ATAG_RAMDISK,   parse_tag_ramdisk   },
+	{ ATAG_INITRD,    parse_tag_initrd    },
+	{ ATAG_SERIAL,    parse_tag_serialnr  },
+	{ ATAG_REVISION,  parse_tag_revision  },
+	{ ATAG_CMDLINE,   parse_tag_cmdline   }
+};
+
+/*
+ * Scan one tag table for this tag, and call its parse function.
+ */
+static int __init
+parse_tag(const struct tagtable *tbl, int size, const struct tag *t)
+{
+	int i;
+
+	for (i = 0; i < size; i++, tbl++)
+		if (t->hdr.tag == tbl->tag) {
+			tbl->parse(t);
+			break;
+		}
+
+	return i < size;
+}
+
+/*
+ * Parse all tags in the list, checking both the global and architecture
+ * specific tag tables.
+ */
+static void __init
+parse_tags(const struct tagtable *tbl, int size, const struct tag *t)
+{
+	/*
+	 * The tag list is terminated with a zero-sized tag.  Size is
+	 * defined to be in units of 32-bit quantities.
+	 */
+	for (; t->hdr.size; t = (struct tag *)((u32 *)t + t->hdr.size)) {
+		if (parse_tag(core_tagtable, ARRAY_SIZE(core_tagtable), t))
+			continue;
+
+		if (tbl && parse_tag(tbl, size, t))
+			continue;
+
+		printk(KERN_WARNING
+		       "Ignoring unrecognised tag 0x%08x\n", t->hdr.tag);
+	}
+}
+
+static void __init parse_params(struct param_struct *params)
+{
+	if (params->u1.s.page_size != PAGE_SIZE) {
+		printk(KERN_WARNING "Warning: bad configuration page, "
+		       "trying to continue\n");
+		return;
+	}
+
+	ROOT_DEV	   = to_kdev_t(params->u1.s.rootdev);
+	system_rev	   = params->u1.s.system_rev;
+	system_serial_low  = params->u1.s.system_serial_low;
+	system_serial_high = params->u1.s.system_serial_high;
+
+	if (params->u1.s.mem_fclk_21285 > 0)
+		mem_fclk_21285 = params->u1.s.mem_fclk_21285;  
+
+	setup_ramdisk((params->u1.s.flags & FLAG_RDLOAD) == 0,
+		      (params->u1.s.flags & FLAG_RDPROMPT) == 0,
+		      params->u1.s.rd_start,
+		      params->u1.s.ramdisk_size);
+
+	setup_initrd(params->u1.s.initrd_start,
+		     params->u1.s.initrd_size);
+
+	if (!(params->u1.s.flags & FLAG_READONLY))
+		root_mountflags &= ~MS_RDONLY;
+
+	strncpy(default_command_line, params->commandline, COMMAND_LINE_SIZE);
+	default_command_line[COMMAND_LINE_SIZE - 1] = '\0';
+
+	if (meminfo.nr_banks == 0) {
+		meminfo.nr_banks      = 1;
+		meminfo.bank[0].start = PHYS_OFFSET;
+		meminfo.bank[0].size  = params->u1.s.nr_pages << PAGE_SHIFT;
+	}
+}
+
 void __init setup_arch(char **cmdline_p)
 {
 	struct param_struct *params = NULL;
 	struct machine_desc *mdesc;
-	struct meminfo meminfo;
 	char *from = default_command_line;
 
-	memset(&meminfo, 0, sizeof(meminfo));
-
-	setup_processor();
-
 	ROOT_DEV = MKDEV(0, 255);
 
+	setup_processor();
 	mdesc = setup_architecture(machine_arch_type);
 	machine_name = mdesc->name;
 
-	if (mdesc->broken_hlt)
-		disable_hlt();
-
 	if (mdesc->soft_reboot)
 		reboot_setup("s");
 
 	if (mdesc->param_offset)
 		params = phys_to_virt(mdesc->param_offset);
 
+	/*
+	 * Do the machine-specific fixups before we parse the
+	 * parameters or tags.
+	 */
 	if (mdesc->fixup)
 		mdesc->fixup(mdesc, params, &from, &meminfo);
 
-	if (params && params->u1.s.page_size != PAGE_SIZE) {
-		printk(KERN_WARNING "Warning: bad configuration page, "
-		       "trying to continue\n");
-		params = NULL;
-	}
-
 	if (params) {
-		ROOT_DEV	   = to_kdev_t(params->u1.s.rootdev);
-		system_rev	   = params->u1.s.system_rev;
-		system_serial_low  = params->u1.s.system_serial_low;
-		system_serial_high = params->u1.s.system_serial_high;
-
-		if (params->u1.s.mem_fclk_21285 > 0)
-			mem_fclk_21285 = params->u1.s.mem_fclk_21285;  
-
-		setup_ramdisk((params->u1.s.flags & FLAG_RDLOAD) == 0,
-			      (params->u1.s.flags & FLAG_RDPROMPT) == 0,
-			      params->u1.s.rd_start,
-			      params->u1.s.ramdisk_size);
-
-		setup_initrd(params->u1.s.initrd_start,
-			     params->u1.s.initrd_size);
-
-		if (!(params->u1.s.flags & FLAG_READONLY))
-			root_mountflags &= ~MS_RDONLY;
+		struct tag *tag = (struct tag *)params;
 
-		from = params->commandline;
+		/*
+		 * Is the first tag the CORE tag?  This differentiates
+		 * between the tag list and the parameter table.
+		 */
+		if (tag->hdr.tag == ATAG_CORE)
+			parse_tags(mdesc->tagtable, mdesc->tagsize, tag);
+		else
+			parse_params(params);
 	}
 
 	if (meminfo.nr_banks == 0) {
 		meminfo.nr_banks      = 1;
 		meminfo.bank[0].start = PHYS_OFFSET;
-		meminfo.bank[0].node  = 0;
-		if (params)
-			meminfo.bank[0].size = params->u1.s.nr_pages << PAGE_SHIFT;
-		else
-			meminfo.bank[0].size = MEM_SIZE;
+		meminfo.bank[0].size  = MEM_SIZE;
 	}
 
 	init_mm.start_code = (unsigned long) &_text;
@@ -385,6 +549,11 @@
 	paging_init(&meminfo, mdesc);
 	request_standard_resources(&meminfo, mdesc);
 
+	/*
+	 * Set up various architecture-specific pointers
+	 */
+	init_arch_irq = mdesc->init_irq;
+
 #ifdef CONFIG_VT
 #if defined(CONFIG_VGA_CONSOLE)
 	conswitchp = &vga_con;
@@ -403,8 +572,8 @@
 		     (int)processor_id & 15, elf_platform);
 
 	p += sprintf(p, "BogoMIPS\t: %lu.%02lu\n",
-		     (loops_per_sec+2500) / 500000,
-		     ((loops_per_sec+2500) / 5000) % 100);
+		     loops_per_jiffy / (500000/HZ),
+		     (loops_per_jiffy / (5000/HZ)) % 100);
 
 	p += sprintf(p, "Hardware\t: %s\n", machine_name);
 
diff -urN linux-2.4.4-pre2/arch/arm/kernel/time-acorn.c linux/arch/arm/kernel/time-acorn.c
--- linux-2.4.4-pre2/arch/arm/kernel/time-acorn.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/kernel/time-acorn.c	Mon Dec  3 22:34:49 2001
@@ -28,17 +28,17 @@
 	unsigned int count1, count2, status1, status2;
 	unsigned long offset = 0;
 
-	status1 = inb(IOC_IRQREQA);
+	status1 = ioc_readb(IOC_IRQREQA);
 	barrier ();
-	outb (0, IOC_T0LATCH);
+	ioc_writeb (0, IOC_T0LATCH);
 	barrier ();
-	count1 = inb(IOC_T0CNTL) | (inb(IOC_T0CNTH) << 8);
+	count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
 	barrier ();
-	status2 = inb(IOC_IRQREQA);
+	status2 = ioc_readb(IOC_IRQREQA);
 	barrier ();
-	outb (0, IOC_T0LATCH);
+	ioc_writeb (0, IOC_T0LATCH);
 	barrier ();
-	count2 = inb(IOC_T0CNTL) | (inb(IOC_T0CNTH) << 8);
+	count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
 
 	if (count2 < count1) {
 		/*
@@ -67,9 +67,9 @@
 
 void __init ioctime_init(void)
 {
-	outb(LATCH & 255, IOC_T0LTCHL);
-	outb(LATCH >> 8, IOC_T0LTCHH);
-	outb(0, IOC_T0GO);
+	ioc_writeb(LATCH & 255, IOC_T0LTCHL);
+	ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
+	ioc_writeb(0, IOC_T0GO);
 
 	gettimeoffset = ioctime_gettimeoffset;
 }
diff -urN linux-2.4.4-pre2/arch/arm/kernel/via82c505.c linux/arch/arm/kernel/via82c505.c
--- linux-2.4.4-pre2/arch/arm/kernel/via82c505.c	Mon Nov 27 17:07:59 2000
+++ linux/arch/arm/kernel/via82c505.c	Mon Dec  3 22:34:49 2001
@@ -106,8 +106,8 @@
 	    break;
 	  case PCI_BASE_ADDRESS_0:
 	    if (size_wanted) {
-	      /* 0x00900000 bytes long */
-	      *value = 0xff700000;
+	      /* 0x00900000 bytes long (0xff700000) */
+	      *value = 0xff000000;
 	      size_wanted = 0;
 	    } else {
 	      *value = FB_START;
@@ -117,7 +117,7 @@
 	    *value = 6;
 	    break;
 	  default:
-	    *value=0;
+	    *value = 0;
 	  }
 	return PCIBIOS_SUCCESSFUL;
 }
diff -urN linux-2.4.4-pre2/arch/arm/lib/Makefile linux/arch/arm/lib/Makefile
--- linux-2.4.4-pre2/arch/arm/lib/Makefile	Tue Mar  6 19:44:35 2001
+++ linux/arch/arm/lib/Makefile	Mon Dec  3 22:34:49 2001
@@ -17,8 +17,6 @@
 obj-m		:=
 obj-n		:=
 
-export-objs	:= io.o
-
 obj-arc		:= ecard.o io-acorn.o floppydma.o
 obj-rpc		:= ecard.o io-acorn.o floppydma.o
 obj-clps7500	:= io-acorn.o
@@ -47,10 +45,6 @@
 
 ifeq ($(PROCESSOR),armo)
   obj-y		+= uaccess-armo.o
-endif
-
-ifneq ($(MACHINE),ebsa110)
-  obj-y		+= io.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.4-pre2/arch/arm/lib/backtrace.S linux/arch/arm/lib/backtrace.S
--- linux-2.4.4-pre2/arch/arm/lib/backtrace.S	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/lib/backtrace.S	Mon Dec  3 22:34:49 2001
@@ -26,7 +26,7 @@
 
 ENTRY(c_backtrace)
 
-#ifndef CONFIG_FRAME_POINTER
+#ifdef CONFIG_NO_FRAME_POINTER
 		mov	pc, lr
 #else
 
diff -urN linux-2.4.4-pre2/arch/arm/lib/delay.S linux/arch/arm/lib/delay.S
--- linux-2.4.4-pre2/arch/arm/lib/delay.S	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/lib/delay.S	Mon Dec  3 22:34:49 2001
@@ -11,19 +11,26 @@
 #include <asm/assembler.h>
 		.text
 
-LC0:		.word	SYMBOL_NAME(loops_per_sec)
+LC0:		.word	SYMBOL_NAME(loops_per_jiffy)
 
+/*
+ * 0 <= r0 <= 2000
+ */
 ENTRY(udelay)
-		mov	r2, #0x1000
-		orr	r2, r2, #0x00c6
+		mov	r2,     #0x6800
+		orr	r2, r2, #0x00db
 		mul	r1, r0, r2
 		ldr	r2, LC0
 		ldr	r2, [r2]
 		mov	r1, r1, lsr #11
 		mov	r2, r2, lsr #11
 		mul	r0, r1, r2
-		movs	r0, r0, lsr #10
+		movs	r0, r0, lsr #6
 		RETINSTR(moveq,pc,lr)
+
+/*
+ * loops = (r0 * 0x10c6 * 100 * loops_per_jiffie) / 2^32
+ */
 
 @ Delay routine
 ENTRY(__delay)
diff -urN linux-2.4.4-pre2/arch/arm/lib/io-shark.c linux/arch/arm/lib/io-shark.c
--- linux-2.4.4-pre2/arch/arm/lib/io-shark.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/lib/io-shark.c	Mon Dec  3 22:34:49 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/arch/arm/lib/io-shark.c
  *
- *  by Alexander.Schulz@stud.uni-karlsruhe.de
+ *  by Alexander Schulz <aschulz@netwinder.org>
  *
  * derived from:
  * linux/arch/arm/lib/io-ebsa.S
diff -urN linux-2.4.4-pre2/arch/arm/lib/io.c linux/arch/arm/lib/io.c
--- linux-2.4.4-pre2/arch/arm/lib/io.c	Sun Feb  6 17:45:25 2000
+++ linux/arch/arm/lib/io.c	Wed Dec 31 16:00:00 1969
@@ -1,50 +0,0 @@
-#include <linux/module.h>
-#include <linux/types.h>
-
-#include <asm/io.h>
-
-/*
- * Copy data from IO memory space to "real" memory space.
- * This needs to be optimized.
- */
-void _memcpy_fromio(void * to, unsigned long from, size_t count)
-{
-	while (count) {
-		count--;
-		*(char *) to = readb(from);
-		((char *) to)++;
-		from++;
-	}
-}
-
-/*
- * Copy data from "real" memory space to IO memory space.
- * This needs to be optimized.
- */
-void _memcpy_toio(unsigned long to, const void * from, size_t count)
-{
-	while (count) {
-		count--;
-		writeb(*(char *) from, to);
-		((char *) from)++;
-		to++;
-	}
-}
-
-/*
- * "memset" on IO memory space.
- * This needs to be optimized.
- */
-void _memset_io(unsigned long dst, int c, size_t count)
-{
-	while (count) {
-		count--;
-		writeb(c, dst);
-		dst++;
-	}
-}
-
-EXPORT_SYMBOL(_memcpy_fromio);
-EXPORT_SYMBOL(_memcpy_toio);
-EXPORT_SYMBOL(_memset_io);
-
diff -urN linux-2.4.4-pre2/arch/arm/mach-ebsa110/Makefile linux/arch/arm/mach-ebsa110/Makefile
--- linux-2.4.4-pre2/arch/arm/mach-ebsa110/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-ebsa110/Makefile	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,23 @@
+#
+# Makefile for the linux kernel.
+#
+# 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).
+
+USE_STANDARD_AS_RULE := true
+
+O_TARGET		:= ebsa110.o
+
+# Object file lists.
+
+obj-y			:= arch.o io.o irq.o mm.o time.o
+obj-m			:=
+obj-n			:=
+obj-			:=
+
+export-objs		:= io.o
+
+obj-$(CONFIG_LEDS)	+= leds.o
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.4-pre2/arch/arm/mach-ebsa110/arch.c linux/arch/arm/mach-ebsa110/arch.c
--- linux-2.4.4-pre2/arch/arm/mach-ebsa110/arch.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-ebsa110/arch.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,33 @@
+/*
+ *  linux/arch/arm/mach-ebsa110/arch.c
+ *
+ *  Architecture specific fixups.  This is where any
+ *  parameters in the params struct are fixed up, or
+ *  any additional architecture specific information
+ *  is pulled from the params struct.
+ */
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+
+#include <asm/elf.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+#include <asm/hardware/dec21285.h>
+
+extern void ebsa110_map_io(void);
+extern void ebsa110_init_irq(void);
+
+MACHINE_START(EBSA110, "EBSA110")
+	MAINTAINER("Russell King")
+	BOOT_MEM(0x00000000, 0xe0000000, 0xe0000000)
+	BOOT_PARAMS(0x00000400)
+	DISABLE_PARPORT(0)
+	DISABLE_PARPORT(2)
+	SOFT_REBOOT
+	MAPIO(ebsa110_map_io)
+	INITIRQ(ebsa110_init_irq)
+MACHINE_END
diff -urN linux-2.4.4-pre2/arch/arm/mach-ebsa110/io.c linux/arch/arm/mach-ebsa110/io.c
--- linux-2.4.4-pre2/arch/arm/mach-ebsa110/io.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-ebsa110/io.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,320 @@
+/*
+ *  linux/arch/arm/mach-ebsa110/isamem.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * Perform "ISA" memory and IO accesses.  The EBSA110 has some "peculiarities"
+ * in the way it handles accesses to odd IO ports on 16-bit devices.  These
+ * devices have their D0-D15 lines connected to the processors D0-D15 lines.
+ * Since they expect all byte IO operations to be performed on D0-D7, and the
+ * StrongARM expects to transfer the byte to these odd addresses on D8-D15,
+ * we must use a trick to get the required behaviour.
+ *
+ * The trick employed here is to use long word stores to odd address -1.  The
+ * glue logic picks this up as a "trick" access, and asserts the LSB of the
+ * peripherals address bus, thereby accessing the odd IO port.  Meanwhile, the
+ * StrongARM transfers its data on D0-D7 as expected.
+ *
+ * Things get more interesting on the pass-1 EBSA110 - the PCMCIA controller
+ * wiring was screwed in such a way that it had limited memory space access.
+ * Luckily, the work-around for this is not too horrible.  See
+ * __isamem_convert_addr for the details.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/page.h>
+
+static u32 __isamem_convert_addr(void *addr)
+{
+	u32 ret, a = (u32) addr;
+
+	/*
+	 * The PCMCIA controller is wired up as follows:
+	 *        +---------+---------+---------+---------+---------+---------+
+	 * PCMCIA | 2 2 2 2 | 1 1 1 1 | 1 1 1 1 | 1 1     |         |         |
+	 *        | 3 2 1 0 | 9 8 7 6 | 5 4 3 2 | 1 0 9 8 | 7 6 5 4 | 3 2 1 0 |
+	 *        +---------+---------+---------+---------+---------+---------+
+	 *  CPU   | 2 2 2 2 | 2 1 1 1 | 1 1 1 1 | 1 1 1   |         |         |
+	 *        | 4 3 2 1 | 0 9 9 8 | 7 6 5 4 | 3 2 0 9 | 8 7 6 5 | 4 3 2 x |
+	 *        +---------+---------+---------+---------+---------+---------+
+	 *
+	 * This means that we can access PCMCIA regions as follows:
+	 *	0x*10000 -> 0x*1ffff
+	 *	0x*70000 -> 0x*7ffff
+	 *	0x*90000 -> 0x*9ffff
+	 *	0x*f0000 -> 0x*fffff
+	 */
+	ret  = (a & 0xf803fe) << 1;
+	ret |= (a & 0x03fc00) << 2;
+
+	ret += 0xe8000000;
+
+	if ((a & 0x20000) == (a & 0x40000) >> 1)
+		return ret;
+
+	BUG();
+	return 0;
+}
+
+/*
+ * read[bwl] and write[bwl]
+ */
+u8 __readb(void *addr)
+{
+	u32 ret, a = __isamem_convert_addr(addr);
+
+	if ((int)addr & 1)
+		ret = __arch_getl(a);
+	else
+		ret = __arch_getb(a);
+	return ret;
+}
+
+u16 __readw(void *addr)
+{
+	u32 a = __isamem_convert_addr(addr);
+
+	if ((int)addr & 1)
+		BUG();
+
+	return __arch_getw(a);
+}
+
+u32 __readl(void *addr)
+{
+	u32 ret, a = __isamem_convert_addr(addr);
+
+	if ((int)addr & 3)
+		BUG();
+
+	ret = __arch_getw(a);
+	ret |= __arch_getw(a + 4) << 16;
+	return ret;
+}
+
+EXPORT_SYMBOL(__readb);
+EXPORT_SYMBOL(__readw);
+EXPORT_SYMBOL(__readl);
+
+void __writeb(u8 val, void *addr)
+{
+	u32 a = __isamem_convert_addr(addr);
+
+	if ((int)addr & 1)
+		__arch_putl(val, a);
+	else
+		__arch_putb(val, a);
+}
+
+void __writew(u16 val, void *addr)
+{
+	u32 a = __isamem_convert_addr(addr);
+
+	if ((int)addr & 1)
+		BUG();
+
+	__arch_putw(val, a);
+}
+
+void __writel(u32 val, void *addr)
+{
+	u32 a = __isamem_convert_addr(addr);
+
+	if ((int)addr & 3)
+		BUG();
+
+	__arch_putw(val, a);
+	__arch_putw(val >> 16, a + 4);
+}
+
+EXPORT_SYMBOL(__writeb);
+EXPORT_SYMBOL(__writew);
+EXPORT_SYMBOL(__writel);
+
+#define SUPERIO_PORT(p) \
+	(((p) >> 3) == (0x3f8 >> 3) || \
+	 ((p) >> 3) == (0x2f8 >> 3) || \
+	 ((p) >> 3) == (0x378 >> 3))
+
+u8 __inb(int port)
+{
+	u32 ret;
+
+	/*
+	 * The SuperIO registers use sane addressing techniques...
+	 */
+	if (SUPERIO_PORT(port))
+		ret = __arch_getb(ISAIO_BASE + (port << 2));
+	else {
+		u32 a = ISAIO_BASE + ((port & ~1) << 1);
+
+		/*
+		 * Shame nothing else does
+		 */
+		if (port & 1)
+			ret = __arch_getl(a);
+		else
+			ret = __arch_getb(a);
+	}
+	return ret;
+}
+
+u16 __inw(int port)
+{
+	u32 ret;
+
+	/*
+	 * The SuperIO registers use sane addressing techniques...
+	 */
+	if (SUPERIO_PORT(port))
+		ret = __arch_getw(ISAIO_BASE + (port << 2));
+	else {
+		u32 a = ISAIO_BASE + ((port & ~1) << 1);
+
+		/*
+		 * Shame nothing else does
+		 */
+		if (port & 1)
+			BUG();
+
+		ret = __arch_getw(a);
+	}
+	return ret;
+}
+
+u32 __inl(int port)
+{
+	BUG();
+}
+
+EXPORT_SYMBOL(__inb);
+EXPORT_SYMBOL(__inw);
+EXPORT_SYMBOL(__inl);
+
+void __outb(u8 val, int port)
+{
+	/*
+	 * The SuperIO registers use sane addressing techniques...
+	 */
+	if (SUPERIO_PORT(port))
+		__arch_putb(val, ISAIO_BASE + (port << 2));
+	else {
+		u32 a = ISAIO_BASE + ((port & ~1) << 1);
+
+		/*
+		 * Shame nothing else does
+		 */
+		if (port & 1)
+			__arch_putl(val, a);
+		else
+			__arch_putb(val, a);
+	}
+}
+
+void __outw(u16 val, int port)
+{
+	u32 off;
+
+	/*
+	 * The SuperIO registers use sane addressing techniques...
+	 */
+	if (SUPERIO_PORT(port))
+		off = port << 2;
+	else {
+		off = (port & ~1) << 1;
+		if (port & 1)
+			BUG();
+
+	}
+	__arch_putw(val, ISAIO_BASE + off);
+}
+
+void __outl(u32 val, int port)
+{
+	BUG();
+}
+
+EXPORT_SYMBOL(__outb);
+EXPORT_SYMBOL(__outw);
+EXPORT_SYMBOL(__outl);
+
+extern void __arch_writesb(unsigned long virt, const void *from, int len);
+extern void __arch_writesw(unsigned long virt, const void *from, int len);
+extern void __arch_writesl(unsigned long virt, const void *from, int len);
+extern void __arch_readsb(unsigned long virt, void *from, int len);
+extern void __arch_readsw(unsigned long virt, void *from, int len);
+extern void __arch_readsl(unsigned long virt, void *from, int len);
+
+void outsb(unsigned int port, const void *from, int len)
+{
+	u32 off;
+
+	if (SUPERIO_PORT(port))
+		off = port << 2;
+	else {
+		off = (port & ~1) << 1;
+		if (port & 1)
+			BUG();
+	}
+
+	__arch_writesb(ISAIO_BASE + off, from, len);
+}
+
+void insb(unsigned int port, void *from, int len)
+{
+	u32 off;
+
+	if (SUPERIO_PORT(port))
+		off = port << 2;
+	else {
+		off = (port & ~1) << 1;
+		if (port & 1)
+			BUG();
+	}
+
+	__arch_readsb(ISAIO_BASE + off, from, len);
+}
+
+void outsw(unsigned int port, const void *from, int len)
+{
+	u32 off;
+
+	if (SUPERIO_PORT(port))
+		off = port << 2;
+	else {
+		off = (port & ~1) << 1;
+		if (port & 1)
+			BUG();
+	}
+
+	__arch_writesw(ISAIO_BASE + off, from, len);
+}
+
+void insw(unsigned int port, void *from, int len)
+{
+	u32 off;
+
+	if (SUPERIO_PORT(port))
+		off = port << 2;
+	else {
+		off = (port & ~1) << 1;
+		if (port & 1)
+			BUG();
+	}
+
+	__arch_readsw(ISAIO_BASE + off, from, len);
+}
+
+void outsl(unsigned int port, const void *from, int len)
+{
+	panic("outsl not supported on this architecture");
+}
+
+void insl(unsigned int port, void *from, int len)
+{
+	panic("insl not supported on this architecture");
+}
diff -urN linux-2.4.4-pre2/arch/arm/mach-ebsa110/irq.c linux/arch/arm/mach-ebsa110/irq.c
--- linux-2.4.4-pre2/arch/arm/mach-ebsa110/irq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-ebsa110/irq.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,55 @@
+/*
+ *  linux/arch/arm/mach-ebsa110/irq.c
+ *
+ *  Copyright (C) 1996-1998 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Changelog:
+ *   22-08-1998	RMK	Restructured IRQ routines
+ */
+#include <linux/init.h>
+
+#include <asm/mach/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "hardware.h"
+
+static void ebsa110_mask_irq(unsigned int irq)
+{
+	__raw_writeb(1 << irq, IRQ_MCLR);
+}
+
+static void ebsa110_unmask_irq(unsigned int irq)
+{
+	__raw_writeb(1 << irq, IRQ_MSET);
+}
+ 
+void __init ebsa110_init_irq(void)
+{
+	unsigned long flags;
+	int irq;
+
+	save_flags_cli (flags);
+	__raw_writeb(0xff, IRQ_MCLR);
+	__raw_writeb(0x55, IRQ_MSET);
+	__raw_writeb(0x00, IRQ_MSET);
+	if (__raw_readb(IRQ_MASK) != 0x55)
+		while (1);
+	__raw_writeb(0xff, IRQ_MCLR);	/* clear all interrupt enables */
+	restore_flags (flags);
+
+	for (irq = 0; irq < NR_IRQS; irq++) {
+		irq_desc[irq].valid	= 1;
+		irq_desc[irq].probe_ok	= 1;
+		irq_desc[irq].mask_ack	= ebsa110_mask_irq;
+		irq_desc[irq].mask	= ebsa110_mask_irq;
+		irq_desc[irq].unmask	= ebsa110_unmask_irq;
+	}
+}
+
diff -urN linux-2.4.4-pre2/arch/arm/mach-ebsa110/leds.c linux/arch/arm/mach-ebsa110/leds.c
--- linux-2.4.4-pre2/arch/arm/mach-ebsa110/leds.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-ebsa110/leds.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,51 @@
+/*
+ *  linux/arch/arm/mach-ebsa110/leds.c
+ *
+ *  Copyright (C) 1998 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  EBSA-110 LED control routines.  We use the led as follows:
+ *
+ *   - Red - toggles state every 50 timer interrupts
+ */
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+static spinlock_t leds_lock;
+
+static void ebsa110_leds_event(led_event_t ledevt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+
+	switch(ledevt) {
+	case led_timer:
+		*(volatile unsigned char *)SOFT_BASE ^= 128;
+		break;
+
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&leds_lock, flags);
+}
+
+static int __init leds_init(void)
+{
+	if (machine_is_ebsa110())
+		leds_event = ebsa110_leds_event;
+
+	return 0;
+}
+
+__initcall(leds_init);
diff -urN linux-2.4.4-pre2/arch/arm/mach-ebsa110/mm.c linux/arch/arm/mach-ebsa110/mm.c
--- linux-2.4.4-pre2/arch/arm/mach-ebsa110/mm.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-ebsa110/mm.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,43 @@
+/*
+ *  linux/arch/arm/mach-ebsa110/mm.c
+ *
+ *  Copyright (C) 1998-1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Extra MM routines for the EBSA-110 architecture
+ */
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+
+#include <asm/mach/map.h>
+
+#include "hardware.h"
+ 
+static struct map_desc ebsa110_io_desc[] __initdata = {
+	/*
+	 * sparse external-decode ISAIO space
+	 */
+	{ IRQ_STAT,    TRICK4_PHYS, PGDIR_SIZE,  DOMAIN_IO, 0, 1, 0, 0 }, /* IRQ_STAT/IRQ_MCLR */
+	{ IRQ_MASK,    TRICK3_PHYS, PGDIR_SIZE,  DOMAIN_IO, 0, 1, 0, 0 }, /* IRQ_MASK/IRQ_MSET */
+	{ SOFT_BASE,   TRICK1_PHYS, PGDIR_SIZE,  DOMAIN_IO, 0, 1, 0, 0 }, /* SOFT_BASE */
+	{ PIT_BASE,    TRICK0_PHYS, PGDIR_SIZE,  DOMAIN_IO, 0, 1, 0, 0 }, /* PIT_BASE */
+
+	/*
+	 * self-decode ISAIO space
+	 */
+	{ ISAIO_BASE,  ISAIO_PHYS,  ISAIO_SIZE,  DOMAIN_IO, 0, 1, 0, 0 },
+	{ ISAMEM_BASE, ISAMEM_PHYS, ISAMEM_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
+	LAST_DESC
+};
+
+void __init ebsa110_map_io(void)
+{
+	iotable_init(ebsa110_io_desc);
+}
diff -urN linux-2.4.4-pre2/arch/arm/mach-ebsa110/time.c linux/arch/arm/mach-ebsa110/time.c
--- linux-2.4.4-pre2/arch/arm/mach-ebsa110/time.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-ebsa110/time.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,113 @@
+/*
+ *  linux/arch/arm/mach-ebsa110/time.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+#define PIT_CTRL		(PIT_BASE + 0x0d)
+#define PIT_T2			(PIT_BASE + 0x09)
+#define PIT_T1			(PIT_BASE + 0x05)
+#define PIT_T0			(PIT_BASE + 0x01)
+
+/*
+ * This is the rate at which your MCLK signal toggles (in Hz)
+ * This was measured on a 10 digit frequency counter sampling
+ * over 1 second.
+ */
+#define MCLK	47894000
+
+/*
+ * This is the rate at which the PIT timers get clocked
+ */
+#define CLKBY7	(MCLK / 7)
+
+/*
+ * If CLKBY7 is larger than this, then we must do software
+ * division of the timer interrupt.
+ */
+#if CLKBY7 > 6553500
+#define DIVISOR	2
+#else
+#define DIVISOR 1
+#endif
+
+/*
+ * This is the counter value
+ */
+#define COUNT	((CLKBY7 + (DIVISOR * HZ / 2)) / (DIVISOR * HZ))
+
+extern unsigned long (*gettimeoffset)(void);
+
+static unsigned long divisor;
+
+/*
+ * Get the time offset from the system PIT.  Note that if we have missed an
+ * interrupt, then the PIT counter will roll over (ie, be negative).
+ * This actually works out to be convenient.
+ */
+static unsigned long ebsa110_gettimeoffset(void)
+{
+	unsigned long offset, count;
+
+	__raw_writeb(0x40, PIT_CTRL);
+	count = __raw_readb(PIT_T1);
+	count |= __raw_readb(PIT_T1) << 8;
+
+	/*
+	 * If count > COUNT, make the number negative.
+	 */
+	if (count > COUNT)
+		count |= 0xffff0000;
+
+	offset = COUNT * (DIVISOR - divisor);
+	offset -= count;
+
+	/*
+	 * `offset' is in units of timer counts.  Convert
+	 * offset to units of microseconds.
+	 */
+	offset = offset * (1000000 / HZ) / (COUNT * DIVISOR);
+
+	return offset;
+}
+
+int ebsa110_reset_timer(void)
+{
+	u32 count;
+
+	/* latch and read timer 1 */
+	__raw_writeb(0x40, PIT_CTRL);
+	count = __raw_readb(PIT_T1);
+	count |= __raw_readb(PIT_T1) << 8;
+
+	count += COUNT;
+
+	__raw_writeb(count & 0xff, PIT_T1);
+	__raw_writeb(count >> 8, PIT_T1);
+
+	if (divisor == 0)
+		divisor = DIVISOR;
+	divisor -= 1;
+	return divisor;
+}
+
+void __init ebsa110_setup_timer(void)
+{
+	/*
+	 * Timer 1, mode 2, LSB/MSB
+	 */
+	__raw_writeb(0x70, PIT_CTRL);
+	__raw_writeb(COUNT & 0xff, PIT_T1);
+	__raw_writeb(COUNT >> 8, PIT_T1);
+	divisor = DIVISOR - 1;
+
+	gettimeoffset = ebsa110_gettimeoffset;
+}
diff -urN linux-2.4.4-pre2/arch/arm/mach-footbridge/Makefile linux/arch/arm/mach-footbridge/Makefile
--- linux-2.4.4-pre2/arch/arm/mach-footbridge/Makefile	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mach-footbridge/Makefile	Mon Dec  3 22:34:49 2001
@@ -5,42 +5,32 @@
 # removes any old dependencies. DON'T put your own dependencies here
 # unless it's something special (ie not a .c file).
 
+USE_STANDARD_AS_RULE := true
+
 O_TARGET		:= footbridge.o
 
 # Object file lists.
 
-obj-y			:= arch.o #dma.o mm.o
+obj-y			:= arch.o irq.o mm.o #dma.o
 obj-m			:=
 obj-n			:=
 obj-			:=
 
 export-objs		:= netwinder-hw.o
 
-ifeq ($(CONFIG_PCI),y)
-obj-$(CONFIG_ARCH_CATS) += cats-pci.o
-obj-$(CONFIG_ARCH_EBSA285) += ebsa285-pci.o
-obj-$(CONFIG_ARCH_NETWINDER) += netwinder-pci.o
-obj-$(CONFIG_ARCH_PERSONAL_SERVER) += personal-pci.o
-endif
-
-ifeq ($(CONFIG_LEDS),y)
-obj-$(CONFIG_ARCH_CO285) += ebsa285-leds.o
-obj-$(CONFIG_ARCH_EBSA285) += ebsa285-leds.o
-obj-$(CONFIG_ARCH_NETWINDER) += netwinder-leds.o
-endif
+pci-$(CONFIG_ARCH_CATS) += cats-pci.o
+pci-$(CONFIG_ARCH_EBSA285) += ebsa285-pci.o
+pci-$(CONFIG_ARCH_NETWINDER) += netwinder-pci.o
+pci-$(CONFIG_ARCH_PERSONAL_SERVER) += personal-pci.o
+
+leds-$(CONFIG_ARCH_CO285) += ebsa285-leds.o
+leds-$(CONFIG_ARCH_EBSA285) += ebsa285-leds.o
+leds-$(CONFIG_ARCH_NETWINDER) += netwinder-leds.o
 
 obj-$(CONFIG_ARCH_CATS) += cats-hw.o
 obj-$(CONFIG_ARCH_NETWINDER) += netwinder-hw.o
 
-# Files that are both resident and modular; remove from modular.
-
-obj-m		:= $(filter-out $(obj-y), $(obj-m))
-
-# Translate to Rules.make lists.
-
-O_OBJS		:= $(filter-out $(export-objs), $(obj-y))
-OX_OBJS 	:= $(filter	$(export-objs), $(obj-y))
-M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
-MX_OBJS 	:= $(sort $(filter     $(export-objs), $(obj-m)))
+obj-$(CONFIG_PCI)	+=$(pci-y)
+obj-$(CONFIG_LEDS)	+=$(leds-y)
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.4-pre2/arch/arm/mach-footbridge/arch.c linux/arch/arm/mach-footbridge/arch.c
--- linux-2.4.4-pre2/arch/arm/mach-footbridge/arch.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mach-footbridge/arch.c	Mon Dec  3 22:34:49 2001
@@ -19,9 +19,8 @@
 
 #include <asm/mach/arch.h>
 
-extern void setup_initrd(unsigned int start, unsigned int size);
-extern void setup_ramdisk(int doload, int prompt, int start, unsigned int rd_sz);
-extern void __init footbridge_map_io(void);
+extern void footbridge_map_io(void);
+extern void footbridge_init_irq(void);
 
 #ifdef CONFIG_ARCH_EBSA285
 
@@ -42,6 +41,7 @@
 	VIDEO(0x000a0000, 0x000bffff)
 	FIXUP(fixup_ebsa285)
 	MAPIO(footbridge_map_io)
+	INITIRQ(footbridge_init_irq)
 MACHINE_END
 #endif
 
@@ -91,6 +91,7 @@
 	DISABLE_PARPORT(2)
 	FIXUP(fixup_netwinder)
 	MAPIO(footbridge_map_io)
+	INITIRQ(footbridge_init_irq)
 MACHINE_END
 #endif
 
@@ -111,9 +112,11 @@
 MACHINE_START(CATS, "Chalice-CATS")
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
+	BOOT_PARAMS(0x00000100)
 	SOFT_REBOOT
 	FIXUP(fixup_cats)
 	MAPIO(footbridge_map_io)
+	INITIRQ(footbridge_init_irq)
 MACHINE_END
 #endif
 
@@ -139,6 +142,7 @@
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0x7cf00000)
 	FIXUP(fixup_coebsa285)
 	MAPIO(footbridge_map_io)
+	INITIRQ(footbridge_init_irq)
 MACHINE_END
 #endif
 
@@ -148,5 +152,6 @@
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
 	BOOT_PARAMS(0x00000100)
 	MAPIO(footbridge_map_io)
+	INITIRQ(footbridge_init_irq)
 MACHINE_END
 #endif
diff -urN linux-2.4.4-pre2/arch/arm/mach-footbridge/irq.c linux/arch/arm/mach-footbridge/irq.c
--- linux-2.4.4-pre2/arch/arm/mach-footbridge/irq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-footbridge/irq.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,223 @@
+/*
+ *  linux/arch/arm/mach-footbridge/irq.c
+ *
+ *  Copyright (C) 1996-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Changelog:
+ *   22-Aug-1998 RMK	Restructured IRQ routines
+ *   03-Sep-1998 PJB	Merged CATS support
+ *   20-Jan-1998 RMK	Started merge of EBSA286, CATS and NetWinder
+ *   26-Jan-1999 PJB	Don't use IACK on CATS
+ *   16-Mar-1999 RMK	Added autodetect of ISA PICs
+ */
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/mach/irq.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/dec21285.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+/*
+ * Footbridge IRQ translation table
+ *  Converts from our IRQ numbers into FootBridge masks
+ */
+static const int fb_irq_mask[] = {
+	IRQ_MASK_UART_RX,	/*  0 */
+	IRQ_MASK_UART_TX,	/*  1 */
+	IRQ_MASK_TIMER1,	/*  2 */
+	IRQ_MASK_TIMER2,	/*  3 */
+	IRQ_MASK_TIMER3,	/*  4 */
+	IRQ_MASK_IN0,		/*  5 */
+	IRQ_MASK_IN1,		/*  6 */
+	IRQ_MASK_IN2,		/*  7 */
+	IRQ_MASK_IN3,		/*  8 */
+	IRQ_MASK_DOORBELLHOST,	/*  9 */
+	IRQ_MASK_DMA1,		/* 10 */
+	IRQ_MASK_DMA2,		/* 11 */
+	IRQ_MASK_PCI,		/* 12 */
+	IRQ_MASK_SDRAMPARITY,	/* 13 */
+	IRQ_MASK_I2OINPOST,	/* 14 */
+	IRQ_MASK_PCI_ABORT,	/* 15 */
+	IRQ_MASK_PCI_SERR,	/* 16 */
+	IRQ_MASK_DISCARD_TIMER,	/* 17 */
+	IRQ_MASK_PCI_DPERR,	/* 18 */
+	IRQ_MASK_PCI_PERR,	/* 19 */
+};
+
+static void fb_mask_irq(unsigned int irq)
+{
+	*CSR_IRQ_DISABLE = fb_irq_mask[_DC21285_INR(irq)];
+}
+
+static void fb_unmask_irq(unsigned int irq)
+{
+	*CSR_IRQ_ENABLE = fb_irq_mask[_DC21285_INR(irq)];
+}
+
+static void __init __fb_init_irq(void)
+{
+	int irq;
+
+	/*
+	 * setup DC21285 IRQs
+	 */
+	*CSR_IRQ_DISABLE = -1;
+	*CSR_FIQ_DISABLE = -1;
+
+	for (irq = _DC21285_IRQ(0); irq < _DC21285_IRQ(20); irq++) {
+		irq_desc[irq].valid	= 1;
+		irq_desc[irq].probe_ok	= 1;
+		irq_desc[irq].mask_ack	= fb_mask_irq;
+		irq_desc[irq].mask	= fb_mask_irq;
+		irq_desc[irq].unmask	= fb_unmask_irq;
+	}
+}
+
+extern int isa_irq;
+
+static void isa_mask_pic_lo_irq(unsigned int irq)
+{
+	unsigned int mask = 1 << (irq & 7);
+
+	outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO);
+}
+
+static void isa_mask_ack_pic_lo_irq(unsigned int irq)
+{
+	unsigned int mask = 1 << (irq & 7);
+
+	outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO);
+	outb(0x20, PIC_LO);
+}
+
+static void isa_unmask_pic_lo_irq(unsigned int irq)
+{
+	unsigned int mask = 1 << (irq & 7);
+
+	outb(inb(PIC_MASK_LO) & ~mask, PIC_MASK_LO);
+}
+
+static void isa_mask_pic_hi_irq(unsigned int irq)
+{
+	unsigned int mask = 1 << (irq & 7);
+
+	outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI);
+}
+
+static void isa_mask_ack_pic_hi_irq(unsigned int irq)
+{
+	unsigned int mask = 1 << (irq & 7);
+
+	outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI);
+	outb(0x62, PIC_LO);
+	outb(0x20, PIC_HI);
+}
+
+static void isa_unmask_pic_hi_irq(unsigned int irq)
+{
+	unsigned int mask = 1 << (irq & 7);
+
+	outb(inb(PIC_MASK_HI) & ~mask, PIC_MASK_HI);
+}
+
+static void no_action(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+static struct irqaction irq_cascade = { handler: no_action, name: "cascade", };
+static struct resource pic1_resource = { "pic1", 0x20, 0x3f };
+static struct resource pic2_resource = { "pic2", 0xa0, 0xbf };
+
+static void __init isa_init_irq(int irq)
+{
+	/*
+	 * Setup, and then probe for an ISA PIC
+	 * If the PIC is not there, then we
+	 * ignore the PIC.
+	 */
+	outb(0x11, PIC_LO);
+	outb(_ISA_IRQ(0), PIC_MASK_LO);	/* IRQ number		*/
+	outb(0x04, PIC_MASK_LO);	/* Slave on Ch2		*/
+	outb(0x01, PIC_MASK_LO);	/* x86			*/
+	outb(0xf5, PIC_MASK_LO);	/* pattern: 11110101	*/
+
+	outb(0x11, PIC_HI);
+	outb(_ISA_IRQ(8), PIC_MASK_HI);	/* IRQ number		*/
+	outb(0x02, PIC_MASK_HI);	/* Slave on Ch1		*/
+	outb(0x01, PIC_MASK_HI);	/* x86			*/
+	outb(0xfa, PIC_MASK_HI);	/* pattern: 11111010	*/
+
+	outb(0x0b, PIC_LO);
+	outb(0x0b, PIC_HI);
+
+	if (inb(PIC_MASK_LO) == 0xf5 && inb(PIC_MASK_HI) == 0xfa) {
+		outb(0xff, PIC_MASK_LO);/* mask all IRQs	*/
+		outb(0xff, PIC_MASK_HI);/* mask all IRQs	*/
+		isa_irq = irq;
+	} else
+		isa_irq = -1;
+
+	if (isa_irq != -1) {
+		for (irq = _ISA_IRQ(0); irq < _ISA_IRQ(8); irq++) {
+			irq_desc[irq].valid	= 1;
+			irq_desc[irq].probe_ok	= 1;
+			irq_desc[irq].mask_ack	= isa_mask_ack_pic_lo_irq;
+			irq_desc[irq].mask	= isa_mask_pic_lo_irq;
+			irq_desc[irq].unmask	= isa_unmask_pic_lo_irq;
+		}
+
+		for (irq = _ISA_IRQ(8); irq < _ISA_IRQ(16); irq++) {
+			irq_desc[irq].valid	= 1;
+			irq_desc[irq].probe_ok	= 1;
+			irq_desc[irq].mask_ack	= isa_mask_ack_pic_hi_irq;
+			irq_desc[irq].mask	= isa_mask_pic_hi_irq;
+			irq_desc[irq].unmask	= isa_unmask_pic_hi_irq;
+		}
+
+		request_resource(&ioport_resource, &pic1_resource);
+		request_resource(&ioport_resource, &pic2_resource);
+		setup_arm_irq(IRQ_ISA_CASCADE, &irq_cascade);
+		setup_arm_irq(isa_irq, &irq_cascade);
+
+		/*
+		 * On the NetWinder, don't automatically
+		 * enable ISA IRQ11 when it is requested.
+		 * There appears to be a missing pull-up
+		 * resistor on this line.
+		 */
+		if (machine_is_netwinder())
+			irq_desc[_ISA_IRQ(11)].noautoenable = 1;
+	}
+}
+
+void __init footbridge_init_irq(void)
+{
+	__fb_init_irq();
+
+	if (!footbridge_cfn_mode())
+		return;
+
+	if (machine_is_ebsa285())
+		/* The following is dependent on which slot
+		 * you plug the Southbridge card into.  We
+		 * currently assume that you plug it into
+		 * the right-hand most slot.
+		 */
+		isa_init_irq(IRQ_PCI);
+
+	if (machine_is_cats())
+		isa_init_irq(IRQ_IN2);
+
+	if (machine_is_netwinder())
+		isa_init_irq(IRQ_IN3);
+}
diff -urN linux-2.4.4-pre2/arch/arm/mach-footbridge/mm.c linux/arch/arm/mach-footbridge/mm.c
--- linux-2.4.4-pre2/arch/arm/mach-footbridge/mm.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-footbridge/mm.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,117 @@
+/*
+ *  linux/arch/arm/mach-footbridge/mm.c
+ *
+ *  Copyright (C) 1998-2000 Russell King, Dave Gilbert.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Extra MM routines for the EBSA285 architecture
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+ 
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/io.h>
+#include <asm/hardware/dec21285.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/map.h>
+
+/*
+ * Common mapping for all systems.  Note that the outbound write flush is
+ * commented out since there is a "No Fix" problem with it.  Not mapping
+ * it means that we have extra bullet protection on our feet.
+ */
+static struct map_desc fb_common_io_desc[] __initdata = {
+ { ARMCSR_BASE,	 DC21285_ARMCSR_BASE,	    ARMCSR_SIZE,  DOMAIN_IO, 0, 1, 0, 0 },
+ { XBUS_BASE,    0x40000000,		    XBUS_SIZE,    DOMAIN_IO, 0, 1, 0, 0 },
+ LAST_DESC
+};
+
+/*
+ * The mapping when the footbridge is in host mode.  We don't map any of
+ * this when we are in add-in mode.
+ */
+static struct map_desc ebsa285_host_io_desc[] __initdata = {
+#if defined(CONFIG_ARCH_FOOTBRIDGE) && defined(CONFIG_FOOTBRIDGE_HOST)
+ { PCIMEM_BASE,  DC21285_PCI_MEM,	    PCIMEM_SIZE,  DOMAIN_IO, 0, 1, 0, 0 },
+ { PCICFG0_BASE, DC21285_PCI_TYPE_0_CONFIG, PCICFG0_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
+ { PCICFG1_BASE, DC21285_PCI_TYPE_1_CONFIG, PCICFG1_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
+ { PCIIACK_BASE, DC21285_PCI_IACK,	    PCIIACK_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
+ { PCIO_BASE,    DC21285_PCI_IO,	    PCIO_SIZE,	  DOMAIN_IO, 0, 1, 0, 0 },
+#endif
+ LAST_DESC
+};
+
+/*
+ * The CO-ebsa285 mapping.
+ */
+static struct map_desc co285_io_desc[] __initdata = {
+#ifdef CONFIG_ARCH_CO285
+ { PCIO_BASE,	 DC21285_PCI_IO,	    PCIO_SIZE,    DOMAIN_IO, 0, 1, 0, 0 },
+ { PCIMEM_BASE,	 DC21285_PCI_MEM,	    PCIMEM_SIZE,  DOMAIN_IO, 0, 1, 0, 0 },
+#endif
+ LAST_DESC
+};
+
+void __init footbridge_map_io(void)
+{
+	struct map_desc *desc = NULL;
+
+	/*
+	 * Set up the common mapping first; we need this to
+	 * determine whether we're in host mode or not.
+	 */
+	iotable_init(fb_common_io_desc);
+
+	/*
+	 * Now, work out what we've got to map in addition on this
+	 * platform.
+	 */
+	if (machine_is_co285())
+		desc = co285_io_desc;
+	else if (footbridge_cfn_mode())
+		desc = ebsa285_host_io_desc;
+
+	if (desc)
+		iotable_init(desc);
+}
+
+#ifdef CONFIG_FOOTBRIDGE_ADDIN
+
+/*
+ * These two functions convert virtual addresses to PCI addresses and PCI
+ * addresses to virtual addresses.  Note that it is only legal to use these
+ * on memory obtained via get_free_page or kmalloc.
+ */
+unsigned long __virt_to_bus(unsigned long res)
+{
+#ifdef CONFIG_DEBUG_ERRORS
+	if (res < PAGE_OFFSET || res >= (unsigned long)high_memory) {
+		printk("__virt_to_bus: invalid virtual address 0x%08lx\n", res);
+		__backtrace();
+	}
+#endif
+	return (res - PAGE_OFFSET) + (*CSR_PCISDRAMBASE & 0xfffffff0);
+}
+
+unsigned long __bus_to_virt(unsigned long res)
+{
+	res -= (*CSR_PCISDRAMBASE & 0xfffffff0);
+	res += PAGE_OFFSET;
+
+#ifdef CONFIG_DEBUG_ERRORS
+	if (res < PAGE_OFFSET || res >= (unsigned long)high_memory) {
+		printk("__bus_to_virt: invalid virtual address 0x%08lx\n", res);
+		__backtrace();
+	}
+#endif
+	return res;
+}
+
+#endif
diff -urN linux-2.4.4-pre2/arch/arm/mach-sa1100/Makefile linux/arch/arm/mach-sa1100/Makefile
--- linux-2.4.4-pre2/arch/arm/mach-sa1100/Makefile	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/mach-sa1100/Makefile	Mon Dec  3 22:34:49 2001
@@ -11,13 +11,14 @@
 
 # Object file lists.
 
-obj-y			:= arch.o hw.o #dma.o mm.o
+obj-y			:= arch.o hw.o dma-sa1100.o # mm.o
 obj-m			:=
 obj-n			:=
 obj-			:=
 
-export-objs		:= hw.o leds.o
+export-objs		:= dma-sa1100.o dma-sa1111.o hw.o leds.o
 
 obj-$(CONFIG_LEDS)	+= leds.o
+obj-$(CONFIG_SA1111)	+= dma-sa1111.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.4-pre2/arch/arm/mach-sa1100/arch.c linux/arch/arm/mach-sa1100/arch.c
--- linux-2.4.4-pre2/arch/arm/mach-sa1100/arch.c	Fri Mar  2 11:12:06 2001
+++ linux/arch/arm/mach-sa1100/arch.c	Mon Dec  3 22:34:49 2001
@@ -19,8 +19,23 @@
 
 #include <asm/mach/arch.h>
 
-extern void setup_initrd(unsigned int start, unsigned int size);
-extern void setup_ramdisk(int doload, int prompt, int start, unsigned int rd_sz);
+extern void genarch_init_irq(void);
+
+
+static void sa1100_power_off(void)
+{
+	mdelay(100);
+	cli();
+	/* disable internal oscillator, float CS lines */
+	PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS);
+	/* set lowest clock */
+	PPCR = 0;
+	/* set all GPIOs to input mode */
+	GPDR = 0;
+	/* enter sleep mode */
+	PMCR = PMCR_SF;
+}
+
 
 static void victor_power_off(void)
 {
@@ -47,10 +62,13 @@
 	mi->bank[__nr].start = (__start), \
 	mi->bank[__nr].size = (__size), \
 	mi->bank[__nr].node = (((unsigned)(__start) - PHYS_OFFSET) >> 27)
+
 static void __init
 fixup_sa1100(struct machine_desc *desc, struct param_struct *params,
 	     char **cmdline, struct meminfo *mi)
 {
+	pm_power_off = sa1100_power_off;
+
 	if (machine_is_assabet()) {
 		/* 
 		 * On Assabet, we must probe for the Neponset board *before*
@@ -82,6 +100,16 @@
 		setup_initrd( 0xc0800000, 3*1024*1024 );
 	}
 
+	else if (machine_is_pangolin()) {
+		SET_BANK( 0, 0xc0000000, 32*1024*1024 );
+		SET_BANK( 1, 0xc8000000, 32*1024*1024 );
+		mi->nr_banks = 2;
+		
+		ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
+		setup_ramdisk( 1, 0, 0, 16384 );
+		setup_initrd( 0xc0800000, 9*1024*1024 );
+	}
+
 	else if (machine_is_brutus()) {
 		SET_BANK( 0, 0xc0000000, 4*1024*1024 );
 		SET_BANK( 1, 0xc8000000, 4*1024*1024 );
@@ -133,7 +161,7 @@
 		setup_initrd(0xc0400000, 4*1024*1024);
 	}
 
-	else if (machine_is_thinclient() || machine_is_graphicsclient()) {
+	else if (machine_is_graphicsclient()) {
 		SET_BANK( 0, 0xc0000000, 16*1024*1024 );
 		mi->nr_banks = 1;
 
@@ -154,15 +182,6 @@
 		if( *((char*)0xc0000100) )
 			*cmdline = ((char *)0xc0000100);
 	}
-	else if (machine_is_tifon()) {
-		SET_BANK( 0, 0xc0000000, 16*1024*1024 );
-		SET_BANK( 1, 0xc8000000, 16*1024*1024 );
-		mi->nr_banks = 2;
-
-		ROOT_DEV = MKDEV(UNNAMED_MAJOR, 0);
-		setup_ramdisk(1, 0, 0, 4096);
-		setup_initrd( 0xd0000000 + 0x1100004, 0x140000 );
-	}
 
 	else if (machine_is_victor()) {
 		SET_BANK( 0, 0xc0000000, 4*1024*1024 );
@@ -180,6 +199,16 @@
 		pm_power_off = victor_power_off;
 	}
 
+        else if (machine_is_sherman()) {
+                SET_BANK( 0, 0xc0000000, 64*1024*1024 );
+                SET_BANK( 1, 0xc8000000, 64*1024*1024 );
+                mi->nr_banks = 2;
+
+                ROOT_DEV = MKDEV( 60, 2 );
+                setup_ramdisk( 1, 0, 0, 8192 );
+//              setup_initrd( 0xc0400000, 8*1024*1024 );
+}
+
 	else if (machine_is_xp860()) {
 		SET_BANK( 0, 0xc0000000, 32*1024*1024 );
 		mi->nr_banks = 1;
@@ -193,6 +222,15 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
+MACHINE_END
+#endif
+#ifdef CONFIG_SA1100_PANGOLIN
+MACHINE_START(PANGOLIN, "Dialogue-Pangolin")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_BITSY
@@ -201,6 +239,7 @@
 	BOOT_PARAMS(0xc0000100)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_BRUTUS
@@ -208,6 +247,7 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_CERF
@@ -216,6 +256,7 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_EMPEG
@@ -223,6 +264,7 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
@@ -230,14 +272,16 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_ITSY
 MACHINE_START(ITSY, "Compaq Itsy")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
-	BOOT_PARAMS(0xc0000100
+	BOOT_PARAMS(0xc0000100)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_LART
@@ -245,6 +289,7 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_NANOENGINE
@@ -252,6 +297,7 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_PLEB
@@ -259,20 +305,7 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
-MACHINE_END
-#endif
-#ifdef CONFIG_SA1100_THINCLIENT
-MACHINE_START(THINCLIENT, "ADS ThinClient")
-	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
-	FIXUP(fixup_sa1100)
-	MAPIO(sa1100_map_io)
-MACHINE_END
-#endif
-#ifdef CONFIG_SA1100_TIFON
-MACHINE_START(TIFON, "Tifon")
-	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
-	FIXUP(fixup_sa1100)
-	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_VICTOR
@@ -280,6 +313,15 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
+MACHINE_END
+#endif
+#ifdef CONFIG_SA1100_SHERMAN
+MACHINE_START(SHERMAN, "Blazie Engineering Sherman")
+        BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+        FIXUP(fixup_sa1100)
+        MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_XP860
@@ -287,5 +329,6 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
 	MAPIO(sa1100_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
 #endif
diff -urN linux-2.4.4-pre2/arch/arm/mach-sa1100/dma-sa1100.c linux/arch/arm/mach-sa1100/dma-sa1100.c
--- linux-2.4.4-pre2/arch/arm/mach-sa1100/dma-sa1100.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-sa1100/dma-sa1100.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,538 @@
+/*
+ * arch/arm/kernel/dma-sa1100.c
+ *
+ * Support functions for the SA11x0 internal DMA channels.
+ * (see also Documentation/arm/SA1100/DMA)
+ *
+ * Copyright (C) 2000 Nicolas Pitre
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTK( s, arg... )  printk( "dma<%s>: " s, dma->device_id , ##arg )
+#else
+#define DPRINTK( x... )
+#endif
+
+
+/*
+ * Maximum physical DMA buffer size
+ */
+#define MAX_DMA_SIZE		0x1fff
+#define MAX_DMA_ORDER		12
+
+
+/*
+ * DMA control register structure
+ */
+typedef struct {
+	volatile u_long DDAR;
+	volatile u_long SetDCSR;
+	volatile u_long ClrDCSR;
+	volatile u_long RdDCSR;
+	volatile dma_addr_t DBSA;
+	volatile u_long DBTA;
+	volatile dma_addr_t DBSB;
+	volatile u_long DBTB;
+} dma_regs_t;
+
+
+/*
+ * DMA buffer structure
+ */
+struct dma_buf_s {
+	int size;		/* buffer size */
+	dma_addr_t dma_start;	/* starting DMA address */
+	dma_addr_t dma_ptr;	/* current DMA pointer position */
+	int ref;		/* number of DMA references */
+	void *id;		/* to identify buffer from outside */
+	struct dma_buf_s *next;	/* next buffer to process */
+};
+
+
+#include "dma.h"
+
+sa1100_dma_t dma_chan[MAX_SA1100_DMA_CHANNELS];
+
+
+/*
+ * DMA processing...
+ */
+
+static inline int start_sa1100_dma(sa1100_dma_t * dma, dma_addr_t dma_ptr, int size)
+{
+	dma_regs_t *regs = dma->regs;
+	int status;
+	int use_bufa;
+
+	status = regs->RdDCSR;
+
+	/* If both DMA buffers are started, there's nothing else we can do. */
+	if ((status & DCSR_STRTA) && (status & DCSR_STRTB)) {
+		DPRINTK("start: st %#x busy\n", status);
+		return -EBUSY;
+	}
+
+	use_bufa = (((status & DCSR_BIU) && (status & DCSR_STRTB)) ||
+		    (!(status & DCSR_BIU) && !(status & DCSR_STRTA)));
+	if (use_bufa) {
+		regs->ClrDCSR = DCSR_DONEA | DCSR_STRTA;
+		regs->DBSA = dma_ptr;
+		regs->DBTA = size;
+		regs->SetDCSR = DCSR_STRTA | DCSR_IE | DCSR_RUN;
+		DPRINTK("start a=%#x s=%d on A\n", dma_ptr, size);
+	} else {
+		regs->ClrDCSR = DCSR_DONEB | DCSR_STRTB;
+		regs->DBSB = dma_ptr;
+		regs->DBTB = size;
+		regs->SetDCSR = DCSR_STRTB | DCSR_IE | DCSR_RUN;
+		DPRINTK("start a=%#x s=%d on B\n", dma_ptr, size);
+	}
+
+	return 0;
+}
+
+
+static int start_dma(sa1100_dma_t *dma, dma_addr_t dma_ptr, int size)
+{
+	if (channel_is_sa1111_sac(dma - dma_chan))
+		return start_sa1111_sac_dma(dma, dma_ptr, size);
+	return start_sa1100_dma(dma, dma_ptr, size);
+}
+
+
+/* This must be called with IRQ disabled */
+static void process_dma(sa1100_dma_t * dma)
+{
+	dma_buf_t *buf;
+	int chunksize;
+
+	for (;;) {
+		buf = dma->tail;
+
+		if (!buf) {
+			/* no more data available */
+			DPRINTK("process: no more buf (dma %s)\n",
+				dma->curr ? "active" : "inactive");
+			/*
+			 * Some devices may require DMA still sending data
+			 * at any time for clock reference, etc.
+			 * Note: if there is still a data buffer being
+			 * processed then the ref count is negative.  This
+			 * allows for the DMA termination to be accounted in
+			 * the proper order.
+			 */
+			if (dma->spin_size && dma->spin_ref >= 0) {
+				chunksize = dma->spin_size;
+				if (chunksize > MAX_DMA_SIZE)
+					chunksize = (1 << MAX_DMA_ORDER);
+				while (start_dma(dma, dma->spin_addr, chunksize) == 0)
+					dma->spin_ref++;
+				if (dma->curr != NULL)
+					dma->spin_ref = -dma->spin_ref;
+			}
+			break;
+		}
+
+		/*
+		 * Let's try to start DMA on the current buffer.
+		 * If DMA is busy then we break here.
+		 */
+		chunksize = buf->size;
+		if (chunksize > MAX_DMA_SIZE)
+			chunksize = (1 << MAX_DMA_ORDER);
+		DPRINTK("process: b=%#x s=%d\n", (int) buf->id, buf->size);
+		if (start_dma(dma, buf->dma_ptr, chunksize) != 0)
+			break;
+		dma->active = 1;
+		if (!dma->curr)
+			dma->curr = buf;
+		buf->ref++;
+		buf->dma_ptr += chunksize;
+		buf->size -= chunksize;
+		if (buf->size == 0) {
+			/* current buffer is done: move tail to the next one */
+			dma->tail = buf->next;
+			DPRINTK("process: next b=%#x\n", (int) dma->tail);
+		}
+	}
+}
+
+
+/* This must be called with IRQ disabled */
+void sa1100_dma_done (sa1100_dma_t *dma)
+{
+	dma_buf_t *buf = dma->curr;
+
+	if (dma->spin_ref > 0) {
+		dma->spin_ref--;
+	} else if (buf) {
+		buf->ref--;
+		if (buf->ref == 0 && buf->size == 0) {
+			/*
+			 * Current buffer is done.
+			 * Move current reference to the next one and send
+			 * the processed buffer to the callback function,
+			 * then discard it.
+			 */
+			DPRINTK("IRQ: buf done\n");
+			dma->curr = buf->next;
+			if (dma->curr == NULL)
+				dma->spin_ref = -dma->spin_ref;
+			if (dma->head == buf)
+				dma->head = NULL;
+			buf->size = buf->dma_ptr - buf->dma_start;
+			if (dma->callback)
+				dma->callback(buf->id, buf->size);
+			kfree(buf);
+		}
+	}
+
+	process_dma(dma);
+}
+
+
+static void dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+	sa1100_dma_t *dma = (sa1100_dma_t *) dev_id;
+	int status = dma->regs->RdDCSR;
+
+	DPRINTK("IRQ: b=%#x st=%#x\n", (int) dma->curr->id, status);
+
+	dma->regs->ClrDCSR = DCSR_ERROR | DCSR_DONEA | DCSR_DONEB;
+	if (!(status & (DCSR_DONEA | DCSR_DONEB)))
+		return;
+
+	sa1100_dma_done (dma);
+}
+
+
+/*
+ * DMA interface functions
+ */
+
+/*
+ * Get dma list
+ * for /proc/dma
+ */
+int sa1100_get_dma_list(char *buf)
+{
+	int i, len = 0;
+
+	for (i = 0; i < MAX_SA1100_DMA_CHANNELS; i++) {
+		if (dma_chan[i].lock)
+			len += sprintf(buf + len, "%2d: %s\n",
+				       i, dma_chan[i].device_id);
+	}
+	return len;
+}
+
+int sa1100_request_dma(dmach_t * channel, const char *device_id)
+{
+	sa1100_dma_t *dma = NULL;
+	dma_regs_t *regs;
+	int ch, err;
+
+	*channel = -1;		/* to be sure we catch the freeing of a misregistered channel */
+
+	for (ch = 0; ch < SA1100_DMA_CHANNELS; ch++) {
+		dma = &dma_chan[ch];
+		if (xchg(&dma->lock, 1) == 0)
+			break;
+	}
+	if (ch >= SA1100_DMA_CHANNELS) {
+		printk(KERN_ERR "%s: no free DMA channel available\n",
+		       device_id);
+		return -EBUSY;
+	}
+
+	err = request_irq(dma->irq, dma_irq_handler, SA_INTERRUPT,
+			  device_id, (void *) dma);
+	if (err) {
+		printk(KERN_ERR
+		       "%s: unable to request IRQ %d for DMA channel %d\n",
+		       device_id, dma->irq, ch);
+		dma->lock = 0;
+		return err;
+	}
+
+	*channel = ch;
+	dma->device_id = device_id;
+	dma->callback = NULL;
+	dma->spin_size = 0;
+
+	regs = dma->regs;
+	regs->ClrDCSR =
+	    (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
+	     DCSR_IE | DCSR_ERROR | DCSR_RUN);
+	regs->DDAR = 0;
+
+	DPRINTK("requested\n");
+	return 0;
+}
+
+
+int sa1100_dma_set_callback(dmach_t channel, dma_callback_t cb)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+
+	dma->callback = cb;
+	DPRINTK("cb = %p\n", cb);
+	return 0;
+}
+
+
+int sa1100_dma_set_device(dmach_t channel, dma_device_t device)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+	dma_regs_t *regs = dma->regs;
+
+	if (dma->ready)
+		return -EINVAL;
+
+	regs->ClrDCSR = DCSR_STRTA | DCSR_STRTB | DCSR_IE | DCSR_RUN;
+	regs->DDAR = device;
+	DPRINTK("DDAR = %#x\n", device);
+	dma->ready = 1;
+	return 0;
+}
+
+
+int sa1100_dma_set_spin(dmach_t channel, dma_addr_t addr, int size)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+	int flags;
+
+	if (channel >= SA1100_DMA_CHANNELS)
+		return -EINVAL;
+
+	DPRINTK("set spin %d at %#x\n", size, addr);
+	local_irq_save(flags);
+	dma->spin_addr = addr;
+	dma->spin_size = size;
+	if (size)
+		process_dma(dma);
+	local_irq_restore(flags);
+	return 0;
+}
+
+
+int sa1100_dma_queue_buffer(dmach_t channel, void *buf_id,
+			    dma_addr_t data, int size)
+{
+	sa1100_dma_t *dma;
+	dma_buf_t *buf;
+	int flags;
+
+	dma = &dma_chan[channel];
+	if (!dma->ready)
+		return -EINVAL;
+
+	buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
+	if (!buf)
+		return -ENOMEM;
+
+	buf->next = NULL;
+	buf->ref = 0;
+	buf->dma_ptr = buf->dma_start = data;
+	buf->size = size;
+	buf->id = buf_id;
+	DPRINTK("queueing b=%#x a=%#x s=%d\n", (int) buf_id, data, size);
+
+	local_irq_save(flags);
+	if (dma->head)
+		dma->head->next = buf;
+	dma->head = buf;
+	if (!dma->tail)
+		dma->tail = buf;
+	process_dma(dma);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+
+int sa1100_dma_get_current(dmach_t channel, void **buf_id, dma_addr_t *addr)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+	dma_regs_t *regs = dma->regs;
+	int flags, ret;
+
+	if (channel_is_sa1111_sac(channel))
+		return sa1111_dma_get_current(channel, buf_id, addr);
+
+	local_irq_save(flags);
+	if (dma->curr && dma->spin_ref <= 0) {
+		dma_buf_t *buf = dma->curr;
+		int status, using_bufa;
+
+		status = regs->RdDCSR;
+		/*
+		 * If we got here, that's because there is, or recently was, a
+		 * buffer being processed.  We must determine whether buffer
+		 * A or B is active.  Two possibilities: either we are
+		 * in the middle of a buffer, or the DMA controller just
+		 * switched to the next toggle but the interrupt hasn't been
+		 * serviced yet.  The former case is straight forward.  In
+		 * the later case, we'll do like if DMA is just at the end
+		 * of the previous toggle since all registers haven't been
+		 * reset yet.  This goes around the edge case and since we're
+		 * always a little behind anyways it shouldn't make a big
+		 * difference.  If DMA has been stopped prior calling this
+		 * then the position is always exact.
+		 */
+		using_bufa = ((!(status & DCSR_BIU) &&  (status & DCSR_STRTA)) ||
+			      ( (status & DCSR_BIU) && !(status & DCSR_STRTB)));
+		if (buf_id)
+			*buf_id = buf->id;
+		*addr = (using_bufa) ? regs->DBSA : regs->DBSB;
+		/*
+		 * Clamp funky pointers sometimes returned by the hardware
+		 * on completed DMA transfers
+		 */
+		if (*addr < buf->dma_start ||
+		    *addr > buf->dma_ptr)
+			*addr = buf->dma_ptr;
+		DPRINTK("curr_pos: b=%#x a=%#x\n", (int)dma->curr->id, *addr);
+		ret = 0;
+	} else {
+		if (buf_id)
+			*buf_id = NULL;
+		*addr = 0;
+		DPRINTK("curr_pos: spinning\n");
+		ret = -ENXIO;
+	}
+	local_irq_restore(flags);
+	return ret;
+}
+
+
+int sa1100_dma_stop(dmach_t channel)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+	dma_regs_t *regs = dma->regs;
+
+	if (channel_is_sa1111_sac(channel))
+		return sa1111_dma_stop(channel);
+
+	regs->ClrDCSR = DCSR_RUN | DCSR_IE;
+	return 0;
+}
+
+
+int sa1100_dma_resume(dmach_t channel)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+	dma_regs_t *regs = dma->regs;
+
+	if (channel_is_sa1111_sac(channel))
+		return sa1111_dma_resume(channel);
+
+	regs->SetDCSR = DCSR_RUN | DCSR_IE;
+	return 0;
+}
+
+
+int sa1100_dma_flush_all(dmach_t channel)
+{
+	sa1100_dma_t *dma;
+	dma_buf_t *buf, *next_buf;
+	int flags;
+
+	dma = &dma_chan[channel];
+	local_irq_save(flags);
+	if (channel_is_sa1111_sac(channel))
+		sa1111_reset_sac_dma(channel);
+	else
+		dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_RUN|DCSR_IE;
+	buf = dma->curr;
+	if (!buf)
+		buf = dma->tail;
+	dma->head = dma->tail = dma->curr = NULL;
+	dma->active = 0;
+	dma->spin_ref = 0;
+	if (dma->spin_size)
+		process_dma(dma);
+	local_irq_restore(flags);
+	while (buf) {
+		next_buf = buf->next;
+		kfree(buf);
+		buf = next_buf;
+	}
+	DPRINTK("flushed\n");
+	return 0;
+}
+
+
+void sa1100_free_dma(dmach_t channel)
+{
+	sa1100_dma_t *dma;
+
+	if ((unsigned) channel >= MAX_SA1100_DMA_CHANNELS)
+		return;
+
+	dma = &dma_chan[channel];
+	if (!dma->lock) {
+		printk(KERN_ERR "Trying to free free DMA%d\n", channel);
+		return;
+	}
+
+	sa1100_dma_set_spin(channel, 0, 0);
+	sa1100_dma_flush_all(channel);
+	dma->ready = 0;
+
+	if (channel_is_sa1111_sac(channel)) {
+		sa1111_cleanup_sac_dma(channel);
+	} else {
+		free_irq(IRQ_DMA0 + channel, (void *) dma);
+	}
+	dma->lock = 0;
+
+	DPRINTK("freed\n");
+}
+
+
+EXPORT_SYMBOL(sa1100_request_dma);
+EXPORT_SYMBOL(sa1100_dma_set_callback);
+EXPORT_SYMBOL(sa1100_dma_set_device);
+EXPORT_SYMBOL(sa1100_dma_set_spin);
+EXPORT_SYMBOL(sa1100_dma_queue_buffer);
+EXPORT_SYMBOL(sa1100_dma_get_current);
+EXPORT_SYMBOL(sa1100_dma_stop);
+EXPORT_SYMBOL(sa1100_dma_resume);
+EXPORT_SYMBOL(sa1100_dma_flush_all);
+EXPORT_SYMBOL(sa1100_free_dma);
+
+
+static int __init sa1100_init_dma(void)
+{
+	int channel;
+	for (channel = 0; channel < SA1100_DMA_CHANNELS; channel++) {
+		dma_chan[channel].regs =
+		    (dma_regs_t *) io_p2v(_DDAR(channel));
+		dma_chan[channel].irq = IRQ_DMA0 + channel;
+	}
+	return 0;
+}
+
+__initcall(sa1100_init_dma);
diff -urN linux-2.4.4-pre2/arch/arm/mach-sa1100/dma-sa1111.c linux/arch/arm/mach-sa1100/dma-sa1111.c
--- linux-2.4.4-pre2/arch/arm/mach-sa1100/dma-sa1111.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-sa1100/dma-sa1111.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,346 @@
+/*
+ *  linux/arch/arm/mach-sa1100/dma-sa1111.c
+ *
+ *  Copyright (C) 2000 John Dorsey
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  4 September 2000 - created.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+
+// #define DEBUG
+#ifdef DEBUG
+#define DPRINTK( s, arg... )  printk( "dma<%s>: " s, dma->device_id , ##arg )
+#else
+#define DPRINTK( x... )
+#endif
+
+
+/*
+ * Control register structure for the SA1111 SAC DMA
+ */
+
+typedef struct {
+	volatile u_long SAD_CS;
+	volatile dma_addr_t SAD_SA;
+	volatile u_long SAD_CA;
+	volatile dma_addr_t SAD_SB;
+	volatile u_long SAD_CB;
+} dma_regs_t;
+
+#include "dma.h"
+
+
+void sa1111_reset_sac_dma(dmach_t channel)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+	dma->regs->SAD_CS = 0;
+	mdelay(1);
+	dma->dma_a = dma->dma_b = 0;
+}
+
+
+int start_sa1111_sac_dma(sa1100_dma_t *dma, dma_addr_t dma_ptr, size_t size)
+{
+  	dma_regs_t *sac_regs = dma->regs;
+
+	DPRINTK(" SAC DMA %cCS %02x at %08x (%d)\n",
+		(sac_regs==&SADTCS)?'T':'R', sac_regs->SAD_CS, dma_ptr, size);
+
+	/* The minimum transfer length requirement has not yet been
+	 * verified:
+	 */
+	if( size < SA1111_SAC_DMA_MIN_XFER )
+	  printk(KERN_WARNING "Warning: SAC xfers below %u bytes may be buggy!"
+		 " (%u bytes)\n", SA1111_SAC_DMA_MIN_XFER, size);
+
+	if( dma->dma_a && dma->dma_b ){
+	  	DPRINTK("  neither engine available! (A %d, B %d)\n",
+			dma->dma_a, dma->dma_b);
+	  	return -1;
+	}
+
+	if( sa1111_check_dma_bug(dma_ptr) )
+	  	printk(KERN_WARNING "Warning: DMA address %08x is buggy!\n",
+		       dma_ptr);
+
+	if( (dma->last_dma || dma->dma_b) && dma->dma_a == 0 ){
+	  	if( sac_regs->SAD_CS & SAD_CS_DBDB ){
+		  	DPRINTK("  awaiting \"done B\" interrupt, not starting\n");
+			return -1;
+		}
+		sac_regs->SAD_SA = SA1111_DMA_ADDR((u_int)dma_ptr);
+		sac_regs->SAD_CA = size;
+		sac_regs->SAD_CS = SAD_CS_DSTA | SAD_CS_DEN;
+		++dma->dma_a;
+		DPRINTK("  with A [%02lx %08lx %04lx]\n", sac_regs->SAD_CS,
+			sac_regs->SAD_SA, sac_regs->SAD_CA);
+	} else {
+	  	if( sac_regs->SAD_CS & SAD_CS_DBDA ){
+		  	DPRINTK("  awaiting \"done A\" interrupt, not starting\n");
+			return -1;
+		}
+		sac_regs->SAD_SB = SA1111_DMA_ADDR((u_int)dma_ptr);
+		sac_regs->SAD_CB = size;
+		sac_regs->SAD_CS = SAD_CS_DSTB | SAD_CS_DEN;
+		++dma->dma_b;
+		DPRINTK("  with B [%02lx %08lx %04lx]\n", sac_regs->SAD_CS,
+			sac_regs->SAD_SB, sac_regs->SAD_CB);
+	}
+
+	/* Additional delay to avoid DMA engine lockup during record: */
+	if( sac_regs == (dma_regs_t*)&SADRCS )
+	  	mdelay(1);	/* NP : wouuuh! ugly... */
+
+	return 0;
+}
+
+
+static void sa1111_sac_dma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+  	sa1100_dma_t *dma = (sa1100_dma_t *) dev_id;
+
+	DPRINTK("irq %d, last DMA serviced was %c, CS %02x\n", irq,
+		dma->last_dma?'B':'A', dma->regs->SAD_CS);
+
+	/* Occasionally, one of the DMA engines (A or B) will
+	 * lock up. We try to deal with this by quietly kicking
+	 * the control register for the afflicted transfer
+	 * direction.
+	 *
+	 * Note for the debugging-inclined: we currently aren't
+	 * properly flushing the DMA engines during channel
+	 * shutdown. A slight hiccup at the beginning of playback
+	 * after a channel has been stopped can be heard as
+	 * evidence of this. Programmatically, this shows up
+	 * as either a locked engine, or a spurious interrupt. -jd
+	 */
+
+	if(irq==AUDXMTDMADONEA || irq==AUDRCVDMADONEA){
+
+	  	if(dma->last_dma == 0){
+		  	DPRINTK("DMA B has locked up!\n");
+			dma->regs->SAD_CS = 0;
+			mdelay(1);
+			dma->dma_a = dma->dma_b = 0;
+		} else {
+		  	if(dma->dma_a == 0)
+			  	DPRINTK("spurious SAC IRQ %d\n", irq);
+			else {
+			  	--dma->dma_a;
+
+				/* Servicing the SAC DMA engines too quickly
+				 * after they issue a DONE interrupt causes
+				 * them to lock up.
+				 */
+				if(irq==AUDRCVDMADONEA || irq==AUDRCVDMADONEB)
+				  	mdelay(1);
+			}
+		}
+
+		dma->regs->SAD_CS = SAD_CS_DBDA | SAD_CS_DEN; /* w1c */
+		dma->last_dma = 0;
+
+	} else {
+
+	  	if(dma->last_dma == 1){
+		  	DPRINTK("DMA A has locked up!\n");
+			dma->regs->SAD_CS = 0;
+			mdelay(1);
+			dma->dma_a = dma->dma_b = 0;
+		} else {
+		  	if(dma->dma_b == 0)
+			  	DPRINTK("spurious SAC IRQ %d\n", irq);
+			else {
+			  	--dma->dma_b;
+
+				/* See lock-up note above. */
+				if(irq==AUDRCVDMADONEA || irq==AUDRCVDMADONEB)
+				  	mdelay(1);
+			}
+		}
+
+		dma->regs->SAD_CS = SAD_CS_DBDB | SAD_CS_DEN; /* w1c */
+		dma->last_dma = 1;
+
+	}
+
+	/* NP: maybe this shouldn't be called in all cases? */
+	sa1100_dma_done (dma);
+}
+
+
+int sa1111_sac_request_dma(dmach_t *channel, const char *device_id,
+			   unsigned int direction)
+{
+	sa1100_dma_t *dma = NULL;
+	int ch, irq, err;
+
+	*channel = -1;		/* to be sure we catch the freeing of a misregistered channel */
+
+	ch = SA1111_SAC_DMA_BASE + direction;
+
+	if (!channel_is_sa1111_sac(ch)) {
+	  	printk(KERN_ERR "%s: invalid SA-1111 SAC DMA channel (%d)\n",
+		       device_id, ch);
+		return -1;
+	}
+
+	dma = &dma_chan[ch];
+
+	if (xchg(&dma->lock, 1) == 1) {
+	  	printk(KERN_ERR "%s: SA-1111 SAC DMA channel %d in use\n",
+		       device_id, ch);
+		return -EBUSY;
+	}
+
+	irq = AUDXMTDMADONEA + direction;
+	err = request_irq(irq, sa1111_sac_dma_irq, SA_INTERRUPT,
+			  device_id, (void *) dma);
+	if (err) {
+		printk(KERN_ERR
+		       "%s: unable to request IRQ %d for DMA channel %d (A)\n",
+		       device_id, irq, ch);
+		dma->lock = 0;
+		return err;
+	}
+
+	irq = AUDXMTDMADONEB + direction;
+	err = request_irq(irq, sa1111_sac_dma_irq, SA_INTERRUPT,
+			  device_id, (void *) dma);
+	if (err) {
+		printk(KERN_ERR
+		       "%s: unable to request IRQ %d for DMA channel %d (B)\n",
+		       device_id, irq, ch);
+		dma->lock = 0;
+		return err;
+	}
+
+	*channel = ch;
+	dma->device_id = device_id;
+	dma->callback = NULL;
+	dma->spin_size = 0;
+	dma->ready = 1;
+
+	return 0;
+}
+
+
+/* FIXME:  need to complete the three following functions */
+
+int sa1111_dma_get_current(dmach_t channel, void **buf_id, dma_addr_t *addr)
+{
+	return -ENOSYS;
+}
+
+int sa1111_dma_stop(dmach_t channel)
+{
+	return 0;
+}
+
+int sa1111_dma_resume(dmach_t channel)
+{
+	return 0;
+}
+
+
+void sa1111_cleanup_sac_dma(dmach_t channel)
+{
+	sa1100_dma_t *dma = &dma_chan[channel];
+	free_irq(AUDXMTDMADONEA + (channel - SA1111_SAC_DMA_BASE), (void*) dma);
+	free_irq(AUDXMTDMADONEB + (channel - SA1111_SAC_DMA_BASE), (void*) dma);
+}
+
+
+/* According to the "Intel StrongARM SA-1111 Microprocessor Companion
+ * Chip Specification Update" (June 2000), errata #7, there is a
+ * significant bug in Serial Audio Controller DMA. If the SAC is
+ * accessing a region of memory above 1MB relative to the bank base,
+ * it is important that address bit 10 _NOT_ be asserted. Depending
+ * on the configuration of the RAM, bit 10 may correspond to one
+ * of several different (processor-relative) address bits.
+ *
+ * This routine only identifies whether or not a given DMA address
+ * is susceptible to the bug.
+ */
+int sa1111_check_dma_bug(dma_addr_t addr){
+	unsigned int physaddr=SA1111_DMA_ADDR((unsigned int)addr);
+
+	/* Section 4.6 of the "Intel StrongARM SA-1111 Development Module
+	 * User's Guide" mentions that jumpers R51 and R52 control the
+	 * target of SA-1111 DMA (either SDRAM bank 0 on Assabet, or
+	 * SDRAM bank 1 on Neponset). The default configuration selects
+	 * Assabet, so any address in bank 1 is necessarily invalid.
+	 */
+	if(machine_is_assabet() && addr >= _DRAMBnk1)
+	  	return -1;
+
+	/* The bug only applies to buffers located more than one megabyte
+	 * above the start of the target bank:
+	 */
+	if(physaddr<(1<<20))
+	  	return 0;
+
+	switch(FExtr(SMCR, SMCR_DRAC)){
+	case 01: /* 10 row + bank address bits, A<20> must not be set */
+	  	if(physaddr & (1<<20))
+		  	return -1;
+		break;
+	case 02: /* 11 row + bank address bits, A<23> must not be set */
+	  	if(physaddr & (1<<23))
+		  	return -1;
+		break;
+	case 03: /* 12 row + bank address bits, A<24> must not be set */
+	  	if(physaddr & (1<<24))
+		  	return -1;
+		break;
+	case 04: /* 13 row + bank address bits, A<25> must not be set */
+	  	if(physaddr & (1<<25))
+		  	return -1;
+		break;
+	case 05: /* 14 row + bank address bits, A<20> must not be set */
+	  	if(physaddr & (1<<20))
+		  	return -1;
+		break;
+	case 06: /* 15 row + bank address bits, A<20> must not be set */
+	  	if(physaddr & (1<<20))
+		  	return -1;
+		break;
+	default:
+	  	printk(KERN_ERR "%s(): invalid SMCR DRAC value 0%o\n",
+		       __FUNCTION__, FExtr(SMCR, SMCR_DRAC));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+EXPORT_SYMBOL(sa1111_sac_request_dma);
+EXPORT_SYMBOL(sa1111_check_dma_bug);
+
+
+static void __init sa1111_init_sac_dma(void)
+{
+	int channel = SA1111_SAC_DMA_BASE;
+	dma_chan[channel++].regs = (dma_regs_t *) &SADTCS;
+	dma_chan[channel++].regs = (dma_regs_t *) &SADRCS;
+}
+
+__initcall(sa1111_init_sac_dma);
diff -urN linux-2.4.4-pre2/arch/arm/mach-sa1100/dma.h linux/arch/arm/mach-sa1100/dma.h
--- linux-2.4.4-pre2/arch/arm/mach-sa1100/dma.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-sa1100/dma.h	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,56 @@
+/*
+ * Definitions shared between dma-sa1100.c and dma-sa1111.c
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+/*
+ * DMA channel structure.
+ */
+
+typedef struct dma_buf_s dma_buf_t;
+
+typedef struct {
+	unsigned int lock;      /* Device is allocated */
+	const char *device_id;  /* Device name */
+	dma_buf_t *head;        /* where to insert buffers */
+	dma_buf_t *tail;        /* where to remove buffers */
+	dma_buf_t *curr;        /* buffer currently DMA'ed */
+	int ready;              /* 1 if DMA can occur */
+	int active;             /* 1 if DMA is actually processing data */
+	dma_regs_t *regs;       /* points to appropriate DMA registers */
+	int irq;                /* IRQ used by the channel */
+	dma_callback_t callback;        /* ... to call when buffers are done */
+	int spin_size;          /* > 0 when DMA should spin when no more buffer */
+	dma_addr_t spin_addr;   /* DMA address to spin onto */
+	int spin_ref;           /* number of spinning references */
+#ifdef CONFIG_SA1111
+	int dma_a, dma_b, last_dma;	/* SA-1111 specific */
+#endif
+} sa1100_dma_t;
+
+extern sa1100_dma_t dma_chan[MAX_SA1100_DMA_CHANNELS];
+
+
+int start_sa1111_sac_dma(sa1100_dma_t *dma, dma_addr_t dma_ptr, size_t size);
+int sa1111_dma_get_current(dmach_t channel, void **buf_id, dma_addr_t *addr);
+int sa1111_dma_stop(dmach_t channel);
+int sa1111_dma_resume(dmach_t channel);
+void sa1111_reset_sac_dma(dmach_t channel);
+void sa1111_cleanup_sac_dma(dmach_t channel);
+
+void sa1100_dma_done (sa1100_dma_t *dma);
+
+
+#ifdef CONFIG_SA1111
+#define channel_is_sa1111_sac(ch) \
+	((ch) >= SA1111_SAC_DMA_BASE && \
+	 (ch) <  SA1111_SAC_DMA_BASE + SA1111_SAC_DMA_CHANNELS)
+#else
+#define channel_is_sa1111_sac(ch) (0)
+#endif
+
diff -urN linux-2.4.4-pre2/arch/arm/mach-shark/Makefile linux/arch/arm/mach-shark/Makefile
--- linux-2.4.4-pre2/arch/arm/mach-shark/Makefile	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/mach-shark/Makefile	Mon Dec  3 22:34:49 2001
@@ -18,6 +18,6 @@
 
 export-objs		:= 
 
-#obj-$(CONFIG_LEDS)	+= leds.o
+obj-$(CONFIG_LEDS)	+= leds.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.4-pre2/arch/arm/mach-shark/arch.c linux/arch/arm/mach-shark/arch.c
--- linux-2.4.4-pre2/arch/arm/mach-shark/arch.c	Tue Sep 19 08:31:53 2000
+++ linux/arch/arm/mach-shark/arch.c	Mon Dec  3 22:34:49 2001
@@ -18,14 +18,31 @@
 
 #include <asm/mach/arch.h>
 
-extern void setup_initrd(unsigned int start, unsigned int size);
-extern void setup_ramdisk(int doload, int prompt, int start, unsigned int rd_sz);
-extern void __init footbridge_map_io(void);
-extern void __init shark_map_io(void);
+static void __init
+fixup_shark(struct machine_desc *desc, struct param_struct *params,
+	char **cmdline, struct meminfo *mi) {
+	int i;
+
+	mi->nr_banks=0;
+	for (i=0;i<NR_BANKS;i++) {
+		if (params->u1.s.pages_in_bank[i] != 0) {
+			mi->nr_banks++;
+			mi->bank[i].node  = 0;
+			mi->bank[i].start = params->u1.s.pages_in_bank[i] & 0xffff0000;
+			mi->bank[i].size  = (params->u1.s.pages_in_bank[i] & 0xffff)*PAGE_SIZE;
+			params->u1.s.pages_in_bank[i] &= 0xffff;
+		}
+	}
+}
+
+extern void shark_map_io(void);
+extern void genarch_init_irq(void);
 
 MACHINE_START(SHARK, "Shark")
 	MAINTAINER("Alexander Schulz")
 	BOOT_MEM(0x08000000, 0x40000000, 0xe0000000)
-	VIDEO(0x06000000, 0x061fffff)
+	BOOT_PARAMS(0x08003000)
+	FIXUP(fixup_shark)
 	MAPIO(shark_map_io)
+	INITIRQ(genarch_init_irq)
 MACHINE_END
diff -urN linux-2.4.4-pre2/arch/arm/mach-shark/dma.c linux/arch/arm/mach-shark/dma.c
--- linux-2.4.4-pre2/arch/arm/mach-shark/dma.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mach-shark/dma.c	Mon Dec  3 22:34:49 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/arch/arm/mach-shark/dma.c
  *
- *  by Alexander.Schulz@stud.uni-karlsruhe.de
+ *  by Alexander Schulz <aschulz@netwinder.org>
  *
  *  derived from:
  *  arch/arm/kernel/dma-ebsa285.c
diff -urN linux-2.4.4-pre2/arch/arm/mach-shark/leds.c linux/arch/arm/mach-shark/leds.c
--- linux-2.4.4-pre2/arch/arm/mach-shark/leds.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-shark/leds.c	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,163 @@
+/*
+ * arch/arm/kernel/leds-shark.c
+ * by Alexander Schulz <aschulz@netwinder.org>
+ *
+ * derived from:
+ * arch/arm/kernel/leds-footbridge.c
+ * Copyright (C) 1998-1999 Russell King
+ *
+ * DIGITAL Shark LED control routines.
+ *
+ * The leds use is as follows:
+ *  - Green front - toggles state every 50 timer interrupts
+ *  - Amber front - Unused, this is a dual color led (Amber/Green)
+ *  - Amber back  - On if system is not idle
+ *
+ * Changelog:
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+static char led_state;
+static short hw_led_state;
+static short saved_state;
+
+static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED;
+
+short sequoia_read(int addr) {
+  outw(addr,0x24);
+  return inw(0x26);
+}
+
+void sequoia_write(short value,short addr) {
+  outw(addr,0x24);
+  outw(value,0x26);
+}
+
+static void sequoia_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+
+	hw_led_state = sequoia_read(0x09);
+
+	switch (evt) {
+	case led_start:
+		hw_led_state |= SEQUOIA_LED_GREEN;
+		hw_led_state |= SEQUOIA_LED_AMBER;
+#ifdef CONFIG_LEDS_CPU
+		hw_led_state |= SEQUOIA_LED_BACK;
+#else
+		hw_led_state &= ~SEQUOIA_LED_BACK;
+#endif
+		led_state |= LED_STATE_ENABLED;
+		break;
+
+	case led_stop:
+		hw_led_state &= ~SEQUOIA_LED_BACK;
+		hw_led_state |= SEQUOIA_LED_GREEN;
+		hw_led_state |= SEQUOIA_LED_AMBER;
+		led_state &= ~LED_STATE_ENABLED;
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		saved_state = hw_led_state;
+		hw_led_state &= ~SEQUOIA_LED_BACK;
+		hw_led_state |= SEQUOIA_LED_GREEN;
+		hw_led_state |= SEQUOIA_LED_AMBER;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = saved_state;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= SEQUOIA_LED_GREEN;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state &= ~SEQUOIA_LED_BACK;
+		break;
+
+	case led_idle_end:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state |= SEQUOIA_LED_BACK;
+		break;
+#endif
+
+	case led_green_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~SEQUOIA_LED_GREEN;
+		break;
+
+	case led_green_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= SEQUOIA_LED_GREEN;
+		break;
+
+	case led_amber_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~SEQUOIA_LED_AMBER;
+		break;
+
+	case led_amber_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= SEQUOIA_LED_AMBER;
+		break;
+
+	case led_red_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= SEQUOIA_LED_BACK;
+		break;
+
+	case led_red_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~SEQUOIA_LED_BACK;
+		break;
+
+	default:
+		break;
+	}
+
+	if  (led_state & LED_STATE_ENABLED)
+		sequoia_write(hw_led_state,0x09);
+
+	spin_unlock_irqrestore(&leds_lock, flags);
+}
+
+static int __init leds_init(void)
+{
+	extern void (*leds_event)(led_event_t);
+	short temp;
+	
+	leds_event = sequoia_leds_event;
+
+	/* Make LEDs independent of power-state */
+	request_region(0x24,4,"sequoia");
+	temp = sequoia_read(0x09);
+	temp |= 1<<10;
+	sequoia_write(temp,0x09);
+	leds_event(led_start);
+	return 0;
+}
+
+__initcall(leds_init);
diff -urN linux-2.4.4-pre2/arch/arm/mach-shark/mm.c linux/arch/arm/mach-shark/mm.c
--- linux-2.4.4-pre2/arch/arm/mach-shark/mm.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mach-shark/mm.c	Mon Dec  3 22:34:49 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/arch/arm/mach-shark/mm.c
  *
- *  by Alexander.Schulz@stud.uni-karlsruhe.de
+ *  by Alexander Schulz <aschulz@netwinder.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -19,8 +19,6 @@
 
 static struct map_desc shark_io_desc[] __initdata = {
 	{ IO_BASE	, IO_START	, IO_SIZE	, DOMAIN_IO, 0, 1, 0, 0 },
-	{ FB_BASE	, FB_START	, FB_SIZE	, DOMAIN_IO, 0, 1, 0, 0 },
-	{ FBREG_BASE	, FBREG_START	, FBREG_SIZE	, DOMAIN_IO, 0, 1, 0, 0 },
 	LAST_DESC
 };
 
diff -urN linux-2.4.4-pre2/arch/arm/mm/Makefile linux/arch/arm/mm/Makefile
--- linux-2.4.4-pre2/arch/arm/mm/Makefile	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/mm/Makefile	Mon Dec  3 22:34:49 2001
@@ -13,13 +13,22 @@
 
 # Object file lists.
 
-obj-y		:= extable.o fault-common.o fault-$(PROCESSOR).o init.o \
-		   mm-$(PROCESSOR).o small_page.o
+obj-y		:= init.o
 obj-m		:=
 obj-n		:=
 obj-		:=
 export-objs	:= proc-syms.o
 
+cpu32-y		:= consistent.o fault-armv.o ioremap.o mm-armv.o
+cpu32-$(CONFIG_MODULES) += proc-syms.o
+
+obj-y		+= extable.o fault-common.o
+obj-$(CONFIG_CPU_26) += fault-armo.o mm-armo.o small_page.o
+obj-$(CONFIG_CPU_32) += $(cpu32-y)
+
+obj-$(CONFIG_DISCONTIGMEM) += discontig.o
+
+# Select the processor-specific files
 p-$(CONFIG_CPU_26)	+= proc-arm2,3.o
 p-$(CONFIG_CPU_ARM610)	+= proc-arm6,7.o
 p-$(CONFIG_CPU_ARM710)	+= proc-arm6,7.o
@@ -29,11 +38,6 @@
 p-$(CONFIG_CPU_SA110)	+= proc-sa110.o
 p-$(CONFIG_CPU_SA1100)	+= proc-sa110.o
 
-obj-$(CONFIG_CPU_32)  	+= consistent.o ioremap.o
-ifeq ($(CONFIG_CPU_32),y)
-obj-$(CONFIG_MODULES)	+= proc-syms.o
-endif
-
 # Integrator follows "new style"
 # Soon, others will do too, and we can get rid of this
 MMMACH		:= mm-$(MACHINE).c
@@ -46,10 +50,4 @@
 include $(TOPDIR)/Rules.make
 
 # Special dependencies
-proc-arm2,3.o:	../lib/constants.h
-proc-arm6,7.o:	../lib/constants.h
-proc-arm720.o:	../lib/constants.h
-proc-arm920.o:	../lib/constants.h
-proc-arm10.o:	../lib/constants.h
-proc-sa110.o:	../lib/constants.h
-
+$(p-y):	$(TOPDIR)/include/asm-arm/constants.h
diff -urN linux-2.4.4-pre2/arch/arm/mm/consistent.c linux/arch/arm/mm/consistent.c
--- linux-2.4.4-pre2/arch/arm/mm/consistent.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/consistent.c	Mon Dec  3 22:34:49 2001
@@ -34,9 +34,9 @@
  */
 void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
 {
-	int order;
-	unsigned long page;
-	void *ret;
+	struct page *page, *end, *free;
+	unsigned long order;
+	void *ret, *virt;
 
 	if (in_interrupt())
 		BUG();
@@ -44,37 +44,47 @@
 	size = PAGE_ALIGN(size);
 	order = get_order(size);
 
-	page = __get_free_pages(gfp, order);
+	page = alloc_pages(gfp, order);
 	if (!page)
 		goto no_page;
 
-	ret = __ioremap(virt_to_phys((void *)page), size, 0);
-	if (ret) {
-		/* free wasted pages */
-		unsigned long end;
-
-		/*
-		 * we need to ensure that there are no
-		 * cachelines in use, or worse dirty in
-		 * this area.
-		 */
-		invalidate_dcache_range(page, page + size);
-		invalidate_dcache_range((unsigned long)ret, (unsigned long)ret + size);
-
-		*dma_handle = __virt_to_bus(page);
-
-		end = page + (PAGE_SIZE << order);
-		page += size;
-		while (page < end) {
-			free_page(page);
-			page += PAGE_SIZE;
-		}
-		return ret;
+	/*
+	 * We could do with a page_to_phys and page_to_bus here.
+	 */
+	virt = page_address(page);
+	*dma_handle = virt_to_bus(virt);
+	ret = __ioremap(virt_to_phys(virt), size, 0);
+	if (!ret)
+		goto no_remap;
+
+#if 0 /* ioremap_does_flush_cache_all */
+	/*
+	 * we need to ensure that there are no cachelines in use, or
+	 * worse dirty in this area.  Really, we don't need to do
+	 * this since __ioremap does a flush_cache_all() anyway. --rmk
+	 */
+	invalidate_dcache_range(virt, virt + size);
+#endif
+
+	/*
+	 * free wasted pages.  We skip the first page since
+	 * we know that it will have count = 1 and won't
+	 * require freeing.
+	 */
+	page = virt_to_page(virt);
+	free = page + (size >> PAGE_SHIFT);
+	end  = page + (1 << order);
+
+	while (++page < end) {
+		set_page_count(page, 1);
+		if (page >= free)
+			__free_page(page);
 	}
+	return ret;
 
-	free_pages(page, order);
+no_remap:
+	__free_pages(page, order);
 no_page:
-	BUG();
 	return NULL;
 }
 
diff -urN linux-2.4.4-pre2/arch/arm/mm/fault-armv.c linux/arch/arm/mm/fault-armv.c
--- linux-2.4.4-pre2/arch/arm/mm/fault-armv.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/mm/fault-armv.c	Mon Dec  3 22:34:49 2001
@@ -2,7 +2,7 @@
  *  linux/arch/arm/mm/fault-armv.c
  *
  *  Copyright (C) 1995  Linus Torvalds
- *  Modifications for ARM processor (c) 1995-1999 Russell King
+ *  Modifications for ARM processor (c) 1995-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -29,7 +29,13 @@
 
 extern void die_if_kernel(const char *str, struct pt_regs *regs, int err);
 extern void show_pte(struct mm_struct *mm, unsigned long addr);
-extern int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs);
+extern int do_page_fault(unsigned long addr, int error_code,
+			 struct pt_regs *regs);
+extern int do_translation_fault(unsigned long addr, int error_code,
+				struct pt_regs *regs);
+extern void do_bad_area(struct task_struct *tsk, struct mm_struct *mm,
+			unsigned long addr, int error_code,
+			struct pt_regs *regs);
 
 #ifdef CONFIG_ALIGNMENT_TRAP
 /*
@@ -37,12 +43,11 @@
  * /proc/sys/debug/alignment, modified and integrated into
  * Linux 2.1 by Russell King
  *
+ * Speed optimisations and better fault handling by Russell King.
+ *
  * NOTE!!! This is not portable onto the ARM6/ARM7 processors yet.  Also,
  * it seems to give a severe performance impact (1 abort/ms - NW runs at
  * ARM6 speeds) with GCC 2.7.2.2 - needs checking with a later GCC/EGCS.
- *
- * IMHO, I don't think that the trap handler is advantageous on ARM6,7
- * processors (they'll run like an ARM3).  We'll see.
  */
 #define CODING_BITS(i)	(i & 0x0e000000)
 
@@ -115,91 +120,323 @@
 __initcall(alignment_init);
 #endif /* CONFIG_SYSCTL */
 
+union offset_union {
+	unsigned long un;
+	  signed long sn;
+};
+
+#define TYPE_ERROR	0
+#define TYPE_FAULT	1
+#define TYPE_LDST	2
+#define TYPE_DONE	3
+
+#define get8_unaligned_check(val,addr,err)		\
+	__asm__(					\
+	"1:	ldrb	%1, [%2]\n"			\
+	"2:\n"						\
+	"	.section .fixup,\"ax\"\n"		\
+	"	.align	2\n"				\
+	"3:	mov	%0, #1\n"			\
+	"	b	2b\n"				\
+	"	.previous\n"				\
+	"	.section __ex_table,\"a\"\n"		\
+	"	.align	3\n"				\
+	"	.long	1b, 3b\n"			\
+	"	.previous\n"				\
+	: "=r" (err), "=&r" (val), "=r" (addr)		\
+	: "0" (err), "2" (addr))
+
+#define get8t_unaligned_check(val,addr,err)		\
+	__asm__(					\
+	"1:	ldrbt	%1, [%2]\n"			\
+	"2:\n"						\
+	"	.section .fixup,\"ax\"\n"		\
+	"	.align	2\n"				\
+	"3:	mov	%0, #1\n"			\
+	"	b	2b\n"				\
+	"	.previous\n"				\
+	"	.section __ex_table,\"a\"\n"		\
+	"	.align	3\n"				\
+	"	.long	1b, 3b\n"			\
+	"	.previous\n"				\
+	: "=r" (err), "=&r" (val), "=r" (addr)		\
+	: "0" (err), "2" (addr))
+
+#define get16_unaligned_check(val,addr)				\
+	do {							\
+		unsigned int err = 0, v, a = addr;		\
+		get8_unaligned_check(val,a,err);		\
+		get8_unaligned_check(v,a,err);			\
+		val |= v << 8;					\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define put16_unaligned_check(val,addr)				\
+	do {							\
+		unsigned int err = 0, v = val, a = addr;	\
+		__asm__(					\
+		"1:	strb	%1, [%2], #1\n"			\
+		"	mov	%1, %1, lsr #8\n"		\
+		"2:	strb	%1, [%2]\n"			\
+		"3:\n"						\
+		"	.section .fixup,\"ax\"\n"		\
+		"	.align	2\n"				\
+		"4:	mov	%0, #1\n"			\
+		"	b	3b\n"				\
+		"	.previous\n"				\
+		"	.section __ex_table,\"a\"\n"		\
+		"	.align	3\n"				\
+		"	.long	1b, 4b\n"			\
+		"	.long	2b, 4b\n"			\
+		"	.previous\n"				\
+		: "=r" (err), "=&r" (v), "=&r" (a)		\
+		: "0" (err), "1" (v), "2" (a));			\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define __put32_unaligned_check(ins,val,addr)			\
+	do {							\
+		unsigned int err = 0, v = val, a = addr;	\
+		__asm__(					\
+		"1:	"ins"	%1, [%2], #1\n"			\
+		"	mov	%1, %1, lsr #8\n"		\
+		"2:	"ins"	%1, [%2], #1\n"			\
+		"	mov	%1, %1, lsr #8\n"		\
+		"3:	"ins"	%1, [%2], #1\n"			\
+		"	mov	%1, %1, lsr #8\n"		\
+		"4:	"ins"	%1, [%2]\n"			\
+		"5:\n"						\
+		"	.section .fixup,\"ax\"\n"		\
+		"	.align	2\n"				\
+		"6:	mov	%0, #1\n"			\
+		"	b	5b\n"				\
+		"	.previous\n"				\
+		"	.section __ex_table,\"a\"\n"		\
+		"	.align	3\n"				\
+		"	.long	1b, 6b\n"			\
+		"	.long	2b, 6b\n"			\
+		"	.long	3b, 6b\n"			\
+		"	.long	4b, 6b\n"			\
+		"	.previous\n"				\
+		: "=r" (err), "=&r" (v), "=&r" (a)		\
+		: "0" (err), "1" (v), "2" (a));			\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define get32_unaligned_check(val,addr)				\
+	do {							\
+		unsigned int err = 0, v, a = addr;		\
+		get8_unaligned_check(val,a,err);		\
+		get8_unaligned_check(v,a,err);			\
+		val |= v << 8;					\
+		get8_unaligned_check(v,a,err);			\
+		val |= v << 16;					\
+		get8_unaligned_check(v,a,err);			\
+		val |= v << 24;					\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define put32_unaligned_check(val,addr)	 \
+	__put32_unaligned_check("strb", val, addr)
+
+#define get32t_unaligned_check(val,addr)			\
+	do {							\
+		unsigned int err = 0, v, a = addr;		\
+		get8t_unaligned_check(val,a,err);		\
+		get8t_unaligned_check(v,a,err);			\
+		val |= v << 8;					\
+		get8t_unaligned_check(v,a,err);			\
+		val |= v << 16;					\
+		get8t_unaligned_check(v,a,err);			\
+		val |= v << 24;					\
+		if (err)					\
+			goto fault;				\
+	} while (0)
+
+#define put32t_unaligned_check(val,addr) \
+	__put32_unaligned_check("strbt", val, addr)
+
+static void
+do_alignment_finish_ldst(unsigned long addr, unsigned long instr, struct pt_regs *regs, union offset_union offset)
+{
+	if (!LDST_U_BIT(instr))
+		offset.un = -offset.un;
+
+	if (!LDST_P_BIT(instr))
+		addr += offset.un;
+
+	if (!LDST_P_BIT(instr) || LDST_W_BIT(instr))
+		regs->uregs[RN_BITS(instr)] = addr;
+}
+
 static int
-do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
+do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *regs)
 {
-	unsigned int instr, rd, rn, correction, nr_regs, regbits;
-	unsigned long eaddr;
-	union { unsigned long un; signed long sn; } offset;
+	unsigned int rd = RD_BITS(instr);
 
-	if (user_mode(regs)) {
-		set_cr(cr_no_alignment);
-		ai_user += 1;
-		return 0;
-	}
+	if ((instr & 0x01f00ff0) == 0x01000090)
+		goto swp;
 
-	ai_sys += 1;
+	if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0)
+		goto bad;
+
+	ai_half += 1;
+
+	if (LDST_L_BIT(instr)) {
+		unsigned long val;
+		get16_unaligned_check(val, addr);
+
+		/* signed half-word? */
+		if (instr & 0x40)
+			val = (signed long)((signed short) val);
+
+		regs->uregs[rd] = val;
+	} else
+		put16_unaligned_check(regs->uregs[rd], addr);
+
+	return TYPE_LDST;
+
+swp:
+	printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
+bad:
+	return TYPE_ERROR;
+
+fault:
+	return TYPE_FAULT;
+}
+
+static int
+do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *regs)
+{
+	unsigned int rd = RD_BITS(instr);
+
+	ai_word += 1;
+
+	if (!LDST_P_BIT(instr) && LDST_W_BIT(instr))
+		goto trans;
+
+	if (LDST_L_BIT(instr))
+		get32_unaligned_check(regs->uregs[rd], addr);
+	else
+		put32_unaligned_check(regs->uregs[rd], addr);
+	return TYPE_LDST;
+
+trans:
+	if (LDST_L_BIT(instr))
+		get32t_unaligned_check(regs->uregs[rd], addr);
+	else
+		put32t_unaligned_check(regs->uregs[rd], addr);
+	return TYPE_LDST;
+
+fault:
+	return TYPE_FAULT;
+}
+
+static int
+do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *regs)
+{
+	unsigned int rd, rn, correction, nr_regs, regbits;
+	unsigned long eaddr;
 
-	instr = *(unsigned long *)instruction_pointer(regs);
 	correction = 4; /* sometimes 8 on ARMv3 */
-	regs->ARM_pc += correction + 4;
+	regs->ARM_pc += correction;
 
 	rd = RD_BITS(instr);
 	rn = RN_BITS(instr);
 	eaddr = regs->uregs[rn];
 
-	switch(CODING_BITS(instr)) {
-	case 0x00000000:
-		if ((instr & 0x0ff00ff0) == 0x01000090) {
-			ai_skipped += 1;
-			printk(KERN_ERR "Unaligned trap: not handling swp instruction\n");
-			return 1;
-		}
+	if (LDM_S_BIT(instr))
+		goto bad;
 
-		if (((instr & 0x0e000090) == 0x00000090) && (instr & 0x60) != 0) {
-			ai_half += 1;
-			if (LDSTH_I_BIT(instr))
-				offset.un = (instr & 0xf00) >> 4 | (instr & 15);
-			else
-				offset.un = regs->uregs[RM_BITS(instr)];
-
-			if (LDST_P_BIT(instr)) {
-				if (LDST_U_BIT(instr))
-					eaddr += offset.un;
-				else
-					eaddr -= offset.un;
-			}
+	ai_multi += 1;
+
+	for (regbits = REGMASK_BITS(instr), nr_regs = 0; regbits; regbits >>= 1)
+		nr_regs += 4;
 
-			/*
-			 * This is a "hint" - we already have eaddr worked out by the
-			 * processor for us.
-			 */
-			if (addr != eaddr)
-				printk(KERN_ERR "LDRHSTRH: PC = %08lx, instr = %08x, "
-					"addr = %08lx, eaddr = %08lx\n",
-					 instruction_pointer(regs), instr, addr, eaddr);
-
-			if (LDST_L_BIT(instr))
-				regs->uregs[rd] = get_unaligned((unsigned short *)eaddr);
-			else
-				put_unaligned(regs->uregs[rd], (unsigned short *)eaddr);
-
-			/* signed half-word? */
-			if (instr & 0x40)
-				regs->uregs[rd] = (long)((short) regs->uregs[rd]);
-
-			if (!LDST_P_BIT(instr)) {
-				if (LDST_U_BIT(instr))
-					eaddr += offset.un;
-				else
-					eaddr -= offset.un;
-				regs->uregs[rn] = eaddr;
-			} else if (LDST_W_BIT(instr))
-				regs->uregs[rn] = eaddr;
-			break;
+	if (!LDST_U_BIT(instr))
+		eaddr -= nr_regs;
+
+	/*
+	 * This is a "hint" - we already have eaddr worked out by the
+	 * processor for us.
+	 */
+	if (addr != eaddr)
+		printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08lx, "
+			"addr = %08lx, eaddr = %08lx\n",
+			 instruction_pointer(regs), instr, addr, eaddr);
+
+	if ((LDST_U_BIT(instr) == 0 && LDST_P_BIT(instr) == 0) ||
+	    (LDST_U_BIT(instr)      && LDST_P_BIT(instr)))
+		eaddr += 4;
+
+	for (regbits = REGMASK_BITS(instr), rd = 0; regbits; regbits >>= 1, rd += 1)
+		if (regbits & 1) {
+			if (LDST_L_BIT(instr)) {
+				get32_unaligned_check(regs->uregs[rd], eaddr);
+				if (rd == 15)
+					correction = 0;
+			} else
+				put32_unaligned_check(regs->uregs[rd], eaddr);
+			eaddr += 4;
 		}
 
-	default:
-		ai_skipped += 1;
-		panic("Alignment trap: not handling instruction %08X at %08lX",
-				instr, regs->ARM_pc - correction - 4);
+	if (LDST_W_BIT(instr)) {
+		if (LDST_P_BIT(instr) && !LDST_U_BIT(instr))
+			eaddr -= nr_regs;
+		else if (LDST_P_BIT(instr))
+			eaddr -= 4;
+		else if (!LDST_U_BIT(instr))
+			eaddr -= 4 + nr_regs;
+		regs->uregs[rn] = eaddr;
+	}
+	regs->ARM_pc -= correction;
+	return TYPE_DONE;
+
+fault:
+	return TYPE_FAULT;
+
+bad:
+	printk(KERN_ERR "Alignment trap: not handling ldm with s-bit set\n");
+	return TYPE_ERROR;
+}
+
+static int
+do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
+{
+	union offset_union offset;
+	unsigned long instr, instrptr;
+	int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
+	unsigned int type;
+
+	if (user_mode(regs))
+		goto user;
+
+	ai_sys += 1;
+
+	instrptr = instruction_pointer(regs);
+	instr = *(unsigned long *)instrptr;
+
+	regs->ARM_pc += 4;
+
+	switch (CODING_BITS(instr)) {
+	case 0x00000000:	/* ldrh or strh */
+		if (LDSTH_I_BIT(instr))
+			offset.un = (instr & 0xf00) >> 4 | (instr & 15);
+		else
+			offset.un = regs->uregs[RM_BITS(instr)];
+		handler = do_alignment_ldrhstrh;
 		break;
 
-	case 0x04000000:
+	case 0x04000000:	/* ldr or str immediate */
 		offset.un = OFFSET_BITS(instr);
-		goto ldr_str;
+		handler = do_alignment_ldrstr;
+		break;
 
-	case 0x06000000:
+	case 0x06000000:	/* ldr or str register */
 		offset.un = regs->uregs[RM_BITS(instr)];
 
 		if (IS_SHIFT(instr)) {
@@ -229,97 +466,49 @@
 				break;
 			}
 		}
-
-	ldr_str:
-		ai_word += 1;
-		if (LDST_P_BIT(instr)) {
-			if (LDST_U_BIT(instr))
-				eaddr += offset.un;
-			else
-				eaddr -= offset.un;
-		} else {
-			if (LDST_W_BIT(instr)) {
-				printk(KERN_ERR "Not handling ldrt/strt correctly\n");
-				return 1;
-			}
-		}
-
-		/*
-		 * This is a "hint" - we already have eaddr worked out by the
-		 * processor for us.
-		 */
-		if (addr != eaddr)
-			printk(KERN_ERR "LDRSTR: PC = %08lx, instr = %08x, "
-				"addr = %08lx, eaddr = %08lx\n",
-				 instruction_pointer(regs), instr, addr, eaddr);
-
-		if (LDST_L_BIT(instr)) {
-			regs->uregs[rd] = get_unaligned((unsigned long *)eaddr);
-			if (rd == 15)
-				correction = 0;
-		} else
-			put_unaligned(regs->uregs[rd], (unsigned long *)eaddr);
-
-		if (!LDST_P_BIT(instr)) {
-			if (LDST_U_BIT(instr))
-				eaddr += offset.un;
-			else
-				eaddr -= offset.un;
-
-			regs->uregs[rn] = eaddr;
-		} else if (LDST_W_BIT(instr))
-			regs->uregs[rn] = eaddr;
+		handler = do_alignment_ldrstr;
 		break;
 
-	case 0x08000000:
-		if (LDM_S_BIT(instr))
-			panic("Alignment trap: not handling LDM with s-bit\n");
-		ai_multi += 1;
-
-		for (regbits = REGMASK_BITS(instr), nr_regs = 0; regbits; regbits >>= 1)
-			nr_regs += 4;
+	case 0x08000000:	/* ldm or stm */
+		handler = do_alignment_ldmstm;
+		break;
 
-		if  (!LDST_U_BIT(instr))
-			eaddr -= nr_regs;
+	default:
+		goto bad;
+	}
 
-		/*
-		 * This is a "hint" - we already have eaddr worked out by the
-		 * processor for us.
-		 */
-		if (addr != eaddr)
-			printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08x, "
-				"addr = %08lx, eaddr = %08lx\n",
-				 instruction_pointer(regs), instr, addr, eaddr);
+	type = handler(addr, instr, regs);
 
-		if ((LDST_U_BIT(instr) == 0 && LDST_P_BIT(instr) == 0) ||
-		    (LDST_U_BIT(instr)      && LDST_P_BIT(instr)))
-			eaddr += 4;
+	if (type == TYPE_ERROR || type == TYPE_FAULT)
+		goto bad_or_fault;
 
-		for (regbits = REGMASK_BITS(instr), rd = 0; regbits; regbits >>= 1, rd += 1)
-			if (regbits & 1) {
-				if (LDST_L_BIT(instr)) {
-					regs->uregs[rd] = get_unaligned((unsigned long *)eaddr);
-					if (rd == 15)
-						correction = 0;
-				} else
-					put_unaligned(regs->uregs[rd], (unsigned long *)eaddr);
-				eaddr += 4;
-			}
+	if (type == TYPE_LDST)
+		do_alignment_finish_ldst(addr, instr, regs, offset);
 
-		if (LDST_W_BIT(instr)) {
-			if (LDST_P_BIT(instr) && !LDST_U_BIT(instr))
-				eaddr -= nr_regs;
-			else if (LDST_P_BIT(instr))
-				eaddr -= 4;
-			else if (!LDST_U_BIT(instr))
-				eaddr -= 4 + nr_regs;
-			regs->uregs[rn] = eaddr;
-		}
-		break;
-	}
+	return 0;
 
-	regs->ARM_pc -= correction;
+bad_or_fault:
+	if (type == TYPE_ERROR)
+		goto bad;
+	regs->ARM_pc -= 4;
+	/*
+	 * We got a fault - fix it up, or die.
+	 */
+	do_bad_area(current, current->mm, addr, error_code, regs);
+	return 0;
 
+bad:
+	/*
+	 * Oops, we didn't handle the instruction.
+	 */
+	printk(KERN_ERR "Alignment trap: not handling instruction "
+		"%08lx at [<%08lx>]", instr, instrptr);
+	ai_skipped += 1;
+	return 1;
+
+user:
+	set_cr(cr_no_alignment);
+	ai_user += 1;
 	return 0;
 }
 
@@ -332,35 +521,13 @@
 /*
  * Some section permission faults need to be handled gracefully, for
  * instance, when they happen due to a __{get,put}_user during an oops).
- * In this case, we should return an error to the __{get,put}_user caller
- * instead of causing another oops.  We should also fixup this fault as
- * the user could pass a pointer to a section to the kernel.
  */
 static int
 do_sect_fault(unsigned long addr, int error_code, struct pt_regs *regs)
 {
-	unsigned long fixup;
-
-	if (user_mode(regs)) {
-#ifdef CONFIG_DEBUG_USER
-		printk("%s: permission fault on section, "
-		       "address=0x%08lx, code %d\n",
-		       current->comm, addr, error_code);
-#endif
-		goto fail;
-	}
-
-	fixup = search_exception_table(instruction_pointer(regs));
-	if (fixup != 0) {
-#ifdef DEBUG
-		printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n",
-			tsk->comm, regs->ARM_pc, addr, fixup);
-#endif
-		regs->ARM_pc = fixup;
-		return 0;
-	}
-fail:
-	return 1;	/* not fixed up */
+	struct task_struct *tsk = current;
+	do_bad_area(tsk, tsk->active_mm, addr, error_code, regs);
+	return 0;
 }
 
 static const struct fsr_info {
@@ -369,17 +536,17 @@
 	char	*name;
 } fsr_info[] = {
 	{ NULL,			SIGSEGV, "vector exception"		   },
-	{ do_alignment,		SIGBUS,	 "alignment exception"		   },
+	{ do_alignment,		SIGILL,	 "alignment exception"		   },
 	{ NULL,			SIGKILL, "terminal exception"		   },
-	{ do_alignment,		SIGBUS,	 "alignment exception"		   },
+	{ do_alignment,		SIGILL,	 "alignment exception"		   },
 	{ NULL,			SIGBUS,	 "external abort on linefetch"	   },
-	{ do_page_fault,	SIGSEGV, "page fault"			   },
+	{ do_translation_fault,	SIGSEGV, "section translation fault"	   },
 	{ NULL,			SIGBUS,	 "external abort on linefetch"	   },
-	{ do_page_fault,	SIGSEGV, "page fault"			   },
+	{ do_page_fault,	SIGSEGV, "page translation fault"	   },
 	{ NULL,			SIGBUS,	 "external abort on non-linefetch" },
-	{ NULL,			SIGSEGV, "domain fault"			   },
+	{ NULL,			SIGSEGV, "section domain fault"		   },
 	{ NULL,			SIGBUS,	 "external abort on non-linefetch" },
-	{ NULL,			SIGSEGV, "domain fault"			   },
+	{ NULL,			SIGSEGV, "page domain fault"		   },
 	{ NULL,			SIGBUS,	 "external abort on translation"   },
 	{ do_sect_fault,	SIGSEGV, "section permission fault"	   },
 	{ NULL,			SIGBUS,	 "external abort on translation"   },
@@ -398,7 +565,7 @@
 	if (addr == regs->ARM_pc)
 		goto sa1_weirdness;
 #endif
-#if defined(CONFIG_CPU_ARM720T) && defined(CONFIG_ALIGNMENT_TRAP)
+#if defined(CONFIG_CPU_ARM720) && defined(CONFIG_ALIGNMENT_TRAP)
 	if (addr & 3 && (fsr & 13) != 1)
 		goto arm720_weirdness;
 #endif
@@ -410,7 +577,6 @@
 		return;
 bad:
 	force_sig(inf->sig, current);
-
 	printk(KERN_ALERT "Unhandled fault: %s (%X) at 0x%08lx\n",
 		inf->name, fsr, addr);
 	show_pte(current->mm, addr);
@@ -431,7 +597,7 @@
 		goto bad;
 	return;
 #endif
-#if defined(CONFIG_CPU_ARM720T) && defined(CONFIG_ALIGNMENT_TRAP)
+#if defined(CONFIG_CPU_ARM720) && defined(CONFIG_ALIGNMENT_TRAP)
 arm720_weirdness:
 	if (!user_mode(regs)) {
 		unsigned long instr;
@@ -462,6 +628,6 @@
 asmlinkage int
 do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
 {
-	do_page_fault(addr, 0, regs);
+	do_translation_fault(addr, 0, regs);
 	return 1;
 }
diff -urN linux-2.4.4-pre2/arch/arm/mm/fault-common.c linux/arch/arm/mm/fault-common.c
--- linux-2.4.4-pre2/arch/arm/mm/fault-common.c	Mon Mar 19 12:35:10 2001
+++ linux/arch/arm/mm/fault-common.c	Mon Dec  3 22:34:49 2001
@@ -2,7 +2,7 @@
  *  linux/arch/arm/mm/fault-common.c
  *
  *  Copyright (C) 1995  Linus Torvalds
- *  Modifications for ARM processor (c) 1995-1999 Russell King
+ *  Modifications for ARM processor (c) 1995-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -93,8 +93,82 @@
 	printk("\n");
 }
 
+/*
+ * Oops.  The kernel tried to access some page that wasn't present.
+ */
+static void
+__do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code,
+		  struct pt_regs *regs)
+{
+	unsigned long fixup;
+
+	/*
+	 * Are we prepared to handle this kernel fault?
+	 */
+	if ((fixup = search_exception_table(instruction_pointer(regs))) != 0) {
+#ifdef DEBUG
+		printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n",
+			tsk->comm, regs->ARM_pc, addr, fixup);
+#endif
+		regs->ARM_pc = fixup;
+		return;
+	}
+
+	/*
+	 * No handler, we'll have to terminate things with extreme prejudice.
+	 */
+	printk(KERN_ALERT
+		"Unable to handle kernel %s at virtual address %08lx\n",
+		(addr < PAGE_SIZE) ? "NULL pointer dereference" :
+		"paging request", addr);
+
+	show_pte(mm, addr);
+	die("Oops", regs, error_code);
+	do_exit(SIGKILL);
+}
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * User mode accesses just cause a SIGSEGV
+ */
+static void
+__do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code,
+		int code, struct pt_regs *regs)
+{
+	struct siginfo si;
+
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_DEBUG "%s: unhandled page fault at pc=0x%08lx, "
+	       "lr=0x%08lx (bad address=0x%08lx, code %d)\n",
+	       tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, error_code);
+#endif
+
+	tsk->thread.address = addr;
+	tsk->thread.error_code = error_code;
+	tsk->thread.trap_no = 14;
+	si.si_signo = SIGSEGV;
+	si.si_errno = 0;
+	si.si_code = code;
+	si.si_addr = (void *)addr;
+	force_sig_info(SIGSEGV, &si, tsk);
+}
+
+void
+do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
+	    int error_code, struct pt_regs *regs)
+{
+	/*
+	 * If we are in kernel mode at this point, we
+	 * have no context to handle this fault with.
+	 */
+	if (user_mode(regs))
+		__do_user_fault(tsk, addr, error_code, SEGV_MAPERR, regs);
+	else
+		__do_kernel_fault(mm, addr, error_code, regs);
+}
+
 static int
-__do_page_fault(struct mm_struct *mm, unsigned long addr, int mode,
+__do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code,
 		struct task_struct *tsk)
 {
 	struct vm_area_struct *vma;
@@ -112,7 +186,7 @@
 	 * memory access, so we can handle it.
 	 */
 good_area:
-	if (READ_FAULT(mode)) /* read? */
+	if (READ_FAULT(error_code)) /* read? */
 		mask = VM_READ|VM_EXEC;
 	else
 		mask = VM_WRITE;
@@ -127,7 +201,7 @@
 	 * than endlessly redo the fault.
 	 */
 survive:
-	fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(mode));
+	fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(error_code));
 
 	/*
 	 * Handle the "normal" cases first - successful and sigbus
@@ -161,62 +235,16 @@
 	return fault;
 }
 
-static int __do_vmalloc_fault(unsigned long addr, struct mm_struct *mm)
-{
-	/* Synchronise this task's top level page-table
-	 * with the 'reference' page table.
-	 */
-	int offset = __pgd_offset(addr);
-	pgd_t *pgd, *pgd_k;
-	pmd_t *pmd, *pmd_k;
-
-	pgd_k = init_mm.pgd + offset;
-	if (!pgd_present(*pgd_k))
-		goto bad_area;
-
-	pgd = mm->pgd + offset;
-#if 0	/* note that we are two-level */
-	if (!pgd_present(*pgd))
-		set_pgd(pgd, *pgd_k);
-#endif
-
-	pmd_k = pmd_offset(pgd_k, addr);
-	if (pmd_none(*pmd_k))
-		goto bad_area;
-
-	pmd = pmd_offset(pgd, addr);
-	if (!pmd_none(*pmd))
-		goto bad_area;
-	set_pmd(pmd, *pmd_k);
-	return 1;
-
-bad_area:
-	return -2;
-}
-
-int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs)
+int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
 {
 	struct task_struct *tsk;
 	struct mm_struct *mm;
-	unsigned long fixup;
 	int fault;
 
 	tsk = current;
 	mm  = tsk->mm;
 
 	/*
-	 * We fault-in kernel-space virtual memory on-demand. The
-	 * 'reference' page table is init_mm.pgd.
-	 *
-	 * NOTE! We MUST NOT take any locks for this case. We may
-	 * be in an interrupt or a critical region, and should
-	 * only copy the information from the master page table,
-	 * nothing more.
-	 */
-	if (addr >= TASK_SIZE)
-		goto vmalloc_fault;
-
-	/*
 	 * If we're in an interrupt or have no user
 	 * context, we must not take the fault..
 	 */
@@ -224,10 +252,9 @@
 		goto no_context;
 
 	down_read(&mm->mmap_sem);
-	fault = __do_page_fault(mm, addr, mode, tsk);
+	fault = __do_page_fault(mm, addr, error_code, tsk);
 	up_read(&mm->mmap_sem);
 
-ret:
 	/*
 	 * Handle the "normal" case first
 	 */
@@ -255,28 +282,9 @@
 		 */
 		printk("VM: killing process %s\n", tsk->comm);
 		do_exit(SIGKILL);
-	} else {
-		/*
-		 * Something tried to access memory that isn't in our memory map..
-		 * User mode accesses just cause a SIGSEGV
-		 */
-		struct siginfo si;
-
-#ifdef CONFIG_DEBUG_USER
-		printk(KERN_DEBUG "%s: unhandled page fault at pc=0x%08lx, "
-		       "lr=0x%08lx (bad address=0x%08lx, code %d)\n",
-		       tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode);
-#endif
-
-		tsk->thread.address = addr;
-		tsk->thread.error_code = mode;
-		tsk->thread.trap_no = 14;
-		si.si_signo = SIGSEGV;
-		si.si_errno = 0;
-		si.si_code = fault == -1 ? SEGV_ACCERR : SEGV_MAPERR;
-		si.si_addr = (void *)addr;
-		force_sig_info(SIGSEGV, &si, tsk);
-	}
+	} else
+		__do_user_fault(tsk, addr, error_code, fault == -1 ?
+				SEGV_ACCERR : SEGV_MAPERR, regs);
 	return 0;
 
 
@@ -290,7 +298,7 @@
 	 * or user mode.
 	 */
 	tsk->thread.address = addr;
-	tsk->thread.error_code = mode;
+	tsk->thread.error_code = error_code;
 	tsk->thread.trap_no = 14;
 	force_sig(SIGBUS, tsk);
 
@@ -299,32 +307,64 @@
 		return 0;
 
 no_context:
-	/* Are we prepared to handle this kernel fault?  */
-	if ((fixup = search_exception_table(instruction_pointer(regs))) != 0) {
-#ifdef DEBUG
-		printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n",
-			tsk->comm, regs->ARM_pc, addr, fixup);
+	__do_kernel_fault(mm, addr, error_code, regs);
+	return 0;
+}
+
+/*
+ * First Level Translation Fault Handler
+ *
+ * We enter here because the first level page table doesn't contain
+ * a valid entry for the address.
+ *
+ * If the address is in kernel space (>= TASK_SIZE), then we are
+ * probably faulting in the vmalloc() area.
+ *
+ * If the init_task's first level page tables contains the relevant
+ * entry, we copy the it to this task.  If not, we send the process
+ * a signal, fixup the exception, or oops the kernel.
+ *
+ * NOTE! We MUST NOT take any locks for this case. We may be in an
+ * interrupt or a critical region, and should only copy the information
+ * from the master page table, nothing more.
+ */
+int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *regs)
+{
+	struct task_struct *tsk;
+	struct mm_struct *mm;
+	int offset;
+	pgd_t *pgd, *pgd_k;
+	pmd_t *pmd, *pmd_k;
+
+	if (addr < TASK_SIZE)
+		return do_page_fault(addr, error_code, regs);
+
+	tsk = current;
+	mm  = tsk->active_mm;
+
+	offset = __pgd_offset(addr);
+
+	pgd_k = init_mm.pgd + offset;
+	pgd   = mm->pgd + offset;
+
+	if (pgd_none(*pgd_k))
+		goto bad_area;
+
+#if 0	/* note that we are two-level */
+	if (!pgd_present(*pgd))
+		set_pgd(pgd, *pgd_k);
 #endif
-		regs->ARM_pc = fixup;
-		return 0;
-	}
 
-	/*
-	 * Oops. The kernel tried to access some bad page. We'll have to
-	 * terminate things with extreme prejudice.
-	 */
-	printk(KERN_ALERT
-		"Unable to handle kernel %s at virtual address %08lx\n",
-		(addr < PAGE_SIZE) ? "NULL pointer dereference" :
-		"paging request", addr);
+	pmd_k = pmd_offset(pgd_k, addr);
+	pmd   = pmd_offset(pgd, addr);
 
-	show_pte(mm, addr);
-	die("Oops", regs, mode);
-	do_exit(SIGKILL);
+	if (pmd_none(*pmd_k))
+		goto bad_area;
 
+	set_pmd(pmd, *pmd_k);
 	return 0;
 
-vmalloc_fault:
-	fault = __do_vmalloc_fault(addr, mm);
-	goto ret;
+bad_area:
+	do_bad_area(tsk, mm, addr, error_code, regs);
+	return 0;
 }
diff -urN linux-2.4.4-pre2/arch/arm/mm/init.c linux/arch/arm/mm/init.c
--- linux-2.4.4-pre2/arch/arm/mm/init.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/mm/init.c	Mon Dec  3 22:34:49 2001
@@ -59,52 +59,10 @@
 static struct meminfo meminfo __initdata = { 0, };
 
 /*
- * empty_bad_page is the page that is used for page faults when
- * linux is out-of-memory. Older versions of linux just did a
- * do_exit(), but using this instead means there is less risk
- * for a process dying in kernel mode, possibly leaving a inode
- * unused etc..
- *
- * empty_bad_pte_table is the accompanying page-table: it is
- * initialized to point to BAD_PAGE entries.
- *
  * empty_zero_page is a special page that is used for
  * zero-initialized data and COW.
  */
 struct page *empty_zero_page;
-struct page *empty_bad_page;
-pte_t *empty_bad_pte_table;
-
-pte_t *get_bad_pte_table(void)
-{
-	pte_t v;
-	int i;
-
-	v = pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED));
-
-	for (i = 0; i < PTRS_PER_PTE; i++)
-		set_pte(empty_bad_pte_table + i, v);
-
-	return empty_bad_pte_table;
-}
-
-void __handle_bad_pmd(pmd_t *pmd)
-{
-	pmd_ERROR(*pmd);
-#ifdef CONFIG_DEBUG_ERRORS
-	__backtrace();
-#endif
-	set_pmd(pmd, mk_user_pmd(get_bad_pte_table()));
-}
-
-void __handle_bad_pmd_kernel(pmd_t *pmd)
-{
-	pmd_ERROR(*pmd);
-#ifdef CONFIG_DEBUG_ERRORS
-	__backtrace();
-#endif
-	set_pmd(pmd, mk_kernel_pmd(get_bad_pte_table()));
-}
 
 #ifndef CONFIG_NO_PGT_CACHE
 struct pgtable_cache_struct quicklists;
@@ -120,12 +78,12 @@
 				freed++;
 			}
 			if(pmd_quicklist) {
-				free_pmd_slow(get_pmd_fast());
+				pmd_free_slow(pmd_alloc_one_fast(NULL, 0));
 				freed++;
 			}
 			if(pte_quicklist) {
-				free_pte_slow(get_pte_fast());
-				 freed++;
+				pte_free_slow(pte_alloc_one_fast(NULL, 0));
+				freed++;
 			}
 		} while(pgtable_cache_size > low);
 	}
@@ -514,18 +472,15 @@
  */
 void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
 {
-	void *zero_page, *bad_page, *bad_table;
+	void *zero_page;
 	int node;
 
 	memcpy(&meminfo, mi, sizeof(meminfo));
 
 	/*
-	 * allocate what we need for the bad pages.
-	 * note that we count on this going ok.
+	 * allocate the zero page.  Note that we count on this going ok.
 	 */
 	zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
-	bad_page  = alloc_bootmem_low_pages(PAGE_SIZE);
-	bad_table = alloc_bootmem_low_pages(TABLE_SIZE);
 
 	/*
 	 * initialise the page tables.
@@ -586,11 +541,7 @@
 	 * the mem_map is initialised
 	 */
 	memzero(zero_page, PAGE_SIZE);
-	memzero(bad_page, PAGE_SIZE);
-
 	empty_zero_page = virt_to_page(zero_page);
-	empty_bad_page  = virt_to_page(bad_page);
-	empty_bad_pte_table = ((pte_t *)bad_table) + TABLE_OFFSET;
 }
 
 /*
diff -urN linux-2.4.4-pre2/arch/arm/mm/ioremap.c linux/arch/arm/mm/ioremap.c
--- linux-2.4.4-pre2/arch/arm/mm/ioremap.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/ioremap.c	Mon Dec  3 22:34:49 2001
@@ -44,14 +44,18 @@
 	end = address + size;
 	if (end > PMD_SIZE)
 		end = PMD_SIZE;
+	if (address >= end)
+		BUG();
 	do {
-		if (!pte_none(*pte))
+		if (!pte_none(*pte)) {
 			printk("remap_area_pte: page already exists\n");
+			BUG();
+		}
 		set_pte(pte, mk_pte_phys(phys_addr, pgprot));
 		address += PAGE_SIZE;
 		phys_addr += PAGE_SIZE;
 		pte++;
-	} while (address < end);
+	} while (address && (address < end));
 }
 
 static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
@@ -67,40 +71,50 @@
 		end = PGDIR_SIZE;
 
 	phys_addr -= address;
+	if (address >= end)
+		BUG();
+
 	pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags);
 	do {
-		pte_t * pte = pte_alloc_kernel(pmd, address);
+		pte_t * pte = pte_alloc(&init_mm, pmd, address);
 		if (!pte)
 			return -ENOMEM;
 		remap_area_pte(pte, address, end - address, address + phys_addr, pgprot);
 		address = (address + PMD_SIZE) & PMD_MASK;
 		pmd++;
-	} while (address < end);
+	} while (address && (address < end));
 	return 0;
 }
 
 static int remap_area_pages(unsigned long address, unsigned long phys_addr,
 				 unsigned long size, unsigned long flags)
 {
+	int error;
 	pgd_t * dir;
 	unsigned long end = address + size;
 
 	phys_addr -= address;
 	dir = pgd_offset(&init_mm, address);
 	flush_cache_all();
-	while (address < end) {
-		pmd_t *pmd = pmd_alloc_kernel(dir, address);
+	if (address >= end)
+		BUG();
+	spin_lock(&init_mm.page_table_lock);
+	do {
+		pmd_t *pmd;
+		pmd = pmd_alloc(&init_mm, dir, address);
+		error = -ENOMEM;
 		if (!pmd)
-			return -ENOMEM;
+			break;
 		if (remap_area_pmd(pmd, address, end - address,
 					 phys_addr + address, flags))
-			return -ENOMEM;
-		set_pgdir(address, *dir);
+			break;
+		error = 0;
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
-	}
+	} while (address && (address < end));
+	spin_unlock(&init_mm.page_table_lock);
 	flush_tlb_all();
-	return 0;
+	return error;
 }
 
 /*
diff -urN linux-2.4.4-pre2/arch/arm/mm/mm-armo.c linux/arch/arm/mm/mm-armo.c
--- linux-2.4.4-pre2/arch/arm/mm/mm-armo.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/mm-armo.c	Mon Dec  3 22:34:49 2001
@@ -22,120 +22,98 @@
 #include <asm/mach/map.h>
 
 #define MEMC_TABLE_SIZE (256*sizeof(unsigned long))
-#define PGD_TABLE_SIZE	(PTRS_PER_PGD * BYTES_PER_PTR)
 
+kmem_cache_t *pte_cache, *pgd_cache;
 int page_nr;
 
-extern unsigned long get_page_2k(int prio);
-extern void free_page_2k(unsigned long);
-extern pte_t *get_bad_pte_table(void);
-
 /*
  * Allocate a page table.  Note that we place the MEMC
  * table before the page directory.  This means we can
  * easily get to both tightly-associated data structures
  * with a single pointer.
- *
- * We actually only need 1152 bytes, 896 bytes is wasted.
- * We could try to fit 7 PTEs into that slot somehow.
  */
-static inline void *alloc_pgd_table(int priority)
+static inline pgd_t *alloc_pgd_table(int priority)
 {
-	unsigned long pg2k;
+	void *pg2k = kmem_cache_alloc(pgd_cache, GFP_KERNEL);
 
-	pg2k = get_page_2k(priority);
 	if (pg2k)
 		pg2k += MEMC_TABLE_SIZE;
 
-	return (void *)pg2k;
+	return (pgd_t *)pg2k;
 }
 
 void free_pgd_slow(pgd_t *pgd)
 {
 	unsigned long tbl = (unsigned long)pgd;
 
+	/*
+	 * CHECKME: are we leaking pte tables here???
+	 */
+
 	tbl -= MEMC_TABLE_SIZE;
-	free_page_2k(tbl);
+
+	kmem_cache_free(pgd_cache, (void *)tbl);
 }
 
-/*
- * FIXME: the following over-allocates by 1600%
- */
-static inline void *alloc_pte_table(int size, int prio)
+pgd_t *get_pgd_slow(struct mm_struct *mm)
 {
-	if (size != 128)
-		printk("invalid table size\n");
-	return (void *)get_page_2k(prio);
-}
-
-void free_pte_slow(pte_t *pte)
-{
-	unsigned long tbl = (unsigned long)pte;
-	free_page_2k(tbl);
-}
-
-pgd_t *get_pgd_slow(void)
-{
-	pgd_t *pgd = (pgd_t *)alloc_pgd_table(GFP_KERNEL);
-	pmd_t *new_pmd;
-
-	if (pgd) {
-		pgd_t *init = pgd_offset(&init_mm, 0);
-		
-		memzero(pgd, USER_PTRS_PER_PGD * sizeof(pgd_t));
-		memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
-			(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
-
-		/*
-		 * On ARM, first page must always be allocated
-		 */
-		if (!pmd_alloc(pgd, 0))
-			goto nomem;
-		else {
-			pmd_t *old_pmd = pmd_offset(init, 0);
-			new_pmd = pmd_offset(pgd, 0);
-
-			if (!pte_alloc(new_pmd, 0))
-				goto nomem_pmd;
-			else {
-				pte_t *new_pte = pte_offset(new_pmd, 0);
-				pte_t *old_pte = pte_offset(old_pmd, 0);
-
-				set_pte (new_pte, *old_pte);
-			}
-		}
-		/* update MEMC tables */
-		cpu_memc_update_all(pgd);
-	}
-	return pgd;
+	pgd_t *new_pgd, *init_pgd;
+	pmd_t *new_pmd, *init_pmd;
+	pte_t *new_pte, *init_pte;
+
+	new_pgd = alloc_pgd_table(GFP_KERNEL);
+	if (!new_pgd)
+		goto no_pgd;
+
+	/*
+	 * This lock is here just to satisfy pmd_alloc and pte_lock
+	 */
+	spin_lock(&mm->page_table_lock);
+
+	/*
+	 * On ARM, first page must always be allocated since it contains
+	 * the machine vectors.
+	 */
+	new_pmd = pmd_alloc(mm, new_pgd, 0);
+	if (!new_pmd)
+		goto no_pmd;
 
-nomem_pmd:
+	new_pte = pte_alloc(mm, new_pmd, 0);
+	if (!new_pte)
+		goto no_pte;
+
+	init_pgd = pgd_offset_k(0);
+	init_pmd = pmd_offset(init_pgd, 0);
+	init_pte = pte_offset(init_pmd, 0);
+
+	set_pte(new_pte, *init_pte);
+
+	/*
+	 * most of the page table entries are zeroed
+	 * wne the table is created.
+	 */
+	memcpy(new_pgd + USER_PTRS_PER_PGD, init_pgd + USER_PTRS_PER_PGD,
+		(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+
+	spin_unlock(&mm->page_table_lock);
+
+	/* update MEMC tables */
+	cpu_memc_update_all(new_pgd);
+	return new_pgd;
+
+no_pte:
+	spin_unlock(&mm->page_table_lock);
 	pmd_free(new_pmd);
-nomem:
-	free_pgd_slow(pgd);
+	free_pgd_slow(new_pgd);
 	return NULL;
-}
 
-pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
-{
-	pte_t *pte;
+no_pmd:
+	spin_unlock(&mm->page_table_lock);
+	free_pgd_slow(new_pgd);
+	return NULL;
 
-	pte = (pte_t *)alloc_pte_table(PTRS_PER_PTE * sizeof(pte_t), GFP_KERNEL);
-	if (pmd_none(*pmd)) {
-		if (pte) {
-			memzero(pte, PTRS_PER_PTE * sizeof(pte_t));
-			set_pmd(pmd, mk_user_pmd(pte));
-			return pte + offset;
-		}
-		set_pmd(pmd, mk_user_pmd(get_bad_pte_table()));
-		return NULL;
-	}
-	free_pte_slow(pte);
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + offset;
+no_pgd:
+	return NULL;
 }
 
 /*
@@ -160,7 +138,7 @@
 
 	pte = alloc_bootmem_low_pages(PTRS_PER_PTE * sizeof(pte_t));
 	pte[0] = mk_pte_phys(PAGE_OFFSET + 491520, PAGE_READONLY);
-	set_pmd(pmd_offset(swapper_pg_dir, 0), mk_kernel_pmd(pte));
+	pmd_populate(&init_mm, pmd_offset(swapper_pg_dir, 0), pte);
 
 	for (i = 1; i < PTRS_PER_PGD; i++)
 		pgd_val(swapper_pg_dir[i]) = 0;
@@ -176,4 +154,31 @@
  */
 void __init create_memmap_holes(struct meminfo *mi)
 {
+}
+
+static void pte_cache_ctor(void *pte, kmem_cache_t *cache, unsigned long flags)
+{
+	memzero(pte, sizeof(pte_t) * PTRS_PER_PTE);
+}
+
+static void pgd_cache_ctor(void *pte, kmem_cache_t *cache, unsigned long flags)
+{
+	pgd_t *pgd = (pte + MEMC_TABLE_SIZE);
+
+	memzero(pgd, USER_PTRS_PER_PGD * sizeof(pgd_t));
+}
+
+void __init pgtable_cache_init(void)
+{
+	pte_cache = kmem_cache_create("pte-cache",
+				sizeof(pte_t) * PTRS_PER_PTE,
+				0, 0, pte_cache_ctor, NULL);
+	if (!pte_cache)
+		BUG();
+
+	pgd_cache = kmem_cache_create("pgd-cache", MEMC_TABLE_SIZE +
+				sizeof(pgd_t) * PTRS_PER_PGD,
+				0, 0, pgd_cache_ctor, NULL);
+	if (!pgd_cache)
+		BUG();
 }
diff -urN linux-2.4.4-pre2/arch/arm/mm/mm-armv.c linux/arch/arm/mm/mm-armv.c
--- linux-2.4.4-pre2/arch/arm/mm/mm-armv.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/arm/mm/mm-armv.c	Mon Dec  3 22:34:49 2001
@@ -22,12 +22,6 @@
 
 #include <asm/mach/map.h>
 
-unsigned long *valid_addr_bitmap;
-
-extern unsigned long get_page_2k(int priority);
-extern void free_page_2k(unsigned long page);
-extern pte_t *get_bad_pte_table(void);
-
 /*
  * These are useful for identifing cache coherency
  * problems by allowing the cache or the cache and
@@ -73,47 +67,68 @@
 /*
  * need to get a 16k page for level 1
  */
-pgd_t *get_pgd_slow(void)
+pgd_t *get_pgd_slow(struct mm_struct *mm)
 {
-	pgd_t *pgd = (pgd_t *)__get_free_pages(GFP_KERNEL,2);
-	pmd_t *new_pmd;
+	pgd_t *new_pgd, *init_pgd;
+	pmd_t *new_pmd, *init_pmd;
+	pte_t *new_pte, *init_pte;
+
+	new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2);
+	if (!new_pgd)
+		goto no_pgd;
+
+	memzero(new_pgd, FIRST_KERNEL_PGD_NR * sizeof(pgd_t));
+
+	/*
+	 * This lock is here just to satisfy pmd_alloc and pte_lock
+	 */
+	spin_lock(&mm->page_table_lock);
 
-	if (pgd) {
-		pgd_t *init = pgd_offset_k(0);
+	/*
+	 * On ARM, first page must always be allocated since it contains
+	 * the machine vectors.
+	 */
+	new_pmd = pmd_alloc(mm, new_pgd, 0);
+	if (!new_pmd)
+		goto no_pmd;
+
+	new_pte = pte_alloc(mm, new_pmd, 0);
+	if (!new_pte)
+		goto no_pte;
+
+	init_pgd = pgd_offset_k(0);
+	init_pmd = pmd_offset(init_pgd, 0);
+	init_pte = pte_offset(init_pmd, 0);
 
-		memzero(pgd, FIRST_KERNEL_PGD_NR * sizeof(pgd_t));
-		memcpy(pgd  + FIRST_KERNEL_PGD_NR, init + FIRST_KERNEL_PGD_NR,
+	set_pte(new_pte, *init_pte);
+
+	/*
+	 * Copy over the kernel and IO PGD entries
+	 */
+	memcpy(new_pgd  + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR,
 		       (PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t));
-		/*
-		 * FIXME: this should not be necessary
-		 */
-		clean_cache_area(pgd, PTRS_PER_PGD * sizeof(pgd_t));
 
-		/*
-		 * On ARM, first page must always be allocated
-		 */
-		if (!pmd_alloc(pgd, 0))
-			goto nomem;
-		else {
-			pmd_t *old_pmd = pmd_offset(init, 0);
-			new_pmd = pmd_offset(pgd, 0);
-
-			if (!pte_alloc(new_pmd, 0))
-				goto nomem_pmd;
-			else {
-				pte_t *new_pte = pte_offset(new_pmd, 0);
-				pte_t *old_pte = pte_offset(old_pmd, 0);
+	spin_unlock(&mm->page_table_lock);
 
-				set_pte(new_pte, *old_pte);
-			}
-		}
-	}
-	return pgd;
+	/*
+	 * FIXME: this should not be necessary
+	 */
+	clean_cache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t));
+
+	return new_pgd;
 
-nomem_pmd:
+no_pte:
+	spin_unlock(&mm->page_table_lock);
 	pmd_free(new_pmd);
-nomem:
-	free_pages((unsigned long)pgd, 2);
+	free_pages((unsigned long)new_pgd, 2);
+	return NULL;
+
+no_pmd:
+	spin_unlock(&mm->page_table_lock);
+	free_pages((unsigned long)new_pgd, 2);
+	return NULL;
+
+no_pgd:
 	return NULL;
 }
 
@@ -142,59 +157,6 @@
 	free_pages((unsigned long) pgd, 2);
 }
 
-pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
-{
-	pte_t *pte;
-
-	pte = (pte_t *)get_page_2k(GFP_KERNEL);
-	if (pmd_none(*pmd)) {
-		if (pte) {
-			memzero(pte, 2 * PTRS_PER_PTE * sizeof(pte_t));
-			clean_cache_area(pte, PTRS_PER_PTE * sizeof(pte_t));
-			pte += PTRS_PER_PTE;
-			set_pmd(pmd, mk_user_pmd(pte));
-			return pte + offset;
-		}
-		set_pmd(pmd, mk_user_pmd(get_bad_pte_table()));
-		return NULL;
-	}
-	free_page_2k((unsigned long)pte);
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + offset;
-}
-
-pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset)
-{
-	pte_t *pte;
-
-	pte = (pte_t *)get_page_2k(GFP_KERNEL);
-	if (pmd_none(*pmd)) {
-		if (pte) {
-			memzero(pte, 2 * PTRS_PER_PTE * sizeof(pte_t));
-			clean_cache_area(pte, PTRS_PER_PTE * sizeof(pte_t));
-			pte += PTRS_PER_PTE;
-			set_pmd(pmd, mk_kernel_pmd(pte));
-			return pte + offset;
-		}
-		set_pmd(pmd, mk_kernel_pmd(get_bad_pte_table()));
-		return NULL;
-	}
-	free_page_2k((unsigned long)pte);
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd_kernel(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + offset;
-}
-
-void free_pte_slow(pte_t *pte)
-{
-	free_page_2k((unsigned long)(pte - PTRS_PER_PTE));
-}
-
 /*
  * Create a SECTION PGD between VIRT and PHYS in domain
  * DOMAIN with protection PROT
@@ -481,4 +443,42 @@
 
 	for (node = 0; node < numnodes; node++)
 		free_unused_memmap_node(node, mi);
+}
+
+/*
+ * PTE table allocation cache.
+ *
+ * This is a move away from our custom 2K page allocator.  We now use the
+ * slab cache to keep track of these objects.
+ *
+ * With this, it is questionable as to whether the PGT cache gains us
+ * anything.  We may be better off dropping the PTE stuff from our PGT
+ * cache implementation.
+ */
+kmem_cache_t *pte_cache;
+
+/*
+ * The constructor gets called for each object within the cache when the
+ * cache page is created.  Note that if slab tries to misalign the blocks,
+ * we BUG() loudly.
+ */
+static void pte_cache_ctor(void *pte, kmem_cache_t *cache, unsigned long flags)
+{
+	unsigned long block = (unsigned long)pte;
+
+	if (block & 2047)
+		BUG();
+
+	memzero(pte, 2 * PTRS_PER_PTE * sizeof(pte_t));
+	cpu_cache_clean_invalidate_range(block, block +
+			PTRS_PER_PTE * sizeof(pte_t), 0);
+}
+
+void __init pgtable_cache_init(void)
+{
+	pte_cache = kmem_cache_create("pte-cache",
+				2 * PTRS_PER_PTE * sizeof(pte_t), 0, 0,
+				pte_cache_ctor, NULL);
+	if (!pte_cache)
+		BUG();
 }
diff -urN linux-2.4.4-pre2/arch/arm/mm/mm-clps7500.c linux/arch/arm/mm/mm-clps7500.c
--- linux-2.4.4-pre2/arch/arm/mm/mm-clps7500.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/mm-clps7500.c	Mon Dec  3 22:34:49 2001
@@ -6,6 +6,7 @@
  *
  * Extra MM routines for CL7500 architecture
  */
+#include <linux/types.h>
 #include <linux/init.h>
 
 #include <asm/hardware.h>
diff -urN linux-2.4.4-pre2/arch/arm/mm/mm-ebsa110.c linux/arch/arm/mm/mm-ebsa110.c
--- linux-2.4.4-pre2/arch/arm/mm/mm-ebsa110.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/mm-ebsa110.c	Wed Dec 31 16:00:00 1969
@@ -1,30 +0,0 @@
-/*
- *  linux/arch/arm/mm/mm-ebsa110.c
- *
- *  Copyright (C) 1998-1999 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- *  Extra MM routines for the EBSA-110 architecture
- */
-#include <linux/mm.h>
-#include <linux/init.h>
-
-#include <asm/hardware.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-
-#include <asm/mach/map.h>
- 
-static struct map_desc ebsa110_io_desc[] __initdata = {
-	{ IO_BASE - PGDIR_SIZE, 0xc0000000, PGDIR_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
-	{ IO_BASE             , IO_START  , IO_SIZE   , DOMAIN_IO, 0, 1, 0, 0 },
-	LAST_DESC
-};
-
-void __init ebsa110_map_io(void)
-{
-	iotable_init(ebsa110_io_desc);
-}
diff -urN linux-2.4.4-pre2/arch/arm/mm/mm-footbridge.c linux/arch/arm/mm/mm-footbridge.c
--- linux-2.4.4-pre2/arch/arm/mm/mm-footbridge.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/mm-footbridge.c	Wed Dec 31 16:00:00 1969
@@ -1,117 +0,0 @@
-/*
- *  linux/arch/arm/mm/mm-footbridge.c
- *
- *  Copyright (C) 1998-2000 Russell King, Dave Gilbert.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- *  Extra MM routines for the EBSA285 architecture
- */
-#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/init.h>
- 
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <asm/io.h>
-#include <asm/hardware/dec21285.h>
-#include <asm/mach-types.h>
-
-#include <asm/mach/map.h>
-
-/*
- * Common mapping for all systems.  Note that the outbound write flush is
- * commented out since there is a "No Fix" problem with it.  Not mapping
- * it means that we have extra bullet protection on our feet.
- */
-static struct map_desc fb_common_io_desc[] __initdata = {
- { ARMCSR_BASE,	 DC21285_ARMCSR_BASE,	    ARMCSR_SIZE,  DOMAIN_IO, 0, 1, 0, 0 },
- { XBUS_BASE,    0x40000000,		    XBUS_SIZE,    DOMAIN_IO, 0, 1, 0, 0 },
- LAST_DESC
-};
-
-/*
- * The mapping when the footbridge is in host mode.  We don't map any of
- * this when we are in add-in mode.
- */
-static struct map_desc ebsa285_host_io_desc[] __initdata = {
-#if defined(CONFIG_ARCH_FOOTBRIDGE) && defined(CONFIG_FOOTBRIDGE_HOST)
- { PCIMEM_BASE,  DC21285_PCI_MEM,	    PCIMEM_SIZE,  DOMAIN_IO, 0, 1, 0, 0 },
- { PCICFG0_BASE, DC21285_PCI_TYPE_0_CONFIG, PCICFG0_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
- { PCICFG1_BASE, DC21285_PCI_TYPE_1_CONFIG, PCICFG1_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
- { PCIIACK_BASE, DC21285_PCI_IACK,	    PCIIACK_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
- { PCIO_BASE,    DC21285_PCI_IO,	    PCIO_SIZE,	  DOMAIN_IO, 0, 1, 0, 0 },
-#endif
- LAST_DESC
-};
-
-/*
- * The CO-ebsa285 mapping.
- */
-static struct map_desc co285_io_desc[] __initdata = {
-#ifdef CONFIG_ARCH_CO285
- { PCIO_BASE,	 DC21285_PCI_IO,	    PCIO_SIZE,    DOMAIN_IO, 0, 1, 0, 0 },
- { PCIMEM_BASE,	 DC21285_PCI_MEM,	    PCIMEM_SIZE,  DOMAIN_IO, 0, 1, 0, 0 },
-#endif
- LAST_DESC
-};
-
-void __init footbridge_map_io(void)
-{
-	struct map_desc *desc = NULL;
-
-	/*
-	 * Set up the common mapping first; we need this to
-	 * determine whether we're in host mode or not.
-	 */
-	iotable_init(fb_common_io_desc);
-
-	/*
-	 * Now, work out what we've got to map in addition on this
-	 * platform.
-	 */
-	if (machine_is_co285())
-		desc = co285_io_desc;
-	else if (footbridge_cfn_mode())
-		desc = ebsa285_host_io_desc;
-
-	if (desc)
-		iotable_init(desc);
-}
-
-#ifdef CONFIG_FOOTBRIDGE_ADDIN
-
-/*
- * These two functions convert virtual addresses to PCI addresses and PCI
- * addresses to virtual addresses.  Note that it is only legal to use these
- * on memory obtained via get_free_page or kmalloc.
- */
-unsigned long __virt_to_bus(unsigned long res)
-{
-#ifdef CONFIG_DEBUG_ERRORS
-	if (res < PAGE_OFFSET || res >= (unsigned long)high_memory) {
-		printk("__virt_to_bus: invalid virtual address 0x%08lx\n", res);
-		__backtrace();
-	}
-#endif
-	return (res - PAGE_OFFSET) + (*CSR_PCISDRAMBASE & 0xfffffff0);
-}
-
-unsigned long __bus_to_virt(unsigned long res)
-{
-	res -= (*CSR_PCISDRAMBASE & 0xfffffff0);
-	res += PAGE_OFFSET;
-
-#ifdef CONFIG_DEBUG_ERRORS
-	if (res < PAGE_OFFSET || res >= (unsigned long)high_memory) {
-		printk("__bus_to_virt: invalid virtual address 0x%08lx\n", res);
-		__backtrace();
-	}
-#endif
-	return res;
-}
-
-#endif
diff -urN linux-2.4.4-pre2/arch/arm/mm/mm-l7200.c linux/arch/arm/mm/mm-l7200.c
--- linux-2.4.4-pre2/arch/arm/mm/mm-l7200.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/mm-l7200.c	Mon Dec  3 22:34:49 2001
@@ -17,6 +17,9 @@
 static struct map_desc l7200_io_desc[] __initdata = {
 	{ IO_BASE,	IO_START,	IO_SIZE,	DOMAIN_IO, 0, 1 ,0 ,0},
 	{ IO_BASE_2,	IO_START_2,	IO_SIZE_2,	DOMAIN_IO, 0, 1 ,0 ,0},
+	{ AUX_BASE,     AUX_START,      AUX_SIZE,       DOMAIN_IO, 0, 1 ,0 ,0},
+	{ FLASH1_BASE,  FLASH1_START,   FLASH1_SIZE,    DOMAIN_IO, 0, 1 ,0 ,0},
+	{ FLASH2_BASE,  FLASH2_START,   FLASH2_SIZE,    DOMAIN_IO, 0, 1 ,0 ,0},
 	LAST_DESC
 };
 
diff -urN linux-2.4.4-pre2/arch/arm/mm/mm-sa1100.c linux/arch/arm/mm/mm-sa1100.c
--- linux-2.4.4-pre2/arch/arm/mm/mm-sa1100.c	Fri Mar  2 11:12:06 2001
+++ linux/arch/arm/mm/mm-sa1100.c	Mon Dec  3 22:34:49 2001
@@ -15,9 +15,8 @@
  *
  */
 #include <linux/config.h>
-#include <linux/mm.h>
+#include <linux/sched.h>
 #include <linux/init.h>
-#include <linux/bootmem.h>
 
 #include <asm/hardware.h>
 #include <asm/pgtable.h>
@@ -109,22 +108,9 @@
   LAST_DESC
 };
 
-static struct map_desc thinclient_io_desc[] __initdata = {
-#ifdef CONFIG_SA1100_THINCLIENT
-#if 0
-  { 0xe8000000, 0x00000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 when JP1 2-4 */
-#else
-  { 0xe8000000, 0x08000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 when JP1 3-4 */
-#endif
-  { 0xf0000000, 0x10000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* CPLD */
-#endif
-  LAST_DESC
-};
-
-static struct map_desc tifon_io_desc[] __initdata = {
-#ifdef CONFIG_SA1100_TIFON
-  { 0xe8000000, 0x00000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */
-  { 0xe8800000, 0x08000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 2 */
+static struct map_desc sherman_io_desc[] __initdata = {
+#ifdef CONFIG_SA1100_SHERMAN
+  { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash*/
 #endif
   LAST_DESC
 };
@@ -138,9 +124,16 @@
 
 static struct map_desc xp860_io_desc[] __initdata = {
 #ifdef CONFIG_SA1100_XP860
-  { 0xf4000000, 0x40000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */
   { 0xf0000000, 0x10000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* SCSI */
   { 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 1, 1, 0, 0 }, /* LAN */
+  { 0xf4000000, 0x40000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA-1111 */
+#endif
+  LAST_DESC
+};
+
+static struct map_desc pangolin_io_desc[] __initdata = {
+#ifdef CONFIG_SA1100_PANGOLIN
+  { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */
 #endif
   LAST_DESC
 };
@@ -153,8 +146,6 @@
 
 	if (machine_is_assabet())
 		desc = assabet_io_desc;
-	else if (machine_is_nanoengine())
-		desc = nanoengine_io_desc;
 	else if (machine_is_bitsy())
 		desc = bitsy_io_desc;
 	else if (machine_is_cerf())
@@ -165,38 +156,21 @@
 		desc = graphicsclient_io_desc;
 	else if (machine_is_lart())
 	        desc = lart_io_desc;
-	else if (machine_is_thinclient())
-		desc = thinclient_io_desc;
-	else if (machine_is_tifon())
-		desc = tifon_io_desc;
+	else if (machine_is_nanoengine())
+		desc = nanoengine_io_desc;
+	else if (machine_is_sherman())
+		desc = sherman_io_desc;
 	else if (machine_is_victor())
 		desc = victor_io_desc;
 	else if (machine_is_xp860())
 		desc = xp860_io_desc;
+	else if (machine_is_pangolin())
+		desc = pangolin_io_desc;
 
 	if (desc)
 		iotable_init(desc);
 }
 
-
-#ifdef CONFIG_DISCONTIGMEM
-
-/*
- * Our node_data structure for discontigous memory.
- * There is 4 possible nodes i.e. the 4 SA1100 RAM banks.
- */
-
-static bootmem_data_t node_bootmem_data[4];
-
-pg_data_t sa1100_node_data[4] =
-{ { bdata: &node_bootmem_data[0] },
-  { bdata: &node_bootmem_data[1] },
-  { bdata: &node_bootmem_data[2] },
-  { bdata: &node_bootmem_data[3] } };
-
-#endif
-
-  
 /*
  * On Assabet, we must probe for the Neponset board *before* paging_init() 
  * has occurred to actually determine the amount of RAM available.  To do so, 
diff -urN linux-2.4.4-pre2/arch/arm/mm/proc-sa110.S linux/arch/arm/mm/proc-sa110.S
--- linux-2.4.4-pre2/arch/arm/mm/proc-sa110.S	Tue Mar  6 19:44:35 2001
+++ linux/arch/arm/mm/proc-sa110.S	Mon Dec  3 22:34:49 2001
@@ -556,7 +556,7 @@
 	mov	pc, lr
 
 /*
- * cpu_sa110_arm920_set_pte(ptep, pte)
+ * cpu_sa110_set_pte(ptep, pte)
  *
  * Set a PTE and flush it out
  */
@@ -737,7 +737,7 @@
 	b	__sa110_setup
 	.long	cpu_arch_name
 	.long	cpu_elf_name
-	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT
+	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT
 	.long	cpu_sa110_info
 	.long	sa110_processor_functions
 	.size	__sa110_proc_info, . - __sa110_proc_info
@@ -750,7 +750,7 @@
 	b	__sa1100_setup
 	.long	cpu_arch_name
 	.long	cpu_elf_name
-	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT
+	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT
 	.long	cpu_sa1100_info
 	.long	sa1100_processor_functions
 	.size	__sa1100_proc_info, . - __sa1100_proc_info
@@ -763,7 +763,7 @@
 	b	__sa1100_setup
 	.long	cpu_arch_name
 	.long	cpu_elf_name
-	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT
+	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT
 	.long	cpu_sa1110_info
 	.long	sa1100_processor_functions
 	.size	__sa1110_proc_info, . - __sa1110_proc_info
diff -urN linux-2.4.4-pre2/arch/arm/mm/small_page.c linux/arch/arm/mm/small_page.c
--- linux-2.4.4-pre2/arch/arm/mm/small_page.c	Mon Sep 18 15:15:25 2000
+++ linux/arch/arm/mm/small_page.c	Mon Dec  3 22:34:49 2001
@@ -202,17 +202,6 @@
 	printk("Trying to free free small page from %p\n", __builtin_return_address(0));
 }
 
-unsigned long get_page_2k(int priority)
-{
-	return __get_small_page(priority, orders+0);
-}
-
-void free_page_2k(unsigned long spage)
-{
-	__free_small_page(spage, orders+0);
-}
-
-#if PAGE_SIZE > 8192
 unsigned long get_page_8k(int priority)
 {
 	return __get_small_page(priority, orders+1);
@@ -222,4 +211,3 @@
 {
 	__free_small_page(spage, orders+1);
 }
-#endif
diff -urN linux-2.4.4-pre2/arch/i386/Makefile linux/arch/i386/Makefile
--- linux-2.4.4-pre2/arch/i386/Makefile	Wed Jan 10 15:06:14 2001
+++ linux/arch/i386/Makefile	Mon Dec  3 22:34:49 2001
@@ -82,6 +82,10 @@
 CFLAGS += -march=i586
 endif
 
+ifdef CONFIG_MCYRIXIII
+CFLAGS += -march=i586
+endif
+
 HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
 
 SUBDIRS += arch/i386/kernel arch/i386/mm arch/i386/lib
diff -urN linux-2.4.4-pre2/arch/i386/config.in linux/arch/i386/config.in
--- linux-2.4.4-pre2/arch/i386/config.in	Mon Dec  3 22:34:41 2001
+++ linux/arch/i386/config.in	Mon Dec  3 22:34:49 2001
@@ -36,11 +36,12 @@
 	 Pentium-III			CONFIG_MPENTIUMIII \
 	 Pentium-4			CONFIG_MPENTIUM4 \
 	 K6/K6-II/K6-III		CONFIG_MK6 \
-	 Athlon/K7			CONFIG_MK7 \
+	 Athlon/Duron/K7		CONFIG_MK7 \
 	 Crusoe				CONFIG_MCRUSOE \
 	 Winchip-C6			CONFIG_MWINCHIPC6 \
 	 Winchip-2			CONFIG_MWINCHIP2 \
-	 Winchip-2A/Winchip-3		CONFIG_MWINCHIP3D" Pentium-Pro
+	 Winchip-2A/Winchip-3		CONFIG_MWINCHIP3D \
+	 CyrixIII/C3			CONFIG_MCYRIXIII" Pentium-Pro
 #
 # Define implied options from the CPU selection here
 #
@@ -113,6 +114,13 @@
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_USE_3DNOW y
    define_bool CONFIG_X86_PGE y
+   define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+fi
+if [ "$CONFIG_MCYRIXIII" = "y" ]; then
+   define_int  CONFIG_X86_L1_CACHE_SHIFT 5
+   define_bool CONFIG_X86_TSC y
+   define_bool CONFIG_X86_ALIGNMENT_16 y
+   define_bool CONFIG_X86_USE_3DNOW y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
 fi
 if [ "$CONFIG_MCRUSOE" = "y" ]; then
diff -urN linux-2.4.4-pre2/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.4.4-pre2/arch/i386/defconfig	Fri Mar 23 16:05:44 2001
+++ linux/arch/i386/defconfig	Mon Dec  3 22:34:49 2001
@@ -35,9 +35,11 @@
 # CONFIG_MWINCHIPC6 is not set
 # CONFIG_MWINCHIP2 is not set
 # CONFIG_MWINCHIP3D is not set
+# CONFIG_MCYRIXIII is not set
 CONFIG_X86_WP_WORKS_OK=y
 CONFIG_X86_INVLPG=y
 CONFIG_X86_CMPXCHG=y
+CONFIG_X86_XADD=y
 CONFIG_X86_BSWAP=y
 CONFIG_X86_POPAD_OK=y
 CONFIG_X86_L1_CACHE_SHIFT=5
@@ -383,6 +385,9 @@
 # CONFIG_NE3210 is not set
 # CONFIG_ES3210 is not set
 # CONFIG_8139TOO is not set
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
 # CONFIG_SIS900 is not set
 # CONFIG_EPIC100 is not set
 # CONFIG_SUNDANCE is not set
@@ -561,6 +566,7 @@
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
 # CONFIG_CRAMFS is not set
+CONFIG_TMPFS=y
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
diff -urN linux-2.4.4-pre2/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.4-pre2/arch/i386/kernel/i386_ksyms.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/i386/kernel/i386_ksyms.c	Mon Dec  3 22:34:49 2001
@@ -80,8 +80,8 @@
 EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__down_failed_trylock);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
-EXPORT_SYMBOL_NOVERS(__down_write_failed);
-EXPORT_SYMBOL_NOVERS(__down_read_failed);
+EXPORT_SYMBOL_NOVERS(__rwsem_down_write_failed);
+EXPORT_SYMBOL_NOVERS(__rwsem_down_read_failed);
 EXPORT_SYMBOL_NOVERS(__rwsem_wake);
 /* Networking helper routines. */
 EXPORT_SYMBOL(csum_partial_copy_generic);
diff -urN linux-2.4.4-pre2/arch/i386/kernel/pci-pc.c linux/arch/i386/kernel/pci-pc.c
--- linux-2.4.4-pre2/arch/i386/kernel/pci-pc.c	Thu Mar 29 11:24:17 2001
+++ linux/arch/i386/kernel/pci-pc.c	Mon Dec  3 22:34:49 2001
@@ -843,6 +843,8 @@
 	pcibios_last_bus = -1;
 }
 
+#if 0
+/* Until we get proper handling pray the BIOS gets it right */
 /*
  * ServerWorks host bridges -- Find and scan all secondary buses.
  * Register 0x44 contains first, 0x45 last bus number routed there.
@@ -860,6 +862,7 @@
 		printk("PCI: ServerWorks host bridge: last bus %02x\n", pcibios_last_bus);
 	}
 }
+#endif
 
 #if 0
 /* Our bus code shouldnt need this fixup any more. Delete once verified */
diff -urN linux-2.4.4-pre2/arch/i386/kernel/semaphore.c linux/arch/i386/kernel/semaphore.c
--- linux-2.4.4-pre2/arch/i386/kernel/semaphore.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/i386/kernel/semaphore.c	Mon Dec  3 22:34:49 2001
@@ -233,291 +233,6 @@
 	"ret"
 );
 
-asm(
-"
-.text
-.align 4
-.globl __down_read_failed
-__down_read_failed:
-	pushl	%edx
-	pushl	%ecx
-	call	down_read_failed
-	popl	%ecx
-	popl	%edx
-	ret
-"
-);
-
-asm(
-"
-.text
-.align 4
-.globl __down_write_failed
-__down_write_failed:
-	pushl	%edx
-	pushl	%ecx
-	call	down_write_failed
-	popl	%ecx
-	popl	%edx
-	ret
-"
-);
-
-asm(
-"
-.text
-.align 4
-.globl __rwsem_wake
-__rwsem_wake:
-	pushl	%edx
-	pushl	%ecx
-	call	rwsem_wake
-	popl	%ecx
-	popl	%edx
-	ret
-"
-);
-
-struct rw_semaphore *FASTCALL(rwsem_wake(struct rw_semaphore *sem));
-struct rw_semaphore *FASTCALL(down_read_failed(struct rw_semaphore *sem));
-struct rw_semaphore *FASTCALL(down_write_failed(struct rw_semaphore *sem));
-
-/*
- * implement exchange and add functionality
- */
-static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
-{
-	int tmp = delta;
-
-#ifndef CONFIG_USING_SPINLOCK_BASED_RWSEM
-	__asm__ __volatile__(
-		LOCK_PREFIX "xadd %0,(%1)"
-		: "+r"(tmp)
-		: "r"(sem)
-		: "memory");
-
-#else
-
-	__asm__ __volatile__(
-		"# beginning rwsem_atomic_update\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  xchgl     %0,(%1)\n\t" /* retrieve the old value */
-		"  addl      %0,(%1)\n\t" /* add 0xffff0001, result in memory */
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* release the spinlock */
-#endif
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		".previous\n"
-		"# ending rwsem_atomic_update\n\t"
-		: "+r"(tmp)
-		: "r"(sem)
-		: "memory");
-
-#endif
-	return tmp+delta;
-}
-
-/*
- * implement compare and exchange functionality on the rw-semaphore count LSW
- */
-static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
-{
-#ifndef CONFIG_USING_SPINLOCK_BASED_RWSEM
-	return cmpxchg((__u16*)&sem->count.counter,0,RWSEM_ACTIVE_BIAS);
-
-#else
-	__u16 prev;
-
-	__asm__ __volatile__(
-		"# beginning rwsem_cmpxchgw\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* try to grab the spinlock */
-		"  js        3f\n" /* jump if failed */
-		"1:\n\t"
-#endif
-		"  cmpw      %w1,(%3)\n\t"
-		"  jne       4f\n\t" /* jump if old doesn't match sem->count LSW */
-		"  movw      %w2,(%3)\n\t" /* replace sem->count LSW with the new value */
-		"2:\n\t"
-#ifdef CONFIG_SMP
-		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* release the spinlock */
-#endif
-		".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
-		"3:\n\t" /* spin on the spinlock till we get it */
-		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t"
-		"  rep;nop   \n\t"
-		"  jle       3b\n\t"
-		"  jmp       1b\n"
-#endif
-		"4:\n\t"
-		"  movw      (%3),%w0\n" /* we'll want to return the current value */
-		"  jmp       2b\n"
-		".previous\n"
-		"# ending rwsem_cmpxchgw\n\t"
-		: "=r"(prev)
-		: "r0"(old), "r"(new), "r"(sem)
-		: "memory");
-
-	return prev;
-#endif
-}
-
-/*
- * wait for the read lock to be granted
- * - need to repeal the increment made inline by the caller
- * - need to throw a write-lock style spanner into the works (sub 0x00010000 from count)
- */
-struct rw_semaphore *down_read_failed(struct rw_semaphore *sem)
-{
-	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait,tsk);
-	int count;
-
-	rwsemdebug("[%d] Entering down_read_failed(%08x)\n",current->pid,atomic_read(&sem->count));
-
-	/* this waitqueue context flag will be cleared when we are granted the lock */
-	__set_bit(RWSEM_WAITING_FOR_READ,&wait.flags);
-	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
-
-	add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
-
-	/* note that we're now waiting on the lock, but no longer actively read-locking */
-	count = rwsem_atomic_update(RWSEM_WAITING_BIAS-RWSEM_ACTIVE_BIAS,sem);
-	rwsemdebug("X(%08x)\n",count);
-
-	/* if there are no longer active locks, wake the front queued process(es) up
-	 * - it might even be this process, since the waker takes a more active part
-	 */
-	if (!(count & RWSEM_ACTIVE_MASK))
-		rwsem_wake(sem);
-
-	/* wait to be given the lock */
-	for (;;) {
-		if (!test_bit(RWSEM_WAITING_FOR_READ,&wait.flags))
-			break;
-		schedule();
-		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
-	}
-
-	remove_wait_queue(&sem->wait,&wait);
-	tsk->state = TASK_RUNNING;
-
-	rwsemdebug("[%d] Leaving down_read_failed(%08x)\n",current->pid,atomic_read(&sem->count));
-
-	return sem;
-}
-
-/*
- * wait for the write lock to be granted
- */
-struct rw_semaphore *down_write_failed(struct rw_semaphore *sem)
-{
-	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait,tsk);
-	int count;
-
-	rwsemdebug("[%d] Entering down_write_failed(%08x)\n",
-		   current->pid,atomic_read(&sem->count));
-
-	/* this waitqueue context flag will be cleared when we are granted the lock */
-	__set_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags);
-	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
-
-	add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
-
-	/* note that we're waiting on the lock, but no longer actively locking */
-	count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
-	rwsemdebug("[%d] updated(%08x)\n",current->pid,count);
-
-	/* if there are no longer active locks, wake the front queued process(es) up
-	 * - it might even be this process, since the waker takes a more active part
-	 */
-	if (!(count & RWSEM_ACTIVE_MASK))
-		rwsem_wake(sem);
-
-	/* wait to be given the lock */
-	for (;;) {
-		if (!test_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags))
-			break;
-		schedule();
-		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
-	}
-
-	remove_wait_queue(&sem->wait,&wait);
-	tsk->state = TASK_RUNNING;
-
-	rwsemdebug("[%d] Leaving down_write_failed(%08x)\n",current->pid,atomic_read(&sem->count));
-
-	return sem;
-}
-
-/*
- * handle the lock being released whilst there are processes blocked on it that can now run
- * - if we come here, then:
- *   - the 'active part' of the count (&0x0000ffff) reached zero (but may no longer be zero)
- *   - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so)
- */
-struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
-{
-	int woken, count;
-
-	rwsemdebug("[%d] Entering rwsem_wake(%08x)\n",current->pid,atomic_read(&sem->count));
-
- try_again:
-	/* try to grab an 'activity' marker
-	 * - need to make sure two copies of rwsem_wake() don't do this for two separate processes
-	 *   simultaneously
-	 * - be horribly naughty, and only deal with the LSW of the atomic counter
-	 */
-	if (rwsem_cmpxchgw(sem,0,RWSEM_ACTIVE_BIAS)!=0) {
-		rwsemdebug("[%d] rwsem_wake: abort wakeup due to renewed activity\n",current->pid);
-		goto out;
-	}
-
-	/* try to grant a single write lock if there's a writer at the front of the queue
-	 * - note we leave the 'active part' of the count incremented by 1 and the waiting part
-	 *   incremented by 0x00010000
-	 */
-	if (wake_up_ctx(&sem->wait,1,-RWSEM_WAITING_FOR_WRITE)==1)
-		goto out;
-
-	/* grant an infinite number of read locks to the readers at the front of the queue
-	 * - note we increment the 'active part' of the count by the number of readers just woken,
-	 *   less one for the activity decrement we've already done
-	 */
-	woken = wake_up_ctx(&sem->wait,65535,-RWSEM_WAITING_FOR_READ);
-	if (woken<=0)
-		goto counter_correction;
-
-	woken *= RWSEM_ACTIVE_BIAS-RWSEM_WAITING_BIAS;
-	woken -= RWSEM_ACTIVE_BIAS;
-	rwsem_atomic_update(woken,sem);
-
- out:
-	rwsemdebug("[%d] Leaving rwsem_wake(%08x)\n",current->pid,atomic_read(&sem->count));
-	return sem;
-
-	/* come here if we need to correct the counter for odd SMP-isms */
- counter_correction:
-	count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
-	rwsemdebug("[%d] corrected(%08x)\n",current->pid,count);
-	if (!(count & RWSEM_ACTIVE_MASK))
-		goto try_again;
-	goto out;
-}
-
 /*
  * rw spinlock fallbacks
  */
diff -urN linux-2.4.4-pre2/arch/i386/lib/Makefile linux/arch/i386/lib/Makefile
--- linux-2.4.4-pre2/arch/i386/lib/Makefile	Mon Dec  3 22:34:41 2001
+++ linux/arch/i386/lib/Makefile	Mon Dec  3 22:34:49 2001
@@ -9,7 +9,7 @@
 
 obj-y = checksum.o old-checksum.o delay.o \
 	usercopy.o getuser.o putuser.o \
-	memcpy.o strstr.o
+	memcpy.o strstr.o rwsem.o
 
 obj-$(CONFIG_X86_USE_3DNOW) += mmx.o
 obj-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o
diff -urN linux-2.4.4-pre2/arch/i386/lib/rwsem.S linux/arch/i386/lib/rwsem.S
--- linux-2.4.4-pre2/arch/i386/lib/rwsem.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/lib/rwsem.S	Mon Dec  3 22:34:49 2001
@@ -0,0 +1,36 @@
+/* rwsem.S: R/W semaphores, register saving wrapper function stubs
+ *
+ * Written by David Howells (dhowells@redhat.com).
+ * Derived from arch/i386/kernel/semaphore.c
+ */
+
+.text
+.align 4
+.globl __rwsem_down_read_failed
+__rwsem_down_read_failed:
+	pushl	%edx
+	pushl	%ecx
+	call	rwsem_down_read_failed
+	popl	%ecx
+	popl	%edx
+	ret
+
+.align 4
+.globl __rwsem_down_write_failed
+__rwsem_down_write_failed:
+	pushl	%edx
+	pushl	%ecx
+	call	rwsem_down_write_failed
+	popl	%ecx
+	popl	%edx
+	ret
+
+.align 4
+.globl __rwsem_wake
+__rwsem_wake:
+	pushl	%edx
+	pushl	%ecx
+	call	rwsem_wake
+	popl	%ecx
+	popl	%edx
+	ret
diff -urN linux-2.4.4-pre2/arch/ia64/kernel/efi.c linux/arch/ia64/kernel/efi.c
--- linux-2.4.4-pre2/arch/ia64/kernel/efi.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/kernel/efi.c	Mon Dec  3 22:34:50 2001
@@ -18,7 +18,6 @@
  * Goutham Rao: <goutham.rao@intel.com>
  *	Skip non-WB memory and ignore empty memory ranges.
  */
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/types.h>
diff -urN linux-2.4.4-pre2/arch/ia64/kernel/mca.c linux/arch/ia64/kernel/mca.c
--- linux-2.4.4-pre2/arch/ia64/kernel/mca.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/kernel/mca.c	Mon Dec  3 22:34:50 2001
@@ -27,7 +27,6 @@
 #include <asm/mca.h>
 
 #include <asm/irq.h>
-#include <asm/machvec.h>
 
 
 typedef struct ia64_fptr {
diff -urN linux-2.4.4-pre2/arch/ia64/kernel/perfmon.c linux/arch/ia64/kernel/perfmon.c
--- linux-2.4.4-pre2/arch/ia64/kernel/perfmon.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/kernel/perfmon.c	Mon Dec  3 22:34:50 2001
@@ -32,7 +32,6 @@
 #include <asm/processor.h>
 #include <asm/signal.h>
 #include <asm/system.h>
-#include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/delay.h> /* for ia64_get_itc() */
 
@@ -468,7 +467,7 @@
 	if (size > current->rlim[RLIMIT_MEMLOCK].rlim_cur) return -EAGAIN;
 
 	/* find some free area in address space */
-	addr = get_unmapped_area(0, size);
+	addr = get_unmapped_area(NULL, 0, size, 0, 0);
 	if (!addr) goto no_addr;
 
 	DBprintk((" entries=%ld aligned size=%ld, unmapped @0x%lx\n", entries, size, addr));
diff -urN linux-2.4.4-pre2/arch/ia64/kernel/sys_ia64.c linux/arch/ia64/kernel/sys_ia64.c
--- linux-2.4.4-pre2/arch/ia64/kernel/sys_ia64.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/kernel/sys_ia64.c	Mon Dec  3 22:34:50 2001
@@ -22,17 +22,16 @@
 #define COLOR_ALIGN(addr)	(((addr) + SHMLBA - 1) & ~(SHMLBA - 1))
 
 unsigned long
-get_unmapped_area (unsigned long addr, unsigned long len)
+arch_get_unmapped_area (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
 	struct vm_area_struct * vmm;
-	long map_shared = (current->thread.flags & IA64_THREAD_MAP_SHARED) != 0;
 
 	if (len > RGN_MAP_LIMIT)
-		return 0;
+		return -ENOMEM;
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
-	if (map_shared)
+	if (flags & MAP_SHARED)
 		addr = COLOR_ALIGN(addr);
 	else
 		addr = PAGE_ALIGN(addr);
@@ -40,13 +39,13 @@
 	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
 		/* At this point:  (!vmm || addr < vmm->vm_end). */
 		if (TASK_SIZE - len < addr)
-			return 0;
+			return -ENOMEM;
 		if (rgn_offset(addr) + len > RGN_MAP_LIMIT)	/* no risk of overflow here... */
-			return 0;
+			return -ENOMEM;
 		if (!vmm || addr + len <= vmm->vm_start)
 			return addr;
 		addr = vmm->vm_end;
-		if (map_shared)
+		if (flags & MAP_SHARED)
 			addr = COLOR_ALIGN(addr);
 	}
 }
@@ -200,14 +199,9 @@
 			return -EBADF;
 	}
 
-	if (flags & MAP_SHARED)
-		current->thread.flags |= IA64_THREAD_MAP_SHARED;
-
 	down_write(&current->mm->mmap_sem);
 	addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 	up_write(&current->mm->mmap_sem);
-
-	current->thread.flags &= ~IA64_THREAD_MAP_SHARED;
 
 	if (file)
 		fput(file);
diff -urN linux-2.4.4-pre2/arch/ia64/kernel/unwind.c linux/arch/ia64/kernel/unwind.c
--- linux-2.4.4-pre2/arch/ia64/kernel/unwind.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/kernel/unwind.c	Mon Dec  3 22:34:50 2001
@@ -24,7 +24,6 @@
  *	o if both the unw.lock spinlock and a script's read-write lock must be
  *	  acquired, then the read-write lock must be acquired first.
  */
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/fprom/fw-emu.c linux/arch/ia64/sn/fprom/fw-emu.c
--- linux-2.4.4-pre2/arch/ia64/sn/fprom/fw-emu.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/fprom/fw-emu.c	Mon Dec  3 22:34:50 2001
@@ -8,7 +8,6 @@
  * Copyright (C) 2000 Silicon Graphics, Inc.
  * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com)
  */
-#include <linux/config.h>
 
 #include <asm/efi.h>
 #include <asm/pal.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/io.c linux/arch/ia64/sn/io/io.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/io.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/io.c	Mon Dec  3 22:34:50 2001
@@ -27,7 +27,6 @@
 #include <asm/sn/intr.h>
 #include <asm/sn/xtalk/xtalkaddrs.h>
 #include <asm/sn/klconfig.h>
-#include <asm/sn/xtalk/xwidget.h>
 #include <asm/sn/io.h>
 #include <asm/sn/sn_cpuid.h>
 
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/klgraph_hack.c linux/arch/ia64/sn/io/klgraph_hack.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/klgraph_hack.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/klgraph_hack.c	Mon Dec  3 22:34:50 2001
@@ -15,7 +15,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/config.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <asm/sn/sgi.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/mem_refcnt.c linux/arch/ia64/sn/io/mem_refcnt.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/mem_refcnt.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/mem_refcnt.c	Mon Dec  3 22:34:50 2001
@@ -9,7 +9,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/config.h>
 #include <asm/sn/sgi.h>
 #include <asm/sn/invent.h>
 #include <asm/sn/hcl.h>
@@ -17,7 +16,6 @@
 #include <asm/sn/nodepda.h>
 #include <asm/sn/hubspc.h>
 #include <asm/sn/iograph.h>
-#include <asm/sn/invent.h>
 #include <asm/sn/mem_refcnt.h>
 #include <asm/sn/hwcntrs.h>
 // From numa_hw.h
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/ml_SN_intr.c linux/arch/ia64/sn/io/ml_SN_intr.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/ml_SN_intr.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/ml_SN_intr.c	Mon Dec  3 22:34:50 2001
@@ -37,13 +37,6 @@
 #include <asm/sn/intr.h>
 
 
-#if defined (CONFIG_SGI_IP35)
-
-#include <asm/sn/pci/pciio.h>		/* For SN1 + pcibr Addressing Limitation */
-#include <asm/sn/pci/pcibr.h>		/* For SN1 + pcibr Addressing Limitation */
-#include <asm/sn/pci/pcibr_private.h>	/* For SN1 + pcibr Addressing Limitation */
-#endif /* SN1 */
-
 #if DEBUG_INTR_TSTAMP_DEBUG
 #include <sys/debug.h>
 #include <sys/idbg.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/ml_iograph.c linux/arch/ia64/sn/io/ml_iograph.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/ml_iograph.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/ml_iograph.c	Mon Dec  3 22:34:50 2001
@@ -20,7 +20,6 @@
 #include <asm/sn/labelcl.h>
 #include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/pci/bridge.h>
-#include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/klconfig.h>
 #include <asm/sn/eeprom.h>
 #include <asm/sn/sn_private.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/module.c linux/arch/ia64/sn/io/module.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/module.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/module.c	Mon Dec  3 22:34:50 2001
@@ -9,7 +9,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/config.h>
 #include <linux/slab.h>
 #include <asm/sn/sgi.h>
 #include <asm/sn/invent.h>
@@ -17,7 +16,6 @@
 #include <asm/sn/labelcl.h>
 #include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/pci/bridge.h>
-#include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/klconfig.h>
 #include <asm/sn/sn1/hubdev.h>
 #include <asm/sn/module.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/pci_bus_cvlink.c linux/arch/ia64/sn/io/pci_bus_cvlink.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/pci_bus_cvlink.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/pci_bus_cvlink.c	Mon Dec  3 22:34:50 2001
@@ -11,7 +11,6 @@
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/pci.h>
-#include <linux/pci_ids.h>
 #include <linux/sched.h>
 #include <linux/ioport.h>
 #include <asm/sn/types.h>
@@ -31,7 +30,6 @@
 #include <asm/sn/intr.h>
 #include <asm/sn/xtalk/xtalkaddrs.h>
 #include <asm/sn/klconfig.h>
-#include <asm/sn/xtalk/xwidget.h>
 #include <asm/sn/io.h>
 
 #include <asm/sn/pci/pciio.h>
@@ -39,7 +37,6 @@
 #include <asm/sn/pci/pcibr.h>
 #include <asm/sn/pci/pcibr_private.h>
 extern int bridge_rev_b_data_check_disable;
-#include <asm/sn/pci/pciio.h>
 #include <asm/sn/pci/pci_bus_cvlink.h>
 
 #define MAX_PCI_XWIDGET 256
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/pci_dma.c linux/arch/ia64/sn/io/pci_dma.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/pci_dma.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/pci_dma.c	Mon Dec  3 22:34:50 2001
@@ -30,11 +30,6 @@
 #include <asm/sn/pci/pcibr_private.h>
 #include <asm/sn/iobus.h>
 #include <asm/sn/types.h>
-#include <asm/sn/sgi.h>
-#include <asm/sn/invent.h>
-#include <asm/sn/hcl.h>
-#include <asm/sn/pci/pcibr.h>
-#include <asm/sn/pci/pcibr_private.h>
 #include <asm/sn/alenlist.h>
 #include <asm/sn/pci/pci_bus_cvlink.h>
 
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/pciba.c linux/arch/ia64/sn/io/pciba.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/pciba.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/pciba.c	Mon Dec  3 22:34:50 2001
@@ -25,10 +25,8 @@
 #include <asm/sn/pci/pcibr.h>
 #include <asm/sn/pci/pcibr_private.h>
 #include <asm/sn/pci/pci_defs.h>
-#include <asm/sn/pci/bridge.h>
 #include <asm/sn/prio.h>
 #include <asm/sn/ioerror_handling.h>
-#include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/ioc3.h>
 #include <asm/sn/eeprom.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/pcibr.c linux/arch/ia64/sn/io/pcibr.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/pcibr.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/pcibr.c	Mon Dec  3 22:34:50 2001
@@ -29,10 +29,8 @@
 #include <asm/sn/pci/pcibr.h>
 #include <asm/sn/pci/pcibr_private.h>
 #include <asm/sn/pci/pci_defs.h>
-#include <asm/sn/pci/bridge.h>
 #include <asm/sn/prio.h>
 #include <asm/sn/ioerror_handling.h>
-#include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/xtalk/xbow.h>
 #include <asm/sn/ioc3.h>
 #include <asm/sn/eeprom.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/xbow.c linux/arch/ia64/sn/io/xbow.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/xbow.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/xbow.c	Mon Dec  3 22:34:50 2001
@@ -9,7 +9,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/config.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <asm/sn/sgi.h>
diff -urN linux-2.4.4-pre2/arch/ia64/sn/io/xtalk.c linux/arch/ia64/sn/io/xtalk.c
--- linux-2.4.4-pre2/arch/ia64/sn/io/xtalk.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/ia64/sn/io/xtalk.c	Mon Dec  3 22:34:50 2001
@@ -9,7 +9,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/config.h>
 #include <linux/slab.h>
 #include <asm/sn/sgi.h>
 #include <asm/sn/iobus.h>
diff -urN linux-2.4.4-pre2/arch/s390/math-emu/math.c linux/arch/s390/math-emu/math.c
--- linux-2.4.4-pre2/arch/s390/math-emu/math.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/s390/math-emu/math.c	Mon Dec  3 22:34:50 2001
@@ -9,6 +9,7 @@
  *          that does not have the IEEE fpu (all processors before G5).
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
diff -urN linux-2.4.4-pre2/arch/sh/kernel/Makefile linux/arch/sh/kernel/Makefile
--- linux-2.4.4-pre2/arch/sh/kernel/Makefile	Thu Jan  4 13:19:13 2001
+++ linux/arch/sh/kernel/Makefile	Mon Dec  3 22:34:50 2001
@@ -20,16 +20,23 @@
 
 obj-$(CONFIG_CF_ENABLER)	+= cf-enabler.o
 obj-$(CONFIG_CPU_SH4)		+= fpu.o
-obj-$(CONFIG_PCI)		+= pci-sh.o 
 obj-$(CONFIG_SH_RTC)            += rtc.o
 obj-$(CONFIG_SH_STANDARD_BIOS)	+= sh_bios.o
 
+ifeq ($(CONFIG_PCI),y)
+obj-y				+= pci-sh.o 
+obj-$(CONFIG_CPU_SUBTYPE_ST40STB1)+= pci_st40.o
+endif
+
 obj-$(CONFIG_SH_HP600)		+= mach_hp600.o
 machine-specific-objs		+= mach_hp600.o
 
 obj-$(CONFIG_SH_SOLUTION_ENGINE)+= mach_se.o setup_se.o io_se.o led_se.o
 machine-specific-objs		+= mach_se.o setup_se.o io_se.o led_se.o
 
+obj-$(CONFIG_SH_CAT68701) 	+= mach_cat68701.o io_cat68701.o
+machine-specific-objs		+= mach_cat68701.o io_cat68701.o
+
 obj-$(CONFIG_SH_CQREEK)		+= setup_cqreek.o
 machine-specific-objs		+= setup_cqreek.o
 
@@ -39,9 +46,13 @@
 obj-$(CONFIG_HD64461)		+= setup_hd64461.o io_hd64461.o
 machine-specific-objs		+= setup_hd64461.o io_hd64461.o
 
+obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) +=irq_intc2.o
+
 # Doesn't compile well, so don't include in machine-specific-objs
-obj-$(CONFIG_HD64465)		+= setup_hd64465.o io_hd64465.o
-obj-$(CONFIG_SH_FOOBAR)		+= mach_foobar.o
+obj-$(CONFIG_HD64465)		+= setup_hd64465.o io_hd64465.o hd64465_gpio.o
+obj-$(CONFIG_SH_DMIDA)		+= mach_dmida.o
+obj-$(CONFIG_SH_EC3104)         += setup_ec3104.o io_ec3104.o mach_ec3104.o
+obj-$(CONFIG_SH_DREAMCAST)	+= mach_dc.o setup_dc.o io_dc.o
 
 ifeq ($(CONFIG_SH_GENERIC),y)
 obj-y		+= $(machine-specific-objs)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/cf-enabler.c linux/arch/sh/kernel/cf-enabler.c
--- linux-2.4.4-pre2/arch/sh/kernel/cf-enabler.c	Thu Aug 10 13:03:25 2000
+++ linux/arch/sh/kernel/cf-enabler.c	Mon Dec  3 22:34:50 2001
@@ -8,13 +8,41 @@
  *  Enable the CF configuration.
  */
 
+#include <linux/config.h>
 #include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 
-#include <asm/hitachi_se.h>
+#define CF_CIS_BASE	0xb8000000
+/*
+ * You can connect Compact Flash directly to the bus of SuperH.
+ * This is the enabler for that.
+ *
+ * SIM: How generic is this really? It looks pretty board, or at
+ * least SH sub-type, specific to me.
+ * I know it doesn't work on the Overdrive!
+ */
+
+/*
+ * 0xB8000000 : Attribute
+ * 0xB8001000 : Common Memory
+ * 0xBA000000 : I/O
+ */
 
+static int __init cf_init_default(void)
+{
+#ifdef CONFIG_IDE
+	/* Enable the card, and set the level interrupt */
+	ctrl_outw(0x0042, CF_CIS_BASE+0x0200);
+#endif
+	make_imask_irq(14);
+	disable_irq(14);
+	return 0;
+}
+
+#if defined(CONFIG_SH_GENERIC) || defined(CONFIG_SH_SOLUTION_ENGINE)
+#include <asm/hitachi_se.h>
 
 /*
  * SolutionEngine
@@ -70,37 +98,14 @@
 	ctrl_outb(0x42, PA_MRSHPC_MW2 + 0x200);
 	return 0;
 }
-
-#define CF_CIS_BASE	0xb8000000
-/*
- * You can connect Compact Flash directly to the bus of SuperH.
- * This is the enabler for that.
- *
- * SIM: How generic is this really? It looks pretty board, or at
- * least SH sub-type, specific to me.
- * I know it doesn't work on the Overdrive!
- */
-
-/*
- * 0xB8000000 : Attribute
- * 0xB8001000 : Common Memory
- * 0xBA000000 : I/O
- */
-
-static int __init cf_init_default(void)
-{
-	/* Enable the card, and set the level interrupt */
-	ctrl_outw(0x0042, CF_CIS_BASE+0x0200);
-	make_imask_irq(14);
-	disable_irq(14);
-	return 0;
-}
+#endif
 
 int __init cf_init(void)
 {
-	if (MACH_SE) {
+#if defined(CONFIG_SH_GENERIC) || defined(CONFIG_SH_SOLUTION_ENGINE)
+	if (MACH_SE)
 		return cf_init_se();
-	}
+#endif
 	return cf_init_default();
 }
 
diff -urN linux-2.4.4-pre2/arch/sh/kernel/entry.S linux/arch/sh/kernel/entry.S
--- linux-2.4.4-pre2/arch/sh/kernel/entry.S	Sun Jan 28 18:56:00 2001
+++ linux/arch/sh/kernel/entry.S	Mon Dec  3 22:34:50 2001
@@ -182,7 +182,7 @@
 	jsr	@r0
 	 mov	r15, r4
 	!
-	tst	#0xff, r0
+	tst	r0, r0
 	bf/s	0f
 	 lds	r10, pr
 	rts
@@ -550,10 +550,10 @@
 ret_with_reschedule:
 	stc	k_current, r1
 	mov.l	@(need_resched,r1), r0
-	tst	#0xff, r0
+	tst	r0, r0
 	bf	reschedule
 	mov.l	@(sigpending,r1), r0
-	tst	#0xff, r0
+	tst	r0, r0
 	bt	restore_all
 signal_return:
 	mov	r15, r4
diff -urN linux-2.4.4-pre2/arch/sh/kernel/hd64465_gpio.c linux/arch/sh/kernel/hd64465_gpio.c
--- linux-2.4.4-pre2/arch/sh/kernel/hd64465_gpio.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/hd64465_gpio.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,185 @@
+/*
+ * $Id: hd64465_gpio.c,v 1.1 2001/01/02 15:35:22 mjd Exp $
+ * by Greg Banks <gbanks@pocketpenguins.com>
+ * (c) 2000 PocketPenguins Inc
+ *
+ * GPIO pin support for HD64465 companion chip.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/hd64465_gpio.h>
+
+#define _PORTOF(portpin)    (((portpin)>>3)&0x7)
+#define _PINOF(portpin)     ((portpin)&0x7)
+
+/* Register addresses parametrised on port */
+#define GPIO_CR(port)	    (HD64465_REG_GPACR+((port)<<1))
+#define GPIO_DR(port)	    (HD64465_REG_GPADR+((port)<<1))
+#define GPIO_ICR(port)	    (HD64465_REG_GPAICR+((port)<<1))
+#define GPIO_ISR(port)	    (HD64465_REG_GPAISR+((port)<<1))
+
+#define GPIO_NPORTS 5
+
+#define MODNAME "hd64465_gpio"
+
+EXPORT_SYMBOL(hd64465_gpio_configure);
+EXPORT_SYMBOL(hd64465_gpio_get_pin);
+EXPORT_SYMBOL(hd64465_gpio_get_port);
+EXPORT_SYMBOL(hd64465_gpio_register_irq);
+EXPORT_SYMBOL(hd64465_gpio_set_pin);
+EXPORT_SYMBOL(hd64465_gpio_set_port);
+EXPORT_SYMBOL(hd64465_gpio_unregister_irq);
+
+/* TODO: each port should be protected with a spinlock */
+
+
+void hd64465_gpio_configure(int portpin, int direction)
+{
+    	unsigned short cr;
+	unsigned int shift = (_PINOF(portpin)<<1);
+
+	cr = inw(GPIO_CR(_PORTOF(portpin)));
+	cr &= ~(3<<shift);
+	cr |= direction<<shift;
+	outw(cr, GPIO_CR(_PORTOF(portpin)));
+}
+
+void hd64465_gpio_set_pin(int portpin, unsigned int value)
+{
+    	unsigned short d;
+	unsigned short mask = 1<<(_PINOF(portpin));
+	
+	d = inw(GPIO_DR(_PORTOF(portpin)));
+	if (value)
+	    d |= mask;
+	else
+	    d &= ~mask;
+	outw(d, GPIO_DR(_PORTOF(portpin)));
+}
+
+unsigned int hd64465_gpio_get_pin(int portpin)
+{
+	return inw(GPIO_DR(_PORTOF(portpin))) & (1<<(_PINOF(portpin)));
+}
+
+/* TODO: for cleaner atomicity semantics, add a mask to this routine */
+
+void hd64465_gpio_set_port(int port, unsigned int value)
+{
+	outw(value, GPIO_DR(port));
+}
+
+unsigned int hd64465_gpio_get_port(int port)
+{
+	return inw(GPIO_DR(port));
+}
+
+
+static struct {
+    void (*func)(int portpin, void *dev);
+    void *dev;
+} handlers[GPIO_NPORTS * 8];
+
+static void hd64465_gpio_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+    	unsigned short port, pin, isr, mask, portpin;
+	
+	for (port=0 ; port<GPIO_NPORTS ; port++) {
+	    isr = inw(GPIO_ISR(port));
+	    
+	    for (pin=0 ; pin<8 ; pin++) {
+	    	mask = 1<<pin;
+	    	if (isr & mask) {
+		    portpin = (port<<3)|pin;
+		    if (handlers[portpin].func != 0)
+		    	handlers[portpin].func(portpin, handlers[portpin].dev);
+    	    	    else
+		    	printk(KERN_NOTICE "unexpected GPIO interrupt, pin %c%d\n",
+			    port+'A', (int)pin);
+		}
+	    }
+	    
+	    /* Write 1s back to ISR to clear it?  That's what the manual says.. */
+	    outw(isr, GPIO_ISR(port));
+	}
+}
+
+void hd64465_gpio_register_irq(int portpin, int mode,
+	void (*handler)(int portpin, void *dev), void *dev)
+{
+    	unsigned long flags;
+	unsigned short icr, mask;
+
+	if (handler == 0)
+	    return;
+	    
+	save_and_cli(flags);
+	
+	handlers[portpin].func = handler;
+	handlers[portpin].dev = dev;
+
+    	/*
+	 * Configure Interrupt Control Register
+	 */
+	icr = inw(GPIO_ICR(_PORTOF(portpin)));
+	mask = (1<<_PINOF(portpin));
+	
+	/* unmask interrupt */
+	icr &= ~mask;
+	
+	/* set TS bit */
+	mask <<= 8;
+	icr &= ~mask;
+	if (mode == HD64465_GPIO_RISING)
+	    icr |= mask;
+	    
+	outw(icr, GPIO_ICR(_PORTOF(portpin)));
+
+	restore_flags(flags);
+}
+
+void hd64465_gpio_unregister_irq(int portpin)
+{
+    	unsigned long flags;
+	unsigned short icr;
+	
+	save_and_cli(flags);
+
+    	/*
+	 * Configure Interrupt Control Register
+	 */
+	icr = inw(GPIO_ICR(_PORTOF(portpin)));
+	icr |= (1<<_PINOF(portpin));	/* mask interrupt */
+	outw(icr, GPIO_ICR(_PORTOF(portpin)));
+
+	handlers[portpin].func = 0;
+	handlers[portpin].dev = 0;
+	
+	restore_flags(flags);
+}
+
+static int __init hd64465_gpio_init(void)
+{
+    	/* TODO: check return values */
+    	request_region(HD64465_REG_GPACR, 0x1000, MODNAME);
+	request_irq(HD64465_IRQ_GPIO, hd64465_gpio_interrupt,
+	    SA_INTERRUPT, MODNAME, 0);
+
+    	printk("HD64465 GPIO layer on irq %d\n", HD64465_IRQ_GPIO);
+	return 0;
+}
+
+static void __exit hd64465_gpio_exit(void)
+{
+    	release_region(HD64465_REG_GPACR, 0x1000);
+	free_irq(HD64465_IRQ_GPIO, 0);
+}
+
+module_init(hd64465_gpio_init);
+module_exit(hd64465_gpio_exit);
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io.c linux/arch/sh/kernel/io.c
--- linux-2.4.4-pre2/arch/sh/kernel/io.c	Mon Oct  2 11:57:34 2000
+++ linux/arch/sh/kernel/io.c	Mon Dec  3 22:34:50 2001
@@ -8,123 +8,149 @@
  */
 
 #include <asm/io.h>
+#include <linux/module.h>
 
-unsigned int _inb(unsigned long port)
+unsigned char _inb(unsigned long port)
 {
 	return __inb(port);
 }
+EXPORT_SYMBOL(_inb);
 
-unsigned int _inw(unsigned long port)
+unsigned short _inw(unsigned long port)
 {
 	return __inw(port);
 }
+EXPORT_SYMBOL(_inw);
 
 unsigned int _inl(unsigned long port)
 {
 	return __inl(port);
 }
+EXPORT_SYMBOL(_inl);
 
 void _outb(unsigned char b, unsigned long port)
 {
 	__outb(b, port);
 }
+EXPORT_SYMBOL(_outb);
 
 void _outw(unsigned short b, unsigned long port)
 {
 	__outw(b, port);
 }
+EXPORT_SYMBOL(_outw);
+
 
 void _outl(unsigned int b, unsigned long port)
 {
 	__outl(b, port);
 }
+EXPORT_SYMBOL(_outl);
+
 
-unsigned int _inb_p(unsigned long port)
+unsigned char _inb_p(unsigned long port)
 {
 	return __inb_p(port);
 }
+EXPORT_SYMBOL(_inb_p);
 
-unsigned int _inw_p(unsigned long port)
+unsigned short _inw_p(unsigned long port)
 {
 	return __inw_p(port);
 }
+EXPORT_SYMBOL(_inw_p);
+
 
 void _outb_p(unsigned char b, unsigned long port)
 {
 	__outb_p(b, port);
 }
+EXPORT_SYMBOL(_outb_p);
 
 void _outw_p(unsigned short b, unsigned long port)
 {
 	__outw_p(b, port);
 }
+EXPORT_SYMBOL(_outw_p);
 
 void _insb(unsigned long port, void *buffer, unsigned long count)
 {
 	return __insb(port, buffer, count);
 }
+EXPORT_SYMBOL(_insb);
 
 void _insw(unsigned long port, void *buffer, unsigned long count)
 {
 	__insw(port, buffer, count);
 }
+EXPORT_SYMBOL(_insw);
 
 void _insl(unsigned long port, void *buffer, unsigned long count)
 {
 	__insl(port, buffer, count);
 }
+EXPORT_SYMBOL(_insl);
 
 void _outsb(unsigned long port, const void *buffer, unsigned long count)
 {
 	__outsb(port, buffer, count);
 }
+EXPORT_SYMBOL(_outsb);
 
 void _outsw(unsigned long port, const void *buffer, unsigned long count)
 {
 	__outsw(port, buffer, count);
 }
+EXPORT_SYMBOL(_outsw);
 
 void _outsl(unsigned long port, const void *buffer, unsigned long count)
 {
 	__outsl(port, buffer, count);
 
 }
+EXPORT_SYMBOL(_outsl);
 
-unsigned long ___raw_readb(unsigned long addr)
+unsigned char ___raw_readb(unsigned long addr)
 {
 	return __readb(addr);
 }
+EXPORT_SYMBOL(___raw_readb);
 
-unsigned long ___raw_readw(unsigned long addr)
+unsigned short ___raw_readw(unsigned long addr)
 {
 	return __readw(addr);
 }
+EXPORT_SYMBOL(___raw_readw);
 
-unsigned long ___raw_readl(unsigned long addr)
+unsigned int ___raw_readl(unsigned long addr)
 {
 	return __readl(addr);
 }
+EXPORT_SYMBOL(___raw_readl);
 
-unsigned long _readb(unsigned long addr)
+unsigned char _readb(unsigned long addr)
 {
 	unsigned long r = __readb(addr);
 	mb();
 	return r;
 }
+EXPORT_SYMBOL(_readb);
 
-unsigned long _readw(unsigned long addr)
+unsigned short _readw(unsigned long addr)
 {
 	unsigned long r = __readw(addr);
 	mb();
 	return r;
 }
+EXPORT_SYMBOL(_readw);
 
-unsigned long _readl(unsigned long addr)
+unsigned int _readl(unsigned long addr)
 {
 	unsigned long r = __readl(addr);
 	mb();
 	return r;
 }
+EXPORT_SYMBOL(_readl);
 
 void ___raw_writeb(unsigned char b, unsigned long addr)
 {
@@ -135,29 +161,34 @@
 {
 	__writew(b, addr);
 }
+EXPORT_SYMBOL(___raw_writew);
 
 void ___raw_writel(unsigned int b, unsigned long addr)
 {
 	__writel(b, addr);
 }
+EXPORT_SYMBOL(___raw_writel);
 
 void _writeb(unsigned char b, unsigned long addr)
 {
 	__writeb(b, addr);
 	mb();
 }
+EXPORT_SYMBOL(_writeb);
 
 void _writew(unsigned short b, unsigned long addr)
 {
 	__writew(b, addr);
 	mb();
 }
+EXPORT_SYMBOL(_writew);
 
 void _writel(unsigned int b, unsigned long addr)
 {
 	__writel(b, addr);
 	mb();
 }
+EXPORT_SYMBOL(_writel);
 
 /*
  * Copy data from IO memory space to "real" memory space.
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_cat68701.c linux/arch/sh/kernel/io_cat68701.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_cat68701.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/io_cat68701.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,248 @@
+/* 
+ * linux/arch/sh/kernel/io_cat68701.c
+ *
+ * Copyright (C) 2000  Niibe Yutaka
+ *               2001  Yutaro Ebihara
+ *
+ * I/O routine and setup routines for A-ONE Corp CAT-68701 SH7708 Board
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <asm/io.h>
+#include <asm/machvec.h>
+#include <linux/module.h>
+
+#define SH3_PCMCIA_BUG_WORKAROUND 1
+#define DUMMY_READ_AREA6	  0xba000000
+
+#define PORT2ADDR(x) (cat68701_isa_port2addr(x))
+
+static inline void delay(void)
+{
+	ctrl_inw(0xa0000000);
+}
+
+unsigned char cat68701_inb(unsigned long port)
+{
+	return *(volatile unsigned char*)PORT2ADDR(port);
+}
+
+unsigned short cat68701_inw(unsigned long port)
+{
+	return *(volatile unsigned short*)PORT2ADDR(port);
+}
+
+unsigned int cat68701_inl(unsigned long port)
+{
+	return *(volatile unsigned long*)PORT2ADDR(port);
+}
+
+unsigned char cat68701_inb_p(unsigned long port)
+{
+	unsigned long v = *(volatile unsigned char*)PORT2ADDR(port);
+
+	delay();
+	return v;
+}
+
+unsigned short cat68701_inw_p(unsigned long port)
+{
+	unsigned long v = *(volatile unsigned short*)PORT2ADDR(port);
+
+	delay();
+	return v;
+}
+
+unsigned int cat68701_inl_p(unsigned long port)
+{
+	unsigned long v = *(volatile unsigned long*)PORT2ADDR(port);
+
+	delay();
+	return v;
+}
+
+void cat68701_insb(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned char *buf=buffer;
+	while(count--) *buf++=inb(port);
+}
+
+void cat68701_insw(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned short *buf=buffer;
+	while(count--) *buf++=inw(port);
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+	ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+void cat68701_insl(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned long *buf=buffer;
+	while(count--) *buf++=inl(port);
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+	ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+void cat68701_outb(unsigned char b, unsigned long port)
+{
+	*(volatile unsigned char*)PORT2ADDR(port) = b;
+}
+
+void cat68701_outw(unsigned short b, unsigned long port)
+{
+	*(volatile unsigned short*)PORT2ADDR(port) = b;
+}
+
+void cat68701_outl(unsigned int b, unsigned long port)
+{
+        *(volatile unsigned long*)PORT2ADDR(port) = b;
+}
+
+void cat68701_outb_p(unsigned char b, unsigned long port)
+{
+	*(volatile unsigned char*)PORT2ADDR(port) = b;
+	delay();
+}
+
+void cat68701_outw_p(unsigned short b, unsigned long port)
+{
+	*(volatile unsigned short*)PORT2ADDR(port) = b;
+	delay();
+}
+
+void cat68701_outl_p(unsigned int b, unsigned long port)
+{
+	*(volatile unsigned long*)PORT2ADDR(port) = b;
+	delay();
+}
+
+void cat68701_outsb(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned char *buf=buffer;
+	while(count--) outb(*buf++, port);
+}
+
+void cat68701_outsw(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned short *buf=buffer;
+	while(count--) outw(*buf++, port);
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+	ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+void cat68701_outsl(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned long *buf=buffer;
+	while(count--) outl(*buf++, port);
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+	ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+unsigned char cat68701_readb(unsigned long addr)
+{
+	return *(volatile unsigned char*)addr;
+}
+
+unsigned short cat68701_readw(unsigned long addr)
+{
+	return *(volatile unsigned short*)addr;
+}
+
+unsigned int cat68701_readl(unsigned long addr)
+{
+	return *(volatile unsigned long*)addr;
+}
+
+void cat68701_writeb(unsigned char b, unsigned long addr)
+{
+	*(volatile unsigned char*)addr = b;
+}
+
+void cat68701_writew(unsigned short b, unsigned long addr)
+{
+	*(volatile unsigned short*)addr = b;
+}
+
+void cat68701_writel(unsigned int b, unsigned long addr)
+{
+        *(volatile unsigned long*)addr = b;
+}
+
+void * cat68701_ioremap(unsigned long offset, unsigned long size)
+{
+	return (void *) P2SEGADDR(offset);
+}
+EXPORT_SYMBOL(cat68701_ioremap);
+
+void cat68701_iounmap(void *addr)
+{
+}
+EXPORT_SYMBOL(cat68701_iounmap);
+
+unsigned long cat68701_isa_port2addr(unsigned long offset)
+{
+  /* CompactFlash (IDE) */
+  if(((offset >= 0x1f0) && (offset <= 0x1f7)) || (offset==0x3f6))
+    return 0xba000000 + offset;
+
+  /* INPUT PORT */
+  if((offset >= 0x3fc) && (offset <= 0x3fd))
+    return 0xb4007000 + offset;
+
+  /* OUTPUT PORT */
+  if((offset >= 0x3fe) && (offset <= 0x3ff))
+    return 0xb4007400 + offset;
+
+  return offset + 0xb4000000; /* other I/O (EREA 5)*/
+}
+
+
+int cat68701_irq_demux(int irq)
+{
+  if(irq==13) return 14;
+  if(irq==7)  return 10;
+  return irq;
+}
+
+
+
+/*-------------------------------------------------------*/
+
+void setup_cat68701(){
+  /* dummy read erea5 (CS8900A) */
+}
+
+void init_cat68701_IRQ(){
+  make_imask_irq(10);
+  make_imask_irq(14);
+}
+
+#ifdef CONFIG_HEARTBEAT
+#include <linux/sched.h>
+void heartbeat_cat68701()
+{
+        static unsigned int cnt = 0, period = 0 , bit = 0;
+        cnt += 1;
+        if (cnt < period) {
+                return;
+        }
+        cnt = 0;
+
+        /* Go through the points (roughly!):
+         * f(0)=10, f(1)=16, f(2)=20, f(5)=35,f(inf)->110
+         */
+        period = 110 - ( (300<<FSHIFT)/
+                         ((avenrun[0]/5) + (3<<FSHIFT)) );
+
+	if(bit){ bit=0; }else{ bit=1; }
+	outw(bit<<15,0x3fe);
+}
+#endif /* CONFIG_HEARTBEAT */
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_dc.c linux/arch/sh/kernel/io_dc.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_dc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/io_dc.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,13 @@
+/*
+ *	$Id: io_dc.c,v 1.1 2001/04/01 15:02:00 yaegashi Exp $
+ *	I/O routines for SEGA Dreamcast
+ */
+
+#include <linux/config.h>
+#include <asm/io.h>
+#include <asm/machvec.h>
+
+unsigned long dreamcast_isa_port2addr(unsigned long offset)
+{
+	return offset + 0xa0000000;
+}
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_ec3104.c linux/arch/sh/kernel/io_ec3104.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_ec3104.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/io_ec3104.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,83 @@
+/*
+ * linux/arch/sh/kernel/io_ec3104.c
+ *  EC3104 companion chip support
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ *
+ */
+/* EC3104 note:
+ * This code was written without any documentation about the EC3104 chip.  While
+ * I hope I got most of the basic functionality right, the register names I use
+ * are most likely completely different from those in the chip documentation.
+ *
+ * If you have any further information about the EC3104, please tell me
+ * (prumpf@tux.org).
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/ec3104.h>
+
+/*
+ * EC3104 has a real ISA bus which we redirect low port accesses to (the
+ * actual device on mine is a ESS 1868, and I don't want to hack the driver
+ * more than strictly necessary).  I am not going to duplicate the
+ * hard coding of PC addresses (for the 16550s aso) here though;  it's just
+ * too ugly.
+ */
+
+#define low_port(port) ((port) < 0x10000)
+
+static inline unsigned long port2addr(unsigned long port)
+{
+	switch(port >> 16) {
+	case 0:
+		return EC3104_ISA_BASE + port * 2;
+
+		/* XXX hack. it's unclear what to do about the serial ports */
+	case 1:
+		return EC3104_BASE + (port&0xffff) * 4;
+
+	default:
+		/* XXX PCMCIA */
+		return 0;
+	}
+}
+
+unsigned char ec3104_inb(unsigned long port)
+{
+	u8 ret;
+
+	ret = *(volatile u8 *)port2addr(port);
+
+	return ret;
+}
+
+unsigned short ec3104_inw(unsigned long port)
+{
+	BUG();
+}
+
+unsigned long ec3104_inl(unsigned long port)
+{
+	BUG();
+}
+
+void ec3104_outb(unsigned char data, unsigned long port)
+{
+	*(volatile u8 *)port2addr(port) = data;
+}
+
+void ec3104_outw(unsigned short data, unsigned long port)
+{
+	BUG();
+}
+
+void ec3104_outl(unsigned long data, unsigned long port)
+{
+	BUG();
+}
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_generic.c linux/arch/sh/kernel/io_generic.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_generic.c	Thu Aug 10 13:03:25 2000
+++ linux/arch/sh/kernel/io_generic.c	Mon Dec  3 22:34:50 2001
@@ -15,6 +15,7 @@
 
 #include <asm/io.h>
 #include <asm/machvec.h>
+#include <linux/module.h>
 
 #if defined(__sh3__)
 /* I'm not sure SH7709 has this kind of bug */
@@ -31,22 +32,22 @@
 	ctrl_inw(0xa0000000);
 }
 
-unsigned long generic_inb(unsigned int port)
+unsigned char generic_inb(unsigned long port)
 {
 	return *(volatile unsigned char*)PORT2ADDR(port);
 }
 
-unsigned long generic_inw(unsigned int port)
+unsigned short generic_inw(unsigned long port)
 {
 	return *(volatile unsigned short*)PORT2ADDR(port);
 }
 
-unsigned long generic_inl(unsigned int port)
+unsigned int generic_inl(unsigned long port)
 {
 	return *(volatile unsigned long*)PORT2ADDR(port);
 }
 
-unsigned long generic_inb_p(unsigned int port)
+unsigned char generic_inb_p(unsigned long port)
 {
 	unsigned long v = *(volatile unsigned char*)PORT2ADDR(port);
 
@@ -54,7 +55,7 @@
 	return v;
 }
 
-unsigned long generic_inw_p(unsigned int port)
+unsigned short generic_inw_p(unsigned long port)
 {
 	unsigned long v = *(volatile unsigned short*)PORT2ADDR(port);
 
@@ -62,7 +63,7 @@
 	return v;
 }
 
-unsigned long generic_inl_p(unsigned int port)
+unsigned int generic_inl_p(unsigned long port)
 {
 	unsigned long v = *(volatile unsigned long*)PORT2ADDR(port);
 
@@ -70,13 +71,13 @@
 	return v;
 }
 
-void generic_insb(unsigned int port, void *buffer, unsigned long count)
+void generic_insb(unsigned long port, void *buffer, unsigned long count)
 {
 	unsigned char *buf=buffer;
 	while(count--) *buf++=inb(port);
 }
 
-void generic_insw(unsigned int port, void *buffer, unsigned long count)
+void generic_insw(unsigned long port, void *buffer, unsigned long count)
 {
 	unsigned short *buf=buffer;
 	while(count--) *buf++=inw(port);
@@ -85,7 +86,7 @@
 #endif
 }
 
-void generic_insl(unsigned int port, void *buffer, unsigned long count)
+void generic_insl(unsigned long port, void *buffer, unsigned long count)
 {
 	unsigned long *buf=buffer;
 	while(count--) *buf++=inl(port);
@@ -94,46 +95,46 @@
 #endif
 }
 
-void generic_outb(unsigned long b, unsigned int port)
+void generic_outb(unsigned char b, unsigned long port)
 {
 	*(volatile unsigned char*)PORT2ADDR(port) = b;
 }
 
-void generic_outw(unsigned long b, unsigned int port)
+void generic_outw(unsigned short b, unsigned long port)
 {
 	*(volatile unsigned short*)PORT2ADDR(port) = b;
 }
 
-void generic_outl(unsigned long b, unsigned int port)
+void generic_outl(unsigned int b, unsigned long port)
 {
         *(volatile unsigned long*)PORT2ADDR(port) = b;
 }
 
-void generic_outb_p(unsigned long b, unsigned int port)
+void generic_outb_p(unsigned char b, unsigned long port)
 {
 	*(volatile unsigned char*)PORT2ADDR(port) = b;
 	delay();
 }
 
-void generic_outw_p(unsigned long b, unsigned int port)
+void generic_outw_p(unsigned short b, unsigned long port)
 {
 	*(volatile unsigned short*)PORT2ADDR(port) = b;
 	delay();
 }
 
-void generic_outl_p(unsigned long b, unsigned int port)
+void generic_outl_p(unsigned int b, unsigned long port)
 {
 	*(volatile unsigned long*)PORT2ADDR(port) = b;
 	delay();
 }
 
-void generic_outsb(unsigned int port, const void *buffer, unsigned long count)
+void generic_outsb(unsigned long port, const void *buffer, unsigned long count)
 {
 	const unsigned char *buf=buffer;
 	while(count--) outb(*buf++, port);
 }
 
-void generic_outsw(unsigned int port, const void *buffer, unsigned long count)
+void generic_outsw(unsigned long port, const void *buffer, unsigned long count)
 {
 	const unsigned short *buf=buffer;
 	while(count--) outw(*buf++, port);
@@ -142,7 +143,7 @@
 #endif
 }
 
-void generic_outsl(unsigned int port, const void *buffer, unsigned long count)
+void generic_outsl(unsigned long port, const void *buffer, unsigned long count)
 {
 	const unsigned long *buf=buffer;
 	while(count--) outl(*buf++, port);
@@ -151,17 +152,17 @@
 #endif
 }
 
-unsigned long generic_readb(unsigned long addr)
+unsigned char generic_readb(unsigned long addr)
 {
 	return *(volatile unsigned char*)addr;
 }
 
-unsigned long generic_readw(unsigned long addr)
+unsigned short generic_readw(unsigned long addr)
 {
 	return *(volatile unsigned short*)addr;
 }
 
-unsigned long generic_readl(unsigned long addr)
+unsigned int generic_readl(unsigned long addr)
 {
 	return *(volatile unsigned long*)addr;
 }
@@ -185,15 +186,12 @@
 {
 	return (void *) P2SEGADDR(offset);
 }
-
-void * generic_ioremap_nocache (unsigned long offset, unsigned long size)
-{
-	return (void *) P2SEGADDR(offset);
-}
+EXPORT_SYMBOL(generic_ioremap);
 
 void generic_iounmap(void *addr)
 {
 }
+EXPORT_SYMBOL(generic_iounmap);
 
 unsigned long generic_isa_port2addr(unsigned long offset)
 {
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_hd64461.c linux/arch/sh/kernel/io_hd64461.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_hd64461.c	Wed Aug  9 13:59:04 2000
+++ linux/arch/sh/kernel/io_hd64461.c	Mon Dec  3 22:34:50 2001
@@ -27,6 +27,12 @@
 	   detail of CF's memory mapped addressing. */
 	if (0x1f0<=port && port<=0x1f7)	return 0xb5000000 + port;
 	if (port == 0x3f6) return 0xb50001fe;
+	if (port == 0x3f7) return 0xb50001ff;
+
+	/* ide1 */
+	if (0x170<=port && port<=0x177)	return 0xba000000 + port;
+	if (port == 0x376) return 0xba000376;
+	if (port == 0x377) return 0xba000377;
 #endif
 
 	/* ??? */
@@ -50,80 +56,80 @@
 	ctrl_inw(0xa0000000);
 }
 
-unsigned long hd64461_inb(unsigned int port)
+unsigned char hd64461_inb(unsigned long port)
 {
 	return *(volatile unsigned char*)PORT2ADDR(port);
 }
 
-unsigned long hd64461_inb_p(unsigned int port)
+unsigned char hd64461_inb_p(unsigned long port)
 {
 	unsigned long v = *(volatile unsigned char*)PORT2ADDR(port);
 	delay();
 	return v;
 }
 
-unsigned long hd64461_inw(unsigned int port)
+unsigned short hd64461_inw(unsigned long port)
 {
 	return *(volatile unsigned short*)PORT2ADDR(port);
 }
 
-unsigned long hd64461_inl(unsigned int port)
+unsigned int hd64461_inl(unsigned long port)
 {
 	return *(volatile unsigned long*)PORT2ADDR(port);
 }
 
-void hd64461_insb(unsigned int port, void *buffer, unsigned long count)
+void hd64461_insb(unsigned long port, void *buffer, unsigned long count)
 {
 	unsigned char *buf=buffer;
 	while(count--) *buf++=inb(port);
 }
 
-void hd64461_insw(unsigned int port, void *buffer, unsigned long count)
+void hd64461_insw(unsigned long port, void *buffer, unsigned long count)
 {
 	unsigned short *buf=buffer;
 	while(count--) *buf++=inw(port);
 }
 
-void hd64461_insl(unsigned int port, void *buffer, unsigned long count)
+void hd64461_insl(unsigned long port, void *buffer, unsigned long count)
 {
 	unsigned long *buf=buffer;
 	while(count--) *buf++=inl(port);
 }
 
-void hd64461_outb(unsigned long b, unsigned int port)
+void hd64461_outb(unsigned char b, unsigned long port)
 {
 	*(volatile unsigned char*)PORT2ADDR(port) = b;
 }
 
-void hd64461_outb_p(unsigned long b, unsigned int port)
+void hd64461_outb_p(unsigned char b, unsigned long port)
 {
 	*(volatile unsigned char*)PORT2ADDR(port) = b;
 	delay();
 }
 
-void hd64461_outw(unsigned long b, unsigned int port)
+void hd64461_outw(unsigned short b, unsigned long port)
 {
 	*(volatile unsigned short*)PORT2ADDR(port) = b;
 }
 
-void hd64461_outl(unsigned long b, unsigned int port)
+void hd64461_outl(unsigned int b, unsigned long port)
 {
         *(volatile unsigned long*)PORT2ADDR(port) = b;
 }
 
-void hd64461_outsb(unsigned int port, const void *buffer, unsigned long count)
+void hd64461_outsb(unsigned long port, const void *buffer, unsigned long count)
 {
 	const unsigned char *buf=buffer;
 	while(count--) outb(*buf++, port);
 }
 
-void hd64461_outsw(unsigned int port, const void *buffer, unsigned long count)
+void hd64461_outsw(unsigned long port, const void *buffer, unsigned long count)
 {
 	const unsigned short *buf=buffer;
 	while(count--) outw(*buf++, port);
 }
 
-void hd64461_outsl(unsigned int port, const void *buffer, unsigned long count)
+void hd64461_outsl(unsigned long port, const void *buffer, unsigned long count)
 {
 	const unsigned long *buf=buffer;
 	while(count--) outl(*buf++, port);
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_hd64465.c linux/arch/sh/kernel/io_hd64465.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_hd64465.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/io_hd64465.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,249 @@
+/*
+ * $Id: io_hd64465.c,v 1.6 2001/02/15 09:13:51 dave_mckay Exp $
+ * by Greg Banks <gbanks@pocketpenguins.com>
+ * (c) 2000 PocketPenguins Inc
+ *
+ * Derived from io_hd64461.c, which bore the message:
+ * Copyright (C) 2000 YAEGASHI Takeshi
+ *
+ * Typical I/O routines for HD64465 system.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/hd64465.h>
+
+
+#define HD64465_DEBUG 0
+
+#if HD64465_DEBUG
+#define DPRINTK(args...)	printk(args)
+#define DIPRINTK(n, args...)	if (hd64465_io_debug>(n)) printk(args)
+#else
+#define DPRINTK(args...)
+#define DIPRINTK(n, args...)
+#endif
+
+
+
+/* This is a hack suitable only for debugging IO port problems */
+int hd64465_io_debug;
+EXPORT_SYMBOL(hd64465_io_debug);
+
+/* Low iomap maps port 0-1K to addresses in 8byte chunks */
+#define HD64465_IOMAP_LO_THRESH 0x400
+#define HD64465_IOMAP_LO_SHIFT	3
+#define HD64465_IOMAP_LO_MASK	((1<<HD64465_IOMAP_LO_SHIFT)-1)
+#define HD64465_IOMAP_LO_NMAP	(HD64465_IOMAP_LO_THRESH>>HD64465_IOMAP_LO_SHIFT)
+static unsigned long	hd64465_iomap_lo[HD64465_IOMAP_LO_NMAP];
+static unsigned char	hd64465_iomap_lo_shift[HD64465_IOMAP_LO_NMAP];
+
+/* High iomap maps port 1K-64K to addresses in 1K chunks */
+#define HD64465_IOMAP_HI_THRESH 0x10000
+#define HD64465_IOMAP_HI_SHIFT	10
+#define HD64465_IOMAP_HI_MASK	((1<<HD64465_IOMAP_HI_SHIFT)-1)
+#define HD64465_IOMAP_HI_NMAP	(HD64465_IOMAP_HI_THRESH>>HD64465_IOMAP_HI_SHIFT)
+static unsigned long	hd64465_iomap_hi[HD64465_IOMAP_HI_NMAP];
+static unsigned char	hd64465_iomap_hi_shift[HD64465_IOMAP_HI_NMAP];
+
+#ifndef MAX
+#define MAX(a,b)    ((a)>(b)?(a):(b))
+#endif
+
+void hd64465_port_map(unsigned short baseport, unsigned int nports,
+		      unsigned long addr, unsigned char shift)
+{
+    	unsigned int port, endport = baseport + nports;
+
+    	DPRINTK("hd64465_port_map(base=0x%04hx, n=0x%04hx, addr=0x%08lx,endport=0x%04x)\n",
+	    baseport, nports, addr,endport);
+	    
+	for (port = baseport ;
+	     port < endport && port < HD64465_IOMAP_LO_THRESH ;
+	     port += (1<<HD64465_IOMAP_LO_SHIFT)) {
+	    DPRINTK("    maplo[0x%x] = 0x%08lx\n", port, addr);
+    	    hd64465_iomap_lo[port>>HD64465_IOMAP_LO_SHIFT] = addr;
+    	    hd64465_iomap_lo_shift[port>>HD64465_IOMAP_LO_SHIFT] = shift;
+	    addr += (1<<(HD64465_IOMAP_LO_SHIFT));
+	}
+
+	for (port = MAX(baseport, HD64465_IOMAP_LO_THRESH) ;
+	     port < endport && port < HD64465_IOMAP_HI_THRESH ;
+	     port += (1<<HD64465_IOMAP_HI_SHIFT)) {
+	    DPRINTK("    maphi[0x%x] = 0x%08lx\n", port, addr);
+    	    hd64465_iomap_hi[port>>HD64465_IOMAP_HI_SHIFT] = addr;
+    	    hd64465_iomap_hi_shift[port>>HD64465_IOMAP_HI_SHIFT] = shift;
+	    addr += (1<<(HD64465_IOMAP_HI_SHIFT));
+	}
+}
+EXPORT_SYMBOL(hd64465_port_map);
+
+void hd64465_port_unmap(unsigned short baseport, unsigned int nports)
+{
+    	unsigned int port, endport = baseport + nports;
+	
+    	DPRINTK("hd64465_port_unmap(base=0x%04hx, n=0x%04hx)\n",
+	    baseport, nports);
+
+	for (port = baseport ;
+	     port < endport && port < HD64465_IOMAP_LO_THRESH ;
+	     port += (1<<HD64465_IOMAP_LO_SHIFT)) {
+    	    hd64465_iomap_lo[port>>HD64465_IOMAP_LO_SHIFT] = 0;
+	}
+
+	for (port = MAX(baseport, HD64465_IOMAP_LO_THRESH) ;
+	     port < endport && port < HD64465_IOMAP_HI_THRESH ;
+	     port += (1<<HD64465_IOMAP_HI_SHIFT)) {
+    	    hd64465_iomap_hi[port>>HD64465_IOMAP_HI_SHIFT] = 0;
+	}
+}
+EXPORT_SYMBOL(hd64465_port_unmap);
+
+static /*__inline__*/ unsigned long PORT2ADDR(unsigned long port)
+{
+    	unsigned long addr = 0;
+	unsigned char shift;
+
+	/* handle remapping of low IO ports */
+	if (port < HD64465_IOMAP_LO_THRESH) {
+	    addr = hd64465_iomap_lo[port >> HD64465_IOMAP_LO_SHIFT];
+	    shift = hd64465_iomap_lo_shift[port >> HD64465_IOMAP_LO_SHIFT];
+	    if (addr != 0)
+	    	addr += (port & HD64465_IOMAP_LO_MASK) << shift;
+	    else
+		printk(KERN_NOTICE "io_hd64465: access to un-mapped port %lx\n", port);
+	} else if (port < HD64465_IOMAP_HI_THRESH) {
+	    addr = hd64465_iomap_hi[port >> HD64465_IOMAP_HI_SHIFT];
+	    shift = hd64465_iomap_hi_shift[port >> HD64465_IOMAP_HI_SHIFT];
+	    if (addr != 0)
+		addr += (port & HD64465_IOMAP_HI_MASK) << shift;
+	    else
+		printk(KERN_NOTICE "io_hd64465: access to un-mapped port %lx\n", port);
+	}
+	    	
+	/* HD64465 internal devices (0xb0000000) */
+	else if (port < 0x20000)
+	    addr = CONFIG_HD64465_IOBASE + port - 0x10000;
+
+	/* Whole physical address space (0xa0000000) */
+	else
+	    addr = P2SEGADDR(port);
+
+    	DIPRINTK(2, "PORT2ADDR(0x%08lx) = 0x%08lx\n", port, addr);
+
+	return addr;
+}
+
+static inline void delay(void)
+{
+	ctrl_inw(0xa0000000);
+}
+
+unsigned char hd64465_inb(unsigned long port)
+{
+	unsigned long addr = PORT2ADDR(port);
+	unsigned long b = (addr == 0 ? 0 : *(volatile unsigned char*)addr);
+
+	DIPRINTK(0, "inb(%08lx) = %02x\n", addr, (unsigned)b);
+	return b;
+}
+
+unsigned char hd64465_inb_p(unsigned long port)
+{
+    	unsigned long v;
+	unsigned long addr = PORT2ADDR(port);
+
+	v = (addr == 0 ? 0 : *(volatile unsigned char*)addr);
+	delay();
+	DIPRINTK(0, "inb_p(%08lx) = %02x\n", addr, (unsigned)v);
+	return v;
+}
+
+unsigned short hd64465_inw(unsigned long port)
+{
+    	unsigned long addr = PORT2ADDR(port);
+	unsigned long b = (addr == 0 ? 0 : *(volatile unsigned short*)addr);
+	DIPRINTK(0, "inw(%08lx) = %04lx\n", addr, b);
+	return b;
+}
+
+unsigned int hd64465_inl(unsigned long port)
+{
+    	unsigned long addr = PORT2ADDR(port);
+	unsigned int b = (addr == 0 ? 0 : *(volatile unsigned long*)addr);
+	DIPRINTK(0, "inl(%08lx) = %08x\n", addr, b);
+	return b;
+}
+
+void hd64465_insb(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned char *buf=buffer;
+	while(count--) *buf++=inb(port);
+}
+
+void hd64465_insw(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned short *buf=buffer;
+	while(count--) *buf++=inw(port);
+}
+
+void hd64465_insl(unsigned long port, void *buffer, unsigned long count)
+{
+	unsigned long *buf=buffer;
+	while(count--) *buf++=inl(port);
+}
+
+void hd64465_outb(unsigned char b, unsigned long port)
+{
+	unsigned long addr = PORT2ADDR(port);
+
+	DIPRINTK(0, "outb(%02x, %08lx)\n", (unsigned)b, addr);
+	if (addr != 0)
+	    *(volatile unsigned char*)addr = b;
+}
+
+void hd64465_outb_p(unsigned char b, unsigned long port)
+{
+	unsigned long addr = PORT2ADDR(port);
+
+	DIPRINTK(0, "outb_p(%02x, %08lx)\n", (unsigned)b, addr);
+    	if (addr != 0)
+	    *(volatile unsigned char*)addr = b;
+	delay();
+}
+
+void hd64465_outw(unsigned short b, unsigned long port)
+{
+	unsigned long addr = PORT2ADDR(port);
+	DIPRINTK(0, "outw(%04x, %08lx)\n", (unsigned)b, addr);
+	if (addr != 0)
+	    *(volatile unsigned short*)addr = b;
+}
+
+void hd64465_outl(unsigned int b, unsigned long port)
+{
+	unsigned long addr = PORT2ADDR(port);
+	DIPRINTK(0, "outl(%08x, %08lx)\n", b, addr);
+	if (addr != 0)
+            *(volatile unsigned long*)addr = b;
+}
+
+void hd64465_outsb(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned char *buf=buffer;
+	while(count--) outb(*buf++, port);
+}
+
+void hd64465_outsw(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned short *buf=buffer;
+	while(count--) outw(*buf++, port);
+}
+
+void hd64465_outsl(unsigned long port, const void *buffer, unsigned long count)
+{
+	const unsigned long *buf=buffer;
+	while(count--) outl(*buf++, port);
+}
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_se.c linux/arch/sh/kernel/io_se.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_se.c	Wed Aug  9 13:59:04 2000
+++ linux/arch/sh/kernel/io_se.c	Mon Dec  3 22:34:50 2001
@@ -43,7 +43,7 @@
 }
 
 static inline int
-shifted_port(unsigned int port)
+shifted_port(unsigned long port)
 {
 	/* For IDE registers, value is not shifted */
 	if ((0x1f0 <= port && port < 0x1f8) || port == 0x3f6)
@@ -53,10 +53,10 @@
 }
 
 #define maybebadio(name,port) \
-  printk("bad PC-like io %s for port 0x%x at 0x%08x\n", \
+  printk("bad PC-like io %s for port 0x%lx at 0x%08x\n", \
 	 #name, (port), (__u32) __builtin_return_address(0))
 
-unsigned long se_inb(unsigned int port)
+unsigned char se_inb(unsigned long port)
 {
 	if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop)
 		return *(__u8 *) (sh_pcic_io_wbase + 0x40000 + port); 
@@ -66,7 +66,7 @@
 		return (*port2adr(port))&0xff; 
 }
 
-unsigned long se_inb_p(unsigned int port)
+unsigned char se_inb_p(unsigned long port)
 {
 	unsigned long v;
 
@@ -80,7 +80,7 @@
 	return v;
 }
 
-unsigned long se_inw(unsigned int port)
+unsigned short se_inw(unsigned long port)
 {
 	if (port >= 0x2000 ||
 	    (sh_pcic_io_start <= port && port <= sh_pcic_io_stop))
@@ -90,13 +90,13 @@
 	return 0;
 }
 
-unsigned long se_inl(unsigned int port)
+unsigned int se_inl(unsigned long port)
 {
 	maybebadio(inl, port);
 	return 0;
 }
 
-void se_outb(unsigned long value, unsigned int port)
+void se_outb(unsigned char value, unsigned long port)
 {
 	if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop)
 		*(__u8 *)(sh_pcic_io_wbase + port) = value; 
@@ -106,7 +106,7 @@
 		*(port2adr(port)) = value;
 }
 
-void se_outb_p(unsigned long value, unsigned int port)
+void se_outb_p(unsigned char value, unsigned long port)
 {
 	if (sh_pcic_io_start <= port && port <= sh_pcic_io_stop)
 		*(__u8 *)(sh_pcic_io_wbase + port) = value; 
@@ -117,7 +117,7 @@
 	delay();
 }
 
-void se_outw(unsigned long value, unsigned int port)
+void se_outw(unsigned short value, unsigned long port)
 {
 	if (port >= 0x2000 ||
 	    (sh_pcic_io_start <= port && port <= sh_pcic_io_stop))
@@ -126,12 +126,12 @@
 		maybebadio(outw, port);
 }
 
-void se_outl(unsigned long value, unsigned int port)
+void se_outl(unsigned int value, unsigned long port)
 {
 	maybebadio(outl, port);
 }
 
-void se_insb(unsigned int port, void *addr, unsigned long count)
+void se_insb(unsigned long port, void *addr, unsigned long count)
 {
 	volatile __u16 *p = port2adr(port);
 
@@ -148,19 +148,19 @@
 	}
 }
 
-void se_insw(unsigned int port, void *addr, unsigned long count)
+void se_insw(unsigned long port, void *addr, unsigned long count)
 {
 	volatile __u16 *p = port2adr(port);
 	while (count--)
 		*((__u16 *) addr)++ = *p;
 }
 
-void se_insl(unsigned int port, void *addr, unsigned long count)
+void se_insl(unsigned long port, void *addr, unsigned long count)
 {
 	maybebadio(insl, port);
 }
 
-void se_outsb(unsigned int port, const void *addr, unsigned long count)
+void se_outsb(unsigned long port, const void *addr, unsigned long count)
 {
 	volatile __u16 *p = port2adr(port);
 
@@ -177,29 +177,29 @@
 	}
 }
 
-void se_outsw(unsigned int port, const void *addr, unsigned long count)
+void se_outsw(unsigned long port, const void *addr, unsigned long count)
 {
 	volatile __u16 *p = port2adr(port);
 	while (count--)
 		*p = *((__u16 *) addr)++;
 }
 
-void se_outsl(unsigned int port, const void *addr, unsigned long count)
+void se_outsl(unsigned long port, const void *addr, unsigned long count)
 {
 	maybebadio(outsw, port);
 }
 
-unsigned long se_readb(unsigned long addr)
+unsigned char se_readb(unsigned long addr)
 {
 	return *(volatile unsigned char*)addr;
 }
 
-unsigned long se_readw(unsigned long addr)
+unsigned short se_readw(unsigned long addr)
 {
 	return *(volatile unsigned short*)addr;
 }
 
-unsigned long se_readl(unsigned long addr)
+unsigned int se_readl(unsigned long addr)
 {
 	return *(volatile unsigned long*)addr;
 }
diff -urN linux-2.4.4-pre2/arch/sh/kernel/io_unknown.c linux/arch/sh/kernel/io_unknown.c
--- linux-2.4.4-pre2/arch/sh/kernel/io_unknown.c	Wed Aug  9 13:59:04 2000
+++ linux/arch/sh/kernel/io_unknown.c	Mon Dec  3 22:34:50 2001
@@ -43,5 +43,4 @@
 UNKNOWN_ALIAS(writel)
 UNKNOWN_ALIAS(isa_port2addr)
 UNKNOWN_ALIAS(ioremap)
-UNKNOWN_ALIAS(ioremap_nocache)
 UNKNOWN_ALIAS(iounmap)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/irq_intc2.c linux/arch/sh/kernel/irq_intc2.c
--- linux-2.4.4-pre2/arch/sh/kernel/irq_intc2.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/irq_intc2.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,127 @@
+/*
+ * linux/arch/sh/kernel/irq_intc2.c
+ *
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.                            
+ *
+ * Interrupt handling for INTC2-based IRQ.
+ *
+ * These are the "new Hitachi style" interrupts, as present on the 
+ * Hitachi 7751 and the STM ST40 STB1.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/machvec.h>
+
+
+struct intc2_data {
+	unsigned int addr;	/* Address of Interrupt Priority Register */
+	int mask; /*Mask to apply */
+};
+
+
+static struct intc2_data intc2_data[NR_INTC2_IRQS];
+
+static void enable_intc2_irq(unsigned int irq);
+static void disable_intc2_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_intc2_irq disable_intc2_irq
+
+static void mask_and_ack_intc2(unsigned int);
+static void end_intc2_irq(unsigned int irq);
+
+static unsigned int startup_intc2_irq(unsigned int irq)
+{ 
+	enable_intc2_irq(irq);
+	return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type intc2_irq_type = {
+	"INTC2-based-IRQ",
+	startup_intc2_irq,
+	shutdown_intc2_irq,
+	enable_intc2_irq,
+	disable_intc2_irq,
+	mask_and_ack_intc2,
+	end_intc2_irq
+};
+
+static void disable_intc2_irq(unsigned int irq)
+{
+	unsigned addr;
+	int offset=irq-INTC2_FIRST_IRQ;
+	unsigned val,flags;
+
+	// Sanity check
+	if(offset<0 || offset>=NR_INTC2_IRQS) return;
+
+	addr=intc2_data[offset].addr+INTC2_INTMSK_OFFSET;
+
+	save_and_cli(flags);
+	val=ctrl_inl(addr);
+	val|=intc2_data[offset].mask;
+	ctrl_outl(val,addr);
+
+	restore_flags(flags);
+}
+
+static void enable_intc2_irq(unsigned int irq)
+{
+	int offset=irq-INTC2_FIRST_IRQ;
+
+	// Sanity check
+	if(offset<0 || offset>=NR_INTC2_IRQS) return;
+
+	ctrl_outl(intc2_data[offset].mask,
+		  intc2_data[offset].addr+INTC2_INTMSKCLR_OFFSET);
+
+}
+
+static void mask_and_ack_intc2(unsigned int irq)
+{
+	disable_intc2_irq(irq);
+}
+
+static void end_intc2_irq(unsigned int irq)
+{
+	enable_intc2_irq(irq);
+}
+
+void make_intc2_irq(unsigned int irq, unsigned int addr, 
+		    unsigned int group,int pos, int priority)
+{
+	int offset=irq-INTC2_FIRST_IRQ;
+	unsigned flags,val;
+
+	if(offset<0 || offset>=NR_INTC2_IRQS) {
+		return;
+	}
+
+	disable_irq_nosync(irq);
+	/* Fill the the data we need */
+	intc2_data[offset].addr=addr;
+	intc2_data[offset].mask=1<<pos;
+		
+	/* Set the priority level */
+	save_and_cli(flags);
+	val=ctrl_inl(addr+INTC2_INTPRI_OFFSET);
+	val|=(priority)<< (group<<4);
+	ctrl_outl(val,addr+INTC2_INTPRI_OFFSET);
+	restore_flags(flags);
+
+	irq_desc[irq].handler=&intc2_irq_type;
+
+	disable_intc2_irq(irq);
+}
+
+
+
diff -urN linux-2.4.4-pre2/arch/sh/kernel/irq_ipr.c linux/arch/sh/kernel/irq_ipr.c
--- linux-2.4.4-pre2/arch/sh/kernel/irq_ipr.c	Mon Oct  2 11:57:34 2000
+++ linux/arch/sh/kernel/irq_ipr.c	Mon Dec  3 22:34:50 2001
@@ -116,14 +116,96 @@
 	disable_ipr_irq(irq);
 }
 
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+static unsigned char pint_map[256];
+static unsigned long portcr_mask = 0;
+
+static void enable_pint_irq(unsigned int irq);
+static void disable_pint_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_pint_irq disable_pint_irq
+
+static void mask_and_ack_pint(unsigned int);
+static void end_pint_irq(unsigned int irq);
+
+static unsigned int startup_pint_irq(unsigned int irq)
+{ 
+	enable_pint_irq(irq);
+	return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type pint_irq_type = {
+	"PINT-IRQ",
+	startup_pint_irq,
+	shutdown_pint_irq,
+	enable_pint_irq,
+	disable_pint_irq,
+	mask_and_ack_pint,
+	end_pint_irq
+};
+
+static void disable_pint_irq(unsigned int irq)
+{
+	unsigned long val, flags;
+
+	save_and_cli(flags);
+	val = ctrl_inw(INTC_INTER);
+	val &= ~(1 << (irq - PINT_IRQ_BASE));
+	ctrl_outw(val, INTC_INTER);	/* disable PINTn */
+	portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
+	restore_flags(flags);
+}
+
+static void enable_pint_irq(unsigned int irq)
+{
+	unsigned long val, flags;
+
+	save_and_cli(flags);
+	val = ctrl_inw(INTC_INTER);
+	val |= 1 << (irq - PINT_IRQ_BASE);
+	ctrl_outw(val, INTC_INTER);	/* enable PINTn */
+	portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
+	restore_flags(flags);
+}
+
+static void mask_and_ack_pint(unsigned int irq)
+{
+	disable_pint_irq(irq);
+}
+
+static void end_pint_irq(unsigned int irq)
+{
+	enable_pint_irq(irq);
+}
+
+void make_pint_irq(unsigned int irq)
+{
+	disable_irq_nosync(irq);
+	irq_desc[irq].handler = &pint_irq_type;
+	disable_pint_irq(irq);
+}
+#endif
+
 void __init init_IRQ(void)
 {
+	int i;
+
 	make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY);
 	make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY);
 
+#ifdef SCI_ERI_IRQ
 	make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
 	make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
 	make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
+#endif
+
+#ifdef SCIF1_ERI_IRQ
+	make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+	make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+	make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+	make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+#endif
 
 #ifdef SCIF_ERI_IRQ
 	make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
@@ -171,12 +253,30 @@
 	 * You should set corresponding bits of PFC to "00"
 	 * to enable these interrupts.
 	 */
-	make_ipr_irq(IRQ0_IRQ, IRQ0_IRP_ADDR, IRQ0_IRP_POS, IRQ0_PRIORITY);
-	make_ipr_irq(IRQ1_IRQ, IRQ1_IRP_ADDR, IRQ1_IRP_POS, IRQ1_PRIORITY);
-	make_ipr_irq(IRQ2_IRQ, IRQ2_IRP_ADDR, IRQ2_IRP_POS, IRQ2_PRIORITY);
-	make_ipr_irq(IRQ3_IRQ, IRQ3_IRP_ADDR, IRQ3_IRP_POS, IRQ3_PRIORITY);
-	make_ipr_irq(IRQ4_IRQ, IRQ4_IRP_ADDR, IRQ4_IRP_POS, IRQ4_PRIORITY);
-	make_ipr_irq(IRQ5_IRQ, IRQ5_IRP_ADDR, IRQ5_IRP_POS, IRQ5_PRIORITY);
+	make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY);
+	make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY);
+	make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY);
+	make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY);
+	make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY);
+	make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY);
+	make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY);
+	make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY);
+	enable_ipr_irq(PINT0_IRQ);
+	enable_ipr_irq(PINT8_IRQ);
+
+	for(i = 0; i < 16; i++)
+		make_pint_irq(PINT_IRQ_BASE + i);
+	for(i = 0; i < 256; i++)
+	{
+		if(i & 1) pint_map[i] = 0;
+		else if(i & 2) pint_map[i] = 1;
+		else if(i & 4) pint_map[i] = 2;
+		else if(i & 8) pint_map[i] = 3;
+		else if(i & 0x10) pint_map[i] = 4;
+		else if(i & 0x20) pint_map[i] = 5;
+		else if(i & 0x40) pint_map[i] = 6;
+		else if(i & 0x80) pint_map[i] = 7;
+	}
 #endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 */
 
 	/* Perform the machine specific initialisation */
@@ -184,3 +284,43 @@
 		sh_mv.mv_init_irq();
 	}
 }
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+int ipr_irq_demux(int irq)
+{
+	unsigned long creg, dreg, d, sav;
+
+	if(irq == PINT0_IRQ)
+	{
+#if defined(CONFIG_CPU_SUBTYPE_SH7707)
+		creg = PORT_PACR;
+		dreg = PORT_PADR;
+#else
+		creg = PORT_PCCR;
+		dreg = PORT_PCDR;
+#endif
+		sav = ctrl_inw(creg);
+		ctrl_outw(sav | portcr_mask, creg);
+		d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff;
+		ctrl_outw(sav, creg);
+		if(d == 0) return irq;
+		return PINT_IRQ_BASE + pint_map[d];
+	}
+	else if(irq == PINT8_IRQ)
+	{
+#if defined(CONFIG_CPU_SUBTYPE_SH7707)
+		creg = PORT_PBCR;
+		dreg = PORT_PBDR;
+#else
+		creg = PORT_PFCR;
+		dreg = PORT_PFDR;
+#endif
+		sav = ctrl_inw(creg);
+		ctrl_outw(sav | (portcr_mask >> 16), creg);
+		d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff;
+		ctrl_outw(sav, creg);
+		if(d == 0) return irq;
+		return PINT_IRQ_BASE + 8 + pint_map[d];
+	}
+	return irq;
+}
+#endif
diff -urN linux-2.4.4-pre2/arch/sh/kernel/mach_cat68701.c linux/arch/sh/kernel/mach_cat68701.c
--- linux-2.4.4-pre2/arch/sh/kernel/mach_cat68701.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/mach_cat68701.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,72 @@
+/*
+ * linux/arch/sh/kernel/mach_cat68701.c
+ *
+ * Copyright (C) 2000 Stuart Menefy (stuart.menefy@st.com)
+ *               2001 Yutaro Ebihara (ebihara@si-linux.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * Machine vector for the A-ONE corp. CAT-68701 SH7708 board
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/machvec.h>
+#include <asm/rtc.h>
+#include <asm/machvec_init.h>
+#include <asm/io_cat68701.h>
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_cat68701 __initmv = {
+	mv_name:		"CAT-68701",
+	mv_nr_irqs:		32,
+	mv_inb:			cat68701_inb,
+	mv_inw:			cat68701_inw,
+	mv_inl:			cat68701_inl,
+	mv_outb:		cat68701_outb,
+	mv_outw:		cat68701_outw,
+	mv_outl:		cat68701_outl,
+
+	mv_inb_p:		cat68701_inb_p,
+	mv_inw_p:		cat68701_inw,
+	mv_inl_p:		cat68701_inl,
+	mv_outb_p:		cat68701_outb_p,
+	mv_outw_p:		cat68701_outw,
+	mv_outl_p:		cat68701_outl,
+
+	mv_insb:		cat68701_insb,
+	mv_insw:		cat68701_insw,
+	mv_insl:		cat68701_insl,
+	mv_outsb:		cat68701_outsb,
+	mv_outsw:		cat68701_outsw,
+	mv_outsl:		cat68701_outsl,
+
+	mv_readb:		cat68701_readb,
+	mv_readw:		cat68701_readw,
+	mv_readl:		cat68701_readl,
+	mv_writeb:		cat68701_writeb,
+	mv_writew:		cat68701_writew,
+	mv_writel:		cat68701_writel,
+
+	mv_ioremap:		cat68701_ioremap,
+	mv_iounmap:		cat68701_iounmap,
+
+	mv_isa_port2addr:	cat68701_isa_port2addr,
+	mv_irq_demux:           cat68701_irq_demux,
+
+	mv_init_arch:		setup_cat68701,
+	mv_init_irq:		init_cat68701_IRQ,
+#ifdef CONFIG_HEARTBEAT
+	mv_heartbeat:		heartbeat_cat68701,
+#endif
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
+
+};
+ALIAS_MV(cat68701)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/mach_dc.c linux/arch/sh/kernel/mach_dc.c
--- linux-2.4.4-pre2/arch/sh/kernel/mach_dc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/mach_dc.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,66 @@
+/*
+ *	$Id: mach_dc.c,v 1.1 2001/04/01 15:02:00 yaegashi Exp $
+ *	SEGA Dreamcast machine vector
+ */
+
+#include <linux/init.h>
+
+#include <asm/machvec.h>
+#include <asm/machvec_init.h>
+
+#include <asm/io_generic.h>
+#include <asm/io_dc.h>
+#include <asm/irq.h>
+
+void __init setup_dreamcast(void);
+void __init dreamcast_pcibios_init(void);
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_dreamcast __initmv = {
+	mv_name:		"dreamcast",
+
+	mv_nr_irqs:		48,
+
+	mv_inb:			generic_inb,
+	mv_inw:			generic_inw,
+	mv_inl:			generic_inl,
+	mv_outb:		generic_outb,
+	mv_outw:		generic_outw,
+	mv_outl:		generic_outl,
+
+	mv_inb_p:		generic_inb_p,
+	mv_inw_p:		generic_inw,
+	mv_inl_p:		generic_inl,
+	mv_outb_p:		generic_outb_p,
+	mv_outw_p:		generic_outw,
+	mv_outl_p:		generic_outl,
+
+	mv_insb:		generic_insb,
+	mv_insw:		generic_insw,
+	mv_insl:		generic_insl,
+	mv_outsb:		generic_outsb,
+	mv_outsw:		generic_outsw,
+	mv_outsl:		generic_outsl,
+
+	mv_readb:		generic_readb,
+	mv_readw:		generic_readw,
+	mv_readl:		generic_readl,
+	mv_writeb:		generic_writeb,
+	mv_writew:		generic_writew,
+	mv_writel:		generic_writel,
+
+	mv_ioremap:		generic_ioremap,
+	mv_iounmap:		generic_iounmap,
+
+	mv_init_arch:		setup_dreamcast,
+#ifdef CONFIG_PCI
+	mv_init_pci:		dreamcast_pcibios_init,
+#endif
+	mv_isa_port2addr:	dreamcast_isa_port2addr,
+
+	mv_hw_dreamcast:		1,
+};
+ALIAS_MV(dreamcast)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/mach_dmida.c linux/arch/sh/kernel/mach_dmida.c
--- linux-2.4.4-pre2/arch/sh/kernel/mach_dmida.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/mach_dmida.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,73 @@
+/*
+ * linux/arch/sh/kernel/mach_dmida.c
+ *
+ * by Greg Banks <gbanks@pocketpenguins.com>
+ * (c) 2000 PocketPenguins Inc
+ *
+ * Derived from mach_hp600.c, which bore the message:
+ * Copyright (C) 2000 Stuart Menefy (stuart.menefy@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * Machine vector for the DataMyte Industrial Digital Assistant(tm).
+ * See http://www.dmida.com
+ *
+ */
+
+#include <linux/init.h>
+
+#include <asm/machvec.h>
+#include <asm/rtc.h>
+#include <asm/machvec_init.h>
+
+#include <asm/io.h>
+#include <asm/hd64465.h>
+#include <asm/irq.h>
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_dmida __initmv = {
+	mv_name:		"DMIDA",
+
+	mv_nr_irqs:		HD64465_IRQ_BASE+HD64465_IRQ_NUM,
+
+	mv_inb:			hd64465_inb,
+	mv_inw:			hd64465_inw,
+	mv_inl:			hd64465_inl,
+	mv_outb:		hd64465_outb,
+	mv_outw:		hd64465_outw,
+	mv_outl:		hd64465_outl,
+
+	mv_inb_p:		hd64465_inb_p,
+	mv_inw_p:		hd64465_inw,
+	mv_inl_p:		hd64465_inl,
+	mv_outb_p:		hd64465_outb_p,
+	mv_outw_p:		hd64465_outw,
+	mv_outl_p:		hd64465_outl,
+
+	mv_insb:		hd64465_insb,
+	mv_insw:		hd64465_insw,
+	mv_insl:		hd64465_insl,
+	mv_outsb:		hd64465_outsb,
+	mv_outsw:		hd64465_outsw,
+	mv_outsl:		hd64465_outsl,
+
+	mv_readb:		generic_readb,
+	mv_readw:		generic_readw,
+	mv_readl:		generic_readl,
+	mv_writeb:		generic_writeb,
+	mv_writew:		generic_writew,
+	mv_writel:		generic_writel,
+
+	mv_irq_demux:		hd64465_irq_demux,
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
+
+	mv_hw_hd64465:		1,
+};
+ALIAS_MV(dmida)
+
diff -urN linux-2.4.4-pre2/arch/sh/kernel/mach_ec3104.c linux/arch/sh/kernel/mach_ec3104.c
--- linux-2.4.4-pre2/arch/sh/kernel/mach_ec3104.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/mach_ec3104.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,69 @@
+/*
+ * linux/arch/sh/kernel/mach_ec3104.c
+ *  EC3104 companion chip support
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ *
+ */
+/* EC3104 note:
+ * This code was written without any documentation about the EC3104 chip.  While
+ * I hope I got most of the basic functionality right, the register names I use
+ * are most likely completely different from those in the chip documentation.
+ *
+ * If you have any further information about the EC3104, please tell me
+ * (prumpf@tux.org).
+ */
+
+#include <linux/init.h>
+
+#include <asm/machvec.h>
+#include <asm/rtc.h>
+#include <asm/machvec_init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_ec3104 __initmv = {
+	mv_name:		"EC3104",
+
+	mv_nr_irqs:		96,
+
+	mv_inb:			ec3104_inb,
+	mv_inw:			ec3104_inw,
+	mv_inl:			ec3104_inl,
+	mv_outb:		ec3104_outb,
+	mv_outw:		ec3104_outw,
+	mv_outl:		ec3104_outl,
+
+	mv_inb_p:		generic_inb_p,
+	mv_inw_p:		generic_inw,
+	mv_inl_p:		generic_inl,
+	mv_outb_p:		generic_outb_p,
+	mv_outw_p:		generic_outw,
+	mv_outl_p:		generic_outl,
+
+	mv_insb:		generic_insb,
+	mv_insw:		generic_insw,
+	mv_insl:		generic_insl,
+	mv_outsb:		generic_outsb,
+	mv_outsw:		generic_outsw,
+	mv_outsl:		generic_outsl,
+
+	mv_readb:		generic_readb,
+	mv_readw:		generic_readw,
+	mv_readl:		generic_readl,
+	mv_writeb:		generic_writeb,
+	mv_writew:		generic_writew,
+	mv_writel:		generic_writel,
+
+	mv_irq_demux:		ec3104_irq_demux,
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
+};
+
+ALIAS_MV(ec3104)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/mach_hp600.c linux/arch/sh/kernel/mach_hp600.c
--- linux-2.4.4-pre2/arch/sh/kernel/mach_hp600.c	Wed Aug  9 13:59:04 2000
+++ linux/arch/sh/kernel/mach_hp600.c	Mon Dec  3 22:34:50 2001
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 
 #include <asm/machvec.h>
+#include <asm/rtc.h>
 #include <asm/machvec_init.h>
 
 #include <asm/io_hd64461.h>
@@ -22,8 +23,8 @@
  * The Machine Vector
  */
 
-struct sh_machine_vector mv_hp600 __initmv = {
-	mv_name:		"HP600",
+struct sh_machine_vector mv_hp620 __initmv = {
+	mv_name:		"hp620",
 
 	mv_nr_irqs:		80, /* HD64461_IRQBASE+16, see hd64461.h */
 
@@ -57,7 +58,101 @@
 
 	mv_irq_demux:		hd64461_irq_demux,
 
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
+
+	mv_hw_hp600:		1,
+	mv_hw_hp620:		1,
+	mv_hw_hd64461:		1,
+};
+ALIAS_MV(hp620)
+
+
+struct sh_machine_vector mv_hp680 __initmv = {
+	mv_name:		"hp680",
+
+	mv_nr_irqs:		80, /* HD64461_IRQBASE+16, see hd64461.h */
+
+	mv_inb:			hd64461_inb,
+	mv_inw:			hd64461_inw,
+	mv_inl:			hd64461_inl,
+	mv_outb:		hd64461_outb,
+	mv_outw:		hd64461_outw,
+	mv_outl:		hd64461_outl,
+
+	mv_inb_p:		hd64461_inb_p,
+	mv_inw_p:		hd64461_inw,
+	mv_inl_p:		hd64461_inl,
+	mv_outb_p:		hd64461_outb_p,
+	mv_outw_p:		hd64461_outw,
+	mv_outl_p:		hd64461_outl,
+
+	mv_insb:		hd64461_insb,
+	mv_insw:		hd64461_insw,
+	mv_insl:		hd64461_insl,
+	mv_outsb:		hd64461_outsb,
+	mv_outsw:		hd64461_outsw,
+	mv_outsl:		hd64461_outsl,
+
+	mv_readb:		generic_readb,
+	mv_readw:		generic_readw,
+	mv_readl:		generic_readl,
+	mv_writeb:		generic_writeb,
+	mv_writew:		generic_writew,
+	mv_writel:		generic_writel,
+
+	mv_irq_demux:		hd64461_irq_demux,
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
+
+	mv_hw_hp600:		1,
+	mv_hw_hp680:		1,
+	mv_hw_hd64461:		1,
+};
+ALIAS_MV(hp680)
+
+
+struct sh_machine_vector mv_hp690 __initmv = {
+	mv_name:		"hp690",
+
+	mv_nr_irqs:		80, /* HD64461_IRQBASE+16, see hd64461.h */
+
+	mv_inb:			hd64461_inb,
+	mv_inw:			hd64461_inw,
+	mv_inl:			hd64461_inl,
+	mv_outb:		hd64461_outb,
+	mv_outw:		hd64461_outw,
+	mv_outl:		hd64461_outl,
+
+	mv_inb_p:		hd64461_inb_p,
+	mv_inw_p:		hd64461_inw,
+	mv_inl_p:		hd64461_inl,
+	mv_outb_p:		hd64461_outb_p,
+	mv_outw_p:		hd64461_outw,
+	mv_outl_p:		hd64461_outl,
+
+	mv_insb:		hd64461_insb,
+	mv_insw:		hd64461_insw,
+	mv_insl:		hd64461_insl,
+	mv_outsb:		hd64461_outsb,
+	mv_outsw:		hd64461_outsw,
+	mv_outsl:		hd64461_outsl,
+
+	mv_readb:		generic_readb,
+	mv_readw:		generic_readw,
+	mv_readl:		generic_readl,
+	mv_writeb:		generic_writeb,
+	mv_writew:		generic_writew,
+	mv_writel:		generic_writel,
+
+	mv_irq_demux:		hd64461_irq_demux,
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
+
 	mv_hw_hp600:		1,
+	mv_hw_hp690:		1,
 	mv_hw_hd64461:		1,
 };
-ALIAS_MV(hp600)
+ALIAS_MV(hp690)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/mach_se.c linux/arch/sh/kernel/mach_se.c
--- linux-2.4.4-pre2/arch/sh/kernel/mach_se.c	Thu Aug 10 13:03:25 2000
+++ linux/arch/sh/kernel/mach_se.c	Mon Dec  3 22:34:50 2001
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 
 #include <asm/machvec.h>
+#include <asm/rtc.h>
 #include <asm/machvec_init.h>
 
 #include <asm/io_se.h>
@@ -65,7 +66,6 @@
 	mv_writel:		se_writel,
 
 	mv_ioremap:		generic_ioremap,
-	mv_ioremap_nocache:	generic_ioremap_nocache,
 	mv_iounmap:		generic_iounmap,
 
 	mv_isa_port2addr:	se_isa_port2addr,
@@ -75,6 +75,9 @@
 #ifdef CONFIG_HEARTBEAT
 	mv_heartbeat:		heartbeat_se,
 #endif
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
 
 	mv_hw_se:		1,
 };
diff -urN linux-2.4.4-pre2/arch/sh/kernel/mach_unknown.c linux/arch/sh/kernel/mach_unknown.c
--- linux-2.4.4-pre2/arch/sh/kernel/mach_unknown.c	Thu Aug 10 13:03:25 2000
+++ linux/arch/sh/kernel/mach_unknown.c	Mon Dec  3 22:34:50 2001
@@ -17,6 +17,7 @@
 
 #include <asm/io_unknown.h>
 
+#include <asm/rtc.h>
 /*
  * The Machine Vector
  */
@@ -61,9 +62,11 @@
 	mv_writel:		unknown_writel,
 
 	mv_ioremap:		unknown_ioremap,
-	mv_ioremap_nocache:	unknown_ioremap_nocache,
 	mv_iounmap:		unknown_iounmap,
 
 	mv_isa_port2addr:	unknown_isa_port2addr,
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
 };
 ALIAS_MV(unknown)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/pci_st40.c linux/arch/sh/kernel/pci_st40.c
--- linux-2.4.4-pre2/arch/sh/kernel/pci_st40.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/pci_st40.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,494 @@
+/* 
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.                            
+ *
+ * Support functions for the ST40 PCI hardware.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <asm/pci.h>
+#include <linux/irq.h>
+
+#include "pci_st40.h"
+
+/* This is in P2 of course */
+#define ST40PCI_BASE_ADDRESS     (0xb0000000)
+#define ST40PCI_MEM_ADDRESS      (ST40PCI_BASE_ADDRESS+0x0)
+#define ST40PCI_IO_ADDRESS       (ST40PCI_BASE_ADDRESS+0x06000000)
+#define ST40PCI_REG_ADDRESS      (ST40PCI_BASE_ADDRESS+0x07000000)
+
+#define ST40PCI_REG(x) (ST40PCI_REG_ADDRESS+(ST40PCI_##x))
+
+#define ST40PCI_WRITE(reg,val) writel((val),ST40PCI_REG(reg))
+#define ST40PCI_WRITE_SHORT(reg,val) writew((val),ST40PCI_REG(reg))
+#define ST40PCI_WRITE_BYTE(reg,val) writeb((val),ST40PCI_REG(reg))
+
+#define ST40PCI_READ(reg) readl(ST40PCI_REG(reg))
+#define ST40PCI_READ_SHORT(reg) readw(ST40PCI_REG(reg))
+#define ST40PCI_READ_BYTE(reg) readb(ST40PCI_REG(reg))
+
+#define ST40PCI_SERR_IRQ        64
+#define ST40PCI_SERR_INT_GROUP  0
+#define ST40PCI_SERR_INT_POS    0
+#define ST40PCI_SERR_INT_PRI    15
+
+#define ST40PCI_ERR_IRQ        65
+#define ST40PCI_ERR_INT_GROUP   1
+#define ST40PCI_ERR_INT_POS     1
+#define ST40PCI_ERR_INT_PRI     14
+
+
+/* Macros to extract PLL params */
+#define PLL_MDIV(reg)  ( ((unsigned)reg) & 0xff )
+#define PLL_NDIV(reg) ( (((unsigned)reg)>>8) & 0xff )
+#define PLL_PDIV(reg) ( (((unsigned)reg)>>16) & 0x3 )
+#define PLL_SETUP(reg) ( (((unsigned)reg)>>19) & 0x1ff )
+
+/* Build up the appropriate settings */
+#define PLL_SET(mdiv,ndiv,pdiv,setup) \
+( ((mdiv)&0xff) | (((ndiv)&0xff)<<8) | (((pdiv)&3)<<16)| (((setup)&0x1ff)<<19))
+
+#define PLLPCICR (0xbb040000+0x10)
+
+#define PLLPCICR_POWERON (1<<28)
+#define PLLPCICR_OUT_EN (1<<29)
+#define PLLPCICR_LOCKSELECT (1<<30)
+#define PLLPCICR_LOCK (1<<31)
+
+
+#define PLL_25MHZ 0x793c8512
+#define PLL_33MHZ PLL_SET(18,88,3,295)
+
+
+static __init void SetPCIPLL(void)
+{
+	/* Stop the PLL */
+	writel(0, PLLPCICR);
+
+	/* Always run at 33Mhz. The PCI clock is totally async 
+	 * to the rest of the system
+	 */
+	writel(PLL_33MHZ | PLLPCICR_POWERON, PLLPCICR);
+
+	printk("ST40PCI: Waiting for PCI PLL to lock\n");
+	while ((readl(PLLPCICR) & PLLPCICR_LOCK) == 0);
+	writel(readl(PLLPCICR) | PLLPCICR_OUT_EN, PLLPCICR);
+}
+
+
+static void st40_pci_irq(int irq, void *dev_instance, struct pt_regs *regs)
+{
+
+	unsigned pci_int, pci_air, pci_cir, pci_aint;
+
+	pci_int = ST40PCI_READ(INT);
+	pci_cir = ST40PCI_READ(CIR);
+	pci_air = ST40PCI_READ(AIR);
+
+	if (pci_int) {
+		printk("PCI INTERRUPT!\n");
+		printk("PCI INT -> 0x%x\n", pci_int & 0xffff);
+		printk("PCI AIR -> 0x%x\n", pci_air);
+		printk("PCI CIR -> 0x%x\n", pci_cir);
+		ST40PCI_WRITE(INT, ~0);
+	}
+
+	pci_aint = ST40PCI_READ(AINT);
+	if (pci_aint) {
+		printk("PCI ARB INTERRUPT!\n");
+		printk("PCI AINT -> 0x%x\n", pci_aint);
+		printk("PCI AIR -> 0x%x\n", pci_air);
+		printk("PCI CIR -> 0x%x\n", pci_cir);
+		ST40PCI_WRITE(AINT, ~0);
+	}
+
+}
+
+
+/* Rounds a number UP to the nearest power of two. Used for
+ * sizing the PCI window.
+ */
+static u32 __init r2p2(u32 num)
+{
+	int i = 31;
+	u32 tmp = num;
+
+	if (num == 0)
+		return 0;
+
+	do {
+		if (tmp & (1 << 31))
+			break;
+		i--;
+		tmp <<= 1;
+	} while (i >= 0);
+
+	tmp = 1 << i;
+	/* If the original number isn't a power of 2, round it up */
+	if (tmp != num)
+		tmp <<= 1;
+
+	return tmp;
+}
+
+
+int __init st40pci_init(unsigned memStart, unsigned memSize)
+{
+	u32 lsr0;
+
+	SetPCIPLL();
+
+	/* Initialises the ST40 pci subsystem, performing a reset, then programming
+	 * up the address space decoders appropriately
+	 */
+
+	/* Should reset core here as well methink */
+
+	ST40PCI_WRITE(CR, CR_LOCK_MASK | CR_SOFT_RESET);
+
+	/* Loop while core resets */
+	while (ST40PCI_READ(CR) & CR_SOFT_RESET);
+
+	/* Now, lets reset all the cards on the bus with extreme prejudice */
+	ST40PCI_WRITE(CR, CR_LOCK_MASK | CR_RSTCTL);
+	udelay(250);
+
+	/* Set bus active, take it out of reset */
+	ST40PCI_WRITE(CR, CR_LOCK_MASK | CR_CFINT | CR_PFCS | CR_PFE);
+
+	/* The PCI spec says that no access must be made to the bus until 1 second
+	 * after reset. This seem ludicrously long, but some delay is needed here
+	 */
+	mdelay(1000);
+
+	/* Switch off interrupts */
+	ST40PCI_WRITE(INTM, 0);
+	ST40PCI_WRITE(AINT, 0);
+
+	/* Allow it to be a master */
+
+	ST40PCI_WRITE_SHORT(CSR_CMD,
+			    PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+			    PCI_COMMAND_IO);
+
+	/* Accesse to the 0xb0000000 -> 0xb6000000 area will go through to 0x10000000 -> 0x16000000
+	 * on the PCI bus. This allows a nice 1-1 bus to phys mapping.
+	 */
+
+
+	ST40PCI_WRITE(MBR, 0x10000000);
+	/* Always set the max size 128M (actually, it is only 96MB wide) */
+	ST40PCI_WRITE(MBMR, 0x07ff0000);
+
+	/* I/O addresses are mapped at 0xb6000000 -> 0xb7000000. These are changed to 0, to 
+	 * allow cards that have legacy io such as vga to function correctly. This gives a 
+	 * maximum of 64K of io/space as only the bottom 16 bits of the address are copied 
+	 * over to the bus  when the transaction is made. 64K of io space is more than enough
+	 */
+	ST40PCI_WRITE(IOBR, 0x0);
+	/* Set up the 64K window */
+	ST40PCI_WRITE(IOBMR, 0x0);
+
+	/* Now we set up the mbars so the PCI bus can see the memory of the machine */
+
+	if (memSize < (64 * 1024)) {
+		printk("Ridiculous memory size of 0x%x?\n",memSize);
+		return 0;
+	}
+
+	lsr0 =
+	    (memSize >
+	     (512 * 1024 * 1024)) ? 0x1fff0001 : ((r2p2(memSize) -
+						   0x10000) | 0x1);
+
+	ST40PCI_WRITE(LSR0, lsr0);
+
+	ST40PCI_WRITE(CSR_MBAR0, memStart);
+	ST40PCI_WRITE(LAR0, memStart);
+
+	/* Maximise timeout values */
+	ST40PCI_WRITE_BYTE(CSR_TRDY, 0xff);
+	ST40PCI_WRITE_BYTE(CSR_RETRY, 0xff);
+	ST40PCI_WRITE_BYTE(CSR_MIT, 0xff);
+
+
+	/* Install the pci interrupt handlers */
+	make_intc2_irq(ST40PCI_SERR_IRQ, INTC2_BASE0,
+		       ST40PCI_SERR_INT_GROUP, ST40PCI_SERR_INT_POS,
+		       ST40PCI_SERR_INT_PRI);
+
+	make_intc2_irq(ST40PCI_ERR_IRQ, INTC2_BASE0, ST40PCI_ERR_INT_GROUP,
+		       ST40PCI_ERR_INT_POS, ST40PCI_ERR_INT_PRI);
+
+
+	return 1;
+}
+
+
+#define SET_CONFIG_BITS(bus,devfn,where)\
+  (((bus) << 16) | ((devfn) << 8) | ((where) & ~3) | (bus!=0))
+
+#define CONFIG_CMD(dev, where) SET_CONFIG_BITS((dev)->bus->number,(dev)->devfn,where)
+
+
+static int CheckForMasterAbort(void)
+{
+	if (ST40PCI_READ(INT) & INT_MADIM) {
+		/* Should we clear config space version as well ??? */
+		ST40PCI_WRITE(INT, INT_MADIM);
+		ST40PCI_WRITE_SHORT(CSR_STATUS, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Write to config register */
+static int st40pci_read_config_byte(struct pci_dev *dev, int where,
+				    u8 * val)
+{
+	ST40PCI_WRITE(PAR, CONFIG_CMD(dev, where));
+
+	*val = ST40PCI_READ_BYTE(PDR + (where & 3));
+
+	if (CheckForMasterAbort())
+		*val = 0xff;
+
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int st40pci_read_config_word(struct pci_dev *dev, int where,
+				    u16 * val)
+{
+	ST40PCI_WRITE(PAR, CONFIG_CMD(dev, where));
+
+	*val = ST40PCI_READ_SHORT(PDR + (where & 2));
+
+	if (CheckForMasterAbort())
+		*val = 0xffff;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int st40pci_read_config_dword(struct pci_dev *dev, int where,
+				     u32 * val)
+{
+
+	ST40PCI_WRITE(PAR, CONFIG_CMD(dev, where));
+
+	*val = ST40PCI_READ(PDR);
+
+	if (CheckForMasterAbort())
+		*val = 0xffffffff;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int st40pci_write_config_byte(struct pci_dev *dev, int where,
+				     u8 val)
+{
+	ST40PCI_WRITE(PAR, CONFIG_CMD(dev, where));
+
+	ST40PCI_WRITE_BYTE(PDR + (where & 3), val);
+
+	CheckForMasterAbort();
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int st40pci_write_config_word(struct pci_dev *dev, int where,
+				     u16 val)
+{
+	ST40PCI_WRITE(PAR, CONFIG_CMD(dev, where));
+
+	ST40PCI_WRITE_SHORT(PDR + (where & 2), val);
+
+	CheckForMasterAbort();
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int st40pci_write_config_dword(struct pci_dev *dev, int where,
+				      u32 val)
+{
+	ST40PCI_WRITE(PAR, CONFIG_CMD(dev, where));
+
+	ST40PCI_WRITE(PDR, val);
+
+	CheckForMasterAbort();
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_config_ops = {
+	st40pci_read_config_byte,
+	st40pci_read_config_word,
+	st40pci_read_config_dword,
+	st40pci_write_config_byte,
+	st40pci_write_config_word,
+	st40pci_write_config_dword
+};
+
+
+/* Everything hangs off this */
+static struct pci_bus *pci_root_bus;
+
+
+static u8 __init no_swizzle(struct pci_dev *dev, u8 * pin)
+{
+	return PCI_SLOT(dev->devfn);
+}
+
+
+/* This needs to be shunted out of here into the board specific bit */
+#define HARP_PCI_IRQ    1
+#define HARP_BRIDGE_IRQ 2
+#define OVERDRIVE_SLOT0_IRQ 0
+
+static int __init map_harp_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	switch (slot) {
+#ifdef CONFIG_SH_STB1_HARP
+	case 2:		/*This is the PCI slot on the */
+		return HARP_PCI_IRQ;
+	case 1:		/* this is the bridge */
+		return HARP_BRIDGE_IRQ;
+#elif defined(CONFIG_SH_STB1_OVERDRIVE)
+	case 1:
+	case 2:
+	case 3:
+		return slot - 1;
+#else
+#error Unknown board
+#endif
+	default:
+		return -1;
+	}
+}
+
+
+void __init
+pcibios_fixup_pbus_ranges(struct pci_bus *bus,
+			  struct pbus_set_ranges_data *ranges)
+{
+	ranges->io_start -= bus->resource[0]->start;
+	ranges->io_end -= bus->resource[0]->start;
+	ranges->mem_start -= bus->resource[1]->start;
+	ranges->mem_end -= bus->resource[1]->start;
+}
+
+void __init st40_pcibios_init(void)
+{
+	extern unsigned long memory_start, memory_end;
+
+	/* The pci subsytem needs to know where memory is and how much 
+	 * of it there is. I've simply made these globals. A better mechanism
+	 * is probably needed.
+	 */
+	st40pci_init(PHYSADDR(memory_start),
+		     PHYSADDR(memory_end) - PHYSADDR(memory_start));
+
+	if (request_irq(ST40PCI_ERR_IRQ, st40_pci_irq, 
+                        SA_INTERRUPT, "st40pci", NULL)) {
+		printk(KERN_ERR "st40pci: Cannot hook interrupt\n");
+		return;
+	}
+
+	/* Enable the PCI interrupts on the device */
+	ST40PCI_WRITE(INTM, ~0);
+	ST40PCI_WRITE(AINT, ~0);
+
+	/* Map the io address apprioately */
+#ifdef CONFIG_HD64465
+	hd64465_port_map(PCIBIOS_MIN_IO, (64 * 1024) - PCIBIOS_MIN_IO + 1,
+			 ST40_IO_ADDR + PCIBIOS_MIN_IO, 0);
+#endif
+
+	/* ok, do the scan man */
+	pci_root_bus = pci_scan_bus(0, &pci_config_ops, NULL);
+	pci_assign_unassigned_resources();
+	pci_fixup_irqs(no_swizzle, map_harp_irq);
+
+}
+
+int pcibios_enable_device(struct pci_dev *dev)
+{
+
+	u16 cmd, old_cmd;
+	int idx;
+	struct resource *r;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	old_cmd = cmd;
+	for (idx = 0; idx < 6; idx++) {
+		r = dev->resource + idx;
+		if (!r->start && r->end) {
+			printk(KERN_ERR
+			       "PCI: Device %s not available because"
+			       " of resource collisions\n",
+			       dev->slot_name);
+			return -EINVAL;
+		}
+		if (r->flags & IORESOURCE_IO)
+			cmd |= PCI_COMMAND_IO;
+		if (r->flags & IORESOURCE_MEM)
+			cmd |= PCI_COMMAND_MEMORY;
+	}
+	if (cmd != old_cmd) {
+		printk("PCI: enabling device %s (%04x -> %04x)\n",
+		       dev->slot_name, old_cmd, cmd);
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+	}
+	return 0;
+
+}
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+}
+
+void pcibios_align_resource(void *data, struct resource *res,
+			    unsigned long size)
+{
+}
+
+void __init pcibios_update_resource(struct pci_dev *dev,
+				    struct resource *root,
+				    struct resource *res, int resource)
+{
+
+	unsigned long where, size;
+	u32 reg;
+
+	printk("PCI: Assigning %3s %08lx to %s\n",
+	       res->flags & IORESOURCE_IO ? "IO" : "MEM",
+	       res->start, dev->name);
+
+	where = PCI_BASE_ADDRESS_0 + resource * 4;
+	size = res->end - res->start;
+
+	pci_read_config_dword(dev, where, &reg);
+	reg = (reg & size) | (((u32) (res->start - root->start)) & ~size);
+	pci_write_config_dword(dev, where, reg);
+
+}
+
+
+void __init pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+	printk("PCI: Assigning IRQ %02d to %s\n", irq, dev->name);
+	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
diff -urN linux-2.4.4-pre2/arch/sh/kernel/pci_st40.h linux/arch/sh/kernel/pci_st40.h
--- linux-2.4.4-pre2/arch/sh/kernel/pci_st40.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/pci_st40.h	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,66 @@
+/* 
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.                            
+ *
+ * Defintions for the ST40 PCI hardware.
+ */
+
+#ifndef __PCI_ST40_H__
+#define __PCI_ST40_H__
+
+#define ST40PCI_VCR_STATUS    0x00
+
+#define ST40PCI_VCR_VERSION   0x08
+
+#define ST40PCI_CR            0x10
+
+#define CR_SOFT_RESET (1<<12)
+#define CR_PFCS       (1<<11)
+#define CR_PFE        (1<<9)
+#define CR_BMAM       (1<<6)
+#define CR_HOST       (1<<5)
+#define CR_CLKEN      (1<<4)
+#define CR_SOCS       (1<<3)
+#define CR_IOCS       (1<<2)
+#define CR_RSTCTL     (1<<1)
+#define CR_CFINT      (1<<0)
+#define CR_LOCK_MASK  0x5a000000
+
+
+#define ST40PCI_LSR0          0X14
+#define ST40PCI_LAR0          0x1c
+
+#define ST40PCI_INT           0x24
+#define INT_MADIM             (1<<2)
+
+
+#define ST40PCI_INTM          0x28
+#define ST40PCI_AIR           0x2c
+#define ST40PCI_CIR           0x30
+#define ST40PCI_AINT          0x40
+#define ST40PCI_AINTM         0x44
+#define ST40PCI_BMIR          0x48
+#define ST40PCI_PAR           0x4c
+#define ST40PCI_MBR           0x50
+#define ST40PCI_IOBR          0x54
+#define ST40PCI_PINT          0x58
+#define ST40PCI_PINTM         0x5c
+#define ST40PCI_MBMR          0x70
+#define ST40PCI_IOBMR         0x74
+#define ST40PCI_PDR           0x78
+
+/* These are configs space registers */
+#define ST40PCI_CSR_VID               0x10000
+#define ST40PCI_CSR_DID               0x10002
+#define ST40PCI_CSR_CMD               0x10004
+#define ST40PCI_CSR_STATUS            0x10006
+#define ST40PCI_CSR_MBAR0             0x10010
+#define ST40PCI_CSR_TRDY              0x10040
+#define ST40PCI_CSR_RETRY             0x10041
+#define ST40PCI_CSR_MIT               0x1000d
+
+#define ST40_IO_ADDR 0xb6000000       
+
+#endif /* __PCI_ST40_H__ */
diff -urN linux-2.4.4-pre2/arch/sh/kernel/process.c linux/arch/sh/kernel/process.c
--- linux-2.4.4-pre2/arch/sh/kernel/process.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/sh/kernel/process.c	Mon Dec  3 22:34:50 2001
@@ -139,10 +139,6 @@
 /*
  * This is the mechanism for creating a new kernel thread.
  *
- * NOTE! Only a kernel-only process(ie the swapper or direct descendants
- * who haven't done an "execve()") should use this: it will work within
- * a system call from a "real" process, but the process memory space will
- * not be free'd until both the parent and the child have exited.
  */
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {	/* Don't use this in BL=1(cli).  Or else, CPU resets! */
@@ -154,7 +150,7 @@
 	register unsigned long __sc9 __asm__ ("r9") = (long) fn;
 
 	__asm__("trapa	#0x12\n\t" 	/* Linux/SH system call */
-		"tst	#0xff, r0\n\t"	/* child or parent? */
+		"tst	r0, r0\n\t"	/* child or parent? */
 		"bf	1f\n\t"		/* parent - jump */
 		"jsr	@r9\n\t"	/* call fn */
 		" mov	r8, r4\n\t"	/* push argument */
diff -urN linux-2.4.4-pre2/arch/sh/kernel/ptrace.c linux/arch/sh/kernel/ptrace.c
--- linux-2.4.4-pre2/arch/sh/kernel/ptrace.c	Fri Dec 29 14:07:20 2000
+++ linux/arch/sh/kernel/ptrace.c	Mon Dec  3 22:34:50 2001
@@ -175,14 +175,16 @@
 	if (request == PTRACE_ATTACH) {
 		if (child == tsk)
 			goto out_tsk;
-		if ((!child->dumpable ||
-		    (tsk->uid != child->euid) ||
+		if(((tsk->uid != child->euid) ||
 		    (tsk->uid != child->suid) ||
 		    (tsk->uid != child->uid) ||
 	 	    (tsk->gid != child->egid) ||
 	 	    (tsk->gid != child->sgid) ||
 	 	    (!cap_issubset(child->cap_permitted, tsk->cap_permitted)) ||
 	 	    (tsk->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+			goto out_tsk;
+		rmb();
+		if (!child->dumpable && !capable(CAP_SYS_PTRACE))
 			goto out_tsk;
 		/* the same process cannot be attached many times */
 		if (child->ptrace & PT_PTRACED)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/rtc.c linux/arch/sh/kernel/rtc.c
--- linux-2.4.4-pre2/arch/sh/kernel/rtc.c	Thu Jan  4 15:25:55 2001
+++ linux/arch/sh/kernel/rtc.c	Mon Dec  3 22:34:50 2001
@@ -7,6 +7,7 @@
 
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/time.h>
 
 #include <asm/io.h>
@@ -44,6 +45,8 @@
 #define RMONAR  	0xfffffeda
 #define RCR1    	0xfffffedc
 #define RCR2    	0xfffffede
+
+#define RTC_BIT_INVERTED	0	/* No bug on SH7708, SH7709A */
 #elif defined(__SH4__)
 /* SH-4 RTC */
 #define R64CNT  	0xffc80000
@@ -62,6 +65,8 @@
 #define RMONAR  	0xffc80034
 #define RCR1    	0xffc80038
 #define RCR2    	0xffc8003c
+
+#define RTC_BIT_INVERTED	0x40	/* bug on SH7750, SH7750S */
 #endif
 
 #ifndef BCD_TO_BIN
@@ -79,7 +84,7 @@
  again:
 	do {
 		ctrl_outb(0, RCR1);  /* Clear CF-bit */
-		sec128 = ctrl_inb(RSECCNT);
+		sec128 = ctrl_inb(R64CNT);
 		sec = ctrl_inb(RSECCNT);
 		min = ctrl_inb(RMINCNT);
 		hr  = ctrl_inb(RHRCNT);
@@ -96,6 +101,14 @@
 #endif
 	} while ((ctrl_inb(RCR1) & RCR1_CF) != 0);
 
+#if RTC_BIT_INVERTED != 0
+	/* Work around to avoid reading correct value. */
+	if (sec128 == RTC_BIT_INVERTED) {
+		schedule_timeout(1);
+		goto again;
+	}
+#endif
+
 	BCD_TO_BIN(yr100);
 	BCD_TO_BIN(yr);
 	BCD_TO_BIN(mon);
@@ -125,7 +138,7 @@
 	}
 
 	tv->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
-	tv->tv_usec = (sec128 * 1000000) / 128;
+	tv->tv_usec = ((sec128 ^ RTC_BIT_INVERTED) * 1000000) / 128;
 }
 
 static int set_rtc_time(unsigned long nowtime)
@@ -170,5 +183,9 @@
 
 int sh_rtc_settimeofday(const struct timeval *tv)
 {
+#if RTC_BIT_INVERTED != 0
+	/* This is not accurate, but better than nothing. */
+	schedule_timeout(HZ/2);
+#endif
 	return set_rtc_time(tv->tv_sec);
 }
diff -urN linux-2.4.4-pre2/arch/sh/kernel/setup.c linux/arch/sh/kernel/setup.c
--- linux-2.4.4-pre2/arch/sh/kernel/setup.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/sh/kernel/setup.c	Mon Dec  3 22:34:50 2001
@@ -50,6 +50,7 @@
 
 struct sh_cpuinfo boot_cpu_data = { CPU_SH_NONE, 0, 0, 0, };
 struct screen_info screen_info;
+unsigned char aux_device_present = 0xaa;
 
 #ifdef CONFIG_BLK_DEV_RAM
 extern int rd_doload;		/* 1 = load ramdisk, 0 = don't load */
@@ -61,6 +62,19 @@
 struct sh_machine_vector sh_mv;
 #endif
 
+/* We need this to satisfy some external references. */
+struct screen_info screen_info = {
+        0, 25,                  /* orig-x, orig-y */
+        0,                      /* unused */
+        0,                      /* orig-video-page */
+        0,                      /* orig-video-mode */
+        80,                     /* orig-video-cols */
+        0,0,0,                  /* ega_ax, ega_bx, ega_cx */
+        25,                     /* orig-video-lines */
+        0,                      /* orig-video-isVGA */
+        16                      /* orig-video-points */
+};
+
 extern void fpu_init(void);
 extern int root_mountflags;
 extern int _text, _etext, _edata, _end;
@@ -115,7 +129,7 @@
 	{ "Kernel data", 0, 0 }
 };
 
-static unsigned long memory_start, memory_end;
+unsigned long memory_start, memory_end;
 
 #ifdef CONFIG_SH_EARLY_PRINTK
 /*
diff -urN linux-2.4.4-pre2/arch/sh/kernel/setup_cqreek.c linux/arch/sh/kernel/setup_cqreek.c
--- linux-2.4.4-pre2/arch/sh/kernel/setup_cqreek.c	Mon Oct  2 11:57:34 2000
+++ linux/arch/sh/kernel/setup_cqreek.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: setup_cqreek.c,v 1.5 2000/09/18 05:51:24 gniibe Exp $
+/* $Id: setup_cqreek.c,v 1.6 2001/02/14 09:36:42 gniibe Exp $
  *
  * arch/sh/kernel/setup_cqreek.c
  *
@@ -18,6 +18,7 @@
 #include <asm/irq.h>
 #include <asm/machvec.h>
 #include <asm/machvec_init.h>
+#include <asm/rtc.h>
 
 #define BRIDGE_FEATURE		0x0002
 
@@ -247,5 +248,11 @@
 	mv_init_irq:		init_cqreek_IRQ,
 
 	mv_isa_port2addr:	cqreek_port2addr,
+
+	mv_ioremap:		generic_ioremap,
+	mv_iounmap:		generic_iounmap,
+
+	mv_rtc_gettimeofday:	sh_rtc_gettimeofday,
+	mv_rtc_settimeofday:	sh_rtc_settimeofday,
 };
 ALIAS_MV(cqreek)
diff -urN linux-2.4.4-pre2/arch/sh/kernel/setup_dc.c linux/arch/sh/kernel/setup_dc.c
--- linux-2.4.4-pre2/arch/sh/kernel/setup_dc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/setup_dc.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,360 @@
+/*
+ *	$Id: setup_dc.c,v 1.1 2001/04/01 15:02:00 yaegashi Exp $
+ *	SEGA Dreamcast support
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define	GAPSPCI_REGS		0x01001400
+#define GAPSPCI_DMA_BASE	0x01840000
+#define GAPSPCI_DMA_SIZE	32768
+#define GAPSPCI_BBA_CONFIG	0x01001600
+
+#define	GAPSPCI_IRQ		11
+#define	GAPSPCI_INTC		0x005f6924
+
+static int gapspci_dma_used;
+
+static struct pci_bus *pci_root_bus;
+
+static void disable_gapspci_irq(unsigned int irq)
+{
+	unsigned long flags;
+	unsigned long intc;
+
+	save_and_cli(flags);
+	intc = inl(GAPSPCI_INTC);
+	intc &= ~(1<<3);
+	outl(intc, GAPSPCI_INTC);
+	restore_flags(flags);
+}
+
+
+static void enable_gapspci_irq(unsigned int irq)
+{
+	unsigned long flags;
+	unsigned long intc;
+
+	save_and_cli(flags);
+	intc = inl(GAPSPCI_INTC);
+	intc |= (1<<3);
+	outl(intc, GAPSPCI_INTC);
+	restore_flags(flags);
+}
+
+
+static void mask_and_ack_gapspci_irq(unsigned int irq)
+{
+	unsigned long flags;
+	unsigned long intc;
+
+	save_and_cli(flags);
+	intc = inl(GAPSPCI_INTC);
+	intc &= ~(1<<3);
+	outl(intc, GAPSPCI_INTC);
+	restore_flags(flags);
+}
+
+
+static void end_gapspci_irq(unsigned int irq)
+{
+	enable_gapspci_irq(irq);
+}
+
+
+static unsigned int startup_gapspci_irq(unsigned int irq)
+{ 
+	enable_gapspci_irq(irq);
+	return 0;
+}
+
+
+static void shutdown_gapspci_irq(unsigned int irq)
+{
+	disable_gapspci_irq(irq);
+}
+
+
+static struct hw_interrupt_type gapspci_irq_type = {
+	"GAPSPCI-IRQ",
+	startup_gapspci_irq,
+	shutdown_gapspci_irq,
+	enable_gapspci_irq,
+	disable_gapspci_irq,
+	mask_and_ack_gapspci_irq,
+	end_gapspci_irq
+};
+
+
+static u8 __init no_swizzle(struct pci_dev *dev, u8 * pin)
+{
+	return PCI_SLOT(dev->devfn);
+}
+
+static int __init map_od_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	return GAPSPCI_IRQ;
+}
+
+int __init setup_dreamcast(void)
+{
+	gapspci_init();
+
+	printk(KERN_INFO "SEGA Dreamcast support.\n");
+#if 0
+	printk(KERN_INFO "BCR1: 0x%08x\n", ctrl_inl(0xff800000));
+	printk(KERN_INFO "BCR2: 0x%08x\n", ctrl_inw(0xff800004));
+	printk(KERN_INFO "WCR1: 0x%08x\n", ctrl_inl(0xff800008));
+	printk(KERN_INFO "WCR2: 0x%08x\n", ctrl_inl(0xff80000c));
+	printk(KERN_INFO "WCR3: 0x%08x\n", ctrl_inl(0xff800010));
+	printk(KERN_INFO "MCR: 0x%08x\n", ctrl_inl(0xff800014));
+	printk(KERN_INFO "PCR: 0x%08x\n", ctrl_inw(0xff800018));
+/*
+ *	BCR1: 0xa3020008
+ *	BCR2: 0x0001
+ *	WCR1: 0x01110111
+ *	WCR2: 0x618066d8
+ *	WCR3: 0x07777777
+ *	MCR: 0xc00a0e24
+ *	PCR: 0x0000
+ */
+#endif
+	return 0;
+}
+
+
+/*
+ *	Dreamcast PCI: Supports SEGA Broadband Adaptor only.
+ */
+
+#define BBA_SELECTED(dev) (dev->bus->number==0 && dev->devfn==0)
+
+static int gapspci_read_config_byte(struct pci_dev *dev, int where,
+                                    u8 * val)
+{
+	if (BBA_SELECTED(dev))
+		*val = inb(GAPSPCI_BBA_CONFIG+where);
+	else
+                *val = 0xff;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int gapspci_read_config_word(struct pci_dev *dev, int where,
+                                    u16 * val)
+{
+        if (BBA_SELECTED(dev))
+		*val = inw(GAPSPCI_BBA_CONFIG+where);
+	else
+                *val = 0xffff;
+
+        return PCIBIOS_SUCCESSFUL;
+}
+
+static int gapspci_read_config_dword(struct pci_dev *dev, int where,
+                                     u32 * val)
+{
+        if (BBA_SELECTED(dev))
+		*val = inl(GAPSPCI_BBA_CONFIG+where);
+	else
+                *val = 0xffffffff;
+
+        return PCIBIOS_SUCCESSFUL;
+}
+
+static int gapspci_write_config_byte(struct pci_dev *dev, int where,
+                                     u8 val)
+{
+        if (BBA_SELECTED(dev))
+		outb(val, GAPSPCI_BBA_CONFIG+where);
+
+        return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int gapspci_write_config_word(struct pci_dev *dev, int where,
+                                     u16 val)
+{
+        if (BBA_SELECTED(dev))
+		outw(val, GAPSPCI_BBA_CONFIG+where);
+
+        return PCIBIOS_SUCCESSFUL;
+}
+
+static int gapspci_write_config_dword(struct pci_dev *dev, int where,
+                                      u32 val)
+{
+        if (BBA_SELECTED(dev))
+		outl(val, GAPSPCI_BBA_CONFIG+where);
+
+        return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_config_ops = {
+        gapspci_read_config_byte,
+        gapspci_read_config_word,
+        gapspci_read_config_dword,
+        gapspci_write_config_byte,
+        gapspci_write_config_word,
+        gapspci_write_config_dword
+};
+
+
+void pcibios_align_resource(void *data, struct resource *res,
+			    unsigned long size)
+{
+}
+
+
+void __init pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+}
+
+
+void __init pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+			     struct resource *res, int resource)
+{
+}
+
+
+int pcibios_enable_device(struct pci_dev *dev)
+{
+
+	u16 cmd, old_cmd;
+	int idx;
+	struct resource *r;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	old_cmd = cmd;
+	for (idx = 0; idx < 6; idx++) {
+		r = dev->resource + idx;
+		if (!r->start && r->end) {
+			printk(KERN_ERR
+			       "PCI: Device %s not available because"
+			       " of resource collisions\n",
+			       dev->slot_name);
+			return -EINVAL;
+		}
+		if (r->flags & IORESOURCE_IO)
+			cmd |= PCI_COMMAND_IO;
+		if (r->flags & IORESOURCE_MEM)
+			cmd |= PCI_COMMAND_MEMORY;
+	}
+	if (cmd != old_cmd) {
+		printk("PCI: enabling device %s (%04x -> %04x)\n",
+		       dev->slot_name, old_cmd, cmd);
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+	}
+	return 0;
+
+}
+
+
+void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+			   dma_addr_t * dma_handle)
+{
+	unsigned long buf;
+
+	if (gapspci_dma_used+size > GAPSPCI_DMA_SIZE)
+		return NULL;
+
+	buf = GAPSPCI_DMA_BASE+gapspci_dma_used;
+
+	gapspci_dma_used = PAGE_ALIGN(gapspci_dma_used+size);
+	
+	printk("pci_alloc_consistent: %d bytes at 0x%08x\n", size, buf);
+
+	*dma_handle = (dma_addr_t)buf;
+
+	return (void *)P2SEGADDR(buf);
+}
+
+
+void pci_free_consistent(struct pci_dev *hwdev, size_t size,
+			 void *vaddr, dma_addr_t dma_handle)
+{
+}
+
+
+void __init
+pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges)
+{
+}                                                                                
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+	struct list_head *ln;
+	struct pci_dev *dev;
+
+	for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
+		dev = pci_dev_b(ln);
+		if (!BBA_SELECTED(dev)) continue;
+
+		printk("PCI: MMIO fixup to %s\n", dev->name);
+		dev->resource[1].start=0x01001700;
+		dev->resource[1].end=0x010017ff;
+	}
+}
+
+
+void __init dreamcast_pcibios_init(void)
+{
+	pci_root_bus = pci_scan_bus(0, &pci_config_ops, NULL);
+	/* pci_assign_unassigned_resources(); */
+	pci_fixup_irqs(no_swizzle, map_od_irq);
+}
+
+
+static __init int gapspci_init(void)
+{
+	int i;
+	char idbuf[16];
+
+	for(i=0; i<16; i++)
+		idbuf[i]=inb(GAPSPCI_REGS+i);
+
+	if(strncmp(idbuf, "GAPSPCI_BRIDGE_2", 16))
+		return -1;
+
+	outl(0x5a14a501, GAPSPCI_REGS+0x18);
+
+	for(i=0; i<1000000; i++);
+
+	if(inl(GAPSPCI_REGS+0x18)!=1)
+		return -1;
+
+	outl(0x01000000, GAPSPCI_REGS+0x20);
+	outl(0x01000000, GAPSPCI_REGS+0x24);
+
+	outl(GAPSPCI_DMA_BASE, GAPSPCI_REGS+0x28);
+	outl(GAPSPCI_DMA_BASE+GAPSPCI_DMA_SIZE, GAPSPCI_REGS+0x2c);
+
+	outl(1, GAPSPCI_REGS+0x14);
+	outl(1, GAPSPCI_REGS+0x34);
+
+	gapspci_dma_used=0;
+
+	/*  */
+	irq_desc[GAPSPCI_IRQ].handler = &gapspci_irq_type;
+
+	/* Setting Broadband Adapter */
+	outw(0xf900, GAPSPCI_BBA_CONFIG+0x06);
+	outl(0x00000000, GAPSPCI_BBA_CONFIG+0x30);
+	outb(0x00, GAPSPCI_BBA_CONFIG+0x3c);
+	outb(0xf0, GAPSPCI_BBA_CONFIG+0x0d);
+	outw(0x0006, GAPSPCI_BBA_CONFIG+0x04);
+	outl(0x00002001, GAPSPCI_BBA_CONFIG+0x10);
+	outl(0x01000000, GAPSPCI_BBA_CONFIG+0x14);
+
+	return 0;
+}
diff -urN linux-2.4.4-pre2/arch/sh/kernel/setup_ec3104.c linux/arch/sh/kernel/setup_ec3104.c
--- linux-2.4.4-pre2/arch/sh/kernel/setup_ec3104.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/setup_ec3104.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,242 @@
+/*
+ * linux/arch/sh/kernel/setup_ec3104.c
+ *  EC3104 companion chip support
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ *
+ */
+/* EC3104 note:
+ * This code was written without any documentation about the EC3104 chip.  While
+ * I hope I got most of the basic functionality right, the register names I use
+ * are most likely completely different from those in the chip documentation.
+ *
+ * If you have any further information about the EC3104, please tell me
+ * (prumpf@tux.org).
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ec3104.h>
+
+/* This is for debugging mostly;  here's the table that I intend to keep
+ * in here:
+ *
+ *   index	function	base addr	power		interrupt bit   
+ *	 0	power		b0ec0000	---		00000001 (unused)
+ *	 1	irqs		b0ec1000	---		00000002 (unused)
+ *	 2	??		b0ec2000	b0ec0008	00000004
+ *	 3	PS2 (1)		b0ec3000	b0ec000c	00000008
+ *	 4	PS2 (2)		b0ec4000	b0ec0010	00000010
+ *	 5	??		b0ec5000	b0ec0014	00000020
+ *	 6	I2C		b0ec6000	b0ec0018	00000040
+ *	 7	serial (1)	b0ec7000	b0ec001c	00000080
+ *	 8	serial (2)	b0ec8000	b0ec0020	00000100
+ *	 9	serial (3)	b0ec9000	b0ec0024	00000200
+ *	10	serial (4)	b0eca000	b0ec0028	00000400
+ *	12	GPIO (1)	b0ecc000	b0ec0030
+ *	13      GPIO (2)	b0ecc000	b0ec0030
+ *	16	pcmcia (1)	b0ed0000	b0ec0040	00010000
+ *	17	pcmcia (2)	b0ed1000	b0ec0044	00020000
+ */
+
+/* I used the register names from another interrupt controller I worked with,
+ * since it seems to be identical to the ec3104 except that all bits are
+ * inverted:
+ *
+ * IRR: Interrupt Request Register (pending and enabled interrupts)
+ * IMR: Interrupt Mask Register (which interrupts are enabled)
+ * IPR: Interrupt Pending Register (pending interrupts, even disabled ones)
+ *
+ * 0 bits mean pending or enabled, 1 bits mean not pending or disabled.  all
+ * IRQs seem to be level-triggered.
+ */
+
+#define EC3104_IRR (EC3104_BASE + 0x1000)
+#define EC3104_IMR (EC3104_BASE + 0x1004)
+#define EC3104_IPR (EC3104_BASE + 0x1008)
+
+#define ctrl_readl(addr) (*(volatile u32 *)(addr))
+#define ctrl_writel(data,addr) (*(volatile u32 *)(addr) = (data))
+#define ctrl_readb(addr) (*(volatile u8 *)(addr))
+
+static char *ec3104_name(unsigned index)
+{
+	switch(index) {
+	case 0:
+		return "power management";
+	case 1:
+		return "interrupts";
+	case 3:
+		return "PS2 (1)";
+	case 4:
+		return "PS2 (2)";
+	case 5:
+		return "I2C (1)";
+	case 6:
+		return "I2C (2)";
+	case 7:
+		return "serial (1)";
+	case 8:
+		return "serial (2)";
+	case 9:
+		return "serial (3)";
+	case 10:
+		return "serial (4)";
+	case 16:
+		return "pcmcia (1)";
+	case 17:
+		return "pcmcia (2)";
+	default: {
+		static char buf[32];
+		
+		sprintf(buf, "unknown (%d)", index);
+
+		return buf;
+		}
+	}
+}
+
+int get_pending_interrupts(char *buf)
+{
+	u32 ipr;
+	u32 bit;
+	char *p = buf;
+
+        p += sprintf(p, "pending: (");
+	
+	ipr = ctrl_inl(EC3104_IPR);
+
+	for (bit = 1; bit < 32; bit++)
+		if (!(ipr & (1<<bit)))
+			p += sprintf(p, "%s ", ec3104_name(bit));
+	
+	p += sprintf(p, ")\n");
+
+	return p - buf;
+}
+	
+static inline u32 ec3104_irq2mask(unsigned int irq)
+{
+	return (1 << (irq - EC3104_IRQBASE));
+}
+
+static inline void mask_ec3104_irq(unsigned int irq)
+{
+	u32 mask;
+
+	mask = ctrl_readl(EC3104_IMR);
+
+	mask |= ec3104_irq2mask(irq);
+	
+	ctrl_writel(mask, EC3104_IMR);
+}
+	
+static inline void unmask_ec3104_irq(unsigned int irq)
+{
+	u32 mask;
+
+	mask = ctrl_readl(EC3104_IMR);
+
+	mask &= ~ec3104_irq2mask(irq);
+	
+	ctrl_writel(mask, EC3104_IMR);
+}
+	
+static void disable_ec3104_irq(unsigned int irq)
+{
+	mask_ec3104_irq(irq);
+}
+
+static void enable_ec3104_irq(unsigned int irq)
+{
+	unmask_ec3104_irq(irq);
+}
+
+static void mask_and_ack_ec3104_irq(unsigned int irq)
+{
+	mask_ec3104_irq(irq);
+}
+
+static void end_ec3104_irq(unsigned int irq)
+{
+	unmask_ec3104_irq(irq);
+}
+
+static unsigned int startup_ec3104_irq(unsigned int irq)
+{
+	unmask_ec3104_irq(irq);
+
+	return 0;
+}
+
+static void shutdown_ec3104_irq(unsigned int irq)
+{
+	mask_ec3104_irq(irq);
+
+}
+
+static struct hw_interrupt_type ec3104_int = {
+	typename:	"EC3104",
+	enable:		enable_ec3104_irq,
+	disable:	disable_ec3104_irq,
+	ack:		mask_and_ack_ec3104_irq,
+	end:		end_ec3104_irq,
+	startup:	startup_ec3104_irq,
+	shutdown:	shutdown_ec3104_irq,
+};
+
+/* Yuck.  the _demux API is ugly */
+int ec3104_irq_demux(int irq)
+{
+	if (irq == EC3104_IRQ) {
+		unsigned int mask;
+
+		mask = ctrl_readl(EC3104_IRR);
+
+		if (mask == 0xffffffff)
+			return EC3104_IRQ;
+		else
+			return EC3104_IRQBASE + ffz(mask);
+	}
+
+	return irq;
+}
+
+int __init setup_ec3104(void)
+{
+	char str[8];
+	int i;
+	
+	if (!MACH_EC3104)
+		printk("!MACH_EC3104\n");
+
+	if (0)
+		return 0;
+
+	for (i=0; i<8; i++)
+		str[i] = ctrl_readb(EC3104_BASE + i);
+
+	for (i = EC3104_IRQBASE; i < EC3104_IRQBASE + 32; i++)
+		irq_desc[i].handler = &ec3104_int;
+
+	printk("initializing EC3104 \"%.8s\" at %08x, IRQ %d, IRQ base %d\n",
+	       str, EC3104_BASE, EC3104_IRQ, EC3104_IRQBASE);
+
+
+	/* mask all interrupts.  this should have been done by the boot
+	 * loader for us but we want to be sure ... */
+	ctrl_writel(0xffffffff, EC3104_IMR);
+	
+	return 0;
+}
+
+module_init(setup_ec3104);
diff -urN linux-2.4.4-pre2/arch/sh/kernel/setup_hd64461.c linux/arch/sh/kernel/setup_hd64461.c
--- linux-2.4.4-pre2/arch/sh/kernel/setup_hd64461.c	Wed Aug  9 13:59:04 2000
+++ linux/arch/sh/kernel/setup_hd64461.c	Mon Dec  3 22:34:50 2001
@@ -88,7 +88,7 @@
 static void hd64461_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	printk(KERN_INFO
-	       "HD64461: spurious interrupt, nirr: 0x%lx nimr: 0x%lx\n",
+	       "HD64461: spurious interrupt, nirr: 0x%x nimr: 0x%x\n",
 	       inw(HD64461_NIRR), inw(HD64461_NIMR));
 }
 
@@ -104,7 +104,7 @@
 		if (irq == 16) irq = CONFIG_HD64461_IRQ;
 		else irq += HD64461_IRQBASE;
 	}
-	return irq;
+	return __irq_demux(irq);
 }
 
 static struct irqaction irq0  = { hd64461_interrupt, SA_INTERRUPT, 0, "HD64461", NULL, NULL};
@@ -120,10 +120,9 @@
 	printk(KERN_INFO "HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n",
 	       CONFIG_HD64461_IOBASE, CONFIG_HD64461_IRQ,
 	       HD64461_IRQBASE, HD64461_IRQBASE+15);
-#ifdef CONFIG_CPU_SUBTYPE_SH7709
-	/* IRQ line for HD64461 should be set level trigger mode("10"). */
-	/* And this should be done earlier than the kernel starts. */
-	ctrl_outw(0x0200, INTC_ICR1); /* when connected to IRQ4. */
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7709) /* Should be at processor specific part.. */
+	outw(0x2240, INTC_ICR1);
 #endif
 	outw(0xffff, HD64461_NIMR);
 
@@ -135,7 +134,7 @@
 
 #ifdef CONFIG_HD64461_ENABLER
 	printk(KERN_INFO "HD64461: enabling PCMCIA devices\n");
-	outb(0x04, HD64461_PCC1CSCIER);
+	outb(0x4c, HD64461_PCC1CSCIER);
 	outb(0x00, HD64461_PCC1CSCR);
 #endif
 
diff -urN linux-2.4.4-pre2/arch/sh/kernel/setup_hd64465.c linux/arch/sh/kernel/setup_hd64465.c
--- linux-2.4.4-pre2/arch/sh/kernel/setup_hd64465.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sh/kernel/setup_hd64465.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,207 @@
+/*
+ * $Id: setup_hd64465.c,v 1.3 2001/02/07 17:52:53 stuart_menefy Exp $
+ *
+ * Setup and IRQ handling code for the HD64465 companion chip.
+ * by Greg Banks <gbanks@pocketpenguins.com>
+ * Copyright (c) 2000 PocketPenguins Inc
+ *
+ * Derived from setup_hd64461.c which bore the message:
+ * Copyright (C) 2000 YAEGASHI Takeshi
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/hd64465.h>
+
+#undef HD64465_DEBUG
+
+#ifdef HD64465_DEBUG
+#define DPRINTK(args...)	printk(args)
+#else
+#define DPRINTK(args...)
+#endif
+
+static void disable_hd64465_irq(unsigned int irq)
+{
+	unsigned long flags;
+	unsigned short nimr;
+	unsigned short mask = 1 << (irq - HD64465_IRQ_BASE);
+
+    	DPRINTK("disable_hd64465_irq(%d): mask=%x\n", irq, mask);
+	save_and_cli(flags);
+	nimr = inw(HD64465_REG_NIMR);
+	nimr |= mask;
+	outw(nimr, HD64465_REG_NIMR);
+	restore_flags(flags);
+}
+
+
+static void enable_hd64465_irq(unsigned int irq)
+{
+	unsigned long flags;
+	unsigned short nimr;
+	unsigned short mask = 1 << (irq - HD64465_IRQ_BASE);
+
+    	DPRINTK("enable_hd64465_irq(%d): mask=%x\n", irq, mask);
+	save_and_cli(flags);
+	nimr = inw(HD64465_REG_NIMR);
+	nimr &= ~mask;
+	outw(nimr, HD64465_REG_NIMR);
+	restore_flags(flags);
+}
+
+
+static void mask_and_ack_hd64465(unsigned int irq)
+{
+	disable_hd64465_irq(irq);
+}
+
+
+static void end_hd64465_irq(unsigned int irq)
+{
+	enable_hd64465_irq(irq);
+}
+
+
+static unsigned int startup_hd64465_irq(unsigned int irq)
+{ 
+	enable_hd64465_irq(irq);
+	return 0;
+}
+
+
+static void shutdown_hd64465_irq(unsigned int irq)
+{
+	disable_hd64465_irq(irq);
+}
+
+
+static struct hw_interrupt_type hd64465_irq_type = {
+	typename:	"HD64465-IRQ",
+	startup:	startup_hd64465_irq,
+	shutdown:	shutdown_hd64465_irq,
+	enable:		enable_hd64465_irq,
+	disable:	disable_hd64465_irq,
+	ack:		mask_and_ack_hd64465,
+	end:		end_hd64465_irq
+};
+
+
+static void hd64465_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	printk(KERN_INFO
+	       "HD64465: spurious interrupt, nirr: 0x%x nimr: 0x%x\n",
+	       inw(HD64465_REG_NIRR), inw(HD64465_REG_NIMR));
+}
+
+
+/*====================================================*/
+
+/*
+ * Support for a secondary IRQ demux step.  This is necessary
+ * because the HD64465 presents a very thin interface to the
+ * PCMCIA bus; a lot of features (such as remapping interrupts)
+ * normally done in hardware by other PCMCIA host bridges is
+ * instead done in software.
+ */
+static struct
+{
+    int (*func)(int, void *);
+    void *dev;
+} hd64465_demux[HD64465_IRQ_NUM];
+
+void hd64465_register_irq_demux(int irq,
+		int (*demux)(int irq, void *dev), void *dev)
+{
+    	hd64465_demux[irq - HD64465_IRQ_BASE].func = demux;
+    	hd64465_demux[irq - HD64465_IRQ_BASE].dev = dev;
+}
+EXPORT_SYMBOL(hd64465_register_irq_demux);
+
+void hd64465_unregister_irq_demux(int irq)
+{
+    	hd64465_demux[irq - HD64465_IRQ_BASE].func = 0;
+}
+EXPORT_SYMBOL(hd64465_unregister_irq_demux);
+
+
+
+int hd64465_irq_demux(int irq)
+{
+	if (irq == CONFIG_HD64465_IRQ) {
+		unsigned short i, bit;
+		unsigned short nirr = inw(HD64465_REG_NIRR);
+		unsigned short nimr = inw(HD64465_REG_NIMR);
+
+    	    	DPRINTK("hd64465_irq_demux, nirr=%04x, nimr=%04x\n", nirr, nimr);
+		nirr &= ~nimr;
+		for (bit = 1, i = 0 ; i < HD64465_IRQ_NUM ; bit <<= 1, i++)
+		    if (nirr & bit)
+		    	break;
+
+    	    	if (i < HD64465_IRQ_NUM) {
+		    irq = HD64465_IRQ_BASE + i;
+    	    	    if (hd64465_demux[i].func != 0)
+		    	irq = hd64465_demux[i].func(irq, hd64465_demux[i].dev);
+		}
+	}
+	return irq;
+}
+
+static struct irqaction irq0  = { hd64465_interrupt, SA_INTERRUPT, 0, "HD64465", NULL, NULL};
+
+
+static int __init setup_hd64465(void)
+{
+	int i;
+	unsigned short rev;
+	unsigned short smscr;
+
+	if (!MACH_HD64465)
+		return 0;
+
+	printk(KERN_INFO "HD64465 configured at 0x%x on irq %d(mapped into %d to %d)\n",
+	       CONFIG_HD64465_IOBASE,
+	       CONFIG_HD64465_IRQ,
+	       HD64465_IRQ_BASE,
+	       HD64465_IRQ_BASE+HD64465_IRQ_NUM-1);
+
+	if (inw(HD64465_REG_SDID) != HD64465_SDID) {
+		printk(KERN_ERR "HD64465 device ID not found, check base address\n");
+	}
+
+	rev = inw(HD64465_REG_SRR);
+	printk(KERN_INFO "HD64465 hardware revision %d.%d\n", (rev >> 8) & 0xff, rev & 0xff);
+	       
+	outw(0xffff, HD64465_REG_NIMR); 	/* mask all interrupts */
+
+	for (i = 0; i < HD64465_IRQ_NUM ; i++) {
+		irq_desc[HD64465_IRQ_BASE + i].handler = &hd64465_irq_type;
+	}
+
+	setup_irq(CONFIG_HD64465_IRQ, &irq0);
+
+#ifdef CONFIG_SERIAL
+	/* wake up the UART from STANDBY at this point */
+	smscr = inw(HD64465_REG_SMSCR);
+	outw(smscr & (~HD64465_SMSCR_UARTST), HD64465_REG_SMSCR);
+
+	/* remap IO ports for first ISA serial port to HD64465 UART */
+	hd64465_port_map(0x3f8, 8, CONFIG_HD64465_IOBASE + 0x8000, 1);
+#endif
+
+	return 0;
+}
+
+module_init(setup_hd64465);
diff -urN linux-2.4.4-pre2/arch/sh/kernel/sh_ksyms.c linux/arch/sh/kernel/sh_ksyms.c
--- linux-2.4.4-pre2/arch/sh/kernel/sh_ksyms.c	Fri Mar  2 11:15:47 2001
+++ linux/arch/sh/kernel/sh_ksyms.c	Mon Dec  3 22:34:50 2001
@@ -17,6 +17,7 @@
 #include <asm/io.h>
 #include <asm/hardirq.h>
 #include <asm/delay.h>
+#include <asm/pgalloc.h>
 #include <linux/irq.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
@@ -45,6 +46,7 @@
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strchr);
 EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strncat);
 
 /* mem exports */
 EXPORT_SYMBOL(memchr);
diff -urN linux-2.4.4-pre2/arch/sh/kernel/time.c linux/arch/sh/kernel/time.c
--- linux-2.4.4-pre2/arch/sh/kernel/time.c	Thu Jan  4 13:19:13 2001
+++ linux/arch/sh/kernel/time.c	Mon Dec  3 22:34:50 2001
@@ -57,6 +57,13 @@
 #define TMU0_TCR	0xffd80010	/* Word access */
 
 #define FRQCR		0xffc00000
+
+/* Core Processor Version Register */
+#define CCN_PVR		0xff000030
+#define CCN_PVR_CHIP_SHIFT 24
+#define CCN_PVR_CHIP_MASK  0xff
+#define CCN_PVR_CHIP_ST40STB1 0x4
+
 #endif
 
 extern rwlock_t xtime_lock;
@@ -289,13 +296,62 @@
 	static int ifc_table[] = { 1, 2, 3, 4, 6, 8, 1, 1 };
 #define bfc_table ifc_table	/* Same */
 	static int pfc_table[] = { 2, 3, 4, 6, 8, 2, 2, 2 };
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+	struct frqcr_data {
+		unsigned short frqcr;
+		struct {
+			unsigned char multiplier;
+			unsigned char divisor;
+		} factor[3];
+	};
+
+	static struct frqcr_data st40_frqcr_table[] = {		
+		{ 0x000, {{1,1}, {1,1}, {1,2}}},
+		{ 0x002, {{1,1}, {1,1}, {1,4}}},
+		{ 0x004, {{1,1}, {1,1}, {1,8}}},
+		{ 0x008, {{1,1}, {1,2}, {1,2}}},
+		{ 0x00A, {{1,1}, {1,2}, {1,4}}},
+		{ 0x00C, {{1,1}, {1,2}, {1,8}}},
+		{ 0x011, {{1,1}, {2,3}, {1,6}}},
+		{ 0x013, {{1,1}, {2,3}, {1,3}}},
+		{ 0x01A, {{1,1}, {1,2}, {1,4}}},
+		{ 0x01C, {{1,1}, {1,2}, {1,8}}},
+		{ 0x023, {{1,1}, {2,3}, {1,3}}},
+		{ 0x02C, {{1,1}, {1,2}, {1,8}}},
+		{ 0x048, {{1,2}, {1,2}, {1,4}}},
+		{ 0x04A, {{1,2}, {1,2}, {1,6}}},
+		{ 0x04C, {{1,2}, {1,2}, {1,8}}},
+		{ 0x05A, {{1,2}, {1,3}, {1,6}}},
+		{ 0x05C, {{1,2}, {1,3}, {1,6}}},
+		{ 0x063, {{1,2}, {1,4}, {1,4}}},
+		{ 0x06C, {{1,2}, {1,4}, {1,8}}},
+		{ 0x091, {{1,3}, {1,3}, {1,6}}},
+		{ 0x093, {{1,3}, {1,3}, {1,6}}},
+		{ 0x0A3, {{1,3}, {1,6}, {1,6}}},
+		{ 0x0DA, {{1,4}, {1,4}, {1,8}}},
+		{ 0x0DC, {{1,4}, {1,4}, {1,8}}},
+		{ 0x0EC, {{1,4}, {1,8}, {1,8}}},
+		{ 0x123, {{1,4}, {1,4}, {1,8}}},
+		{ 0x16C, {{1,4}, {1,8}, {1,8}}},
+	};
+#endif
 #endif
 
+#if defined(CONFIG_SH_DREAMCAST)
+	xtime.tv_sec = 0;
+	xtime.tv_usec = 0;
+#else
 	rtc_gettimeofday(&xtime);
+#endif
 
 	setup_irq(TIMER_IRQ, &irq0);
 
+#if defined(CONFIG_SH_DREAMCAST)
+	timer_freq = 50*1000*1000/4;
+#else
 	timer_freq = get_timer_frequency();
+#endif
 
 	module_clock = timer_freq * 4;
 
@@ -316,15 +372,53 @@
 	}
 #elif defined(__SH4__)
 	{
+		unsigned long pvr;
 		frqcr = ctrl_inw(FRQCR);
-		ifc  = ifc_table[(frqcr>> 6) & 0x0007];
-		bfc  = bfc_table[(frqcr>> 3) & 0x0007];
-		pfc = pfc_table[frqcr & 0x0007];
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+		/* This should probably be moved into the SH3 probing code, and then use the processor
+		 * structure to determine which CPU we are running on.
+		 */
+		pvr = ctrl_inl(CCN_PVR);
+		printk("PVR %08x\n", pvr);
+
+		if (((pvr >>CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) == CCN_PVR_CHIP_ST40STB1) {
+			/* Unfortunatly the STB1 FRQCR values are different from the 7750 ones */
+			struct frqcr_data *d;
+			int a;
+
+			for (a=0; a<ARRAY_SIZE(st40_frqcr_table); a++) {
+				d = &st40_frqcr_table[a];
+				if (d->frqcr == (frqcr & 0x1ff))
+					break;
+			}
+			if (a == ARRAY_SIZE(st40_frqcr_table)) {
+				d = st40_frqcr_table;
+				printk("ERROR: Unrecognised FRQCR value, using default multipliers\n");
+			}
+
+			printk("Clock multipliers: CPU: %d/%d Bus: %d/%d Periph: %d/%d\n",
+			       d->factor[0].multiplier, d->factor[0].divisor,
+			       d->factor[1].multiplier, d->factor[1].divisor,
+			       d->factor[2].multiplier, d->factor[2].divisor);
+			
+			master_clock = module_clock * d->factor[2].divisor    / d->factor[2].multiplier;
+			bus_clock    = master_clock * d->factor[1].multiplier / d->factor[1].divisor;
+			cpu_clock    = master_clock * d->factor[0].multiplier / d->factor[0].divisor;
+			goto skip_calc;
+		} else
+#endif
+		{
+			ifc  = ifc_table[(frqcr>> 6) & 0x0007];
+			bfc  = bfc_table[(frqcr>> 3) & 0x0007];
+			pfc = pfc_table[frqcr & 0x0007];
+		}
 	}
 #endif
 	master_clock = module_clock * pfc;
 	bus_clock = master_clock / bfc;
 	cpu_clock = master_clock / ifc;
+ skip_calc:
 	printk("CPU clock: %d.%02dMHz\n",
 	       (cpu_clock / 1000000), (cpu_clock % 1000000)/10000);
 	printk("Bus clock: %d.%02dMHz\n",
diff -urN linux-2.4.4-pre2/arch/sh/mm/cache.c linux/arch/sh/mm/cache.c
--- linux-2.4.4-pre2/arch/sh/mm/cache.c	Sun Jan 28 18:56:00 2001
+++ linux/arch/sh/mm/cache.c	Mon Dec  3 22:34:50 2001
@@ -148,7 +148,13 @@
 		cpu_data->type = CPU_SH7729;
 	}
 #elif defined(__SH4__)
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+	cpu_data->type = CPU_ST40STB1;
+#elif defined(CONFIG_CPU_SUBTYPE_SH7750)
 	cpu_data->type = CPU_SH7750;
+#else
+#error Unknown SH4 CPU type
+#endif
 #endif
 }
 
diff -urN linux-2.4.4-pre2/arch/sh/mm/extable.c linux/arch/sh/mm/extable.c
--- linux-2.4.4-pre2/arch/sh/mm/extable.c	Wed Nov 10 08:31:37 1999
+++ linux/arch/sh/mm/extable.c	Mon Dec  3 22:34:50 2001
@@ -46,7 +46,7 @@
 	/* The kernel is the last "module" -- no need to treat it special.  */
 	struct module *mp;
 	for (mp = module_list; mp != NULL; mp = mp->next) {
-		if (mp->ex_table_start == NULL)
+		if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING)))
 			continue;
 		ret = search_one_table(mp->ex_table_start,
 				       mp->ex_table_end - 1, addr);
diff -urN linux-2.4.4-pre2/arch/sh/mm/init.c linux/arch/sh/mm/init.c
--- linux-2.4.4-pre2/arch/sh/mm/init.c	Tue Nov 28 22:43:39 2000
+++ linux/arch/sh/mm/init.c	Mon Dec  3 22:34:50 2001
@@ -45,104 +45,25 @@
 
 extern unsigned long init_smp_mappings(unsigned long);
 
-/*
- * BAD_PAGE is the page that is used for page faults when linux
- * is out-of-memory. Older versions of linux just did a
- * do_exit(), but using this instead means there is less risk
- * for a process dying in kernel mode, possibly leaving an inode
- * unused etc..
- *
- * BAD_PAGETABLE is the accompanying page-table: it is initialized
- * to point to BAD_PAGE entries.
- *
- * ZERO_PAGE is a special page that is used for zero-initialized
- * data and COW.
- */
-
-unsigned long empty_bad_page[1024];
-pte_t empty_bad_pte_table[PTRS_PER_PTE];
 extern unsigned long empty_zero_page[1024];
 
-static pte_t * get_bad_pte_table(void)
-{
-	pte_t v;
-	int i;
-
-	v = pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED));
-
-	for (i = 0; i < PAGE_SIZE/sizeof(pte_t); i++)
-		empty_bad_pte_table[i] = v;
-
-	return empty_bad_pte_table;
-}
-
-void __handle_bad_pmd(pmd_t *pmd)
-{
-	pmd_ERROR(*pmd);
-	set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table())));
-}
-
-void __handle_bad_pmd_kernel(pmd_t *pmd)
-{
-	pmd_ERROR(*pmd);
-	set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(get_bad_pte_table())));
-}
-
-pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset)
-{
-	pte_t *pte;
-
-	pte = (pte_t *) __get_free_page(GFP_KERNEL);
-	if (pmd_none(*pmd)) {
-		if (pte) {
-			clear_page(pte);
-			set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
-			return pte + offset;
-		}
-		set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(get_bad_pte_table())));
-		return NULL;
-	}
-	free_page((unsigned long)pte);
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd_kernel(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + offset;
-}
-
-pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
-{
-	unsigned long pte;
-
-	pte = (unsigned long) __get_free_page(GFP_KERNEL);
-	if (pmd_none(*pmd)) {
-		if (pte) {
-			clear_page((void *)pte);
-			set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)));
-			return (pte_t *)pte + offset;
-		}
-		set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table())));
-		return NULL;
-	}
-	free_page(pte);
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + offset;
-}
-
 int do_check_pgt_cache(int low, int high)
 {
 	int freed = 0;
 	if (pgtable_cache_size > high) {
 		do {
-			if (pgd_quicklist)
-				free_pgd_slow(get_pgd_fast()), freed++;
-			if (pmd_quicklist)
-				free_pmd_slow(get_pmd_fast()), freed++;
-			if (pte_quicklist)
-				free_pte_slow(get_pte_fast()), freed++;
+			if (pgd_quicklist) {
+				free_pgd_slow(get_pgd_fast());
+				freed++;
+			}
+			if (pmd_quicklist) {
+				pmd_free_slow(pmd_alloc_one_fast(NULL, 0));
+				freed++;
+			}
+			if (pte_quicklist) {
+				pte_free_slow(pte_alloc_one_fast(NULL, 0));
+				freed++;
+			}
 		} while (pgtable_cache_size > low);
 	}
 	return freed;
diff -urN linux-2.4.4-pre2/arch/sh/mm/ioremap.c linux/arch/sh/mm/ioremap.c
--- linux-2.4.4-pre2/arch/sh/mm/ioremap.c	Fri Oct 13 12:06:52 2000
+++ linux/arch/sh/mm/ioremap.c	Mon Dec  3 22:34:50 2001
@@ -25,14 +25,18 @@
 	end = address + size;
 	if (end > PMD_SIZE)
 		end = PMD_SIZE;
+	if (address >= end)
+		BUG();
 	do {
-		if (!pte_none(*pte))
+		if (!pte_none(*pte)) {
 			printk("remap_area_pte: page already exists\n");
+			BUG();
+		}
 		set_pte(pte, mk_pte_phys(phys_addr, pgprot));
 		address += PAGE_SIZE;
 		phys_addr += PAGE_SIZE;
 		pte++;
-	} while (address < end);
+	} while (address && address < end);
 }
 
 static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
@@ -45,38 +49,48 @@
 	if (end > PGDIR_SIZE)
 		end = PGDIR_SIZE;
 	phys_addr -= address;
+	if (address >= end)
+		BUG();
 	do {
-		pte_t * pte = pte_alloc_kernel(pmd, address);
+		pte_t * pte = pte_alloc(&init_mm, pmd, address);
 		if (!pte)
 			return -ENOMEM;
 		remap_area_pte(pte, address, end - address, address + phys_addr, flags);
 		address = (address + PMD_SIZE) & PMD_MASK;
 		pmd++;
-	} while (address < end);
+	} while (address && address < end);
 	return 0;
 }
 
 static int remap_area_pages(unsigned long address, unsigned long phys_addr,
-	unsigned long size, unsigned long flags)
+			    unsigned long size, unsigned long flags)
 {
+	int error;
 	pgd_t * dir;
 	unsigned long end = address + size;
 
 	phys_addr -= address;
-	dir = pgd_offset_k(address);
+	dir = pgd_offset(&init_mm, address);
 	flush_cache_all();
-	while (address < end) {
-		pmd_t *pmd = pmd_alloc_kernel(dir, address);
+	if (address >= end)
+		BUG();
+	spin_lock(&init_mm.page_table_lock);
+	do {
+		pmd_t *pmd;
+		pmd = pmd_alloc(&init_mm, dir, address);
+		error = -ENOMEM;
 		if (!pmd)
-			return -ENOMEM;
+			break;
 		if (remap_area_pmd(pmd, address, end - address,
 					phys_addr + address, flags))
-			return -ENOMEM;
+			break;
+		error = 0;
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
-	}
+	} while (address && (address < end));
+	spin_unlock(&init_mm.page_table_lock);
 	flush_tlb_all();
-	return 0;
+	return error;
 }
 
 /*
@@ -106,7 +120,7 @@
 	/*
 	 * Don't remap the low PCI/ISA area, it's always mapped..
 	 */
-	if (phys_addr >= 0xA0000 && last_addr <= 0x100000)
+	if (phys_addr >= 0xA0000 && last_addr < 0x100000)
 		return phys_to_virt(phys_addr);
 
 	/*
diff -urN linux-2.4.4-pre2/arch/sparc/kernel/sys_sparc.c linux/arch/sparc/kernel/sys_sparc.c
--- linux-2.4.4-pre2/arch/sparc/kernel/sys_sparc.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc/kernel/sys_sparc.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.68 2001/03/24 09:36:10 davem Exp $
+/* $Id: sys_sparc.c,v 1.69 2001/03/27 02:36:37 davem Exp $
  * linux/arch/sparc/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -36,19 +36,28 @@
 
 #define COLOUR_ALIGN(addr)      (((addr)+SHMLBA-1)&~(SHMLBA-1))
 
-unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
 	struct vm_area_struct * vmm;
 
+	if (flags & MAP_FIXED) {
+		/* We do not accept a shared mapping if it would violate
+		 * cache aliasing constraints.
+		 */
+		if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
+			return -EINVAL;
+		return addr;
+	}
+
 	/* See asm-sparc/uaccess.h */
 	if (len > TASK_SIZE - PAGE_SIZE)
-		return 0;
+		return -ENOMEM;
 	if (ARCH_SUN4C_SUN4 && len > 0x20000000)
-		return 0;
+		return -ENOMEM;
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
-	if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+	if (flags & MAP_SHARED)
 		addr = COLOUR_ALIGN(addr);
 	else
 		addr = PAGE_ALIGN(addr);
@@ -60,11 +69,11 @@
 			vmm = find_vma(current->mm, PAGE_OFFSET);
 		}
 		if (TASK_SIZE - PAGE_SIZE - len < addr)
-			return 0;
+			return -ENOMEM;
 		if (!vmm || addr + len <= vmm->vm_start)
 			return addr;
 		addr = vmm->vm_end;
-		if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+		if (flags & MAP_SHARED)
 			addr = COLOUR_ALIGN(addr);
 	}
 }
@@ -233,15 +242,10 @@
 
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
-	if (flags & MAP_SHARED)
-		current->thread.flags |= SPARC_FLAG_MMAPSHARED;
-
 	down_write(&current->mm->mmap_sem);
 	retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 	up_write(&current->mm->mmap_sem);
 
-	current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
-
 out_putf:
 	if (file)
 		fput(file);
@@ -285,9 +289,6 @@
 	    new_len > TASK_SIZE - PAGE_SIZE)
 		goto out;
 	down_write(&current->mm->mmap_sem);
-	vma = find_vma(current->mm, addr);
-	if (vma && (vma->vm_flags & VM_SHARED))
-		current->thread.flags |= SPARC_FLAG_MMAPSHARED;
 	if (flags & MREMAP_FIXED) {
 		if (ARCH_SUN4C_SUN4 &&
 		    new_addr < 0xe0000000 &&
@@ -298,17 +299,31 @@
 	} else if ((ARCH_SUN4C_SUN4 && addr < 0xe0000000 &&
 		    addr + new_len > 0x20000000) ||
 		   addr + new_len > TASK_SIZE - PAGE_SIZE) {
+		unsigned long (*get_addr)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+		unsigned long map_flags = 0;
+		struct file *file = NULL;
+
 		ret = -ENOMEM;
 		if (!(flags & MREMAP_MAYMOVE))
 			goto out_sem;
-		new_addr = get_unmapped_area (addr, new_len);
-		if (!new_addr)
+
+		vma = find_vma(current->mm, addr);
+		if (vma) {
+			if (vma->vm_flags & VM_SHARED)
+				map_flags |= MAP_SHARED;
+			file = vma->vm_file;
+		}
+
+		new_addr = get_unmapped_area(file, addr, new_len,
+				     vma ? vma->vm_pgoff : 0,
+				     map_flags);
+		ret = new_addr;
+		if (new_addr & ~PAGE_MASK)
 			goto out_sem;
 		flags |= MREMAP_FIXED;
 	}
 	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
 out_sem:
-	current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
 	up_write(&current->mm->mmap_sem);
 out:
 	return ret;       
diff -urN linux-2.4.4-pre2/arch/sparc/lib/memset.S linux/arch/sparc/lib/memset.S
--- linux-2.4.4-pre2/arch/sparc/lib/memset.S	Mon Jun 19 17:59:38 2000
+++ linux/arch/sparc/lib/memset.S	Mon Dec  3 22:34:50 2001
@@ -188,7 +188,7 @@
 	b 30f
 	 add	%o0, %o1, %o0
 30:
-/* %o4 is faulting address, %o5 is %pc where fault occured */
+/* %o4 is faulting address, %o5 is %pc where fault occurred */
 	save	%sp, -104, %sp
 	mov	%i5, %o0
 	mov	%i7, %o1
diff -urN linux-2.4.4-pre2/arch/sparc/mm/generic.c linux/arch/sparc/mm/generic.c
--- linux-2.4.4-pre2/arch/sparc/mm/generic.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/sparc/mm/generic.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: generic.c,v 1.10 2000/08/09 00:00:15 davem Exp $
+/* $Id: generic.c,v 1.12 2001/04/09 21:40:46 davem Exp $
  * generic.c: Generic Sparc mm routines that are not dependent upon
  *            MMU type but are Sparc specific.
  *
@@ -21,7 +21,7 @@
 		struct page *ptpage = pte_page(page);
 		if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
 			return;
-		page_cache_release(page);
+		page_cache_release(ptpage);
 		return;
 	}
 	swap_free(pte_to_swp_entry(page));
diff -urN linux-2.4.4-pre2/arch/sparc64/config.in linux/arch/sparc64/config.in
--- linux-2.4.4-pre2/arch/sparc64/config.in	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/config.in	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.136 2001/03/24 06:04:24 davem Exp $
+# $Id: config.in,v 1.139 2001/04/03 12:29:38 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -20,6 +20,8 @@
 
 mainmenu_option next_comment
 comment 'General setup'
+
+tristate 'UltraSPARC-III bootbus i2c controller driver' CONFIG_BBC_I2C
 
 define_bool CONFIG_VT y
 define_bool CONFIG_VT_CONSOLE y
diff -urN linux-2.4.4-pre2/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- linux-2.4.4-pre2/arch/sparc64/defconfig	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/defconfig	Mon Dec  3 22:34:50 2001
@@ -17,6 +17,7 @@
 #
 # General setup
 #
+CONFIG_BBC_I2C=m
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
 # CONFIG_SMP is not set
@@ -519,11 +520,13 @@
 # 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_CODEPAGE_932 is not set
 # CONFIG_NLS_CODEPAGE_936 is not set
-# CONFIG_NLS_CODEPAGE_949 is not set
 # CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
 # CONFIG_NLS_ISO8859_1 is not set
 # CONFIG_NLS_ISO8859_2 is not set
 # CONFIG_NLS_ISO8859_3 is not set
@@ -531,11 +534,12 @@
 # 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_ISO8859_13 is not set
 # CONFIG_NLS_ISO8859_14 is not set
 # CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
 # CONFIG_NLS_UTF8 is not set
 
 #
@@ -606,7 +610,6 @@
 # USB Serial Converter support
 #
 CONFIG_USB_SERIAL=m
-# CONFIG_USB_SERIAL_DEBUG is not set
 CONFIG_USB_SERIAL_GENERIC=y
 CONFIG_USB_SERIAL_BELKIN=m
 CONFIG_USB_SERIAL_WHITEHEAT=m
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile
--- linux-2.4.4-pre2/arch/sparc64/kernel/Makefile	Tue Mar  6 22:44:16 2001
+++ linux/arch/sparc64/kernel/Makefile	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.64 2001/02/28 05:59:45 davem Exp $
+# $Id: Makefile,v 1.66 2001/04/03 12:29:38 davem Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -24,7 +24,7 @@
 		   traps.o devices.o auxio.o \
 		   irq.o ptrace.o time.o sys_sparc.o signal.o \
 		   unaligned.o central.o pci.o starfire.o semaphore.o \
-		   power.o sbus.o iommu_common.o sparc64_ksyms.o
+		   power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o
 
 obj-$(CONFIG_PCI)	 += ebus.o pci_common.o pci_iommu.o \
 			    pci_psycho.o pci_sabre.o pci_schizo.o
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/chmc.c linux/arch/sparc64/kernel/chmc.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/chmc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/chmc.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,451 @@
+/* $Id: chmc.c,v 1.3 2001/04/03 12:49:47 davem Exp $
+ * memctrlr.c: Driver for UltraSPARC-III memory controller.
+ *
+ * Copyright (C) 2001 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <asm/spitfire.h>
+#include <asm/chmctrl.h>
+#include <asm/oplib.h>
+#include <asm/io.h>
+
+#define CHMCTRL_NDGRPS	2
+#define CHMCTRL_NDIMMS	4
+
+#define DIMMS_PER_MC	(CHMCTRL_NDGRPS * CHMCTRL_NDIMMS)
+
+/* OBP memory-layout property format. */
+struct obp_map {
+	unsigned char	dimm_map[144];
+	unsigned char	pin_map[576];
+};
+
+#define DIMM_LABEL_SZ	8
+
+struct obp_mem_layout {
+	/* One max 8-byte string label per DIMM.  Usually
+	 * this matches the label on the motherboard where
+	 * that DIMM resides.
+	 */
+	char		dimm_labels[DIMMS_PER_MC][DIMM_LABEL_SZ];
+
+	/* If symmetric use map[0], else it is
+	 * asymmetric and map[1] should be used.
+	 */
+	char		symmetric;
+
+	struct obp_map	map[2];
+};
+
+#define CHMCTRL_NBANKS	4
+
+struct bank_info {
+	struct mctrl_info	*mp;
+	int			bank_id;
+
+	u64			raw_reg;
+	int			valid;
+	int			uk;
+	int			um;
+	int			lk;
+	int			lm;
+	int			interleave;
+	unsigned long		base;
+	unsigned long		size;
+};
+
+struct mctrl_info {
+	struct list_head	list;
+	int			portid;
+	int			index;
+
+	struct obp_mem_layout	layout_prop;
+	int			layout_size;
+
+	void			*regs;
+
+	u64			timing_control1;
+	u64			timing_control2;
+	u64			timing_control3;
+	u64			timing_control4;
+	u64			memaddr_control;
+
+	struct bank_info	logical_banks[CHMCTRL_NBANKS];
+};
+
+static LIST_HEAD(mctrl_list);
+
+/* Does BANK decode PHYS_ADDR? */
+static int bank_match(struct bank_info *bp, unsigned long phys_addr)
+{
+	unsigned long upper_bits = (phys_addr & PA_UPPER_BITS) >> PA_UPPER_BITS_SHIFT;
+	unsigned long lower_bits = (phys_addr & PA_LOWER_BITS) >> PA_LOWER_BITS_SHIFT;
+
+	/* Bank must be enabled to match. */
+	if (bp->valid == 0)
+		return 0;
+
+	/* Would BANK match upper bits? */
+	upper_bits ^= bp->um;		/* What bits are different? */
+	upper_bits  = ~upper_bits;	/* Invert. */
+	upper_bits |= bp->uk;		/* What bits don't matter for matching? */
+	upper_bits  = ~upper_bits;	/* Invert. */
+
+	if (upper_bits)
+		return 0;
+
+	/* Would BANK match lower bits? */
+	lower_bits ^= bp->lm;		/* What bits are different? */
+	lower_bits  = ~lower_bits;	/* Invert. */
+	lower_bits |= bp->lk;		/* What bits don't matter for matching? */
+	lower_bits  = ~lower_bits;	/* Invert. */
+
+	if (lower_bits)
+		return 0;
+
+	/* I always knew you'd be the one. */
+	return 1;
+}
+
+/* Given PHYS_ADDR, search memory controller banks for a match. */
+static struct bank_info *find_bank(unsigned long phys_addr)
+{
+	struct list_head *mctrl_head = &mctrl_list;
+	struct list_head *mctrl_entry = mctrl_head->next;
+
+	for (;;) {
+		struct mctrl_info *mp =
+			list_entry(mctrl_entry, struct mctrl_info, list);
+		int bank_no;
+
+		if (mctrl_entry == mctrl_head)
+			break;
+		mctrl_entry = mctrl_entry->next;
+
+		for (bank_no = 0; bank_no < CHMCTRL_NBANKS; bank_no++) {
+			struct bank_info *bp;
+
+			bp = &mp->logical_banks[bank_no];
+			if (bank_match(bp, phys_addr))
+				return bp;
+		}
+	}
+
+	return NULL;
+}
+
+/* This is the main purpose of this driver. */
+#define SYNDROME_MIN	-1
+#define SYNDROME_MAX	144
+int chmc_getunumber(int syndrome_code,
+		    unsigned long phys_addr,
+		    char *buf, int buflen)
+{
+	struct bank_info *bp;
+	struct obp_mem_layout *prop;
+	int bank_in_controller, first_dimm;
+
+	bp = find_bank(phys_addr);
+	if (bp == NULL ||
+	    syndrome_code < SYNDROME_MIN ||
+	    syndrome_code > SYNDROME_MAX) {
+		buf[0] = '?';
+		buf[1] = '?';
+		buf[2] = '?';
+		buf[3] = '\0';
+		return 0;
+	}
+
+	prop = &bp->mp->layout_prop;
+	bank_in_controller = bp->bank_id & (CHMCTRL_NBANKS - 1);
+	first_dimm  = (bank_in_controller & (CHMCTRL_NDGRPS - 1));
+	first_dimm *= CHMCTRL_NDIMMS;
+
+	if (syndrome_code != SYNDROME_MIN) {
+		struct obp_map *map;
+		int qword, where_in_line, where, map_index, map_offset;
+		unsigned int map_val;
+
+		/* Yaay, single bit error so we can figure out
+		 * the exact dimm.
+		 */
+		if (prop->symmetric)
+			map = &prop->map[0];
+		else
+			map = &prop->map[1];
+
+		/* Covert syndrome code into the way the bits are
+		 * positioned on the bus.
+		 */
+		if (syndrome_code < 144 - 16)
+			syndrome_code += 16;
+		else if (syndrome_code < 144)
+			syndrome_code -= (144 - 7);
+		else if (syndrome_code < (144 + 3))
+			syndrome_code -= (144 + 3 - 4);
+		else
+			syndrome_code -= 144 + 3;
+
+		/* All this magic has to do with how a cache line
+		 * comes over the wire on Safari.  A 64-bit line
+		 * comes over in 4 quadword cycles, each of which
+		 * transmit ECC/MTAG info as well as the actual
+		 * data.  144 bits per quadword, 576 total.
+		 */
+#define LINE_SIZE	64
+#define LINE_ADDR_MSK	(LINE_SIZE - 1)
+#define QW_PER_LINE	4
+#define QW_BYTES	(LINE_SIZE / QW_PER_LINE)
+#define QW_BITS		144
+#define LAST_BIT	(576 - 1)
+
+		qword = (phys_addr & LINE_ADDR_MSK) / QW_BYTES;
+		where_in_line = ((3 - qword) * QW_BITS) + syndrome_code;
+		where = (LAST_BIT - where_in_line);
+		map_index = where >> 2;
+		map_offset = where & 0x3;
+		map_val = map->dimm_map[map_index];
+		map_val = ((map_val >> ((3 - map_offset) << 1)) & (2 - 1));
+
+		sprintf(buf, "%s, pin %3d",
+			prop->dimm_labels[first_dimm + map_val],
+			map->pin_map[where_in_line]);
+	} else {
+		int dimm;
+
+		/* Multi-bit error, we just dump out all the
+		 * dimm labels assosciated with this bank.
+		 */
+		for (dimm = 0; dimm < CHMCTRL_NDIMMS; dimm++) {
+			sprintf(buf, "%s ",
+				prop->dimm_labels[first_dimm + dimm]);
+			buf += strlen(buf);
+		}
+	}
+	return 0;
+}
+
+/* Accessing the registers is slightly complicated.  If you want
+ * to get at the memory controller which is on the same processor
+ * the code is executing, you must use special ASI load/store else
+ * you go through the global mapping.
+ */
+static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset)
+{
+	unsigned long ret;
+
+	if (mp->portid == smp_processor_id()) {
+		__asm__ __volatile__("ldxa	[%1] %2, %0"
+				     : "=r" (ret)
+				     : "r" (offset), "i" (ASI_MCU_CTRL_REG));
+	} else {
+		__asm__ __volatile__("ldxa	[%1] %2, %0"
+				     : "=r" (ret)
+				     : "r" (mp->regs + offset),
+				       "i" (ASI_PHYS_BYPASS_EC_E));
+	}
+	return ret;
+}
+
+#if 0 /* currently unused */
+static void write_mcreg(struct mctrl_info *mp, unsigned long offset, u64 val)
+{
+	if (mp->portid == smp_processor_id()) {
+		__asm__ __volatile__("stxa	%0, [%1] %2"
+				     : : "r" (val),
+				         "r" (offset), "i" (ASI_MCU_CTRL_REG));
+	} else {
+		__asm__ __volatile__("ldxa	%0, [%1] %2"
+				     : : "r" (val),
+				         "r" (mp->regs + offset),
+				         "i" (ASI_PHYS_BYPASS_EC_E));
+	}
+}
+#endif
+
+static void interpret_one_decode_reg(struct mctrl_info *mp, int which_bank, u64 val)
+{
+	struct bank_info *p = &mp->logical_banks[which_bank];
+
+	p->mp = mp;
+	p->bank_id = (CHMCTRL_NBANKS * mp->portid) + which_bank;
+	p->raw_reg = val;
+	p->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT;
+	p->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT;
+	p->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT;
+	p->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT;
+	p->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT;
+
+	p->base  =  (p->um);
+	p->base &= ~(p->uk);
+	p->base <<= PA_UPPER_BITS_SHIFT;
+
+	switch(p->lk) {
+	case 0xf:
+	default:
+		p->interleave = 1;
+		break;
+
+	case 0xe:
+		p->interleave = 2;
+		break;
+
+	case 0xc:
+		p->interleave = 4;
+		break;
+
+	case 0x8:
+		p->interleave = 8;
+		break;
+
+	case 0x0:
+		p->interleave = 16;
+		break;
+	};
+
+	/* UK[10] is reserved, and UK[11] is not set for the SDRAM
+	 * bank size definition.
+	 */
+	p->size = (((unsigned long)p->uk &
+		    ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT;
+	p->size /= p->interleave;
+}
+
+static void fetch_decode_regs(struct mctrl_info *mp)
+{
+	if (mp->layout_size == 0)
+		return;
+
+	interpret_one_decode_reg(mp, 0,
+				 read_mcreg(mp, CHMCTRL_DECODE1));
+	interpret_one_decode_reg(mp, 1,
+				 read_mcreg(mp, CHMCTRL_DECODE2));
+	interpret_one_decode_reg(mp, 2,
+				 read_mcreg(mp, CHMCTRL_DECODE3));
+	interpret_one_decode_reg(mp, 3,
+				 read_mcreg(mp, CHMCTRL_DECODE4));
+}
+
+static int init_one_mctrl(int node, int index)
+{
+	struct mctrl_info *mp = kmalloc(sizeof(*mp), GFP_KERNEL);
+	int portid = prom_getintdefault(node, "portid", -1);
+	struct linux_prom64_registers p_reg_prop;
+	int t;
+
+	if (!mp)
+		return -1;
+	memset(mp, 0, sizeof(*mp));
+	if (portid == -1)
+		goto fail;
+
+	mp->portid = portid;
+	mp->layout_size = prom_getproplen(node, "memory-layout");
+	if (mp->layout_size < 0)
+		mp->layout_size = 0;
+	if (mp->layout_size > sizeof(mp->layout_prop))
+		goto fail;
+
+	if (mp->layout_size > 0)
+		prom_getproperty(node, "memory-layout",
+				 (char *) &mp->layout_prop,
+				 mp->layout_size);
+
+	t = prom_getproperty(node, "reg",
+			     (char *) &p_reg_prop,
+			     sizeof(p_reg_prop));
+	if (t < 0 || p_reg_prop.reg_size != 0x48)
+		goto fail;
+
+	mp->regs = ioremap(p_reg_prop.phys_addr, p_reg_prop.reg_size);
+	if (mp->regs == NULL)
+		goto fail;
+
+	if (mp->layout_size != 0UL) {
+		mp->timing_control1 = read_mcreg(mp, CHMCTRL_TCTRL1);
+		mp->timing_control2 = read_mcreg(mp, CHMCTRL_TCTRL2);
+		mp->timing_control3 = read_mcreg(mp, CHMCTRL_TCTRL3);
+		mp->timing_control4 = read_mcreg(mp, CHMCTRL_TCTRL4);
+		mp->memaddr_control = read_mcreg(mp, CHMCTRL_MACTRL);
+	}
+
+	fetch_decode_regs(mp);
+
+	mp->index = index;
+
+	list_add(&mp->list, &mctrl_list);
+
+	/* Report the device. */
+	printk(KERN_INFO "chmc%d: US3 memory controller at %p [%s]\n",
+	       mp->index,
+	       mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE"));
+
+	return 0;
+
+fail:
+	if (mp) {
+		if (mp->regs != NULL)
+			iounmap(mp->regs);
+		kfree(mp);
+	}
+	return -1;
+}
+
+static int __init probe_for_string(char *name, int index)
+{
+	int node = prom_getchild(prom_root_node);
+
+	while ((node = prom_searchsiblings(node, name)) != 0) {
+		int ret = init_one_mctrl(node, index);
+
+		if (!ret)
+			index++;
+
+		node = prom_getsibling(node);
+		if (!node)
+			break;
+	}
+
+	return index;
+}
+
+static int __init chmc_init(void)
+{
+	int index;
+
+	/* This driver is only for cheetah platforms. */
+	if (tlb_type != cheetah)
+		return -ENODEV;
+
+	index = probe_for_string("memory-controller", 0);
+	index = probe_for_string("mc-us3", index);
+
+	return 0;
+}
+
+static void __exit chmc_cleanup(void)
+{
+	struct list_head *head = &mctrl_list;
+	struct list_head *tmp = head->next;
+
+	for (;;) {
+		struct mctrl_info *p =
+			list_entry(tmp, struct mctrl_info, list);
+		if (tmp == head)
+			break;
+		tmp = tmp->next;
+
+		list_del(&p->list);
+		iounmap(p->regs);
+		kfree(p);
+	}
+}
+
+module_init(chmc_init);
+module_exit(chmc_cleanup);
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/dtlb_prot.S linux/arch/sparc64/kernel/dtlb_prot.S
--- linux-2.4.4-pre2/arch/sparc64/kernel/dtlb_prot.S	Sun Nov 12 20:37:16 2000
+++ linux/arch/sparc64/kernel/dtlb_prot.S	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: dtlb_prot.S,v 1.21 2000/11/10 08:28:45 davem Exp $
+/* $Id: dtlb_prot.S,v 1.22 2001/04/11 23:40:32 davem Exp $
  * dtlb_prot.S: DTLB protection trap strategy.
  *              This is included directly into the trap table.
  *
@@ -43,7 +43,7 @@
 	nop
 	nop
 
-/* PROT ** ICACHE line 3: Unused...	*/
+/* PROT ** ICACHE line 4: Unused...	*/
 	nop
 	nop
 	nop
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S
--- linux-2.4.4-pre2/arch/sparc64/kernel/entry.S	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/entry.S	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.127 2001/03/23 07:56:30 davem Exp $
+/* $Id: entry.S,v 1.128 2001/03/28 10:56:34 davem Exp $
  * arch/sparc64/kernel/entry.S:  Sparc64 trap low-level entry points.
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -19,6 +19,7 @@
 #include <asm/pgtable.h>
 #include <asm/processor.h>
 #include <asm/visasm.h>
+#include <asm/estate.h>
 
 /* #define SYSCALL_TRACING */
 
@@ -788,6 +789,284 @@
 	call	cee_log
 	 add	%sp, STACK_BIAS + REGWIN_SZ, %o2
 	ba,a,pt	%xcc, rtrap_clr_l6
+
+	/* Capture I/D/E-cache state into per-cpu error scoreboard.
+	 *
+	 * %g1:		(TL>=0) ? 1 : 0
+	 * %g2:		scratch
+	 * %g3:		scratch
+	 * %g4:		AFSR
+	 * %g5:		AFAR
+	 * %g6:		current thread ptr
+	 * %g7:		scratch
+	 */
+#define CHEETAH_LOG_ERROR						\
+	/* Put "TL1" software bit into AFSR. */				\
+	and		%g1, 0x1, %g1;					\
+	sllx		%g1, 63, %g2;					\
+	or		%g4, %g2, %g4;					\
+	/* Get log entry pointer for this cpu at this trap level. */	\
+	ldxa		[%g0] ASI_SAFARI_CONFIG, %g2;			\
+	srlx		%g2, 17, %g2;					\
+	and		%g2, 0x3ff, %g2;				\
+	sllx		%g2, 9, %g2;					\
+	sethi		%hi(cheetah_error_log), %g3;			\
+	ldx		[%g3 + %lo(cheetah_error_log)], %g3;		\
+	brz,pn		%g3, 80f;					\
+	 nop;								\
+	add		%g3, %g2, %g3;					\
+	sllx		%g1, 8, %g1;					\
+	add		%g3, %g1, %g1;					\
+	/* %g1 holds pointer to the top of the logging scoreboard */	\
+	ldx		[%g1 + 0x0], %g7;				\
+	cmp		%g7, -1;					\
+	bne,pn		%xcc, 80f;					\
+	 nop;								\
+	stx		%g4, [%g1 + 0x0];				\
+	stx		%g5, [%g1 + 0x8];				\
+	add		%g1, 0x10, %g1;					\
+	/* %g1 now points to D-cache logging area */			\
+	set		0x3ff8, %g2;	/* DC_addr mask		*/	\
+	and		%g5, %g2, %g2;	/* DC_addr bits of AFAR	*/	\
+	srlx		%g5, 12, %g3;					\
+	or		%g3, 1, %g3;	/* PHYS tag + valid	*/	\
+10:	ldxa		[%g2] ASI_DCACHE_TAG, %g7;			\
+	cmp		%g3, %g7;	/* TAG match?		*/	\
+	bne,pt		%xcc, 13f;					\
+	 nop;								\
+	/* Yep, what we want, capture state. */				\
+	stx		%g2, [%g1 + 0x20];				\
+	stx		%g7, [%g1 + 0x28];				\
+	/* A membar Sync is required before and after utag access. */	\
+	membar		#Sync;						\
+	ldxa		[%g2] ASI_DCACHE_UTAG, %g7;			\
+	membar		#Sync;						\
+	stx		%g7, [%g1 + 0x30];				\
+	ldxa		[%g2] ASI_DCACHE_SNOOP_TAG, %g7;		\
+	stx		%g7, [%g1 + 0x38];				\
+	clr		%g3;						\
+12:	ldxa		[%g2 + %g3] ASI_DCACHE_DATA, %g7;		\
+	stx		%g7, [%g1];					\
+	add		%g3, (1 << 5), %g3;				\
+	cmp		%g3, (4 << 5);					\
+	bl,pt		%xcc, 12b;					\
+	 add		%g1, 0x8, %g1;					\
+	ba,pt		%xcc, 20f;					\
+	 add		%g1, 0x20, %g1;					\
+13:	sethi		%hi(1 << 14), %g7;				\
+	add		%g2, %g7, %g2;					\
+	srlx		%g2, 14, %g7;					\
+	cmp		%g7, 4;						\
+	bl,pt		%xcc, 10b;					\
+	 nop;								\
+	add		%g1, 0x40, %g1;					\
+20:	/* %g1 now points to I-cache logging area */			\
+	set		0x1fe0, %g2;	/* IC_addr mask		*/	\
+	and		%g5, %g2, %g2;	/* IC_addr bits of AFAR	*/	\
+	sllx		%g2, 1, %g2;	/* IC_addr[13:6]==VA[12:5] */	\
+	srlx		%g5, (13 - 8), %g3; /* Make PTAG */		\
+	andn		%g3, 0xff, %g3;	/* Mask off undefined bits */	\
+21:	ldxa		[%g2] ASI_IC_TAG, %g7;				\
+	andn		%g7, 0xff, %g7;					\
+	cmp		%g3, %g7;					\
+	bne,pt		%xcc, 23f;					\
+	 nop;								\
+	/* Yep, what we want, capture state. */				\
+	stx		%g2, [%g1 + 0x40];				\
+	stx		%g7, [%g1 + 0x48];				\
+	add		%g2, (1 << 3), %g2;				\
+	ldxa		[%g2] ASI_IC_TAG, %g7;				\
+	add		%g2, (1 << 3), %g2;				\
+	stx		%g7, [%g1 + 0x50];				\
+	ldxa		[%g2] ASI_IC_TAG, %g7;				\
+	add		%g2, (1 << 3), %g2;				\
+	stx		%g7, [%g1 + 0x60];				\
+	ldxa		[%g2] ASI_IC_TAG, %g7;				\
+	stx		%g7, [%g1 + 0x68];				\
+	sub		%g2, (3 << 3), %g2;				\
+	ldxa		[%g2] ASI_IC_STAG, %g7;				\
+	stx		%g7, [%g1 + 0x58];				\
+	clr		%g3;						\
+	srlx		%g2, 2, %g2;					\
+22:	ldxa		[%g2 + %g3] ASI_IC_INSTR, %g7;			\
+	stx		%g7, [%g1];					\
+	add		%g3, (1 << 3), %g3;				\
+	cmp		%g3, (8 << 3);					\
+	bl,pt		%xcc, 22b;					\
+	 add		%g1, 0x8, %g1;					\
+	ba,pt		%xcc, 30f;					\
+	 add		%g1, 0x30, %g1;					\
+23:	sethi		%hi(1 << 14), %g7;				\
+	add		%g2, %g7, %g2;					\
+	srlx		%g2, 14, %g7;					\
+	cmp		%g7, 4;						\
+	bl,pt		%xcc, 21b;					\
+	 nop;								\
+	add		%g1, 0x70, %g1;					\
+30:	/* %g1 now points to E-cache logging area */			\
+	andn		%g5, (32 - 1), %g2;	/* E-cache subblock */	\
+	stx		%g2, [%g1 + 0x20];				\
+	ldxa		[%g2] ASI_EC_TAG_DATA, %g7;			\
+	stx		%g7, [%g1 + 0x28];				\
+	ldxa		[%g2] ASI_EC_R, %g0;				\
+	clr		%g3;						\
+31:	ldxa		[%g3] ASI_EC_DATA, %g7;				\
+	stx		%g7, [%g1 + %g3];				\
+	add		%g3, 0x8, %g3;					\
+	cmp		%g3, 0x20;					\
+	bl,pt		%xcc, 31b;					\
+	 nop;								\
+80:	/* DONE */
+
+	/* These get patched into the trap table at boot time
+	 * once we know we have a cheetah processor.
+	 */
+	.globl		cheetah_fecc_trap_vector, cheetah_fecc_trap_vector_tl1
+cheetah_fecc_trap_vector:
+	membar		#Sync
+	ldxa		[%g0] ASI_DCU_CONTROL_REG, %g1
+	andn		%g1, DCU_DC | DCU_IC, %g1
+	stxa		%g1, [%g0] ASI_DCU_CONTROL_REG
+	membar		#Sync
+	sethi		%hi(cheetah_fast_ecc), %g2
+	jmpl		%g2 + %lo(cheetah_fast_ecc), %g0
+	 mov		0, %g1
+cheetah_fecc_trap_vector_tl1:
+	membar		#Sync
+	ldxa		[%g0] ASI_DCU_CONTROL_REG, %g1
+	andn		%g1, DCU_DC | DCU_IC, %g1
+	stxa		%g1, [%g0] ASI_DCU_CONTROL_REG
+	membar		#Sync
+	sethi		%hi(cheetah_fast_ecc), %g2
+	jmpl		%g2 + %lo(cheetah_fast_ecc), %g0
+	 mov		1, %g1
+	.globl	cheetah_cee_trap_vector, cheetah_cee_trap_vector_tl1
+cheetah_cee_trap_vector:
+	membar		#Sync
+	ldxa		[%g0] ASI_DCU_CONTROL_REG, %g1
+	andn		%g1, DCU_IC, %g1
+	stxa		%g1, [%g0] ASI_DCU_CONTROL_REG
+	membar		#Sync
+	sethi		%hi(cheetah_cee), %g2
+	jmpl		%g2 + %lo(cheetah_cee), %g0
+	 mov		0, %g1
+cheetah_cee_trap_vector_tl1:
+	membar		#Sync
+	ldxa		[%g0] ASI_DCU_CONTROL_REG, %g1
+	andn		%g1, DCU_IC, %g1
+	stxa		%g1, [%g0] ASI_DCU_CONTROL_REG
+	membar		#Sync
+	sethi		%hi(cheetah_cee), %g2
+	jmpl		%g2 + %lo(cheetah_cee), %g0
+	 mov		1, %g1
+	.globl	cheetah_deferred_trap_vector, cheetah_deferred_trap_vector_tl1
+cheetah_deferred_trap_vector:
+	membar		#Sync
+	ldxa		[%g0] ASI_DCU_CONTROL_REG, %g1;
+	andn		%g1, DCU_DC | DCU_IC, %g1;
+	stxa		%g1, [%g0] ASI_DCU_CONTROL_REG;
+	membar		#Sync;
+	sethi		%hi(cheetah_deferred_trap), %g2
+	jmpl		%g2 + %lo(cheetah_deferred_trap), %g0
+	 mov		0, %g1
+cheetah_deferred_trap_vector_tl1:
+	membar		#Sync;
+	ldxa		[%g0] ASI_DCU_CONTROL_REG, %g1;
+	andn		%g1, DCU_DC | DCU_IC, %g1;
+	stxa		%g1, [%g0] ASI_DCU_CONTROL_REG;
+	membar		#Sync;
+	sethi		%hi(cheetah_deferred_trap), %g2
+	jmpl		%g2 + %lo(cheetah_deferred_trap), %g0
+	 mov		1, %g1
+
+	/* Cheetah FECC trap handling, we get here from tl{0,1}_fecc
+	 * in the trap table.  That code has done a memory barrier
+	 * and has disabled both the I-cache and D-cache in the DCU
+	 * control register.  The I-cache is disabled so that we may
+	 * capture the corrupted cache line, and the D-cache is disabled
+	 * because corrupt data may have been placed there and we don't
+	 * want to reference it.
+	 *
+	 * %g1 is one if this trap occured at %tl >= 1.
+	 *
+	 * Next, we turn off error reporting so that we don't recurse.
+	 */
+	.globl		cheetah_fast_ecc
+cheetah_fast_ecc:
+	ldxa		[%g0] ASI_ESTATE_ERROR_EN, %g2
+	andn		%g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
+	stxa		%g2, [%g0] ASI_ESTATE_ERROR_EN
+	membar		#Sync
+
+	/* Fetch and clear AFSR/AFAR */
+	ldxa		[%g0] ASI_AFSR, %g4
+	ldxa		[%g0] ASI_AFAR, %g5
+	stxa		%g4, [%g0] ASI_AFSR
+	membar		#Sync
+
+	CHEETAH_LOG_ERROR
+
+	rdpr		%pil, %g2
+	wrpr		%g0, 15, %pil
+	ba,pt		%xcc, etrap_irq
+	 rd		%pc, %g7
+	mov		%l4, %o1
+	mov		%l5, %o2
+	call		cheetah_fecc_handler
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	ba,a,pt		%xcc, rtrap_clr_l6
+
+	/* Our caller has disabled I-cache and performed membar Sync. */
+	.globl		cheetah_cee
+cheetah_cee:
+	ldxa		[%g0] ASI_ESTATE_ERROR_EN, %g2
+	andn		%g2, ESTATE_ERROR_CEEN, %g2
+	stxa		%g2, [%g0] ASI_ESTATE_ERROR_EN
+	membar		#Sync
+
+	/* Fetch and clear AFSR/AFAR */
+	ldxa		[%g0] ASI_AFSR, %g4
+	ldxa		[%g0] ASI_AFAR, %g5
+	stxa		%g4, [%g0] ASI_AFSR
+	membar		#Sync
+
+	CHEETAH_LOG_ERROR
+
+	rdpr		%pil, %g2
+	wrpr		%g0, 15, %pil
+	ba,pt		%xcc, etrap_irq
+	 rd		%pc, %g7
+	mov		%l4, %o1
+	mov		%l5, %o2
+	call		cheetah_cee_handler
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	ba,a,pt		%xcc, rtrap_clr_l6
+
+	/* Our caller has disabled I-cache+D-cache and performed membar Sync. */
+	.globl		cheetah_deferred_trap
+cheetah_deferred_trap:
+	ldxa		[%g0] ASI_ESTATE_ERROR_EN, %g2
+	andn		%g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
+	stxa		%g2, [%g0] ASI_ESTATE_ERROR_EN
+	membar		#Sync
+
+	/* Fetch and clear AFSR/AFAR */
+	ldxa		[%g0] ASI_AFSR, %g4
+	ldxa		[%g0] ASI_AFAR, %g5
+	stxa		%g4, [%g0] ASI_AFSR
+	membar		#Sync
+
+	CHEETAH_LOG_ERROR
+
+	rdpr		%pil, %g2
+	wrpr		%g0, 15, %pil
+	ba,pt		%xcc, etrap_irq
+	 rd		%pc, %g7
+	mov		%l4, %o1
+	mov		%l5, %o2
+	call		cheetah_deferred_handler
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	ba,a,pt		%xcc, rtrap_clr_l6
 
 	.globl		__do_privact
 __do_privact:
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S
--- linux-2.4.4-pre2/arch/sparc64/kernel/head.S	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/head.S	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.75 2001/03/22 09:54:26 davem Exp $
+/* $Id: head.S,v 1.77 2001/04/05 12:44:34 davem Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -90,13 +90,25 @@
 	mov	DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1
 	wr	%g1, %asr18
 
-	sethi	%uhi(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
-	or	%g5, %ulo(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
+	sethi	%uhi(DCU_ME | DCU_RE | /*DCU_PE |*/ DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
+	or	%g5, %ulo(DCU_ME | DCU_RE | /*DCU_PE |*/ DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
 	sllx	%g5, 32, %g5
 	or	%g5, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g5
-	ldxa	[%g0] ASI_DCU_CONTROL_REG, %g3
-	or	%g5, %g3, %g5
 	stxa	%g5, [%g0] ASI_DCU_CONTROL_REG
+	membar	#Sync
+
+	mov	TSB_EXTENSION_P, %g3
+	stxa	%g0, [%g3] ASI_DMMU
+	stxa	%g0, [%g3] ASI_IMMU
+	membar	#Sync
+
+	mov	TSB_EXTENSION_S, %g3
+	stxa	%g0, [%g3] ASI_DMMU
+	membar	#Sync
+
+	mov	TSB_EXTENSION_N, %g3
+	stxa	%g0, [%g3] ASI_DMMU
+	stxa	%g0, [%g3] ASI_IMMU
 	membar	#Sync
 
 	wrpr    %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/ioctl32.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/ioctl32.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.110 2001/03/22 12:51:25 davem Exp $
+/* $Id: ioctl32.c,v 1.111 2001/03/27 07:28:43 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -527,6 +527,55 @@
 	return err;
 }
 
+static inline int ethtool_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	struct ifreq ifr;
+	mm_segment_t old_fs;
+	int err, len;
+	u32 data, ethcmd;
+	
+	if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
+		return -EFAULT;
+	ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL);
+	if (!ifr.ifr_data)
+		return -EAGAIN;
+
+	__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
+
+	if (get_user(ethcmd, (u32 *)A(data))) {
+		err = -EFAULT;
+		goto out;
+	}
+	switch (ethcmd) {
+	case ETHTOOL_GDRVINFO:	len = sizeof(struct ethtool_drvinfo); break;
+	case ETHTOOL_GSET:
+	case ETHTOOL_SSET:
+	default:		len = sizeof(struct ethtool_cmd); break;
+	}
+
+	if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) {
+		err = -EFAULT;
+		goto out;
+	}
+
+	old_fs = get_fs();
+	set_fs (KERNEL_DS);
+	err = sys_ioctl (fd, cmd, (unsigned long)&ifr);
+	set_fs (old_fs);
+	if (!err) {
+		u32 data;
+
+		__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
+		len = copy_to_user((char *)A(data), ifr.ifr_data, len);
+		if (len)
+			err = -EFAULT;
+	}
+
+out:
+	free_page((unsigned long)ifr.ifr_data);
+	return err;
+}
+
 static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long arg)
 {
 	struct ifreq ifr;
@@ -548,23 +597,11 @@
 	case SIOCGPPPSTATS:
 	case SIOCGPPPCSTATS:
 	case SIOCGPPPVER:
-	case SIOCETHTOOL:
 		if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
 			return -EFAULT;
 		ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL);
 		if (!ifr.ifr_data)
 			return -EAGAIN;
-		if(cmd == SIOCETHTOOL) {
-			u32 data;
-
-			__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
-			if(copy_from_user(ifr.ifr_data,
-					  (char *)A(data),
-					  sizeof(struct ethtool_cmd))) {
-				free_page((unsigned long)ifr.ifr_data);
-				return -EFAULT;
-			}
-		}
 		break;
 	default:
 		if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
@@ -594,14 +631,11 @@
 		case SIOCGPPPSTATS:
 		case SIOCGPPPCSTATS:
 		case SIOCGPPPVER:
-		case SIOCETHTOOL:
 		{
 			u32 data;
 			int len;
 
 			__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
-			if(cmd == SIOCETHTOOL)
-				len = sizeof(struct ethtool_cmd);
 			if(cmd == SIOCGPPPVER)
 				len = strlen((char *)ifr.ifr_data) + 1;
 			else if(cmd == SIOCGPPPCSTATS)
@@ -627,6 +661,14 @@
 				err = -EFAULT;
 			break;
 		}
+	} else {
+		switch (cmd) {
+		case SIOCGPPPSTATS:
+		case SIOCGPPPCSTATS:
+		case SIOCGPPPVER:
+			free_page((unsigned long)ifr.ifr_data);
+			break;
+		}
 	}
 	return err;
 }
@@ -3705,7 +3747,7 @@
 HANDLE_IOCTL(SIOCGPPPVER, dev_ifsioc)
 HANDLE_IOCTL(SIOCGIFTXQLEN, dev_ifsioc)
 HANDLE_IOCTL(SIOCSIFTXQLEN, dev_ifsioc)
-HANDLE_IOCTL(SIOCETHTOOL, dev_ifsioc)
+HANDLE_IOCTL(SIOCETHTOOL, ethtool_ioctl)
 HANDLE_IOCTL(SIOCADDRT, routing_ioctl)
 HANDLE_IOCTL(SIOCDELRT, routing_ioctl)
 /* Note SIOCRTMSG is no longer, so this is safe and * the user would have seen just an -EINVAL anyways. */
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/pci.c linux/arch/sparc64/kernel/pci.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/pci.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/pci.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: pci.c,v 1.23 2001/03/14 04:17:14 davem Exp $
+/* $Id: pci.c,v 1.24 2001/03/28 10:56:34 davem Exp $
  * pci.c: UltraSparc PCI controller support.
  *
  * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com)
@@ -73,6 +73,7 @@
 
 spinlock_t pci_poke_lock = SPIN_LOCK_UNLOCKED;
 volatile int pci_poke_in_progress;
+volatile int pci_poke_cpu = -1;
 volatile int pci_poke_faulted;
 
 /* Probe for all PCI controllers in the system. */
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/pci_impl.h linux/arch/sparc64/kernel/pci_impl.h
--- linux-2.4.4-pre2/arch/sparc64/kernel/pci_impl.h	Mon Mar 27 10:35:56 2000
+++ linux/arch/sparc64/kernel/pci_impl.h	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: pci_impl.h,v 1.6 2000/03/25 05:18:11 davem Exp $
+/* $Id: pci_impl.h,v 1.7 2001/03/28 10:56:34 davem Exp $
  * pci_impl.h: Helper definitions for PCI controller support.
  *
  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -41,6 +41,7 @@
 /* Configuration space access. */
 extern spinlock_t pci_poke_lock;
 extern volatile int pci_poke_in_progress;
+extern volatile int pci_poke_cpu;
 extern volatile int pci_poke_faulted;
 
 static __inline__ void pci_config_read8(u8 *addr, u8 *ret)
@@ -49,6 +50,7 @@
 	u8 byte;
 
 	spin_lock_irqsave(&pci_poke_lock, flags);
+	pci_poke_cpu = smp_processor_id();
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -58,6 +60,7 @@
 			     : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
 			     : "memory");
 	pci_poke_in_progress = 0;
+	pci_poke_cpu = -1;
 	if (!pci_poke_faulted)
 		*ret = byte;
 	spin_unlock_irqrestore(&pci_poke_lock, flags);
@@ -69,6 +72,7 @@
 	u16 word;
 
 	spin_lock_irqsave(&pci_poke_lock, flags);
+	pci_poke_cpu = smp_processor_id();
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -78,6 +82,7 @@
 			     : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
 			     : "memory");
 	pci_poke_in_progress = 0;
+	pci_poke_cpu = -1;
 	if (!pci_poke_faulted)
 		*ret = word;
 	spin_unlock_irqrestore(&pci_poke_lock, flags);
@@ -89,6 +94,7 @@
 	u32 dword;
 
 	spin_lock_irqsave(&pci_poke_lock, flags);
+	pci_poke_cpu = smp_processor_id();
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -98,6 +104,7 @@
 			     : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
 			     : "memory");
 	pci_poke_in_progress = 0;
+	pci_poke_cpu = -1;
 	if (!pci_poke_faulted)
 		*ret = dword;
 	spin_unlock_irqrestore(&pci_poke_lock, flags);
@@ -108,6 +115,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&pci_poke_lock, flags);
+	pci_poke_cpu = smp_processor_id();
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -117,6 +125,7 @@
 			     : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
 			     : "memory");
 	pci_poke_in_progress = 0;
+	pci_poke_cpu = -1;
 	spin_unlock_irqrestore(&pci_poke_lock, flags);
 }
 
@@ -125,6 +134,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&pci_poke_lock, flags);
+	pci_poke_cpu = smp_processor_id();
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -134,6 +144,7 @@
 			     : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
 			     : "memory");
 	pci_poke_in_progress = 0;
+	pci_poke_cpu = -1;
 	spin_unlock_irqrestore(&pci_poke_lock, flags);
 }
 
@@ -142,6 +153,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&pci_poke_lock, flags);
+	pci_poke_cpu = smp_processor_id();
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -151,6 +163,7 @@
 			     : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
 			     : "memory");
 	pci_poke_in_progress = 0;
+	pci_poke_cpu = -1;
 	spin_unlock_irqrestore(&pci_poke_lock, flags);
 }
 
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/process.c	Mon Mar 26 11:01:17 2001
+++ linux/arch/sparc64/kernel/process.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.116 2001/03/24 09:36:01 davem Exp $
+/*  $Id: process.c,v 1.117 2001/03/30 07:10:41 davem Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/smp.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/smp.c	Mon Dec  3 22:34:50 2001
@@ -415,9 +415,7 @@
 
 	/* Setup the dispatch data registers. */
 	__asm__ __volatile__("stxa	%0, [%3] %6\n\t"
-			     "membar	#Sync\n\t"
 			     "stxa	%1, [%4] %6\n\t"
-			     "membar	#Sync\n\t"
 			     "stxa	%2, [%5] %6\n\t"
 			     "membar	#Sync\n\t"
 			     : /* no outputs */
@@ -994,6 +992,8 @@
 
 cycles_t cacheflush_time;
 
+extern unsigned long cheetah_tune_scheduling(void);
+
 static void __init smp_tune_scheduling (void)
 {
 	unsigned long orig_flush_base, flush_base, flags, *p;
@@ -1011,6 +1011,11 @@
 	 * of moving a process from one cpu to another).
 	 */
 	printk("SMP: Calibrating ecache flush... ");
+	if (tlb_type == cheetah) {
+		cacheflush_time = cheetah_tune_scheduling();
+		goto report;
+	}
+
 	ecache_size = prom_getintdefault(linux_cpus[0].prom_node,
 					 "ecache-size", (512 * 1024));
 	if (ecache_size > (4 * 1024 * 1024))
@@ -1063,7 +1068,7 @@
 		cacheflush_time = ((ecache_size << 2) +
 				   (ecache_size << 1));
 	}
-
+report:
 	printk("Using heuristic of %d cycles.\n",
 	       (int) cacheflush_time);
 }
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/sparc64_ksyms.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.102 2001/03/24 09:36:01 davem Exp $
+/* $Id: sparc64_ksyms.c,v 1.104 2001/04/05 11:08:11 davem Exp $
  * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -149,6 +149,8 @@
 EXPORT_SYMBOL(_do_write_unlock);
 #endif
 
+EXPORT_SYMBOL(smp_call_function);
+
 #endif
 
 /* semaphores */
@@ -180,7 +182,7 @@
 EXPORT_SYMBOL(__flushw_user);
 
 EXPORT_SYMBOL(tlb_type);
-
+EXPORT_SYMBOL(get_fb_unmapped_area);
 EXPORT_SYMBOL(flush_icache_range);
 EXPORT_SYMBOL(__flush_dcache_page);
 
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/sys_sparc.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/sys_sparc.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.50 2001/03/24 09:36:10 davem Exp $
+/* $Id: sys_sparc.c,v 1.51 2001/03/27 02:36:37 davem Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -42,19 +42,28 @@
 
 #define COLOUR_ALIGN(addr)	(((addr)+SHMLBA-1)&~(SHMLBA-1))
 
-unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
 	struct vm_area_struct * vmm;
 	unsigned long task_size = TASK_SIZE;
 
+	if (flags & MAP_FIXED) {
+		/* We do not accept a shared mapping if it would violate
+		 * cache aliasing constraints.
+		 */
+		if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
+			return -EINVAL;
+		return addr;
+	}
+
 	if (current->thread.flags & SPARC_FLAG_32BIT)
 		task_size = 0xf0000000UL;
 	if (len > task_size || len > -PAGE_OFFSET)
-		return 0;
+		return -ENOMEM;
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
-	if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+	if (flags & MAP_SHARED)
 		addr = COLOUR_ALIGN(addr);
 	else
 		addr = PAGE_ALIGN(addr);
@@ -68,15 +77,58 @@
 			vmm = find_vma(current->mm, PAGE_OFFSET);
 		}
 		if (task_size < addr)
-			return 0;
+			return -ENOMEM;
 		if (!vmm || addr + len <= vmm->vm_start)
 			return addr;
 		addr = vmm->vm_end;
-		if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+		if (flags & MAP_SHARED)
 			addr = COLOUR_ALIGN(addr);
 	}
 }
 
+/* Try to align mapping such that we align it as much as possible. */
+unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	unsigned long align_goal, addr = -ENOMEM;
+
+	if (flags & MAP_FIXED) {
+		/* Ok, don't mess with it. */
+		return get_unmapped_area(NULL, addr, len, pgoff, flags);
+	}
+	flags &= ~MAP_SHARED;
+
+	align_goal = PAGE_SIZE;
+	if (len >= (4UL * 1024 * 1024))
+		align_goal = (4UL * 1024 * 1024);
+	else if (len >= (512UL * 1024))
+		align_goal = (512UL * 1024);
+	else if (len >= (64UL * 1024))
+		align_goal = (64UL * 1024);
+
+	do {
+		addr = get_unmapped_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags);
+		if (!(addr & ~PAGE_MASK)) {
+			addr = (addr + (align_goal - 1UL)) & ~(align_goal - 1UL);
+			break;
+		}
+
+		if (align_goal == (4UL * 1024 * 1024))
+			align_goal = (512UL * 1024);
+		else if (align_goal == (512UL * 1024))
+			align_goal = (64UL * 1024);
+		else
+			align_goal = PAGE_SIZE;
+	} while ((addr & ~PAGE_MASK) && align_goal > PAGE_SIZE);
+
+	/* Mapping is smaller than 64K or larger areas could not
+	 * be obtained.
+	 */
+	if (addr & ~PAGE_MASK)
+		addr = get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+
+	return addr;
+}
+
 extern asmlinkage unsigned long sys_brk(unsigned long brk);
 
 asmlinkage unsigned long sparc_brk(unsigned long brk)
@@ -240,15 +292,10 @@
 			goto out_putf;
 	}
 
-	if (flags & MAP_SHARED)
-		current->thread.flags |= SPARC_FLAG_MMAPSHARED;
-
 	down_write(&current->mm->mmap_sem);
 	retval = do_mmap(file, addr, len, prot, flags, off);
 	up_write(&current->mm->mmap_sem);
 
-	current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
-
 out_putf:
 	if (file)
 		fput(file);
@@ -286,25 +333,37 @@
 	if (addr < PAGE_OFFSET && addr + old_len > -PAGE_OFFSET)
 		goto out;
 	down_write(&current->mm->mmap_sem);
-	vma = find_vma(current->mm, addr);
-	if (vma && (vma->vm_flags & VM_SHARED))
-		current->thread.flags |= SPARC_FLAG_MMAPSHARED;
 	if (flags & MREMAP_FIXED) {
 		if (new_addr < PAGE_OFFSET &&
 		    new_addr + new_len > -PAGE_OFFSET)
 			goto out_sem;
 	} else if (addr < PAGE_OFFSET && addr + new_len > -PAGE_OFFSET) {
+		unsigned long (*get_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+		unsigned long map_flags = 0;
+		struct file *file = NULL;
+
 		ret = -ENOMEM;
 		if (!(flags & MREMAP_MAYMOVE))
 			goto out_sem;
-		new_addr = get_unmapped_area(addr, new_len);
-		if (!new_addr)
+
+		vma = find_vma(current->mm, addr);
+		if (vma) {
+			if (vma->vm_flags & VM_SHARED)
+				map_flags |= MAP_SHARED;
+			file = vma->vm_file;
+		}
+
+		/* MREMAP_FIXED checked above. */
+		new_addr = get_unmapped_area(file, addr, new_len,
+				    vma ? vma->vm_pgoff : 0,
+				    map_flags);
+		ret = new_addr;
+		if (new_addr & ~PAGE_MASK)
 			goto out_sem;
 		flags |= MREMAP_FIXED;
 	}
 	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
 out_sem:
-	current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
 	up_write(&current->mm->mmap_sem);
 out:
 	return ret;       
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/sys_sparc32.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.174 2001/03/24 09:36:10 davem Exp $
+/* $Id: sys_sparc32.c,v 1.175 2001/03/27 02:36:37 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -4134,24 +4134,36 @@
 	if (addr > 0xf0000000UL - old_len)
 		goto out;
 	down_write(&current->mm->mmap_sem);
-	vma = find_vma(current->mm, addr);
-	if (vma && (vma->vm_flags & VM_SHARED))
-		current->thread.flags |= SPARC_FLAG_MMAPSHARED;
 	if (flags & MREMAP_FIXED) {
 		if (new_addr > 0xf0000000UL - new_len)
 			goto out_sem;
 	} else if (addr > 0xf0000000UL - new_len) {
+		unsigned long (*get_addr)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+		unsigned long map_flags = 0;
+		struct file *file = NULL;
+
 		ret = -ENOMEM;
 		if (!(flags & MREMAP_MAYMOVE))
 			goto out_sem;
-		new_addr = get_unmapped_area(addr, new_len);
-		if (!new_addr)
+
+		vma = find_vma(current->mm, addr);
+		if (vma) {
+			if (vma->vm_flags & VM_SHARED)
+				map_flags |= MAP_SHARED;
+			file = vma->vm_file;
+		}
+
+		/* MREMAP_FIXED checked above. */
+		new_addr = get_unmapped_addr(file, addr, new_len,
+				    vma ? vma->vm_pgoff : 0,
+				    map_flags);
+		ret = new_addr;
+		if (new_addr & ~PAGE_MASK)
 			goto out_sem;
 		flags |= MREMAP_FIXED;
 	}
 	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
 out_sem:
-	current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
 	up_write(&current->mm->mmap_sem);
 out:
 	return ret;       
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/trampoline.S linux/arch/sparc64/kernel/trampoline.S
--- linux-2.4.4-pre2/arch/sparc64/kernel/trampoline.S	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/trampoline.S	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: trampoline.S,v 1.19 2001/03/22 09:54:26 davem Exp $
+/* $Id: trampoline.S,v 1.21 2001/04/05 12:44:34 davem Exp $
  * trampoline.S: Jump start slave processors on sparc64.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -44,14 +44,26 @@
 	mov		DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1
 	wr		%g1, %asr18
 
-	sethi		%uhi(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
-	or		%g5, %ulo(DCU_ME | DCU_RE | DCU_PE | DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
+	sethi		%uhi(DCU_ME | DCU_RE | /*DCU_PE |*/ DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
+	or		%g5, %ulo(DCU_ME | DCU_RE | /*DCU_PE |*/ DCU_HPE | DCU_SPE | DCU_SL | DCU_WE), %g5
 	sllx		%g5, 32, %g5
 	or		%g5, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g5
-	ldxa		[%g0] ASI_DCU_CONTROL_REG, %g3
-	or		%g5, %g3, %g5
 	stxa		%g5, [%g0] ASI_DCU_CONTROL_REG
 	membar		#Sync
+
+	mov	TSB_EXTENSION_P, %g3
+	stxa	%g0, [%g3] ASI_DMMU
+	stxa	%g0, [%g3] ASI_IMMU
+	membar	#Sync
+
+	mov	TSB_EXTENSION_S, %g3
+	stxa	%g0, [%g3] ASI_DMMU
+	membar	#Sync
+
+	mov	TSB_EXTENSION_N, %g3
+	stxa	%g0, [%g3] ASI_DMMU
+	stxa	%g0, [%g3] ASI_IMMU
+	membar	#Sync
 
 	/* Disable STICK_INT interrupts. */
 	sethi		%hi(0x80000000), %g5
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/traps.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/traps.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.73 2001/03/22 07:26:03 davem Exp $
+/* $Id: traps.c,v 1.76 2001/04/03 13:46:31 davem Exp $
  * arch/sparc64/kernel/traps.c
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -15,6 +15,7 @@
 #include <linux/signal.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/mm.h>
 
 #include <asm/delay.h>
 #include <asm/system.h>
@@ -27,231 +28,13 @@
 #include <asm/fpumacro.h>
 #include <asm/lsu.h>
 #include <asm/dcu.h>
+#include <asm/estate.h>
+#include <asm/chafsr.h>
 #include <asm/psrcompat.h>
 #ifdef CONFIG_KMOD
 #include <linux/kmod.h>
 #endif
 
-/* #define SYSCALL_TRACING */
-/* #define VERBOSE_SYSCALL_TRACING */
-/* #define DEBUG_FPU */
-
-#ifdef SYSCALL_TRACING
-#ifdef VERBOSE_SYSCALL_TRACING
-struct sdesc {
-	int	scall_num;
-	char	*name;
-	int	num_args;
-	char	arg_is_string[6];
-} sdesc_entries[] = {
-	{ 0, "setup", 0, },
-	{ 1, "exit", 1, { 0, } },
-	{ 2, "fork", 0, },
-	{ 3, "read", 3, { 0, 0, 0, } },
-	{ 4, "write", 3, { 0, 0, 0, } },
-	{ 5, "open", 3, { 1, 0, 0, } },
-	{ 6, "close", 1, { 0, } },
-	{ 7, "wait4", 4, { 0, 0, 0, 0, } },
-	{ 8, "creat", 2, { 1, 0, } },
-	{ 9, "link", 2, { 1, 1, } },
-	{ 10, "unlink", 1, { 1, } },
-	{ 11, "execv", 2, { 1, 0, } },
-	{ 12, "chdir", 1, { 1, } },
-	{ 15, "chmod", 2, { 1, 0, } },
-	{ 16, "chown", 3, { 1, 0, 0, } },
-	{ 17, "brk", 1, { 0, } },
-	{ 19, "lseek", 3, { 0, 0, 0, } },
-	{ 27, "alarm", 1, { 0, } },
-	{ 29, "pause", 0, },
-	{ 33, "access", 2, { 1, 0, } },
-	{ 36, "sync", 0, },
-	{ 37, "kill", 2, { 0, 0, } },
-	{ 38, "stat", 2, { 1, 0, } },
-	{ 40, "lstat", 2, { 1, 0, } },
-	{ 41, "dup", 1, { 0, } },
-	{ 42, "pipd", 0, },
-	{ 54, "ioctl", 3, { 0, 0, 0, } },
-	{ 57, "symlink", 2, { 1, 1, } },
-	{ 58, "readlink", 3, { 1, 0, 0, } },
-	{ 59, "execve", 3, { 1, 0, 0, } },
-	{ 60, "umask", 1, { 0, } },
-	{ 62, "fstat", 2, { 0, 0, } },
-	{ 64, "getpagesize", 0, },
-	{ 71, "mmap", 6, { 0, 0, 0, 0, 0, 0, } },
-	{ 73, "munmap", 2, { 0, 0, } },
-	{ 74, "mprotect", 3, { 0, 0, 0, } },
-	{ 83, "setitimer", 3, { 0, 0, 0, } },
-	{ 90, "dup2", 2, { 0, 0, } },
-	{ 92, "fcntl", 3, { 0, 0, 0, } },
-	{ 93, "select", 5, { 0, 0, 0, 0, 0, } },
-	{ 97, "socket", 3, { 0, 0, 0, } },
-	{ 98, "connect", 3, { 0, 0, 0, } },
-	{ 99, "accept", 3, { 0, 0, 0, } },
-	{ 101, "send", 4, { 0, 0, 0, 0, } },
-	{ 102, "recv", 4, { 0, 0, 0, 0, } },
-	{ 104, "bind", 3, { 0, 0, 0, } },
-	{ 105, "setsockopt", 5, { 0, 0, 0, 0, 0, } },
-	{ 106, "listen", 2, { 0, 0, } },
-	{ 120, "readv", 3, { 0, 0, 0, } },
-	{ 121, "writev", 3, { 0, 0, 0, } },
-	{ 123, "fchown", 3, { 0, 0, 0, } },
-	{ 124, "fchmod", 2, { 0, 0, } },
-	{ 128, "rename", 2, { 1, 1, } },
-	{ 129, "truncate", 2, { 1, 0, } },
-	{ 130, "ftruncate", 2, { 0, 0, } },
-	{ 131, "flock", 2, { 0, 0, } },
-	{ 136, "mkdir", 2, { 1, 0, } },
-	{ 137, "rmdir", 1, { 1, } },
-	{ 146, "killpg", 1, { 0, } },
-	{ 157, "statfs", 2, { 1, 0, } },
-	{ 158, "fstatfs", 2, { 0, 0, } },
-	{ 159, "umount", 1, { 1, } },
-	{ 167, "mount", 5, { 1, 1, 1, 0, 0, } },
-	{ 174, "getdents", 3, { 0, 0, 0, } },
-	{ 176, "fchdir", 2, { 0, 0, } },
-	{ 198, "sigaction", 3, { 0, 0, 0, } },
-	{ 201, "sigsuspend", 1, { 0, } },
-	{ 206, "socketcall", 2, { 0, 0, } },
-	{ 216, "sigreturn", 0, },
-	{ 230, "newselect", 5, { 0, 0, 0, 0, 0, } },
-	{ 236, "llseek", 5, { 0, 0, 0, 0, 0, } },
-	{ 251, "sysctl", 1, { 0, } },
-};
-#define NUM_SDESC_ENTRIES (sizeof(sdesc_entries) / sizeof(sdesc_entries[0]))
-#endif
-
-#ifdef VERBOSE_SYSCALL_TRACING
-static char scall_strbuf[512];
-#endif
-
-void syscall_trace_entry(unsigned long g1, struct pt_regs *regs)
-{
-#ifdef VERBOSE_SYSCALL_TRACING
-	struct sdesc *sdp;
-	int i;
-#endif
-
-#if 0
-	if (!current->pid) return;
-#endif	
-	printk("SYS[%s:%d]: PC(%016lx) <%3d> ",
-	       current->comm, current->pid, regs->tpc, (int)g1);
-#ifdef VERBOSE_SYSCALL_TRACING
-	sdp = NULL;
-	for(i = 0; i < NUM_SDESC_ENTRIES; i++)
-		if(sdesc_entries[i].scall_num == g1) {
-			sdp = &sdesc_entries[i];
-			break;
-		}
-	if(sdp) {
-		printk("%s(", sdp->name);
-		for(i = 0; i < sdp->num_args; i++) {
-			if(i)
-				printk(",");
-			if(!sdp->arg_is_string[i]) {
-				if (current->thread.flags & SPARC_FLAG_32BIT)
-					printk("%08x", (unsigned int)regs->u_regs[UREG_I0 + i]);
-				else
-					printk("%016lx", regs->u_regs[UREG_I0 + i]);
-			} else {
-				if (current->thread.flags & SPARC_FLAG_32BIT)
-					strncpy_from_user(scall_strbuf,
-							  (char *)(regs->u_regs[UREG_I0 + i] & 0xffffffff),
-							  512);
-				else
-					strncpy_from_user(scall_strbuf,
-							  (char *)regs->u_regs[UREG_I0 + i],
-							  512);
-				printk("%s", scall_strbuf);
-			}
-		}
-		printk(") ");
-	}
-#endif
-}
-
-unsigned long syscall_trace_exit(unsigned long retval, struct pt_regs *regs)
-{
-#if 0
-	if (current->pid)
-#endif
-		printk("ret[%016lx]\n", retval);
-	return retval;
-}
-#endif /* SYSCALL_TRACING */
-
-#if 1
-void rtrap_check(struct pt_regs *regs)
-{
-	register unsigned long pgd_phys asm("o1");
-	register unsigned long pgd_cache asm("o2");
-	register unsigned long g1_or_g3 asm("o3");
-	register unsigned long g2 asm("o4");
-	unsigned long ctx;
-
-#if 0
-	do {
-		unsigned long test;
-		__asm__ __volatile__("rdpr	%%pstate, %0"
-				     : "=r" (test));
-		if((test & PSTATE_MG) != 0 ||
-		   (test & PSTATE_IE) == 0) {
-			printk("rtrap_check: Bogus pstate[%016lx]\n", test);
-			return;
-		}
-	} while(0);
-#endif
-
-	__asm__ __volatile__("
-	rdpr	%%pstate, %%o5
-	wrpr	%%o5, %4, %%pstate
-	or	%%g1, %%g3, %2
-	mov	%%g2, %3
-	mov	%%g7, %0
-	mov	%5, %1
-	ldxa	[%1] %6, %1
-	wrpr	%%o5, 0x0, %%pstate"
-	: "=r" (pgd_phys), "=r" (pgd_cache),
-	  "=r" (g1_or_g3), "=r" (g2)
-	: "i" (PSTATE_IE | PSTATE_MG), "i" (TSB_REG),
-	  "i" (ASI_DMMU)
-	: "o5");
-
-	ctx = spitfire_get_secondary_context();
-
-	if((pgd_phys != __pa(current->mm->pgd)) ||
-	   ((pgd_cache != 0) &&
-	    (pgd_cache != pgd_val(current->mm->pgd[0])<<11UL)) ||
-	   (g1_or_g3 != (0xfffffffe00000000UL | 0x0000000000000018UL)) ||
-#define KERN_HIGHBITS		((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
-#define KERN_LOWBITS		(_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
-	   (g2 != (KERN_HIGHBITS | KERN_LOWBITS)) ||
-#undef KERN_HIGHBITS
-#undef KERN_LOWBITS
-	   ((ctx != (current->mm->context & 0x3ff)) ||
-	    (ctx == 0) ||
-	    (CTX_HWBITS(current->mm->context) != ctx))) {
-		printk("SHIT[%s:%d]: "
-		       "(PP[%016lx] CACH[%016lx] CTX[%lx] g1g3[%016lx] g2[%016lx]) ",
-		       current->comm, current->pid,
-		       pgd_phys, pgd_cache, ctx, g1_or_g3, g2);
-		printk("SHIT[%s:%d]: "
-		       "[PP[%016lx] CACH[%016lx] CTX[%lx]] PC[%016lx:%016lx]\n",
-		       current->comm, current->pid,
-		       __pa(current->mm->pgd),
-		       pgd_val(current->mm->pgd[0]),
-		       current->mm->context & 0x3ff,
-		       regs->tpc, regs->tnpc);
-		show_regs(regs);
-#if 1
-		__sti();
-		while(1)
-			barrier();
-#endif
-	}
-}
-#endif
-
 void bad_trap (struct pt_regs *regs, long lvl)
 {
 	siginfo_t info;
@@ -290,10 +73,8 @@
 	siginfo_t info;
 
 	if (regs->tstate & TSTATE_PRIV) {
-#if 1
-		printk("instruction_access_exception: Shit SFSR[%016lx] SFAR[%016lx], going.\n",
+		printk("instruction_access_exception: SFSR[%016lx] SFAR[%016lx], going.\n",
 		       sfsr, sfar);
-#endif
 		die_if_kernel("Iax", regs);
 	}
 	if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) {
@@ -331,16 +112,11 @@
 			return;
 		}
 		/* Shit... */
-#if 1
-		printk("data_access_exception: Shit SFSR[%016lx] SFAR[%016lx], going.\n",
+		printk("data_access_exception: SFSR[%016lx] SFAR[%016lx], going.\n",
 		       sfsr, sfar);
-#endif
 		die_if_kernel("Dax", regs);
 	}
-#if 0
-	else
-		rtrap_check(regs);
-#endif
+
 	info.si_signo = SIGSEGV;
 	info.si_errno = 0;
 	info.si_code = SEGV_MAPERR;
@@ -352,6 +128,7 @@
 #ifdef CONFIG_PCI
 /* This is really pathetic... */
 extern volatile int pci_poke_in_progress;
+extern volatile int pci_poke_cpu;
 extern volatile int pci_poke_faulted;
 #endif
 
@@ -405,7 +182,7 @@
 void do_dae(struct pt_regs *regs)
 {
 #ifdef CONFIG_PCI
-	if (pci_poke_in_progress) {
+	if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
 		clean_and_reenable_l1_caches();
 
 		pci_poke_faulted = 1;
@@ -539,6 +316,986 @@
 	}
 }
 
+/* Cheetah error trap handling. */
+static unsigned long ecache_flush_physbase;
+static unsigned long ecache_flush_linesize;
+static unsigned long ecache_flush_size;
+
+/* WARNING: The error trap handlers in assembly know the precise
+ *	    layout of the following structure.
+ *
+ * C-level handlers below use this information to log the error
+ * and then determine how to recover (if possible).
+ */
+struct cheetah_err_info {
+/*0x00*/u64 afsr;
+/*0x08*/u64 afar;
+
+	/* D-cache state */
+/*0x10*/u64 dcache_data[4];	/* The actual data	*/
+/*0x30*/u64 dcache_index;	/* D-cache index	*/
+/*0x38*/u64 dcache_tag;		/* D-cache tag/valid	*/
+/*0x40*/u64 dcache_utag;	/* D-cache microtag	*/
+/*0x48*/u64 dcache_stag;	/* D-cache snooptag	*/
+
+	/* I-cache state */
+/*0x50*/u64 icache_data[8];	/* The actual insns + predecode	*/
+/*0x90*/u64 icache_index;	/* I-cache index	*/
+/*0x98*/u64 icache_tag;		/* I-cache phys tag	*/
+/*0xa0*/u64 icache_utag;	/* I-cache microtag	*/
+/*0xa8*/u64 icache_stag;	/* I-cache snooptag	*/
+/*0xb0*/u64 icache_upper;	/* I-cache upper-tag	*/
+/*0xb8*/u64 icache_lower;	/* I-cache lower-tag	*/
+
+	/* E-cache state */
+/*0xc0*/u64 ecache_data[4];	/* 32 bytes from staging registers */
+/*0xe0*/u64 ecache_index;	/* E-cache index	*/
+/*0xe8*/u64 ecache_tag;		/* E-cache tag/state	*/
+
+/*0xf0*/u64 __pad[32 - 30];
+};
+#define CHAFSR_INVALID		((u64)-1L)
+
+/* This is allocated at boot time based upon the largest hardware
+ * cpu ID in the system.  We allocate two entries per cpu, one for
+ * TL==0 logging and one for TL >= 1 logging.
+ */
+struct cheetah_err_info *cheetah_error_log;
+
+static __inline__ struct cheetah_err_info *cheetah_get_error_log(unsigned long afsr)
+{
+	struct cheetah_err_info *p;
+	int cpu = smp_processor_id();
+
+	if (!cheetah_error_log)
+		return NULL;
+
+	p = cheetah_error_log + (cpu * 2);
+	if ((afsr & CHAFSR_TL1) != 0UL)
+		p++;
+
+	return p;
+}
+
+extern unsigned int tl0_fecc[], tl1_fecc[];
+extern unsigned int tl0_cee[], tl1_cee[];
+extern unsigned int tl0_iae[], tl1_iae[];
+extern unsigned int tl0_dae[], tl1_dae[];
+extern unsigned int cheetah_fecc_trap_vector[], cheetah_fecc_trap_vector_tl1[];
+extern unsigned int cheetah_cee_trap_vector[], cheetah_cee_trap_vector_tl1[];
+extern unsigned int cheetah_deferred_trap_vector[], cheetah_deferred_trap_vector_tl1[];
+
+void cheetah_ecache_flush_init(void)
+{
+	unsigned long largest_size, smallest_linesize, order;
+	char type[16];
+	int node, highest_cpu, i;
+
+	/* Scan all cpu device tree nodes, note two values:
+	 * 1) largest E-cache size
+	 * 2) smallest E-cache line size
+	 */
+	largest_size = 0UL;
+	smallest_linesize = ~0UL;
+	node = prom_getchild(prom_root_node);
+	while ((node = prom_getsibling(node)) != 0) {
+		prom_getstring(node, "device_type", type, sizeof(type));
+		if (!strcmp(type, "cpu")) {
+			unsigned long val;
+
+			val = prom_getintdefault(node, "ecache-size",
+						 (2 * 1024 * 1024));
+			if (val > largest_size)
+				largest_size = val;
+			val = prom_getintdefault(node, "ecache-line-size", 64);
+			if (val < smallest_linesize)
+				smallest_linesize = val;
+		}
+	}
+	if (largest_size == 0UL || smallest_linesize == ~0UL) {
+		prom_printf("cheetah_ecache_flush_init: Cannot probe cpu E-cache "
+			    "parameters.\n");
+		prom_halt();
+	}
+
+	ecache_flush_size = (2 * largest_size);
+	ecache_flush_linesize = smallest_linesize;
+
+	/* Discover a physically contiguous chunk of physical
+	 * memory in 'sp_banks' of size ecache_flush_size calculated
+	 * above.  Store the physical base of this area at
+	 * ecache_flush_physbase.
+	 */
+	for (node = 0; ; node++) {
+		if (sp_banks[node].num_bytes == 0)
+			break;
+		if (sp_banks[node].num_bytes >= ecache_flush_size) {
+			ecache_flush_physbase = sp_banks[node].base_addr;
+			break;
+		}
+	}
+
+	/* Note: Zero would be a valid value of ecache_flush_physbase so
+	 * don't use that as the success test. :-)
+	 */
+	if (sp_banks[node].num_bytes == 0) {
+		prom_printf("cheetah_ecache_flush_init: Cannot find %d byte "
+			    "contiguous physical memory.\n", ecache_flush_size);
+		prom_halt();
+	}
+
+	/* Now allocate error trap reporting scoreboard. */
+	highest_cpu = 0;
+#ifdef CONFIG_SMP
+	for (i = 0; i < NR_CPUS; i++) {
+		if ((1UL << i) & cpu_present_map)
+			highest_cpu = i;
+	}
+#endif
+	highest_cpu++;
+	node = highest_cpu * (2 * sizeof(struct cheetah_err_info));
+	for (order = 0; order < MAX_ORDER; order++) {
+		if ((PAGE_SIZE << order) >= node)
+			break;
+	}
+	cheetah_error_log = (struct cheetah_err_info *)
+		__get_free_pages(GFP_KERNEL, order);
+	if (!cheetah_error_log) {
+		prom_printf("cheetah_ecache_flush_init: Failed to allocate "
+			    "error logging scoreboard (%d bytes).\n", node);
+		prom_halt();
+	}
+	memset(cheetah_error_log, 0, PAGE_SIZE << order);
+
+	/* Mark all AFSRs as invalid so that the trap handler will
+	 * log new new information there.
+	 */
+	for (i = 0; i < 2 * highest_cpu; i++)
+		cheetah_error_log[i].afsr = CHAFSR_INVALID;
+
+	/* Now patch trap tables. */
+	memcpy(tl0_fecc, cheetah_fecc_trap_vector, (8 * 4));
+	memcpy(tl1_fecc, cheetah_fecc_trap_vector_tl1, (8 * 4));
+	memcpy(tl0_cee, cheetah_cee_trap_vector, (8 * 4));
+	memcpy(tl1_cee, cheetah_cee_trap_vector_tl1, (8 * 4));
+	memcpy(tl0_iae, cheetah_deferred_trap_vector, (8 * 4));
+	memcpy(tl1_iae, cheetah_deferred_trap_vector_tl1, (8 * 4));
+	memcpy(tl0_dae, cheetah_deferred_trap_vector, (8 * 4));
+	memcpy(tl1_dae, cheetah_deferred_trap_vector_tl1, (8 * 4));
+	flushi(PAGE_OFFSET);
+}
+
+static void cheetah_flush_ecache(void)
+{
+	unsigned long flush_base = ecache_flush_physbase;
+	unsigned long flush_linesize = ecache_flush_linesize;
+	unsigned long flush_size = ecache_flush_size;
+
+	__asm__ __volatile__("1: subcc	%0, %4, %0\n\t"
+			     "   bne,pt	%%xcc, 1b\n\t"
+			     "    ldxa	[%2 + %0] %3, %%g0\n\t"
+			     : "=&r" (flush_size)
+			     : "0" (flush_size), "r" (flush_base),
+			       "i" (ASI_PHYS_USE_EC), "r" (flush_linesize));
+}
+
+static void cheetah_flush_ecache_line(unsigned long physaddr)
+{
+	unsigned long alias;
+
+	physaddr &= ~(8UL - 1UL);
+	physaddr = (ecache_flush_physbase +
+		    (physaddr & ((ecache_flush_size>>1UL) - 1UL)));
+	alias = physaddr + (ecache_flush_size >> 1UL);
+	__asm__ __volatile__("ldxa [%0] %2, %%g0\n\t"
+			     "ldxa [%1] %2, %%g0\n\t"
+			     "membar #Sync"
+			     : /* no outputs */
+			     : "r" (physaddr), "r" (alias),
+			       "i" (ASI_PHYS_USE_EC));
+}
+
+#ifdef CONFIG_SMP
+unsigned long cheetah_tune_scheduling(void)
+{
+	unsigned long tick1, tick2, raw;
+
+	__asm__ __volatile__("rd %%tick, %0" : "=r" (tick1));
+	cheetah_flush_ecache();
+	__asm__ __volatile__("rd %%tick, %0" : "=r" (tick2));
+
+	raw = (tick2 - tick1);
+
+	return (raw - (raw >> 2));
+}
+#endif
+
+/* Unfortunately, the diagnostic access to the I-cache tags we need to
+ * use to clear the thing interferes with I-cache coherency transactions.
+ *
+ * So we must only flush the I-cache when it is disabled.
+ */
+static void cheetah_flush_icache(void)
+{
+	unsigned long dcu_save, i;
+
+	/* Save current DCU, disable I-cache. */
+	__asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
+			     "or %0, %2, %%g1\n\t"
+			     "stxa %%g1, [%%g0] %1\n\t"
+			     "membar #Sync"
+			     : "=r" (dcu_save)
+			     : "i" (ASI_DCU_CONTROL_REG), "i" (DCU_IC)
+			     : "g1");
+
+	/* Clear the valid bits in all the tags. */
+	for (i = 0; i < (1 << 16); i += (1 << 5)) {
+		__asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+				     "membar #Sync"
+				     : /* no outputs */
+				     : "r" (i | (2 << 3)), "i" (ASI_IC_TAG));
+	}
+
+	/* Restore DCU register */
+	__asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
+			     "membar #Sync"
+			     : /* no outputs */
+			     : "r" (dcu_save), "i" (ASI_DCU_CONTROL_REG));
+}
+
+static void cheetah_flush_dcache(void)
+{
+	unsigned long i;
+
+	for (i = 0; i < (1 << 16); i += (1 << 5)) {
+		__asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+				     "membar #Sync"
+				     : /* no outputs */
+				     : "r" (i), "i" (ASI_DCACHE_TAG));
+	}
+}
+
+/* Conversion tables used to frob Cheetah AFSR syndrome values into
+ * something palatable to the memory controller driver get_unumber
+ * routine.
+ */
+#define MT0	137
+#define MT1	138
+#define MT2	139
+#define NONE	254
+#define MTC0	140
+#define MTC1	141
+#define MTC2	142
+#define MTC3	143
+#define C0	128
+#define C1	129
+#define C2	130
+#define C3	131
+#define C4	132
+#define C5	133
+#define C6	134
+#define C7	135
+#define C8	136
+#define M2	144
+#define M3	145
+#define M4	146
+#define M	147
+static unsigned char cheetah_ecc_syntab[] = {
+/*00*/NONE, C0, C1, M2, C2, M2, M3, 47, C3, M2, M2, 53, M2, 41, 29, M,
+/*01*/C4, M, M, 50, M2, 38, 25, M2, M2, 33, 24, M2, 11, M, M2, 16,
+/*02*/C5, M, M, 46, M2, 37, 19, M2, M, 31, 32, M, 7, M2, M2, 10,
+/*03*/M2, 40, 13, M2, 59, M, M2, 66, M, M2, M2, 0, M2, 67, 71, M,
+/*04*/C6, M, M, 43, M, 36, 18, M, M2, 49, 15, M, 63, M2, M2, 6,
+/*05*/M2, 44, 28, M2, M, M2, M2, 52, 68, M2, M2, 62, M2, M3, M3, M4,
+/*06*/M2, 26, 106, M2, 64, M, M2, 2, 120, M, M2, M3, M, M3, M3, M4,
+/*07*/116, M2, M2, M3, M2, M3, M, M4, M2, 58, 54, M2, M, M4, M4, M3,
+/*08*/C7, M2, M, 42, M, 35, 17, M2, M, 45, 14, M2, 21, M2, M2, 5,
+/*09*/M, 27, M, M, 99, M, M, 3, 114, M2, M2, 20, M2, M3, M3, M,
+/*0a*/M2, 23, 113, M2, 112, M2, M, 51, 95, M, M2, M3, M2, M3, M3, M2,
+/*0b*/103, M, M2, M3, M2, M3, M3, M4, M2, 48, M, M, 73, M2, M, M3,
+/*0c*/M2, 22, 110, M2, 109, M2, M, 9, 108, M2, M, M3, M2, M3, M3, M,
+/*0d*/102, M2, M, M, M2, M3, M3, M, M2, M3, M3, M2, M, M4, M, M3,
+/*0e*/98, M, M2, M3, M2, M, M3, M4, M2, M3, M3, M4, M3, M, M, M,
+/*0f*/M2, M3, M3, M, M3, M, M, M, 56, M4, M, M3, M4, M, M, M,
+/*10*/C8, M, M2, 39, M, 34, 105, M2, M, 30, 104, M, 101, M, M, 4,
+/*11*/M, M, 100, M, 83, M, M2, 12, 87, M, M, 57, M2, M, M3, M,
+/*12*/M2, 97, 82, M2, 78, M2, M2, 1, 96, M, M, M, M, M, M3, M2,
+/*13*/94, M, M2, M3, M2, M, M3, M, M2, M, 79, M, 69, M, M4, M,
+/*14*/M2, 93, 92, M, 91, M, M2, 8, 90, M2, M2, M, M, M, M, M4,
+/*15*/89, M, M, M3, M2, M3, M3, M, M, M, M3, M2, M3, M2, M, M3,
+/*16*/86, M, M2, M3, M2, M, M3, M, M2, M, M3, M, M3, M, M, M3,
+/*17*/M, M, M3, M2, M3, M2, M4, M, 60, M, M2, M3, M4, M, M, M2,
+/*18*/M2, 88, 85, M2, 84, M, M2, 55, 81, M2, M2, M3, M2, M3, M3, M4,
+/*19*/77, M, M, M, M2, M3, M, M, M2, M3, M3, M4, M3, M2, M, M,
+/*1a*/74, M, M2, M3, M, M, M3, M, M, M, M3, M, M3, M, M4, M3,
+/*1b*/M2, 70, 107, M4, 65, M2, M2, M, 127, M, M, M, M2, M3, M3, M,
+/*1c*/80, M2, M2, 72, M, 119, 118, M, M2, 126, 76, M, 125, M, M4, M3,
+/*1d*/M2, 115, 124, M, 75, M, M, M3, 61, M, M4, M, M4, M, M, M,
+/*1e*/M, 123, 122, M4, 121, M4, M, M3, 117, M2, M2, M3, M4, M3, M, M,
+/*1f*/111, M, M, M, M4, M3, M3, M, M, M, M3, M, M3, M2, M, M
+};
+static unsigned char cheetah_mtag_syntab[] = {
+       NONE, MTC0,
+       MTC1, NONE,
+       MTC2, NONE,
+       NONE, MT0,
+       MTC3, NONE,
+       NONE, MT1,
+       NONE, MT2,
+       NONE, NONE
+};
+
+/* This table is ordered in priority of errors and matches the
+ * AFAR overwrite policy as well.
+ */
+static struct {
+	unsigned long mask;
+	char *name;
+} cheetah_error_table[] = {
+	{	CHAFSR_PERR,	"System interface protocol error"			},
+	{	CHAFSR_IERR,	"Internal processor error"				},
+	{	CHAFSR_ISAP,	"System request parity error on incoming addresss"	},
+	{	CHAFSR_UCU,	"Uncorrectable E-cache ECC error for ifetch/data"	},
+	{	CHAFSR_UCC,	"SW Correctable E-cache ECC error for ifetch/data"	},
+	{	CHAFSR_UE,	"Uncorrectable system bus data ECC error for read"	},
+	{	CHAFSR_EDU,	"Uncorrectable E-cache ECC error for stmerge/blkld"	},
+	{	CHAFSR_EMU,	"Uncorrectable system bus MTAG error"			},
+	{	CHAFSR_WDU,	"Uncorrectable E-cache ECC error for writeback"		},
+	{	CHAFSR_CPU,	"Uncorrectable ECC error for copyout"			},
+	{	CHAFSR_CE,	"HW corrected system bus data ECC error for read"	},
+	{	CHAFSR_EDC,	"HW corrected E-cache ECC error for stmerge/blkld"	},
+	{	CHAFSR_EMC,	"HW corrected system bus MTAG ECC error"		},
+	{	CHAFSR_WDC,	"HW corrected E-cache ECC error for writeback"		},
+	{	CHAFSR_CPC,	"HW corrected ECC error for copyout"			},
+	{	CHAFSR_TO,	"Unmapped error from system bus"			},
+	{	CHAFSR_BERR,	"Bus error response from system bus"			},
+	/* These two do not update the AFAR. */
+	{	CHAFSR_IVC,	"HW corrected system bus data ECC error for ivec read"	},
+	{	CHAFSR_IVU,	"Uncorrectable system bus data ECC error for ivec read"	},
+	{	0,		NULL							}
+};
+
+/* Return the highest priority error conditon mentioned. */
+static __inline__ unsigned long cheetah_get_hipri(unsigned long afsr)
+{
+	unsigned long tmp = 0;
+	int i;
+
+	for (i = 0; cheetah_error_table[i].mask; i++) {
+		if ((tmp = (afsr & cheetah_error_table[i].mask)) != 0UL)
+			return tmp;
+	}
+	return tmp;
+}
+
+static char *cheetah_get_string(unsigned long bit)
+{
+	int i;
+
+	for (i = 0; cheetah_error_table[i].mask; i++) {
+		if ((bit & cheetah_error_table[i].mask) != 0UL)
+			return cheetah_error_table[i].name;
+	}
+	return "???";
+}
+
+extern int chmc_getunumber(int, unsigned long, char *, int);
+
+static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *info,
+			       unsigned long afsr, unsigned long afar, int recoverable)
+{
+	unsigned long hipri;
+	char unum[256];
+
+	printk("%s" "ERROR(%d): Cheetah error trap taken afsr[%016lx] afar[%016lx] TL1(%d)\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       afsr, afar,
+	       (afsr & CHAFSR_TL1) ? 1 : 0);
+	printk("%s" "ERROR(%d): TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       regs->tpc, regs->tnpc, regs->tstate);
+	printk("%s" "ERROR(%d): M_SYND(%lx),  E_SYND(%lx)%s%s\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT,
+	       (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT,
+	       (afsr & CHAFSR_ME) ? ", Multiple Errors" : "",
+	       (afsr & CHAFSR_PRIV) ? ", Privileged" : "");
+	hipri = cheetah_get_hipri(afsr);
+	printk("%s" "ERROR(%d): Highest priority error (%016lx) \"%s\"\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       hipri, cheetah_get_string(hipri));
+
+	/* Try to get unumber if relevant. */
+#define ESYND_ERRORS	(CHAFSR_IVC | CHAFSR_IVU | \
+			 CHAFSR_CPC | CHAFSR_CPU | \
+			 CHAFSR_UE  | CHAFSR_CE  | \
+			 CHAFSR_EDC | CHAFSR_EDU  | \
+			 CHAFSR_UCC | CHAFSR_UCU  | \
+			 CHAFSR_WDU | CHAFSR_WDC)
+#define MSYND_ERRORS	(CHAFSR_EMC | CHAFSR_EMU)
+	if (afsr & ESYND_ERRORS) {
+		int syndrome;
+		int ret;
+
+		syndrome = (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT;
+		syndrome = cheetah_ecc_syntab[syndrome];
+		ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum));
+		if (ret != -1)
+			printk("%s" "ERROR(%d): AFAR E-syndrome [%s]\n",
+			       (recoverable ? KERN_WARNING : KERN_CRIT),
+			       smp_processor_id(), unum);
+	} else if (afsr & MSYND_ERRORS) {
+		int syndrome;
+		int ret;
+
+		syndrome = (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT;
+		syndrome = cheetah_mtag_syntab[syndrome];
+		ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum));
+		if (ret != -1)
+			printk("%s" "ERROR(%d): AFAR M-syndrome [%s]\n",
+			       (recoverable ? KERN_WARNING : KERN_CRIT),
+			       smp_processor_id(), unum);
+	}
+
+	/* Now dump the cache snapshots. */
+	printk("%s" "ERROR(%d): D-cache idx[%x] tag[%016lx] utag[%016lx] stag[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       (int) info->dcache_index,
+	       info->dcache_tag,
+	       info->dcache_utag,
+	       info->dcache_stag);
+	printk("%s" "ERROR(%d): D-cache data0[%016lx] data1[%016lx] data2[%016lx] data3[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       info->dcache_data[0],
+	       info->dcache_data[1],
+	       info->dcache_data[2],
+	       info->dcache_data[3]);
+	printk("%s" "ERROR(%d): I-cache idx[%x] tag[%016lx] utag[%016lx] stag[%016lx] "
+	       "u[%016lx] l[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       (int) info->icache_index,
+	       info->icache_tag,
+	       info->icache_utag,
+	       info->icache_stag,
+	       info->icache_upper,
+	       info->icache_lower);
+	printk("%s" "ERROR(%d): I-cache INSN0[%016lx] INSN1[%016lx] INSN2[%016lx] INSN3[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       info->icache_data[0],
+	       info->icache_data[1],
+	       info->icache_data[2],
+	       info->icache_data[3]);
+	printk("%s" "ERROR(%d): I-cache INSN4[%016lx] INSN5[%016lx] INSN6[%016lx] INSN7[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       info->icache_data[4],
+	       info->icache_data[5],
+	       info->icache_data[6],
+	       info->icache_data[7]);
+	printk("%s" "ERROR(%d): E-cache idx[%x] tag[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       (int) info->ecache_index, info->ecache_tag);
+	printk("%s" "ERROR(%d): E-cache data0[%016lx] data1[%016lx] data2[%016lx] data3[%016lx]\n",
+	       (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+	       info->ecache_data[0],
+	       info->ecache_data[1],
+	       info->ecache_data[2],
+	       info->ecache_data[3]);
+
+	afsr = (afsr & ~hipri) & CHAFSR_ERRORS;
+	while (afsr != 0UL) {
+		unsigned long bit = cheetah_get_hipri(afsr);
+
+		printk("%s" "ERROR: Multiple-error (%016lx) \"%s\"\n",
+		       (recoverable ? KERN_WARNING : KERN_CRIT),
+		       bit, cheetah_get_string(bit));
+
+		afsr &= ~bit;
+	}
+
+	if (!recoverable)
+		printk(KERN_CRIT "ERROR: This condition is not recoverable.\n");
+}
+
+static int cheetah_recheck_errors(struct cheetah_err_info *logp)
+{
+	unsigned long afsr, afar;
+	int ret = 0;
+
+	__asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
+			     : "=r" (afsr)
+			     : "i" (ASI_AFSR));
+	if ((afsr & CHAFSR_ERRORS) != 0) {
+		if (logp != NULL) {
+			__asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
+					     : "=r" (afar)
+					     : "i" (ASI_AFAR));
+			logp->afsr = afsr;
+			logp->afar = afar;
+		}
+		ret = 1;
+	}
+	__asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
+			     "membar #Sync\n\t"
+			     : : "r" (afsr), "i" (ASI_AFSR));
+
+	return ret;
+}
+
+void cheetah_fecc_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
+{
+	struct cheetah_err_info local_snapshot, *p;
+	int recoverable;
+
+	/* Flush E-cache */
+	cheetah_flush_ecache();
+
+	p = cheetah_get_error_log(afsr);
+	if (!p) {
+		prom_printf("ERROR: Early Fast-ECC error afsr[%016lx] afar[%016lx]\n",
+			    afsr, afar);
+		prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
+			    smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
+		prom_halt();
+	}
+
+	/* Grab snapshot of logged error. */
+	memcpy(&local_snapshot, p, sizeof(local_snapshot));
+
+	/* If the current trap snapshot does not match what the
+	 * trap handler passed along into our args, big trouble.
+	 * In such a case, mark the local copy as invalid.
+	 *
+	 * Else, it matches and we mark the afsr in the non-local
+	 * copy as invalid so we may log new error traps there.
+	 */
+	if (p->afsr != afsr || p->afar != afar)
+		local_snapshot.afsr = CHAFSR_INVALID;
+	else
+		p->afsr = CHAFSR_INVALID;
+
+	cheetah_flush_icache();
+	cheetah_flush_dcache();
+
+	/* Re-enable I-cache/D-cache */
+	__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+			     "or %%g1, %1, %%g1\n\t"
+			     "stxa %%g1, [%%g0] %0\n\t"
+			     "membar #Sync"
+			     : /* no outputs */
+			     : "i" (ASI_DCU_CONTROL_REG),
+			       "i" (DCU_DC | DCU_IC)
+			     : "g1");
+
+	/* Re-enable error reporting */
+	__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+			     "or %%g1, %1, %%g1\n\t"
+			     "stxa %%g1, [%%g0] %0\n\t"
+			     "membar #Sync"
+			     : /* no outputs */
+			     : "i" (ASI_ESTATE_ERROR_EN),
+			       "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
+			     : "g1");
+
+	/* Decide if we can continue after handling this trap and
+	 * logging the error.
+	 */
+	recoverable = 1;
+	if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
+		recoverable = 0;
+
+	/* Re-check AFSR/AFAR.  What we are looking for here is whether a new
+	 * error was logged while we had error reporting traps disabled.
+	 */
+	if (cheetah_recheck_errors(&local_snapshot)) {
+		unsigned long new_afsr = local_snapshot.afsr;
+
+		/* If we got a new asynchronous error, die... */
+		if (new_afsr & (CHAFSR_EMU | CHAFSR_EDU |
+				CHAFSR_WDU | CHAFSR_CPU |
+				CHAFSR_IVU | CHAFSR_UE |
+				CHAFSR_BERR | CHAFSR_TO))
+			recoverable = 0;
+	}
+
+	/* Log errors. */
+	cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
+
+	if (!recoverable)
+		panic("Irrecoverable Fast-ECC error trap.\n");
+
+	/* Flush E-cache to kick the error trap handlers out. */
+	cheetah_flush_ecache();
+}
+
+/* Try to fix a correctable error by pushing the line out from
+ * the E-cache.  Recheck error reporting registers to see if the
+ * problem is intermittent.
+ */
+static int cheetah_fix_ce(unsigned long physaddr)
+{
+	unsigned long orig_estate;
+	unsigned long alias1, alias2;
+	int ret;
+
+	/* Make sure correctable error traps are disabled. */
+	__asm__ __volatile__("ldxa	[%%g0] %2, %0\n\t"
+			     "andn	%0, %1, %%g1\n\t"
+			     "stxa	%%g1, [%%g0] %2\n\t"
+			     "membar	#Sync"
+			     : "=&r" (orig_estate)
+			     : "i" (ESTATE_ERROR_CEEN),
+			       "i" (ASI_ESTATE_ERROR_EN)
+			     : "g1");
+
+	/* We calculate alias addresses that will force the
+	 * cache line in question out of the E-cache.  Then
+	 * we bring it back in with an atomic instruction so
+	 * that we get it in some modified/exclusive state,
+	 * then we displace it again to try and get proper ECC
+	 * pushed back into the system.
+	 */
+	physaddr &= ~(8UL - 1UL);
+	alias1 = (ecache_flush_physbase +
+		  (physaddr & ((ecache_flush_size >> 1) - 1)));
+	alias2 = alias1 + (ecache_flush_size >> 1);
+	__asm__ __volatile__("ldxa	[%0] %3, %%g0\n\t"
+			     "ldxa	[%1] %3, %%g0\n\t"
+			     "casxa	[%2] %3, %%g0, %%g0\n\t"
+			     "ldxa	[%0] %3, %%g0\n\t"
+			     "ldxa	[%1] %3, %%g0\n\t"
+			     "membar	#Sync"
+			     : /* no outputs */
+			     : "r" (alias1), "r" (alias2),
+			       "r" (physaddr), "i" (ASI_PHYS_USE_EC));
+
+	/* Did that trigger another error? */
+	if (cheetah_recheck_errors(NULL)) {
+		/* Try one more time. */
+		__asm__ __volatile__("ldxa [%0] %1, %%g0\n\t"
+				     "membar #Sync"
+				     : : "r" (physaddr), "i" (ASI_PHYS_USE_EC));
+		if (cheetah_recheck_errors(NULL))
+			ret = 2;
+		else
+			ret = 1;
+	} else {
+		/* No new error, intermittent problem. */
+		ret = 0;
+	}
+
+	/* Restore error enables. */
+	__asm__ __volatile__("stxa	%0, [%%g0] %1\n\t"
+			     "membar	#Sync"
+			     : : "r" (orig_estate), "i" (ASI_ESTATE_ERROR_EN));
+
+	return ret;
+}
+
+/* Return non-zero if PADDR is a valid physical memory address. */
+static int cheetah_check_main_memory(unsigned long paddr)
+{
+	int i;
+
+	for (i = 0; ; i++) {
+		if (sp_banks[i].num_bytes == 0)
+			break;
+		if (paddr >= sp_banks[i].base_addr &&
+		    paddr < (sp_banks[i].base_addr + sp_banks[i].num_bytes))
+			return 1;
+	}
+	return 0;
+}
+
+void cheetah_cee_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
+{
+	struct cheetah_err_info local_snapshot, *p;
+	int recoverable, is_memory;
+
+	p = cheetah_get_error_log(afsr);
+	if (!p) {
+		prom_printf("ERROR: Early CEE error afsr[%016lx] afar[%016lx]\n",
+			    afsr, afar);
+		prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
+			    smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
+		prom_halt();
+	}
+
+	/* Grab snapshot of logged error. */
+	memcpy(&local_snapshot, p, sizeof(local_snapshot));
+
+	/* If the current trap snapshot does not match what the
+	 * trap handler passed along into our args, big trouble.
+	 * In such a case, mark the local copy as invalid.
+	 *
+	 * Else, it matches and we mark the afsr in the non-local
+	 * copy as invalid so we may log new error traps there.
+	 */
+	if (p->afsr != afsr || p->afar != afar)
+		local_snapshot.afsr = CHAFSR_INVALID;
+	else
+		p->afsr = CHAFSR_INVALID;
+
+	is_memory = cheetah_check_main_memory(afar);
+
+	if (is_memory && (afsr & CHAFSR_CE) != 0UL) {
+		/* XXX Might want to log the results of this operation
+		 * XXX somewhere... -DaveM
+		 */
+		cheetah_fix_ce(afar);
+	}
+
+	{
+		int flush_all, flush_line;
+
+		flush_all = flush_line = 0;
+		if ((afsr & CHAFSR_EDC) != 0UL) {
+			if ((afsr & CHAFSR_ERRORS) == CHAFSR_EDC)
+				flush_line = 1;
+			else
+				flush_all = 1;
+		} else if ((afsr & CHAFSR_CPC) != 0UL) {
+			if ((afsr & CHAFSR_ERRORS) == CHAFSR_CPC)
+				flush_line = 1;
+			else
+				flush_all = 1;
+		}
+
+		/* Trap handler only disabled I-cache, flush it. */
+		cheetah_flush_icache();
+
+		/* Re-enable I-cache */
+		__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+				     "or %%g1, %1, %%g1\n\t"
+				     "stxa %%g1, [%%g0] %0\n\t"
+				     "membar #Sync"
+				     : /* no outputs */
+				     : "i" (ASI_DCU_CONTROL_REG),
+				     "i" (DCU_IC)
+				     : "g1");
+
+		if (flush_all)
+			cheetah_flush_ecache();
+		else if (flush_line)
+			cheetah_flush_ecache_line(afar);
+	}
+
+	/* Re-enable error reporting */
+	__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+			     "or %%g1, %1, %%g1\n\t"
+			     "stxa %%g1, [%%g0] %0\n\t"
+			     "membar #Sync"
+			     : /* no outputs */
+			     : "i" (ASI_ESTATE_ERROR_EN),
+			       "i" (ESTATE_ERROR_CEEN)
+			     : "g1");
+
+	/* Decide if we can continue after handling this trap and
+	 * logging the error.
+	 */
+	recoverable = 1;
+	if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
+		recoverable = 0;
+
+	/* Re-check AFSR/AFAR */
+	(void) cheetah_recheck_errors(&local_snapshot);
+
+	/* Log errors. */
+	cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
+
+	if (!recoverable)
+		panic("Irrecoverable Correctable-ECC error trap.\n");
+}
+
+void cheetah_deferred_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
+{
+	struct cheetah_err_info local_snapshot, *p;
+	int recoverable, is_memory;
+
+#ifdef CONFIG_PCI
+	/* Check for the special PCI poke sequence. */
+	if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
+		cheetah_flush_icache();
+		cheetah_flush_dcache();
+
+		/* Re-enable I-cache/D-cache */
+		__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+				     "or %%g1, %1, %%g1\n\t"
+				     "stxa %%g1, [%%g0] %0\n\t"
+				     "membar #Sync"
+				     : /* no outputs */
+				     : "i" (ASI_DCU_CONTROL_REG),
+				       "i" (DCU_DC | DCU_IC)
+				     : "g1");
+
+		/* Re-enable error reporting */
+		__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+				     "or %%g1, %1, %%g1\n\t"
+				     "stxa %%g1, [%%g0] %0\n\t"
+				     "membar #Sync"
+				     : /* no outputs */
+				     : "i" (ASI_ESTATE_ERROR_EN),
+				       "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
+				     : "g1");
+
+		(void) cheetah_recheck_errors(NULL);
+
+		pci_poke_faulted = 1;
+		regs->tpc += 4;
+		regs->tnpc = regs->tpc + 4;
+		return;
+	}
+#endif
+
+	p = cheetah_get_error_log(afsr);
+	if (!p) {
+		prom_printf("ERROR: Early deferred error afsr[%016lx] afar[%016lx]\n",
+			    afsr, afar);
+		prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
+			    smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
+		prom_halt();
+	}
+
+	/* Grab snapshot of logged error. */
+	memcpy(&local_snapshot, p, sizeof(local_snapshot));
+
+	/* If the current trap snapshot does not match what the
+	 * trap handler passed along into our args, big trouble.
+	 * In such a case, mark the local copy as invalid.
+	 *
+	 * Else, it matches and we mark the afsr in the non-local
+	 * copy as invalid so we may log new error traps there.
+	 */
+	if (p->afsr != afsr || p->afar != afar)
+		local_snapshot.afsr = CHAFSR_INVALID;
+	else
+		p->afsr = CHAFSR_INVALID;
+
+	is_memory = cheetah_check_main_memory(afar);
+
+	{
+		int flush_all, flush_line;
+
+		flush_all = flush_line = 0;
+		if ((afsr & CHAFSR_EDU) != 0UL) {
+			if ((afsr & CHAFSR_ERRORS) == CHAFSR_EDU)
+				flush_line = 1;
+			else
+				flush_all = 1;
+		} else if ((afsr & CHAFSR_BERR) != 0UL) {
+			if ((afsr & CHAFSR_ERRORS) == CHAFSR_BERR)
+				flush_line = 1;
+			else
+				flush_all = 1;
+		}
+
+		cheetah_flush_icache();
+		cheetah_flush_dcache();
+
+		/* Re-enable I/D caches */
+		__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+				     "or %%g1, %1, %%g1\n\t"
+				     "stxa %%g1, [%%g0] %0\n\t"
+				     "membar #Sync"
+				     : /* no outputs */
+				     : "i" (ASI_DCU_CONTROL_REG),
+				     "i" (DCU_IC | DCU_DC)
+				     : "g1");
+
+		if (flush_all)
+			cheetah_flush_ecache();
+		else if (flush_line)
+			cheetah_flush_ecache_line(afar);
+	}
+
+	/* Re-enable error reporting */
+	__asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+			     "or %%g1, %1, %%g1\n\t"
+			     "stxa %%g1, [%%g0] %0\n\t"
+			     "membar #Sync"
+			     : /* no outputs */
+			     : "i" (ASI_ESTATE_ERROR_EN),
+			     "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
+			     : "g1");
+
+	/* Decide if we can continue after handling this trap and
+	 * logging the error.
+	 */
+	recoverable = 1;
+	if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
+		recoverable = 0;
+
+	/* Re-check AFSR/AFAR.  What we are looking for here is whether a new
+	 * error was logged while we had error reporting traps disabled.
+	 */
+	if (cheetah_recheck_errors(&local_snapshot)) {
+		unsigned long new_afsr = local_snapshot.afsr;
+
+		/* If we got a new asynchronous error, die... */
+		if (new_afsr & (CHAFSR_EMU | CHAFSR_EDU |
+				CHAFSR_WDU | CHAFSR_CPU |
+				CHAFSR_IVU | CHAFSR_UE |
+				CHAFSR_BERR | CHAFSR_TO))
+			recoverable = 0;
+	}
+
+	/* Log errors. */
+	cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
+
+	/* "Recoverable" here means we try to yank the page from ever
+	 * being newly used again.  This depends upon a few things:
+	 * 1) Must be main memory, and AFAR must be valid.
+	 * 2) If we trapped from use, OK.
+	 * 3) Else, if we trapped from kernel we must find exception
+	 *    table entry (ie. we have to have been accessing user
+	 *    space).
+	 *
+	 * If AFAR is not in main memory, or we trapped from kernel
+	 * and cannot find an exception table entry, it is unacceptable
+	 * to try and continue.
+	 */
+	if (recoverable && is_memory) {
+		if ((regs->tstate & TSTATE_PRIV) == 0UL) {
+			/* OK, usermode access. */
+			recoverable = 1;
+		} else {
+			unsigned long g2 = regs->u_regs[UREG_G2];
+			unsigned long fixup = search_exception_table(regs->tpc, &g2);
+
+			if (fixup != 0UL) {
+				/* OK, kernel access to userspace. */
+				recoverable = 1;
+
+			} else {
+				/* BAD, privileged state is corrupted. */
+				recoverable = 0;
+			}
+
+			if (recoverable) {
+				struct page *page = virt_to_page(__va(afar));
+
+				if (VALID_PAGE(page))
+					get_page(page);
+				else
+					recoverable = 0;
+
+				/* Only perform fixup if we still have a
+				 * recoverable condition.
+				 */
+				if (fixup != 0UL && recoverable) {
+					regs->tpc = fixup;
+					regs->tnpc = regs->tpc + 4;
+					regs->u_regs[UREG_G2] = g2;
+				}
+			}
+		}
+	} else {
+		recoverable = 0;
+	}
+
+	if (!recoverable)
+		panic("Irrecoverable deferred error trap.\n");
+}
+
 void do_fpe_common(struct pt_regs *regs)
 {
 	if(regs->tstate & TSTATE_PRIV) {
@@ -575,9 +1332,6 @@
 
 void do_fpieee(struct pt_regs *regs)
 {
-#ifdef DEBUG_FPU
-	printk("fpieee %016lx\n", current->thread.xfsr[0]);
-#endif
 	do_fpe_common(regs);
 }
 
@@ -594,10 +1348,8 @@
 		ret = do_mathemu(regs, f);
 		break;
 	}
-	if (ret) return;
-#ifdef DEBUG_FPU
-	printk("fpother %016lx\n", current->thread.xfsr[0]);
-#endif
+	if (ret)
+		return;
 	do_fpe_common(regs);
 }
 
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/ttable.S linux/arch/sparc64/kernel/ttable.S
--- linux-2.4.4-pre2/arch/sparc64/kernel/ttable.S	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/ttable.S	Mon Dec  3 22:34:50 2001
@@ -1,12 +1,16 @@
-/* $Id: ttable.S,v 1.32 2001/03/23 07:56:30 davem Exp $
- * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions.
+/* $Id: ttable.S,v 1.33 2001/03/28 10:56:34 davem Exp $
+ * ttable.S: Sparc V9 Trap Table(s) with SpitFire/Cheetah extensions.
  *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 2001 David S. Miller (davem@caip.rutgers.edu)
  */
 
 #include <linux/config.h>
 
-	.globl	sparc64_ttable_tl0, sparc64_ttable_tl1,
+	.globl	sparc64_ttable_tl0, sparc64_ttable_tl1
+	.globl	tl0_fecc, tl1_fecc
+	.globl	tl0_cee, tl1_cee
+	.globl	tl0_iae, tl1_iae
+	.globl	tl0_dae, tl1_dae
 
 sparc64_ttable_tl0:
 tl0_resv000:	BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3)
@@ -64,7 +68,8 @@
 #include	"dtlb_base.S"
 tl0_daprot:
 #include	"dtlb_prot.S"
-tl0_resv070:	BTRAP(0x70) BTRAP(0x71) BTRAP(0x72) BTRAP(0x73) BTRAP(0x74) BTRAP(0x75)
+tl0_fecc:	BTRAP(0x70)	/* Fast-ECC on Cheetah */
+tl0_resv071:	BTRAP(0x71) BTRAP(0x72) BTRAP(0x73) BTRAP(0x74) BTRAP(0x75)
 tl0_resv076:	BTRAP(0x76) BTRAP(0x77) BTRAP(0x78) BTRAP(0x79) BTRAP(0x7a) BTRAP(0x7b)
 tl0_resv07c:	BTRAP(0x7c) BTRAP(0x7d) BTRAP(0x7e) BTRAP(0x7f)
 tl0_s0n:	SPILL_0_NORMAL
@@ -203,13 +208,30 @@
 tl1_ivec:	TRAP_IVEC
 tl1_paw:	TRAPTL1(do_paw_tl1)
 tl1_vaw:	TRAPTL1(do_vaw_tl1)
-tl1_cee:	TRAPTL1_CEE
+
+		/* The grotty trick to save %g1 into current->thread.kernel_cntd0
+		 * is because when we take this trap we could be interrupting trap
+		 * code already using the trap alternate global registers.  It is
+		 * better to corrupt a performance counter than corrupt trap register
+		 * state.  We cross our fingers and pray that this store/load does
+		 * not cause yet another CEE trap.
+		 */
+tl1_cee:	membar	#Sync
+		stx	%g1, [%g6 + AOFF_task_thread + AOFF_thread_kernel_cntd0]
+		ldxa	[%g0] ASI_AFSR, %g1
+		membar	#Sync
+		stxa	%g1, [%g0] ASI_AFSR
+		membar	#Sync
+		ldx	[%g6 + AOFF_task_thread + AOFF_thread_kernel_cntd0], %g1
+		retry
+
 tl1_iamiss:	BTRAPTL1(0x64) BTRAPTL1(0x65) BTRAPTL1(0x66) BTRAPTL1(0x67)
 tl1_damiss:
 #include	"dtlb_backend.S"
 tl1_daprot:
 #include	"dtlb_prot.S"
-tl1_resv070:	BTRAPTL1(0x70) BTRAPTL1(0x71) BTRAPTL1(0x72) BTRAPTL1(0x73)
+tl1_fecc:	BTRAP(0x70)	/* Fast-ECC on Cheetah */
+tl1_resc071:	BTRAPTL1(0x71) BTRAPTL1(0x72) BTRAPTL1(0x73)
 tl1_resv074:	BTRAPTL1(0x74) BTRAPTL1(0x75) BTRAPTL1(0x76) BTRAPTL1(0x77)
 tl1_resv078:	BTRAPTL1(0x78) BTRAPTL1(0x79) BTRAPTL1(0x7a) BTRAPTL1(0x7b)
 tl1_resv07c:	BTRAPTL1(0x7c) BTRAPTL1(0x7d) BTRAPTL1(0x7e) BTRAPTL1(0x7f)
diff -urN linux-2.4.4-pre2/arch/sparc64/kernel/unaligned.c linux/arch/sparc64/kernel/unaligned.c
--- linux-2.4.4-pre2/arch/sparc64/kernel/unaligned.c	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/kernel/unaligned.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.21 2001/03/21 11:46:20 davem Exp $
+/* $Id: unaligned.c,v 1.23 2001/04/09 04:29:03 davem Exp $
  * unaligned.c: Unaligned load/store trap handling with special
  *              cases for the kernel to do them more quickly.
  *
@@ -12,6 +12,7 @@
 #include <linux/mm.h>
 #include <asm/asi.h>
 #include <asm/ptrace.h>
+#include <asm/pstate.h>
 #include <asm/processor.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -376,6 +377,9 @@
 	regs->tpc = fixup;
 	regs->tnpc = regs->tpc + 4;
 	regs->u_regs [UREG_G2] = g2;
+
+	regs->tstate &= ~TSTATE_ASI;
+	regs->tstate |= (ASI_AIUS << 24UL);
 }
 
 asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, unsigned long sfar, unsigned long sfsr)
diff -urN linux-2.4.4-pre2/arch/sparc64/lib/blockops.S linux/arch/sparc64/lib/blockops.S
--- linux-2.4.4-pre2/arch/sparc64/lib/blockops.S	Sun Mar 25 18:14:21 2001
+++ linux/arch/sparc64/lib/blockops.S	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: blockops.S,v 1.30 2001/03/22 13:10:10 davem Exp $
+/* $Id: blockops.S,v 1.31 2001/04/05 11:08:12 davem Exp $
  * blockops.S: UltraSparc block zero optimized routines.
  *
  * Copyright (C) 1996, 1998, 1999, 2000 David S. Miller (davem@redhat.com)
@@ -399,7 +399,6 @@
 	jmpl		%o7 + 0x8, %g0
 	 wrpr		%g3, 0x0, %pstate
 
-	/* We will write cheetah optimized versions later. */
 	.globl		cheetah_patch_pgcopyops
 cheetah_patch_pgcopyops:
 	sethi		%hi(FIX_INSN_1), %g1
diff -urN linux-2.4.4-pre2/arch/sparc64/mm/generic.c linux/arch/sparc64/mm/generic.c
--- linux-2.4.4-pre2/arch/sparc64/mm/generic.c	Mon Dec  3 22:34:41 2001
+++ linux/arch/sparc64/mm/generic.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: generic.c,v 1.16 2001/03/25 04:40:05 davem Exp $
+/* $Id: generic.c,v 1.17 2001/04/09 04:08:06 davem Exp $
  * generic.c: Generic Sparc mm routines that are not dependent upon
  *            MMU type but are Sparc specific.
  *
@@ -8,6 +8,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/swap.h>
+#include <linux/pagemap.h>
 
 #include <asm/pgalloc.h>
 #include <asm/pgtable.h>
diff -urN linux-2.4.4-pre2/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- linux-2.4.4-pre2/arch/sparc64/mm/init.c	Mon Mar 26 15:30:06 2001
+++ linux/arch/sparc64/mm/init.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.172 2001/03/24 09:36:01 davem Exp $
+/*  $Id: init.c,v 1.174 2001/03/30 07:10:42 davem Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
@@ -1126,6 +1126,7 @@
 /* paging_init() sets up the page tables */
 
 extern void sun_serial_setup(void);
+extern void cheetah_ecache_flush_init(void);
 
 static unsigned long last_valid_pfn;
 
@@ -1466,6 +1467,9 @@
 	       datapages << (PAGE_SHIFT-10), 
 	       initpages << (PAGE_SHIFT-10), 
 	       PAGE_OFFSET, (last_valid_pfn << PAGE_SHIFT));
+
+	if (tlb_type == cheetah)
+		cheetah_ecache_flush_init();
 }
 
 void free_initmem (void)
diff -urN linux-2.4.4-pre2/arch/sparc64/mm/modutil.c linux/arch/sparc64/mm/modutil.c
--- linux-2.4.4-pre2/arch/sparc64/mm/modutil.c	Sun Feb 18 19:49:54 2001
+++ linux/arch/sparc64/mm/modutil.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/*  $Id: modutil.c,v 1.7 2001/02/13 01:16:44 davem Exp $
+/*  $Id: modutil.c,v 1.8 2001/04/04 00:49:39 davem Exp $
  *  arch/sparc64/mm/modutil.c
  *
  *  Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -59,7 +59,7 @@
 	*p = area;
 
 	if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, GFP_KERNEL, PAGE_KERNEL)) {
-		vfree(addr);
+		module_unmap(addr);
 		return NULL;
 	}
 	return addr;
diff -urN linux-2.4.4-pre2/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-2.4.4-pre2/drivers/block/ll_rw_blk.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/block/ll_rw_blk.c	Mon Dec  3 22:34:50 2001
@@ -231,8 +231,7 @@
  *    If a driver processes several requests at once, it must remove them (or
  *    at least all but one of them) from the request queue.
  *
- *    When a queue is plugged (see blk_queue_pluggable()) the head will be
- *    assumed to be inactive.
+ *    When a queue is plugged the head will be assumed to be inactive.
  **/
  
 void blk_queue_headactive(request_queue_t * q, int active)
@@ -241,35 +240,6 @@
 }
 
 /**
- * blk_queue_pluggable - define a plugging function for a request queue
- * @q:   the request queue to which the function will apply
- * @plug: the function to be called to plug a queue
- *
- * Description:
- *   A request queue will be "plugged" if a request is added to it
- *   while it is empty.  This allows a number of requests to be added
- *   before any are processed, thus providing an opportunity for these
- *   requests to be merged or re-ordered.
- *   The default plugging function (generic_plug_device()) sets the
- *   "plugged" flag for the queue and adds a task to the $tq_disk task
- *   queue to unplug the queue and call the request function at a
- *   later time.
- *
- *   A device driver may provide an alternate plugging function by
- *   passing it to blk_queue_pluggable().  This function should set
- *   the "plugged" flag if it want calls to the request_function to be
- *   blocked, and should place a task on $tq_disk which will unplug
- *   the queue.  Alternately it can simply do nothing and there-by
- *   disable plugging of the device.
- **/
-
-void blk_queue_pluggable (request_queue_t * q, plug_device_fn *plug)
-{
-	q->plug_device_fn = plug;
-}
-
-
-/**
  * blk_queue_make_request - define an alternate make_request function for a device
  * @q:  the request queue for the device to be affected
  * @mfn: the alternate make_request function
@@ -590,7 +560,7 @@
 	list_add(&req->queue, insert_here);
 }
 
-void inline blk_refill_freelist(request_queue_t *q, int rw)
+inline void blk_refill_freelist(request_queue_t *q, int rw)
 {
 	if (q->pending_free[rw]) {
 		list_splice(&q->pending_freelist[rw], &q->request_freelist[rw]);
@@ -602,7 +572,7 @@
 /*
  * Must be called with io_request_lock held and interrupts disabled
  */
-void inline blkdev_release_request(struct request *req)
+inline void blkdev_release_request(struct request *req)
 {
 	request_queue_t *q = req->q;
 	int rw = req->cmd;
@@ -1347,7 +1317,6 @@
 EXPORT_SYMBOL(__blk_get_queue);
 EXPORT_SYMBOL(blk_cleanup_queue);
 EXPORT_SYMBOL(blk_queue_headactive);
-EXPORT_SYMBOL(blk_queue_pluggable);
 EXPORT_SYMBOL(blk_queue_make_request);
 EXPORT_SYMBOL(generic_make_request);
 EXPORT_SYMBOL(blkdev_release_request);
diff -urN linux-2.4.4-pre2/drivers/char/drm/ffb_drv.c linux/drivers/char/drm/ffb_drv.c
--- linux-2.4.4-pre2/drivers/char/drm/ffb_drv.c	Sun Mar 25 18:14:20 2001
+++ linux/drivers/char/drm/ffb_drv.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: ffb_drv.c,v 1.9 2001/03/23 07:58:39 davem Exp $
+/* $Id: ffb_drv.c,v 1.11 2001/03/27 02:36:37 davem Exp $
  * ffb_drv.c: Creator/Creator3D direct rendering driver.
  *
  * Copyright (C) 2000 David S. Miller (davem@redhat.com)
@@ -6,8 +6,10 @@
 
 #include "drmP.h"
 
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
+#include <asm/shmparam.h>
 #include <asm/oplib.h>
 #include <asm/upa.h>
 
@@ -34,6 +36,7 @@
 static int  ffb_unlock(struct inode *inode, struct file *filp,
 		       unsigned int cmd, unsigned long arg);
 static int ffb_mmap(struct file *filp, struct vm_area_struct *vma);
+static unsigned long ffb_get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
 
 /* From ffb_context.c */
 extern int ffb_resctx(struct inode *, struct file *, unsigned int, unsigned long);
@@ -46,15 +49,16 @@
 extern int ffb_context_switch(drm_device_t *, int, int);
 
 static struct file_operations ffb_fops = {
-	owner:		THIS_MODULE,
-	open:		ffb_open,
-	flush:		drm_flush,
-	release:	ffb_release,
-	ioctl:		ffb_ioctl,
-	mmap:		ffb_mmap,
-	read:		drm_read,
-	fasync:		drm_fasync,
-	poll:		drm_poll,
+	owner:			THIS_MODULE,
+	open:			ffb_open,
+	flush:			drm_flush,
+	release:		ffb_release,
+	ioctl:			ffb_ioctl,
+	mmap:			ffb_mmap,
+	read:			drm_read,
+	fasync:			drm_fasync,
+	poll:			drm_poll,
+	get_unmapped_area:	ffb_get_unmapped_area,
 };
 
 /* This is just a template, we make a new copy for each FFB
@@ -770,45 +774,6 @@
 	return 0;
 }
 
-static void align_fb_mapping(struct vm_area_struct *vma)
-{
-	unsigned long j, alignment;
-
-	j = vma->vm_end - vma->vm_start;
-	for (alignment = (4 * 1024 * 1024); alignment > PAGE_SIZE; alignment >>= 3)
-		if (j >= alignment)
-			break;
-	if (alignment > PAGE_SIZE) {
-		j = alignment;
-		alignment = j - (vma->vm_start & (j - 1));
-		if (alignment != j) {
-			struct vm_area_struct *vmm = find_vma(current->mm,vma->vm_start);
-
-			if (!vmm || vmm->vm_start >= vma->vm_end + alignment) {
-				vma->vm_start += alignment;
-				vma->vm_end += alignment;
-			}
-		}
-	}
-}
-
-/* The problem here is, due to virtual cache aliasing,
- * we must make sure the shared memory area lands in the
- * same dcache line for both the kernel and all drm clients.
- */
-static void align_shm_mapping(struct vm_area_struct *vma, unsigned long kvirt)
-{
-	kvirt &= PAGE_SIZE;
-	if ((vma->vm_start & PAGE_SIZE) != kvirt) {
-		struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
-
-		if (!vmm || vmm->vm_start >= vma->vm_end + PAGE_SIZE) {
-			vma->vm_start += PAGE_SIZE;
-			vma->vm_end += PAGE_SIZE;
-		}
-	}
-}
-
 extern struct vm_operations_struct drm_vm_ops;
 extern struct vm_operations_struct drm_vm_shm_ops;
 extern struct vm_operations_struct drm_vm_shm_lock_ops;
@@ -868,7 +833,6 @@
 
 	switch (map->type) {
 	case _DRM_FRAME_BUFFER:
-		align_fb_mapping(vma);
 		/* FALLTHROUGH */
 
 	case _DRM_REGISTERS:
@@ -887,7 +851,6 @@
 		vma->vm_ops = &drm_vm_ops;
 		break;
 	case _DRM_SHM:
-		align_shm_mapping(vma, (unsigned long)dev->lock.hw_lock);
 		if (map->flags & _DRM_CONTAINS_LOCK)
 			vma->vm_ops = &drm_vm_shm_lock_ops;
 		else {
@@ -909,6 +872,69 @@
 	vma->vm_file = filp; /* Needed for drm_vm_open() */
 	drm_vm_open(vma);
 	return 0;
+}
+
+static drm_map_t *ffb_find_map(struct file *filp, unsigned long off)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev;
+	drm_map_t	*map;
+	int		i;
+
+	if (!priv || (dev = priv->dev) == NULL)
+		return NULL;
+
+	for (i = 0; i < dev->map_count; i++) {
+		unsigned long uoff;
+
+		map = dev->maplist[i];
+
+		/* Ok, a little hack to make 32-bit apps work. */
+		uoff = (map->offset & 0xffffffff);
+		if (uoff == off)
+			return map;
+	}
+	return NULL;
+}
+
+static unsigned long ffb_get_unmapped_area(struct file *filp, unsigned long hint, unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	drm_map_t *map = ffb_find_map(filp, pgoff << PAGE_SHIFT);
+	unsigned long addr = -ENOMEM;
+
+	if (!map)
+		return get_unmapped_area(NULL, hint, len, pgoff, flags);
+
+	if (map->type == _DRM_FRAME_BUFFER ||
+	    map->type == _DRM_REGISTERS) {
+#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
+		addr = get_fb_unmapped_area(filp, hint, len, pgoff, flags);
+#else
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+#endif
+	} else if (map->type == _DRM_SHM && SHMLBA > PAGE_SIZE) {
+		unsigned long slack = SHMLBA - PAGE_SIZE;
+
+		addr = get_unmapped_area(NULL, hint, len + slack, pgoff, flags);
+		if (!(addr & ~PAGE_MASK)) {
+			unsigned long kvirt = (unsigned long) map->handle;
+
+			if ((kvirt & (SHMLBA - 1)) != (addr & (SHMLBA - 1))) {
+				unsigned long koff, aoff;
+
+				koff = kvirt & (SHMLBA - 1);
+				aoff = addr & (SHMLBA - 1);
+				if (koff < aoff)
+					koff += SHMLBA;
+
+				addr += (koff - aoff);
+			}
+		}
+	} else {
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+	}
+
+	return addr;
 }
 
 module_init(ffb_init);
diff -urN linux-2.4.4-pre2/drivers/char/hp600_keyb.c linux/drivers/char/hp600_keyb.c
--- linux-2.4.4-pre2/drivers/char/hp600_keyb.c	Fri Sep 22 14:21:17 2000
+++ linux/drivers/char/hp600_keyb.c	Mon Dec  3 22:34:50 2001
@@ -1,13 +1,17 @@
 /*
- *	$Id: hp600_keyb.c,v 1.1 2000/06/10 21:45:30 yaegashi Exp $
+ *	$Id$ 
  *	Copyright (C) 2000 YAEGASHI Takeshi
- *	HP600 keyboard scan routine and translate table
+ *	HP600 keyboard scan routine and translation table
+ *	Copyright (C) 2000 Niibe Yutaka
+ *	HP620 keyboard translation table
  */
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/init.h>
-#include <linux/delay.h>
+
+#include <asm/machvec.h>
+#include <asm/delay.h>
 #include <asm/io.h>
 #include "scan_keyb.h"
 
@@ -17,55 +21,119 @@
 #define PFDR 0xa400012a
 #define PGDR 0xa400012c
 #define PHDR 0xa400012e
+#define PJDR 0xa4000130
+#define PKDR 0xa4000132
+#define PLDR 0xa4000134
+
+static const unsigned char hp620_japanese_table[] = {
+	/* PTD1 */
+	0x0a, 0x0b, 0x0c, 0x00, 0x00, 0x0e, 0x00, 0x00,
+	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+	/* PTD5 */
+	0x18, 0x19, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	/* PTD7 */
+	0x26, 0x1a, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+	/* PTE0 */
+	0x27, 0x1b, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x2a, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	/* PTE1 */
+	0x35, 0x28, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0x3a, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x34,
+	/* PTE3 */
+	0x48, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x2d, 0x2e, 0x7b, 0x30, 0x31, 0x32, 0x33,
+	/* PTE6 */
+	0x4b, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x2c, 0x38, 0x00, 0x39, 0x79, 0x7d, 0x73,
+	/* PTE7 */
+	0x41, 0x42, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+	/* **** */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
 
-static const unsigned char hp690_japanese_table[]={
-	0x00, 0x00, 0x00, 0x01, 0x00, 0x29, 0x70, 0x3a,
-	0x3f, 0x3e, 0x40, 0x41, 0x42, 0x3d, 0x3c, 0x3b,
-
-	0x00, 0x00, 0x00, 0x2c, 0x00, 0x1c, 0x28, 0x35,
-	0x31, 0x30, 0x32, 0x33, 0x34, 0x2f, 0x2e, 0x2d,
-
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x50,
-	0x7b, 0x38, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00,
 
+static const unsigned char hp680_japanese_table[] = {
+	/* PTD1 */
+	0x3a, 0x70, 0x29, 0x00, 0x01, 0x00, 0x00, 0x00,
+	0x3b, 0x3c, 0x3d, 0x42, 0x41, 0x40, 0x3e, 0x3f,
+	/* PTD5 */
+	0x35, 0x28, 0x1c, 0x00, 0x2c, 0x00, 0x00, 0x00,
+	0x2d, 0x2e, 0x2f, 0x34, 0x33, 0x32, 0x30, 0x31,
+	/* PTD7 */
+	0x50, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x38, 0x7b,
+	/* PTE0 */
+	0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00,
+	0x1d, 0x00, 0x39, 0x53, 0x73, 0xf9, 0x00, 0x00,
+	/* PTE1 */
+	0x27, 0x1b, 0x2b, 0x00, 0x1e, 0x00, 0x00, 0x00,
+	0x1f, 0x20, 0x21, 0x26, 0x25, 0x24, 0x22, 0x23,
+	/* PTE3 */
+	0x48, 0x7d, 0x36, 0x00, 0x0f, 0x00, 0x00, 0x00,
+	0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	/* PTE6 */
+	0x19, 0x1a, 0x0e, 0x00, 0x10, 0x00, 0x00, 0x00,
+	0x11, 0x12, 0x13, 0x18, 0x17, 0x16, 0x14, 0x15,
+	/* PTE7 */
+	0x0b, 0x0c, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00,
+	0x03, 0x04, 0x05, 0x0a, 0x09, 0x08, 0x06, 0x07,
+	/* **** */
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0xf9, 0x73, 0x53, 0x39, 0x00, 0x1d,
-
-	0x00, 0x00, 0x00, 0x1e, 0x00, 0x2b, 0x1b, 0x27,
-	0x23, 0x22, 0x24, 0x25, 0x26, 0x21, 0x20, 0x1f,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
 
-	0x00, 0x00, 0x00, 0x0f, 0x00, 0x36, 0x7d, 0x48,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00,
 
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x1a, 0x19,
-	0x15, 0x14, 0x16, 0x17, 0x18, 0x13, 0x12, 0x11,
+static int hp620_japanese_scan_kbd(unsigned char *s)
+{
+	int i;
+	unsigned char matrix_switch[] = {
+		0xfd, 0xff,	/* PTD1 */
+		0xdf, 0xff,	/* PTD5 */
+		0x7f, 0xff,	/* PTD7 */
+		0xff, 0xfe,	/* PTE0 */
+		0xff, 0xfd,	/* PTE1 */
+		0xff, 0xf7,	/* PTE3 */
+		0xff, 0xbf,	/* PTE6 */
+		0xff, 0x7f,	/* PTE7 */
+	}, *t=matrix_switch;
 
-	0x00, 0x00, 0x00, 0x02, 0x00, 0x0d, 0x0c, 0x0b,
-	0x07, 0x06, 0x08, 0x09, 0x0a, 0x05, 0x04, 0x03,
+	for(i=0; i<8; i++) {
+		ctrl_outb(*t++, PDDR);
+		ctrl_outb(*t++, PEDR);
+		udelay(50);
+		*s++=ctrl_inb(PCDR);
+		*s++=ctrl_inb(PFDR);
+	}
 
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
+	ctrl_outb(0xff, PDDR);
+	ctrl_outb(0xff, PEDR);
 
+	*s++=ctrl_inb(PGDR);
+	*s++=ctrl_inb(PHDR);
 
-static const unsigned char hp690_switch[]= {
-	0xfd, 0xff,
-	0xdf, 0xff,
-	0x7f, 0xff,
-	0xff, 0xfe,
-	0xff, 0xfd,
-	0xff, 0xf7,
-	0xff, 0xbf,
-	0xff, 0x7f,
-};
+	return 0;
+}
 
 
-static void hp690_japanese_scan_kbd(unsigned char *s)
+static int hp680_japanese_scan_kbd(unsigned char *s)
 {
 	int i;
-	unsigned const char *t=hp690_switch;
+	unsigned char matrix_switch[] = {
+		0xfd, 0xff,	/* PTD1 */
+		0xdf, 0xff,	/* PTD5 */
+		0x7f, 0xff,	/* PTD7 */
+		0xff, 0xfe,	/* PTE0 */
+		0xff, 0xfd,	/* PTE1 */
+		0xff, 0xf7,	/* PTE3 */
+		0xff, 0xbf,	/* PTE6 */
+		0xff, 0x7f,	/* PTE7 */
+	}, *t=matrix_switch;
 
-	for(i=0; i<9; i++) {
+	for(i=0; i<8; i++) {
 		ctrl_outb(*t++, PDDR);
 		ctrl_outb(*t++, PEDR);
 		*s++=ctrl_inb(PCDR);
@@ -77,18 +145,25 @@
 
 	*s++=ctrl_inb(PGDR);
 	*s++=ctrl_inb(PHDR);
+
+	return 0;
 }
 
 
 void __init hp600_kbd_init_hw(void)
 {
 	scan_kbd_init();
-	register_scan_keyboard(hp690_japanese_scan_kbd,
-			       hp690_japanese_table, 18);
+
+	if (MACH_HP620)
+		register_scan_keyboard(hp620_japanese_scan_kbd,
+				       hp620_japanese_table, 18);
+	else if (MACH_HP680 || MACH_HP690) 
+		register_scan_keyboard(hp680_japanese_scan_kbd,
+				       hp680_japanese_table, 18);
+
 	printk(KERN_INFO "HP600 matrix scan keyboard registered\n");
 }
 
-
 /****************************************************************
 HP Jornada 690(Japanese version) keyboard scan matrix
 
@@ -102,7 +177,6 @@
 PTE6	REC			Q	on/off	BS	@	P	
 PTE7	REC			1	on/off	^	-	0	
 
-
 	PTF7	PTF6	PTF5	PTF4	PTF3	PTF2	PTF1	PTF0
 PTD1	F5	F4	F6	F7	F8	F3	F2	F1	
 PTD5	N	B	M	,	.	V	C	X	
@@ -129,3 +203,136 @@
 L: 0x0c3c 0x26	F	F	IP	F	F	IP	IP	F
 
 ****************************************************************/
+
+/****************************************************************
+HP Jornada 620(Japanese version) keyboard scan matrix
+
+	PTC7	PTC6	PTC5	PTC4	PTC3	PTC2	PTC1	PTC0
+PTD1	EREC		BS	Ctrl	on/off	-	0	9
+PTD5	EREC		BS	Ctrl	on/off	^	P	O
+PTD7	EREC		BS	Ctrl	on/off	]	@	L
+PTE0	EREC		BS	Ctrl	on/off	Han/Zen [	;
+PTE1	EREC		BS	Ctrl	on/off	Enter	:	/
+PTE3	EREC		BS	Ctrl	on/off		Right	Up
+PTE6	EREC		BS	Ctrl	on/off		Down	Left
+PTE7	EREC		BS	Ctrl	on/off		F8	F7
+
+	PTF7	PTF6	PTF5	PTF4	PTF3	PTF2	PTF1	PTF0
+PTD1	8	7	6	5	4	3	2	1
+PTD5	I	U	Y	T	R	E	W	Q
+PTD7	K	J	H	G	F	D	S	A
+PTE0						ESC	Tab	Shift
+PTE1	.			V			Caps	Hira
+PTE3	,	M	N	B	Muhen	C	X
+PTE6	_	\	Henkan	Space		Alt	Z
+PTE7	F6	F5	F4	F3	F2	F1		REC
+
+	PTH0
+*	on/off
+
+		7	6	5	4	3	2	1	0
+C: 0xffff 0xff	IP	IP	IP	IP	IP	IP	IP	IP
+D: 0x4404 0xaf	O	F	O	F	F	F	O	F
+E: 0x5045 0xff	O	O	F	F	O	F	O	O
+F: 0xffff 0xff	IP	IP	IP	IP	IP	IP	IP	IP
+G: 0xd5ff 0x00	IP	O	O	O	IP	IP	IP	IP
+H: 0x63ff 0xd1	O	I	F	IP	IP	IP	IP	IP
+J: 0x0004 0x02	F	F	F	F	F	F	O	F
+K: 0x0401 0xff	F	F	O	F	F	F	F	O
+L: 0x0c00 0x20	F	F	IP	F	F	F	F	F
+
+ADCSR: 0x08
+ADCR: 0x3f
+
+ ****************************************************************/
+
+/****************************************************************
+Japanese 109 keyboard scan code layout
+
+                                              E02A-     E1- 
+01    3B 3C 3D 3E  3F 40 41 42  43 44 57 58   E037  46  1045
+                                                                   
+29 02 03 04 05 06 07 08 09 0A 0B 0C 0D 7D 0E  E052 E047 E049   45 E035 37  4A
+0F  10 11 12 13 14 15 16 17 18 19 1A 1B   1C  E053 E04F E051   47  48  49  4E
+3A   1E 1F 20 21 22 23 24 25 26 27 28 2B                       4B  4C  4D
+2A    2C 2D 2E 2F 30 31 32 33 34 35 73    36       E048        4F  50  51  E0-
+1D  DB  38  7B   39   79 70  E038 DC DD E01D  E04B E050 E04D     52    53  1C
+
+****************************************************************/
+
+#if 0
+int __init hp620_keyboard_test(void)
+{
+	int i;
+	unsigned char s[18];
+	unsigned long a, b, c, d;
+
+	printk("PCCR: %04lx, PCDR: %02lx\n",
+	       ctrl_inw(0xa4000104), ctrl_inb(0xa4000124));
+	printk("PDCR: %04lx, PDDR: %02lx\n",
+	       ctrl_inw(0xa4000106), ctrl_inb(0xa4000126));
+	printk("PECR: %04lx, PEDR: %02lx\n",
+	       ctrl_inw(0xa4000108), ctrl_inb(0xa4000128));
+	printk("PFCR: %04lx, PFDR: %02lx\n",
+	       ctrl_inw(0xa400010a), ctrl_inb(0xa400012a));
+	printk("PGCR: %04lx, PGDR: %02lx\n",
+	       ctrl_inw(0xa400010c), ctrl_inb(0xa400012c));
+	printk("PHCR: %04lx, PHDR: %02lx\n",
+	       ctrl_inw(0xa400010e), ctrl_inb(0xa400012e));
+	printk("PJCR: %04lx, PJDR: %02lx\n",
+	       ctrl_inw(0xa4000110), ctrl_inb(0xa4000130));
+	printk("PKCR: %04lx, PKDR: %02lx\n",
+	       ctrl_inw(0xa4000112), ctrl_inb(0xa4000132));
+	printk("PLCR: %04lx, PLDR: %02lx\n",
+	       ctrl_inw(0xa4000114), ctrl_inb(0xa4000134));
+
+	printk("ADCSR: %02lx, ADCR: %02lx\n",
+	       ctrl_inb(0xa4000090), ctrl_inb(0xa4000092));
+
+	ctrl_inb(0xa4000004);
+	ctrl_inb(0xa4000006);
+	ctrl_inb(0xa4000008);
+	ctrl_outb(0, 0xa4000004);
+	ctrl_outb(0, 0xa4000006);
+	ctrl_outb(0, 0xa4000008);
+	ctrl_outb(0, 0xa4000090);
+	ctrl_outb(0x3b, 0xa4000090);
+
+	while(1) {
+		hp620_japanese_scan_kbd(s);
+		for(i=0; i<18; i+=2)
+			printk("%02x%02x ", s[i], s[i+1]);
+
+#if 0
+		ctrl_outb(~2, PJDR);
+		printk("%02lx%02lx ", ctrl_inb(PCDR), ctrl_inb(PFDR));
+		ctrl_outb(0xff, PJDR);
+		ctrl_outb(~1, PKDR);
+		printk("%02lx%02lx ", ctrl_inb(PCDR), ctrl_inb(PFDR));
+		ctrl_outb(~32, PKDR);
+		printk("%02lx%02lx ", ctrl_inb(PCDR), ctrl_inb(PFDR));
+		ctrl_outb(0xff, PKDR);
+#endif
+
+		printk("%02lx%02lx%02lx%02lx ", a, b, c, d);
+		if(ctrl_inb(0xa4000090)&0x80) {
+			a=ctrl_inb(0xa4000080);
+			b=ctrl_inb(0xa4000084);
+			c=ctrl_inb(0xa4000088);
+			d=ctrl_inb(0xa400008c);
+			ctrl_outb(0x3b, 0xa4000090);
+		}
+		printk("%02lx%02lx%02lx ",
+		       ctrl_inb(0xa4000004),
+		       ctrl_inb(0xa4000006),
+		       ctrl_inb(0xa4000008));
+
+		printk("\n");
+	}
+
+	return 0;
+}
+module_init(keyboard_probe);
+#endif
+
+
diff -urN linux-2.4.4-pre2/drivers/char/i810_rng.c linux/drivers/char/i810_rng.c
--- linux-2.4.4-pre2/drivers/char/i810_rng.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/char/i810_rng.c	Mon Dec  3 22:34:50 2001
@@ -35,7 +35,7 @@
 /*
  * core module and version information
  */
-#define RNG_VERSION "0.9.5"
+#define RNG_VERSION "0.9.6"
 #define RNG_MODULE_NAME "i810_rng"
 #define RNG_DRIVER_NAME   RNG_MODULE_NAME " hardware driver " RNG_VERSION
 #define PFX RNG_MODULE_NAME ": "
@@ -81,26 +81,13 @@
 #define RNG_ADDR			0xFFBC015F
 #define RNG_ADDR_LEN			3
 
-#define RNG_MAX_ENTROPY			8 /* max entropy h/w is capable of */
-
 #define RNG_MISCDEV_MINOR		183 /* official */
 
-
-/*
- * number of bytes required for a FIPS test.
- * do not alter unless you really, I mean
- * REALLY know what you are doing.
- */
-#define RNG_FIPS_TEST_THRESHOLD	2500
-
-
 /*
  * various RNG status variables.  they are globals
  * as we only support a single RNG device
  */
-static int rng_hw_enabled;		/* is the RNG h/w enabled? */
 static void *rng_mem;			/* token to our ioremap'd RNG register area */
-static struct pci_dev *rng_pdev;	/* Firmware Hub PCI device found during PCI probe */
 static struct semaphore rng_open_sem;	/* Semaphore for serializing rng_open/release */
 
 
@@ -113,18 +100,17 @@
 	return readb (rng_mem + RNG_HW_STATUS);
 }
 
-
-static inline void rng_hwstatus_set (u8 hw_status)
+static inline u8 rng_hwstatus_set (u8 hw_status)
 {
 	assert (rng_mem != NULL);
 	writeb (hw_status, rng_mem + RNG_HW_STATUS);
+	return rng_hwstatus ();
 }
 
 
 static inline int rng_data_present (void)
 {
 	assert (rng_mem != NULL);
-	assert (rng_hw_enabled > 0);
 
 	return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0;
 }
@@ -133,65 +119,67 @@
 static inline int rng_data_read (void)
 {
 	assert (rng_mem != NULL);
-	assert (rng_hw_enabled > 0);
 
 	return readb (rng_mem + RNG_DATA);
 }
 
-
 /*
- * rng_enable - enable or disable the RNG hardware
+ * rng_enable - enable the RNG hardware
  */
-static int rng_enable (int enable)
+
+static int rng_enable (void)
 {
-	int rc = 0, action = 0;
+	int rc = 0;
 	u8 hw_status, new_status;
 
 	DPRINTK ("ENTER\n");
 
 	hw_status = rng_hwstatus ();
 
-	if (enable) {
-		rng_hw_enabled++;
-		MOD_INC_USE_COUNT;
-	} else {
-		if (rng_hw_enabled) {
-			rng_hw_enabled--;
-			MOD_DEC_USE_COUNT;
+	if ((hw_status & RNG_ENABLED) == 0) {
+		new_status = rng_hwstatus_set (hw_status | RNG_ENABLED);
+
+		if (new_status & RNG_ENABLED)
+			printk (KERN_INFO PFX "RNG h/w enabled\n");
+		else {
+			printk (KERN_ERR PFX "Unable to enable the RNG\n");
+			rc = -EIO;
 		}
 	}
 
-	if (rng_hw_enabled && ((hw_status & RNG_ENABLED) == 0)) {
-		rng_hwstatus_set (hw_status | RNG_ENABLED);
-		action = 1;
-	}
+	DPRINTK ("EXIT, returning %d\n", rc);
+	return rc;
+}
 
-	else if (!rng_hw_enabled && (hw_status & RNG_ENABLED)) {
-		rng_hwstatus_set (hw_status & ~RNG_ENABLED);
-		action = 2;
-	}
+/*
+ * rng_disable - disable the RNG hardware
+ */
 
-	new_status = rng_hwstatus ();
+static void rng_disable(void)
+{
+	u8 hw_status, new_status;
 
-	if (action == 1) {
-		if (new_status & RNG_ENABLED)
-			printk (KERN_INFO PFX "RNG h/w enabled\n");
-		else
-			printk (KERN_ERR PFX "Unable to enable the RNG\n");
-	} else if (action == 2) {
+	DPRINTK ("ENTER\n");
+
+	hw_status = rng_hwstatus ();
+
+	if (hw_status & RNG_ENABLED) {
+		new_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
+	
 		if ((new_status & RNG_ENABLED) == 0)
 			printk (KERN_INFO PFX "RNG h/w disabled\n");
-		else
+		else {
 			printk (KERN_ERR PFX "Unable to disable the RNG\n");
+		}
 	}
 
-	DPRINTK ("EXIT, returning %d\n", rc);
-	return rc;
+	DPRINTK ("EXIT\n");
 }
 
-
 static int rng_dev_open (struct inode *inode, struct file *filp)
 {
+	int rc;
+
 	if ((filp->f_mode & FMODE_READ) == 0)
 		return -EINVAL;
 	if (filp->f_mode & FMODE_WRITE)
@@ -206,9 +194,10 @@
 			return -ERESTARTSYS;
 	}
 
-	if (rng_enable (1)) {
+	rc = rng_enable ();
+	if (rc) {
 		up (&rng_open_sem);
-		return -EIO;
+		return rc;
 	}
 
 	return 0;
@@ -217,7 +206,7 @@
 
 static int rng_dev_release (struct inode *inode, struct file *filp)
 {
-	rng_enable(0);
+	rng_disable ();
 	up (&rng_open_sem);
 	return 0;
 }
@@ -315,8 +304,9 @@
 	}
 
 	/* turn RNG h/w off, if it's on */
-	rc = rng_enable (0);
-	if (rc) {
+	if (hw_status & RNG_ENABLED)
+		hw_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
+	if (hw_status & RNG_ENABLED) {
 		printk (KERN_ERR PFX "cannot disable RNG, aborting\n");
 		goto err_out_free_map;
 	}
@@ -381,8 +371,6 @@
 
 	printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
 
-	rng_pdev = pdev;
-
 	DPRINTK ("EXIT, returning 0\n");
 	return 0;
 }
@@ -395,13 +383,9 @@
 {
 	DPRINTK ("ENTER\n");
 
-	assert (rng_hw_enabled == 0);
-
 	misc_deregister (&rng_miscdev);
 
 	iounmap (rng_mem);
-
-	rng_pdev = NULL;
 
 	DPRINTK ("EXIT\n");
 }
diff -urN linux-2.4.4-pre2/drivers/char/ip2.c linux/drivers/char/ip2.c
--- linux-2.4.4-pre2/drivers/char/ip2.c	Fri Feb 16 16:02:35 2001
+++ linux/drivers/char/ip2.c	Mon Dec  3 22:34:50 2001
@@ -6,7 +6,6 @@
 //  __initdata should work as advertized
 //
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/init.h>
diff -urN linux-2.4.4-pre2/drivers/char/machzwd.c linux/drivers/char/machzwd.c
--- linux-2.4.4-pre2/drivers/char/machzwd.c	Tue Mar  6 19:44:34 2001
+++ linux/drivers/char/machzwd.c	Mon Dec  3 22:34:50 2001
@@ -26,6 +26,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/types.h>
diff -urN linux-2.4.4-pre2/drivers/char/scan_keyb.c linux/drivers/char/scan_keyb.c
--- linux-2.4.4-pre2/drivers/char/scan_keyb.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/char/scan_keyb.c	Mon Dec  3 22:34:50 2001
@@ -18,33 +18,40 @@
 #include <linux/miscdevice.h>
 #include <linux/slab.h>
 #include <linux/kbd_kern.h>
+#include <linux/timer.h>
+
+#define SCANHZ	(HZ/20)
 
 struct scan_keyboard {
 	struct scan_keyboard *next;
-	void (*scan)(unsigned char *buffer);
+	int (*scan)(unsigned char *buffer);
 	const unsigned char *table;
 	unsigned char *s0, *s1;
 	int length;
 };
-		     
+
+static int scan_jiffies=0;
 static struct scan_keyboard *keyboards=NULL;
-static struct tq_struct task_scan_kbd;	
+struct timer_list scan_timer;
 
 static void check_kbd(const unsigned char *table,
 		      unsigned char *new, unsigned char *old, int length)
 {
 	int need_tasklet_schedule=0;
-	unsigned char xor, bit;
+	unsigned int xor, bit;
 	
 	while(length-->0) {
 		if((xor=*new^*old)==0) {
 			table+=8;
 		}
 		else {
-			for(bit=0x80; bit!=0; bit>>=1) {
+			for(bit=0x01; bit<0x100; bit<<=1) {
 				if(xor&bit) {
 					handle_scancode(*table, !(*new&bit));
 					need_tasklet_schedule=1;
+#if 0
+					printk("0x%x %s\n", *table, (*new&bit)?"released":"pressed");
+#endif
 				}
 				table++;
 			}
@@ -57,26 +64,39 @@
 }
 
 
-static void scan_kbd(void *dummy)
+static void scan_kbd(unsigned long dummy)
 {
 	struct scan_keyboard *kbd;
 
+	scan_jiffies++;
+
 	for(kbd=keyboards; kbd!=NULL; kbd=kbd->next) {
-		if(jiffies&1) {
-			kbd->scan(kbd->s0);
-			check_kbd(kbd->table, kbd->s0, kbd->s1, kbd->length);
+		if(scan_jiffies&1) {
+			if(!kbd->scan(kbd->s0))
+				check_kbd(kbd->table,
+					  kbd->s0, kbd->s1, kbd->length);
+			else
+				memcpy(kbd->s0, kbd->s1, kbd->length);
 		}
 		else {
-			kbd->scan(kbd->s1);
-			check_kbd(kbd->table, kbd->s1, kbd->s0, kbd->length);
+			if(!kbd->scan(kbd->s1))
+				check_kbd(kbd->table,
+					  kbd->s1, kbd->s0, kbd->length);
+			else
+				memcpy(kbd->s1, kbd->s0, kbd->length);
 		}
 		
 	}
-	queue_task(&task_scan_kbd, &tq_timer);
+
+	init_timer(&scan_timer);
+	scan_timer.expires = jiffies + SCANHZ;
+	scan_timer.data = 0;
+	scan_timer.function = scan_kbd;
+	add_timer(&scan_timer);
 }
 
 
-int register_scan_keyboard(void (*scan)(unsigned char *buffer),
+int register_scan_keyboard(int (*scan)(unsigned char *buffer),
 			   const unsigned char *table,
 			   int length)
 {
@@ -98,8 +118,8 @@
 	if (kbd->s1 == NULL)
 		goto error_free_s0;
 
-	kbd->scan(kbd->s0);
-	kbd->scan(kbd->s1);
+	memset(kbd->s0, -1, kbd->length);
+	memset(kbd->s1, -1, kbd->length);
 	
 	kbd->next=keyboards;
 	keyboards=kbd;
@@ -119,11 +139,11 @@
 			      
 void __init scan_kbd_init(void)
 {
+	init_timer(&scan_timer);
+	scan_timer.expires = jiffies + SCANHZ;
+	scan_timer.data = 0;
+	scan_timer.function = scan_kbd;
+	add_timer(&scan_timer);
 
-	INIT_LIST_HEAD(task_scan_kbd.list);
-	task_scan_kbd.sync=0;
-	task_scan_kbd.routine=scan_kbd;
-	task_scan_kbd.data=NULL;
-	queue_task(&task_scan_kbd, &tq_timer);
 	printk(KERN_INFO "Generic scan keyboard driver initialized\n");
 }
diff -urN linux-2.4.4-pre2/drivers/char/scan_keyb.h linux/drivers/char/scan_keyb.h
--- linux-2.4.4-pre2/drivers/char/scan_keyb.h	Fri Jul 21 14:21:06 2000
+++ linux/drivers/char/scan_keyb.h	Mon Dec  3 22:34:50 2001
@@ -6,7 +6,7 @@
  *	Generic scan keyboard driver
  */
 
-int register_scan_keyboard(void (*scan)(unsigned char *buffer),
+int register_scan_keyboard(int (*scan)(unsigned char *buffer),
 			   const unsigned char *table,
 			   int length);
 
diff -urN linux-2.4.4-pre2/drivers/char/sh-sci.c linux/drivers/char/sh-sci.c
--- linux-2.4.4-pre2/drivers/char/sh-sci.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/char/sh-sci.c	Mon Dec  3 22:34:50 2001
@@ -74,6 +74,8 @@
 static void sci_hungup(void *ptr);
 static void sci_close(void *ptr);
 static int sci_chars_in_buffer(void *ptr);
+static int sci_request_irq(struct sci_port *port);
+static void sci_free_irq(struct sci_port *port);
 static int sci_init_drivers(void);
 
 static struct tty_driver sci_driver, sci_callout_driver;
@@ -83,8 +85,8 @@
 static struct termios *sci_termios[SCI_NPORTS];
 static struct termios *sci_termios_locked[SCI_NPORTS];
 
-int sci_refcount;
-int sci_debug = 0;
+static int sci_refcount;
+static int sci_debug = 0;
 
 #ifdef MODULE
 MODULE_PARM(sci_debug, "i");
@@ -102,7 +104,7 @@
 	do
 		status = sci_in(port, SCxSR);
 	while (!(status & SCxSR_TDxE(port)));
-  
+	
 	sci_out(port, SCxTDR, c);
 	sci_in(port, SCxSR);            /* Dummy read */
 	sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
@@ -626,10 +628,10 @@
 		dprintk("sci: BREAK detected\n");
 	}
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7750)
+#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_ST40STB1)
 	/* XXX: Handle SCIF overrun error */
-	if (port->type == PORT_SCIF && (ctrl_inw(SCLSR2) & SCIF_ORER) != 0) {
-		ctrl_outw(0, SCLSR2);
+	if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) {
+		sci_out(port, SCLSR, 0);
 		if(tty->flip.count<TTY_FLIPBUF_SIZE) {
 			copied++;
 			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
@@ -796,6 +798,7 @@
 	port->gs.flags &= ~ GS_ACTIVE;
 	if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL)
 		sci_setsignals(port, 0, 0);
+	sci_free_irq(port);
 }
 
 /* ********************************************************************** *
@@ -828,8 +831,7 @@
 	 */
 	retval = gs_init_port(&port->gs);
 	if (retval) {
-		port->gs.count--;
-		return retval;
+		goto failed_1;
 	}
 
 	port->gs.flags |= GS_ACTIVE;
@@ -837,14 +839,17 @@
 
 	if (port->gs.count == 1) {
 		MOD_INC_USE_COUNT;
+
+		retval = sci_request_irq(port);
+		if (retval) {
+			goto failed_2;
+		}
 	}
 
 	retval = gs_block_til_ready(port, filp);
 
 	if (retval) {
-		MOD_DEC_USE_COUNT;
-		port->gs.count--;
-		return retval;
+		goto failed_3;
 	}
 
 	if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)) {
@@ -870,6 +875,14 @@
 	port->gs.pgrp = current->pgrp;
 
 	return 0;
+
+failed_3:
+	sci_free_irq(port);
+failed_2:
+	MOD_DEC_USE_COUNT;
+failed_1:
+	port->gs.count--;
+	return retval;
 }
 
 static void sci_hungup(void *ptr)
@@ -1094,29 +1107,46 @@
 	return 0;
 }
 
-int __init sci_init(void)
+static int sci_request_irq(struct sci_port *port)
 {
-	struct sci_port *port;
-	int i, j;
+	int i;
 	void (*handlers[4])(int irq, void *ptr, struct pt_regs *regs) = {
 		sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt,
 		sci_br_interrupt,
 	};
 
+	for (i=0; i<4; i++) {
+		if (!port->irqs[i]) continue;
+		if (request_irq(port->irqs[i], handlers[i], SA_INTERRUPT,
+				"sci", port)) {
+			printk(KERN_ERR "sci: Cannot allocate irq.\n");
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+static void sci_free_irq(struct sci_port *port)
+{
+	int i;
+
+	for (i=0; i<4; i++) {
+		if (!port->irqs[i]) continue;
+		free_irq(port->irqs[i], port);
+	}
+}
+
+int __init sci_init(void)
+{
+	struct sci_port *port;
+	int j;
+
 	printk("SuperH SCI(F) driver initialized\n");
 
 	for (j=0; j<SCI_NPORTS; j++) {
 		port = &sci_ports[j];
 		printk("ttySC%d at 0x%08x is a %s\n", j, port->base,
 		       (port->type == PORT_SCI) ? "SCI" : "SCIF");
-		for (i=0; i<4; i++) {
-			if (!port->irqs[i]) continue;
-			if (request_irq(port->irqs[i], handlers[i], SA_INTERRUPT,
-					"sci", port)) {
-				printk(KERN_ERR "sci: Cannot allocate irq.\n");
-				return -ENODEV;
-			}
-		}
 	}
 
 	sci_init_drivers();
@@ -1135,11 +1165,6 @@
 
 void cleanup_module(void)
 {
-	int i;
-
-	for (i=SCI_ERI_IRQ; i<SCI_TEI_IRQ; i++) /* XXX: irq_end?? */
-		free_irq(i, port);
-
 	tty_unregister_driver(&sci_driver);
 	tty_unregister_driver(&sci_callout_driver);
 }
diff -urN linux-2.4.4-pre2/drivers/char/sh-sci.h linux/drivers/char/sh-sci.h
--- linux-2.4.4-pre2/drivers/char/sh-sci.h	Mon Oct  2 11:57:34 2000
+++ linux/drivers/char/sh-sci.h	Mon Dec  3 22:34:50 2001
@@ -25,6 +25,7 @@
 #define SH3_SCIF_IRQS { 56,  57,  59,  58 }
 #define SH3_IRDA_IRQS { 52,  53,  55,  54 }
 #define SH4_SCIF_IRQS { 40,  41,  43,  42 }
+#define STB1_SCIF1_IRQS {23, 24,  26,  25 }
 
 #if defined(CONFIG_CPU_SUBTYPE_SH7708)
 # define SCI_NPORTS 1
@@ -53,12 +54,22 @@
 }
 # define SCSPTR1 0xffe0001c /* 8  bit SCI */
 # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */
-# define SCLSR2  0xFFE80024 /* 16 bit SCIF */
 # define SCIF_ORER 0x0001   /* overrun error bit */
 # define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \
 	0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \
 	0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ )
 # define SCI_AND_SCIF
+#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
+# define SCI_NPORTS 2
+# define SCI_INIT { \
+  { {}, PORT_SCIF, 0xffe00000, STB1_SCIF1_IRQS, sci_init_pins_scif }, \
+  { {}, PORT_SCIF, 0xffe80000, SH4_SCIF_IRQS,   sci_init_pins_scif }  \
+}
+# define SCSPTR1 0xffe00020 /* 16 bit SCIF */
+# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
+# define SCIF_ORER 0x0001   /* overrun error bit */
+# define SCSCR_INIT(port)          0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
+# define SCIF_ONLY
 #else
 # error CPU subtype not defined
 #endif
@@ -260,6 +271,7 @@
 SCIx_FNS(SCxRDR, 0x0a,  8, 0x14,  8, 0x0A,  8, 0x14,  8)
 SCIF_FNS(SCFCR,                      0x0c,  8, 0x18, 16)
 SCIF_FNS(SCFDR,                      0x0e, 16, 0x1C, 16)
+SCIF_FNS(SCLSR,                         0,  0, 0x24, 16)
 
 #define sci_in(port, reg) sci_##reg##_in(port)
 #define sci_out(port, reg, value) sci_##reg##_out(port, value)
@@ -285,11 +297,24 @@
 #elif defined(CONFIG_CPU_SUBTYPE_SH7750)
 static inline int sci_rxd_in(struct sci_port *port)
 {
+#ifndef SCIF_ONLY
 	if (port->base == 0xffe00000)
 		return ctrl_inb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */
+#endif
+#ifndef SCI_ONLY
 	if (port->base == 0xffe80000)
 		return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
+#endif
 	return 1;
+}
+#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
+static inline int sci_rxd_in(struct sci_port *port)
+{
+	if (port->base == 0xffe00000)
+		return ctrl_inw(SCSPTR1)&0x0001 ? 1 : 0; /* SCIF */
+	else
+		return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */
+
 }
 #endif
 
diff -urN linux-2.4.4-pre2/drivers/md/lvm.c linux/drivers/md/lvm.c
--- linux-2.4.4-pre2/drivers/md/lvm.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/md/lvm.c	Mon Dec  3 22:34:50 2001
@@ -166,13 +166,6 @@
 
 #include <linux/config.h>
 #include <linux/version.h>
-
-#ifdef MODVERSIONS
-#undef MODULE
-#define MODULE
-#include <linux/modversions.h>
-#endif
-
 #include <linux/module.h>
 
 #include <linux/kernel.h>
diff -urN linux-2.4.4-pre2/drivers/net/3c505.c linux/drivers/net/3c505.c
--- linux-2.4.4-pre2/drivers/net/3c505.c	Tue Mar 20 12:05:00 2001
+++ linux/drivers/net/3c505.c	Mon Dec  3 22:34:50 2001
@@ -149,7 +149,7 @@
 #ifdef ELP_DEBUG
 static const int elp_debug = ELP_DEBUG;
 #else
-static const int elp_debug = 0;
+static const int elp_debug;
 #endif
 
 /*
diff -urN linux-2.4.4-pre2/drivers/net/3c507.c linux/drivers/net/3c507.c
--- linux-2.4.4-pre2/drivers/net/3c507.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/3c507.c	Mon Dec  3 22:34:50 2001
@@ -323,7 +323,7 @@
 
 static int __init el16_probe1(struct net_device *dev, int ioaddr)
 {
-	static unsigned char init_ID_done = 0, version_printed = 0;
+	static unsigned char init_ID_done, version_printed;
 	int i, irq, irqval, retval;
 	struct net_local *lp;
 
@@ -858,7 +858,7 @@
 #ifdef MODULE
 static struct net_device dev_3c507;
 static int io = 0x300;
-static int irq = 0;
+static int irq;
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
 
diff -urN linux-2.4.4-pre2/drivers/net/3c509.c linux/drivers/net/3c509.c
--- linux-2.4.4-pre2/drivers/net/3c509.c	Sun Mar 25 18:14:25 2001
+++ linux/drivers/net/3c509.c	Mon Dec  3 22:34:50 2001
@@ -144,7 +144,7 @@
 	char mca_slot;
 };
 static int id_port __initdata = 0x110;	/* Start with 0x110 to avoid new sound cards.*/
-static struct net_device *el3_root_dev = NULL;
+static struct net_device *el3_root_dev;
 
 static ushort id_read_eeprom(int index);
 static ushort read_eeprom(int ioaddr, int index);
@@ -209,10 +209,10 @@
 	short lrs_state = 0xff, i;
 	int ioaddr, irq, if_port;
 	u16 phys_addr[3];
-	static int current_tag = 0;
+	static int current_tag;
 	int mca_slot = -1;
 #ifdef CONFIG_ISAPNP
-	static int pnp_cards = 0;
+	static int pnp_cards;
 #endif /* CONFIG_ISAPNP */
 
 	if (dev) SET_MODULE_OWNER(dev);
@@ -942,7 +942,7 @@
 	int ioaddr = dev->base_addr;
 
 	if (el3_debug > 1) {
-		static int old = 0;
+		static int old;
 		if (old != dev->mc_count) {
 			old = dev->mc_count;
 			printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count);
diff -urN linux-2.4.4-pre2/drivers/net/3c515.c linux/drivers/net/3c515.c
--- linux-2.4.4-pre2/drivers/net/3c515.c	Tue Mar 20 12:05:00 2001
+++ linux/drivers/net/3c515.c	Mon Dec  3 22:34:50 2001
@@ -407,7 +407,7 @@
 #ifdef MODULE
 static int debug = -1;
 /* A list of all installed Vortex devices, for removing the driver module. */
-static struct net_device *root_corkscrew_dev = NULL;
+static struct net_device *root_corkscrew_dev;
 
 int init_module(void)
 {
@@ -445,7 +445,7 @@
 	static int ioaddr;
 #ifdef CONFIG_ISAPNP
 	short i;
-	static int pnp_cards = 0;
+	static int pnp_cards;
 #endif
 
 #ifdef CONFIG_ISAPNP
@@ -1166,7 +1166,7 @@
 		printk("%s: interrupt, status %4.4x, timer %d.\n",
 			dev->name, status, latency);
 	if ((status & 0xE000) != 0xE000) {
-		static int donedidthis = 0;
+		static int donedidthis;
 		/* Some interrupt controllers store a bogus interrupt from boot-time.
 		   Ignore a single early interrupt, but don't hang the machine for
 		   other interrupt problems. */
@@ -1234,7 +1234,7 @@
 				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
 			}
 			if (status & StatsFull) {	/* Empty statistics. */
-				static int DoneDidThat = 0;
+				static int DoneDidThat;
 				if (corkscrew_debug > 4)
 					printk("%s: Updating stats.\n",
 					       dev->name);
diff -urN linux-2.4.4-pre2/drivers/net/3c523.c linux/drivers/net/3c523.c
--- linux-2.4.4-pre2/drivers/net/3c523.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/3c523.c	Mon Dec  3 22:34:50 2001
@@ -404,7 +404,7 @@
 
 int __init elmc_probe(struct net_device *dev)
 {
-	static int slot = 0;
+	static int slot;
 	int base_addr = dev->base_addr;
 	int irq = dev->irq;
 	u_char status = 0;
diff -urN linux-2.4.4-pre2/drivers/net/3c527.c linux/drivers/net/3c527.c
--- linux-2.4.4-pre2/drivers/net/3c527.c	Sun Mar  4 14:05:04 2001
+++ linux/drivers/net/3c527.c	Mon Dec  3 22:34:50 2001
@@ -270,7 +270,7 @@
 
 static int __init mc32_probe1(struct net_device *dev, int slot)
 {
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	int i, err;
 	u8 POS;
 	u32 base;
diff -urN linux-2.4.4-pre2/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- linux-2.4.4-pre2/drivers/net/3c59x.c	Tue Mar  6 19:28:33 2001
+++ linux/drivers/net/3c59x.c	Mon Dec  3 22:34:50 2001
@@ -192,6 +192,7 @@
 #error You must compile this driver with "-O".
 #endif
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
diff -urN linux-2.4.4-pre2/drivers/net/82596.c linux/drivers/net/82596.c
--- linux-2.4.4-pre2/drivers/net/82596.c	Tue Mar  6 19:28:33 2001
+++ linux/drivers/net/82596.c	Mon Dec  3 22:34:50 2001
@@ -1095,7 +1095,7 @@
 	int i;
 	struct i596_private *lp;
 	char eth_addr[8];
-	static int probed = 0;
+	static int probed;
 
 	if (probed)
 		return -ENODEV;
diff -urN linux-2.4.4-pre2/drivers/net/a2065.c linux/drivers/net/a2065.c
--- linux-2.4.4-pre2/drivers/net/a2065.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/a2065.c	Mon Dec  3 22:34:50 2001
@@ -137,7 +137,7 @@
 };
 
 #ifdef MODULE
-static struct lance_private *root_a2065_dev = NULL;
+static struct lance_private *root_a2065_dev;
 #endif
 
 #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
diff -urN linux-2.4.4-pre2/drivers/net/acenic.c linux/drivers/net/acenic.c
--- linux-2.4.4-pre2/drivers/net/acenic.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/acenic.c	Mon Dec  3 22:34:50 2001
@@ -2,7 +2,7 @@
  * acenic.c: Linux driver for the Alteon AceNIC Gigabit Ethernet card
  *           and other Tigon based cards.
  *
- * Copyright 1998-2000 by Jes Sorensen, <jes@linuxcare.com>.
+ * Copyright 1998-2001 by Jes Sorensen, <jes@linuxcare.com>.
  *
  * Thanks to Alteon and 3Com for providing hardware and documentation
  * enabling me to write this driver.
@@ -62,6 +62,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/mm.h>
+#include <linux/highmem.h>
 #include <linux/sockios.h>
 
 #ifdef SIOCETHTOOL
@@ -282,6 +283,10 @@
 
 #include "acenic_firmware.h"
 
+#ifndef tigon2FwReleaseLocal
+#define tigon2FwReleaseLocal 0
+#endif
+
 /*
  * This driver currently supports Tigon I and Tigon II based cards
  * including the Alteon AceNIC, the 3Com 3C985[B] and NetGear
@@ -464,7 +469,7 @@
  * Jumbo frames are enabled, the user wants optimal tuning for that case.
  */
 #define DEF_TX_COAL		400 /* 996 */
-#define DEF_TX_MAX_DESC		40
+#define DEF_TX_MAX_DESC		60  /* was 40 */
 #define DEF_RX_COAL		120 /* 1000 */
 #define DEF_RX_MAX_DESC		25
 #define DEF_TX_RATIO		21 /* 24 */
@@ -475,10 +480,26 @@
 #define DEF_JUMBO_RX_MAX_DESC	6
 #define DEF_JUMBO_TX_RATIO	21
 
-#define TX_COAL_INTS_ONLY	0	/* seems not worth it */
+#if tigon2FwReleaseLocal < 20001118
+/*
+ * Standard firmware and early modifications duplicate
+ * IRQ load without this flag (coal timer is never reset).
+ * Note that with this flag tx_coal should be less than
+ * time to xmit full tx ring.
+ * 400usec is not so bad for tx ring size of 128.
+ */
+#define TX_COAL_INTS_ONLY	1	/* worth it */
+#else
+/*
+ * With modified firmware, this is not necessary, but still useful.
+ */
+#define TX_COAL_INTS_ONLY	1
+#endif
+
 #define DEF_TRACE		0
 #define DEF_STAT		(2 * TICKS_PER_SEC)
 
+
 static int link[ACE_MAX_MOD_PARMS];
 static int trace[ACE_MAX_MOD_PARMS];
 static int tx_coal_tick[ACE_MAX_MOD_PARMS];
@@ -489,7 +510,7 @@
 static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
 
 static char version[] __initdata = 
-  "acenic.c: v0.50 02/02/2001  Jes Sorensen, linux-acenic@SunSITE.dk\n"
+  "acenic.c: v0.80 03/08/2001  Jes Sorensen, linux-acenic@SunSITE.dk\n"
   "                            http://home.cern.ch/~jes/gige/acenic.html\n";
 
 static struct net_device *root_dev;
@@ -560,6 +581,12 @@
 		dev->irq = pdev->irq;
 		dev->open = &ace_open;
 		dev->hard_start_xmit = &ace_start_xmit;
+		dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA;
+		if (1) {
+			static void ace_watchdog(struct net_device *dev);
+			dev->tx_timeout = &ace_watchdog;
+			dev->watchdog_timeo = 5*HZ;
+		}
 		dev->stop = &ace_close;
 		dev->get_stats = &ace_get_stats;
 		dev->set_multicast_list = &ace_set_multicast_list;
@@ -762,15 +789,17 @@
 			struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb;
 
 			if (skb) {
+#ifndef DUMMY_PCI_UNMAP
 				dma_addr_t mapping;
 
 				mapping = ap->skb->rx_std_skbuff[i].mapping;
-
-				ap->rx_std_ring[i].size = 0;
-				ap->skb->rx_std_skbuff[i].skb = NULL;
 				pci_unmap_single(ap->pdev, mapping,
 						 ACE_STD_BUFSIZE - (2 + 16),
 						 PCI_DMA_FROMDEVICE);
+#endif
+
+				ap->rx_std_ring[i].size = 0;
+				ap->skb->rx_std_skbuff[i].skb = NULL;
 				dev_kfree_skb(skb);
 			}
 		}
@@ -779,14 +808,16 @@
 				struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb;
 
 				if (skb) {
+#ifndef DUMMY_PCI_UNMAP
 					dma_addr_t mapping;
 
 					mapping = ap->skb->rx_mini_skbuff[i].mapping;
-					ap->rx_mini_ring[i].size = 0;
-					ap->skb->rx_mini_skbuff[i].skb = NULL;
 					pci_unmap_single(ap->pdev, mapping,
 							 ACE_MINI_BUFSIZE - (2 + 16),
 							 PCI_DMA_FROMDEVICE);
+#endif
+					ap->rx_mini_ring[i].size = 0;
+					ap->skb->rx_mini_skbuff[i].skb = NULL;
 					dev_kfree_skb(skb);
 				}
 			}
@@ -794,15 +825,17 @@
 		for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) {
 			struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb;
 			if (skb) {
+#ifndef DUMMY_PCI_UNMAP
 				dma_addr_t mapping;
 
 				mapping = ap->skb->rx_jumbo_skbuff[i].mapping;
-
-				ap->rx_jumbo_ring[i].size = 0;
-				ap->skb->rx_jumbo_skbuff[i].skb = NULL;
 				pci_unmap_single(ap->pdev, mapping,
 						 ACE_JUMBO_BUFSIZE - (2 + 16),
 						 PCI_DMA_FROMDEVICE);
+#endif
+
+				ap->rx_jumbo_ring[i].size = 0;
+				ap->skb->rx_jumbo_skbuff[i].skb = NULL;
 				dev_kfree_skb(skb);
 			}
 		}
@@ -1177,10 +1210,21 @@
 				printk(KERN_INFO "  Disabling PCI memory "
 				       "write and invalidate\n");
 			}
+#ifdef __alpha__
+			/* This maximizes throughput on my alpha. */
+			tmp |= DMA_WRITE_MAX_128;
+#endif
 		} else if (ap->pci_command & PCI_COMMAND_INVALIDATE) {
 			printk(KERN_INFO "  PCI memory write & invalidate "
 			       "enabled by BIOS, enabling counter measures\n");
 
+#ifdef __alpha__
+			/* All the docs sy MUST NOT. Well, I did.
+			 * Nothing terrible happens, if we load wrong size.
+			 * Bit w&i still works better!
+			 */
+			tmp |= DMA_WRITE_MAX_128;
+#else
 			switch(SMP_CACHE_BYTES) {
 			case 16:
 				tmp |= DMA_WRITE_MAX_16;
@@ -1199,6 +1243,7 @@
 				pci_write_config_word(ap->pdev, PCI_COMMAND,
 						      ap->pci_command);
 			}
+#endif
 		}
 	}
 
@@ -1218,6 +1263,9 @@
 	tmp |= DMA_READ_MAX_64;
 	tmp |= DMA_WRITE_MAX_64;
 #endif
+#ifdef __alpha__
+	tmp |= DMA_READ_MAX_128;
+#endif
 	writel(tmp, &regs->PciState);
 
 #if 0
@@ -1313,7 +1361,7 @@
 
 	set_aceaddr(&info->rx_std_ctrl.rngptr, ap->rx_ring_base_dma);
 	info->rx_std_ctrl.max_len = ACE_STD_MTU + ETH_HLEN + 4;
-	info->rx_std_ctrl.flags = RCB_FLG_TCP_UDP_SUM;
+	info->rx_std_ctrl.flags = RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR;
 
 	memset(ap->rx_std_ring, 0,
 	       RX_STD_RING_ENTRIES * sizeof(struct rx_desc));
@@ -1328,7 +1376,7 @@
 		    (ap->rx_ring_base_dma +
 		     (sizeof(struct rx_desc) * RX_STD_RING_ENTRIES)));
 	info->rx_jumbo_ctrl.max_len = 0;
-	info->rx_jumbo_ctrl.flags = RCB_FLG_TCP_UDP_SUM;
+	info->rx_jumbo_ctrl.flags = RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR;
 
 	memset(ap->rx_jumbo_ring, 0,
 	       RX_JUMBO_RING_ENTRIES * sizeof(struct rx_desc));
@@ -1349,7 +1397,7 @@
 			      (RX_STD_RING_ENTRIES +
 			       RX_JUMBO_RING_ENTRIES))));
 		info->rx_mini_ctrl.max_len = ACE_MINI_SIZE;
-		info->rx_mini_ctrl.flags = RCB_FLG_TCP_UDP_SUM;
+		info->rx_mini_ctrl.flags = RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR;
 
 		for (i = 0; i < RX_MINI_RING_ENTRIES; i++)
 			ap->rx_mini_ring[i].flags =
@@ -1384,13 +1432,10 @@
 	set_aceaddr(&info->tx_ctrl.rngptr, ap->tx_ring_dma);
 
 	info->tx_ctrl.max_len = TX_RING_ENTRIES;
-
-	tmp = 0;
+	tmp = RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR|RCB_FLG_TX_HOST_RING;
 #if TX_COAL_INTS_ONLY
 	tmp |= RCB_FLG_COAL_INT_ONLY;
 #endif
-	tmp |= RCB_FLG_TX_HOST_RING;
-
 	info->tx_ctrl.flags = tmp;
 
 	set_aceaddr(&info->tx_csm_ptr, ap->tx_csm_dma);
@@ -1398,8 +1443,13 @@
 	/*
 	 * Potential item for tuning parameter
 	 */
+#if 0 /* NO */
+	writel(DMA_THRESH_16W, &regs->DmaReadCfg);
+	writel(DMA_THRESH_16W, &regs->DmaWriteCfg);
+#else
 	writel(DMA_THRESH_8W, &regs->DmaReadCfg);
 	writel(DMA_THRESH_8W, &regs->DmaWriteCfg);
+#endif
 
 	writel(0, &regs->MaskInt);
 	writel(1, &regs->IfIdx);
@@ -1430,7 +1480,7 @@
 		if (trace[board_idx])
 			writel(trace[board_idx], &regs->TuneTrace);
 
-		if ((tx_ratio[board_idx] >= 0) && (tx_ratio[board_idx] < 64))
+		if ((tx_ratio[board_idx] > 0) && (tx_ratio[board_idx] < 64))
 			writel(tx_ratio[board_idx], &regs->TxBufRat);
 	}
 
@@ -1501,7 +1551,6 @@
 	 * tx ints before we are up and running, which may cause a null
 	 * pointer access in the int handler.
 	 */
-	ap->tx_full = 0;
 	ap->cur_rx = 0;
 	ap->tx_prd = *(ap->tx_csm) = ap->tx_ret_csm = 0;
 
@@ -1510,6 +1559,11 @@
 	writel(0, &regs->RxRetCsm);
 
 	/*
+	 * Zero the stats before starting the interface
+	 */
+	memset(&ap->stats, 0, sizeof(ap->stats));
+
+	/*
 	 * Start the NIC CPU
 	 */
 	writel(readl(&regs->CpuCtrl) & ~(CPU_HALT|CPU_TRACE), &regs->CpuCtrl);
@@ -1611,12 +1665,9 @@
 }
 
 
-/*
- * Monitor the card to detect hangs.
- */
-static void ace_timer(unsigned long data)
+static void ace_watchdog(struct net_device *data)
 {
-	struct net_device *dev = (struct net_device *)data;
+	struct net_device *dev = data;
 	struct ace_private *ap = dev->priv;
 	struct ace_regs *regs = ap->regs;
 
@@ -1628,13 +1679,13 @@
 	if (*ap->tx_csm != ap->tx_ret_csm) {
 		printk(KERN_WARNING "%s: Transmitter is stuck, %08x\n",
 		       dev->name, (unsigned int)readl(&regs->HostCtrl));
+		/* This can happen due to ieee flow control. */
+	} else {
+		printk(KERN_DEBUG "%s: BUG... transmitter died. Kicking it.\n", dev->name);
+		netif_wake_queue(dev);
 	}
-
-	ap->timer.expires = jiffies + (5/2*HZ);
-	add_timer(&ap->timer);
 }
 
-
 static void ace_tasklet(unsigned long dev)
 {
 	struct ace_private *ap = ((struct net_device *)dev)->priv;
@@ -1719,7 +1770,9 @@
 					 ACE_STD_BUFSIZE - (2 + 16),
 					 PCI_DMA_FROMDEVICE);
 		ap->skb->rx_std_skbuff[idx].skb = skb;
+#ifndef DUMMY_PCI_UNMAP
 		ap->skb->rx_std_skbuff[idx].mapping = mapping;
+#endif
 
 		rd = &ap->rx_std_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -1781,7 +1834,9 @@
 					 ACE_MINI_BUFSIZE - (2 + 16),
 					 PCI_DMA_FROMDEVICE);
 		ap->skb->rx_mini_skbuff[idx].skb = skb;
+#ifndef DUMMY_PCI_UNMAP
 		ap->skb->rx_mini_skbuff[idx].mapping = mapping;
+#endif
 
 		rd = &ap->rx_mini_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -1840,7 +1895,9 @@
 					 ACE_JUMBO_BUFSIZE - (2 + 16),
 					 PCI_DMA_FROMDEVICE);
 		ap->skb->rx_jumbo_skbuff[idx].skb = skb;
+#ifndef DUMMY_PCI_UNMAP
 		ap->skb->rx_jumbo_skbuff[idx].mapping = mapping;
+#endif
 
 		rd = &ap->rx_jumbo_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -1870,8 +1927,9 @@
 	clear_bit(0, &ap->jumbo_refill_busy);
 	return;
  error_out:
-	printk(KERN_INFO "Out of memory when allocating "
-	       "jumbo receive buffers\n");
+	if (net_ratelimit())
+		printk(KERN_INFO "Out of memory when allocating "
+		       "jumbo receive buffers\n");
 	goto out;
 }
 
@@ -1902,9 +1960,16 @@
 			u16 code = ap->evt_ring[evtcsm].code;
 			switch (code) {
 			case E_C_LINK_UP:
-				printk(KERN_WARNING "%s: Optical link UP\n",
-				       dev->name);
+			{
+				u32 state = readl(&ap->regs->GigLnkState);
+				printk(KERN_WARNING "%s: Optical link UP "
+				       "(%s Duplex, Flow Control: %s%s)\n",
+				       dev->name,
+				       state & LNK_FULL_DUPLEX ? "Full":"Half",
+				       state & LNK_TX_FLOW_CTL_Y ? "TX " : "",
+				       state & LNK_RX_FLOW_CTL_Y ? "RX" : "");
 				break;
+			}
 			case E_C_LINK_DOWN:
 				printk(KERN_WARNING "%s: Optical link DOWN\n",
 				       dev->name);
@@ -1965,8 +2030,6 @@
 			ap->rx_jumbo_skbprd = 0;
 			printk(KERN_INFO "%s: Jumbo ring flushed\n",
 			       dev->name);
-			if (!ap->tx_full)
-				netif_wake_queue(dev);
 			clear_bit(0, &ap->jumbo_refill_busy);
 			break;
 		}
@@ -1994,12 +2057,13 @@
 		struct sk_buff *skb;
 		struct rx_desc *rxdesc, *retdesc;
 		u32 skbidx;
-		int desc_type, mapsize;
+		int bd_flags, desc_type, mapsize;
 		u16 csum;
 
 		retdesc = &ap->rx_return_ring[idx];
 		skbidx = retdesc->idx;
-		desc_type = retdesc->flags & (BD_FLG_JUMBO | BD_FLG_MINI);
+		bd_flags = retdesc->flags;
+		desc_type = bd_flags & (BD_FLG_JUMBO | BD_FLG_MINI);
 
 		switch(desc_type) {
 			/*
@@ -2036,8 +2100,10 @@
 
 		skb = rip->skb;
 		rip->skb = NULL;
+#ifndef DUMMY_PCI_UNMAP
 		pci_unmap_single(ap->pdev, rip->mapping, mapsize,
 				 PCI_DMA_FROMDEVICE);
+#endif
 		skb_put(skb, retdesc->size);
 #if 0
 		/* unncessary */
@@ -2053,15 +2119,15 @@
 		skb->protocol = eth_type_trans(skb, dev);
 
 		/*
-		 * If the checksum is correct and this is not a
-		 * fragment, tell the stack that the data is correct.
+		 * Instead of forcing the poor tigon mips cpu to calculate
+		 * pseudo hdr checksum, we do this ourselves.
 		 */
-		if(!(csum ^ 0xffff) &&
-		   (!(((struct iphdr *)skb->data)->frag_off &
-		      __constant_htons(IP_MF|IP_OFFSET))))
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-		else
+		if (bd_flags & BD_FLG_TCP_UDP_SUM) {
+			skb->csum = htons(csum);
+			skb->ip_summed = CHECKSUM_HW;
+		} else {
 			skb->ip_summed = CHECKSUM_NONE;
+		}
 
 		netif_rx(skb);		/* send it up */
 
@@ -2094,6 +2160,75 @@
 }
 
 
+static inline void ace_tx_int(struct net_device *dev,
+			      u32 txcsm, u32 idx)
+{
+	struct ace_private *ap = dev->priv;
+
+	do {
+		struct sk_buff *skb;
+#ifndef DUMMY_PCI_UNMAP
+		dma_addr_t mapping;
+#endif
+		struct tx_ring_info *info;
+
+		info = ap->skb->tx_skbuff + idx;
+		skb = info->skb;
+#ifndef DUMMY_PCI_UNMAP
+		mapping = info->mapping;
+
+		if (mapping) {
+			pci_unmap_single(ap->pdev, mapping, info->maplen,
+					 PCI_DMA_TODEVICE);
+			info->mapping = 0;
+		}
+#endif
+		if (skb) {
+			ap->stats.tx_packets++;
+			ap->stats.tx_bytes += skb->len;
+			dev_kfree_skb_irq(skb);
+			info->skb = NULL;
+		}
+
+		idx = (idx + 1) % TX_RING_ENTRIES;
+	} while (idx != txcsm);
+
+	if (netif_queue_stopped(dev))
+		netif_wake_queue(dev);
+
+	wmb();
+	ap->tx_ret_csm = txcsm;
+
+	/* So... tx_ret_csm is advanced _after_ check for device wakeup.
+	 *
+	 * We could try to make it before. In this case we would get
+	 * the following race condition: hard_start_xmit on other cpu
+	 * enters after we advanced tx_ret_csm and fills space,
+	 * which we have just freed, so that we make illegal device wakeup.
+	 * There is no good way to workaround this (at entry
+	 * to ace_start_xmit detects this condition and prevents
+	 * ring corruption, but it is not a good workaround.)
+	 *
+	 * When tx_ret_csm is advanced after, we wake up device _only_
+	 * if we really have some space in ring (though the core doing
+	 * hard_start_xmit can see full ring for some period and has to
+	 * synchronize.) Superb.
+	 * BUT! We get another subtle race condition. hard_start_xmit
+	 * may think that ring is full between wakeup and advancing
+	 * tx_ret_csm and will stop device instantly! It is not so bad.
+	 * We are guaranteed that there is something in ring, so that
+	 * the next irq will resume transmission. To speedup this we could
+	 * mark descriptor, which closes ring with BD_FLG_COAL_NOW
+	 * (see ace_start_xmit).
+	 *
+	 * Well, this dilemma exists in all lock-free devices.
+	 * We, following scheme used in drivers by Donald Becker,
+	 * select the least dangerous.
+	 *							--ANK
+	 */
+}
+
+
 static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
 {
 	struct ace_private *ap;
@@ -2115,9 +2250,14 @@
 		return;
 
 	/*
-	 * Tell the card not to generate interrupts while we are in here.
+	 * ACK intr now. Otherwise we will lose updates to rx_ret_prd,
+	 * which happened _after_ rxretprd = *ap->rx_ret_prd; but before
+	 * writel(0, &regs->Mb0Lo).
+	 *
+	 * "IRQ avoidance" recommended in docs applies to IRQs served
+	 * threads and it is wrong even for that case.
 	 */
-	writel(1, &regs->Mb0Lo);
+	writel(0, &regs->Mb0Lo);
 
 	/*
 	 * There is no conflict between transmit handling in
@@ -2136,75 +2276,15 @@
 	idx = ap->tx_ret_csm;
 
 	if (txcsm != idx) {
-		do {
-			struct sk_buff *skb;
-
-			skb = ap->skb->tx_skbuff[idx].skb;
-			/*
-			 * Race condition between the code cleaning
-			 * the tx queue in the interrupt handler and the
-			 * interface close,
-			 *
-			 * This is a kludge that really should be fixed 
-			 * by preventing the driver from generating a tx
-			 * interrupt when the packet has already been
-			 * removed from the tx queue.
-			 *
-			 * Nailed by Don Dugger and Chip Salzenberg of
-			 * VA Linux.
-			 */
-			if (skb) {
-				dma_addr_t mapping;
-
-				mapping = ap->skb->tx_skbuff[idx].mapping;
-
-				ap->stats.tx_packets++;
-				ap->stats.tx_bytes += skb->len;
-				pci_unmap_single(ap->pdev, mapping, skb->len,
-						 PCI_DMA_TODEVICE);
-				dev_kfree_skb_irq(skb);
-
-				ap->skb->tx_skbuff[idx].skb = NULL;
-			}
-
-			/*
-			 * Question here is whether one should not skip
-			 * these writes - I have never seen any errors
-			 * caused by the NIC actually trying to access
-			 * these incorrectly.
-			 */
-#ifdef ACE_64BIT_PTR
-			ap->tx_ring[idx].addr.addrhi = 0;
-#endif
-			ap->tx_ring[idx].addr.addrlo = 0;
-			ap->tx_ring[idx].flagsize = 0;
-
-			idx = (idx + 1) % TX_RING_ENTRIES;
-		} while (idx != txcsm);
-
 		/*
-		 * Once we actually get to this point the tx ring has
-		 * already been trimmed thus it cannot be full!
-		 * Ie. skip the comparison of the tx producer vs. the
-		 * consumer.
+		 * If each skb takes only one descriptor this check degenerates
+		 * to identity, because new space has just been opened.
+		 * But if skbs are fragmented we must check that this index
+		 * update releases enough of space, otherwise we just
+		 * wait for device to make more work.
 		 */
-		if (netif_queue_stopped(dev) && xchg(&ap->tx_full, 0)) {
-			/*
-			 * This does not need to be atomic (and expensive),
-			 * I've seen cases where it would fail otherwise ;-(
-			 */
-			netif_wake_queue(dev);
-			ace_mark_net_bh();
-
-			/*
-			 * TX ring is no longer full, aka the
-			 * transmitter is working fine - kill timer.
-			 */
-			del_timer(&ap->timer);
-		}
-
-		ap->tx_ret_csm = txcsm;
-		wmb();
+		if (!tx_ring_full(txcsm, ap->tx_prd))
+			ace_tx_int(dev, txcsm, idx);
 	}
 
 	evtcsm = readl(&regs->EvtCsm);
@@ -2272,11 +2352,6 @@
 			tasklet_schedule(&ap->ace_tasklet);
 		}
 	}
-
-	/*
-	 * Allow the card to generate interrupts again
-	 */
-	writel(0, &regs->Mb0Lo);
 }
 
 
@@ -2296,11 +2371,6 @@
 
 	writel(dev->mtu + ETH_HLEN + 4, &regs->IfMtu);
 
-	/*
-	 * Zero the stats when restarting the interface...
-	 */
-	memset(&ap->stats, 0, sizeof(ap->stats));
-
 	cmd.evt = C_CLEAR_STATS;
 	cmd.code = 0;
 	cmd.idx = 0;
@@ -2338,13 +2408,6 @@
 	ACE_MOD_INC_USE_COUNT;
 
 	/*
-	 * Setup the timer
-	 */
-	init_timer(&ap->timer);
-	ap->timer.data = (unsigned long)dev;
-	ap->timer.function = ace_timer;
-
-	/*
 	 * Setup the bottom half rx ring refill handler
 	 */
 	tasklet_init(&ap->ace_tasklet, ace_tasklet, (unsigned long)dev);
@@ -2361,13 +2424,17 @@
 	short i;
 
 	ace_if_down(dev);
+
+	/*
+	 * Without (or before) releasing irq and stopping hardware, this
+	 * is an absolute non-sense, by the way. It will be reset instantly
+	 * by the first irq.
+	 */
 	netif_stop_queue(dev);
 
 	ap = dev->priv;
 	regs = ap->regs;
 
-	del_timer(&ap->timer);
-
 	if (ap->promisc) {
 		cmd.evt = C_SET_PROMISC_MODE;
 		cmd.code = C_C_PROMISC_DISABLE;
@@ -2392,16 +2459,26 @@
 
 	for (i = 0; i < TX_RING_ENTRIES; i++) {
 		struct sk_buff *skb;
+#ifndef DUMMY_PCI_UNMAP
 		dma_addr_t mapping;
+#endif
+		struct tx_ring_info *info;
 
-		skb = ap->skb->tx_skbuff[i].skb;
-		mapping = ap->skb->tx_skbuff[i].mapping;
-		if (skb) {
-			memset(&ap->tx_ring[i].addr, 0, sizeof(struct tx_desc));
-			pci_unmap_single(ap->pdev, mapping, skb->len,
+		info = ap->skb->tx_skbuff + i;
+		skb = info->skb;
+#ifndef DUMMY_PCI_UNMAP
+		mapping = info->mapping;
+
+		if (mapping) {
+			memset(ap->tx_ring+i, 0, sizeof(struct tx_desc));
+			pci_unmap_single(ap->pdev, mapping, info->maplen,
 					 PCI_DMA_TODEVICE);
+			info->mapping = 0;
+		}
+#endif
+		if (skb) {
 			dev_kfree_skb(skb);
-			ap->skb->tx_skbuff[i].skb = NULL;
+			info->skb = NULL;
 		}
 	}
 
@@ -2419,85 +2496,216 @@
 }
 
 
+/*
+ * Following below should be (in more clean form!) in arch/ARCH/kernel/pci_*.
+ * For now, let it stay here.
+ */
+#if defined(CONFIG_HIGHMEM) && MAX_SKB_FRAGS
+#ifndef DUMMY_PCI_UNMAP
+#error Sorry, cannot DMA from high memory on this architecture.
+#endif
+
+#if defined(CONFIG_X86)
+#define DMAADDR_OFFSET	0
+typedef unsigned long long dmaaddr_high_t;
+#elif defined(CONFIG_PPC)
+#define DMAADDR_OFFSET PCI_DRAM_OFFSET
+typedef unsigned long dmaaddr_high_t;
+#endif
+
+
+static inline dmaaddr_high_t
+pci_map_single_high(struct pci_dev *hwdev, struct page *page,
+		    int offset, size_t size, int dir)
+{
+	dmaaddr_high_t phys;
+
+	phys = (page-mem_map) *	(dmaaddr_high_t) PAGE_SIZE + offset;
+
+	return (phys + DMAADDR_OFFSET);
+}
+
+#else
+
+typedef unsigned long dmaaddr_high_t;
+
+static inline dmaaddr_high_t
+pci_map_single_high(struct pci_dev *hwdev, struct page *page,
+		    int offset, size_t size, int dir)
+{
+	return pci_map_single(hwdev, page_address(page) + offset, size, dir);
+}
+
+#endif
+
+
+static inline dmaaddr_high_t
+ace_map_tx_skb(struct ace_private *ap, struct sk_buff *skb,
+	       struct sk_buff *tail, u32 idx)
+{
+	unsigned long addr;
+	struct tx_ring_info *info;
+
+	addr = pci_map_single(ap->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+
+	info = ap->skb->tx_skbuff + idx;
+	info->skb = tail;
+#ifndef DUMMY_PCI_UNMAP
+	info->mapping = addr;
+	info->maplen = skb->len;
+#endif
+	return addr;
+}
+
+
+static inline void
+ace_load_tx_bd(struct tx_desc *desc, dmaaddr_high_t addr, u32 flagsize)
+{
+#if !USE_TX_COAL_NOW
+	flagsize &= ~BD_FLG_COAL_NOW;
+#endif
+
+#ifdef ACE_64BIT_PTR
+	desc->addr.addrhi = addr >> 32;
+#endif
+	desc->addr.addrlo = addr;
+	desc->flagsize = flagsize;
+}
+
+
 static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct ace_private *ap = dev->priv;
 	struct ace_regs *regs = ap->regs;
-	unsigned long addr;
+	struct tx_desc *desc;
 	u32 idx, flagsize;
 
-	/*
+ 	/*
 	 * This only happens with pre-softnet, ie. 2.2.x kernels.
-	 */
+ 	 */
 	if (early_stop_netif_stop_queue(dev))
-		return 1;
+ 		return 1;
+
 
+restart:
 	idx = ap->tx_prd;
 
-	if ((idx + 1) % TX_RING_ENTRIES == ap->tx_ret_csm) {
-		ap->tx_full = 1;
-#if DEBUG
-		printk("%s: trying to transmit while the tx ring is full "
-		       "- this should not happen!\n", dev->name);
+	if (tx_ring_full(ap->tx_ret_csm, idx))
+		goto overflow;
+
+#if MAX_SKB_FRAGS
+	if (!skb_shinfo(skb)->nr_frags)
 #endif
-		return 1;
-	}
+	{
+		unsigned long addr;
 
-	ap->skb->tx_skbuff[idx].skb = skb;
-	ap->skb->tx_skbuff[idx].mapping =
-		pci_map_single(ap->pdev, skb->data, skb->len,
-			       PCI_DMA_TODEVICE);
-	addr = (unsigned long) ap->skb->tx_skbuff[idx].mapping;
-	flagsize = (skb->len << 16) | (BD_FLG_END) ;
-	set_aceaddr(&ap->tx_ring[idx].addr, addr);
-	ap->tx_ring[idx].flagsize = flagsize;
-	wmb();
-	idx = (idx + 1) % TX_RING_ENTRIES;
+		addr = ace_map_tx_skb(ap, skb, skb, idx);
+		flagsize = (skb->len << 16) | (BD_FLG_END);
+		if (skb->ip_summed == CHECKSUM_HW)
+			flagsize |= BD_FLG_TCP_UDP_SUM;
+		desc = ap->tx_ring + idx;
+		idx = (idx + 1) % TX_RING_ENTRIES;
+
+		/* Look at ace_tx_int for explanations. */
+		if (tx_ring_full(ap->tx_ret_csm, idx))
+			flagsize |= BD_FLG_COAL_NOW;
+
+		ace_load_tx_bd(desc, addr, flagsize);
+	}
+#if MAX_SKB_FRAGS
+	else {
+		unsigned long addr;
+		int i, len = 0;
+
+		addr = ace_map_tx_skb(ap, skb, NULL, idx);
+		flagsize = ((skb->len - skb->data_len) << 16);
+		if (skb->ip_summed == CHECKSUM_HW)
+			flagsize |= BD_FLG_TCP_UDP_SUM;
+
+		ace_load_tx_bd(ap->tx_ring + idx, addr, flagsize);
+
+		idx = (idx + 1) % TX_RING_ENTRIES;
+
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+			struct tx_ring_info *info;
+			dmaaddr_high_t phys;
+
+			len += frag->size;
+			info = ap->skb->tx_skbuff + idx;
+			desc = ap->tx_ring + idx;
+
+			phys = pci_map_single_high(ap->pdev, frag->page,
+						   frag->page_offset,
+						   frag->size,
+						   PCI_DMA_TODEVICE);
+
+			flagsize = (frag->size << 16);
+			if (skb->ip_summed == CHECKSUM_HW)
+				flagsize |= BD_FLG_TCP_UDP_SUM;
+			idx = (idx + 1) % TX_RING_ENTRIES;
 
-	ap->tx_prd = idx;
-	ace_set_txprd(regs, ap, idx);
+			if (i == skb_shinfo(skb)->nr_frags-1) {
+				flagsize |= BD_FLG_END;
+				if (tx_ring_full(ap->tx_ret_csm, idx))
+					flagsize |= BD_FLG_COAL_NOW;
+
+				/*
+				 * Only the last fragment frees
+				 * the skb!
+				 */
+				info->skb = skb;
+			} else {
+				info->skb = NULL;
+			}
+#ifndef DUMMY_PCI_UNMAP
+			info->mapping = phys;
+			info->maplen = frag->size;
+#endif
+			ace_load_tx_bd(desc, phys, flagsize);
+		}
+	}
+#endif
 
-	/*
-	 * tx_csm is set by the NIC whereas we set tx_ret_csm which
-	 * is always trying to catch tx_csm
-	 */
-	if ((idx + 2) % TX_RING_ENTRIES == ap->tx_ret_csm) {
-		ap->tx_full = 1;
-		/*
-		 * Queue is full, add timer to detect whether the
-		 * transmitter is stuck. Use mod_timer as we can get
-		 * into the situation where we risk adding several
-		 * timers.
-		 */
-		mod_timer(&ap->timer, jiffies + (3 * HZ));
+ 	wmb();
+ 	ap->tx_prd = idx;
+ 	ace_set_txprd(regs, ap, idx);
+
+	if (flagsize & BD_FLG_COAL_NOW) {
+		netif_stop_queue(dev);
 
 		/*
-		 * The following check will fix a race between the interrupt
-		 * handler increasing the tx_ret_csm and testing for tx_full
-		 * and this tx routine's testing the tx_ret_csm and setting
-		 * the tx_full; note that this fix makes assumptions on the
-		 * ordering of writes (sequential consistency will fly; TSO
-		 * processor order would work too) but that's what lock-less
-		 * programming is all about
+		 * A TX-descriptor producer (an IRQ) might have gotten
+		 * inbetween, making the ring free again. Since xmit is
+		 * serialized, this is the only situation we have to
+		 * re-test.
 		 */
-		if (((idx + 2) % TX_RING_ENTRIES != ap->tx_ret_csm)
-			&& xchg(&ap->tx_full, 0)) {
-			del_timer(&ap->timer);
-			/*
-			 * We may not need this one in the post softnet era
-			 * in this case this can be changed to a
-			 * early_stop_netif_wake_queue(dev);
-			 */
+		if (!tx_ring_full(ap->tx_ret_csm, idx))
 			netif_wake_queue(dev);
-		} else {
-			late_stop_netif_stop_queue(dev);
-		}
-	} else {
-		early_stop_netif_wake_queue(dev);
 	}
 
 	dev->trans_start = jiffies;
 	return 0;
+
+overflow:
+	/*
+	 * This race condition is unavoidable with lock-free drivers.
+	 * We wake up the queue _before_ tx_prd is advanced, so that we can
+	 * enter hard_start_xmit too early, while tx ring still looks closed.
+	 * This happens ~1-4 times per 100000 packets, so that we can allow
+	 * to loop syncing to other CPU. Probably, we need an additional
+	 * wmb() in ace_tx_intr as well.
+	 *
+	 * Note that this race is relieved by reserving one more entry
+	 * in tx ring than it is necessary (see original non-SG driver).
+	 * However, with SG we need to reserve 2*MAX_SKB_FRAGS+1, which
+	 * is already overkill.
+	 *
+	 * Alternative is to return with 1 not throttling queue. In this
+	 * case loop becomes longer, no more useful effects.
+	 */
+	barrier();
+	goto restart;
 }
 
 
@@ -2522,7 +2730,6 @@
 			ace_set_rxtx_parms(dev, 1);
 		}
 	} else {
-		netif_stop_queue(dev);
 		while (test_and_set_bit(0, &ap->jumbo_refill_busy));
 		synchronize_irq();
 		ace_set_rxtx_parms(dev, 0);
@@ -2542,27 +2749,12 @@
 
 static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-#ifdef SIOCETHTOOL
 	struct ace_private *ap = dev->priv;
 	struct ace_regs *regs = ap->regs;
+#ifdef SIOCETHTOOL
 	struct ethtool_cmd ecmd;
 	u32 link, speed;
 
-#ifdef SPIN_DEBUG
-	if (cmd == (SIOCDEVPRIVATE+0x0e)) {
-		printk(KERN_NOTICE "%s: dumping debug info\n", dev->name);
-		printk(KERN_NOTICE "%s: tbusy %li, tx_ret_csm %i, "
-		       "tx_prd %i\n", dev->name, dev->tbusy,
-		       ap->tx_ret_csm, ap->tx_prd);
-		printk(KERN_NOTICE "%s: cur_rx %i, std_refill %li, "
-		       "mini_rx %i, mini_refill %li\n", dev->name,
-		       atomic_read(&ap->cur_rx_bufs), ap->std_refill_busy,
-		       atomic_read(&ap->cur_mini_bufs), ap->mini_refill_busy);
-		printk(KERN_NOTICE "%s: CpuCtrl %08x\n",
-		       dev->name, readl(&regs->CpuCtrl));
-		return 0;
-	}
-#endif
 	if (cmd != SIOCETHTOOL)
 		return -EOPNOTSUPP;
 	if (copy_from_user(&ecmd, ifr->ifr_data, sizeof(ecmd)))
@@ -2699,7 +2891,8 @@
 
 	regs = ((struct ace_private *)dev->priv)->regs;
 	writel(da[0] << 8 | da[1], &regs->MacAddrHi);
-	writel((da[2] << 24) | (da[3] << 16) | (da[4] << 8) | da[5] , &regs->MacAddrLo);
+	writel((da[2] << 24) | (da[3] << 16) | (da[4] << 8) | da[5],
+	       &regs->MacAddrLo);
 
 	cmd.evt = C_SET_MAC_ADDR;
 	cmd.code = 0;
@@ -2774,11 +2967,12 @@
 	ap->stats.multicast = readl(&mac_stats->kept_mc);
 	ap->stats.collisions = readl(&mac_stats->coll);
 
-	return(&ap->stats);
+	return &ap->stats;
 }
 
 
-static void __init ace_copy(struct ace_regs *regs, void *src, u32 dest, int size)
+static void __init ace_copy(struct ace_regs *regs, void *src,
+			    u32 dest, int size)
 {
 	unsigned long tdest;
 	u32 *wsrc;
@@ -3124,6 +3318,6 @@
 
 /*
  * Local variables:
- * compile-command: "gcc -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o acenic.o acenic.c"
+ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o acenic.o acenic.c"
  * End:
  */
diff -urN linux-2.4.4-pre2/drivers/net/acenic.h linux/drivers/net/acenic.h
--- linux-2.4.4-pre2/drivers/net/acenic.h	Sat Dec 30 11:23:13 2000
+++ linux/drivers/net/acenic.h	Mon Dec  3 22:34:50 2001
@@ -1,6 +1,19 @@
 #ifndef _ACENIC_H_
 #define _ACENIC_H_
 
+
+/*
+ * Generate TX index update each time, when TX ring is closed.
+ * Normally, this is not useful, because results in more dma (and irqs
+ * without TX_COAL_INTS_ONLY).
+ */
+#define USE_TX_COAL_NOW	 0
+
+#ifndef MAX_SKB_FRAGS
+#define MAX_SKB_FRAGS 0
+#endif
+
+
 /*
  * Addressing:
  *
@@ -258,7 +271,12 @@
  * DMA config
  */
 
+#define DMA_THRESH_1W		0x10
+#define DMA_THRESH_2W		0x20
+#define DMA_THRESH_4W		0x40
 #define DMA_THRESH_8W		0x80
+#define DMA_THRESH_16W		0x100
+#define DMA_THRESH_32W		0x0	/* not described in doc, but exists. */
 
 
 /*
@@ -399,6 +417,7 @@
 #define BD_FLG_IP_SUM		0x02
 #define BD_FLG_END		0x04
 #define BD_FLG_JUMBO		0x10
+#define BD_FLG_COAL_NOW		0x800
 #define BD_FLG_MINI		0x1000
 
 
@@ -407,6 +426,7 @@
  */
 #define RCB_FLG_TCP_UDP_SUM	0x01
 #define RCB_FLG_IP_SUM		0x02
+#define RCB_FLG_NO_PSEUDO_HDR	0x08
 #define RCB_FLG_VLAN_ASSIST	0x10
 #define RCB_FLG_COAL_INT_ONLY	0x20
 #define RCB_FLG_TX_HOST_RING	0x40
@@ -561,12 +581,29 @@
 	aceaddr	stats2_ptr;
 };
 
+#if defined(CONFIG_X86) || defined(CONFIG_PPC)
+/* Intel has null pci_unmap_single, no reasons to remember mapping. */
+#define DUMMY_PCI_UNMAP
+#endif
 
 struct ring_info {
 	struct sk_buff		*skb;
+#ifndef DUMMY_PCI_UNMAP
 	dma_addr_t		mapping;
+#endif
 };
 
+/* Funny... As soon as we add maplen on alpha, it starts to work
+ * much slower. Hmm... is it because struct does not fit to one cacheline?
+ * So, split tx_ring_info.
+ */
+struct tx_ring_info {
+	struct sk_buff		*skb;
+#ifndef DUMMY_PCI_UNMAP
+	dma_addr_t		mapping;
+	int			maplen;
+#endif
+};
 
 /*
  * struct ace_skb holding the rings of skb's. This is an awful lot of
@@ -575,7 +612,7 @@
  */
 struct ace_skb
 {
-	struct ring_info	tx_skbuff[TX_RING_ENTRIES];
+	struct tx_ring_info	tx_skbuff[TX_RING_ENTRIES];
 	struct ring_info	rx_std_skbuff[RX_STD_RING_ENTRIES];
 	struct ring_info	rx_mini_skbuff[RX_MINI_RING_ENTRIES];
 	struct ring_info	rx_jumbo_skbuff[RX_JUMBO_RING_ENTRIES];
@@ -605,11 +642,10 @@
 	/*
 	 * TX elements
 	 */
-	struct tx_desc		*tx_ring
-				__attribute__ ((aligned (SMP_CACHE_BYTES)));
-	struct timer_list	timer;		/* used by TX handling only */
+	struct tx_desc		*tx_ring;
 	u32			tx_prd;
-	volatile u32		tx_full, tx_ret_csm;
+	volatile u32		tx_ret_csm;
+	struct timer_list	timer;
 
 	/*
 	 * RX elements
@@ -657,6 +693,22 @@
 };
 
 
+#define TX_RESERVED	MAX_SKB_FRAGS
+
+static inline int tx_space (u32 csm, u32 prd)
+{
+	return (csm - prd - 1) & (TX_RING_ENTRIES - 1);
+}
+
+#define tx_free(ap) 		tx_space((ap)->tx_ret_csm, (ap)->tx_prd)
+
+#if MAX_SKB_FRAGS
+#define tx_ring_full(csm, prd)	(tx_space(csm, prd) <= TX_RESERVED)
+#else
+#define tx_ring_full		0
+#endif
+
+
 static inline void set_aceaddr(aceaddr *aa, dma_addr_t addr)
 {
 	unsigned long baddr = (unsigned long) addr;
@@ -717,14 +769,10 @@
 static int ace_open(struct net_device *dev);
 static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static int ace_close(struct net_device *dev);
-static void ace_timer(unsigned long data);
 static void ace_tasklet(unsigned long dev);
 static void ace_dump_trace(struct ace_private *ap);
 static void ace_set_multicast_list(struct net_device *dev);
 static int ace_change_mtu(struct net_device *dev, int new_mtu);
-#ifdef SKB_RECYCLE
-extern int ace_recycle(struct sk_buff *skb);
-#endif
 static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
 static int ace_set_mac_addr(struct net_device *dev, void *p);
 static void ace_set_rxtx_parms(struct net_device *dev, int jumbo);
diff -urN linux-2.4.4-pre2/drivers/net/am79c961a.c linux/drivers/net/am79c961a.c
--- linux-2.4.4-pre2/drivers/net/am79c961a.c	Tue Feb 13 13:15:04 2001
+++ linux/drivers/net/am79c961a.c	Mon Dec  3 22:34:50 2001
@@ -637,7 +637,7 @@
 
 static void __init am79c961_banner(void)
 {
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 
 	if (net_debug && version_printed++ == 0)
 		printk(KERN_INFO "%s", version);
diff -urN linux-2.4.4-pre2/drivers/net/apne.c linux/drivers/net/apne.c
--- linux-2.4.4-pre2/drivers/net/apne.c	Mon Nov 27 17:11:26 2000
+++ linux/drivers/net/apne.c	Mon Dec  3 22:34:50 2001
@@ -119,7 +119,7 @@
 static const char *version =
     "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
 
-static int apne_owned = 0;	/* signal if card already owned */
+static int apne_owned;	/* signal if card already owned */
 
 int __init apne_probe(struct net_device *dev)
 {
@@ -173,7 +173,7 @@
 #ifndef MANUAL_HWADDR0
     int neX000, ctron;
 #endif
-    static unsigned version_printed = 0;
+    static unsigned version_printed;
     static u32 pcmcia_offsets[16]={
                 0,   1+GAYLE_ODD,   2,   3+GAYLE_ODD,
                 4,   5+GAYLE_ODD,   6,   7+GAYLE_ODD,
diff -urN linux-2.4.4-pre2/drivers/net/ariadne.c linux/drivers/net/ariadne.c
--- linux-2.4.4-pre2/drivers/net/ariadne.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/ariadne.c	Mon Dec  3 22:34:50 2001
@@ -121,7 +121,7 @@
 };
 
 #ifdef MODULE
-static struct ariadne_private *root_ariadne_dev = NULL;
+static struct ariadne_private *root_ariadne_dev;
 #endif
 
 static int ariadne_open(struct net_device *dev);
diff -urN linux-2.4.4-pre2/drivers/net/ariadne2.c linux/drivers/net/ariadne2.c
--- linux-2.4.4-pre2/drivers/net/ariadne2.c	Thu Jan  4 13:00:55 2001
+++ linux/drivers/net/ariadne2.c	Mon Dec  3 22:34:50 2001
@@ -62,7 +62,7 @@
 #define WORDSWAP(a)	((((a)>>8)&0xff) | ((a)<<8))
 
 #ifdef MODULE
-static struct net_device *root_ariadne2_dev = NULL;
+static struct net_device *root_ariadne2_dev;
 #endif
 
 static const struct card_info {
diff -urN linux-2.4.4-pre2/drivers/net/arlan-proc.c linux/drivers/net/arlan-proc.c
--- linux-2.4.4-pre2/drivers/net/arlan-proc.c	Tue Mar  6 19:28:33 2001
+++ linux/drivers/net/arlan-proc.c	Mon Dec  3 22:34:50 2001
@@ -1031,7 +1031,7 @@
 
 
 
-static struct ctl_table_header *arlan_device_sysctl_header = NULL;
+static struct ctl_table_header *arlan_device_sysctl_header;
 
 int init_arlan_proc(void)
 {
diff -urN linux-2.4.4-pre2/drivers/net/atari_bionet.c linux/drivers/net/atari_bionet.c
--- linux-2.4.4-pre2/drivers/net/atari_bionet.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/atari_bionet.c	Mon Dec  3 22:34:50 2001
@@ -327,8 +327,8 @@
 int __init 
 bionet_probe(struct net_device *dev){
 	unsigned char station_addr[6];
-	static unsigned version_printed = 0;
-	static int no_more_found = 0; /* avoid "Probing for..." printed 4 times */
+	static unsigned version_printed;
+	static int no_more_found;	/* avoid "Probing for..." printed 4 times */
 	int i;
 
 	if (!MACH_IS_ATARI || no_more_found)
diff -urN linux-2.4.4-pre2/drivers/net/atari_pamsnet.c linux/drivers/net/atari_pamsnet.c
--- linux-2.4.4-pre2/drivers/net/atari_pamsnet.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/atari_pamsnet.c	Mon Dec  3 22:34:50 2001
@@ -569,9 +569,9 @@
 	HADDR *hwaddr;
 
 	unsigned char station_addr[6];
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	/* avoid "Probing for..." printed 4 times - the driver is supporting only one adapter now! */
-	static int no_more_found = 0;
+	static int no_more_found;
 
 	if (no_more_found)
 		return -ENODEV;
diff -urN linux-2.4.4-pre2/drivers/net/atp.c linux/drivers/net/atp.c
--- linux-2.4.4-pre2/drivers/net/atp.c	Tue Mar  6 19:28:33 2001
+++ linux/drivers/net/atp.c	Mon Dec  3 22:34:50 2001
@@ -209,7 +209,7 @@
 
 
 /* A list of all installed ATP devices, for removing the driver module. */
-static struct net_device *root_atp_dev = NULL;
+static struct net_device *root_atp_dev;
 
 /* Check for a network adapter of this type, and return '0' iff one exists.
    If dev->base_addr == 0, probe all likely locations.
@@ -584,7 +584,7 @@
 	struct net_device *dev = (struct net_device *)dev_instance;
 	struct net_local *lp;
 	long ioaddr;
-	static int num_tx_since_rx = 0;
+	static int num_tx_since_rx;
 	int boguscount = max_interrupt_work;
 
 	if (dev == NULL) {
diff -urN linux-2.4.4-pre2/drivers/net/bagetlance.c linux/drivers/net/bagetlance.c
--- linux-2.4.4-pre2/drivers/net/bagetlance.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/bagetlance.c	Mon Dec  3 22:34:50 2001
@@ -471,7 +471,7 @@
 int __init bagetlance_probe( struct net_device *dev )
 
 {	int i;
-	static int found = 0;
+	static int found;
 
 	SET_MODULE_OWNER(dev);
 
@@ -519,7 +519,7 @@
 	struct lance_private	*lp;
 	struct lance_ioreg		*IO;
 	int 					i;
-	static int 				did_version = 0;
+	static int 				did_version;
 	unsigned short			save1, save2;
 
 	PROBE_PRINT(( "Probing for Lance card at mem %#lx io %#lx\n",
diff -urN linux-2.4.4-pre2/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
--- linux-2.4.4-pre2/drivers/net/cs89x0.c	Tue Mar  6 19:28:34 2001
+++ linux/drivers/net/cs89x0.c	Mon Dec  3 22:34:50 2001
@@ -130,7 +130,6 @@
 #include <asm/dma.h>
 #endif
 #include <linux/errno.h>
-#include <linux/init.h>
 #include <linux/spinlock.h>
 
 #include <linux/netdevice.h>
@@ -361,7 +360,7 @@
 cs89x0_probe1(struct net_device *dev, int ioaddr)
 {
 	struct net_local *lp;
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	int i;
 	unsigned rev_type = 0;
 	int eeprom_buff[CHKSUM_LEN];
@@ -1590,14 +1589,14 @@
  * avoid breaking someone's startup scripts 
  */
 
-static int io=0;
-static int irq=0;
-static int debug=0;
+static int io;
+static int irq;
+static int debug;
 static char media[8];
 static int duplex=-1;
 
-static int use_dma = 0;			/* These generate unused var warnings if ALLOW_DMA = 0 */
-static int dma=0;
+static int use_dma;			/* These generate unused var warnings if ALLOW_DMA = 0 */
+static int dma;
 static int dmasize=16;			/* or 64 */
 
 MODULE_PARM(io, "i");
diff -urN linux-2.4.4-pre2/drivers/net/daynaport.c linux/drivers/net/daynaport.c
--- linux-2.4.4-pre2/drivers/net/daynaport.c	Sat Mar  3 10:55:47 2001
+++ linux/drivers/net/daynaport.c	Mon Dec  3 22:34:50 2001
@@ -250,12 +250,12 @@
 
 int __init mac8390_probe(struct net_device *dev)
 {
-	static int slots = 0;
+	static int slots;
 	volatile unsigned short *i;
 	volatile unsigned char *p;
 	int plen;
 	int id;
-	static struct nubus_dev* ndev = NULL;
+	static struct nubus_dev* ndev;
 
 	/* Find the first card that hasn't already been seen */
 	while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK,
diff -urN linux-2.4.4-pre2/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- linux-2.4.4-pre2/drivers/net/de4x5.c	Tue Mar  6 19:28:35 2001
+++ linux/drivers/net/de4x5.c	Mon Dec  3 22:34:50 2001
@@ -585,7 +585,7 @@
 #ifdef DE4X5_PARM
 static char *args = DE4X5_PARM;
 #else
-static char *args = NULL;
+static char *args;
 #endif
 
 struct parameters {
@@ -666,7 +666,7 @@
 #define DESC_ALIGN
 
 #ifndef DEC_ONLY                        /* See README.de4x5 for using this */
-static int dec_only = 0;
+static int dec_only;
 #else
 static int dec_only = 1;
 #endif
@@ -1039,24 +1039,24 @@
 MODULE_PARM(dec_only, "i");
 MODULE_PARM(args, "s");
 # else
-static int loading_module = 0;
+static int loading_module;
 #endif /* MODULE */
 
 static char name[DE4X5_NAME_LENGTH + 1];
 #if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__)
 static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
-static int lastEISA = 0;
+static int lastEISA;
 #  ifdef DE4X5_FORCE_EISA                 /* Force an EISA bus probe or not */
 static int forceEISA = 1;
 #  else
-static int forceEISA = 0;
+static int forceEISA;
 #  endif
 #endif
-static int num_de4x5s = 0;
-static int cfrv = 0, useSROM = 0;
+static int num_de4x5s;
+static int cfrv, useSROM;
 static int lastPCI = -1;
-static struct net_device *lastModule = NULL;
-static struct pci_dev *pdev = NULL;
+static struct net_device *lastModule;
+static struct pci_dev *pdev;
 
 /*
 ** List the SROM infoleaf functions and chipsets
diff -urN linux-2.4.4-pre2/drivers/net/de600.c linux/drivers/net/de600.c
--- linux-2.4.4-pre2/drivers/net/de600.c	Sat Mar  3 10:55:47 2001
+++ linux/drivers/net/de600.c	Mon Dec  3 22:34:50 2001
@@ -265,14 +265,14 @@
 /*
  * D-Link driver variables:
  */
-static volatile int		rx_page		= 0;
+static volatile int		rx_page;
 
 #define TX_PAGES 2
 static volatile int		tx_fifo[TX_PAGES];
-static volatile int		tx_fifo_in = 0;
-static volatile int		tx_fifo_out = 0;
+static volatile int		tx_fifo_in;
+static volatile int		tx_fifo_out;
 static volatile int		free_tx_pages = TX_PAGES;
-static int			was_down = 0;
+static int			was_down;
 
 /*
  * Convenience macros/functions for D-Link adapter
diff -urN linux-2.4.4-pre2/drivers/net/de620.c linux/drivers/net/de620.c
--- linux-2.4.4-pre2/drivers/net/de620.c	Tue Mar  6 19:28:34 2001
+++ linux/drivers/net/de620.c	Mon Dec  3 22:34:50 2001
@@ -183,8 +183,8 @@
  * Make a clone skip the Ethernet-address range check:
  *	insmod de620.o clone=1
  */
-static int bnc = 0;
-static int utp = 0;
+static int bnc;
+static int utp;
 static int io  = DE620_IO;
 static int irq = DE620_IRQ;
 static int clone = DE620_CLONE;
@@ -722,7 +722,7 @@
 static int adapter_init(struct net_device *dev)
 {
 	int i;
-	static int was_down = 0;
+	static int was_down;
 
 	if ((nic_data.Model == 3) || (nic_data.Model == 0)) { /* CT */
 		EIPRegister = NCTL0;
diff -urN linux-2.4.4-pre2/drivers/net/declance.c linux/drivers/net/declance.c
--- linux-2.4.4-pre2/drivers/net/declance.c	Tue Mar 20 12:05:00 2001
+++ linux/drivers/net/declance.c	Mon Dec  3 22:34:50 2001
@@ -1217,7 +1217,7 @@
 /* Find all the lance cards on the system and initialize them */
 static int __init dec_lance_probe(void)
 {
-	static int called = 0;
+	static int called;
 
 #ifdef MODULE
 	root_lance_dev = NULL;
diff -urN linux-2.4.4-pre2/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- linux-2.4.4-pre2/drivers/net/dgrs.c	Tue Mar  6 19:28:35 2001
+++ linux/drivers/net/dgrs.c	Mon Dec  3 22:34:50 2001
@@ -174,7 +174,7 @@
 static uchar	dgrs_ipaddr[4] = { 0xff, 0xff, 0xff, 0xff};
 static uchar	dgrs_iptrap[4] = { 0xff, 0xff, 0xff, 0xff};
 static __u32	dgrs_ipxnet = -1;
-static int	dgrs_nicmode = 0;
+static int	dgrs_nicmode;
 
 /*
  *	Chain of device structures
diff -urN linux-2.4.4-pre2/drivers/net/e2100.c linux/drivers/net/e2100.c
--- linux-2.4.4-pre2/drivers/net/e2100.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/e2100.c	Mon Dec  3 22:34:50 2001
@@ -140,7 +140,7 @@
 {
 	int i, status, retval;
 	unsigned char *station_addr = dev->dev_addr;
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 
 	if (!request_region(ioaddr, E21_IO_EXTENT, dev->name))
 		return -EBUSY;
diff -urN linux-2.4.4-pre2/drivers/net/ethertap.c linux/drivers/net/ethertap.c
--- linux-2.4.4-pre2/drivers/net/ethertap.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/ethertap.c	Mon Dec  3 22:34:50 2001
@@ -43,7 +43,7 @@
 static void set_multicast_list(struct net_device *dev);
 #endif
 
-static int ethertap_debug = 0;
+static int ethertap_debug;
 
 static struct net_device *tap_map[32];	/* Returns the tap device for a given netlink */
 
diff -urN linux-2.4.4-pre2/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- linux-2.4.4-pre2/drivers/net/ewrk3.c	Sat Mar  3 10:55:48 2001
+++ linux/drivers/net/ewrk3.c	Mon Dec  3 22:34:50 2001
@@ -330,12 +330,12 @@
 #else
 static u_char irq[] =
 {5, 0, 10, 3, 11, 9, 15, 12};
-static int autoprobed = 0, loading_module = 0;
+static int autoprobed, loading_module;
 
 #endif				/* MODULE */
 
 static char name[EWRK3_STRLEN + 1];
-static int num_ewrk3s = 0, num_eth = 0;
+static int num_ewrk3s, num_eth;
 
 /*
    ** Miscellaneous defines...
diff -urN linux-2.4.4-pre2/drivers/net/hamachi.c linux/drivers/net/hamachi.c
--- linux-2.4.4-pre2/drivers/net/hamachi.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/hamachi.c	Mon Dec  3 22:34:50 2001
@@ -44,7 +44,7 @@
 #define hamachi_debug debug
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 40;
-static int mtu = 0;
+static int mtu;
 /* Default values selected by testing on a dual processor PIII-450 */
 /* These six interrupt control parameters may be set directly when loading the
  * module, or through the rx_params and tx_params variables
@@ -60,13 +60,13 @@
    -Setting to > 1518 causes all frames to be copied
 	-Setting to 0 disables copies
 */
-static int rx_copybreak = 0;
+static int rx_copybreak;
 
 /* An override for the hardware detection of bus width.
 	Set to 1 to force 32 bit PCI bus detection.  Set to 4 to force 64 bit.
 	Add 2 to disable parity detection.
 */
-static int force32 = 0;
+static int force32;
 
 
 /* Used to pass the media type, etc.
@@ -548,13 +548,13 @@
 static int __init hamachi_init_one (struct pci_dev *pdev,
 				    const struct pci_device_id *ent)
 {
-	static int did_version = 0;			/* Already printed version info. */
+	static int did_version;			/* Already printed version info. */
 	struct hamachi_private *hmp;
 	int option, i, rx_int_var, tx_int_var, boguscnt;
 	int chip_id = ent->driver_data;
 	int irq;
 	long ioaddr;
-	static int card_idx = 0;
+	static int card_idx;
 	struct net_device *dev;
 
 	if (hamachi_debug > 0  &&  did_version++ == 0)
diff -urN linux-2.4.4-pre2/drivers/net/hplance.c linux/drivers/net/hplance.c
--- linux-2.4.4-pre2/drivers/net/hplance.c	Tue Feb 13 13:15:04 2001
+++ linux/drivers/net/hplance.c	Mon Dec  3 22:34:50 2001
@@ -63,7 +63,7 @@
 static unsigned short hplance_readrdp(struct hplance_private *lp);
 
 #ifdef MODULE
-static struct hplance_private *root_hplance_dev = NULL;
+static struct hplance_private *root_hplance_dev;
 #endif
 
 /* Find all the HP Lance boards and initialise them... */
diff -urN linux-2.4.4-pre2/drivers/net/hydra.c linux/drivers/net/hydra.c
--- linux-2.4.4-pre2/drivers/net/hydra.c	Tue Feb 13 13:15:04 2001
+++ linux/drivers/net/hydra.c	Mon Dec  3 22:34:50 2001
@@ -60,7 +60,7 @@
 #define WORDSWAP(a)     ((((a)>>8)&0xff) | ((a)<<8))
 
 #ifdef MODULE
-static struct net_device *root_hydra_dev = NULL;
+static struct net_device *root_hydra_dev;
 #endif
 
 static int __init hydra_probe(void);
diff -urN linux-2.4.4-pre2/drivers/net/isa-skeleton.c linux/drivers/net/isa-skeleton.c
--- linux-2.4.4-pre2/drivers/net/isa-skeleton.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/isa-skeleton.c	Mon Dec  3 22:34:50 2001
@@ -163,7 +163,7 @@
 static int __init netcard_probe1(struct net_device *dev, int ioaddr)
 {
 	struct net_local *np;
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	int i;
 
 	/*
diff -urN linux-2.4.4-pre2/drivers/net/jazzsonic.c linux/drivers/net/jazzsonic.c
--- linux-2.4.4-pre2/drivers/net/jazzsonic.c	Sat Mar  3 10:55:47 2001
+++ linux/drivers/net/jazzsonic.c	Mon Dec  3 22:34:50 2001
@@ -118,7 +118,7 @@
 static int __init sonic_probe1(struct net_device *dev,
                                unsigned int base_addr, unsigned int irq)
 {
-    static unsigned version_printed = 0;
+    static unsigned version_printed;
     unsigned int silicon_revision;
     unsigned int val;
     struct sonic_local *lp;
diff -urN linux-2.4.4-pre2/drivers/net/lance.c linux/drivers/net/lance.c
--- linux-2.4.4-pre2/drivers/net/lance.c	Tue Mar  6 19:28:35 2001
+++ linux/drivers/net/lance.c	Mon Dec  3 22:34:50 2001
@@ -389,9 +389,9 @@
 	int i, reset_val, lance_version;
 	const char *chipname;
 	/* Flags for specific chips or boards. */
-	unsigned char hpJ2405A = 0;			/* HP ISA adaptor */
-	int hp_builtin = 0;					/* HP on-board ethernet. */
-	static int did_version = 0;			/* Already printed version info. */
+	unsigned char hpJ2405A = 0;		/* HP ISA adaptor */
+	int hp_builtin = 0;			/* HP on-board ethernet. */
+	static int did_version;			/* Already printed version info. */
 	unsigned long flags;
 
 	/* First we look for special cases.
diff -urN linux-2.4.4-pre2/drivers/net/loopback.c linux/drivers/net/loopback.c
--- linux-2.4.4-pre2/drivers/net/loopback.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/loopback.c	Mon Dec  3 22:34:50 2001
@@ -105,7 +105,7 @@
 /* Initialize the rest of the LOOPBACK device. */
 int __init loopback_init(struct net_device *dev)
 {
-	dev->mtu		= PAGE_SIZE - LOOPBACK_OVERHEAD;
+	dev->mtu		= (16 * 1024) + 20 + 20 + 12;
 	dev->hard_start_xmit	= loopback_xmit;
 	dev->hard_header	= eth_header;
 	dev->hard_header_cache	= eth_header_cache;
@@ -116,14 +116,12 @@
 	dev->type		= ARPHRD_LOOPBACK;	/* 0x0001		*/
 	dev->rebuild_header	= eth_rebuild_header;
 	dev->flags		= IFF_LOOPBACK;
+	dev->features		= NETIF_F_SG|NETIF_F_FRAGLIST|NETIF_F_NO_CSUM|NETIF_F_HIGHDMA;
 	dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
 	if (dev->priv == NULL)
 			return -ENOMEM;
 	memset(dev->priv, 0, sizeof(struct net_device_stats));
 	dev->get_stats = get_stats;
-
-	if (num_physpages >= ((128*1024*1024)>>PAGE_SHIFT))
-		dev->mtu = 4096*4 - LOOPBACK_OVERHEAD;
 
 	/*
 	 *	Fill in the generic fields of the device structure. 
diff -urN linux-2.4.4-pre2/drivers/net/mace.c linux/drivers/net/mace.c
--- linux-2.4.4-pre2/drivers/net/mace.c	Tue Mar 20 12:05:01 2001
+++ linux/drivers/net/mace.c	Mon Dec  3 22:34:50 2001
@@ -20,7 +20,7 @@
 #include <asm/pgtable.h>
 #include "mace.h"
 
-static struct net_device *mace_devs = NULL;
+static struct net_device *mace_devs;
 
 #define N_RX_RING	8
 #define N_TX_RING	6
diff -urN linux-2.4.4-pre2/drivers/net/macmace.c linux/drivers/net/macmace.c
--- linux-2.4.4-pre2/drivers/net/macmace.c	Sat Mar  3 10:55:47 2001
+++ linux/drivers/net/macmace.c	Mon Dec  3 22:34:50 2001
@@ -200,7 +200,7 @@
 int mace68k_probe(struct net_device *unused)
 {
 	int j;
-	static int once=0;
+	static int once;
 	struct mace68k_data *mp;
 	unsigned char *addr;
 	struct net_device *dev;
diff -urN linux-2.4.4-pre2/drivers/net/mvme147.c linux/drivers/net/mvme147.c
--- linux-2.4.4-pre2/drivers/net/mvme147.c	Tue Feb 13 13:15:04 2001
+++ linux/drivers/net/mvme147.c	Mon Dec  3 22:34:50 2001
@@ -66,13 +66,13 @@
 typedef unsigned short (*readrdp_t)(void *);
 
 #ifdef MODULE
-static struct m147lance_private *root_m147lance_dev = NULL;
+static struct m147lance_private *root_m147lance_dev;
 #endif
 
 /* Initialise the one and only on-board 7990 */
 int __init mvme147lance_probe(struct net_device *dev)
 {
-	static int called = 0;
+	static int called;
 	static const char name[] = "MVME147 LANCE";
 	struct m147lance_private *lp;
 	u_long *addr;
diff -urN linux-2.4.4-pre2/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- linux-2.4.4-pre2/drivers/net/myri_sbus.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/myri_sbus.c	Mon Dec  3 22:34:50 2001
@@ -89,7 +89,7 @@
 #endif
 
 #ifdef MODULE
-static struct myri_eth *root_myri_dev = NULL;
+static struct myri_eth *root_myri_dev;
 #endif
 
 static void myri_reset_off(unsigned long lp, unsigned long cregs)
@@ -886,7 +886,7 @@
 
 static int __init myri_ether_init(struct net_device *dev, struct sbus_dev *sdev, int num)
 {
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	struct myri_eth *mp;
 	unsigned char prop_buf[32];
 	int i;
@@ -1109,7 +1109,7 @@
 	struct net_device *dev = NULL;
 	struct sbus_bus *bus;
 	struct sbus_dev *sdev = 0;
-	static int called = 0;
+	static int called;
 	int cards = 0, v;
 
 #ifdef MODULE
diff -urN linux-2.4.4-pre2/drivers/net/natsemi.c linux/drivers/net/natsemi.c
--- linux-2.4.4-pre2/drivers/net/natsemi.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/natsemi.c	Mon Dec  3 22:34:50 2001
@@ -64,14 +64,14 @@
 static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
-static int mtu = 0;
+static int mtu;
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    This chip uses a 512 element hash table based on the Ethernet CRC.  */
 static int multicast_filter_limit = 100;
 
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1518 effectively disables this feature. */
-static int rx_copybreak = 0;
+static int rx_copybreak;
 
 /* Used to pass the media type, etc.
    Both 'options[]' and 'full_duplex[]' should exist for driver
diff -urN linux-2.4.4-pre2/drivers/net/ne.c linux/drivers/net/ne.c
--- linux-2.4.4-pre2/drivers/net/ne.c	Tue Mar  6 19:28:34 2001
+++ linux/drivers/net/ne.c	Mon Dec  3 22:34:50 2001
@@ -238,7 +238,7 @@
 	int start_page, stop_page;
 	int neX000, ctron, copam, bad_card;
 	int reg0, ret;
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 
 	if (!request_region(ioaddr, NE_IO_EXTENT, dev->name))
 		return -EBUSY;
diff -urN linux-2.4.4-pre2/drivers/net/net_init.c linux/drivers/net/net_init.c
--- linux-2.4.4-pre2/drivers/net/net_init.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/net_init.c	Mon Dec  3 22:34:50 2001
@@ -171,6 +171,7 @@
 	return dev;
 }
 
+#if defined(CONFIG_HIPPI) || defined(CONFIG_TR) || defined(CONFIG_NET_FC)
 static int __register_netdev(struct net_device *dev)
 {
 	dev_init_buffers(dev);
@@ -181,6 +182,7 @@
 	}
 	return 0;
 }
+#endif
 
 /**
  * init_etherdev - Register ethernet device
diff -urN linux-2.4.4-pre2/drivers/net/ni5010.c linux/drivers/net/ni5010.c
--- linux-2.4.4-pre2/drivers/net/ni5010.c	Tue Mar  6 19:28:34 2001
+++ linux/drivers/net/ni5010.c	Mon Dec  3 22:34:50 2001
@@ -74,7 +74,7 @@
 	"ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n";
 	
 /* bufsize_rcv == 0 means autoprobing */
-static unsigned int bufsize_rcv = 0;
+static unsigned int bufsize_rcv;
 
 #define jumpered_interrupts	/* IRQ line jumpered on board */
 #undef jumpered_dma		/* No DMA used */
@@ -186,7 +186,7 @@
 
 static int __init ni5010_probe1(struct net_device *dev, int ioaddr)
 {
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	int i;
 	unsigned int data = 0;
 	int boguscount = 40;
diff -urN linux-2.4.4-pre2/drivers/net/ni52.c linux/drivers/net/ni52.c
--- linux-2.4.4-pre2/drivers/net/ni52.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/ni52.c	Mon Dec  3 22:34:50 2001
@@ -99,9 +99,9 @@
  * < 30.Sep.93: first versions
  */
 
-static int debuglevel = 0; /* debug-printk 0: off 1: a few 2: more */
-static int automatic_resume = 0; /* experimental .. better should be zero */
-static int rfdadd = 0; /* rfdadd=1 may be better for 8K MEM cards */
+static int debuglevel;	/* debug-printk 0: off 1: a few 2: more */
+static int automatic_resume; /* experimental .. better should be zero */
+static int rfdadd;	/* rfdadd=1 may be better for 8K MEM cards */
 static int fifo=0x8;	/* don't change */
 
 /* #define REALLY_SLOW_IO */
@@ -1289,8 +1289,8 @@
 /* set: io,irq,memstart,memend or set it when calling insmod */
 static int irq=9;
 static int io=0x300;
-static long memstart=0; /* e.g 0xd0000 */
-static long memend=0;	 /* e.g 0xd4000 */
+static long memstart;	/* e.g 0xd0000 */
+static long memend;	/* e.g 0xd4000 */
 
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
diff -urN linux-2.4.4-pre2/drivers/net/pci-skeleton.c linux/drivers/net/pci-skeleton.c
--- linux-2.4.4-pre2/drivers/net/pci-skeleton.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/net/pci-skeleton.c	Mon Dec  3 22:34:50 2001
@@ -740,7 +740,7 @@
 	int i, addr_len, option;
 	void *ioaddr = NULL;
 	static int board_idx = -1;
-	static int printed_version = 0;
+	static int printed_version;
 	u8 tmp;
 
 	DPRINTK ("ENTER\n");
diff -urN linux-2.4.4-pre2/drivers/net/pcmcia/hermes.c linux/drivers/net/pcmcia/hermes.c
--- linux-2.4.4-pre2/drivers/net/pcmcia/hermes.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/pcmcia/hermes.c	Mon Dec  3 22:34:50 2001
@@ -19,7 +19,6 @@
 
 static const char *version = "hermes.c: 12 Dec 2000 David Gibson <hermes@gibson.dropbear.id.au>";
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/smp.h>
diff -urN linux-2.4.4-pre2/drivers/net/plip.c linux/drivers/net/plip.c
--- linux-2.4.4-pre2/drivers/net/plip.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/plip.c	Mon Dec  3 22:34:50 2001
@@ -1293,7 +1293,7 @@
 }
 
 static int parport[PLIP_MAX] = { [0 ... PLIP_MAX-1] = -1 };
-static int timid = 0;
+static int timid;
 
 MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i");
 MODULE_PARM(timid, "1i");
@@ -1314,7 +1314,7 @@
  * available to use. */
 static void plip_attach (struct parport *port)
 {
-	static int i = 0;
+	static int i;
 
 	if ((parport[0] == -1 && (!timid || !port->devices)) || 
 	    plip_searchfor(parport, port->number)) {
@@ -1377,7 +1377,7 @@
 
 #ifndef MODULE
 
-static int parport_ptr = 0;
+static int parport_ptr;
 
 static int __init plip_setup(char *str)
 {
diff -urN linux-2.4.4-pre2/drivers/net/ptifddi.c linux/drivers/net/ptifddi.c
--- linux-2.4.4-pre2/drivers/net/ptifddi.c	Tue Jul 11 15:46:08 2000
+++ linux/drivers/net/ptifddi.c	Mon Dec  3 22:34:50 2001
@@ -16,7 +16,7 @@
 #include "ptifddi_asm.h"
 
 #ifdef MODULE
-static struct ptifddi *root_pti_dev = NULL;
+static struct ptifddi *root_pti_dev;
 #endif
 
 static inline void pti_reset(struct ptifddi *pp)
@@ -151,7 +151,7 @@
 
 static inline int pti_fddi_init(struct net_device *dev, struct sbus_dev *sdev, int num)
 {
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	struct ptifddi *pp;
 	int i;
 
@@ -208,7 +208,7 @@
 {
 	struct sbus_bus *bus;
 	struct sbus_dev *sdev = 0;
-	static int called = 0;
+	static int called;
 	int cards = 0, v;
 
 	if(called)
diff -urN linux-2.4.4-pre2/drivers/net/rcpci45.c linux/drivers/net/rcpci45.c
--- linux-2.4.4-pre2/drivers/net/rcpci45.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/rcpci45.c	Mon Dec  3 22:34:50 2001
@@ -91,7 +91,7 @@
 #define DEFAULT_RECV_INIT_CONTEXT  0xA17  
 
 
-static U32 DriverControlWord =  0;
+static U32 DriverControlWord;
 
 static void rc_timer(unsigned long);
 
diff -urN linux-2.4.4-pre2/drivers/net/sgiseeq.c linux/drivers/net/sgiseeq.c
--- linux-2.4.4-pre2/drivers/net/sgiseeq.c	Tue Mar  6 19:28:35 2001
+++ linux/drivers/net/sgiseeq.c	Mon Dec  3 22:34:50 2001
@@ -206,7 +206,7 @@
 
 void sgiseeq_dump_rings(void)
 {
-	static int once = 0;
+	static int once;
 	struct sgiseeq_rx_desc *r = gpriv->srings.rx_desc;
 	struct sgiseeq_tx_desc *t = gpriv->srings.tx_desc;
 	volatile struct hpc3_ethregs *hregs = gpriv->hregs;
@@ -611,7 +611,7 @@
 int sgiseeq_init(struct net_device *dev, struct sgiseeq_regs *sregs,
 		 struct hpc3_ethregs *hregs, int irq)
 {
-	static unsigned version_printed = 0;
+	static unsigned version_printed;
 	int i;
 	struct sgiseeq_private *sp;
 
@@ -704,7 +704,7 @@
 
 int sgiseeq_probe(struct net_device *dev)
 {
-	static int initialized = 0;
+	static int initialized;
 	char *ep;
 
 	if (initialized)	/* Already initialized? */
diff -urN linux-2.4.4-pre2/drivers/net/sis900.c linux/drivers/net/sis900.c
--- linux-2.4.4-pre2/drivers/net/sis900.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/net/sis900.c	Mon Dec  3 22:34:50 2001
@@ -1,11 +1,11 @@
 /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
    Copyright 1999 Silicon Integrated System Corporation 
-   Revision:	1.07.08	Jan. 8 2001
+   Revision:	1.07.11	Apr. 10 2001
    
    Modified from the driver which is originally written by Donald Becker.
    
    This software may be used and distributed according to the terms
-   of the GNU General Public License (GPL), incorporated herein by reference.
+   of the GNU Public License (GPL), incorporated herein by reference.
    Drivers based on this skeleton fall under the GPL and must retain
    the authorship (implicit copyright) notice.
    
@@ -18,6 +18,8 @@
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm
 
+   Rev 1.07.11 Apr.  2 2001 Hui-Fen Hsu  updates PCI drivers to use the new pci_set_dma_mask for kernel 2.4.3
+   Rev 1.07.10 Mar.  1 2001 Hui-Fen Hsu <hfhsu@sis.com.tw> some bug fix & 635M/B support 
    Rev 1.07.09 Feb.  9 2001 Dave Jones <davej@suse.de> PCI enable cleanup
    Rev 1.07.08 Jan.  8 2001 Lei-Chun Chang added RTL8201 PHY support
    Rev 1.07.07 Nov. 29 2000 Lei-Chun Chang added kernel-doc extractable documentation and 630 workaround fix
@@ -45,7 +47,7 @@
 #include <linux/timer.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
-#include <linux/slab.h>
+#include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/netdevice.h>
@@ -61,9 +63,9 @@
 #include "sis900.h"
 
 static const char *version =
-"sis900.c: v1.07.09  2/9/2001\n";
+"sis900.c: v1.07.11  4/10/2001\n";
 
-static int max_interrupt_work = 20;
+static int max_interrupt_work = 40;
 static int multicast_filter_limit = 128;
 
 #define sis900_debug debug
@@ -91,31 +93,33 @@
 };
 MODULE_DEVICE_TABLE (pci, sis900_pci_tbl);
 
-static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
-static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
-static void ics1893_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
-static void rtl8201_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
+static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex);
 
 static struct mii_chip_info {
 	const char * name;
 	u16 phy_id0;
 	u16 phy_id1;
-	void (*read_mode) (struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
+	u8  phy_types;
+#define	HOME 	0x0001
+#define LAN	0x0002
+#define MIX	0x0003
 } mii_chip_table[] = {
-	{"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
-	{"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
-	{"AMD 79C901 10BASE-T PHY",  0x0000, 0x35b9, amd79c901_read_mode},
-	{"AMD 79C901 HomePNA PHY",   0x0000, 0x35c8, amd79c901_read_mode},
-	{"ICS 1893 Integrated PHYceiver"   , 0x0015, 0xf441,ics1893_read_mode},
-	{"RTL 8201 10/100Mbps Phyceiver"   , 0x0000, 0x8201,rtl8201_read_mode},
+	{ "SiS 900 Internal MII PHY", 		0x001d, 0x8000, LAN },
+	{ "SiS 7014 Physical Layer Solution", 	0x0016, 0xf830, LAN },
+	{ "AMD 79C901 10BASE-T PHY",  		0x0000, 0x6B70, LAN },
+	{ "AMD 79C901 HomePNA PHY",		0x0000, 0x6B90, HOME},
+	{ "ICS LAN PHY",			0x0015, 0xF440, LAN },
+	{ "NS 83851 PHY",			0x2000, 0x5C20, MIX },
 	{0,},
 };
 
 struct mii_phy {
 	struct mii_phy * next;
-	struct mii_chip_info * chip_info;
 	int phy_addr;
+	u16 phy_id0;
+	u16 phy_id1;
 	u16 status;
+	u8  phy_types;
 };
 
 typedef struct _BufferDesc {
@@ -131,11 +135,13 @@
 	spinlock_t lock;
 
 	struct mii_phy * mii;
+	struct mii_phy * first_mii; /* record the first mii structure */
 	unsigned int cur_phy;
 
-	struct timer_list timer;			/* Link status detection timer. */
+	struct timer_list timer; /* Link status detection timer. */
+	u8     autong_complete; /* 1: auto-negotiate complete  */
 
-	unsigned int cur_rx, dirty_rx;		/* producer/comsumer pointers for Tx/Rx ring */
+	unsigned int cur_rx, dirty_rx; /* producer/comsumer pointers for Tx/Rx ring */
 	unsigned int cur_tx, dirty_tx;
 
 	/* The saved address of a sent/receive-in-place packet buffer */
@@ -171,11 +177,16 @@
 static int sis900_close(struct net_device *net_dev);
 static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd);
 static struct net_device_stats *sis900_get_stats(struct net_device *net_dev);
-static u16 sis900_compute_hashtable_index(u8 *addr);
+static u16 sis900_compute_hashtable_index(u8 *addr, u8 revision);
 static void set_rx_mode(struct net_device *net_dev);
 static void sis900_reset(struct net_device *net_dev);
 static void sis630_set_eq(struct net_device *net_dev, u8 revision);
 static int sis900_set_config(struct net_device *dev, struct ifmap *map);
+static u16 sis900_default_phy(struct net_device * net_dev);
+static void sis900_set_capability( struct net_device *net_dev ,struct mii_phy *phy);
+static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr);
+static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr);
+static void sis900_set_mode (long ioaddr, int speed, int duplex);
 
 /**
  *	sis900_get_mac_addr: - Get MAC address for stand alone SiS900 model
@@ -239,6 +250,44 @@
 	return 1;
 }
 
+
+/**
+ *	sis635_get_mac_addr: - Get MAC address for SIS635 model
+ *	@pci_dev: the sis900 pci device
+ *	@net_dev: the net device to get address for 
+ *
+ *	SiS635 model, set MAC Reload Bit to load Mac address from APC
+ *	to rfdr. rfdr is accessed through rfcr. MAC address is read into 
+ *	@net_dev->dev_addr.
+ */
+
+static int __devinit sis635_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev)
+{
+	long ioaddr = net_dev->base_addr;
+	u32 rfcrSave;
+	u32 i;
+
+	rfcrSave = inl(rfcr + ioaddr);
+
+	outl(rfcrSave | RELOAD, ioaddr + cr);
+	outl(0, ioaddr + cr);
+
+	/* disable packet filtering before setting filter */
+	outl(rfcrSave & ~RFEN, rfcr + ioaddr);
+
+	/* load MAC addr to filter data register */
+	for (i = 0 ; i < 3 ; i++) {
+		outl((i << RFADDR_shift), ioaddr + rfcr);
+		*( ((u16 *)net_dev->dev_addr) + i) = inw(ioaddr + rfdr);
+	}
+
+	/* enable packet filitering */
+	outl(rfcrSave | RFEN, rfcr + ioaddr);
+
+	return 1;
+}
+
+
 /**
  *	sis900_probe: - Probe for sis900 device
  *	@pci_dev: the sis900 pci device
@@ -253,64 +302,41 @@
 static int __devinit sis900_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
 {
 	struct sis900_private *sis_priv;
-	long ioaddr;
 	struct net_device *net_dev;
-	int irq;
+	long ioaddr;
 	int i, ret;
 	u8 revision;
 	char *card_name = card_names[pci_id->driver_data];
 
 	/* setup various bits in PCI command register */
-	ret = pci_enable_device (pci_dev);
-	if (ret) return ret;
-
+	ret = pci_enable_device(pci_dev);
+	if(ret) return ret;
+	
 	i = pci_set_dma_mask(pci_dev, SIS900_DMA_MASK);
-	if (i) {
-		printk(KERN_ERR "sis900.c: architecture does not support "
-		       "32bit PCI busmaster DMA\n");
+	if(i){
+		printk(KERN_ERR "sis900.c: architecture does not support"
+			"32bit PCI busmaster DMA\n");
 		return i;
 	}
-
+	
 	pci_set_master(pci_dev);
-
-	irq = pci_dev->irq;
-	ioaddr = pci_resource_start(pci_dev, 0);
-
+	
 	net_dev = alloc_etherdev(sizeof(struct sis900_private));
 	if (!net_dev)
 		return -ENOMEM;
 	SET_MODULE_OWNER(net_dev);
 
+	/* We do a request_region() to register /proc/ioports info. */
+	ioaddr = pci_resource_start(pci_dev, 0);	
 	ret = pci_request_regions(pci_dev, "sis900");
 	if (ret)
 		goto err_out;
 
-	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
-	if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV)
-		ret = sis630e_get_mac_addr(pci_dev, net_dev);
-	else if (revision == SIS630S_900_REV)
-		ret = sis630e_get_mac_addr(pci_dev, net_dev);
-	else
-		ret = sis900_get_mac_addr(pci_dev, net_dev);
-
-	if (ret == 0) {
-		ret = -ENODEV;
-		goto err_out_region;
-	}
-
 	sis_priv = net_dev->priv;
-
-	/* We do a request_region() to register /proc/ioports info. */
 	net_dev->base_addr = ioaddr;
-	net_dev->irq = irq;
+	net_dev->irq = pci_dev->irq;
 	sis_priv->pci_dev = pci_dev;
 	spin_lock_init(&sis_priv->lock);
-	
-	/* probe for mii transciver */
-	if (sis900_mii_probe(net_dev) == 0) {
-		ret = -ENODEV;
-		goto err_out_region;
-	}
 
 	pci_set_drvdata(pci_dev, net_dev);
 
@@ -324,25 +350,47 @@
 	net_dev->do_ioctl = &mii_ioctl;
 	net_dev->tx_timeout = sis900_tx_timeout;
 	net_dev->watchdog_timeo = TX_TIMEOUT;
-
+	
 	ret = register_netdev(net_dev);
 	if (ret)
 		goto err_out_cleardev;
+		
+	/* Get Mac address according to the chip revision */
+	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
+	ret = 0;
+
+	if (revision == SIS630E_900_REV)
+		ret = sis630e_get_mac_addr(pci_dev, net_dev);
+	else if ((revision > 0x81) && (revision <= 0x90) )
+		ret = sis635_get_mac_addr(pci_dev, net_dev);
+	else
+		ret = sis900_get_mac_addr(pci_dev, net_dev);
+
+	if (ret == 0) {
+		ret = -ENODEV;
+		goto err_out_region;
+	}
+
+	/* probe for mii transciver */
+	if (sis900_mii_probe(net_dev) == 0) {
+		ret = -ENODEV;
+		goto err_out_region;
+	}
 
 	/* print some information about our NIC */
 	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name,
-	       card_name, ioaddr, irq);
+	       card_name, ioaddr, net_dev->irq);
 	for (i = 0; i < 5; i++)
 		printk("%2.2x:", (u8)net_dev->dev_addr[i]);
 	printk("%2.2x.\n", net_dev->dev_addr[i]);
 
 	return 0;
 
-err_out_cleardev:
-	pci_set_drvdata(pci_dev, NULL);
-err_out_region:
+ err_out_cleardev:
+ 	pci_set_drvdata(pci_dev, NULL);
+ err_out_region:
 	pci_release_regions(pci_dev);
-err_out:
+ err_out:
 	kfree(net_dev);
 	return ret;
 }
@@ -350,7 +398,7 @@
 /**
  *	sis900_mii_probe: - Probe MII PHY for sis900
  *	@net_dev: the net device to probe for
- *
+ *	
  *	Search for total of 32 possible mii phy addresses.
  *	Identify and set current phy if found one,
  *	return error if it failed to found.
@@ -359,59 +407,83 @@
 static int __init sis900_mii_probe (struct net_device * net_dev)
 {
 	struct sis900_private * sis_priv = net_dev->priv;
+	u16 poll_bit = MII_STAT_LINK, status = 0;
+	unsigned int timeout = jiffies + 5 * HZ;
 	int phy_addr;
 	u8 revision;
 
 	sis_priv->mii = NULL;
 
 	/* search for total of 32 possible mii phy addresses */
-	for (phy_addr = 0; phy_addr < 32; phy_addr++) {
+	for (phy_addr = 0; phy_addr < 32; phy_addr++) {	
+		struct mii_phy * mii_phy = NULL;
 		u16 mii_status;
-		u16 phy_id0, phy_id1;
 		int i;
 
-		mii_status = mdio_read(net_dev, phy_addr, MII_STATUS);
+		mii_phy = NULL;
+		for(i = 0; i < 2; i++)
+			mii_status = mdio_read(net_dev, phy_addr, MII_STATUS);
+
 		if (mii_status == 0xffff || mii_status == 0x0000)
 			/* the mii is not accessable, try next one */
 			continue;
+		
+		if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) == NULL) {
+			printk(KERN_INFO "Cannot allocate mem for struct mii_phy\n");
+			return 0;
+		}
+		
+		mii_phy->phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0);
+		mii_phy->phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);		
+		mii_phy->phy_addr = phy_addr;
+		mii_phy->status = mii_status;
+		mii_phy->next = sis_priv->mii;
+		sis_priv->mii = mii_phy;
+		sis_priv->first_mii = mii_phy;
 
-		phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0);
-		phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);
-
-		/* search our mii table for the current mii */ 
 		for (i = 0; mii_chip_table[i].phy_id1; i++)
-			if (phy_id0 == mii_chip_table[i].phy_id0 &&
-			    phy_id1 == mii_chip_table[i].phy_id1) {
-				struct mii_phy * mii_phy;
-
-				printk(KERN_INFO
-				       "%s: %s transceiver found at address %d.\n",
-				       net_dev->name, mii_chip_table[i].name,
-				       phy_addr);
-				if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) {
-					mii_phy->chip_info = mii_chip_table+i;
-					mii_phy->phy_addr = phy_addr;
-					mii_phy->status = mdio_read(net_dev, phy_addr,
-								    MII_STATUS);
-					mii_phy->next = sis_priv->mii;
-					sis_priv->mii = mii_phy;
-				}
-				/* the current mii is on our mii_info_table,
-				   try next address */
+			if ((mii_phy->phy_id0 == mii_chip_table[i].phy_id0 ) &&
+			    ((mii_phy->phy_id1 & 0xFFF0) == mii_chip_table[i].phy_id1)){
+				mii_phy->phy_types = mii_chip_table[i].phy_types;
+				if (mii_chip_table[i].phy_types == MIX)
+					mii_phy->phy_types =
+						(mii_status & (MII_STAT_CAN_TX_FDX | MII_STAT_CAN_TX)) ? LAN : HOME;
+				printk(KERN_INFO "%s: %s transceiver found at address %d.\n",
+				       net_dev->name, mii_chip_table[i].name, phy_addr);
 				break;
 			}
+			
+		if( !mii_chip_table[i].phy_id1 )
+			printk(KERN_INFO "%s: Unknown PHY transceiver found at address %d.\n",
+			       net_dev->name, phy_addr);			
 	}
-
+	
 	if (sis_priv->mii == NULL) {
 		printk(KERN_INFO "%s: No MII transceivers found!\n",
 		       net_dev->name);
 		return 0;
 	}
 
-	/* arbitrary choose that last PHY as current PHY */
-	sis_priv->cur_phy = sis_priv->mii->phy_addr;
-	printk(KERN_INFO "%s: Using %s as default\n", net_dev->name,
-	       sis_priv->mii->chip_info->name);
+	/* select default PHY for mac */
+	sis_priv->mii = NULL;
+	sis900_default_phy( net_dev );
+
+	/* Reset phy if default phy is internal sis900 */
+        if ((sis_priv->mii->phy_id0 == 0x001D) &&
+	    ((sis_priv->mii->phy_id1&0xFFF0) == 0x8000))
+        	status = sis900_reset_phy(net_dev, sis_priv->cur_phy);
+
+	if(status & MII_STAT_LINK){
+		while (poll_bit) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(0);
+			poll_bit ^= (mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS) & poll_bit);
+			if (jiffies >= timeout) {
+				printk(KERN_WARNING "%s: reset phy and link down now\n", net_dev->name);
+				return -ETIME;
+			}
+		}
+	}
 
 	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
 	if (revision == SIS630E_900_REV) {
@@ -420,7 +492,7 @@
 		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG1, 0x22);
 		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG2, 0xff00);
 		mdio_write(net_dev, sis_priv->cur_phy, MII_MASK, 0xffc0);
-		mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, 0x1000);	
+		//mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, 0x1000);	
 	}
 
 	if (sis_priv->mii->status & MII_STAT_LINK)
@@ -431,6 +503,86 @@
 	return 1;
 }
 
+/**
+ *	sis900_default_phy: - Select default PHY for sis900 mac.
+ *	@net_dev: the net device to probe for
+ *
+ *	Select first detected PHY with link as default.
+ *	If no one is link on, select PHY whose types is HOME as default.
+ *	If HOME doesn't exist, select LAN.
+ */
+
+static u16 sis900_default_phy(struct net_device * net_dev)
+{
+	struct sis900_private * sis_priv = net_dev->priv;
+ 	struct mii_phy *phy = NULL, *phy_home = NULL, *default_phy = NULL;
+	u16 status;
+
+        for( phy=sis_priv->first_mii; phy; phy=phy->next ){
+		status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+		status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+
+		/* Link ON & Not select deafalut PHY */
+		 if ( (status & MII_STAT_LINK) && !(default_phy) )
+		 	default_phy = phy;
+		 else{
+			status = mdio_read(net_dev, phy->phy_addr, MII_CONTROL);
+			mdio_write(net_dev, phy->phy_addr, MII_CONTROL,
+				status | MII_CNTL_AUTO | MII_CNTL_ISOLATE);
+			if( phy->phy_types == HOME )
+				phy_home = phy;
+		 }
+	}
+
+	if( (!default_phy) && phy_home )
+		default_phy = phy_home;
+	else if(!default_phy)
+		default_phy = sis_priv->first_mii;
+
+	if( sis_priv->mii != default_phy ){
+		sis_priv->mii = default_phy;
+		sis_priv->cur_phy = default_phy->phy_addr;
+		printk(KERN_INFO "%s: Using transceiver found at address %d as default\n", net_dev->name,sis_priv->cur_phy);
+	}
+	
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
+	status &= (~MII_CNTL_ISOLATE);
+
+	mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, status);	
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+
+	return status;	
+}
+
+
+/**
+ * 	sis900_set_capability: - set the media capability of network adapter.
+ *	@net_dev : the net device to probe for
+ *	@mii_phy : default PHY
+ *
+ *	Set the media capability of network adapter according to
+ *	mii status register. It's necessary before auto-negotiate.
+ */
+ 
+static void sis900_set_capability( struct net_device *net_dev , struct mii_phy *phy )
+{
+	u16 cap;
+	u16 status;
+	
+	status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+	status = mdio_read(net_dev, phy->phy_addr, MII_STATUS);
+	
+	cap = MII_NWAY_CSMA_CD |
+		((phy->status & MII_STAT_CAN_TX_FDX)? MII_NWAY_TX_FDX:0) |
+		((phy->status & MII_STAT_CAN_TX)    ? MII_NWAY_TX:0) |
+		((phy->status & MII_STAT_CAN_T_FDX) ? MII_NWAY_T_FDX:0)|
+		((phy->status & MII_STAT_CAN_T)     ? MII_NWAY_T:0);
+
+	mdio_write(net_dev, phy->phy_addr, MII_ANADV, cap);
+}
+
+
 /* Delay between EEPROM clock transitions. */
 #define eeprom_delay()  inl(ee_addr)
 
@@ -605,6 +757,30 @@
 	return;
 }
 
+
+/**
+ *	sis900_reset_phy: - reset sis900 mii phy.
+ *	@net_dev: the net device to write
+ *	@phy_addr: default phy address
+ *
+ *	Some specific phy can't work properly without reset.
+ *	This function will be called during initialization and
+ *	link status change from ON to DOWN.
+ */
+
+static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr)
+{
+	int i = 0;
+	u16 status;
+
+	while (i++ < 2)
+		status = mdio_read(net_dev, phy_addr, MII_STATUS);
+
+	mdio_write( net_dev, phy_addr, MII_CONTROL, MII_CNTL_RESET );
+	
+	return status;
+}
+
 /**
  *	sis900_open: - open sis900 device
  *	@net_dev: the net device to open
@@ -626,9 +802,7 @@
 
 	/* Equalizer workaround Rule */
 	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
-	if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
-	    revision == SIS630A_900_REV)
-		sis630_set_eq(net_dev,revision);
+	sis630_set_eq(net_dev, revision);
 
 	ret = request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev);
 	if (ret)
@@ -679,7 +853,7 @@
 	rfcrSave = inl(rfcr + ioaddr);
 
 	/* disable packet filtering before setting filter */
-	outl(rfcrSave & ~RFEN, rfcr);
+	outl(rfcrSave & ~RFEN, rfcr + ioaddr);
 
 	/* load MAC addr to filter data register */
 	for (i = 0 ; i < 3 ; i++) {
@@ -820,7 +994,11 @@
 	int i, maxcount=10;
 	struct pci_dev *dev=NULL;
 
-	if ((dev = pci_find_device(SIS630_DEVICE_ID, SIS630_VENDOR_ID, dev)))
+	if ( !(revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
+	       revision == SIS630A_900_REV) )
+		return;
+
+	if ((dev = pci_find_device(SIS630_VENDOR_ID, SIS630_DEVICE_ID, dev)))
 		pci_read_config_byte(dev, PCI_CLASS_REVISION, &host_bridge_rev);
 
 	if (netif_carrier_ok(net_dev)) {
@@ -842,13 +1020,6 @@
 			else if (max_value >= 15)
 				eq_value=(max_value == min_value) ? max_value+6 : max_value+5;
 		}
-		/* 630A0 rule to determine the equalizer value */
-		if (revision == SIS630A_900_REV && host_bridge_rev == SIS630A0) {
-			if (max_value < 5)
-				eq_value=max_value+3;
-			else if (max_value >= 5)
-				eq_value=max_value+5;
-		}
 		/* 630B0&B1 rule to determine the equalizer value */
 		if (revision == SIS630A_900_REV && 
 		    (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) {
@@ -865,7 +1036,11 @@
 	}
 	else {
 		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV);
-		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF);
+		if (revision == SIS630A_900_REV && 
+		    (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) 
+			mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2200) & 0xBFFF);
+		else
+			mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF);
 	}
 	return;
 }
@@ -887,60 +1062,52 @@
 	u16 status;
 	u8 revision;
 
-	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
-	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+	if (!sis_priv->autong_complete){
+		int speed, duplex = 0;
 
-	/* current mii phy is failed to link, try another one */
-	while (!(status & MII_STAT_LINK)) {
-		if (mii_phy->next == NULL) {
-			if (netif_carrier_ok(net_dev)) {
-				/* link stat change from ON to OFF */
-				next_tick = HZ;
-				netif_carrier_off(net_dev);
-
-				/* Equalizer workaround Rule */
-				pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
-				if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
-				    revision == SIS630A_900_REV)
-					sis630_set_eq(net_dev, revision);
-
-				printk(KERN_INFO "%s: Media Link Off\n",
-				       net_dev->name);
-			}
-			sis_priv->timer.expires = jiffies + next_tick;
-			add_timer(&sis_priv->timer);
-			return;
+		sis900_read_mode(net_dev, &speed, &duplex);
+		if (duplex){
+			sis900_set_mode(net_dev->base_addr, speed, duplex);
+			pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+			sis630_set_eq(net_dev, revision);
 		}
-		mii_phy = mii_phy->next;
-		status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS);
-	}
 
-	if (!netif_carrier_ok(net_dev)) {
-		/* link stat change forn OFF to ON, read and report link mode */
-		netif_carrier_on(net_dev);
-		next_tick = 5*HZ;
+		sis_priv->timer.expires = jiffies + HZ;
+		add_timer(&sis_priv->timer);
+		return;
+	}
 
-		/* Equalizer workaround Rule */
-		pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
-		if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV ||
-		    revision == SIS630A_900_REV)
-			sis630_set_eq(net_dev, revision);
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
 
-		/* change what cur_phy means */
-		if (mii_phy->phy_addr != sis_priv->cur_phy) {
-			printk(KERN_INFO "%s: Changing transceiver to %s\n",
-			       net_dev->name, mii_phy->chip_info->name);
-			/* disable previous PHY */
-			status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
-			mdio_write(net_dev, sis_priv->cur_phy,
-				   MII_CONTROL, status | MII_CNTL_ISOLATE);
-			/* enable next PHY */
-			status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL);
-			mdio_write(net_dev, mii_phy->phy_addr,
-				   MII_CONTROL, status & ~MII_CNTL_ISOLATE);
-			sis_priv->cur_phy = mii_phy->phy_addr;
+	/* Link OFF -> ON */
+	if (!netif_carrier_ok(net_dev)) {
+	LookForLink:
+		/* Search for new PHY */
+		status = sis900_default_phy(net_dev);
+		mii_phy = sis_priv->mii;
+
+		if (status & MII_STAT_LINK){
+			sis900_check_mode(net_dev, mii_phy);
+			netif_carrier_on(net_dev);
 		}
-		sis900_check_mode(net_dev, mii_phy);
+	}
+	/* Link ON -> OFF */
+	else {
+                if (!(status & MII_STAT_LINK)){
+                	netif_carrier_off(net_dev);
+                	printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+
+                	/* Change mode issue */
+                	if ((mii_phy->phy_id0 == 0x001D) && 
+			    ((mii_phy->phy_id1 & 0xFFF0) == 0x8000))
+               			sis900_reset_phy(net_dev,  sis_priv->cur_phy);
+  
+                	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+			sis630_set_eq(net_dev, revision);
+  
+                	goto LookForLink;
+                }
 	}
 
 	sis_priv->timer.expires = jiffies + next_tick;
@@ -952,8 +1119,11 @@
  *	@net_dev: the net device to be checked
  *	@mii_phy: the mii phy
  *
- *	call mii_phy->chip_info->read_mode function
- *	to check the speed and duplex mode for sis900
+ *	Older driver gets the media mode from mii status output
+ *	register. Now we set our media capability and auto-negotiate
+ *	to get the upper bound of speed and duplex between two ends.
+ *	If the types of mii phy is HOME, it doesn't need to auto-negotiate
+ *	and autong_complete should be set to 1.
  */
 
 static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy)
@@ -961,12 +1131,44 @@
 	struct sis900_private *sis_priv = net_dev->priv;
 	long ioaddr = net_dev->base_addr;
 	int speed, duplex;
-	u32 tx_flags = 0, rx_flags = 0;
 
-	mii_phy->chip_info->read_mode(net_dev, sis_priv->cur_phy, &speed, &duplex);
+	if( mii_phy->phy_types == LAN  ){
+		outl( ~EXD & inl( ioaddr + cfg ), ioaddr + cfg);
+		sis900_set_capability(net_dev , mii_phy);
+		sis900_auto_negotiate(net_dev, sis_priv->cur_phy);
+	}else{
+		outl(EXD | inl( ioaddr + cfg ), ioaddr + cfg);
+		speed = HW_SPEED_HOME;
+		duplex = FDX_CAPABLE_HALF_SELECTED;
+		sis900_set_mode(ioaddr, speed, duplex);
+		sis_priv->autong_complete = 1;
+	}
+}
 
-	tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
-	rx_flags = RX_DMA_BURST << RxMXDMA_shift;
+/**
+ *	sis900_set_mode: - Set the media mode of mac register.
+ *	@speed : the transmit speed to be determined
+ *	@duplex: the duplex mode to be determined
+ *
+ *	Set the media mode of mac register txcfg/rxcfg according to
+ *	speed and duplex of phy. Bit EDB_MASTER_EN indicates the EDB
+ *	bus is used instead of PCI bus. When this bit is set 1, the
+ *	Max DMA Burst Size for TX/RX DMA should be no larger than 16
+ *	double words.
+ */
+
+static void sis900_set_mode (long ioaddr, int speed, int duplex)
+{
+	u32 tx_flags = 0, rx_flags = 0;
+
+	if( inl(ioaddr + cfg) & EDB_MASTER_EN ){
+		tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+		rx_flags = DMA_BURST_64 << RxMXDMA_shift;
+	}
+	else{
+		tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+		rx_flags = DMA_BURST_512 << RxMXDMA_shift;
+	}
 
 	if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) {
 		rx_flags |= (RxDRNT_10 << RxDRNT_shift);
@@ -987,184 +1189,86 @@
 }
 
 /**
- *	sis900_read_mode: - read media mode for sis900 internal phy
+ *	sis900_auto_negotiate:  Set the Auto-Negotiation Enable/Reset bit.
  *	@net_dev: the net device to read mode for
  *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
  *
- *	read MII_STSOUT register from sis900 internal phy
- *	to determine the speed and duplex mode for sis900
+ *	If the adapter is link-on, set the auto-negotiate enable/reset bit.
+ *	autong_complete should be set to 0 when starting auto-negotiation.
+ *	autong_complete should be set to 1 if we didn't start auto-negotiation.
+ *	sis900_timer will wait for link on again if autong_complete = 0.
  */
 
-static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
+static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr)
 {
+	struct sis900_private *sis_priv = net_dev->priv;
 	int i = 0;
 	u32 status;
-
-	/* STSOUT register is Latched on Transition, read operation updates it */
+	
 	while (i++ < 2)
-		status = mdio_read(net_dev, phy_addr, MII_STSOUT);
-
-	if (status & MII_STSOUT_SPD)
-		*speed = HW_SPEED_100_MBPS;
-	else
-		*speed = HW_SPEED_10_MBPS;
-
-	if (status & MII_STSOUT_DPLX)
-		*duplex = FDX_CAPABLE_FULL_SELECTED;
-	else
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
+		status = mdio_read(net_dev, phy_addr, MII_STATUS);
 
-	if (status & MII_STSOUT_LINK_FAIL)
+	if (!(status & MII_STAT_LINK)){
 		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-	else
-		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-		       net_dev->name,
-		       *speed == HW_SPEED_100_MBPS ?
-		       "100mbps" : "10mbps",
-		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-		       "full" : "half");
+		sis_priv->autong_complete = 1;
+		netif_carrier_off(net_dev);
+		return;
+	}
+
+	/* (Re)start AutoNegotiate */
+	mdio_write(net_dev, phy_addr, MII_CONTROL,
+		   MII_CNTL_AUTO | MII_CNTL_RST_AUTO);
+	sis_priv->autong_complete = 0;
 }
 
+
 /**
- *	amd79c901_read_mode: - read media mode for amd79c901 phy
+ *	sis900_read_mode: - read media mode for sis900 internal phy
  *	@net_dev: the net device to read mode for
- *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
+ *	@speed  : the transmit speed to be determined
+ *	@duplex : the duplex mode to be determined
  *
- *	read MII_STATUS register from amd79c901 phy
- *	to determine the speed and duplex mode for sis900
+ *	The capability of remote end will be put in mii register autorec
+ *	after auto-negotiation. Use AND operation to get the upper bound
+ *	of speed and duplex between two ends.
  */
 
-static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
+static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex)
 {
-	int i;
-	u16 status;
+	struct sis900_private *sis_priv = net_dev->priv;
+	int phy_addr = sis_priv->cur_phy;
+	u32 status;
+	u16 autoadv, autorec;
+	int i = 0;
 
-	for (i = 0; i < 2; i++)
+	while (i++ < 2)
 		status = mdio_read(net_dev, phy_addr, MII_STATUS);
 
-	if (status & MII_STAT_CAN_AUTO) {
-		/* 10BASE-T PHY */
-		for (i = 0; i < 2; i++)
-			status = mdio_read(net_dev, phy_addr, MII_STATUS_SUMMARY);
-		if (status & MII_STSSUM_SPD)
-			*speed = HW_SPEED_100_MBPS;
-		else
-			*speed = HW_SPEED_10_MBPS;
-		if (status & MII_STSSUM_DPLX)
-			*duplex = FDX_CAPABLE_FULL_SELECTED;
-		else
-			*duplex = FDX_CAPABLE_HALF_SELECTED;
+	if (!(status & MII_STAT_LINK))
+		return;
 
-		if (status & MII_STSSUM_LINK)
-			printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-			       net_dev->name,
-			       *speed == HW_SPEED_100_MBPS ?
-			       "100mbps" : "10mbps",
-			       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-			       "full" : "half");
-		else
-			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-	}
-	else {
-		/* HomePNA */
-		*speed = HW_SPEED_HOME;
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
-		if (status & MII_STAT_LINK)
-			printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n",
-			       net_dev->name);
-		else
-			printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-	}
-}
-
-/**
- *	ics1893_read_mode: - read media mode for ICS1893 PHY
- *	@net_dev: the net device to read mode for
- *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
- *
- *	ICS1893 PHY use Quick Poll Detailed Status register
- *	to determine the speed and duplex mode for sis900
- */
+	/* AutoNegotiate completed */
+	autoadv = mdio_read(net_dev, phy_addr, MII_ANADV);
+	autorec = mdio_read(net_dev, phy_addr, MII_ANLPAR);
+	status = autoadv & autorec;
 
-static void ics1893_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
-{
-	int i = 0;
-	u32 status;
-
-	/* MII_QPDSTS is Latched, read twice in succession will reflect the current state */
-	for (i = 0; i < 2; i++)
-		status = mdio_read(net_dev, phy_addr, MII_QPDSTS);
-
-	if (status & MII_STSICS_SPD)
+	if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX))
 		*speed = HW_SPEED_100_MBPS;
 	else
 		*speed = HW_SPEED_10_MBPS;
-
-	if (status & MII_STSICS_DPLX)
+	if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX))
 		*duplex = FDX_CAPABLE_FULL_SELECTED;
 	else
 		*duplex = FDX_CAPABLE_HALF_SELECTED;
 
-	if (status & MII_STSICS_LINKSTS)
-		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-		       net_dev->name,
-		       *speed == HW_SPEED_100_MBPS ?
-		       "100mbps" : "10mbps",
-		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-		       "full" : "half");
-	else
-		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
-}
-
-/**
- *	rtl8201_read_mode: - read media mode for rtl8201 phy
- *	@net_dev: the net device to read mode for
- *	@phy_addr: mii phy address
- *	@speed: the transmit speed to be determined
- *	@duplex: the duplex mode to be determined
- *
- *	read MII_STATUS register from rtl8201 phy
- *	to determine the speed and duplex mode for sis900
- */
-
-static void rtl8201_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
-{
-	u32 status;
-
-	status = mdio_read(net_dev, phy_addr, MII_STATUS);
-
-	if (status & MII_STAT_CAN_TX_FDX) {
-		*speed = HW_SPEED_100_MBPS;
-		*duplex = FDX_CAPABLE_FULL_SELECTED;
-	}
-	else if (status & MII_STAT_CAN_TX) {
-		*speed = HW_SPEED_100_MBPS;
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
-	}
-	else if (status & MII_STAT_CAN_T_FDX) {
-		*speed = HW_SPEED_10_MBPS;
-		*duplex = FDX_CAPABLE_FULL_SELECTED;
-	}
-	else if (status & MII_STAT_CAN_T) {
-		*speed = HW_SPEED_10_MBPS;
-		*duplex = FDX_CAPABLE_HALF_SELECTED;
-	}
-
-	if (status & MII_STAT_LINK)
-		printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
-		       net_dev->name,
-		       *speed == HW_SPEED_100_MBPS ?
-		       "100mbps" : "10mbps",
-		       *duplex == FDX_CAPABLE_FULL_SELECTED ?
-		       "full" : "half");
-	else
-		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+	sis_priv->autong_complete = 1;
+	
+	printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
+	       net_dev->name,
+	       *speed == HW_SPEED_100_MBPS ?
+	       "100mbps" : "10mbps",
+	       *duplex == FDX_CAPABLE_FULL_SELECTED ?
+	       "full" : "half");
 }
 
 /**
@@ -1628,83 +1732,81 @@
         
 	u16 status;
 
-	/* 
-	   )*/    
 	if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
-        /* we switch on the ifmap->port field. I couldn't find anything
-           like a definition or standard for the values of that field.
-           I think the meaning of those values is device specific. But
-           since I would like to change the media type via the ifconfig
-           command I use the definition from linux/netdevice.h 
-           (which seems to be different from the ifport(pcmcia) definition) 
-        */
+		/* we switch on the ifmap->port field. I couldn't find anything
+		   like a definition or standard for the values of that field.
+		   I think the meaning of those values is device specific. But
+		   since I would like to change the media type via the ifconfig
+		   command I use the definition from linux/netdevice.h 
+		   (which seems to be different from the ifport(pcmcia) definition) 
+		*/
 		switch(map->port){
-			case IF_PORT_UNKNOWN: /* use auto here */   
-                		dev->if_port = map->port;
-                		/* we are going to change the media type, so the Link will
-                		be temporary down and we need to reflect that here. When
-                		the Link comes up again, it will be sensed by the sis_timer
-                		procedure, which also does all the rest for us */
-				netif_carrier_off(dev);
+		case IF_PORT_UNKNOWN: /* use auto here */   
+			dev->if_port = map->port;
+			/* we are going to change the media type, so the Link will
+			   be temporary down and we need to reflect that here. When
+			   the Link comes up again, it will be sensed by the sis_timer
+			   procedure, which also does all the rest for us */
+			netif_carrier_off(dev);
                 
-                		/* read current state */
-                		status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
+			/* read current state */
+			status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
                 
-                		/* enable auto negotiation and reset the negotioation
-                		(I dont really know what the auto negatiotiation reset
-                		really means, but it sounds for me right to do one here)*/
-                		mdio_write(dev, mii_phy->phy_addr,
-                                           MII_CONTROL, status | MII_CNTL_AUTO | MII_CNTL_RST_AUTO);
+			/* enable auto negotiation and reset the negotioation
+			   (I dont really know what the auto negatiotiation reset
+			   really means, but it sounds for me right to do one here)*/
+			mdio_write(dev, mii_phy->phy_addr,
+				   MII_CONTROL, status | MII_CNTL_AUTO | MII_CNTL_RST_AUTO);
 
-            			break;
+			break;
             
-            		case IF_PORT_10BASET: /* 10BaseT */         
-                		dev->if_port = map->port;
+		case IF_PORT_10BASET: /* 10BaseT */         
+			dev->if_port = map->port;
                 
-                		/* we are going to change the media type, so the Link will
-                		be temporary down and we need to reflect that here. When
-                		the Link comes up again, it will be sensed by the sis_timer
-                		procedure, which also does all the rest for us */
-				netif_carrier_off(dev);
+			/* we are going to change the media type, so the Link will
+			   be temporary down and we need to reflect that here. When
+			   the Link comes up again, it will be sensed by the sis_timer
+			   procedure, which also does all the rest for us */
+			netif_carrier_off(dev);
         
-                		/* set Speed to 10Mbps */
-                		/* read current state */
-                		status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
+			/* set Speed to 10Mbps */
+			/* read current state */
+			status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
                 
-                		/* disable auto negotiation and force 10MBit mode*/
-                		mdio_write(dev, mii_phy->phy_addr,
-                                           MII_CONTROL, status & ~(MII_CNTL_SPEED | MII_CNTL_AUTO));
-            			break;
+			/* disable auto negotiation and force 10MBit mode*/
+			mdio_write(dev, mii_phy->phy_addr,
+				   MII_CONTROL, status & ~(MII_CNTL_SPEED | MII_CNTL_AUTO));
+			break;
             
-            		case IF_PORT_100BASET: /* 100BaseT */
-            		case IF_PORT_100BASETX: /* 100BaseTx */ 
-                		dev->if_port = map->port;
+		case IF_PORT_100BASET: /* 100BaseT */
+		case IF_PORT_100BASETX: /* 100BaseTx */ 
+			dev->if_port = map->port;
                 
-                		/* we are going to change the media type, so the Link will
-                		be temporary down and we need to reflect that here. When
-                		the Link comes up again, it will be sensed by the sis_timer
-                		procedure, which also does all the rest for us */
-				netif_carrier_off(dev);
+			/* we are going to change the media type, so the Link will
+			   be temporary down and we need to reflect that here. When
+			   the Link comes up again, it will be sensed by the sis_timer
+			   procedure, which also does all the rest for us */
+			netif_carrier_off(dev);
                 
-                		/* set Speed to 100Mbps */
-                		/* disable auto negotiation and enable 100MBit Mode */
-                		status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
-                		mdio_write(dev, mii_phy->phy_addr,
-                                           MII_CONTROL, (status & ~MII_CNTL_SPEED) | MII_CNTL_SPEED);
+			/* set Speed to 100Mbps */
+			/* disable auto negotiation and enable 100MBit Mode */
+			status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
+			mdio_write(dev, mii_phy->phy_addr,
+				   MII_CONTROL, (status & ~MII_CNTL_SPEED) | MII_CNTL_SPEED);
                 
-            			break;
+			break;
             
-            		case IF_PORT_10BASE2: /* 10Base2 */
-            		case IF_PORT_AUI: /* AUI */
-            		case IF_PORT_100BASEFX: /* 100BaseFx */
+		case IF_PORT_10BASE2: /* 10Base2 */
+		case IF_PORT_AUI: /* AUI */
+		case IF_PORT_100BASEFX: /* 100BaseFx */
                 	/* These Modes are not supported (are they?)*/
-                		printk(KERN_INFO "Not supported");
-                		return -EOPNOTSUPP;
-            			break;
+			printk(KERN_INFO "Not supported");
+			return -EOPNOTSUPP;
+			break;
             
-            		default:
-                		printk(KERN_INFO "Invalid");
-                		return -EINVAL;
+		default:
+			printk(KERN_INFO "Invalid");
+			return -EINVAL;
 		}
 	}
 	return 0;
@@ -1713,12 +1815,15 @@
 /**
  *	sis900_compute_hashtable_index: - compute hashtable index 
  *	@addr: multicast address
+ *	@revision: revision id of chip
  *
  *	SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast
  *	hash table, which makes this function a little bit different from other drivers
+ *	SiS 900 B0 & 635 M/B uses the most significat 8 bits to index 256 bits
+ *   	multicast hash table. 
  */
 
-static u16 sis900_compute_hashtable_index(u8 *addr)
+static u16 sis900_compute_hashtable_index(u8 *addr, u8 revision)
 {
 
 /* what is the correct value of the POLYNOMIAL ??
@@ -1741,51 +1846,63 @@
 			byte >>= 1;
 		}
 	}
-	/* leave 7 most siginifant bits */ 
-	return ((int)(crc >> 25));
+
+	/* leave 8 or 7 most siginifant bits */
+	if ((revision == SIS635A_900_REV) || (revision == SIS900B_900_REV))
+		return ((int)(crc >> 24));
+	else
+		return ((int)(crc >> 25));
 }
 
 /**
  *	set_rx_mode: - Set SiS900 receive mode 
  *	@net_dev: the net device to be set
  *
- *	Set SiS900 receive mode for promiscuous, multicast, or broadcast mode. 
+ *	Set SiS900 receive mode for promiscuous, multicast, or broadcast mode.
  *	And set the appropriate multicast filter.
+ *	Multicast hash table changes from 128 to 256 bits for 635M/B & 900B0.
  */
 
 static void set_rx_mode(struct net_device *net_dev)
 {
 	long ioaddr = net_dev->base_addr;
-	u16 mc_filter[8];			/* 128 bits multicast hash table */
-	int i;
+	struct sis900_private * sis_priv = net_dev->priv;
+	u16 mc_filter[16] = {0};	/* 256/128 bits multicast hash table */
+	int i, table_entries;
 	u32 rx_mode;
+	u8 revision;
+
+	/* 635 Hash Table entires = 256(2^16) */
+	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+	if((revision == SIS635A_900_REV) || (revision == SIS900B_900_REV))
+		table_entries = 16;
+	else
+		table_entries = 8;
 
 	if (net_dev->flags & IFF_PROMISC) {
 		/* Accept any kinds of packets */
 		rx_mode = RFPromiscuous;
-		for (i = 0; i < 8; i++)
+		for (i = 0; i < table_entries; i++)
 			mc_filter[i] = 0xffff;
 	} else if ((net_dev->mc_count > multicast_filter_limit) ||
 		   (net_dev->flags & IFF_ALLMULTI)) {
 		/* too many multicast addresses or accept all multicast packet */
 		rx_mode = RFAAB | RFAAM;
-		for (i = 0; i < 8; i++)
+		for (i = 0; i < table_entries; i++)
 			mc_filter[i] = 0xffff;
 	} else {
 		/* Accept Broadcast packet, destination address matchs our MAC address,
 		   use Receive Filter to reject unwanted MCAST packet */
 		struct dev_mc_list *mclist;
 		rx_mode = RFAAB;
-		for (i = 0; i < 8; i++)
-			mc_filter[i]=0;
 		for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count;
 		     i++, mclist = mclist->next)
-			set_bit(sis900_compute_hashtable_index(mclist->dmi_addr),
+			set_bit(sis900_compute_hashtable_index(mclist->dmi_addr, revision),
 				mc_filter);
 	}
 
 	/* update Multicast Hash Table in Receive Filter */
-	for (i = 0; i < 8; i++) {
+	for (i = 0; i < table_entries; i++) {
                 /* why plus 0x04 ??, That makes the correct value for hash table. */
 		outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
 		outl(mc_filter[i], ioaddr + rfdr);
@@ -1804,7 +1921,7 @@
 		outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg);
 		/* restore cr */
 		outl(cr_saved, ioaddr + cr);
-	}		
+	}
 
 	return;
 }
@@ -1815,13 +1932,16 @@
  *
  *	reset sis900 MAC and wait until finished
  *	reset through command register
+ *	change backoff algorithm for 900B0 & 635 M/B
  */
 
 static void sis900_reset(struct net_device *net_dev)
 {
+	struct sis900_private * sis_priv = net_dev->priv;
 	long ioaddr = net_dev->base_addr;
 	int i = 0;
 	u32 status = TxRCMP | RxRCMP;
+	u8  revision;
 
 	outl(0, ioaddr + ier);
 	outl(0, ioaddr + imr);
@@ -1834,7 +1954,11 @@
 		status ^= (inl(isr + ioaddr) & status);
 	}
 
-	outl(PESEL, ioaddr + cfg);
+	pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
+	if( (revision == SIS635A_900_REV) || (revision == SIS900B_900_REV) )
+		outl(PESEL | RND_CNT, ioaddr + cfg);
+	else
+		outl(PESEL, ioaddr + cfg);
 }
 
 /**
@@ -1847,7 +1971,15 @@
 static void __devexit sis900_remove(struct pci_dev *pci_dev)
 {
 	struct net_device *net_dev = pci_get_drvdata(pci_dev);
-		
+	struct sis900_private * sis_priv = net_dev->priv;
+	struct mii_phy *phy = NULL;
+
+	while (sis_priv->first_mii) {
+		phy = sis_priv->first_mii;
+		sis_priv->first_mii = phy->next;
+		kfree(phy);
+	}
+
 	unregister_netdev(net_dev);
 	kfree(net_dev);
 	pci_release_regions(pci_dev);
diff -urN linux-2.4.4-pre2/drivers/net/sis900.h linux/drivers/net/sis900.h
--- linux-2.4.4-pre2/drivers/net/sis900.h	Fri Dec 29 14:07:22 2000
+++ linux/drivers/net/sis900.h	Mon Dec  3 22:34:50 2001
@@ -50,7 +50,10 @@
 enum sis900_configuration_register_bits {
 	DESCRFMT = 0x00000100 /* 7016 specific */, REQALG = 0x00000080,
 	SB    = 0x00000040, POW = 0x00000020, EXD = 0x00000010, 
-	PESEL = 0x00000008, LPM = 0x00000004, BEM = 0x00000001
+	PESEL = 0x00000008, LPM = 0x00000004, BEM = 0x00000001,
+	/* 635 & 900B Specific */
+	RND_CNT = 0x00000400, FAIR_BACKOFF = 0x00000200,
+	EDB_MASTER_EN = 0x00002000
 };
 
 enum sis900_eeprom_access_reigster_bits {
@@ -78,8 +81,10 @@
 #define MAX_DMA_RANGE	7	/* actually 0 means MAXIMUM !! */
 #define TxMXDMA_shift   	20
 #define RxMXDMA_shift    20
-#define TX_DMA_BURST    	0
-#define RX_DMA_BURST    	0
+
+enum sis900_tx_rx_dma{
+	DMA_BURST_512 = 0,	DMA_BURST_64 = 5
+};
 
 /* transmit FIFO threshholds */
 #define TX_FILL_THRESH   16	/* 1/4 FIFO size */
@@ -233,7 +238,8 @@
 
 enum sis900_revision_id {
 	SIS630A_900_REV = 0x80,		SIS630E_900_REV = 0x81,
-	SIS630S_900_REV = 0x82,		SIS630EA1_900_REV = 0x83
+	SIS630S_900_REV = 0x82,		SIS630EA1_900_REV = 0x83,
+	SIS635A_900_REV = 0x90,		SIS900B_900_REV = 0x03
 };
 
 enum sis630_revision_id {
@@ -263,8 +269,8 @@
 /* PCI stuff, should be move to pic.h */
 #define PCI_DEVICE_ID_SI_900	0x900   
 #define PCI_DEVICE_ID_SI_7016	0x7016
-#define SIS630_VENDOR_ID        0x0630
-#define SIS630_DEVICE_ID        0x1039
+#define SIS630_VENDOR_ID        0x1039
+#define SIS630_DEVICE_ID        0x0630
 
 /* ioctl for accessing MII transceiver */
 #define SIOCGMIIPHY (SIOCDEVPRIVATE)		/* Get the PHY in use. */
diff -urN linux-2.4.4-pre2/drivers/net/starfire.c linux/drivers/net/starfire.c
--- linux-2.4.4-pre2/drivers/net/starfire.c	Fri Aug 11 15:57:58 2000
+++ linux/drivers/net/starfire.c	Mon Dec  3 22:34:50 2001
@@ -97,9 +97,6 @@
 /* Include files, designed to support most kernel versions 2.0.0 and later. */
 #include <linux/version.h>
 #include <linux/module.h>
-#if LINUX_VERSION_CODE < 0x20300  &&  defined(MODVERSIONS)
-#include <linux/modversions.h>
-#endif
 
 #include <linux/kernel.h>
 #include <linux/string.h>
diff -urN linux-2.4.4-pre2/drivers/net/stnic.c linux/drivers/net/stnic.c
--- linux-2.4.4-pre2/drivers/net/stnic.c	Sat Dec 30 11:23:14 2000
+++ linux/drivers/net/stnic.c	Mon Dec  3 22:34:50 2001
@@ -7,6 +7,7 @@
  * Copyright (C) 1999 kaz Kojima
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/kernel.h>
@@ -21,6 +22,9 @@
 #include <asm/io.h>
 #include <asm/hitachi_se.h>
 #include <asm/machvec.h>
+#ifdef CONFIG_SH_STANDARD_BIOS 
+#include <asm/sh_bios.h>
+#endif
 
 #include "8390.h"
 
@@ -74,6 +78,7 @@
   vword trash;
   trash = *(vword *) 0xa0000000;
   trash = *(vword *) 0xa0000000;
+  trash = *(vword *) 0xa0000000;
 }
 
 static inline byte
@@ -112,10 +117,13 @@
   /* Allocate dev->priv and fill in 8390 specific dev fields. */
   if (ethdev_init (dev))
     {
-      printk ("Unable to get memory for dev->priv.\n");
+      printk (KERN_EMERG "Unable to get memory for dev->priv.\n");
       return -ENOMEM;
     }
 
+#ifdef CONFIG_SH_STANDARD_BIOS 
+  sh_bios_get_node_addr (stnic_eadr);
+#endif
   for (i = 0; i < ETHER_ADDR_LEN; i++)
     dev->dev_addr[i] = stnic_eadr[i];
 
@@ -129,7 +137,7 @@
      share and the board will usually be enabled. */
   i = request_irq (dev->irq, ei_interrupt, 0, dev->name, dev);
   if (i)  {
-      printk (" unable to get IRQ %d.\n", dev->irq);
+      printk (KERN_EMERG " unable to get IRQ %d.\n", dev->irq);
       unregister_netdev(dev);
       kfree(dev->priv);
       kfree(dev);
@@ -157,7 +165,7 @@
 stnic_open (struct net_device *dev)
 {
 #if 0
-  printk ("stnic open\n");
+  printk (KERN_DEBUG "stnic open\n");
 #endif
   ei_open (dev);
   return 0;
@@ -176,7 +184,7 @@
   *(vhalf *) PA_83902_RST = 0;
   udelay (5);
   if (ei_debug > 1)
-    printk("8390 reset done (%ld).\n", jiffies);
+    printk (KERN_WARNING "8390 reset done (%ld).\n", jiffies);
   *(vhalf *) PA_83902_RST = ~0;
   udelay (5);
 }
@@ -206,7 +214,7 @@
 #endif
 
   if (ei_debug > 1)
-    printk ("ring %x status %02x next %02x count %04x.\n",
+    printk (KERN_DEBUG "ring %x status %02x next %02x count %04x.\n",
 	    ring_page, hdr->status, hdr->next, hdr->count);
 
   STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
@@ -254,22 +262,14 @@
 stnic_block_output (struct net_device *dev, int length,
 		    const unsigned char *buf, int output_page)
 {
-#if 0
-  STNIC_WRITE (PG0_RBCR0, 1);
-  STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
-#else  /* XXX: I don't know why but this works.  -- gniibe  */
-  STNIC_WRITE (PG0_RBCR0, 0x42);
-  STNIC_WRITE (PG0_RBCR1, 0x00);
-  STNIC_WRITE (PG0_RBCR0, 0x42);
-  STNIC_WRITE (PG0_RBCR1, 0x00);
+  STNIC_WRITE (PG0_RBCR0, 1);	/* Write non-zero value */
   STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
   STNIC_DELAY ();
-#endif
 
-  STNIC_WRITE (PG0_RSAR0, 0);
-  STNIC_WRITE (PG0_RSAR1, output_page);
   STNIC_WRITE (PG0_RBCR0, length & 0xff);
   STNIC_WRITE (PG0_RBCR1, length >> 8);
+  STNIC_WRITE (PG0_RSAR0, 0);
+  STNIC_WRITE (PG0_RSAR1, output_page);
   STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA);
 
   if (length & 1)
diff -urN linux-2.4.4-pre2/drivers/net/sungem.c linux/drivers/net/sungem.c
--- linux-2.4.4-pre2/drivers/net/sungem.c	Sun Mar 25 18:14:21 2001
+++ linux/drivers/net/sungem.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sungem.c,v 1.8 2001/03/22 22:48:51 davem Exp $
+/* $Id: sungem.c,v 1.11 2001/04/04 14:49:40 davem Exp $
  * sungem.c: Sun GEM ethernet driver.
  *
  * Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com)
@@ -159,13 +159,20 @@
 			       dev->name);
 	}
 
-	if (pcs_miistat & PCS_MIISTAT_LS)
+	if (pcs_miistat & PCS_MIISTAT_LS) {
 		printk(KERN_INFO "%s: PCS link is now up.\n",
 		       dev->name);
-	else
+	} else {
 		printk(KERN_INFO "%s: PCS link is now down.\n",
 		       dev->name);
 
+		/* If this happens and the link timer is not running,
+		 * reset so we re-negotiate.
+		 */
+		if (!timer_pending(&gp->link_timer))
+			return 1;
+	}
+
 	return 0;
 }
 
@@ -441,7 +448,7 @@
 			struct gem_rxd *rxd =
 				&gp->init_block->rxd[cluster_start];
 			for (;;) {
-				rxd->status_word = cpu_to_le64(RXDCTRL_FRESH);
+				rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
 				rxd++;
 				cluster_start = NEXT_RX(cluster_start);
 				if (cluster_start == curr)
@@ -491,19 +498,19 @@
 		if (len > RX_COPY_THRESHOLD) {
 			struct sk_buff *new_skb;
 
-			new_skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+			new_skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), GFP_ATOMIC);
 			if (new_skb == NULL) {
 				drops++;
 				goto drop_it;
 			}
 			pci_unmap_single(gp->pdev, dma_addr,
-					 RX_BUF_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
+					 RX_BUF_ALLOC_SIZE(gp), PCI_DMA_FROMDEVICE);
 			gp->rx_skbs[entry] = new_skb;
 			new_skb->dev = gp->dev;
 			skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET));
 			rxd->buffer = cpu_to_le64(pci_map_single(gp->pdev,
 								 new_skb->data,
-								 RX_BUF_ALLOC_SIZE,
+								 RX_BUF_ALLOC_SIZE(gp),
 								 PCI_DMA_FROMDEVICE));
 			skb_reserve(new_skb, RX_OFFSET);
 
@@ -527,6 +534,8 @@
 			skb = copy_skb;
 		}
 
+		skb->csum = ((status & RXDCTRL_TCPCSUM) ^ 0xffff);
+		skb->ip_summed = CHECKSUM_HW;
 		skb->protocol = eth_type_trans(skb, gp->dev);
 		netif_rx(skb);
 
@@ -548,8 +557,8 @@
 
 static void gem_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	struct net_device *dev = (struct net_device *) dev_id;
-	struct gem *gp = (struct gem *) dev->priv;
+	struct net_device *dev = dev_id;
+	struct gem *gp = dev->priv;
 	u32 gem_status = readl(gp->regs + GREG_STAT);
 
 	spin_lock(&gp->lock);
@@ -567,9 +576,36 @@
 	spin_unlock(&gp->lock);
 }
 
+static void gem_tx_timeout(struct net_device *dev)
+{
+	struct gem *gp = dev->priv;
+
+	printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
+	printk(KERN_ERR "%s: TX_STATE[%08x:%08x:%08x]\n",
+	       dev->name,
+	       readl(gp->regs + TXDMA_CFG),
+	       readl(gp->regs + MAC_TXSTAT),
+	       readl(gp->regs + MAC_TXCFG));
+	printk(KERN_ERR "%s: RX_STATE[%08x:%08x:%08x]\n",
+	       dev->name,
+	       readl(gp->regs + RXDMA_CFG),
+	       readl(gp->regs + MAC_RXSTAT),
+	       readl(gp->regs + MAC_RXCFG));
+
+	spin_lock_irq(&gp->lock);
+
+	gem_stop(gp, gp->regs);
+	gem_init_rings(gp, 1);
+	gem_init_hw(gp);
+
+	spin_unlock_irq(&gp->lock);
+
+	netif_wake_queue(dev);
+}
+
 static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct gem *gp = (struct gem *) dev->priv;
+	struct gem *gp = dev->priv;
 	long len;
 	int entry, avail;
 	u32 mapping;
@@ -602,6 +638,30 @@
 	return 0;
 }
 
+/* Jumbo-grams don't seem to work :-( */
+#if 1
+#define MAX_MTU		1500
+#else
+#define MAX_MTU		9000
+#endif
+
+static int gem_change_mtu(struct net_device *dev, int new_mtu)
+{
+	struct gem *gp = dev->priv;
+
+	if (new_mtu < 0 || new_mtu > MAX_MTU)
+		return -EINVAL;
+
+	spin_lock_irq(&gp->lock);
+	gem_stop(gp, gp->regs);
+	dev->mtu = new_mtu;
+	gem_init_rings(gp, 1);
+	gem_init_hw(gp);
+	spin_unlock_irq(&gp->lock);
+
+	return 0;
+}
+
 #define STOP_TRIES 32
 
 static void gem_stop(struct gem *gp, unsigned long regs)
@@ -676,8 +736,29 @@
 	} else if (full_duplex) {
 		val |= MAC_XIFCFG_FLED;
 	}
+
+	if (speed == 1000)
+		val |= (MAC_XIFCFG_GMII);
+
 	writel(val, gp->regs + MAC_XIFCFG);
 
+	/* If gigabit and half-duplex, enable carrier extension
+	 * mode.  Else, disable it.
+	 */
+	if (speed == 1000 && !full_duplex) {
+		val = readl(gp->regs + MAC_TXCFG);
+		writel(val | MAC_TXCFG_TCE, gp->regs + MAC_TXCFG);
+
+		val = readl(gp->regs + MAC_RXCFG);
+		writel(val | MAC_RXCFG_RCE, gp->regs + MAC_RXCFG);
+	} else {
+		val = readl(gp->regs + MAC_TXCFG);
+		writel(val & ~MAC_TXCFG_TCE, gp->regs + MAC_TXCFG);
+
+		val = readl(gp->regs + MAC_RXCFG);
+		writel(val & ~MAC_RXCFG_RCE, gp->regs + MAC_RXCFG);
+	}
+
 	if (gp->phy_type == phy_serialink ||
 	    gp->phy_type == phy_serdes) {
 		u32 pcs_lpa = readl(gp->regs + PCS_MIILP);
@@ -689,10 +770,6 @@
 			val &= ~(MAC_MCCFG_SPE | MAC_MCCFG_RPE);
 		writel(val, gp->regs + MAC_MCCFG);
 
-		/* XXX Set up PCS MII Control and Serialink Control
-		 * XXX registers.
-		 */
-
 		if (!full_duplex)
 			writel(512, gp->regs + MAC_STIME);
 		else
@@ -767,7 +844,16 @@
 			restart_timer = gem_mdio_link_not_up(gp);
 		}
 	} else {
-		/* XXX Code PCS support... XXX */
+		u32 val = readl(gp->regs + PCS_MIISTAT);
+
+		if (!(val & PCS_MIISTAT_LS))
+			val = readl(gp->regs + PCS_MIISTAT);
+
+		if ((val & PCS_MIISTAT_LS) == 0) {
+			restart_timer = 1;
+		} else {
+			gem_set_link_modes(gp);
+		}
 	}
 
 	if (restart_timer) {
@@ -792,7 +878,7 @@
 			skb = gp->rx_skbs[i];
 			dma_addr = (u32) le64_to_cpu(rxd->buffer);
 			pci_unmap_single(gp->pdev, dma_addr,
-					 RX_BUF_ALLOC_SIZE,
+					 RX_BUF_ALLOC_SIZE(gp),
 					 PCI_DMA_FROMDEVICE);
 			dev_kfree_skb_any(skb);
 			gp->rx_skbs[i] = NULL;
@@ -834,7 +920,7 @@
 		struct sk_buff *skb;
 		struct gem_rxd *rxd = &gb->rxd[i];
 
-		skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags);
+		skb = gem_alloc_skb(RX_BUF_ALLOC_SIZE(gp), gfp_flags);
 		if (!skb) {
 			rxd->buffer = 0;
 			rxd->status_word = 0;
@@ -845,10 +931,10 @@
 		skb->dev = dev;
 		skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET));
 		dma_addr = pci_map_single(gp->pdev, skb->data,
-					  RX_BUF_ALLOC_SIZE,
+					  RX_BUF_ALLOC_SIZE(gp),
 					  PCI_DMA_FROMDEVICE);
 		rxd->buffer = cpu_to_le64(dma_addr);
-		rxd->status_word = cpu_to_le64(RXDCTRL_FRESH);
+		rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
 		skb_reserve(skb, RX_OFFSET);
 	}
 
@@ -863,15 +949,19 @@
 static void gem_init_phy(struct gem *gp)
 {
 	if (gp->pdev->device == PCI_DEVICE_ID_SUN_GEM) {
+		u32 val;
+
 		/* Init datapath mode register. */
 		if (gp->phy_type == phy_mii_mdio0 ||
 		    gp->phy_type == phy_mii_mdio1) {
-			writel(PCS_DMODE_MGM, gp->regs + PCS_DMODE);
+			val = PCS_DMODE_MGM;
 		} else if (gp->phy_type == phy_serialink) {
-			writel(PCS_DMODE_SM, gp->regs + PCS_DMODE);
+			val = PCS_DMODE_SM | PCS_DMODE_GMOE;
 		} else {
-			writel(PCS_DMODE_ESM, gp->regs + PCS_DMODE);
+			val = PCS_DMODE_ESM;
 		}
+
+		writel(val, gp->regs + PCS_DMODE);
 	}
 
 	if (gp->phy_type == phy_mii_mdio0 ||
@@ -899,7 +989,59 @@
 		val |= (PHY_CTRL_ANRES | PHY_CTRL_ANENAB);
 		phy_write(gp, PHY_CTRL, val);
 	} else {
-		/* XXX Implement me XXX */
+		u32 val;
+		int limit;
+
+		/* Reset PCS unit. */
+		val = readl(gp->regs + PCS_MIICTRL);
+		val |= PCS_MIICTRL_RST;
+		writeb(val, gp->regs + PCS_MIICTRL);
+
+		limit = 32;
+		while (readl(gp->regs + PCS_MIICTRL) & PCS_MIICTRL_RST) {
+			udelay(100);
+			if (limit-- <= 0)
+				break;
+		}
+		if (limit <= 0)
+			printk(KERN_WARNING "%s: PCS reset bit would not clear.\n",
+			       gp->dev->name);
+
+		/* Make sure PCS is disabled while changing advertisement
+		 * configuration.
+		 */
+		val = readl(gp->regs + PCS_CFG);
+		val &= ~(PCS_CFG_ENABLE | PCS_CFG_TO);
+		writel(val, gp->regs + PCS_CFG);
+
+		/* Advertise all capabilities. */
+		val = readl(gp->regs + PCS_MIIADV);
+		val |= (PCS_MIIADV_FD | PCS_MIIADV_HD |
+			PCS_MIIADV_SP | PCS_MIIADV_AP);
+		writel(val, gp->regs + PCS_MIIADV);
+
+		/* Enable and restart auto-negotiation, disable wrapback/loopback,
+		 * and re-enable PCS.
+		 */
+		val = readl(gp->regs + PCS_MIICTRL);
+		val |= (PCS_MIICTRL_RAN | PCS_MIICTRL_ANE);
+		val &= ~PCS_MIICTRL_WB;
+		writel(val, gp->regs + PCS_MIICTRL);
+
+		val = readl(gp->regs + PCS_CFG);
+		val |= PCS_CFG_ENABLE;
+		writel(val, gp->regs + PCS_CFG);
+
+		/* Make sure serialink loopback is off.  The meaning
+		 * of this bit is logically inverted based upon whether
+		 * you are in Serialink or SERDES mode.
+		 */
+		val = readl(gp->regs + PCS_SCTRL);
+		if (gp->phy_type == phy_serialink)
+			val &= ~PCS_SCTRL_LOOP;
+		else
+			val |= PCS_SCTRL_LOOP;
+		writel(val, gp->regs + PCS_SCTRL);
 	}
 }
 
@@ -907,7 +1049,7 @@
 {
 	u32 val;
 
-	val = (TXDMA_CFG_BASE | (0x4ff << 10) | TXDMA_CFG_PMODE);
+	val = (TXDMA_CFG_BASE | (0x7ff << 10) | TXDMA_CFG_PMODE);
 	writel(val, gp->regs + TXDMA_CFG);
 
 	writel(0, gp->regs + TXDMA_DBHI);
@@ -916,7 +1058,7 @@
 	writel(0, gp->regs + TXDMA_KICK);
 
 	val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) |
-	       ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128);
+	       ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_512);
 	writel(val, gp->regs + RXDMA_CFG);
 
 	writel(0, gp->regs + RXDMA_DBHI);
@@ -931,12 +1073,12 @@
 	writel(val, gp->regs + RXDMA_PTHRESH);
 
 	if (readl(gp->regs + GREG_BIFCFG) & GREG_BIFCFG_M66EN)
-		writel(((5 & RXDMA_BLANK_IPKTS) |
-			((8 << 12) & RXDMA_BLANK_ITIME)),
+		writel(((6 & RXDMA_BLANK_IPKTS) |
+			((4 << 12) & RXDMA_BLANK_ITIME)),
 		       gp->regs + RXDMA_BLANK);
 	else
-		writel(((5 & RXDMA_BLANK_IPKTS) |
-			((4 << 12) & RXDMA_BLANK_ITIME)),
+		writel(((6 & RXDMA_BLANK_IPKTS) |
+			((2 << 12) & RXDMA_BLANK_ITIME)),
 		       gp->regs + RXDMA_BLANK);
 }
 
@@ -955,7 +1097,7 @@
 	writel(0x04, gp->regs + MAC_IPG2);
 	writel(0x40, gp->regs + MAC_STIME);
 	writel(0x40, gp->regs + MAC_MINFSZ);
-	writel(0x5ee, gp->regs + MAC_MAXFSZ);
+	writel(0x20000000 | (gp->dev->mtu + 18), gp->regs + MAC_MAXFSZ);
 	writel(0x07, gp->regs + MAC_PASIZE);
 	writel(0x04, gp->regs + MAC_JAMSIZE);
 	writel(0x10, gp->regs + MAC_ATTLIM);
@@ -1097,7 +1239,7 @@
 
 static int gem_open(struct net_device *dev)
 {
-	struct gem *gp = (struct gem *) dev->priv;
+	struct gem *gp = dev->priv;
 	unsigned long regs = gp->regs;
 
 	del_timer(&gp->link_timer);
@@ -1117,6 +1259,9 @@
 {
 	struct gem *gp = dev->priv;
 
+	del_timer(&gp->link_timer);
+	gem_stop(gp, gp->regs);
+	gem_clean_rings(gp);
 	free_irq(gp->pdev->irq, (void *)dev);
 	return 0;
 }
@@ -1263,13 +1408,17 @@
 	/* Determine initial PHY interface type guess.  MDIO1 is the
 	 * external PHY and thus takes precedence over MDIO0.
 	 */
-	if (mif_cfg & MIF_CFG_MDI1)
+	if (mif_cfg & MIF_CFG_MDI1) {
 		gp->phy_type = phy_mii_mdio1;
-	else if (mif_cfg & MIF_CFG_MDI0)
+		mif_cfg |= MIF_CFG_PSELECT;
+		writel(mif_cfg, gp->regs + MIF_CFG);
+	} else if (mif_cfg & MIF_CFG_MDI0) {
 		gp->phy_type = phy_mii_mdio0;
-	else
+		mif_cfg &= ~MIF_CFG_PSELECT;
+		writel(mif_cfg, gp->regs + MIF_CFG);
+	} else {
 		gp->phy_type = phy_serialink;
-
+	}
 	if (gp->phy_type == phy_mii_mdio1 ||
 	    gp->phy_type == phy_mii_mdio0) {
 		int i;
@@ -1279,6 +1428,13 @@
 			if (phy_read(gp, PHY_CTRL) != 0xffff)
 				break;
 		}
+		if (i == 32) {
+			if (pdev->device != PCI_DEVICE_ID_SUN_GEM) {
+				printk(KERN_ERR PFX "RIO MII phy will not respond.\n");
+				return -1;
+			}
+			gp->phy_type = phy_serdes;
+		}
 	}
 
 	/* Fetch the FIFO configurations now too. */
@@ -1309,23 +1465,50 @@
 	if (gp->rx_fifo_sz <= (2 * 1024)) {
 		gp->rx_pause_off = gp->rx_pause_on = gp->rx_fifo_sz;
 	} else {
-		int off = ((gp->rx_fifo_sz * 3) / 4);
-		int on = off - (1 * 1024);
+		int off = (gp->rx_fifo_sz - (5 * 1024));
+		int on = off - 1024;
 
 		gp->rx_pause_off = off;
 		gp->rx_pause_on = on;
 	}
 
 	{
-		u32 bifcfg = readl(gp->regs + GREG_BIFCFG);
+		u32 cfg = readl(gp->regs + GREG_BIFCFG);
+
+		cfg |= GREG_BIFCFG_B64DIS;
+		writel(cfg, gp->regs + GREG_BIFCFG);
 
-		bifcfg |= GREG_BIFCFG_B64DIS;
-		writel(bifcfg, gp->regs + GREG_BIFCFG);
+		cfg  = GREG_CFG_IBURST;
+		cfg |= ((31 << 1) & GREG_CFG_TXDMALIM);
+		cfg |= ((31 << 6) & GREG_CFG_RXDMALIM);
+		writel(cfg, gp->regs + GREG_CFG);
 	}
 
 	return 0;
 }
 
+static void __devinit gem_get_device_address(struct gem *gp)
+{
+	struct net_device *dev = gp->dev;
+	struct pci_dev *pdev = gp->pdev;
+
+#ifdef __sparc__
+	struct pcidev_cookie *pcp = pdev->sysdata;
+	int node = -1;
+
+	if (pcp != NULL) {
+		node = pcp->prom_node;
+		if (prom_getproplen(node, "local-mac-address") == 6)
+			prom_getproperty(node, "local-mac-address",
+					 dev->dev_addr, 6);
+		else
+			node = -1;
+	}
+	if (node == -1)
+		memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
+#endif
+}
+
 static int __devinit gem_init_one(struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
 {
@@ -1352,6 +1535,7 @@
 		printk(KERN_ERR PFX "Etherdev init failed, aborting.\n");
 		return -ENOMEM;
 	}
+	SET_MODULE_OWNER(dev);
 
 	if (!request_mem_region(gemreg_base, gemreg_len, dev->name)) {
 		printk(KERN_ERR PFX "MMIO resource (0x%lx@0x%lx) unavailable, "
@@ -1372,6 +1556,7 @@
 
 	gp->pdev = pdev;
 	dev->base_addr = (long) pdev;
+	gp->dev = dev;
 
 	spin_lock_init(&gp->lock);
 
@@ -1402,9 +1587,7 @@
 	printk(KERN_INFO "%s: Sun GEM (PCI) 10/100/1000BaseT Ethernet ",
 	       dev->name);
 
-#ifdef __sparc__
-	memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
-#endif
+	gem_get_device_address(gp);
 
 	for (i = 0; i < 6; i++)
 		printk("%2.2x%c", dev->dev_addr[i],
@@ -1415,13 +1598,15 @@
 	gp->link_timer.function = gem_link_timer;
 	gp->link_timer.data = (unsigned long) gp;
 
-	gp->dev = dev;
 	dev->open = gem_open;
 	dev->stop = gem_close;
 	dev->hard_start_xmit = gem_start_xmit;
 	dev->get_stats = gem_get_stats;
 	dev->set_multicast_list = gem_set_multicast;
 	dev->do_ioctl = gem_ioctl;
+	dev->tx_timeout = gem_tx_timeout;
+	dev->watchdog_timeo = 5 * HZ;
+	dev->change_mtu = gem_change_mtu;
 	dev->irq = pdev->irq;
 	dev->dma = 0;
 
diff -urN linux-2.4.4-pre2/drivers/net/sungem.h linux/drivers/net/sungem.h
--- linux-2.4.4-pre2/drivers/net/sungem.h	Sun Mar 25 18:14:21 2001
+++ linux/drivers/net/sungem.h	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sungem.h,v 1.5 2001/03/21 23:02:04 davem Exp $
+/* $Id: sungem.h,v 1.7 2001/04/04 14:49:40 davem Exp $
  * sungem.h: Definitions for Sun GEM ethernet driver.
  *
  * Copyright (C) 2000 David S. Miller (davem@redhat.com)
@@ -727,7 +727,10 @@
 #define PCS_DMODE_MGM	0x00000004	/* MII/GMII mode		*/
 #define PCS_DMODE_GMOE	0x00000008	/* GMII Output Enable		*/
 
-/* Serialink Control Register. */
+/* Serialink Control Register.
+ *
+ * NOTE: When in SERDES mode, the loopback bit has inverse logic.
+ */
 #define PCS_SCTRL_LOOP	0x00000001	/* Loopback enable		*/
 #define PCS_SCTRL_ESCD	0x00000002	/* Enable sync char detection	*/
 #define PCS_SCTRL_LOCK	0x00000004	/* Lock to reference clock	*/
@@ -837,8 +840,8 @@
 #define RXDCTRL_ALTMAC	0x2000000000000000	/* Matched ALT MAC	*/
 #define RXDCTRL_BAD	0x4000000000000000	/* Frame has bad CRC	*/
 
-#define RXDCTRL_FRESH	\
-	((((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16) & RXDCTRL_BUFSZ) | \
+#define RXDCTRL_FRESH(gp)	\
+	((((RX_BUF_ALLOC_SIZE(gp) - RX_OFFSET) << 16) & RXDCTRL_BUFSZ) | \
 	 RXDCTRL_OWN)
 
 #define TX_RING_SIZE 128
@@ -897,7 +900,7 @@
 	  (GP)->tx_old - (GP)->tx_new - 1)
 
 #define RX_OFFSET          2
-#define RX_BUF_ALLOC_SIZE  (1546 + RX_OFFSET + 64)
+#define RX_BUF_ALLOC_SIZE(gp)	((gp)->dev->mtu + 46 + RX_OFFSET + 64)
 
 #define RX_COPY_THRESHOLD  256
 
diff -urN linux-2.4.4-pre2/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- linux-2.4.4-pre2/drivers/net/sunhme.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/sunhme.c	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sunhme.c,v 1.105 2000/12/05 02:00:36 anton Exp $
+/* $Id: sunhme.c,v 1.115 2001/03/29 06:37:09 davem Exp $
  * sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
  *           auto carrier detecting ethernet driver.  Also known as the
  *           "Happy Meal Ethernet" found on SunSwift SBUS cards.
@@ -1277,12 +1277,23 @@
 			struct sk_buff *skb = hp->tx_skbs[i];
 			struct happy_meal_txd *txd;
 			u32 dma_addr;
+			int frag;
 
-			txd = &hp->happy_block->happy_meal_txd[i];
-			dma_addr = hme_read_desc32(hp, &txd->tx_addr);
-			hme_dma_unmap(hp, dma_addr, skb->len, DMA_TODEVICE);
-			dev_kfree_skb_any(skb);
 			hp->tx_skbs[i] = NULL;
+
+			for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
+				txd = &hp->happy_block->happy_meal_txd[i];
+				dma_addr = hme_read_desc32(hp, &txd->tx_addr);
+				hme_dma_unmap(hp, dma_addr,
+					      (hme_read_desc32(hp, &txd->tx_flags)
+					       & TXFLAG_SIZE),
+					      DMA_TODEVICE);
+
+				if (frag != skb_shinfo(skb)->nr_frags)
+					i++;
+			}
+
+			dev_kfree_skb_any(skb);
 		}
 	}
 }
@@ -1842,7 +1853,7 @@
 
 	if (status & GREG_STAT_NORXD) {
 		/* This is harmless, it just means the system is
-		 * quite loaded and the incomming packet rate was
+		 * quite loaded and the incoming packet rate was
 		 * faster than the interrupt handler could keep up
 		 * with.
 		 */
@@ -1958,19 +1969,40 @@
 	TXD(("TX<"));
 	while (elem != hp->tx_new) {
 		struct sk_buff *skb;
-		u32 flags, dma_addr;
+		u32 flags, dma_addr, dma_len;
+		int frag;
 
 		TXD(("[%d]", elem));
 		this = &txbase[elem];
 		flags = hme_read_desc32(hp, &this->tx_flags);
 		if (flags & TXFLAG_OWN)
 			break;
-		dma_addr = hme_read_desc32(hp, &this->tx_addr);
 		skb = hp->tx_skbs[elem];
-		hme_dma_unmap(hp, dma_addr, skb->len, DMA_TODEVICE);
+		if (skb_shinfo(skb)->nr_frags) {
+			int last;
+
+			last = elem + skb_shinfo(skb)->nr_frags;
+			last &= (TX_RING_SIZE - 1);
+			flags = hme_read_desc32(hp, &txbase[last].tx_flags);
+			if (flags & TXFLAG_OWN)
+				break;
+		}
 		hp->tx_skbs[elem] = NULL;
 		hp->net_stats.tx_bytes += skb->len;
 
+		for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
+			dma_addr = hme_read_desc32(hp, &this->tx_addr);
+			dma_len = hme_read_desc32(hp, &this->tx_flags);
+
+			dma_len &= TXFLAG_SIZE;
+			hme_dma_unmap(hp, dma_addr, dma_len, DMA_TODEVICE);
+
+			if (frag != skb_shinfo(skb)->nr_frags) {
+				elem = NEXT_TX(elem);
+				this = &txbase[elem];
+			}
+		}
+
 		dev_kfree_skb_irq(skb);
 
 		hp->net_stats.tx_packets++;
@@ -2079,10 +2111,8 @@
 		}
 
 		/* This card is _fucking_ hot... */
-		if (!(csum ^ 0xffff))
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-		else
-			skb->ip_summed = CHECKSUM_NONE;
+		skb->csum = (csum ^ 0xffff);
+		skb->ip_summed = CHECKSUM_HW;
 
 		RXD(("len=%d csum=%4x]", len, csum));
 		skb->protocol = eth_type_trans(skb, dev);
@@ -2104,7 +2134,7 @@
 static void happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct net_device *dev = (struct net_device *) dev_id;
-	struct happy_meal *hp  = (struct happy_meal *) dev->priv;
+	struct happy_meal *hp  = dev->priv;
 	u32 happy_status       = hme_read32(hp, hp->gregs + GREG_STAT);
 
 	HMD(("happy_meal_interrupt: status=%08x ", happy_status));
@@ -2141,7 +2171,7 @@
 
 	for (i = 0; i < 4; i++) {
 		struct net_device *dev = qp->happy_meals[i];
-		struct happy_meal *hp  = (struct happy_meal *) dev->priv;
+		struct happy_meal *hp  = dev->priv;
 		u32 happy_status       = hme_read32(hp, hp->gregs + GREG_STAT);
 
 		HMD(("quattro_interrupt: status=%08x ", happy_status));
@@ -2179,8 +2209,7 @@
 
 static int happy_meal_open(struct net_device *dev)
 {
-	struct happy_meal *hp = (struct happy_meal *) dev->priv;
-	int res;
+	struct happy_meal *hp = dev->priv;
 
 	HMD(("happy_meal_open: "));
 
@@ -2204,16 +2233,12 @@
 	}
 
 	HMD(("to happy_meal_init\n"));
-	res = happy_meal_init(hp, 0);
-	if (!res) {
-		MOD_INC_USE_COUNT;
-	}
-	return res;
+	return happy_meal_init(hp, 0);
 }
 
 static int happy_meal_close(struct net_device *dev)
 {
-	struct happy_meal *hp = (struct happy_meal *) dev->priv;
+	struct happy_meal *hp = dev->priv;
 
 	happy_meal_stop(hp, hp->gregs);
 	happy_meal_clean_rings(hp);
@@ -2226,9 +2251,8 @@
 	 * time and never unregister.
 	 */
 	if ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO)
-		free_irq(dev->irq, (void *)dev);
+		free_irq(dev->irq, dev);
 
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -2241,7 +2265,7 @@
 #ifdef CONFIG_SBUS
 static void happy_meal_tx_timeout(struct net_device *dev)
 {
-	struct happy_meal *hp = (struct happy_meal *) dev->priv;
+	struct happy_meal *hp = dev->priv;
 
 	printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 	tx_dump_log();
@@ -2256,22 +2280,79 @@
 
 static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct happy_meal *hp = (struct happy_meal *) dev->priv;
-	int len, entry;
-	u32 mapping;
-
-	len = skb->len;
-	mapping = hme_dma_map(hp, skb->data, len, DMA_TODEVICE);
+	struct happy_meal *hp = dev->priv;
+ 	int entry;
+ 	u32 tx_flags;
+
+	tx_flags = TXFLAG_OWN;
+	if (skb->ip_summed == CHECKSUM_HW) {
+		u32 csum_start_off, csum_stuff_off;
+
+		csum_start_off = (u32) (skb->h.raw - skb->data);
+		csum_stuff_off = (u32) ((skb->h.raw + skb->csum) - skb->data);
+
+		tx_flags = (TXFLAG_OWN | TXFLAG_CSENABLE |
+			    ((csum_start_off << 14) & TXFLAG_CSBUFBEGIN) |
+			    ((csum_stuff_off << 20) & TXFLAG_CSLOCATION));
+	}
 
 	spin_lock_irq(&hp->happy_lock);
 
+ 	if (TX_BUFFS_AVAIL(hp) <= (skb_shinfo(skb)->nr_frags + 1)) {
+		netif_stop_queue(dev);
+		spin_unlock_irq(&hp->happy_lock);
+		return 1;
+	}
+
 	entry = hp->tx_new;
 	SXD(("SX<l[%d]e[%d]>", len, entry));
 	hp->tx_skbs[entry] = skb;
-	hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
-		      (TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE)),
-		      mapping);
-	hp->tx_new = NEXT_TX(entry);
+
+	if (skb_shinfo(skb)->nr_frags == 0) {
+		u32 mapping, len;
+
+		len = skb->len;
+		mapping = hme_dma_map(hp, skb->data, len, DMA_TODEVICE);
+		tx_flags |= (TXFLAG_SOP | TXFLAG_EOP);
+		hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
+			      (tx_flags | (len & TXFLAG_SIZE)),
+			      mapping);
+		entry = NEXT_TX(entry);
+	} else {
+		u32 first_len, first_mapping;
+		int frag, first_entry = entry;
+
+		/* We must give this initial chunk to the device last.
+		 * Otherwise we could race with the device.
+		 */
+		first_len = skb->len - skb->data_len;
+		first_mapping = hme_dma_map(hp, skb->data, first_len, DMA_TODEVICE);
+		entry = NEXT_TX(entry);
+
+		for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
+			skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
+			u32 len, mapping, this_txflags;
+
+			len = this_frag->size;
+			mapping = hme_dma_map(hp,
+					      ((void *) page_address(this_frag->page) +
+					       this_frag->page_offset),
+					      len, DMA_TODEVICE);
+			this_txflags = tx_flags;
+			if (frag == skb_shinfo(skb)->nr_frags - 1)
+				this_txflags |= TXFLAG_EOP;
+			hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
+				      (this_txflags | (len & TXFLAG_SIZE)),
+				      mapping);
+			entry = NEXT_TX(entry);
+		}
+		hme_write_txd(hp, &hp->happy_block->happy_meal_txd[first_entry],
+			      (tx_flags | TXFLAG_SOP | (first_len & TXFLAG_SIZE)),
+			      first_mapping);
+	}
+
+	hp->tx_new = entry;
+
 	if (TX_BUFFS_AVAIL(hp) <= 0)
 		netif_stop_queue(dev);
 
@@ -2288,7 +2369,7 @@
 
 static struct net_device_stats *happy_meal_get_stats(struct net_device *dev)
 {
-	struct happy_meal *hp = (struct happy_meal *) dev->priv;
+	struct happy_meal *hp = dev->priv;
 
 	happy_meal_get_counters(hp, hp->bigmacregs);
 	return &hp->net_stats;
@@ -2296,7 +2377,7 @@
 
 static void happy_meal_set_multicast(struct net_device *dev)
 {
-	struct happy_meal *hp = (struct happy_meal *) dev->priv;
+	struct happy_meal *hp = dev->priv;
 	unsigned long bregs = hp->bigmacregs;
 	struct dev_mc_list *dmi = dev->mc_list;
 	char *addrs;
@@ -2355,7 +2436,7 @@
 static int happy_meal_ioctl(struct net_device *dev,
 			    struct ifreq *rq, int cmd)
 {
-	struct happy_meal *hp = (struct happy_meal *) dev->priv;
+	struct happy_meal *hp = dev->priv;
 	struct ethtool_cmd *ep_user = (struct ethtool_cmd *) rq->ifr_data;
 	struct ethtool_cmd ecmd;
 
@@ -2480,27 +2561,23 @@
 	struct sbus_bus *sbus;
 	struct sbus_dev *sdev;
 	struct quattro *qp;
+	int i;
 
 	if (qfe_sbus_list == NULL)
 		goto found;
 
 	for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
-		for (sdev = qp->quattro_dev;
-		     sdev != NULL;
-		     sdev = sdev->next) {
+		for (i = 0, sdev = qp->quattro_dev;
+		     (sdev != NULL) && (i < 4);
+		     sdev = sdev->next, i++) {
 			if (sdev == goal_sdev)
 				return qp;
 		}
 	}
 	for_each_sbus(sbus) {
 		for_each_sbusdev(sdev, sbus) {
-			if (sdev->child != NULL) {
-				struct sbus_dev *p;
-
-				for (p = sdev->child; p != NULL; p = p->next)
-					if (p == goal_sdev)
-						goto found;
-			}
+			if (sdev == goal_sdev)
+				goto found;
 		}
 	}
 
@@ -2578,12 +2655,11 @@
 #endif /* CONFIG_PCI */
 
 #ifdef CONFIG_SBUS
-static int __init happy_meal_sbus_init(struct net_device *dev,
-				       struct sbus_dev *sdev,
-				       int is_qfe)
+static int __init happy_meal_sbus_init(struct sbus_dev *sdev, int is_qfe)
 {
 	struct quattro *qp = NULL;
 	struct happy_meal *hp;
+	struct net_device *dev;
 	int i, qfe_slot = -1;
 
 	if (is_qfe) {
@@ -2596,13 +2672,12 @@
 		if (qfe_slot == 4)
 			return -ENODEV;
 	}
-	if (dev == NULL) {
-		dev = init_etherdev(0, sizeof(struct happy_meal));
-	} else {
-		dev->priv = kmalloc(sizeof(struct happy_meal), GFP_KERNEL);
-		if (dev->priv == NULL)
-			return -ENOMEM;
-	}
+
+	dev = init_etherdev(NULL, sizeof(struct happy_meal));
+	if (!dev)
+		return -ENOMEM;
+	SET_MODULE_OWNER(dev);
+
 	if (hme_version_printed++ == 0)
 		printk(KERN_INFO "%s", version);
 
@@ -2637,7 +2712,7 @@
 		       dev->dev_addr[i], i == 5 ? ' ' : ':');
 	printk("\n");
 
-	hp = (struct happy_meal *) dev->priv;
+	hp = dev->priv;
 	memset(hp, 0, sizeof(*hp));
 
 	hp->happy_dev = sdev;
@@ -2733,6 +2808,9 @@
 	dev->watchdog_timeo = 5*HZ;
 	dev->do_ioctl = &happy_meal_ioctl;
 
+	/* Happy Meal can do it all... */
+	dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+
 	dev->irq = sdev->irqs[0];
 
 #if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
@@ -2766,7 +2844,7 @@
 #endif
 
 #ifdef CONFIG_PCI
-static int __init happy_meal_pci_init(struct net_device *dev, struct pci_dev *pdev)
+static int __init happy_meal_pci_init(struct pci_dev *pdev)
 {
 	struct quattro *qp = NULL;
 #ifdef __sparc__
@@ -2774,6 +2852,7 @@
 	int node;
 #endif
 	struct happy_meal *hp;
+	struct net_device *dev;
 	unsigned long hpreg_base;
 	int i, qfe_slot = -1;
 	char prom_name[64];
@@ -2803,13 +2882,12 @@
 		if (qfe_slot == 4)
 			return -ENODEV;
 	}
-	if (dev == NULL) {
-		dev = init_etherdev(0, sizeof(struct happy_meal));
-	} else {
-		dev->priv = kmalloc(sizeof(struct happy_meal), GFP_KERNEL);
-		if (dev->priv == NULL)
-			return -ENOMEM;
-	}
+
+	dev = init_etherdev(NULL, sizeof(struct happy_meal));
+	if (!dev)
+		return -ENOMEM;
+	SET_MODULE_OWNER(dev);
+
 	if (hme_version_printed++ == 0)
 		printk(KERN_INFO "%s", version);
 
@@ -2856,7 +2934,10 @@
 		printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n");
 		return -ENODEV;
 	}
-	hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000);
+	if ((hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000)) == 0) {
+		printk(KERN_ERR "happymeal(PCI): Unable to remap card memory.\n");
+		return -ENODEV;
+	}
 
 	for (i = 0; i < 6; i++) {
 		if (macaddr[i] != 0)
@@ -2941,6 +3022,9 @@
 	dev->irq = pdev->irq;
 	dev->dma = 0;
 
+	/* Happy Meal can do it all... */
+	dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+
 #if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
 	/* Hook up PCI register/dma accessors. */
 	hp->read_desc32 = pci_hme_read_desc32;
@@ -2972,25 +3056,29 @@
 #endif
 
 #ifdef CONFIG_SBUS
-static int __init happy_meal_sbus_probe(struct net_device *dev)
+static int __init happy_meal_sbus_probe(void)
 {
 	struct sbus_bus *sbus;
 	struct sbus_dev *sdev;
 	int cards = 0;
+	char model[128];
 
 	for_each_sbus(sbus) {
 		for_each_sbusdev(sdev, sbus) {
 			char *name = sdev->prom_name;
 
-			if (cards)
-				dev = NULL;
 			if (!strcmp(name, "SUNW,hme")) {
 				cards++;
-				happy_meal_sbus_init(dev, sdev, 0);
+				prom_getstring(sdev->prom_node, "model",
+					       model, sizeof(model));
+				if (!strcmp(model, "SUNW,sbus-qfe"))
+					happy_meal_sbus_init(sdev, 1);
+				else
+					happy_meal_sbus_init(sdev, 0);
 			} else if (!strcmp(name, "qfe") ||
 				   !strcmp(name, "SUNW,qfe")) {
 				cards++;
-				happy_meal_sbus_init(dev, sdev, 1);
+				happy_meal_sbus_init(sdev, 1);
 			}
 		}
 	}
@@ -3001,7 +3089,7 @@
 #endif
 
 #ifdef CONFIG_PCI
-static int __init happy_meal_pci_probe(struct net_device *dev)
+static int __init happy_meal_pci_probe(void)
 {
 	struct pci_dev *pdev = NULL;
 	int cards = 0;
@@ -3010,10 +3098,8 @@
 				       PCI_DEVICE_ID_SUN_HAPPYMEAL, pdev)) != NULL) {
 		if (pci_enable_device(pdev))
 			continue;
-		if (cards)
-			dev = NULL;
 		cards++;
-		happy_meal_pci_init(dev, pdev);
+		happy_meal_pci_init(pdev);
 	}
 	return cards;
 }
@@ -3021,7 +3107,6 @@
 
 static int __init happy_meal_probe(void)
 {
-	struct net_device *dev = NULL;
 	static int called = 0;
 	int cards;
 
@@ -3033,12 +3118,10 @@
 
 	cards = 0;
 #ifdef CONFIG_SBUS
-	cards += happy_meal_sbus_probe(dev);
-	if (cards != 0)
-		dev = NULL;
+	cards += happy_meal_sbus_probe();
 #endif
 #ifdef CONFIG_PCI
-	cards += happy_meal_pci_probe(dev);
+	cards += happy_meal_pci_probe();
 #endif
 	if (!cards)
 		return -ENODEV;
@@ -3048,12 +3131,23 @@
 
 static void __exit happy_meal_cleanup_module(void)
 {
+#ifdef CONFIG_SBUS
+	struct quattro *last_seen_qfe = NULL;
+#endif
+
 	while (root_happy_dev) {
 		struct happy_meal *hp = root_happy_dev;
 		struct happy_meal *next = root_happy_dev->next_module;
 
 #ifdef CONFIG_SBUS
 		if (!(hp->happy_flags & HFLAG_PCI)) {
+			if (hp->happy_flags & HFLAG_QUATTRO) {
+				if (hp->qfe_parent != last_seen_qfe) {
+					free_irq(hp->dev->irq, hp->qfe_parent);
+					last_seen_qfe = hp->qfe_parent;
+				}
+			}
+
 			sbus_iounmap(hp->gregs, GREG_REG_SIZE);
 			sbus_iounmap(hp->etxregs, ETX_REG_SIZE);
 			sbus_iounmap(hp->erxregs, ERX_REG_SIZE);
@@ -3071,6 +3165,7 @@
 					    PAGE_SIZE,
 					    hp->happy_block,
 					    hp->hblock_dvma);
+			iounmap((void *)hp->gregs);
 		}
 #endif
 		unregister_netdev(hp->dev);
diff -urN linux-2.4.4-pre2/drivers/net/sunhme.h linux/drivers/net/sunhme.h
--- linux-2.4.4-pre2/drivers/net/sunhme.h	Mon Dec 11 13:01:05 2000
+++ linux/drivers/net/sunhme.h	Mon Dec  3 22:34:50 2001
@@ -1,4 +1,4 @@
-/* $Id: sunhme.h,v 1.31 2000/11/12 10:23:30 davem Exp $
+/* $Id: sunhme.h,v 1.32 2000/12/13 18:31:47 davem Exp $
  * sunhme.h: Definitions for Sparc HME/BigMac 10/100baseT ethernet driver.
  *           Also known as the "Happy Meal".
  *
@@ -151,7 +151,8 @@
 #define ERX_CFG_SIZE128      0x00000400 /* Receive ring size == 128  */
 #define ERX_CFG_SIZE256      0x00000600 /* Receive ring size == 256  */
 #define ERX_CFG_RESV3        0x0000f800 /* Unused...                 */
-#define ERX_CFG_CSUMSTART    0x007f0000 /* Offset of checksum start  */
+#define ERX_CFG_CSUMSTART    0x007f0000 /* Offset of checksum start,
+					 * in halfwords. */
 
 /* I'd like a Big Mac, small fries, small coke, and SparcLinux please. */
 #define BMAC_XIFCFG	0x0000UL	/* XIF config register                */
@@ -445,14 +446,31 @@
 #define TX_RING_SIZE       32         /* Must be >16 and <255, multiple of 16  */
 #define RX_RING_SIZE       32         /* see ERX_CFG_SIZE* for possible values */
 
+#if (TX_RING_SIZE < 16 || TX_RING_SIZE > 256 || (TX_RING_SIZE % 16) != 0)
+#error TX_RING_SIZE holds illegal value
+#endif
+
 #define TX_RING_MAXSIZE    256
 #define RX_RING_MAXSIZE    256
 
-/* 34 byte offset for checksum computation.  This works because ip_input() will clear out
- * the skb->csum and skb->ip_summed fields and recompute the csum if IP options are
- * present in the header.  34 == (ethernet header len) + sizeof(struct iphdr)
- */
-#define ERX_CFG_DEFAULT(off) (ERX_CFG_DMAENABLE|((off)<<3)|ERX_CFG_SIZE32|(0x22<<16))
+/* We use a 14 byte offset for checksum computation. */
+#if (RX_RING_SIZE == 32)
+#define ERX_CFG_DEFAULT(off) (ERX_CFG_DMAENABLE|((off)<<3)|ERX_CFG_SIZE32|((14/2)<<16))
+#else
+#if (RX_RING_SIZE == 64)
+#define ERX_CFG_DEFAULT(off) (ERX_CFG_DMAENABLE|((off)<<3)|ERX_CFG_SIZE64|((14/2)<<16))
+#else
+#if (RX_RING_SIZE == 128)
+#define ERX_CFG_DEFAULT(off) (ERX_CFG_DMAENABLE|((off)<<3)|ERX_CFG_SIZE128|((14/2)<<16))
+#else
+#if (RX_RING_SIZE == 256)
+#define ERX_CFG_DEFAULT(off) (ERX_CFG_DMAENABLE|((off)<<3)|ERX_CFG_SIZE256|((14/2)<<16))
+#else
+#error RX_RING_SIZE holds illegal value
+#endif
+#endif
+#endif
+#endif
 
 #define NEXT_RX(num)       (((num) + 1) & (RX_RING_SIZE - 1))
 #define NEXT_TX(num)       (((num) + 1) & (TX_RING_SIZE - 1))
diff -urN linux-2.4.4-pre2/drivers/net/wan/Config.in linux/drivers/net/wan/Config.in
--- linux-2.4.4-pre2/drivers/net/wan/Config.in	Tue Mar  6 19:44:37 2001
+++ linux/drivers/net/wan/Config.in	Mon Dec  3 22:34:50 2001
@@ -75,15 +75,13 @@
    if [ "$CONFIG_WAN_ROUTER" != "n" ]; then
       bool '  WAN router drivers' CONFIG_WAN_ROUTER_DRIVERS
       if [ "$CONFIG_WAN_ROUTER_DRIVERS" = "y" ]; then
-	 dep_tristate '    Sangoma WANPIPE(tm) multiprotocol cards' CONFIG_VENDOR_SANGOMA $CONFIG_WAN_ROUTER_DRIVERS
+	 dep_tristate '    Sangoma WANPIPE(tm) multiprotocol cards' CONFIG_VENDOR_SANGOMA $CONFIG_WAN_ROUTER
 	 if [ "$CONFIG_VENDOR_SANGOMA" != "n" ]; then
-	    int '      Maximum number of cards' CONFIG_WANPIPE_CARDS 1
 	    bool '      WANPIPE Cisco HDLC support' CONFIG_WANPIPE_CHDLC
-	    if [ "$CONFIG_OBSOLETE" = "y" ]; then
-	       bool '      WANPIPE Frame Relay support (OBSOLETE)' CONFIG_WANPIPE_FR
-	       bool '      WANPIPE X.25 support (OBSOLETE)' CONFIG_WANPIPE_X25
-	    fi
+	    bool '      WANPIPE Frame Relay support' CONFIG_WANPIPE_FR
+	    bool '      WANPIPE X.25 support' CONFIG_WANPIPE_X25
 	    bool '      WANPIPE PPP support' CONFIG_WANPIPE_PPP
+	    bool '      WANPIPE Multi-Port PPP support' CONFIG_WANPIPE_MULTPPP
 	 fi
 	 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 	    dep_tristate '    Cyclom 2X(tm) cards (EXPERIMENTAL)' CONFIG_CYCLADES_SYNC $CONFIG_WAN_ROUTER_DRIVERS
diff -urN linux-2.4.4-pre2/drivers/net/wan/Makefile linux/drivers/net/wan/Makefile
--- linux-2.4.4-pre2/drivers/net/wan/Makefile	Tue Mar  6 19:44:37 2001
+++ linux/drivers/net/wan/Makefile	Mon Dec  3 22:34:50 2001
@@ -12,11 +12,12 @@
 export-objs =	z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o
 list-multi =	wanpipe.o cyclomx.o
 
-wanpipe-objs = sdlamain.o $(wanpipe-y)
+wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y)
 wanpipe-$(CONFIG_WANPIPE_X25)	+= sdla_x25.o
 wanpipe-$(CONFIG_WANPIPE_FR)	+= sdla_fr.o
 wanpipe-$(CONFIG_WANPIPE_CHDLC)	+= sdla_chdlc.o
 wanpipe-$(CONFIG_WANPIPE_PPP)	+= sdla_ppp.o
+wanpipe-$(CONFIG_WANPIPE_MULTPPP) += wanpipe_multppp.o
 
 cyclomx-objs = cycx_main.o $(cyclomx-y)  
 cyclomx-$(CONFIG_CYCLOMX_X25)	+= cycx_x25.o
@@ -45,7 +46,11 @@
  
 obj-$(CONFIG_DLCI)		+= dlci.o 
 obj-$(CONFIG_SDLA)		+= sdla.o
-obj-$(CONFIG_VENDOR_SANGOMA)	+= sdladrv.o wanpipe.o
+ifeq ($(CONFIG_WANPIPE_MULTPPP),y)
+  obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o syncppp.o	
+else
+  obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o
+endif
 obj-$(CONFIG_CYCLADES_SYNC)	+= cycx_drv.o cyclomx.o
 obj-$(CONFIG_LAPBETHER)		+= lapbether.o
 obj-$(CONFIG_SBNI)		+= sbni.o
diff -urN linux-2.4.4-pre2/drivers/net/wan/sdla_chdlc.c linux/drivers/net/wan/sdla_chdlc.c
--- linux-2.4.4-pre2/drivers/net/wan/sdla_chdlc.c	Tue Mar  6 19:44:36 2001
+++ linux/drivers/net/wan/sdla_chdlc.c	Mon Dec  3 22:34:50 2001
@@ -4,14 +4,40 @@
 * Authors: 	Nenad Corbic <ncorbic@sangoma.com>
 *		Gideon Hack  
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2001 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
-* Feb 28, 2000  Jeff Garzik	softnet updates
+* Feb 28, 2001  Nenad Corbic	Updated if_tx_timeout() routine for 
+* 				2.4.X kernels.
+* Jan 25, 2001  Nenad Corbic	Added a TTY Sync serial driver over the
+* 				HDLC streaming protocol
+* 				Added a TTY Async serial driver over the
+* 				Async protocol.
+* Dec 15, 2000  Nenad Corbic    Updated for 2.4.X Kernel support
+* Nov 13, 2000  Nenad Corbic    Added true interface type encoding option.
+* 				Tcpdump doesn't support CHDLC inteface
+* 				types, to fix this "true type" option will set
+* 				the interface type to RAW IP mode.
+* Nov 07, 2000  Nenad Corbic	Added security features for UDP debugging:
+*                               Deny all and specify allowed requests.
+* Jun 20, 2000  Nenad Corbic	Fixed the API IP ERROR bug. Caused by the 
+*                               latest update.
+* May 09, 2000	Nenad Corbic	Option to bring down an interface
+*                               upon disconnect.
+* Mar 23, 2000  Nenad Corbic	Improved task queue, bh handling.
+* Mar 16, 2000	Nenad Corbic	Fixed the SLARP Dynamic IP addressing.
+* Mar 06, 2000  Nenad Corbic	Bug Fix: corrupted mbox recovery.
+* Feb 10, 2000  Gideon Hack     Added ASYNC support.
+* Feb 09, 2000  Nenad Corbic    Fixed two shutdown bugs in update() and
+*                               if_stats() functions.
+* Jan 24, 2000  Nenad Corbic    Fixed a startup wanpipe state racing,  
+*                               condition between if_open and isr. 
+* Jan 10, 2000  Nenad Corbic    Added new socket API support.
+* Dev 15, 1999  Nenad Corbic    Fixed up header files for 2.0.X kernels
 * Nov 20, 1999  Nenad Corbic 	Fixed zero length API bug.
 * Sep 30, 1999  Nenad Corbic    Fixed dynamic IP and route setup.
 * Sep 23, 1999  Nenad Corbic    Added SMP support, fixed tracing 
@@ -22,18 +48,26 @@
 * Aug 07, 1998	David Fong	Initial version.
 *****************************************************************************/
 
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <linux/if_arp.h>	/* ARPHRD_* defines */
-#include <linux/inetdevice.h>
-#include <asm/uaccess.h>
+
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ #include <asm/uaccess.h>
+ #include <linux/inetdevice.h>
+ #include <linux/netdevice.h>
+#else 				
+ #include <asm/segment.h>
+ #include <net/route.h>          /* Adding new route entries : 2.0.X kernels */
+#endif
+
 #include <linux/in.h>		/* sockaddr_in */
 #include <linux/inet.h>	
 #include <linux/if.h>
@@ -42,32 +76,42 @@
 #include <asm/io.h>
 
 #include <linux/sdla_chdlc.h>		/* CHDLC firmware API definitions */
+#include <linux/sdla_asy.h>           	/* CHDLC (async) API definitions */
 
-/****** Defines & Macros ****************************************************/
+#include <linux/if_wanpipe_common.h>    /* Socket Driver common area */
+#include <linux/if_wanpipe.h>		
+
+/* TTY Includes */
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
 
-#ifdef	_DEBUG_
-#define	STATIC
-#else
-#define	STATIC		static
-#endif
+
+/****** Defines & Macros ****************************************************/
 
 /* reasons for enabling the timer interrupt on the adapter */
-#define TMR_INT_ENABLED_UDP   	0x0001
-#define TMR_INT_ENABLED_UPDATE	0x0002
- 
+#define TMR_INT_ENABLED_UDP   		0x01
+#define TMR_INT_ENABLED_UPDATE		0x02
+#define TMR_INT_ENABLED_CONFIG		0x10
+
+#define MAX_IP_ERRORS	10
+
+#define TTY_CHDLC_MAX_MTU	2000
 #define	CHDLC_DFLT_DATA_LEN	1500		/* default MTU */
 #define CHDLC_HDR_LEN		1
 
-#define IFF_POINTTOPOINT 0x10
-
-#define WANPIPE 0x00
-#define API	0x01
 #define CHDLC_API 0x01
 
 #define PORT(x)   (x == 0 ? "PRIMARY" : "SECONDARY" )
+#define MAX_BH_BUFF	10
+
+//#define PRINT_DEBUG
+#ifdef PRINT_DEBUG
+#define dbg_printk(format, a...) printk(format, ## a)
+#else
+#define dbg_printk(format, a...)
+#endif  
 
-#define TX_TIMEOUT	(5*HZ)
- 
 /******Data Structures*****************************************************/
 
 /* This structure is placed in the private data area of the device structure.
@@ -77,9 +121,7 @@
 
 typedef struct chdlc_private_area
 {
-	/* This member must be first. */
-	struct net_device *slave;		/* WAN slave */
-
+	wanpipe_common_t common;
 	sdla_t		*card;
 	int 		TracingEnabled;		/* For enabling Tracing */
 	unsigned long 	curr_trace_addr;	/* Used for Tracing */
@@ -96,12 +138,38 @@
 	unsigned long 	router_up_time;
         u32             IP_address;		/* IP addressing */
         u32             IP_netmask;
+	u32		ip_local;
+	u32		ip_remote;
+	u32 		ip_local_tmp;
+	u32		ip_remote_tmp;
+	u8		ip_error;
+	u8		config_chdlc;
+	u8 		config_chdlc_timeout;
 	unsigned char  mc;			/* Mulitcast support on/off */
 	unsigned short udp_pkt_lgth;		/* udp packet processing */
 	char udp_pkt_src;
 	char udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT];
 	unsigned short timer_int_enabled;
 	char update_comms_stats;		/* updating comms stats */
+
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	bh_data_t *bh_head;	  	  /* Circular buffer for chdlc_bh */
+	unsigned long  tq_working;
+	volatile int  bh_write;
+	volatile int  bh_read;
+	atomic_t  bh_buff_used;
+      #endif
+	
+	unsigned char interface_down;
+
+	/* Polling task queue. Each interface
+         * has its own task queue, which is used
+         * to defer events from the interrupt */
+	struct tq_struct poll_task;
+	struct timer_list poll_delay_timer;
+
+	u8 gateway;
+	u8 true_if_encoding;
 	//FIXME: add driver stats as per frame relay!
 
 } chdlc_private_area_t;
@@ -125,41 +193,47 @@
 /****** Function Prototypes *************************************************/
 /* WAN link driver entry points. These are called by the WAN router module. */
 static int update (wan_device_t* wandev);
-static int new_if (wan_device_t* wandev, struct net_device* dev,
+static int new_if (wan_device_t* wandev, netdevice_t* dev,
 	wanif_conf_t* conf);
-static int del_if (wan_device_t* wandev, struct net_device* dev);
 
 /* Network device interface */
-static int if_init   (struct net_device* dev);
-static int if_open   (struct net_device* dev);
-static int if_close  (struct net_device* dev);
-static void if_tx_timeout (struct net_device *dev);
-static int if_header (struct sk_buff* skb, struct net_device* dev,
+static int if_init   (netdevice_t* dev);
+static int if_open   (netdevice_t* dev);
+static int if_close  (netdevice_t* dev);
+static int if_header (struct sk_buff* skb, netdevice_t* dev,
 	unsigned short type, void* daddr, void* saddr, unsigned len);
-#ifdef LINUX_2_1
-static int if_rebuild_hdr (struct sk_buff *skb);
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+  static int if_rebuild_hdr (struct sk_buff *skb);
+  static struct net_device_stats* if_stats (netdevice_t* dev);
+
 #else
-static int if_rebuild_hdr (void* hdr, struct net_device* dev, unsigned long raddr,
+  static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
         struct sk_buff* skb);
+  static struct enet_statistics* if_stats (netdevice_t* dev);
 #endif
-static int if_send (struct sk_buff* skb, struct net_device* dev);
-static struct net_device_stats* if_stats (struct net_device* dev);
+  
+static int if_send (struct sk_buff* skb, netdevice_t* dev);
 
 /* CHDLC Firmware interface functions */
 static int chdlc_configure 	(sdla_t* card, void* data);
 static int chdlc_comm_enable 	(sdla_t* card);
-static int chdlc_comm_disable 	(sdla_t* card);
 static int chdlc_read_version 	(sdla_t* card, char* str);
 static int chdlc_set_intr_mode 	(sdla_t* card, unsigned mode);
 static int chdlc_send (sdla_t* card, void* data, unsigned len);
 static int chdlc_read_comm_err_stats (sdla_t* card);
 static int chdlc_read_op_stats (sdla_t* card);
+static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb);
 
 
+static int chdlc_disable_comm_shutdown (sdla_t *card);
+#ifdef LINUX_2_4
+  static void if_tx_timeout (netdevice_t *dev);
+#endif
+
 /* Miscellaneous CHDLC Functions */
 static int set_chdlc_config (sdla_t* card);
-static void init_chdlc_tx_rx_buff( sdla_t* card, struct net_device *dev );
-static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb);
+static void init_chdlc_tx_rx_buff( sdla_t* card);
 static int process_chdlc_exception(sdla_t *card);
 static int process_global_exception(sdla_t *card);
 static int update_comms_stats(sdla_t* card,
@@ -168,23 +242,40 @@
 static int unconfigure_ip (sdla_t* card);
 static void process_route(sdla_t *card);
 static void port_set_state (sdla_t *card, int);
+static int config_chdlc (sdla_t *card);
+static void disable_comm (sdla_t *card);
+
+static void trigger_chdlc_poll (netdevice_t *);
+static void chdlc_poll (netdevice_t *);
+static void chdlc_poll_delay (unsigned long dev_ptr);
 
 
+/* Miscellaneous asynchronous interface Functions */
+static int set_asy_config (sdla_t* card);
+static int asy_comm_enable (sdla_t* card);
+
 /* Interrupt handlers */
 static void wpc_isr (sdla_t* card);
 static void rx_intr (sdla_t* card);
 static void timer_intr(sdla_t *);
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+  /* Bottom half handlers */
+  static void chdlc_bh (netdevice_t *);
+  static int chdlc_bh_cleanup (netdevice_t *);
+  static int bh_enqueue (netdevice_t *, struct sk_buff *);
+#endif
+
 /* Miscellaneous functions */
-static int chk_bcast_mcast_addr(sdla_t* card, struct net_device* dev,
+static int chk_bcast_mcast_addr(sdla_t* card, netdevice_t* dev,
 				struct sk_buff *skb);
 static int reply_udp( unsigned char *data, unsigned int mbox_len );
-static int intr_test( sdla_t* card, struct net_device *dev );
+static int intr_test( sdla_t* card);
 static int udp_pkt_type( struct sk_buff *skb , sdla_t* card);
 static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card,
-                                struct sk_buff *skb, struct net_device* dev,
+                                struct sk_buff *skb, netdevice_t* dev,
                                 chdlc_private_area_t* chdlc_priv_area);
-static int process_udp_mgmt_pkt(sdla_t* card, struct net_device* dev,  
+static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev,  
 				chdlc_private_area_t* chdlc_priv_area);
 static unsigned short calc_checksum (char *, int);
 static void s508_lock (sdla_t *card, unsigned long *smp_flags);
@@ -192,6 +283,46 @@
 
 
 static int  Intr_test_counter;
+
+/* TTY Global Definitions */
+
+#if defined(LINUX_2_4) || defined(LINUX_2_1)
+
+#define NR_PORTS 4
+#define WAN_TTY_MAJOR 226
+#define WAN_TTY_MINOR 0
+
+#define WAN_CARD(port) (tty_card_map[port])
+#define MIN_PORT 0
+#define MAX_PORT NR_PORTS-1 
+
+#define CRC_LENGTH 2
+
+static int wanpipe_tty_init(sdla_t *card);
+static void wanpipe_tty_receive(sdla_t *, unsigned, unsigned int);
+static void wanpipe_tty_trigger_poll(sdla_t *card);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount=1;
+static int tty_init_cnt=0;
+
+static struct serial_state rs_table[NR_PORTS];
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+static char tty_driver_mode=WANOPT_TTY_SYNC;
+
+static char *opt_decode[] = {"NONE","CRTSCTS","XONXOFF-RX",
+	  	             "CRTSCTS XONXOFF-RX","XONXOFF-TX",
+		             "CRTSCTS XONXOFF-TX","CRTSCTS XONXOFF"};
+static char *p_decode[] = {"NONE","ODD","EVEN"};
+
+static void* tty_card_map[NR_PORTS] = {NULL,NULL,NULL,NULL};
+
+#endif
+
+
 /****** Public Functions ****************************************************/
 
 /*============================================================================
@@ -211,6 +342,7 @@
 	unsigned char port_num;
 	int err;
 	unsigned long max_permitted_baud = 0;
+	SHARED_MEMORY_INFO_STRUCT *flags;
 
 	union
 		{
@@ -234,7 +366,7 @@
 			if (conf->comm_port != card->next->u.c.comm_port){
 				card->u.c.comm_port = conf->comm_port;
 			}else{
-				printk(KERN_ERR "%s: ERROR - %s port used!\n",
+				printk(KERN_INFO "%s: ERROR - %s port used!\n",
         		        	card->wandev.name, PORT(conf->comm_port));
 				return -EINVAL;
 			}
@@ -242,7 +374,7 @@
 			card->u.c.comm_port = conf->comm_port;
 		}
 	}else{
-		printk(KERN_ERR "%s: ERROR - Invalid Port Selected!\n",
+		printk(KERN_INFO "%s: ERROR - Invalid Port Selected!\n",
                 			card->wandev.name);
 		return -EINVAL;
 	}
@@ -305,15 +437,11 @@
 	card->exec			= NULL;
 	card->wandev.update		= &update;
  	card->wandev.new_if		= &new_if;
-	card->wandev.del_if		= &del_if;
-	card->wandev.state		= WAN_DUALPORT;
+	card->wandev.del_if		= NULL;
 	card->wandev.udp_port   	= conf->udp_port;
-
+	card->disable_comm		= &disable_comm;
 	card->wandev.new_if_cnt = 0;
 
-	/* This is for the ports link state */
-	card->u.c.state = WAN_DISCONNECTED;
-	
 	/* reset the number of times the 'update()' proc has been called */
 	card->u.c.update_call_count = 0;
 	
@@ -331,18 +459,27 @@
 
 	port_num = card->u.c.comm_port;
 
+	/* in API mode, we can configure for "receive only" buffering */
+	if(card->hw.type == SDLA_S514) {
+		card->u.c.receive_only = conf->receive_only;
+		if(conf->receive_only) {
+			printk(KERN_INFO
+				"%s: Configured for 'receive only' mode\n",
+                                card->devname);
+		}
+	}
+
 	/* Setup Port Bps */
 
 	if(card->wandev.clocking) {
-                      
-		if(port_num == WANOPT_PRI) {
+		if((port_num == WANOPT_PRI) || card->u.c.receive_only) {
 			/* For Primary Port 0 */
                		max_permitted_baud =
 				(card->hw.type == SDLA_S514) ?
 				PRI_MAX_BAUD_RATE_S514 : 
 				PRI_MAX_BAUD_RATE_S508;
-		}
-		else if(port_num == WANOPT_SEC) {
+
+		}else if(port_num == WANOPT_SEC) {
 			/* For Secondary Port 1 */
                         max_permitted_baud =
                                (card->hw.type == SDLA_S514) ?
@@ -357,14 +494,14 @@
  				printk(KERN_INFO "%s: Baud rate set to %lu bps\n", 
 					card->wandev.name, max_permitted_baud);
 			}
-                             
 			card->wandev.bps = conf->bps;
 	}else{
         	card->wandev.bps = 0;
   	}
 
 	/* Setup the Port MTU */
-	if(port_num == WANOPT_PRI) {
+	if((port_num == WANOPT_PRI) || card->u.c.receive_only) {
+
 		/* For Primary Port 0 */
 		card->wandev.mtu =
 			(conf->mtu >= MIN_LGTH_CHDLC_DATA_CFG) ?
@@ -387,8 +524,6 @@
 	mb1->command = READ_CHDLC_CONFIGURATION;
 	err = sdla_exec(mb1) ? mb1->return_code : CMD_TIMEOUT;
 	if(err != COMMAND_OK) {
-		clear_bit(1, (void*)&card->wandev.critical);
-
                 if(card->hw.type != SDLA_S514)
                 	enable_irq(card->hw.irq);
 
@@ -406,6 +541,72 @@
 			ptr_shared_mem_info_struct % SDLA_WINDOWSIZE));
 	}
 
+	flags = card->u.c.flags;
+	
+	/* This is for the ports link state */
+	card->wandev.state = WAN_DUALPORT;
+	card->u.c.state = WAN_DISCONNECTED;
+
+
+	if (!card->wandev.piggyback){	
+		int err;
+
+		/* Perform interrupt testing */
+		err = intr_test(card);
+
+		if(err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) { 
+			printk(KERN_INFO "%s: Interrupt test failed (%i)\n",
+					card->devname, Intr_test_counter);
+			printk(KERN_INFO "%s: Please choose another interrupt\n",
+					card->devname);
+			return -EIO;
+		}
+		
+		printk(KERN_INFO "%s: Interrupt test passed (%i)\n", 
+				card->devname, Intr_test_counter);
+		card->configured = 1;
+	}
+
+	if ((card->tty_opt=conf->tty) == WANOPT_YES){
+              #if defined(LINUX_2_4) || defined(LINUX_2_1)	
+		int err;
+		card->tty_minor = conf->tty_minor;
+
+		/* On ASYNC connections internal clocking 
+		 * is mandatory */
+		if ((card->u.c.async_mode = conf->tty_mode)){
+			card->wandev.clocking = 1;
+		}
+		err=wanpipe_tty_init(card);
+		if (err){
+			return err;
+		}
+              #else
+		printk(KERN_INFO "%s: Error: TTY driver is not supported on 2.0.X kernels!\n",
+				card->devname);	
+		return -EINVAL;  
+              #endif
+	}else{
+	
+
+		if (chdlc_set_intr_mode(card, APP_INT_ON_TIMER)){
+			printk (KERN_INFO "%s: 
+				Failed to set interrupt triggers!\n",
+				card->devname);
+			return -EIO;	
+        	}
+	
+		/* Mask the Timer interrupt */
+		flags->interrupt_info_struct.interrupt_permission &= 
+			~APP_INT_ON_TIMER;
+	}
+
+	/* If we are using CHDLC in backup mode, this flag will
+	 * indicate not to look for IP addresses in config_chdlc()*/
+	card->u.c.backup = conf->backup;
+	
+	printk(KERN_INFO "\n");
+
 	return 0;
 }
 
@@ -427,8 +628,8 @@
 static int update (wan_device_t* wandev)
 {
 	sdla_t* card = wandev->private;
- 	struct net_device* dev = card->wandev.dev;
-        volatile chdlc_private_area_t* chdlc_priv_area = dev->priv;
+ 	netdevice_t* dev;
+        volatile chdlc_private_area_t* chdlc_priv_area;
         SHARED_MEMORY_INFO_STRUCT *flags;
 	unsigned long timeout;
 
@@ -442,10 +643,14 @@
 	/* more sanity checks */
         if(!card->u.c.flags)
                 return -ENODEV;
-	if(test_bit(1, (void*)&card->wandev.critical))
+
+	if(test_bit(PERI_CRIT, (void*)&card->wandev.critical))
                 return -EAGAIN;
 
-	if(!netif_running(dev))
+	if((dev=card->wandev.dev) == NULL)
+		return -ENODEV;
+
+	if((chdlc_priv_area=dev->priv) == NULL)
 		return -ENODEV;
 
       	flags = card->u.c.flags;
@@ -488,13 +693,17 @@
  * Return:	0	o.k.
  *		< 0	failure (channel will not be created)
  */
-static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* conf)
+static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf)
 {
 	sdla_t* card = wandev->private;
 	chdlc_private_area_t* chdlc_priv_area;
 
+
+	printk(KERN_INFO "%s: Configuring Interface: %s\n",
+			card->devname, conf->name);
+ 
 	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
-		printk(KERN_INFO "%s: invalid interface name!\n",
+		printk(KERN_INFO "%s: Invalid interface name!\n",
 			card->devname);
 		return -EINVAL;
 	}
@@ -506,9 +715,11 @@
 		return -ENOMEM;
 
 	memset(chdlc_priv_area, 0, sizeof(chdlc_private_area_t));
-	
+
 	chdlc_priv_area->card = card; 
-	
+	chdlc_priv_area->common.sk = NULL;
+	chdlc_priv_area->common.func = NULL;	
+
 	/* initialize data */
 	strcpy(card->u.c.if_name, conf->name);
 
@@ -523,110 +734,216 @@
 	chdlc_priv_area->route_status = NO_ROUTE;
 	chdlc_priv_area->route_removed = 0;
 
-	/* Setup protocol options */
+	card->u.c.async_mode = conf->async_mode;
+	
+	/* setup for asynchronous mode */
+	if(conf->async_mode) {
+		printk(KERN_INFO "%s: Configuring for asynchronous mode\n",
+			wandev->name);
 
-	card->u.c.protocol_options = 0;
+		if(card->u.c.comm_port == WANOPT_PRI) {
+			printk(KERN_INFO
+				"%s:Asynchronous mode on secondary port only\n",
+					wandev->name);
+			kfree(chdlc_priv_area);
+			return -EINVAL;
+		}
 
-	if (conf->ignore_dcd == WANOPT_YES){
-		card->u.c.protocol_options |= IGNORE_DCD_FOR_LINK_STAT;
-	}
+	       	if(strcmp(conf->usedby, "WANPIPE") == 0) {
+			printk(KERN_INFO
+                                "%s: Running in WANIPE Async Mode\n",                                        			wandev->name);
+			card->u.c.usedby = WANPIPE;
+		}else{
+			card->u.c.usedby = API;
+		}
 
-	if (conf->ignore_cts == WANOPT_YES){
-		card->u.c.protocol_options |= IGNORE_CTS_FOR_LINK_STAT;
-	}
+		if(!card->wandev.clocking) {
+			printk(KERN_INFO
+				"%s: Asynch. clocking must be 'Internal'\n",
+				wandev->name);
+			kfree(chdlc_priv_area);
+			return -EINVAL;
+		}
 
-	if (conf->ignore_keepalive == WANOPT_YES) {
-		card->u.c.protocol_options |= IGNORE_KPALV_FOR_LINK_STAT;
-		card->u.c.kpalv_tx  = MIN_Tx_KPALV_TIMER; 
-		card->u.c.kpalv_rx  = MIN_Rx_KPALV_TIMER; 
-		card->u.c.kpalv_err = MIN_KPALV_ERR_TOL; 
+		if((card->wandev.bps < MIN_ASY_BAUD_RATE) ||
+			(card->wandev.bps > MAX_ASY_BAUD_RATE)) {
+			printk(KERN_INFO "%s: Selected baud rate is invalid.\n",
+				wandev->name);
+			printk(KERN_INFO "Must be between %u and %u bps.\n",
+				MIN_ASY_BAUD_RATE, MAX_ASY_BAUD_RATE);
+			kfree(chdlc_priv_area);
+			return -EINVAL;
+		}
 
-	} else {   /* Do not ignore keepalives */
+		card->u.c.api_options = 0;
+                if (conf->asy_data_trans == WANOPT_YES) {
+                        card->u.c.api_options |= ASY_RX_DATA_TRANSPARENT;
+                }
+		
+		card->u.c.protocol_options = 0;
+		if (conf->rts_hs_for_receive == WANOPT_YES) {
+			card->u.c.protocol_options |= ASY_RTS_HS_FOR_RX;
+	        }
+                if (conf->xon_xoff_hs_for_receive == WANOPT_YES) {
+                        card->u.c.protocol_options |= ASY_XON_XOFF_HS_FOR_RX;
+                }
+                if (conf->xon_xoff_hs_for_transmit == WANOPT_YES) {
+                        card->u.c.protocol_options |= ASY_XON_XOFF_HS_FOR_TX;
+                }
+                if (conf->dcd_hs_for_transmit == WANOPT_YES) {
+                        card->u.c.protocol_options |= ASY_DCD_HS_FOR_TX;
+                }
+                if (conf->cts_hs_for_transmit == WANOPT_YES) {
+                        card->u.c.protocol_options |= ASY_CTS_HS_FOR_TX;
+                }
 
-		card->u.c.kpalv_tx =
-  	   		(conf->keepalive_tx_tmr - MIN_Tx_KPALV_TIMER) >= 0 ?
-	   		min (conf->keepalive_tx_tmr, MAX_Tx_KPALV_TIMER) :
-	   					DEFAULT_Tx_KPALV_TIMER;
+		card->u.c.tx_bits_per_char = conf->tx_bits_per_char;
+                card->u.c.rx_bits_per_char = conf->rx_bits_per_char;
+                card->u.c.stop_bits = conf->stop_bits;
+		card->u.c.parity = conf->parity;
+		card->u.c.break_timer = conf->break_timer;
+		card->u.c.inter_char_timer = conf->inter_char_timer;
+		card->u.c.rx_complete_length = conf->rx_complete_length;
+		card->u.c.xon_char = conf->xon_char;
+
+	} else {	/* setup for synchronous mode */
+
+		card->u.c.protocol_options = 0;
+		if (conf->ignore_dcd == WANOPT_YES){
+			card->u.c.protocol_options |= IGNORE_DCD_FOR_LINK_STAT;
+		}
+		if (conf->ignore_cts == WANOPT_YES){
+			card->u.c.protocol_options |= IGNORE_CTS_FOR_LINK_STAT;
+		}
 
-		card->u.c.kpalv_rx =
-	   		(conf->keepalive_rx_tmr - MIN_Rx_KPALV_TIMER) >= 0 ?
-	   		min (conf->keepalive_rx_tmr, MAX_Rx_KPALV_TIMER) :
-	   					DEFAULT_Rx_KPALV_TIMER;
+		if (conf->ignore_keepalive == WANOPT_YES) {
+			card->u.c.protocol_options |=
+				IGNORE_KPALV_FOR_LINK_STAT;
+			card->u.c.kpalv_tx  = MIN_Tx_KPALV_TIMER; 
+			card->u.c.kpalv_rx  = MIN_Rx_KPALV_TIMER; 
+			card->u.c.kpalv_err = MIN_KPALV_ERR_TOL; 
+
+		} else {   /* Do not ignore keepalives */
+			card->u.c.kpalv_tx =
+				((conf->keepalive_tx_tmr - MIN_Tx_KPALV_TIMER)
+				>= 0) ?
+	   			min(conf->keepalive_tx_tmr,MAX_Tx_KPALV_TIMER) :
+				DEFAULT_Tx_KPALV_TIMER;
+
+			card->u.c.kpalv_rx =
+		   		((conf->keepalive_rx_tmr - MIN_Rx_KPALV_TIMER)
+				>= 0) ?
+	   			min(conf->keepalive_rx_tmr,MAX_Rx_KPALV_TIMER) :
+				DEFAULT_Rx_KPALV_TIMER;
+
+			card->u.c.kpalv_err =
+		   		((conf->keepalive_err_margin-MIN_KPALV_ERR_TOL)
+				>= 0) ?
+	   			min(conf->keepalive_err_margin,
+				MAX_KPALV_ERR_TOL) : 
+	   			DEFAULT_KPALV_ERR_TOL;
+		}
 
-		card->u.c.kpalv_err =
-	   		(conf->keepalive_err_margin - MIN_KPALV_ERR_TOL) >= 0 ?
-	   		min (conf->keepalive_err_margin, MAX_KPALV_ERR_TOL) : 
-	   					DEFAULT_KPALV_ERR_TOL;
-	}
+		/* Setup slarp timer to control delay between slarps */
+		card->u.c.slarp_timer = 
+			((conf->slarp_timer - MIN_SLARP_REQ_TIMER) >= 0) ?
+			min (conf->slarp_timer, MAX_SLARP_REQ_TIMER) :
+			DEFAULT_SLARP_REQ_TIMER;
+
+	      #ifdef LINUX_2_0
+		if (card->u.c.slarp_timer){
+			printk(KERN_INFO 
+				"%s: Error: Dynamic IP support not available for 2.0.X kernels\n",
+					card->devname);
+			printk(KERN_INFO "%s:        Defaulting to Static IP addressing\n",
+						card->devname);
+		}
+		card->u.c.slarp_timer=0;
+	      #endif	
 
 
-	/* Setup slarp timer to control delay between slarps 
-         */ 
-	card->u.c.slarp_timer = 
-		(conf->slarp_timer - MIN_SLARP_REQ_TIMER) >=0 ?
-		min (conf->slarp_timer, MAX_SLARP_REQ_TIMER) :
-					DEFAULT_SLARP_REQ_TIMER;
+		if (conf->hdlc_streaming == WANOPT_YES) {
+			printk(KERN_INFO "%s: Enabling HDLC STREAMING Mode\n",
+				wandev->name);
+			card->u.c.protocol_options = HDLC_STREAMING_MODE;
+		}
 
+		if ((chdlc_priv_area->true_if_encoding = conf->true_if_encoding) == WANOPT_YES){
+			printk(KERN_INFO 
+				"%s: Enabling, true interface type encoding.\n",
+				card->devname);
+		}
+		
+        	/* Setup wanpipe as a router (WANPIPE) or as an API */
+		if( strcmp(conf->usedby, "WANPIPE") == 0) {
 
-	/* If HDLC_STRAMING is enabled then IGNORE DCD, CTS and KEEPALIVES
-         * are automatically ignored 
-	 */
-	if (conf->hdlc_streaming == WANOPT_YES) {
-		printk(KERN_INFO "%s: Enabling HDLC STREAMING Mode\n",
-			wandev->name);
-		card->u.c.protocol_options = HDLC_STREAMING_MODE;
+			printk(KERN_INFO "%s: Running in WANPIPE mode!\n",
+				wandev->name);
+			card->u.c.usedby = WANPIPE;
+
+			/* Option to bring down the interface when 
+        		 * the link goes down */
+			if (conf->if_down){
+				set_bit(DYN_OPT_ON,&chdlc_priv_area->interface_down);
+				printk(KERN_INFO 
+				 "%s: Dynamic interface configuration enabled\n",
+				   card->devname);
+			} 
+
+		} else if( strcmp(conf->usedby, "API") == 0) {
+                      #if defined(LINUX_2_1) || defined(LINUX_2_4) 
+			card->u.c.usedby = API;
+			printk(KERN_INFO "%s: Running in API mode !\n",
+				wandev->name);
+                      #else
+			printk(KERN_INFO "%s: API Mode is not supported for kernels lower than 2.2.X!\n",
+				wandev->name);
+			printk(KERN_INFO "%s: Please upgrade to a 2.2.X kernel fro the API support\n",
+				wandev->name);
+	                kfree(chdlc_priv_area);
+			return -EINVAL;
+                      #endif
+		}
 	}
 
-
-        /* Setup wanpipe as a router (WANPIPE) or as an API */
-	if( strcmp(conf->usedby, "WANPIPE") == 0) {
-		printk(KERN_INFO "%s: Running in WANPIPE mode !\n",wandev->name);
-		card->u.c.usedby = WANPIPE;
-
-	} else if( strcmp(conf->usedby, "API") == 0){
-
-#ifdef CHDLC_API 
-		card->u.c.usedby = API;
-		printk(KERN_INFO "%s: Running in API mode !\n",wandev->name);
-#else
-		printk(KERN_INFO "%s: API Mode is not supported!\n",
-			wandev->name);
-		printk(KERN_INFO "%s: Chdlc API patch can be obtained from Sangoma Tech.\n",
-					wandev->name);
-                kfree(chdlc_priv_area);
-		return -EINVAL;
-#endif
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	/* Tells us that if this interface is a
+         * gateway or not */
+	if ((chdlc_priv_area->gateway = conf->gateway) == WANOPT_YES){
+		printk(KERN_INFO "%s: Interface %s is set as a gateway.\n",
+			card->devname,card->u.c.if_name);
 	}
-
+      #endif
 
 	/* Get Multicast Information */
 	chdlc_priv_area->mc = conf->mc;
 
 	/* prepare network device data space for registration */
-	strcpy(dev->name, card->u.c.if_name);
+      #ifdef LINUX_2_4
+	strcpy(dev->name,card->u.c.if_name);
+      #else
+	dev->name = (char *)kmalloc(strlen(card->u.c.if_name) + 2, GFP_KERNEL); 
+	sprintf(dev->name, "%s", card->u.c.if_name);
+      #endif
+
 	dev->init = &if_init;
 	dev->priv = chdlc_priv_area;
 
-	return 0;
-}
-
-/*============================================================================
- * Delete logical channel.
- */
-static int del_if (wan_device_t* wandev, struct net_device* dev)
-{
-
-/* FIXME: This code generates kernel panic during
-          router stop!. Investigate futher.
-	  (Error is dereferencing a NULL pointer)
-
-	if(dev->priv){
-
-		kfree(dev->priv);
-                dev->priv = NULL;
+	/* Initialize the polling task routine */
+      #ifndef LINUX_2_4
+	chdlc_priv_area->poll_task.next = NULL;
+      #endif
+	chdlc_priv_area->poll_task.sync=0;
+	chdlc_priv_area->poll_task.routine = (void*)(void*)chdlc_poll;
+	chdlc_priv_area->poll_task.data = dev;
+
+	/* Initialize the polling delay timer */
+	init_timer(&chdlc_priv_area->poll_delay_timer);
+	chdlc_priv_area->poll_delay_timer.data = (unsigned long)dev;
+	chdlc_priv_area->poll_delay_timer.function = chdlc_poll_delay;
+	
+	printk(KERN_INFO "\n");
 
-        }
-*/
 	return 0;
 }
 
@@ -640,14 +957,14 @@
  * interface registration.  Returning anything but zero will fail interface
  * registration.
  */
-static int if_init (struct net_device* dev)
+static int if_init (netdevice_t* dev)
 	{
 	chdlc_private_area_t* chdlc_priv_area = dev->priv;
 	sdla_t* card = chdlc_priv_area->card;
 	wan_device_t* wandev = &card->wandev;
-#ifndef LINUX_2_1
+      #ifdef LINUX_2_0
 	int i;
-#endif
+      #endif
 
 	/* Initialize device driver entry points */
 	dev->open		= &if_open;
@@ -656,22 +973,41 @@
 	dev->rebuild_header	= &if_rebuild_hdr;
 	dev->hard_start_xmit	= &if_send;
 	dev->get_stats		= &if_stats;
+      #ifdef LINUX_2_4
 	dev->tx_timeout		= &if_tx_timeout;
 	dev->watchdog_timeo	= TX_TIMEOUT;
+      #endif
 
+	
 	/* Initialize media-specific parameters */
-	dev->flags		|= IFF_POINTTOPOINT;
+	dev->flags		|= IFF_POINTOPOINT;
+	dev->flags		|= IFF_NOARP;
 
 	/* Enable Mulitcasting if user selected */
 	if (chdlc_priv_area->mc == WANOPT_YES){
 		dev->flags 	|= IFF_MULTICAST;
 	}
-
-#ifndef LINUX_2_1
+	
+      #ifdef LINUX_2_0
 	dev->family		= AF_INET;
-#endif	
-	dev->type		= ARPHRD_PPP;	/* ARP hw type -- dummy value */
+      #endif	
+
+	if (chdlc_priv_area->true_if_encoding){
+	      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+		dev->type	= ARPHRD_HDLC; /* This breaks the tcpdump */
+	      #else
+		dev->type	= ARPHRD_PPP;
+	      #endif
+	}else{
+		dev->type	= ARPHRD_PPP;
+	}
+	
 	dev->mtu		= card->wandev.mtu;
+	/* for API usage, add the API header size to the requested MTU size */
+	if(card->u.c.usedby == API) {
+		dev->mtu += sizeof(api_tx_hdr_t);
+	}
+ 
 	dev->hard_header_len	= CHDLC_HDR_LEN;
 
 	/* Initialize hardware parameters */
@@ -688,12 +1024,12 @@
         dev->tx_queue_len = 100;
    
 	/* Initialize socket buffers */
-#ifdef LINUX_2_1
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 	dev_init_buffers(dev);
-#else
+      #else
         for (i = 0; i < DEV_NUMBUFFS; ++i)
                 skb_queue_head_init(&dev->buffs[i]);
-#endif
+      #endif
 
 	return 0;
 }
@@ -705,85 +1041,63 @@
  *
  * Return 0 if O.k. or errno.
  */
-static int if_open (struct net_device* dev)
+static int if_open (netdevice_t* dev)
 {
 	chdlc_private_area_t* chdlc_priv_area = dev->priv;
 	sdla_t* card = chdlc_priv_area->card;
-	SHARED_MEMORY_INFO_STRUCT* flags = card->u.c.flags;
 	struct timeval tv;
 	int err = 0;
 
 	/* Only one open per interface is allowed */
 
-	if(netif_running(dev))
+	if (is_dev_running(dev))
 		return -EBUSY;
-	
-	if(test_and_set_bit(1, (void*)&card->wandev.critical)) {
-		return -EAGAIN;
-	}
-	
-	/* Setup the Board for CHDLC */
-	if (set_chdlc_config(card)) {
-		clear_bit(1, (void*)&card->wandev.critical);
-		return -EIO;
-	}
-
-	if (!card->configured && !card->wandev.piggyback){	
-		/* Perform interrupt testing */
-		err = intr_test(card, dev);
-
-		if(err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) { 
-			printk(KERN_ERR "%s: Interrupt test failed (%i)\n",
-					card->devname, Intr_test_counter);
-			printk(KERN_ERR "%s: Please choose another interrupt\n",
-					card->devname);
-			clear_bit(1, (void*)&card->wandev.critical);
-			return -EIO;
-		}
-		
-		printk(KERN_INFO "%s: Interrupt test passed (%i)\n", 
-				card->devname, Intr_test_counter);
-		card->configured = 1;
-	}else{
-		printk(KERN_INFO "%s: Card configured, skip interrupt test\n", 
-				card->devname);
-	}
-
-	/* Initialize Rx/Tx buffer control fields */
-	init_chdlc_tx_rx_buff(card, dev);
-
-	/* Set interrupt mode and mask */
-        if (chdlc_set_intr_mode(card, APP_INT_ON_RX_FRAME |
-                		APP_INT_ON_GLOBAL_EXCEP_COND |
-                		APP_INT_ON_TX_FRAME |
-                		APP_INT_ON_CHDLC_EXCEP_COND | APP_INT_ON_TIMER)){
-
-			clear_bit(1, (void*)&card->wandev.critical);
-                        return -EIO;
-        }
-	
-
-	/* Mask the Transmit and Timer interrupt */
-	flags->interrupt_info_struct.interrupt_permission &= 
-		~(APP_INT_ON_TX_FRAME | APP_INT_ON_TIMER);
-
-
-	/* Enable communications */
-	if (chdlc_comm_enable(card)) {
-		clear_bit(1, (void*)&card->wandev.critical);
-		return -EIO;
-	}
-
-	clear_bit(1, (void*)&card->wandev.critical);
 
-	port_set_state(card, WAN_CONNECTING);
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	/* Initialize the task queue */
+	chdlc_priv_area->tq_working=0;
+
+      #ifndef LINUX_2_4
+	chdlc_priv_area->common.wanpipe_task.next = NULL;
+      #endif
+	chdlc_priv_area->common.wanpipe_task.sync = 0;
+	chdlc_priv_area->common.wanpipe_task.routine = (void *)(void *)chdlc_bh;
+	chdlc_priv_area->common.wanpipe_task.data = dev;
+
+	/* Allocate and initialize BH circular buffer */
+	/* Add 1 to MAX_BH_BUFF so we don't have test with (MAX_BH_BUFF-1) */
+	chdlc_priv_area->bh_head = kmalloc((sizeof(bh_data_t)*(MAX_BH_BUFF+1)),GFP_ATOMIC);
+	memset(chdlc_priv_area->bh_head,0,(sizeof(bh_data_t)*(MAX_BH_BUFF+1)));
+	atomic_set(&chdlc_priv_area->bh_buff_used, 0);
+      #endif
+ 
 	do_gettimeofday(&tv);
 	chdlc_priv_area->router_start_time = tv.tv_sec;
- 
+
+      #ifdef LINUX_2_4
 	netif_start_queue(dev);
-	dev->flags |= IFF_POINTTOPOINT;
+      #else
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	dev->start = 1;
+      #endif
+
 	wanpipe_open(card);
 
+	/* TTY is configured during wanpipe_set_termios
+	 * call, not here */
+	if (card->tty_opt)
+		return err;
+	
+	set_bit(0,&chdlc_priv_area->config_chdlc);
+	chdlc_priv_area->config_chdlc_timeout=jiffies;
+	del_timer(&chdlc_priv_area->poll_delay_timer);
+
+	/* Start the CHDLC configuration after 1sec delay.
+	 * This will give the interface initilization time
+	 * to finish its configuration */
+	chdlc_priv_area->poll_delay_timer.expires=jiffies+HZ;
+	add_timer(&chdlc_priv_area->poll_delay_timer);
 	return err;
 }
 
@@ -792,25 +1106,76 @@
  * o if this is the last close, then disable communications and interrupts.
  * o reset flags.
  */
-static int if_close (struct net_device* dev)
+static int if_close (netdevice_t* dev)
 {
 	chdlc_private_area_t* chdlc_priv_area = dev->priv;
 	sdla_t* card = chdlc_priv_area->card;
 
-	if(test_and_set_bit(1, (void*)&card->wandev.critical))
-		return -EAGAIN;
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 
-	netif_stop_queue(dev);
+	if (chdlc_priv_area->bh_head){
+		int i;
+		struct sk_buff *skb;
+	
+		for (i=0; i<(MAX_BH_BUFF+1); i++){
+			skb = ((bh_data_t *)&chdlc_priv_area->bh_head[i])->skb;
+			if (skb != NULL){
+                		wan_dev_kfree_skb(skb, FREE_READ);
+			}
+		}
+		kfree(chdlc_priv_area->bh_head);
+		chdlc_priv_area->bh_head=NULL;
+	}
+      #endif
+
+	stop_net_queue(dev);
+      #ifndef LINUX_2_4
+	dev->start=0;
+      #endif
 	wanpipe_close(card);
-	port_set_state(card, WAN_DISCONNECTED);
-	chdlc_set_intr_mode(card, 0);
-	chdlc_comm_disable(card);
+	del_timer(&chdlc_priv_area->poll_delay_timer);
+	return 0;
+}
+
+static void disable_comm (sdla_t *card)
+{
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+	
+	if (card->u.c.comm_enabled){
+		chdlc_disable_comm_shutdown (card);
+	}else{
+		flags->interrupt_info_struct.interrupt_permission = 0;	
+	}
 
-	clear_bit(1, (void*)&card->wandev.critical);
+      #if defined(LINUX_2_4) || defined(LINUX_2_1)	
+	if (!tty_init_cnt)
+		return;
 
-	return 0;
+	if (card->tty_opt){
+		struct serial_state * state;
+		if (!(--tty_init_cnt)){
+			int e1,e2;
+			*serial_driver.refcount=0;
+			
+			if ((e1 = tty_unregister_driver(&serial_driver)))
+				printk("SERIAL: failed to unregister serial driver (%d)\n",
+				       e1);
+			if ((e2 = tty_unregister_driver(&callout_driver)))
+				printk("SERIAL: failed to unregister callout driver (%d)\n", 
+				       e2);
+			printk(KERN_INFO "%s: Unregistering TTY Driver, Major %i\n",
+					card->devname,WAN_TTY_MAJOR);
+		}
+		card->tty=NULL;
+		tty_card_map[card->tty_minor]=NULL;
+		state = &rs_table[card->tty_minor];
+		memset(state,0,sizeof(state));
+	}
+      #endif
+	return;
 }
 
+
 /*============================================================================
  * Build media header.
  *
@@ -820,7 +1185,7 @@
  *
  * Return:	media header length.
  */
-static int if_header (struct sk_buff* skb, struct net_device* dev,
+static int if_header (struct sk_buff* skb, netdevice_t* dev,
 	unsigned short type, void* daddr, void* saddr, unsigned len)
 {
 	skb->protocol = htons(type);
@@ -828,48 +1193,49 @@
 	return CHDLC_HDR_LEN;
 }
 
+
+#ifdef LINUX_2_4
 /*============================================================================
- * Re-build media header.
- *
- * Return:	1	physical address resolved.
- *		0	physical address not resolved
+ * Handle transmit timeout event from netif watchdog
  */
-#ifdef LINUX_2_1
-static int if_rebuild_hdr (struct sk_buff *skb)
+static void if_tx_timeout (netdevice_t *dev)
 {
-	return 1;
-}
-#else
-static int if_rebuild_hdr (void* hdr, struct net_device* dev, unsigned long raddr,
-                           struct sk_buff* skb)
-{
-        return 1;
+    	chdlc_private_area_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	
+	/* If our device stays busy for at least 5 seconds then we will
+	 * kick start the device by making dev->tbusy = 0.  We expect
+	 * that our device never stays busy more than 5 seconds. So this                 
+	 * is only used as a last resort.
+	 */
+
+	++card->wandev.stats.collisions;
+
+	printk (KERN_INFO "%s: Transmit timed out on %s\n", card->devname,dev->name);
+	netif_wake_queue (dev);
 }
 #endif
 
 
+
 /*============================================================================
- * Handle transmit timeout event from netif watchdog
+ * Re-build media header.
+ *
+ * Return:	1	physical address resolved.
+ *		0	physical address not resolved
  */
-static void if_tx_timeout (struct net_device *dev)
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static int if_rebuild_hdr (struct sk_buff *skb)
 {
-	chdlc_private_area_t *chdlc_priv_area = dev->priv;
-	sdla_t *card = chdlc_priv_area->card;
-
-	/* If our device stays busy for at least 5 seconds then we will
-	 * kick start the device by making dev->tbusy = 0.  We expect 
-	 * that our device never stays busy more than 5 seconds. So this
-	 * is only used as a last resort. 
-	 */
-	++card->wandev.stats.collisions;
-
-	printk (KERN_INFO "%s: Transmit timeout !\n",
-		card->devname);
-
-	/* unbusy the interface */
-	netif_wake_queue (dev);
+	return 1;
 }
-
+#else
+static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
+                           struct sk_buff* skb)
+{
+        return 1;
+}
+#endif
 
 /*============================================================================
  * Send a packet on a network interface.
@@ -888,7 +1254,7 @@
  * 2. Setting tbusy flag will inhibit further transmit requests from the
  *    protocol stack and can be used for flow control with protocol layer.
  */
-static int if_send (struct sk_buff* skb, struct net_device* dev)
+static int if_send (struct sk_buff* skb, netdevice_t* dev)
 {
 	chdlc_private_area_t *chdlc_priv_area = dev->priv;
 	sdla_t *card = chdlc_priv_area->card;
@@ -896,33 +1262,68 @@
 	INTERRUPT_INFORMATION_STRUCT *chdlc_int = &flags->interrupt_info_struct;
 	int udp_type = 0;
 	unsigned long smp_flags;
+	int err=0;
 
-	if(skb == NULL) {
+      #ifdef LINUX_2_4
+	netif_stop_queue(dev);
+      #endif
+	
+	if (skb == NULL){
 		/* If we get here, some higher layer thinks we've missed an
 		 * tx-done interrupt.
 		 */
 		printk(KERN_INFO "%s: interface %s got kicked!\n",
 			card->devname, dev->name);
-		netif_wake_queue(dev);
+
+		wake_net_dev(dev);
 		return 0;
 	}
 
-   	if(ntohs(skb->protocol) != 0x16) {
+      #ifndef LINUX_2_4
+	if (dev->tbusy){
+
+		/* If our device stays busy for at least 5 seconds then we will
+		 * kick start the device by making dev->tbusy = 0.  We expect 
+		 * that our device never stays busy more than 5 seconds. So this
+		 * is only used as a last resort. 
+		 */
+                ++card->wandev.stats.collisions;
+		if((jiffies - chdlc_priv_area->tick_counter) < (5 * HZ)) {
+			return 1;
+		}
+
+		printk (KERN_INFO "%s: Transmit timeout !\n",
+			card->devname);
+
+		/* unbusy the interface */
+		clear_bit(0,&dev->tbusy);
+	}
+      #endif
+
+   	if (ntohs(skb->protocol) != htons(PVC_PROT)){
 
 		/* check the udp packet type */
+		
 		udp_type = udp_pkt_type(skb, card);
-		if(udp_type == UDP_CPIPE_TYPE) {
+
+		if (udp_type == UDP_CPIPE_TYPE){
                         if(store_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, dev,
-                                chdlc_priv_area))
+                                chdlc_priv_area)){
 	                	chdlc_int->interrupt_permission |=
 					APP_INT_ON_TIMER;
+			}
+			start_net_queue(dev);
 			return 0;
 		}
 
 		/* check to see if the source IP address is a broadcast or */
 		/* multicast IP address */
-                if(chk_bcast_mcast_addr(card, dev, skb))
-                        return 0;
+                if(chk_bcast_mcast_addr(card, dev, skb)){
+			++card->wandev.stats.tx_dropped;
+			wan_dev_kfree_skb(skb,FREE_WRITE);
+			start_net_queue(dev);
+			return 0;
+		}
         }
 
 	/* Lock the 508 Card: SMP is supported */
@@ -930,29 +1331,24 @@
 		s508_lock(card,&smp_flags);
 	} 
 
-    	if(test_and_set_bit(0, (void*)&card->wandev.critical)) {
+    	if(test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
 	
 		printk(KERN_INFO "%s: Critical in if_send: %lx\n",
 					card->wandev.name,card->wandev.critical);
                 ++card->wandev.stats.tx_dropped;
-#ifdef LINUX_2_1
-    		dev_kfree_skb(skb);
-#else
-                dev_kfree_skb(skb, FREE_WRITE);
-#endif	
-		if(card->hw.type != SDLA_S514){
-			s508_unlock(card,&smp_flags);
-		}
-		return 0;
+		start_net_queue(dev);
+		goto if_send_exit_crit;
 	}
 
-	if(card->u.c.state != WAN_CONNECTED)
+	if(card->u.c.state != WAN_CONNECTED){
        		++card->wandev.stats.tx_dropped;
-
-	else if(!skb->protocol)
+		start_net_queue(dev);
+		
+	}else if(!skb->protocol){
         	++card->wandev.stats.tx_errors;
-
-	else {
+		start_net_queue(dev);
+		
+	}else {
 		void* data = skb->data;
 		unsigned len = skb->len;
 		unsigned char attr;
@@ -964,18 +1360,14 @@
 		if (card->u.c.usedby == API){
 			api_tx_hdr_t* api_tx_hdr;
 
-			if (len <= sizeof(api_tx_hdr_t)){
-#ifdef LINUX_2_1
- 		               	dev_kfree_skb(skb);
-#else
-                		dev_kfree_skb(skb, FREE_WRITE);
-#endif
+			/* discard the frame if we are configured for */
+			/* 'receive only' mode or if there is no data */
+			if (card->u.c.receive_only ||
+				(len <= sizeof(api_tx_hdr_t))) {
+				
 				++card->wandev.stats.tx_dropped;
-				clear_bit(0, (void*)&card->wandev.critical);
-				if(card->hw.type != SDLA_S514){
-					s508_unlock(card,&smp_flags);
-				}
-                		return 0;
+				start_net_queue(dev);
+				goto if_send_exit_crit;
 			}
 				
 			api_tx_hdr = (api_tx_hdr_t *)data;
@@ -985,24 +1377,36 @@
 		}
 
 		if(chdlc_send(card, data, len)) {
-			netif_stop_queue(dev);
-			chdlc_priv_area->tick_counter = jiffies;
-			chdlc_int->interrupt_permission |= APP_INT_ON_TX_FRAME;
-		}
-		else {
+			stop_net_queue(dev);
+		}else{
 			++card->wandev.stats.tx_packets;
+		      #if defined(LINUX_2_1) || defined(LINUX_2_4)
                         card->wandev.stats.tx_bytes += len;
+		      #endif
+			
+			start_net_queue(dev);
+			
+		      #ifdef LINUX_2_4
+		 	dev->trans_start = jiffies;
+		      #endif
 		}	
 	}
 
-	if (!netif_queue_stopped(dev))
-		dev_kfree_skb(skb);
+if_send_exit_crit:
+	
+	if (!(err=is_queue_stopped(dev))) {
+		wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+		chdlc_priv_area->tick_counter = jiffies;
+		chdlc_int->interrupt_permission |= APP_INT_ON_TX_FRAME;
+	}
 
-	clear_bit(0, (void*)&card->wandev.critical);
+	clear_bit(SEND_CRIT, (void*)&card->wandev.critical);
 	if(card->hw.type != SDLA_S514){
 		s508_unlock(card,&smp_flags);
 	}
-	return netif_queue_stopped(dev);
+	
+	return err;
 }
 
 
@@ -1011,19 +1415,19 @@
  * multicast source IP address.
  */
 
-static int chk_bcast_mcast_addr(sdla_t *card, struct net_device* dev,
+static int chk_bcast_mcast_addr(sdla_t *card, netdevice_t* dev,
 				struct sk_buff *skb)
 {
 	u32 src_ip_addr;
         u32 broadcast_ip_addr = 0;
-#ifdef LINUX_2_1
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
         struct in_device *in_dev;
-#endif
+      #endif
         /* read the IP source address from the outgoing packet */
         src_ip_addr = *(u32 *)(skb->data + 12);
 
 	/* read the IP broadcast address for the device */
-#ifdef LINUX_2_1
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
         in_dev = dev->ip_ptr;
         if(in_dev != NULL) {
                 struct in_ifaddr *ifa= in_dev->ifa_list;
@@ -1032,20 +1436,14 @@
                 else
                         return 0;
         }
-#else
+      #else
         broadcast_ip_addr = dev->pa_brdaddr;
-#endif
+      #endif
  
         /* check if the IP Source Address is a Broadcast address */
         if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) {
                 printk(KERN_INFO "%s: Broadcast Source Address silently discarded\n",
 				card->devname);
-#ifdef LINUX_2_1
-                dev_kfree_skb(skb);
-#else
-                dev_kfree_skb(skb, FREE_WRITE);
-#endif
-                ++card->wandev.stats.tx_dropped;
                 return 1;
         } 
 
@@ -1054,12 +1452,6 @@
 		(ntohl(src_ip_addr) <= 0xFFFFFFFE)) {
                 printk(KERN_INFO "%s: Multicast Source Address silently discarded\n",
 				card->devname);
-#ifdef LINUX_2_1
-                dev_kfree_skb(skb);
-#else
-                dev_kfree_skb(skb, FREE_WRITE);
-#endif
-                ++card->wandev.stats.tx_dropped;
                 return 1;
         }
 
@@ -1169,23 +1561,29 @@
 
 /*============================================================================
  * Get ethernet-style interface statistics.
- * Return a pointer to struct net_device_stats.
+ * Return a pointer to struct enet_statistics.
  */
-#ifdef LINUX_2_1
-static struct net_device_stats* if_stats (struct net_device* dev)
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats* if_stats (netdevice_t* dev)
 {
 	sdla_t *my_card;
-	chdlc_private_area_t* chdlc_priv_area = dev->priv;
+	chdlc_private_area_t* chdlc_priv_area;
+
+	if ((chdlc_priv_area=dev->priv) == NULL)
+		return NULL;
 
 	my_card = chdlc_priv_area->card;
 	return &my_card->wandev.stats; 
 }
 #else
-static struct net_device_stats* if_stats (struct net_device* dev)
+static struct enet_statistics* if_stats (netdevice_t* dev)
 {
         sdla_t *my_card;
         chdlc_private_area_t* chdlc_priv_area = dev->priv;
 
+	if ((chdlc_priv_area=dev->priv) == NULL)
+		return NULL;
+
         my_card = chdlc_priv_area->card;
         return &my_card->wandev.stats;
 }
@@ -1261,44 +1659,63 @@
 }
 
 
-/*============================================================================
- * Enable communications.
- */
+/*===========================================================
+ * chdlc_disable_comm_shutdown
+ *
+ * Shutdown() disables the communications. We must
+ * have a sparate functions, because we must not
+ * call chdlc_error() hander since the private
+ * area has already been replaced */
 
-static int chdlc_comm_enable (sdla_t* card)
+static int chdlc_disable_comm_shutdown (sdla_t *card)
 {
-	int err;
 	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+	CHDLC_INT_TRIGGERS_STRUCT* int_data =
+		 (CHDLC_INT_TRIGGERS_STRUCT *)mb->data;
+	int err;
+
+	/* Disable Interrutps */
+	int_data->CHDLC_interrupt_triggers 	= 0;
+	int_data->IRQ				= card->hw.irq;
+	int_data->interrupt_timer               = 1;
+   
+	mb->buffer_length = sizeof(CHDLC_INT_TRIGGERS_STRUCT);
+	mb->command = SET_CHDLC_INTERRUPT_TRIGGERS;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
 
+	/* Disable Communications */
+
+	if (card->u.c.async_mode) {
+		mb->command = DISABLE_ASY_COMMUNICATIONS;
+	}else{
+		mb->command = DISABLE_CHDLC_COMMUNICATIONS;
+	}
+	
 	mb->buffer_length = 0;
-	mb->command = ENABLE_CHDLC_COMMUNICATIONS;
 	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
-	if (err != COMMAND_OK)
-		chdlc_error(card, err, mb);
-	return err;
+	
+	card->u.c.comm_enabled = 0;
+	
+	return 0;
 }
 
 /*============================================================================
- * Disable communications and Drop the Modem lines (DCD and RTS).
+ * Enable communications.
  */
-static int chdlc_comm_disable (sdla_t* card)
+
+static int chdlc_comm_enable (sdla_t* card)
 {
 	int err;
 	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
 
 	mb->buffer_length = 0;
-	mb->command = DISABLE_CHDLC_COMMUNICATIONS;
-	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
-	if (err != COMMAND_OK)
-		chdlc_error(card,err,mb);
-
-	mb->command = SET_MODEM_STATUS;
-	mb->buffer_length = 1;
-	mb->data[0] = 0;
+	mb->command = ENABLE_CHDLC_COMMUNICATIONS;
 	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
 	if (err != COMMAND_OK)
-		chdlc_error(card,err,mb);
-
+		chdlc_error(card, err, mb);
+	else
+		card->u.c.comm_enabled = 1;
+	
 	return err;
 }
 
@@ -1397,10 +1814,10 @@
 	
 	/* Update transmit buffer control fields */
 	card->u.c.txbuf = ++txbuf;
-
+	
 	if ((void*)txbuf > card->u.c.txbuf_last)
 		card->u.c.txbuf = card->u.c.txbuf_base;
-
+	
 	return 0;
 }
 
@@ -1420,7 +1837,7 @@
 	switch (err) {
 
 	case CMD_TIMEOUT:
-		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+		printk(KERN_INFO "%s: command 0x%02X timed out!\n",
 			card->devname, cmd);
 		break;
 
@@ -1440,17 +1857,112 @@
 	return 0;
 }
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+/********** Bottom Half Handlers ********************************************/
+
+/* NOTE: There is no API, BH support for Kernels lower than 2.2.X.
+ *       DO NOT INSERT ANY CODE HERE, NOTICE THE 
+ *       PREPROCESSOR STATEMENT ABOVE, UNLESS YOU KNOW WHAT YOU ARE
+ *       DOING */
+
+static void chdlc_bh (netdevice_t * dev)
+{
+	chdlc_private_area_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	struct sk_buff *skb;
+
+	if (atomic_read(&chan->bh_buff_used) == 0){
+		clear_bit(0, &chan->tq_working);
+		return;
+	}
+
+	while (atomic_read(&chan->bh_buff_used)){
+
+		skb  = ((bh_data_t *)&chan->bh_head[chan->bh_read])->skb;
+
+		if (skb != NULL){
+
+			if (chan->common.sk == NULL || chan->common.func == NULL){
+				++card->wandev.stats.rx_dropped;
+				wan_dev_kfree_skb(skb, FREE_READ);
+				chdlc_bh_cleanup(dev);
+				continue;
+			}
+
+			if (chan->common.func(skb,dev,chan->common.sk) != 0){
+				/* Sock full cannot send, queue us for another
+                                 * try */
+				atomic_set(&chan->common.receive_block,1);
+				return;
+			}else{
+				chdlc_bh_cleanup(dev);
+			}
+		}else{
+			chdlc_bh_cleanup(dev);
+		}
+	}	
+	clear_bit(0, &chan->tq_working);
+
+	return;
+}
+
+static int chdlc_bh_cleanup (netdevice_t *dev)
+{
+	chdlc_private_area_t* chan = dev->priv;
+
+	((bh_data_t *)&chan->bh_head[chan->bh_read])->skb = NULL;
+
+	if (chan->bh_read == MAX_BH_BUFF){
+		chan->bh_read=0;
+	}else{
+		++chan->bh_read;	
+	}
+
+	atomic_dec(&chan->bh_buff_used);
+	return 0;
+}
+
+
+
+static int bh_enqueue (netdevice_t *dev, struct sk_buff *skb)
+{
+	/* Check for full */
+	chdlc_private_area_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+
+	if (atomic_read(&chan->bh_buff_used) == (MAX_BH_BUFF+1)){
+		++card->wandev.stats.rx_dropped;
+		wan_dev_kfree_skb(skb, FREE_READ);
+		return 1; 
+	}
+
+	((bh_data_t *)&chan->bh_head[chan->bh_write])->skb = skb;
+
+	if (chan->bh_write == MAX_BH_BUFF){
+		chan->bh_write=0;
+	}else{
+		++chan->bh_write;
+	}
+
+	atomic_inc(&chan->bh_buff_used);
+
+	return 0;
+}
+
+/* END OF API BH Support */
+
+#endif
+
 /****** Interrupt Handlers **************************************************/
 
 /*============================================================================
  * Cisco HDLC interrupt service routine.
  */
-STATIC void wpc_isr (sdla_t* card)
+static void wpc_isr (sdla_t* card)
 {
-	struct net_device* dev;
-	chdlc_private_area_t* chdlc_priv_area;
+	netdevice_t* dev;
 	SHARED_MEMORY_INFO_STRUCT* flags = NULL;
-	int i, interrupt_serviced = 0;
+	int i;
 	sdla_t *my_card;
 
 
@@ -1462,95 +1974,97 @@
 	flags = card->u.c.flags;
 	if (!flags->interrupt_info_struct.interrupt_type){
 		/* Check for a second port (piggybacking) */
-		if((my_card = card->next)){
+		if ((my_card = card->next)){
 			flags = my_card->u.c.flags;
 			if (flags->interrupt_info_struct.interrupt_type){
 				card = my_card;
+				card->isr(card);
+				return;
 			}
 		}
 	}
 
+	flags = card->u.c.flags;
+	card->in_isr = 1;
 	dev = card->wandev.dev;
 	
-	card->in_isr = 1;
+	/* If we get an interrupt with no network device, stop the interrupts
+	 * and issue an error */
+	if (!card->tty_opt && !dev && 
+	    flags->interrupt_info_struct.interrupt_type != 
+	    	COMMAND_COMPLETE_APP_INT_PEND){
+
+		goto isr_done;
+	}
 	
 	/* if critical due to peripheral operations
 	 * ie. update() or getstats() then reset the interrupt and
 	 * wait for the board to retrigger.
 	 */
-	if(test_bit(1, (void*)&card->wandev.critical)) {
-               	if(card->u.c.flags != NULL) {
-                       	flags = card->u.c.flags;
-			if(flags->interrupt_info_struct.
-					interrupt_type) {
-					flags->interrupt_info_struct.
-						interrupt_type = 0;
-			}
-		}
-		card->in_isr = 0;
-		return;
+	if(test_bit(PERI_CRIT, (void*)&card->wandev.critical)) {
+		printk(KERN_INFO "ISR CRIT TO PERI\n");
+		goto isr_done;
 	}
 
-
 	/* On a 508 Card, if critical due to if_send 
-         * Major Error !!!
-	 */
+         * Major Error !!! */
 	if(card->hw.type != SDLA_S514) {
-		if(test_and_set_bit(0, (void*)&card->wandev.critical)) {
+		if(test_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
 			printk(KERN_INFO "%s: Critical while in ISR: %lx\n",
 				card->devname, card->wandev.critical);
 			card->in_isr = 0;
+			flags->interrupt_info_struct.interrupt_type = 0;
 			return;
 		}
 	}
 
-	/* FIXME: Take this check out later in the future */
-	if(card->u.c.flags != NULL) {
-	
-		flags = card->u.c.flags;
+	switch(flags->interrupt_info_struct.interrupt_type) {
 
-		switch(flags->interrupt_info_struct.interrupt_type) {
+	case RX_APP_INT_PEND:	/* 0x01: receive interrupt */
+		rx_intr(card);
+		break;
 
-		case RX_APP_INT_PEND:	/* 0x01: receive interrupt */
-			interrupt_serviced = 1;
-			rx_intr(card);
-			break;
+	case TX_APP_INT_PEND:	/* 0x02: transmit interrupt */
+		flags->interrupt_info_struct.interrupt_permission &=
+			 ~APP_INT_ON_TX_FRAME;
 
-		case TX_APP_INT_PEND:	/* 0x02: transmit interrupt */
-			interrupt_serviced = 1;
-			flags->interrupt_info_struct.interrupt_permission &=
-				 ~APP_INT_ON_TX_FRAME;
+	      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 
-			chdlc_priv_area = dev->priv;
-			netif_wake_queue(dev);
+		if (card->tty_opt){
+			wanpipe_tty_trigger_poll(card);
 			break;
+		}
 
-		case COMMAND_COMPLETE_APP_INT_PEND:/* 0x04: cmd cplt */
-			interrupt_serviced = 1;
-			++ Intr_test_counter;
-			break;
+		if (dev && is_queue_stopped(dev)){
+			if (card->u.c.usedby == API){
+				start_net_queue(dev);
+				wakeup_sk_bh(dev);
+			}else{
+				wake_net_dev(dev);
+			}
+		}
+	      #else
+		wake_net_dev(dev);
+	      #endif
+		break;
 
-		case CHDLC_EXCEP_COND_APP_INT_PEND:	/* 0x20 */
-			interrupt_serviced = 1;
-			process_chdlc_exception(card);
-			break;
+	case COMMAND_COMPLETE_APP_INT_PEND:/* 0x04: cmd cplt */
+		++ Intr_test_counter;
+		break;
 
-		case GLOBAL_EXCEP_COND_APP_INT_PEND:
-			interrupt_serviced = 1;
-			process_global_exception(card);
-			break;
+	case CHDLC_EXCEP_COND_APP_INT_PEND:	/* 0x20 */
+		process_chdlc_exception(card);
+		break;
 
-		case TIMER_APP_INT_PEND:
-			interrupt_serviced = 1;
-			timer_intr(card);
-			break;
+	case GLOBAL_EXCEP_COND_APP_INT_PEND:
+		process_global_exception(card);
+		break;
 
-		default:
-			break;
-		}	
-	}
+	case TIMER_APP_INT_PEND:
+		timer_intr(card);
+		break;
 
-	if(!interrupt_serviced) {
+	default:
 		printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", 
 			card->devname,
 			flags->interrupt_info_struct.interrupt_type);
@@ -1563,14 +2077,14 @@
 			printk(KERN_INFO "%c", 
 				flags->global_info_struct.codeversion[i]); 
 		printk(KERN_INFO "\n");	
+		break;
 	}
 
+isr_done:
+
 	card->in_isr = 0;
 	flags->interrupt_info_struct.interrupt_type = 0;
-        if(card->hw.type != SDLA_S514){
-        	clear_bit(0, (void*)&card->wandev.critical);
-	}
-        
+	return;
 }
 
 /*============================================================================
@@ -1578,15 +2092,16 @@
  */
 static void rx_intr (sdla_t* card)
 {
-	struct net_device 			*dev;
-	chdlc_private_area_t		*chdlc_priv_area;
-	SHARED_MEMORY_INFO_STRUCT 	*flags = card->u.c.flags;
-	CHDLC_DATA_RX_STATUS_EL_STRUCT	*rxbuf = card->u.c.rxmb;
-	struct sk_buff			*skb;
-	unsigned 			len;
-	void				*buf;
-	int 				i,udp_type;
-	
+	netdevice_t *dev;
+	chdlc_private_area_t *chdlc_priv_area;
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+	CHDLC_DATA_RX_STATUS_EL_STRUCT *rxbuf = card->u.c.rxmb;
+	struct sk_buff *skb;
+	unsigned len;
+	unsigned addr = rxbuf->ptr_data_bfr;
+	void *buf;
+	int i,udp_type;
+
 	if (rxbuf->opp_flag != 0x01) {
 		printk(KERN_INFO 
 			"%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", 
@@ -1600,90 +2115,132 @@
                         printk(KERN_INFO "%c",
                                 flags->global_info_struct.codeversion[i]);
                 printk(KERN_INFO "\n");
+
+
+		/* Bug Fix: Mar 6 2000
+                 * If we get a corrupted mailbox, it measn that driver 
+                 * is out of sync with the firmware. There is no recovery.
+                 * If we don't turn off all interrupts for this card
+                 * the machine will crash. 
+                 */
+		printk(KERN_INFO "%s: Critical router failure ...!!!\n", card->devname);
+		printk(KERN_INFO "Please contact Sangoma Technologies !\n");
+		chdlc_set_intr_mode(card,0);	
 		return;
 	}
 
+	len  = rxbuf->frame_length;
+
+      #if defined(LINUX_2_4) || defined(LINUX_2_1)	
+	if (card->tty_opt){
+
+		if (rxbuf->error_flag){	
+			goto rx_exit;
+		}
+
+		if (len <= CRC_LENGTH){
+			goto rx_exit;
+		}
+		
+		if (!card->u.c.async_mode){
+			len -= CRC_LENGTH;
+		}
+
+		wanpipe_tty_receive(card,addr,len);
+		goto rx_exit;
+	}
+      #endif
+
 	dev = card->wandev.dev;
-	chdlc_priv_area = dev->priv;
 
-	if(dev && netif_running(dev)) {
+	if (!dev){
+		goto rx_exit;
+	}
+
+	if (!is_dev_running(dev))
+		goto rx_exit;
 
-		len  = rxbuf->frame_length;
+	chdlc_priv_area = dev->priv;
 
-		/* Allocate socket buffer */
-		skb = dev_alloc_skb(len);
+	
+	/* Allocate socket buffer */
+	skb = dev_alloc_skb(len);
 
-		if (skb != NULL) {
-			/* Copy data to the socket buffer */
-			unsigned addr = rxbuf->ptr_data_bfr;
-		
-			if((addr + len) >
-			card->u.c.rx_top + 1) {
-				unsigned tmp = 
-				card->u.c.rx_top - addr + 1;
-				buf = skb_put(skb, tmp);
-				sdla_peek(&card->hw, addr, buf, tmp);
-				addr = card->u.c.rx_base;
-				len -= tmp;
-			}
+	if (skb == NULL) {
+		printk(KERN_INFO "%s: no socket buffers available!\n",
+					card->devname);
+		++card->wandev.stats.rx_dropped;
+		goto rx_exit;
+	}
+
+	/* Copy data to the socket buffer */
+	if((addr + len) > card->u.c.rx_top + 1) {
+		unsigned tmp = card->u.c.rx_top - addr + 1;
+		buf = skb_put(skb, tmp);
+		sdla_peek(&card->hw, addr, buf, tmp);
+		addr = card->u.c.rx_base;
+		len -= tmp;
+	}
 		
-			buf = skb_put(skb, len);
-			sdla_peek(&card->hw, addr, buf, len);
+	buf = skb_put(skb, len);
+	sdla_peek(&card->hw, addr, buf, len);
 
-			/* Decapsulate packet */
-			skb->protocol = htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
 
-			card->wandev.stats.rx_packets ++;
-#ifdef LINUX_2_1
-			card->wandev.stats.rx_bytes += skb->len;
+	card->wandev.stats.rx_packets ++;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	card->wandev.stats.rx_bytes += skb->len;
 #endif
-			udp_type = udp_pkt_type( skb, card );
+	udp_type = udp_pkt_type( skb, card );
 
-			if(udp_type == UDP_CPIPE_TYPE) {
-				if(store_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK,
-					card, skb, dev, chdlc_priv_area)) {
-       				        flags->interrupt_info_struct.
+	if(udp_type == UDP_CPIPE_TYPE) {
+		if(store_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK,
+   				      card, skb, dev, chdlc_priv_area)) {
+     		        flags->interrupt_info_struct.
 						interrupt_permission |= 
 							APP_INT_ON_TIMER; 
-				}
-
-			} else {
- 
-				if(card->u.c.usedby == API) {
-					api_rx_hdr_t* api_rx_hdr;
-              				skb_push(skb, sizeof(api_rx_hdr_t));
-                                        api_rx_hdr =
-						(api_rx_hdr_t*)&skb->data[0x00];
-					api_rx_hdr->error_flag =
-						rxbuf->error_flag;
-          				api_rx_hdr->time_stamp =
-                                                rxbuf->time_stamp;
-                                	skb->protocol = htons(0x16);
-                                	skb->pkt_type = PACKET_HOST;
-				}
-
-/* FIXME: we should check to see if the received packet is a multicast packet so that we can increment the multicast statistic
-                                ++ chdlc_priv_area->if_stats.multicast;
-*/
-                               	/* Pass it up the protocol stack */
-                                skb->dev = dev;
-                                skb->mac.raw  = skb->data;
-                                netif_rx(skb);
-			}
+		}
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	} else if(card->u.c.usedby == API) {
 
-		} else {
-			printk(KERN_INFO
-				"%s: no socket buffers available!\n",
-					card->devname);
-			++card->wandev.stats.rx_dropped;
+		api_rx_hdr_t* api_rx_hdr;
+       		skb_push(skb, sizeof(api_rx_hdr_t));
+                api_rx_hdr = (api_rx_hdr_t*)&skb->data[0x00];
+		api_rx_hdr->error_flag = rxbuf->error_flag;
+     		api_rx_hdr->time_stamp = rxbuf->time_stamp;
+
+                skb->protocol = htons(PVC_PROT);
+     		skb->mac.raw  = skb->data;
+		skb->dev      = dev;
+               	skb->pkt_type = WAN_PACKET_DATA;
+
+		bh_enqueue(dev, skb);
+
+		if (!test_and_set_bit(0,&chdlc_priv_area->tq_working)){
+			wanpipe_queue_tq(&chdlc_priv_area->common.wanpipe_task);
+		        wanpipe_mark_bh();
 		}
-     	}
+#endif
+	}else{
+		/* FIXME: we should check to see if the received packet is a 
+                          multicast packet so that we can increment the multicast 
+                          statistic
+                          ++ chdlc_priv_area->if_stats.multicast;
+		*/
+               	/* Pass it up the protocol stack */
+	
+                skb->dev = dev;
+                skb->mac.raw  = skb->data;
+                netif_rx(skb);
+	}
 
+rx_exit:
 	/* Release buffer element and calculate a pointer to the next one */
 	rxbuf->opp_flag = 0x00;
 	card->u.c.rxmb = ++ rxbuf;
-	if((void*)rxbuf > card->u.c.rxbuf_last)
+	if((void*)rxbuf > card->u.c.rxbuf_last){
 		card->u.c.rxmb = card->u.c.rxbuf_base;
+	}
 }
 
 /*============================================================================
@@ -1694,14 +2251,26 @@
  */
 void timer_intr(sdla_t *card)
 {
-        struct net_device* dev;
+        netdevice_t* dev;
         chdlc_private_area_t* chdlc_priv_area = NULL;
         SHARED_MEMORY_INFO_STRUCT* flags = NULL;
 
-        dev = card->wandev.dev; 
+        if ((dev = card->wandev.dev)==NULL){
+		flags = card->u.c.flags;
+                flags->interrupt_info_struct.interrupt_permission &=
+                        ~APP_INT_ON_TIMER;
+		return;
+	}
+	
         chdlc_priv_area = dev->priv;
 
-	/* process a udp call if pending */
+	if (chdlc_priv_area->timer_int_enabled & TMR_INT_ENABLED_CONFIG) {
+		if (!config_chdlc(card)){
+			chdlc_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_CONFIG;
+		}
+	}
+
+	/* process a udp call if pending */
        	if(chdlc_priv_area->timer_int_enabled & TMR_INT_ENABLED_UDP) {
                	process_udp_mgmt_pkt(card, dev,
                        chdlc_priv_area);
@@ -1733,16 +2302,14 @@
 
 static int set_chdlc_config(sdla_t* card)
 {
-
-	struct net_device * dev = card->wandev.dev;
-	chdlc_private_area_t *chdlc_priv_area = dev->priv;
 	CHDLC_CONFIGURATION_STRUCT cfg;
 
 	memset(&cfg, 0, sizeof(CHDLC_CONFIGURATION_STRUCT));
 
-	if(card->wandev.clocking)
+	if(card->wandev.clocking){
 		cfg.baud_rate = card->wandev.bps;
-
+	}
+		
 	cfg.line_config_options = (card->wandev.interface == WANOPT_RS232) ?
 		INTERFACE_LEVEL_RS232 : INTERFACE_LEVEL_V35;
 
@@ -1750,9 +2317,18 @@
 	cfg.modem_status_timer		= 100;
 
 	cfg.CHDLC_protocol_options	= card->u.c.protocol_options;
-	cfg.percent_data_buffer_for_Tx	= 50;
+
+	if (card->tty_opt){
+		cfg.CHDLC_API_options	= DISCARD_RX_ERROR_FRAMES;
+	}
+	
+	cfg.percent_data_buffer_for_Tx  = (card->u.c.receive_only) ? 0 : 50;
 	cfg.CHDLC_statistics_options	= (CHDLC_TX_DATA_BYTE_COUNT_STAT |
 		CHDLC_RX_DATA_BYTE_COUNT_STAT);
+	
+	if (card->tty_opt){
+		card->wandev.mtu = TTY_CHDLC_MAX_MTU;
+	}
 	cfg.max_CHDLC_data_field_length	= card->wandev.mtu;
 	cfg.transmit_keepalive_timer	= card->u.c.kpalv_tx;
 	cfg.receive_keepalive_timer	= card->u.c.kpalv_rx;
@@ -1762,9 +2338,12 @@
 	if (cfg.SLARP_request_timer) {
 		cfg.IP_address		= 0;
 		cfg.IP_netmask		= 0;
-	}
-	else {
-#ifdef LINUX_2_1
+		
+	}else if (card->wandev.dev){
+		netdevice_t * dev = card->wandev.dev;
+		chdlc_private_area_t *chdlc_priv_area = dev->priv;
+		
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
                 struct in_device *in_dev = dev->ip_ptr;
 
 		if(in_dev != NULL) {
@@ -1773,10 +2352,8 @@
 			if (ifa != NULL ) {
 				cfg.IP_address	= ntohl(ifa->ifa_local);
 				cfg.IP_netmask	= ntohl(ifa->ifa_mask); 
-				chdlc_priv_area->IP_address = 
-					ntohl(ifa->ifa_local);
-				chdlc_priv_area->IP_netmask =
-					ntohl(ifa->ifa_mask); 
+				chdlc_priv_area->IP_address = ntohl(ifa->ifa_local);
+				chdlc_priv_area->IP_netmask = ntohl(ifa->ifa_mask); 
 			}
 		}
 #else
@@ -1803,6 +2380,70 @@
 }
 
 
+/*-----------------------------------------------------------------------------
+   set_asy_config() used to set asynchronous configuration options on the board
+------------------------------------------------------------------------------*/
+
+static int set_asy_config(sdla_t* card)
+{
+
+        ASY_CONFIGURATION_STRUCT cfg;
+ 	CHDLC_MAILBOX_STRUCT *mailbox = card->mbox;
+	int err;
+
+	memset(&cfg, 0, sizeof(ASY_CONFIGURATION_STRUCT));
+
+	if(card->wandev.clocking)
+		cfg.baud_rate = card->wandev.bps;
+
+	cfg.line_config_options = (card->wandev.interface == WANOPT_RS232) ?
+		INTERFACE_LEVEL_RS232 : INTERFACE_LEVEL_V35;
+
+	cfg.modem_config_options	= 0;
+	cfg.asy_API_options 		= card->u.c.api_options;
+	cfg.asy_protocol_options	= card->u.c.protocol_options;
+	cfg.Tx_bits_per_char		= card->u.c.tx_bits_per_char;
+	cfg.Rx_bits_per_char		= card->u.c.rx_bits_per_char;
+	cfg.stop_bits			= card->u.c.stop_bits;
+	cfg.parity			= card->u.c.parity;
+	cfg.break_timer			= card->u.c.break_timer;
+	cfg.asy_Rx_inter_char_timer	= card->u.c.inter_char_timer; 
+	cfg.asy_Rx_complete_length	= card->u.c.rx_complete_length; 
+	cfg.XON_char			= card->u.c.xon_char;
+	cfg.XOFF_char			= card->u.c.xoff_char;
+	cfg.asy_statistics_options	= (CHDLC_TX_DATA_BYTE_COUNT_STAT |
+		CHDLC_RX_DATA_BYTE_COUNT_STAT);
+
+	mailbox->buffer_length = sizeof(ASY_CONFIGURATION_STRUCT);
+	memcpy(mailbox->data, &cfg, mailbox->buffer_length);
+	mailbox->command = SET_ASY_CONFIGURATION;
+	err = sdla_exec(mailbox) ? mailbox->return_code : CMD_TIMEOUT;
+	if (err != COMMAND_OK) 
+		chdlc_error (card, err, mailbox);
+	return err;
+}
+
+/*============================================================================
+ * Enable asynchronous communications.
+ */
+
+static int asy_comm_enable (sdla_t* card)
+{
+
+	int err;
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+
+	mb->buffer_length = 0;
+	mb->command = ENABLE_ASY_COMMUNICATIONS;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+	if (err != COMMAND_OK && card->wandev.dev)
+		chdlc_error(card, err, mb);
+	
+	if (!err)
+		card->u.c.comm_enabled = 1;
+
+	return err;
+}
 
 /*============================================================================
  * Process global exception condition
@@ -1830,7 +2471,8 @@
 					printk(KERN_INFO "%s: DCD high, CTS low\n",card->devname);
 					break;
 				case (CTS_HIGH):
-                                        printk(KERN_INFO "%s: DCD low, CTS high\n",card->devname);                                        break;
+                                        printk(KERN_INFO "%s: DCD low, CTS high\n",card->devname); 
+					break;
                                 case ((DCD_HIGH | CTS_HIGH)):
                                         printk(KERN_INFO "%s: DCD high, CTS high\n",card->devname);
                                         break;
@@ -1850,6 +2492,19 @@
 				card->devname); 
 			break;
 
+		case 0x17:
+			if (card->tty_opt){
+				if (card->tty && card->tty_open){ 
+					printk(KERN_INFO 
+						"%s: Modem Hangup Exception: Hanging Up!\n",
+						card->devname);
+					tty_hangup(card->tty);
+				}
+				break;
+			}
+
+			/* If TTY is not used just drop throught */
+			
                 default:
                         printk(KERN_INFO "%s: Global exception %x\n",
 				card->devname, mbox->return_code);
@@ -1877,11 +2532,13 @@
 
 		case EXCEP_LINK_ACTIVE:
 			port_set_state(card, WAN_CONNECTED);
+			trigger_chdlc_poll(card->wandev.dev);
 			break;
 
 		case EXCEP_LINK_INACTIVE_MODEM:
 			port_set_state(card, WAN_DISCONNECTED);
 			unconfigure_ip(card);
+			trigger_chdlc_poll(card->wandev.dev);
 			break;
 
 		case EXCEP_LINK_INACTIVE_KPALV:
@@ -1889,6 +2546,7 @@
 			printk(KERN_INFO "%s: Keepalive timer expired.\n",
 				 		card->devname);
 			unconfigure_ip(card);
+			trigger_chdlc_poll(card->wandev.dev);
 			break;
 
 		case EXCEP_IP_ADDRESS_DISCOVERED:
@@ -1919,10 +2577,16 @@
 
 static int configure_ip (sdla_t* card)
 {
-	struct net_device *dev = card->wandev.dev;
-        chdlc_private_area_t *chdlc_priv_area = dev->priv;
+	netdevice_t *dev = card->wandev.dev;
+        chdlc_private_area_t *chdlc_priv_area;
         char err;
 
+	if (!dev)
+		return 0;
+
+	chdlc_priv_area = dev->priv;
+	
+	
         /* set to discover */
         if(card->u.c.slarp_timer != 0x00) {
 		CHDLC_MAILBOX_STRUCT* mb = card->mbox;
@@ -1940,14 +2604,14 @@
 		cfg = (CHDLC_CONFIGURATION_STRUCT *)mb->data;
                 chdlc_priv_area->IP_address = cfg->IP_address;
                 chdlc_priv_area->IP_netmask = cfg->IP_netmask;
-        }
 
-	/* Set flag to add route */
-	chdlc_priv_area->route_status = ADD_ROUTE;
+		/* Set flag to add route */
+		chdlc_priv_area->route_status = ADD_ROUTE;
 
-	/* The idea here is to add the route in the poll routine.
-	   This way, we aren't in interrupt context when adding routes */
-	card->poll = process_route;
+		/* The idea here is to add the route in the poll routine.
+	   	This way, we aren't in interrupt context when adding routes */
+		trigger_chdlc_poll(dev);
+        }
 
 	return 0;
 }
@@ -1960,17 +2624,23 @@
 
 static int unconfigure_ip (sdla_t* card)
 {
-	struct net_device *dev = card->wandev.dev;
-	chdlc_private_area_t *chdlc_priv_area= dev->priv;
+	netdevice_t *dev = card->wandev.dev;
+	chdlc_private_area_t *chdlc_priv_area;
+
+	if (!dev)
+		return 0;
 
+	chdlc_priv_area= dev->priv;
+	
 	if (chdlc_priv_area->route_status == ROUTE_ADDED) {
-	      	chdlc_priv_area->route_status = REMOVE_ROUTE;
-		/* The idea here is to delete the route in 
-		 * the poll routine.
-	   	 * This way, we aren't in interrupt context 
-		 * when adding routes 
+
+		/* Note: If this function is called, the 
+                 * port state has been DISCONNECTED.  This state
+                 * change will trigger a poll_disconnected 
+                 * function, that will check for this condition. 
 		 */
-                card->poll = process_route;
+		chdlc_priv_area->route_status = REMOVE_ROUTE;
+
 	}
 	return 0;
 }
@@ -1982,14 +2652,14 @@
 
 static void process_route (sdla_t *card)
 {
-        struct net_device *dev = card->wandev.dev;
+        netdevice_t *dev = card->wandev.dev;
         unsigned char port_num;
         chdlc_private_area_t *chdlc_priv_area = NULL;
 	u32 local_IP_addr = 0;
 	u32 remote_IP_addr = 0;
 	u32 IP_netmask, IP_addr;
         int err = 0;
-#ifdef LINUX_2_1
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct in_device *in_dev;
 	mm_segment_t fs;
 	struct ifreq if_info;
@@ -2002,16 +2672,25 @@
         chdlc_priv_area = dev->priv;
         port_num = card->u.c.comm_port;
 
+	/* Bug Fix Mar 16 2000
+	 * AND the IP address to the Mask before checking
+         * the last two bits. */
+
 	if((chdlc_priv_area->route_status == ADD_ROUTE) &&
-		((chdlc_priv_area->IP_address & 0x000000FF) > 2)) {
+		((chdlc_priv_area->IP_address & ~chdlc_priv_area->IP_netmask) > 2)) {
+
 		printk(KERN_INFO "%s: Dynamic route failure.\n",card->devname);
+
                 if(card->u.c.slarp_timer) {
+
 			printk(KERN_INFO "%s: Bad IP address %s received\n",
 				card->devname,
 				in_ntoa(ntohl(chdlc_priv_area->IP_address)));
                         printk(KERN_INFO "%s: from remote station.\n",
 				card->devname);
+
                 }else{ 
+
                         printk(KERN_INFO "%s: Bad IP address %s issued\n",
 				card->devname,
 				in_ntoa(ntohl(chdlc_priv_area->IP_address)));
@@ -2033,11 +2712,10 @@
 
  	        /* do not remove a bad route that has already been removed */
         	if(chdlc_priv_area->route_removed) {
-                	card->poll = NULL;
 	                return;
         	}
 
-#ifdef LINUX_2_1
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
                 in_dev = dev->ip_ptr;
 
                 if(in_dev != NULL) {
@@ -2058,7 +2736,17 @@
 		*/
 		IP_netmask = ntohl(chdlc_priv_area->IP_netmask);
 	        remote_IP_addr = ntohl(chdlc_priv_area->IP_address);
-        	local_IP_addr = (remote_IP_addr & ntohl(0xFFFFFF00)) +
+	
+
+		/* If Netmask is 255.255.255.255 the local address
+                 * calculation will fail. Default it back to 255.255.255.0 */
+		if (IP_netmask == 0xffffffff)
+			IP_netmask &= 0x00ffffff;
+
+		/* Bug Fix Mar 16 2000
+		 * AND the Remote IP address with IP netmask, instead
+                 * of static netmask of 255.255.255.0 */
+        	local_IP_addr = (remote_IP_addr & IP_netmask) +
                 	(~remote_IP_addr & ntohl(0x0003));
 
 	        if(!card->u.c.slarp_timer) {
@@ -2071,7 +2759,7 @@
         fs = get_fs();                  /* Save file system  */
         set_fs(get_ds());               /* Get user space block */
 
-#ifdef LINUX_2_1
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
         /* Setup a structure for adding/removing routes */
         memset(&if_info, 0, sizeof(if_info));
         strcpy(if_info.ifr_name, dev->name);
@@ -2099,7 +2787,7 @@
 	case ADD_ROUTE:
 
 		if(!card->u.c.slarp_timer) {
-#ifdef LINUX_2_1
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 			if_data2 = (struct sockaddr_in *)&if_info.ifr_dstaddr;
 			if_data2->sin_addr.s_addr = remote_IP_addr;
 			if_data2->sin_family = AF_INET;
@@ -2108,7 +2796,7 @@
                         err = ip_rt_new(&route);
 #endif
 		} else { 
-#ifdef LINUX_2_1
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 			if_data1 = (struct sockaddr_in *)&if_info.ifr_addr;
 			if_data1->sin_addr.s_addr = local_IP_addr;
 			if_data1->sin_family = AF_INET;
@@ -2141,7 +2829,7 @@
 
 	case REMOVE_ROUTE:
 	
-#ifdef LINUX_2_1
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 		/* Change the local ip address of the interface to 0.
 		 * This will also delete the destination route.
 		 */
@@ -2179,9 +2867,6 @@
 
         set_fs(fs);                     /* Restore file system */
 
-        /* Once we've processed the route, stop polling */
-        card->poll = NULL;
-
 }
 
 
@@ -2190,7 +2875,7 @@
  */
 
 static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card,
-                                struct sk_buff *skb, struct net_device* dev,
+                                struct sk_buff *skb, netdevice_t* dev,
                                 chdlc_private_area_t* chdlc_priv_area )
 {
 	int udp_pkt_stored = 0;
@@ -2204,15 +2889,12 @@
 		udp_pkt_stored = 1;
 	}
 
-#ifdef LINUX_2_1
-	dev_kfree_skb(skb);
-#else
-	if(udp_pkt_src == UDP_PKT_FRM_STACK)
-		dev_kfree_skb(skb, FREE_WRITE);
-	else
-                dev_kfree_skb(skb, FREE_READ);
-#endif
-	
+	if(udp_pkt_src == UDP_PKT_FRM_STACK){
+		wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+                wan_dev_kfree_skb(skb, FREE_READ);
+	}
+		
 	return(udp_pkt_stored);
 }
 
@@ -2221,7 +2903,7 @@
  * Process UDP management packet.
  */
 
-static int process_udp_mgmt_pkt(sdla_t* card, struct net_device* dev,
+static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev,
 				chdlc_private_area_t* chdlc_priv_area ) 
 {
 	unsigned char *buf;
@@ -2240,22 +2922,30 @@
 
 	chdlc_udp_pkt = (chdlc_udp_pkt_t *) chdlc_priv_area->udp_pkt_data;
 
-	switch(chdlc_udp_pkt->cblock.command) {
+	if(chdlc_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK){
 
-	   	case FT1_MONITOR_STATUS_CTRL:
-		case CPIPE_ENABLE_TRACING:
-	   	case CPIPE_DISABLE_TRACING:
- 	  	case CPIPE_GET_TRACE_INFO:
-	   	case SET_FT1_MODE:
-			if(chdlc_priv_area->udp_pkt_src == 
-				UDP_PKT_FRM_NETWORK) {
-			     udp_mgmt_req_valid = 0;
-       			}	 
-       			break;
-	   
-		default:
-       			break;
-  	} 
+		/* Only these commands are support for remote debugging.
+		 * All others are not */
+		switch(chdlc_udp_pkt->cblock.command) {
+
+			case READ_GLOBAL_STATISTICS:
+			case READ_MODEM_STATUS:  
+			case READ_CHDLC_LINK_STATUS:
+			case CPIPE_ROUTER_UP_TIME:
+			case READ_COMMS_ERROR_STATS:
+			case READ_CHDLC_OPERATIONAL_STATS:
+
+			/* These two commands are executed for
+			 * each request */
+			case READ_CHDLC_CONFIGURATION:
+			case READ_CHDLC_CODE_VERSION:
+				udp_mgmt_req_valid = 1;
+				break;
+			default:
+				udp_mgmt_req_valid = 0;
+				break;
+		} 
+	}
 	
   	if(!udp_mgmt_req_valid) {
 
@@ -2265,6 +2955,12 @@
     		/* set return code */
 		chdlc_udp_pkt->cblock.return_code = 0xCD;
 
+		if (net_ratelimit()){	
+			printk(KERN_INFO 
+			"%s: Warning, Illegal UDP command attempted from network: %x\n",
+			card->devname,chdlc_udp_pkt->cblock.command);
+		}
+
    	} else {
 	   	unsigned long trace_status_cfg_addr = 0;
 		TRACE_STATUS_EL_CFG_STRUCT trace_cfg_struct;
@@ -2488,11 +3184,12 @@
 
 			((unsigned char *)chdlc_udp_pkt->data )[1] =
 				flags->FT1_info_struct.parallel_port_B_input;
-				 
+				
 			chdlc_udp_pkt->cblock.return_code = COMMAND_OK;
+			chdlc_udp_pkt->cblock.buffer_length = 2;
 			mb->buffer_length = 2;
 			break;
-		
+
 		case CPIPE_ROUTER_UP_TIME:
 			do_gettimeofday( &tv );
 			chdlc_priv_area->router_up_time = tv.tv_sec - 
@@ -2500,6 +3197,8 @@
 			*(unsigned long *)&chdlc_udp_pkt->data = 
 					chdlc_priv_area->router_up_time;	
 			mb->buffer_length = sizeof(unsigned long);
+			chdlc_udp_pkt->cblock.buffer_length = sizeof(unsigned long);
+			chdlc_udp_pkt->cblock.return_code = COMMAND_OK;
 			break;
 
    		case FT1_MONITOR_STATUS_CTRL:
@@ -2525,8 +3224,10 @@
 		  			break;
 	   	    	     	} 
 	      		} 	
-	
+			goto dflt_1;
+
 		default:
+dflt_1:
 			/* it's a board command */
 			mb->command = chdlc_udp_pkt->cblock.command;
 			mb->buffer_length = chdlc_udp_pkt->cblock.buffer_length;
@@ -2556,12 +3257,19 @@
 
      	len = reply_udp(chdlc_priv_area->udp_pkt_data, mb->buffer_length);
 	
-     	if(chdlc_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-		if(!chdlc_send(card, chdlc_priv_area->udp_pkt_data, len)) {
-			++ card->wandev.stats.tx_packets;
-#ifdef LINUX_2_1
-			card->wandev.stats.tx_bytes += len;
-#endif
+
+     	if(chdlc_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK){
+
+		/* Must check if we interrupted if_send() routine. The
+		 * tx buffers might be used. If so drop the packet */
+	   	if (!test_bit(SEND_CRIT,&card->wandev.critical)) {
+		
+			if(!chdlc_send(card, chdlc_priv_area->udp_pkt_data, len)) {
+				++ card->wandev.stats.tx_packets;
+				#if defined(LINUX_2_1) || defined(LINUX_2_4)
+				card->wandev.stats.tx_bytes += len;
+				#endif
+			}
 		}
 	} else {	
 	
@@ -2595,7 +3303,7 @@
  * Initialize Receive and Transmit Buffers.
  */
 
-static void init_chdlc_tx_rx_buff( sdla_t* card, struct net_device *dev )
+static void init_chdlc_tx_rx_buff( sdla_t* card)
 {
 	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
 	CHDLC_TX_STATUS_EL_CFG_STRUCT *tx_config;
@@ -2607,7 +3315,9 @@
 	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
 
 	if(err != COMMAND_OK) {
-		chdlc_error(card,err,mb);
+		if (card->wandev.dev){
+			chdlc_error(card,err,mb);
+		}
 		return;
 	}
 
@@ -2678,19 +3388,13 @@
  * Perform Interrupt Test by running READ_CHDLC_CODE_VERSION command MAX_INTR
  * _TEST_COUNTER times.
  */
-static int intr_test( sdla_t* card, struct net_device *dev )
+static int intr_test( sdla_t* card)
 {
 	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
 	int err,i;
 
 	Intr_test_counter = 0;
-
-	/* The critical flag is unset because during intialization (if_open) 
-	 * we want the interrupts to be enabled so that when the wpc_isr is
-	 * called it does not exit due to critical flag set.
-	 */ 
-
-	clear_bit(1, (void*)&card->wandev.critical);
+	
 	err = chdlc_set_intr_mode(card, APP_INT_ON_COMMAND_COMPLETE);
 
 	if (err == CMD_OK) { 
@@ -2707,8 +3411,6 @@
 	}
 
 	err = chdlc_set_intr_mode(card, 0);
-	set_bit(1, (void*)&card->wandev.critical);
-
 
 	if (err != CMD_OK)
 		return err;
@@ -2723,13 +3425,33 @@
 {
 	 chdlc_udp_pkt_t *chdlc_udp_pkt = (chdlc_udp_pkt_t *)skb->data;
 
+#ifdef _WAN_UDP_DEBUG
+		printk(KERN_INFO "SIG %s = %s\n\
+				  UPP %x = %x\n\
+				  PRT %x = %x\n\
+				  REQ %i = %i\n\
+				  36 th = %x 37th = %x\n",
+				  chdlc_udp_pkt->wp_mgmt.signature,
+				  UDPMGMT_SIGNATURE,
+				  chdlc_udp_pkt->udp_pkt.udp_dst_port,
+				  ntohs(card->wandev.udp_port),
+				  chdlc_udp_pkt->ip_pkt.protocol,
+				  UDPMGMT_UDP_PROTOCOL,
+				  chdlc_udp_pkt->wp_mgmt.request_reply,
+				  UDPMGMT_REQUEST,
+				  skb->data[36], skb->data[37]);
+#endif	
+		
 	if (!strncmp(chdlc_udp_pkt->wp_mgmt.signature,UDPMGMT_SIGNATURE,8) &&
 	   (chdlc_udp_pkt->udp_pkt.udp_dst_port == ntohs(card->wandev.udp_port)) &&
 	   (chdlc_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) &&
 	   (chdlc_udp_pkt->wp_mgmt.request_reply == UDPMGMT_REQUEST)) {
+
 		return UDP_CPIPE_TYPE;
+
+	}else{ 
+		return UDP_INVALID_TYPE;
 	}
-	else return UDP_INVALID_TYPE;
 }
 
 /*============================================================================
@@ -2744,7 +3466,7 @@
                 case WAN_CONNECTED:
                         printk (KERN_INFO "%s: Link connected!\n",
                                 card->devname);
-                      break;
+                      	break;
 
                 case WAN_CONNECTING:
                         printk (KERN_INFO "%s: Link connecting...\n",
@@ -2758,31 +3480,1268 @@
                 }
 
                 card->wandev.state = card->u.c.state = state;
+		if (card->wandev.dev){
+			netdevice_t *dev = card->wandev.dev;
+			chdlc_private_area_t *chdlc_priv_area = dev->priv;
+			chdlc_priv_area->common.state = state;
+		}
         }
 }
 
-void s508_lock (sdla_t *card, unsigned long *smp_flags)
+/*===========================================================================
+ * config_chdlc
+ *
+ *	Configure the chdlc protocol and enable communications.		
+ *
+ *   	The if_open() function binds this function to the poll routine.
+ *      Therefore, this function will run every time the chdlc interface
+ *      is brought up. We cannot run this function from the if_open 
+ *      because if_open does not have access to the remote IP address.
+ *      
+ *	If the communications are not enabled, proceed to configure
+ *      the card and enable communications.
+ *
+ *      If the communications are enabled, it means that the interface
+ *      was shutdown by ether the user or driver. In this case, we 
+ *      have to check that the IP addresses have not changed.  If
+ *      the IP addresses have changed, we have to reconfigure the firmware
+ *      and update the changed IP addresses.  Otherwise, just exit.
+ *
+ */
+
+static int config_chdlc (sdla_t *card)
 {
-#ifdef CONFIG_SMP
-                spin_lock_irqsave(&card->lock, *smp_flags);
-                if (card->next){
-                        spin_lock(&card->next->lock);
-                }
-#else
-                disable_irq(card->hw.irq);
-#endif                                                                     
+	netdevice_t *dev = card->wandev.dev;
+	chdlc_private_area_t *chdlc_priv_area = dev->priv;
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+
+	if (card->u.c.comm_enabled){
+
+		/* Jun 20. 2000: NC
+		 * IP addresses are not used in the API mode */
+		
+		if ((chdlc_priv_area->ip_local_tmp != chdlc_priv_area->ip_local ||
+		     chdlc_priv_area->ip_remote_tmp != chdlc_priv_area->ip_remote) && 
+		     card->u.c.usedby == WANPIPE) {
+			
+			/* The IP addersses have changed, we must
+                         * stop the communications and reconfigure
+                         * the card. Reason: the firmware must know
+                         * the local and remote IP addresses. */
+			disable_comm(card);
+			port_set_state(card, WAN_DISCONNECTED);
+			printk(KERN_INFO 
+				"%s: IP addresses changed!\n",
+					card->devname);
+			printk(KERN_INFO 
+				"%s: Restarting communications ...\n",
+					card->devname);
+		}else{ 
+			/* IP addresses are the same and the link is up, 
+                         * we dont have to do anything here. Therefore, exit */
+			return 0;
+		}
+	}
+
+	chdlc_priv_area->ip_local = chdlc_priv_area->ip_local_tmp;
+	chdlc_priv_area->ip_remote = chdlc_priv_area->ip_remote_tmp;
+
+
+	/* Setup the Board for asynchronous mode */
+	if (card->u.c.async_mode){
+		
+		if (set_asy_config(card)) {
+			printk (KERN_INFO "%s: Failed CHDLC Async configuration!\n",
+				card->devname);
+			return 0;
+		}
+	}else{
+		/* Setup the Board for CHDLC */
+		if (set_chdlc_config(card)) {
+			printk (KERN_INFO "%s: Failed CHDLC configuration!\n",
+				card->devname);
+			return 0;
+		}
+	}
+
+	/* Set interrupt mode and mask */
+        if (chdlc_set_intr_mode(card, APP_INT_ON_RX_FRAME |
+                		APP_INT_ON_GLOBAL_EXCEP_COND |
+                		APP_INT_ON_TX_FRAME |
+                		APP_INT_ON_CHDLC_EXCEP_COND | APP_INT_ON_TIMER)){
+		printk (KERN_INFO "%s: Failed to set interrupt triggers!\n",
+				card->devname);
+		return 0;	
+        }
+	
+
+	/* Mask the Transmit and Timer interrupt */
+	flags->interrupt_info_struct.interrupt_permission &= 
+		~(APP_INT_ON_TX_FRAME | APP_INT_ON_TIMER);
+
+	/* In TTY mode, receive interrupt will be enabled during
+	 * wanpipe_tty_open() operation */
+	if (card->tty_opt){
+		flags->interrupt_info_struct.interrupt_permission &= ~APP_INT_ON_RX_FRAME;
+	}
+
+	/* Enable communications */
+ 	if (card->u.c.async_mode){
+		if (asy_comm_enable(card) != 0) {
+			printk(KERN_INFO "%s: Failed to enable async commnunication!\n",
+					card->devname);
+			flags->interrupt_info_struct.interrupt_permission = 0;
+			card->u.c.comm_enabled=0;
+			chdlc_set_intr_mode(card,0);
+			return 0;
+		}
+        }else{ 
+		if (chdlc_comm_enable(card) != 0) {
+			printk(KERN_INFO "%s: Failed to enable chdlc communications!\n",
+					card->devname);
+			flags->interrupt_info_struct.interrupt_permission = 0;
+			card->u.c.comm_enabled=0;
+			chdlc_set_intr_mode(card,0);
+			return 0;
+		}
+	}
+
+	/* Initialize Rx/Tx buffer control fields */
+	init_chdlc_tx_rx_buff(card);
+	port_set_state(card, WAN_CONNECTING);
+	return 0; 
 }
 
-void s508_unlock (sdla_t *card, unsigned long *smp_flags)
+
+/*============================================================
+ * chdlc_poll
+ *	
+ * Rationale:
+ * 	We cannot manipulate the routing tables, or
+ *      ip addresses withing the interrupt. Therefore
+ *      we must perform such actons outside an interrupt 
+ *      at a later time. 
+ *
+ * Description:	
+ *	CHDLC polling routine, responsible for 
+ *     	shutting down interfaces upon disconnect
+ *     	and adding/removing routes. 
+ *      
+ * Usage:        
+ * 	This function is executed for each CHDLC  
+ * 	interface through a tq_schedule bottom half.
+ *      
+ *      trigger_chdlc_poll() function is used to kick
+ *      the chldc_poll routine.  
+ */
+
+static void chdlc_poll (netdevice_t *dev)
 {
-#ifdef CONFIG_SMP
-                        if (card->next){
-                                spin_unlock(&card->next->lock);
-                        }
-                        spin_unlock_irqrestore(&card->lock, *smp_flags);
-#else
-                        enable_irq(card->hw.irq);
-#endif           
+	chdlc_private_area_t *chdlc_priv_area;
+	sdla_t *card;
+	u8 check_gateway=0;	
+	SHARED_MEMORY_INFO_STRUCT* flags;
+
+	
+	if (!dev || (chdlc_priv_area=dev->priv) == NULL)
+		return;
+
+	card = chdlc_priv_area->card;
+	flags = card->u.c.flags;
+	
+	/* (Re)Configuraiton is in progress, stop what you are 
+	 * doing and get out */
+	if (test_bit(PERI_CRIT,&card->wandev.critical)){
+		clear_bit(POLL_CRIT,&card->wandev.critical);
+		return;
+	}
+	
+	/* if_open() function has triggered the polling routine
+	 * to determine the configured IP addresses.  Once the
+	 * addresses are found, trigger the chdlc configuration */
+	if (test_bit(0,&chdlc_priv_area->config_chdlc)){
+
+		chdlc_priv_area->ip_local_tmp  = get_ip_address(dev,WAN_LOCAL_IP);
+		chdlc_priv_area->ip_remote_tmp = get_ip_address(dev,WAN_POINTOPOINT_IP);
+	
+	       /* Jun 20. 2000 Bug Fix
+	 	* Only perform this check in WANPIPE mode, since
+	 	* IP addresses are not used in the API mode. */
+	
+		if (chdlc_priv_area->ip_local_tmp == chdlc_priv_area->ip_remote_tmp && 
+		    card->u.c.slarp_timer == 0x00 && 
+		    !card->u.c.backup && 
+		    card->u.c.usedby == WANPIPE){
+
+			if (++chdlc_priv_area->ip_error > MAX_IP_ERRORS){
+				printk(KERN_INFO "\n%s: --- WARNING ---\n",
+						card->devname);
+				printk(KERN_INFO 
+				"%s: The local IP address is the same as the\n",
+						card->devname);
+				printk(KERN_INFO 
+				"%s: Point-to-Point IP address.\n",
+						card->devname);
+				printk(KERN_INFO "%s: --- WARNING ---\n\n",
+						card->devname);
+			}else{
+				clear_bit(POLL_CRIT,&card->wandev.critical);
+				chdlc_priv_area->poll_delay_timer.expires = jiffies+HZ;
+				add_timer(&chdlc_priv_area->poll_delay_timer);
+				return;
+			}
+		}
+
+		clear_bit(0,&chdlc_priv_area->config_chdlc);
+		clear_bit(POLL_CRIT,&card->wandev.critical);
+		
+		chdlc_priv_area->timer_int_enabled |= TMR_INT_ENABLED_CONFIG;
+		flags->interrupt_info_struct.interrupt_permission |= APP_INT_ON_TIMER;
+		return;
+	}
+	/* Dynamic interface implementation, as well as dynamic
+	 * routing.  */
+	
+	switch (card->u.c.state){
+
+	case WAN_DISCONNECTED:
+
+		/* If the dynamic interface configuration is on, and interface 
+		 * is up, then bring down the netowrk interface */
+		
+		if (test_bit(DYN_OPT_ON,&chdlc_priv_area->interface_down) && 
+		    !test_bit(DEV_DOWN,  &chdlc_priv_area->interface_down) &&		
+		    card->wandev.dev->flags & IFF_UP){	
+
+			printk(KERN_INFO "%s: Interface %s down.\n",
+				card->devname,card->wandev.dev->name);
+			change_dev_flags(card->wandev.dev,(card->wandev.dev->flags&~IFF_UP));
+			set_bit(DEV_DOWN,&chdlc_priv_area->interface_down);
+			chdlc_priv_area->route_status = NO_ROUTE;
+
+		}else{
+			/* We need to check if the local IP address is
+               	  	 * zero. If it is, we shouldn't try to remove it.
+                 	 */
+
+			if (card->wandev.dev->flags & IFF_UP && 
+		    	    get_ip_address(card->wandev.dev,WAN_LOCAL_IP) && 
+		    	    chdlc_priv_area->route_status != NO_ROUTE &&
+			    card->u.c.slarp_timer){
+
+				process_route(card);
+			}
+		}
+		break;
+
+	case WAN_CONNECTED:
+
+		/* In SMP machine this code can execute before the interface
+		 * comes up.  In this case, we must make sure that we do not
+		 * try to bring up the interface before dev_open() is finished */
+
+
+		/* DEV_DOWN will be set only when we bring down the interface
+		 * for the very first time. This way we know that it was us
+		 * that brought the interface down */
+		
+		if (test_bit(DYN_OPT_ON,&chdlc_priv_area->interface_down) &&
+		    test_bit(DEV_DOWN,  &chdlc_priv_area->interface_down) &&
+		    !(card->wandev.dev->flags & IFF_UP)){
+			
+			printk(KERN_INFO "%s: Interface %s up.\n",
+				card->devname,card->wandev.dev->name);
+			change_dev_flags(card->wandev.dev,(card->wandev.dev->flags|IFF_UP));
+			clear_bit(DEV_DOWN,&chdlc_priv_area->interface_down);
+			check_gateway=1;
+		}
+
+		if (chdlc_priv_area->route_status == ADD_ROUTE && 
+		    card->u.c.slarp_timer){ 
+
+			process_route(card);
+			check_gateway=1;
+		}
+
+		if (chdlc_priv_area->gateway && check_gateway)
+			add_gateway(card,dev);
+
+		break;
+	}	
+
+	clear_bit(POLL_CRIT,&card->wandev.critical);
 }
+
+/*============================================================
+ * trigger_chdlc_poll
+ *
+ * Description:
+ * 	Add a chdlc_poll() task into a tq_scheduler bh handler
+ *      for a specific dlci/interface.  This will kick
+ *      the fr_poll() routine at a later time. 
+ *
+ * Usage:
+ * 	Interrupts use this to defer a taks to 
+ *      a polling routine.
+ *
+ */	
+static void trigger_chdlc_poll (netdevice_t *dev)
+{
+	chdlc_private_area_t *chdlc_priv_area;
+	sdla_t *card;
+
+	if (!dev)
+		return;
+	
+	if ((chdlc_priv_area = dev->priv)==NULL)
+		return;
+
+	card = chdlc_priv_area->card;
+	
+	if (test_and_set_bit(POLL_CRIT,&card->wandev.critical)){
+		return;
+	}
+	if (test_bit(PERI_CRIT,&card->wandev.critical)){
+		return; 
+	}
+      #ifdef LINUX_2_4
+	schedule_task(&chdlc_priv_area->poll_task);
+      #else
+	queue_task(&chdlc_priv_area->poll_task, &tq_scheduler);
+      #endif
+	return;
+}
+
+
+static void chdlc_poll_delay (unsigned long dev_ptr)
+{
+	netdevice_t *dev = (netdevice_t *)dev_ptr;
+	trigger_chdlc_poll(dev);
+}
+
+
+void s508_lock (sdla_t *card, unsigned long *smp_flags)
+{
+#if defined(__SMP__) || defined(LINUX_2_4)
+	spin_lock_irqsave(&card->wandev.lock, *smp_flags);
+        if (card->next){
+        	spin_lock(&card->next->wandev.lock);
+	}
+#else
+        disable_irq(card->hw.irq);
+#endif                                                                     
+}
+
+void s508_unlock (sdla_t *card, unsigned long *smp_flags)
+{
+#if defined(__SMP__) || defined(LINUX_2_4)
+        if (card->next){
+        	spin_unlock(&card->next->wandev.lock);
+        }
+        spin_unlock_irqrestore(&card->wandev.lock, *smp_flags);
+#else
+        enable_irq(card->hw.irq);
+#endif           
+}
+
+//*********** TTY SECTION ****************
+#if defined(LINUX_2_4) || defined(LINUX_2_1)
+
+static void wanpipe_tty_trigger_tx_irq(sdla_t *card)
+{
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+	INTERRUPT_INFORMATION_STRUCT *chdlc_int = &flags->interrupt_info_struct;
+	chdlc_int->interrupt_permission |= APP_INT_ON_TX_FRAME;
+}
+
+static void wanpipe_tty_trigger_poll(sdla_t *card)
+{
+      #ifdef LINUX_2_4
+	schedule_task(&card->tty_task_queue);
+      #else
+	queue_task(&card->tty_task_queue, &tq_scheduler);
+      #endif
+}
+
+static void tty_poll_task (void* data)
+{
+	sdla_t *card = (sdla_t*)data;
+	struct tty_struct *tty;
+
+	if ((tty=card->tty)==NULL)
+		return;
+	
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup){
+		(tty->ldisc.write_wakeup)(tty);
+	}
+	wake_up_interruptible(&tty->write_wait);
+      #if defined(SERIAL_HAVE_POLL_WAIT) || \
+         (defined LINUX_2_1 && LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15))
+	wake_up_interruptible(&tty->poll_wait);
+      #endif	
+	return;
+}
+
+static void wanpipe_tty_close(struct tty_struct *tty, struct file * filp)
+{
+	sdla_t *card;
+	unsigned long smp_flags;
+	
+	if (!tty || !tty->driver_data){
+		return;
+	}
+	
+	card = (sdla_t*)tty->driver_data;
+	
+	if (!card)
+		return;
+
+	printk(KERN_INFO "%s: Closing TTY Driver!\n",
+			card->devname);
+
+	/* Sanity Check */
+	if (!card->tty_open)
+		return;
+	
+	wanpipe_close(card);
+	if (--card->tty_open == 0){
+
+		lock_adapter_irq(&card->wandev.lock,&smp_flags);	
+		card->tty=NULL;
+		chdlc_disable_comm_shutdown(card);
+		unlock_adapter_irq(&card->wandev.lock,&smp_flags);
+
+		if (card->tty_buf){
+			kfree(card->tty_buf);
+			card->tty_buf=NULL;			
+		}
+
+		if (card->tty_rx){
+			kfree(card->tty_rx);
+			card->tty_rx=NULL;
+		}
+	}
+	return;
+}
+static int wanpipe_tty_open(struct tty_struct *tty, struct file * filp)
+{
+	unsigned long smp_flags;
+	sdla_t *card;
+	
+	if (!tty){
+		return -ENODEV;
+	}
+	
+	if (!tty->driver_data){
+		int port;
+		port = MINOR(tty->device) - tty->driver.minor_start;
+		if ((port < 0) || (port >= NR_PORTS)) 
+			return -ENODEV;
+		
+		tty->driver_data = WAN_CARD(port);
+		if (!tty->driver_data)
+			return -ENODEV;
+	}
+
+	card = (sdla_t*)tty->driver_data;
+
+	if (!card){
+		lock_adapter_irq(&card->wandev.lock,&smp_flags);	
+		card->tty=NULL;
+		unlock_adapter_irq(&card->wandev.lock,&smp_flags);
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "%s: Opening TTY Driver!\n",
+			card->devname);
+
+	if (card->tty_open == 0){
+		lock_adapter_irq(&card->wandev.lock,&smp_flags);	
+		card->tty=tty;
+		unlock_adapter_irq(&card->wandev.lock,&smp_flags);
+
+		if (!card->tty_buf){
+			card->tty_buf = kmalloc(TTY_CHDLC_MAX_MTU, GFP_KERNEL);
+			if (!card->tty_buf){
+				card->tty_buf=NULL;
+				card->tty=NULL;
+				return -ENOMEM;	
+			}
+		}
+
+		if (!card->tty_rx){
+			card->tty_rx = kmalloc(TTY_CHDLC_MAX_MTU, GFP_KERNEL);
+			if (!card->tty_rx){
+				/* Free the buffer above */
+				kfree(card->tty_buf);
+				card->tty_buf=NULL;
+				card->tty=NULL;
+				return -ENOMEM;	
+			}
+		}
+	}
+
+	++card->tty_open;
+	wanpipe_open(card);
+	return 0;
+}
+
+static int wanpipe_tty_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	unsigned long smp_flags=0;
+	sdla_t *card=NULL;
+
+	if (!tty){
+		dbg_printk(KERN_INFO "NO TTY in Write\n");
+		return -ENODEV;
+	}
+
+	card = (sdla_t *)tty->driver_data;
+			
+	if (!card){
+		dbg_printk(KERN_INFO "No Card in TTY Write\n");
+		return -ENODEV;
+	}	
+
+	if (count > card->wandev.mtu){
+		dbg_printk(KERN_INFO "Frame too big in Write %i Max: %i\n",
+				count,card->wandev.mtu);
+		return -EINVAL;
+	}
+	
+	if (card->wandev.state != WAN_CONNECTED){
+		dbg_printk(KERN_INFO "Card not connected in TTY Write\n");
+		return -EINVAL;
+	}
+
+	/* Lock the 508 Card: SMP is supported */
+      	if(card->hw.type != SDLA_S514){
+		s508_lock(card,&smp_flags);
+	} 
+	
+	if (test_and_set_bit(SEND_CRIT,(void*)&card->wandev.critical)){
+		printk(KERN_INFO "%s: Critical in TTY Write\n",
+				card->devname);
+		
+		/* Lock the 508 Card: SMP is supported */
+		if(card->hw.type != SDLA_S514)
+			s508_unlock(card,&smp_flags);
+		
+		return -EINVAL; 
+	}
+	
+	if (from_user) {
+		
+		unsigned char *tmp_buf;
+		
+		if ((tmp_buf=card->tty_buf)==NULL){
+			dbg_printk(KERN_INFO "No TTY BUF in Write\n");
+			
+			clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+			
+			if(card->hw.type != SDLA_S514)
+				s508_unlock(card,&smp_flags);
+			
+			return -ENOMEM;
+		}
+		
+		if (copy_from_user(tmp_buf,buf,count)){
+			dbg_printk(KERN_INFO "%s: Failed to copy from user!\n",
+					card->devname);
+			
+			clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+			
+			if(card->hw.type != SDLA_S514)
+				s508_unlock(card,&smp_flags);
+			
+			return -EINVAL;
+		}
+
+		if (chdlc_send(card,(void*)tmp_buf,count)){
+			dbg_printk(KERN_INFO "%s: Failed to send, retry later: user!\n",
+					card->devname);
+			
+			clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+			
+			wanpipe_tty_trigger_tx_irq(card);
+			
+			if(card->hw.type != SDLA_S514)
+				s508_unlock(card,&smp_flags);
+			return 0;
+		}
+
+	}else{
+	 	if (chdlc_send(card,(void*)buf,count)){
+			dbg_printk(KERN_INFO "%s: Failed to send, retry later: kernel!\n",
+					card->devname);
+			clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+	
+			wanpipe_tty_trigger_tx_irq(card);
+			
+			if(card->hw.type != SDLA_S514)
+				s508_unlock(card,&smp_flags);
+			return 0;
+		}
+	}
+	dbg_printk(KERN_INFO "%s: Packet sent OK: %i\n",card->devname,count);
+	clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+	
+	if(card->hw.type != SDLA_S514)
+		s508_unlock(card,&smp_flags);
+
+	return count;
+}
+
+static void wanpipe_tty_receive(sdla_t *card, unsigned addr, unsigned int len)
+{
+	unsigned offset=0;
+	unsigned olen=len;
+	char fp=0;
+	struct tty_struct *tty;
+	int i;
+	
+	if (!card->tty_open){
+		dbg_printk(KERN_INFO "%s: TTY not open during receive\n",
+				card->devname);
+		return;
+	}
+	
+	if ((tty=card->tty) == NULL){
+		dbg_printk(KERN_INFO "%s: No TTY on receive\n",
+				card->devname);
+		return;
+	}
+	
+	if (!tty->driver_data){
+		dbg_printk(KERN_INFO "%s: No Driver Data, or Flip on receive\n",
+				card->devname);
+		return;
+	}
+	
+
+	if (card->u.c.async_mode){
+		if ((tty->flip.count+len) >= TTY_FLIPBUF_SIZE){
+			if (net_ratelimit()){
+				printk(KERN_INFO 
+					"%s: Received packet size too big: %i bytes, Max: %i!\n",
+					card->devname,len,TTY_FLIPBUF_SIZE);
+			}
+			return;
+		}
+
+		
+		if((addr + len) > card->u.c.rx_top + 1) {
+			offset = card->u.c.rx_top - addr + 1;
+			
+			sdla_peek(&card->hw, addr, tty->flip.char_buf_ptr, offset);
+			
+			addr = card->u.c.rx_base;
+			len -= offset;
+			
+			tty->flip.char_buf_ptr+=offset;
+			tty->flip.count+=offset;
+			for (i=0;i<offset;i++){
+				*tty->flip.flag_buf_ptr = 0;
+				tty->flip.flag_buf_ptr++;
+			}
+		}
+		
+		sdla_peek(&card->hw, addr, tty->flip.char_buf_ptr, len);
+			
+		tty->flip.char_buf_ptr+=len;
+		card->tty->flip.count+=len;
+		for (i=0;i<len;i++){
+			*tty->flip.flag_buf_ptr = 0;
+			tty->flip.flag_buf_ptr++;
+		}
+
+		tty->low_latency=1;
+		tty_flip_buffer_push(tty);
+	}else{
+		if (!card->tty_rx){	
+			if (net_ratelimit()){
+				printk(KERN_INFO 
+				"%s: Receive sync buffer not available!\n",
+				 card->devname);
+			}
+			return;
+		}
+	
+		if (len > TTY_CHDLC_MAX_MTU){
+			if (net_ratelimit()){
+				printk(KERN_INFO 
+				"%s: Received packet size too big: %i bytes, Max: %i!\n",
+					card->devname,len,TTY_FLIPBUF_SIZE);
+			}
+			return;
+		}
+
+		
+		if((addr + len) > card->u.c.rx_top + 1) {
+			offset = card->u.c.rx_top - addr + 1;
+			
+			sdla_peek(&card->hw, addr, card->tty_rx, offset);
+			
+			addr = card->u.c.rx_base;
+			len -= offset;
+		}
+		sdla_peek(&card->hw, addr, card->tty_rx+offset, len);
+		if (tty->ldisc.receive_buf){
+			tty->ldisc.receive_buf(tty,card->tty_rx,&fp,olen);
+		}else{
+			if (net_ratelimit()){
+				printk(KERN_INFO 
+					"%s: NO TTY Sync line discipline!\n",
+					card->devname);
+			}
+		}
+	}
+
+	dbg_printk(KERN_INFO "%s: Received Data %i\n",card->devname,olen);
+	return;
+}
+
+#if 0
+static int wanpipe_tty_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+#endif
+
+static void wanpipe_tty_stop(struct tty_struct *tty)
+{
+	return;
+}
+
+static void wanpipe_tty_start(struct tty_struct *tty)
+{
+	return;
+}
+
+static int config_tty (sdla_t *card)
+{
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+
+	/* Setup the Board for asynchronous mode */
+	if (card->u.c.async_mode){
+		
+		if (set_asy_config(card)) {
+			printk (KERN_INFO "%s: Failed CHDLC Async configuration!\n",
+				card->devname);
+			return -EINVAL;
+		}
+	}else{
+		/* Setup the Board for CHDLC */
+		if (set_chdlc_config(card)) {
+			printk (KERN_INFO "%s: Failed CHDLC configuration!\n",
+				card->devname);
+			return -EINVAL;
+		}
+	}
+
+	/* Set interrupt mode and mask */
+        if (chdlc_set_intr_mode(card, APP_INT_ON_RX_FRAME |
+                		APP_INT_ON_GLOBAL_EXCEP_COND |
+                		APP_INT_ON_TX_FRAME |
+                		APP_INT_ON_CHDLC_EXCEP_COND | APP_INT_ON_TIMER)){
+		printk (KERN_INFO "%s: Failed to set interrupt triggers!\n",
+				card->devname);
+		return -EINVAL;	
+        }
+	
+
+	/* Mask the Transmit and Timer interrupt */
+	flags->interrupt_info_struct.interrupt_permission &= 
+		~(APP_INT_ON_TX_FRAME | APP_INT_ON_TIMER);
+
+	
+	/* Enable communications */
+ 	if (card->u.c.async_mode){
+		if (asy_comm_enable(card) != 0) {
+			printk(KERN_INFO "%s: Failed to enable async commnunication!\n",
+					card->devname);
+			flags->interrupt_info_struct.interrupt_permission = 0;
+			card->u.c.comm_enabled=0;
+			chdlc_set_intr_mode(card,0);
+			return -EINVAL;
+		}
+        }else{ 
+		if (chdlc_comm_enable(card) != 0) {
+			printk(KERN_INFO "%s: Failed to enable chdlc communications!\n",
+					card->devname);
+			flags->interrupt_info_struct.interrupt_permission = 0;
+			card->u.c.comm_enabled=0;
+			chdlc_set_intr_mode(card,0);
+			return -EINVAL;
+		}
+	}
+
+	/* Initialize Rx/Tx buffer control fields */
+	init_chdlc_tx_rx_buff(card);
+	port_set_state(card, WAN_CONNECTING);
+	return 0; 
+}
+
+
+static int change_speed(sdla_t *card, struct tty_struct *tty,
+			 struct termios *old_termios)
+{
+	int	baud, ret=0;
+	unsigned cflag; 
+	int	dbits,sbits,parity,handshaking;
+
+	cflag = tty->termios->c_cflag;
+
+	/* There is always one stop bit */
+	sbits=WANOPT_ONE;
+	
+	/* Parity is defaulted to NONE */
+	parity = WANOPT_NONE;
+
+	handshaking=0;
+	
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	      case CS5: dbits = 5; break;
+	      case CS6: dbits = 6; break;
+	      case CS7: dbits = 7; break;
+	      case CS8: dbits = 8; break;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  dbits = 8; break;
+	}
+	
+	/* One more stop bit should be supported, thus increment
+	 * the number of stop bits Max=2 */
+	if (cflag & CSTOPB) {
+		sbits = WANOPT_TWO;
+	}
+	if (cflag & PARENB) {
+		parity = WANOPT_EVEN;
+	}
+	if (cflag & PARODD){
+		parity = WANOPT_ODD;
+	}
+
+	/* Determine divisor based on baud rate */
+	baud = tty_get_baud_rate(tty);
+
+	if (!baud)
+		baud = 9600;	/* B0 transition handled in rs_set_termios */
+
+	if (cflag & CRTSCTS) {
+		handshaking|=ASY_RTS_HS_FOR_RX;
+	}
+	
+	if (I_IGNPAR(tty))
+		parity = WANOPT_NONE;
+
+	if (I_IXOFF(tty)){
+		handshaking|=ASY_XON_XOFF_HS_FOR_RX;
+		handshaking|=ASY_XON_XOFF_HS_FOR_TX;
+	}
+
+	if (I_IXON(tty)){
+		handshaking|=ASY_XON_XOFF_HS_FOR_RX;
+		handshaking|=ASY_XON_XOFF_HS_FOR_TX;
+	}
+
+	if (card->u.c.async_mode){
+		if (card->wandev.bps != baud)
+			ret=1;
+		card->wandev.bps = baud;
+	}
+
+	if (card->u.c.async_mode){
+		if (card->u.c.protocol_options != handshaking)
+			ret=1;
+		card->u.c.protocol_options = handshaking;
+
+		if (card->u.c.tx_bits_per_char != dbits)
+			ret=1;
+		card->u.c.tx_bits_per_char = dbits;
+
+		if (card->u.c.rx_bits_per_char != dbits)
+			ret=1;
+		card->u.c.rx_bits_per_char = dbits;
+		
+		if (card->u.c.stop_bits != sbits)
+			ret=1;
+		card->u.c.stop_bits = sbits;
+
+		if (card->u.c.parity != parity)
+			ret=1;
+		card->u.c.parity = parity;	
+
+		card->u.c.break_timer = 50;
+		card->u.c.inter_char_timer = 10;
+		card->u.c.rx_complete_length = 100;
+		card->u.c.xon_char = 0xFE;
+	}else{
+		card->u.c.protocol_options = HDLC_STREAMING_MODE;
+	}
+	
+	return ret;
+}
+
+	
+static void wanpipe_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	sdla_t *card;
+	int err=1;
+
+	if (!tty){
+		return;
+	}
+
+	card = (sdla_t *)tty->driver_data;
+			
+	if (!card)
+		return;
+
+	if (change_speed(card, tty, old_termios) || !card->u.c.comm_enabled){
+		unsigned long smp_flags;
+		
+		if (card->u.c.comm_enabled){
+			lock_adapter_irq(&card->wandev.lock,&smp_flags);
+			chdlc_disable_comm_shutdown(card);
+			unlock_adapter_irq(&card->wandev.lock,&smp_flags);
+		}
+		lock_adapter_irq(&card->wandev.lock,&smp_flags);
+		err = config_tty(card);
+		unlock_adapter_irq(&card->wandev.lock,&smp_flags);
+		if (card->u.c.async_mode){
+			printk(KERN_INFO "%s: TTY Async Configuration:\n"
+				 "   Baud        =%i\n"
+				 "   Handshaking =%s\n"
+				 "   Tx Dbits    =%i\n"
+				 "   Rx Dbits    =%i\n"
+				 "   Parity      =%s\n"
+				 "   Stop Bits   =%i\n",
+				 card->devname,
+				 card->wandev.bps,
+				 opt_decode[card->u.c.protocol_options],
+				 card->u.c.tx_bits_per_char,
+				 card->u.c.rx_bits_per_char,
+				 p_decode[card->u.c.parity] ,
+				 card->u.c.stop_bits);
+		}else{
+			printk(KERN_INFO "%s: TTY Sync Configuration:\n"
+				 "   Baud        =%i\n"
+				 "   Protocol    =HDLC_STREAMING\n",
+				 card->devname,card->wandev.bps);
+		}
+		if (!err){
+			port_set_state(card,WAN_CONNECTED);
+		}else{
+			port_set_state(card,WAN_DISCONNECTED);
+		}
+	}
+	return;
+}
+
+static void wanpipe_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	sdla_t *card;
+	unsigned long smp_flags=0;
+
+	if (!tty){
+		return;
+	}
+	
+	card = (sdla_t *)tty->driver_data;
+			
+	if (!card)
+		return;
+
+	if (card->wandev.state != WAN_CONNECTED)
+		return;
+
+	if(card->hw.type != SDLA_S514)
+		s508_lock(card,&smp_flags);
+	
+	if (test_and_set_bit(SEND_CRIT,(void*)&card->wandev.critical)){
+		
+		wanpipe_tty_trigger_tx_irq(card);
+
+		if(card->hw.type != SDLA_S514)
+			s508_unlock(card,&smp_flags);
+		return;
+	}
+
+	if (chdlc_send(card,(void*)&ch,1)){
+		wanpipe_tty_trigger_tx_irq(card);
+		dbg_printk("%s: Failed to TX char!\n",card->devname);
+	}
+	
+	dbg_printk("%s: Char TX OK\n",card->devname);
+	
+	clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+	
+	if(card->hw.type != SDLA_S514)
+		s508_unlock(card,&smp_flags);
+	
+	return;
+}
+
+static void wanpipe_tty_flush_chars(struct tty_struct *tty)
+{
+	return;
+}
+
+static void wanpipe_tty_flush_buffer(struct tty_struct *tty)
+{
+	if (!tty)
+		return;
+	
+	wake_up_interruptible(&tty->write_wait);
+#if defined(SERIAL_HAVE_POLL_WAIT) || \
+         (defined LINUX_2_1 && LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15))
+	wake_up_interruptible(&tty->poll_wait);
+#endif
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+
+	return;
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void wanpipe_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+	return;
+}
+
+
+static int wanpipe_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	return 0;
+}
+
+
+static int wanpipe_tty_write_room(struct tty_struct *tty)
+{
+	sdla_t *card;
+
+	printk(KERN_INFO "TTY Write Room\n");
+	
+	if (!tty){
+		return 0;
+	}
+
+	card = (sdla_t *)tty->driver_data;
+	if (!card)
+		return 0;
+
+	if (card->wandev.state != WAN_CONNECTED)
+		return 0;
+	
+	return SEC_MAX_NO_DATA_BYTES_IN_FRAME;
+}
+
+
+static int set_modem_status(sdla_t *card, unsigned char data)
+{
+	CHDLC_MAILBOX_STRUCT *mb = card->mbox;
+	int err;
+
+	mb->buffer_length=1;
+	mb->command=SET_MODEM_STATUS;
+	mb->data[0]=data;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+	if (err != COMMAND_OK) 
+		chdlc_error (card, err, mb);
+	
+	return err;
+}
+
+static void wanpipe_tty_hangup(struct tty_struct *tty)
+{
+	sdla_t *card;
+	unsigned long smp_flags;
+
+	printk(KERN_INFO "TTY Hangup!\n");
+	
+	if (!tty){
+		return;
+	}
+
+	card = (sdla_t *)tty->driver_data;
+	if (!card)
+		return;
+
+	lock_adapter_irq(&card->wandev.lock,&smp_flags);
+	set_modem_status(card,0);
+	unlock_adapter_irq(&card->wandev.lock,&smp_flags);
+	return;
+}
+
+static void wanpipe_tty_break(struct tty_struct *tty, int break_state)
+{
+	return;
+}
+
+static void wanpipe_tty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	return;
+}
+
+static void wanpipe_tty_throttle(struct tty_struct * tty)
+{
+	return;
+}
+
+static void wanpipe_tty_unthrottle(struct tty_struct * tty)
+{
+	return;
+}
+
+int wanpipe_tty_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	return 0;
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int wanpipe_tty_init(sdla_t *card)
+{
+	struct serial_state * state;
+	
+	/* Initialize the tty_driver structure */
+
+	if (card->tty_minor < 0 || card->tty_minor > NR_PORTS){
+		printk(KERN_INFO "%s: Illegal Minor TTY number (0-4): %i\n",
+				card->devname,card->tty_minor);
+		return -EINVAL;
+	}
+
+	if (WAN_CARD(card->tty_minor)){
+		printk(KERN_INFO "%s: TTY Minor %i, already in use\n",
+				card->devname,card->tty_minor);
+		return -EBUSY;
+	}
+
+	if (tty_init_cnt==0){
+		
+		printk(KERN_INFO "%s: TTY %s Driver Init: Major %i, Minor Range %i-%i\n",
+				card->devname,
+				card->u.c.async_mode ? "ASYNC" : "SYNC",
+				WAN_TTY_MAJOR,MIN_PORT,MAX_PORT);
+		
+		tty_driver_mode = card->u.c.async_mode;
+		
+		memset(&serial_driver, 0, sizeof(struct tty_driver));
+		serial_driver.magic = TTY_DRIVER_MAGIC;
+		serial_driver.driver_name = "wanpipe_tty"; 
+		serial_driver.name = "ttyW";
+		serial_driver.major = WAN_TTY_MAJOR;
+		serial_driver.minor_start = WAN_TTY_MINOR;
+		serial_driver.num = NR_PORTS; 
+		serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+		serial_driver.subtype = SERIAL_TYPE_NORMAL;
+		
+		serial_driver.init_termios = tty_std_termios;
+		serial_driver.init_termios.c_cflag =
+			B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+		serial_driver.flags = TTY_DRIVER_REAL_RAW;
+		
+		serial_driver.refcount = &serial_refcount;
+		serial_driver.table = serial_table;
+		serial_driver.termios = serial_termios;
+		serial_driver.termios_locked = serial_termios_locked;
+
+		serial_driver.open = wanpipe_tty_open;
+		serial_driver.close = wanpipe_tty_close;
+		serial_driver.write = wanpipe_tty_write;
+		
+		serial_driver.put_char = wanpipe_tty_put_char;
+		serial_driver.flush_chars = wanpipe_tty_flush_chars;
+		serial_driver.write_room = wanpipe_tty_write_room;
+		serial_driver.chars_in_buffer = wanpipe_tty_chars_in_buffer;
+		serial_driver.flush_buffer = wanpipe_tty_flush_buffer;
+		//serial_driver.ioctl = wanpipe_tty_ioctl;
+		serial_driver.throttle = wanpipe_tty_throttle;
+		serial_driver.unthrottle = wanpipe_tty_unthrottle;
+		serial_driver.send_xchar = wanpipe_tty_send_xchar;
+		serial_driver.set_termios = wanpipe_tty_set_termios;
+		serial_driver.stop = wanpipe_tty_stop;
+		serial_driver.start = wanpipe_tty_start;
+		serial_driver.hangup = wanpipe_tty_hangup;
+		serial_driver.break_ctl = wanpipe_tty_break;
+		serial_driver.wait_until_sent = wanpipe_tty_wait_until_sent;
+		serial_driver.read_proc = wanpipe_tty_read_proc;
+		
+		/*
+		 * The callout device is just like normal device except for
+		 * major number and the subtype code.
+		 */
+		callout_driver = serial_driver;
+		callout_driver.name = "cuw";
+		callout_driver.major = TTYAUX_MAJOR;
+		callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+		callout_driver.read_proc = 0;
+		callout_driver.proc_entry = 0;
+
+		if (tty_register_driver(&serial_driver)){
+			printk(KERN_INFO "%s: Failed to register serial driver!\n",
+					card->devname);
+		}
+
+		if (tty_register_driver(&callout_driver)){
+			printk(KERN_INFO "%s: Failed to register callout driver!\n",
+					card->devname);
+		}
+
+	}
+
+
+	/* The subsequent ports must comply to the initial configuration */
+	if (tty_driver_mode != card->u.c.async_mode){
+		printk(KERN_INFO "%s: Error: TTY Driver operation mode mismatch!\n",
+				card->devname);
+		printk(KERN_INFO "%s: The TTY driver is configured for %s!\n",
+				card->devname, tty_driver_mode ? "ASYNC" : "SYNC");
+		return -EINVAL;
+	}
+	
+	tty_init_cnt++;
+	
+	printk(KERN_INFO "%s: Initializing TTY %s Driver Minor %i\n",
+			card->devname,
+			tty_driver_mode ? "ASYNC" : "SYNC",
+			card->tty_minor);
+	
+	tty_card_map[card->tty_minor] = card;
+	state = &rs_table[card->tty_minor];
+	
+	state->magic = SSTATE_MAGIC;
+	state->line = 0;
+	state->type = PORT_UNKNOWN;
+	state->custom_divisor = 0;
+	state->close_delay = 5*HZ/10;
+	state->closing_wait = 30*HZ;
+	state->callout_termios = callout_driver.init_termios;
+	state->normal_termios = serial_driver.init_termios;
+	state->icount.cts = state->icount.dsr = 
+		state->icount.rng = state->icount.dcd = 0;
+	state->icount.rx = state->icount.tx = 0;
+	state->icount.frame = state->icount.parity = 0;
+	state->icount.overrun = state->icount.brk = 0;
+	state->irq = card->wandev.irq; 
+
+	card->tty_task_queue.routine = tty_poll_task;
+	card->tty_task_queue.data = (void*)card;
+	return 0;
+}
+
+#endif
+
 
 /****** End ****************************************************************/
diff -urN linux-2.4.4-pre2/drivers/net/wan/sdla_fr.c linux/drivers/net/wan/sdla_fr.c
--- linux-2.4.4-pre2/drivers/net/wan/sdla_fr.c	Tue Mar  6 19:44:36 2001
+++ linux/drivers/net/wan/sdla_fr.c	Mon Dec  3 22:34:50 2001
@@ -4,14 +4,43 @@
 * Author(s):	Nenad Corbic  <ncorbic@sangoma.com>
 *		Gideon Hack
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2001 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
-* Feb 28, 2000  Jeff Garzik	o softnet updates
+* Nov 23, 2000  Nenad Corbic    o Added support for 2.4.X kernels
+* Nov 15, 2000  David Rokavarg  
+*               Nenad Corbic	o Added frame relay bridging support.
+* 				  Original code from Mark Wells and Kristian Hoffmann has
+* 				  been integrated into the frame relay driver.
+* Nov 13, 2000  Nenad Corbic    o Added true interface type encoding option.
+* 				  Tcpdump doesn't support Frame Relay inteface
+* 				  types, to fix this true type option will set
+* 				  the interface type to RAW IP mode.
+* Nov 07, 2000  Nenad Corbic	o Added security features for UDP debugging:
+*                                 Deny all and specify allowed requests.
+* Nov 06, 2000  Nenad Corbic	o Wanpipe interfaces conform to raw packet interfaces.  
+*                                 Moved the if_header into the if_send() routine.
+*                                 The if_header() was breaking the libpcap 
+*                                 support. i.e. support for tcpdump, ethereal ...
+* Oct 12. 2000  Nenad Corbic    o Added error message in fr_configure
+* Jul 31, 2000  Nenad Corbic	o Fixed the Router UP Time.
+* Apr 28, 2000  Nenad Corbic	o Added the option to shutdown an interface
+*                                 when the channel gets disconnected.
+* Apr 28, 2000  Nenad Corbic 	o Added M.Grants patch: disallow duplicate
+*                                 interface setups. 
+* Apr 25, 2000  Nenad Corbic	o Added M.Grants patch: dynamically add/remove 
+*                                 new dlcis/interfaces.
+* Mar 23, 2000  Nenad Corbic 	o Improved task queue, bh handling.
+* Mar 16, 2000	Nenad Corbic	o Added Inverse ARP support
+* Mar 13, 2000  Nenad Corbic	o Added new socket API support.
+* Mar 06, 2000  Nenad Corbic	o Bug Fix: corrupted mbox recovery.
+* Feb 24, 2000  Nenad Corbic    o Fixed up FT1 UDP debugging problem.
+* Dev 15, 1999  Nenad Corbic    o Fixed up header files for 2.0.X kernels
+*
 * Nov 08, 1999  Nenad Corbic    o Combined all debug UDP calls into one function
 *                               o Removed the ARP support. This has to be done
 *                                 in the next version.
@@ -109,12 +138,12 @@
 * Jan 02, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
 
-#include <linux/config.h>
+#include <linux/version.h>
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <linux/if_arp.h>	/* ARPHRD_* defines */
@@ -123,14 +152,29 @@
 #include <linux/time.h>	 	/* for do_gettimeofday */	
 #include <linux/in.h>		/* sockaddr_in */
 #include <linux/inet.h>		/* in_ntoa(), etc... */
-#include <asm/uaccess.h>
-#include <linux/inetdevice.h>
+#include <asm/errno.h>
+
 #include <linux/ip.h>
-#include <net/route.h>          /* Dynamic Route Creation */
 #include <linux/if.h>
 
-#include <linux/sdla_fr.h>	/* frame relay firmware API definitions */
- 
+#include <linux/if_wanpipe_common.h>	/* Wanpipe Socket */
+#include <linux/if_wanpipe.h>	
+
+#include <linux/sdla_fr.h>		/* frame relay firmware API definitions */
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ #include <asm/uaccess.h>
+ #include <linux/inetdevice.h>
+ #include <linux/netdevice.h>
+
+#else
+ #include <asm/segment.h>
+#endif
+
+#include <net/route.h>          	/* Dynamic Route Creation */
+#include <linux/etherdevice.h>		/* eth_type_trans() used for bridging */
+#include <linux/random.h>
+
 /****** Defines & Macros ****************************************************/
 
 #define	MAX_CMD_RETRY	10		/* max number of firmware retries */
@@ -151,25 +195,20 @@
 #define CIR_ENABLED	0x00
 #define CIR_DISABLED	0x01
 
-#define WANPIPE 0x00
-#define API     0x01
 #define FRAME_RELAY_API 1
-
-#define TX_TIMEOUT	(5*HZ)
+#define MAX_BH_BUFF	10
 
 /* For handle_IPXWAN() */
 #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
  
 /****** Data Structures *****************************************************/
 
-/* This is an extention of the 'struct net_device' we create for each network
+/* This is an extention of the 'struct device' we create for each network
  * interface to keep the rest of channel-specific data.
  */
 typedef struct fr_channel
 {
-	/* This member must be first. */
-	struct net_device *slave;	/* WAN slave */
-
+	wanpipe_common_t common;
 	char name[WAN_IFNAME_SZ+1];	/* interface name, ASCIIZ */
 	unsigned dlci_configured  ;	/* check whether configured or not */
 	unsigned cir_status;		/* check whether CIR enabled or not */
@@ -183,7 +222,6 @@
 	unsigned long router_start_time;/* Router start time in seconds */
 	unsigned long tick_counter;	/* counter for transmit time out */
 	char dev_pending_devtint;	/* interface pending dev_tint() */
-	char state;			/* channel state */
 	void *dlci_int_interface;	/* pointer to the DLCI Interface */ 
 	unsigned long IB_addr;		/* physical address of Interface Byte */
 	unsigned long state_tick;	/* time of the last state change */
@@ -192,19 +230,52 @@
 	sdla_t *card;			/* -> owner */
 	unsigned route_flag;		/* Add/Rem dest addr in route tables */
 	unsigned inarp;			/* Inverse Arp Request status */ 
+	unsigned char inarp_ready;	/* Ready to send requests */
 	int inarp_interval;		/* Time between InArp Requests */
 	unsigned long inarp_tick;	/* InArp jiffies tick counter */
+	unsigned char interface_down;	/* Bring interface down on disconnect */
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct net_device_stats ifstats;	/* interface statistics */
+      #else
+	struct enet_statistics ifstats;
+      #endif	
 	if_send_stat_t drvstats_if_send;
         rx_intr_stat_t drvstats_rx_intr;
         pipe_mgmt_stat_t drvstats_gen;
-
-	unsigned char usedby;  /* Used by WANPIPE or API */
-
 	unsigned long router_up_time;
 
 	unsigned short transmit_length;
-	char transmit_buffer[FR_MAX_NO_DATA_BYTES_IN_FRAME];
+	struct sk_buff *delay_skb;
+
+
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	bh_data_t *bh_head;	  	  /* Circular buffer for chdlc_bh */
+	unsigned long  tq_working;
+	volatile int  bh_write;
+	volatile int  bh_read;
+	atomic_t  bh_buff_used;
+      #endif
+
+	/* Polling task queue. Each interface
+         * has its own task queue, which is used
+         * to defer events from the interrupt */
+	struct tq_struct fr_poll_task;
+	struct timer_list fr_arp_timer;
+
+	u32 ip_local;
+	u32 ip_remote;
+	u8  config_dlci;
+	u32 unconfig_dlci;
+
+	/* Whether this interface should be setup as a gateway.
+	 * Used by dynamic route setup code */
+	u8  gateway;
+
+	/* True interface type */
+	u8 true_if_encoding;
+	u8 fr_header[FR_HEADER_LEN];
+	char fr_header_len;
+
 } fr_channel_t;
 
 /* Route Flag options */
@@ -212,6 +283,7 @@
 #define ADD_ROUTE 	0x01
 #define ROUTE_ADDED	0x02
 #define REMOVE_ROUTE 	0x03
+#define ARP_REQ		0x04
 
 /* inarp options */
 #define INARP_NONE		0x00
@@ -221,6 +293,10 @@
 /* reasons for enabling the timer interrupt on the adapter */
 #define TMR_INT_ENABLED_UDP   	0x01
 #define TMR_INT_ENABLED_UPDATE 	0x02
+#define TMR_INT_ENABLED_ARP	0x04
+#define TMR_INT_ENABLED_UPDATE_STATE 	0x08
+#define TMR_INT_ENABLED_CONFIG	0x10
+#define TMR_INT_ENABLED_UNCONFIG	0x20
 
 
 typedef struct dlci_status
@@ -248,7 +324,6 @@
 /* variable for keeping track of enabling/disabling FT1 monitor status */
 static int rCount = 0;
 
-extern int ip_rt_ioctl(unsigned int, void *);
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
@@ -256,27 +331,43 @@
  * interrupt test routine 
  */
 static int Intr_test_counter;
+
 /****** Function Prototypes *************************************************/
 
 /* WAN link driver entry points. These are called by the WAN router module. */
 static int update(wan_device_t *wandev);
-static int new_if(wan_device_t *wandev, struct net_device *dev, wanif_conf_t *conf);
-static int del_if(wan_device_t *wandev, struct net_device *dev);
+static int new_if(wan_device_t *wandev, netdevice_t *dev, wanif_conf_t *conf);
+static int del_if(wan_device_t *wandev, netdevice_t *dev);
+static void disable_comm (sdla_t *card);
 
 /* WANPIPE-specific entry points */
 static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data);
 
 /* Network device interface */
-static int if_init(struct net_device *dev);
-static int if_open(struct net_device *dev);
-static int if_close(struct net_device *dev);
-static int if_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);
-static int if_rebuild_hdr(struct sk_buff *skb);
-static int if_send(struct sk_buff *skb, struct net_device *dev);
-static void if_tx_timeout (struct net_device *dev);
-static int chk_bcast_mcast_addr(sdla_t *card, struct net_device* dev,
+static int if_init(netdevice_t *dev);
+static int if_open(netdevice_t *dev);
+static int if_close(netdevice_t *dev);
+
+
+#ifdef LINUX_2_4
+static void if_tx_timeout (netdevice_t *dev);
+#endif
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static int if_rebuild_hdr (struct sk_buff *skb);
+#else
+static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
+        struct sk_buff* skb);
+#endif
+
+static int if_send(struct sk_buff *skb, netdevice_t *dev);
+static int chk_bcast_mcast_addr(sdla_t *card, netdevice_t* dev,
                                 struct sk_buff *skb);
-static struct net_device_stats *if_stats(struct net_device *dev);
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats *if_stats(netdevice_t *dev);
+#else
+static struct enet_statistics* if_stats (netdevice_t* dev);
+#endif
 
 /* Interrupt handlers */
 static void fr_isr(sdla_t *card);
@@ -292,7 +383,7 @@
 static int fr_init_dlci (sdla_t *card, fr_channel_t *chan);
 static int fr_set_intr_mode (sdla_t *card, unsigned mode, unsigned mtu, unsigned short timeout);
 static int fr_comm_enable(sdla_t *card);
-static int fr_comm_disable(sdla_t *card);
+static void fr_comm_disable(sdla_t *card);
 static int fr_get_err_stats(sdla_t *card);
 static int fr_get_stats(sdla_t *card);
 static int fr_add_dlci(sdla_t *card, int dlci);
@@ -301,6 +392,13 @@
 static int fr_issue_isf(sdla_t *card, int isf);
 static int fr_send(sdla_t *card, int dlci, unsigned char attr, int len,
 	void *buf);
+static int fr_send_data_header(sdla_t *card, int dlci, unsigned char attr, int len,
+	void *buf,unsigned char hdr_len);
+static unsigned int fr_send_hdr(sdla_t *card, int dlci, unsigned int offset);
+
+static int check_dlci_config (sdla_t *card, fr_channel_t *chan);
+static void initialize_rx_tx_buffers (sdla_t *card);
+
 
 /* Firmware asynchronous event handlers */
 static int fr_event(sdla_t *card, int event, fr_mbox_t *mbox);
@@ -308,9 +406,9 @@
 static int fr_dlci_change(sdla_t *card, fr_mbox_t *mbox);
 
 /* Miscellaneous functions */
-static int update_chan_state(struct net_device *dev);
-static void set_chan_state(struct net_device *dev, int state);
-static struct net_device *find_channel(sdla_t *card, unsigned dlci);
+static int update_chan_state(netdevice_t *dev);
+static void set_chan_state(netdevice_t *dev, int state);
+static netdevice_t *find_channel(sdla_t *card, unsigned dlci);
 static int is_tx_ready(sdla_t *card, fr_channel_t *chan);
 static unsigned int dec_to_uint(unsigned char *str, int len);
 static int reply_udp( unsigned char *data, unsigned int mbox_len );
@@ -319,14 +417,38 @@
 static void init_chan_statistics( fr_channel_t* chan );
 static void init_global_statistics( sdla_t* card );
 static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan );
-static void setup_for_delayed_transmit(struct net_device* dev, void* buf,
-	unsigned len);
+static int setup_for_delayed_transmit(netdevice_t* dev, struct sk_buff *skb);
+
+netdevice_t * move_dev_to_next (sdla_t *, netdevice_t *);
+static int check_tx_status(sdla_t *, netdevice_t *);
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+/* Frame Relay Socket API */
+static void trigger_fr_bh (fr_channel_t *);
+static void fr_bh (netdevice_t *);
+static int fr_bh_cleanup (netdevice_t *);
+static int bh_enqueue (netdevice_t *, struct sk_buff *);
+#endif
+
+static void trigger_fr_poll (netdevice_t *);
+static void fr_poll (netdevice_t *);
+//static void add_gateway (netdevice_t *);
+
+static void trigger_unconfig_fr (netdevice_t *dev);
+static void unconfig_fr (sdla_t *);
+
+static void trigger_config_fr (sdla_t *);
+static void config_fr (sdla_t *);
 
 
 /* Inverse ARP and Dynamic routing functions */
-int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, struct net_device *dev);
+int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, netdevice_t *dev);
 int is_arp(void *buf);
-int send_inarp_request(sdla_t *card, struct net_device *dev);
+int send_inarp_request(sdla_t *card, netdevice_t *dev);
+
+static void trigger_fr_arp (netdevice_t *);
+static void fr_arp (unsigned long data);
+
 
 /* Udp management functions */
 static int process_udp_mgmt_pkt(sdla_t *card);
@@ -346,6 +468,7 @@
 void 	s508_s514_lock(sdla_t *card, unsigned long *smp_flags);
 
 unsigned short calc_checksum (char *, int);
+static int setup_fr_header(struct sk_buff** skb, netdevice_t* dev, char op_mode);
 
 
 /****** Public Functions ****************************************************/
@@ -366,15 +489,20 @@
 {
 
 	int err;
+	fr508_flags_t* flags;
 
 	union
 	{
 		char str[80];
 		fr_conf_t cfg;
 	} u;
+
 	fr_buf_info_t* buf_info;
 	int i;
 
+
+	printk(KERN_INFO "\n");
+
 	/* Verify configuration ID */
 	if (conf->config_id != WANCONFIG_FR) {
 		
@@ -403,6 +531,8 @@
 			return -EINVAL;
 	}
 
+	flags = card->flags;
+
 	/* Read firmware version.  Note that when adapter initializes, it
 	 * clears the mailbox, so it may appear that the first command was
 	 * executed successfully when in fact it was merely erased. To work
@@ -429,7 +559,7 @@
 	memset(card->u.f.dlci_to_dev_map, 0, sizeof(card->u.f.dlci_to_dev_map));
  	
 	/* Configure adapter firmware */
-	
+
 	u.cfg.mtu	= conf->mtu;
 	u.cfg.kbps	= conf->bps / 1000;
 
@@ -546,9 +676,7 @@
                         (void*)(buf_info->rse_base +
                         (buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) +
                         card->hw.dpmbase);
-	}
-
-	else {	
+	}else{	
 		buf_info = (void*)(card->hw.dpmbase + FR508_RXBC_OFFS);
 
 		card->rxmb = (void*)(buf_info->rse_next -
@@ -582,6 +710,8 @@
 	card->wandev.state	= WAN_DISCONNECTED;
 	card->wandev.ttl	= conf->ttl;
         card->wandev.udp_port 	= conf->udp_port;       
+	card->disable_comm	= &disable_comm;	
+	card->u.f.arp_dev 	= NULL;
 
 	/* Intialize global statistics for a card */
 	init_global_statistics( card );
@@ -593,11 +723,13 @@
 	card->intr_mode = INTR_TEST_MODE;
 	err = intr_test( card );
 
+	printk(KERN_INFO "%s: End of Interrupt Test rc=0x%x  count=%i\n",
+			card->devname,err,Intr_test_counter); 
+	
 	if (err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) {
-		printk( 
-			"%s: Interrupt Test Failed, Counter: %i\n", 
+		printk(KERN_ERR "%s: Interrupt Test Failed, Counter: %i\n", 
 			card->devname, Intr_test_counter);
-		printk( "Please choose another interrupt\n");
+		printk(KERN_ERR "Please choose another interrupt\n");
 		err = -EIO;
 		return err;
 	}
@@ -606,6 +738,32 @@
 			card->devname, Intr_test_counter);
 
 
+	/* Apr 28 2000. Nenad Corbic
+	 * Enable commnunications here, not in if_open or new_if, since
+         * interfaces come down when the link is disconnected. 
+         */
+	 
+	/* If you enable comms and then set ints, you get a Tx int as you
+	 * perform the SET_INT_TRIGGERS command. So, we only set int
+	 * triggers and then adjust the interrupt mask (to disable Tx ints)
+	 * before enabling comms. 
+	 */	
+        if (fr_set_intr_mode(card, (FR_INTR_RXRDY | FR_INTR_TXRDY |
+		FR_INTR_DLC | FR_INTR_TIMER | FR_INTR_TX_MULT_DLCIs) ,
+		card->wandev.mtu, 0)) {
+		return -EIO;
+	}
+
+	flags->imask &= ~(FR_INTR_TXRDY | FR_INTR_TIMER);
+ 
+	if (fr_comm_enable(card)) {
+		return -EIO;
+	}	
+	wanpipe_set_state(card, WAN_CONNECTED);
+	spin_lock_init(&card->u.f.if_send_lock);
+	
+	printk(KERN_INFO "\n");
+
         return 0;
 }
 
@@ -627,9 +785,6 @@
 	if (wandev->state == WAN_UNCONFIGURED)
 		return -ENODEV;
 
-	if (test_bit(1, (void*)&wandev->critical))
-		return -EAGAIN;
-
 	card = wandev->private;
 	flags = card->flags;
 
@@ -646,6 +801,7 @@
  			return -EAGAIN;
 		}
         }
+
 	return 0;
 }
 
@@ -661,16 +817,17 @@
  * Return:	0	o.k.
  *		< 0	failure (channel will not be created)
  */
-static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* conf)
+static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf)
 {
 	sdla_t* card = wandev->private;
 	fr_channel_t* chan;
 	int dlci = 0;
 	int err = 0;
+
 	
 	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
 		
-		printk(KERN_INFO "%s: invalid interface name!\n",
+		printk(KERN_INFO "%s: Invalid interface name!\n",
 			card->devname);
 		return -EINVAL;
 	}
@@ -684,7 +841,7 @@
 	memset(chan, 0, sizeof(fr_channel_t));
 	strcpy(chan->name, conf->name);
 	chan->card = card;
- 
+
 	/* verify media address */
 	if (is_digit(conf->addr[0])) {
 
@@ -697,38 +854,92 @@
 		} else {
 		
 			printk(KERN_ERR
-				"%s: invalid DLCI %u on interface %s!\n",
+				"%s: Invalid DLCI %u on interface %s!\n",
 				wandev->name, dlci, chan->name);
 			err = -EINVAL;
 		}
 
 	} else {
 		printk(KERN_ERR
-			"%s: invalid media address on interface %s!\n",
+			"%s: Invalid media address on interface %s!\n",
 			wandev->name, chan->name);
 		err = -EINVAL;
 	}
 
-        /* Setup wanpipe as a router (WANPIPE) or as an API */
-        if(strcmp(conf->usedby, "WANPIPE") == 0){
-                printk(KERN_INFO "%s: Running in WANPIPE mode %s\n",
-			wandev->name, chan->name);
-                chan->usedby = WANPIPE;
+	if ((chan->true_if_encoding = conf->true_if_encoding) == WANOPT_YES){
+		printk(KERN_INFO 
+			"%s: Enabling, true interface type encoding.\n",
+			card->devname);
+	}
+	
+
+
+    /* Setup wanpipe as a router (WANPIPE) even if it is
+	 * a bridged DLCI, or as an API 
+	 */
+        if (strcmp(conf->usedby, "WANPIPE")  == 0  || 
+	    strcmp(conf->usedby, "BRIDGE")   == 0  ||
+	    strcmp(conf->usedby, "BRIDGE_N") == 0){
+		
+		if(strcmp(conf->usedby, "WANPIPE") == 0){
+			chan->common.usedby = WANPIPE;
+			
+	                printk(KERN_INFO "%s: Running in WANPIPE mode.\n", 
+					card->devname);
+			
+		}else if(strcmp(conf->usedby, "BRIDGE") == 0){
+			
+			chan->common.usedby = BRIDGE;
+			
+                      #if defined(LINUX_2_1) || defined(LINUX_2_4) 
+			printk(KERN_INFO "%s: Running in WANPIPE (BRIDGE) mode.\n", 
+					card->devname);
+                      #else
+			printk(KERN_INFO "%s: WANPIPE Bridging mode not supported in 2.0.X kernels.\n",
+					card->devname);
+			err = -EPROTONOSUPPORT;
+                      #endif
+			
+		}else if( strcmp(conf->usedby, "BRIDGE_N") == 0 ){
+			
+			chan->common.usedby = BRIDGE_NODE;
+			
+                      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+			printk(KERN_INFO "%s: Running in WANPIPE (BRIDGE_NODE) mode.\n", 
+					card->devname);
+                      #else
+			printk(KERN_INFO "%s: WANPIPE Bridging mode not supported in 2.0.X kernels.\n",
+					card->devname);
+			err = -EPROTONOSUPPORT;
+                      #endif
+		}
+
+		if (!err){
+			/* Dynamic interface configuration option.
+			 * On disconnect, if the options is selected,
+			 * the interface will be brought down */
+			if (conf->if_down == WANOPT_YES){ 
+				set_bit(DYN_OPT_ON,&chan->interface_down);
+				printk(KERN_INFO 
+				    "%s: Dynamic interface configuration enabled.\n",
+					card->devname);
+			}
+		}
 
         } else if(strcmp(conf->usedby, "API") == 0){
 
-#ifdef FRAME_RELAY_API
-                chan->usedby = API;
-                printk(KERN_INFO "%s: Running in API mode %s\n",
-			wandev->name, chan->name);
-#else
-                printk(KERN_INFO "%s: API Mode is not supported !\n",
+              #if defined(LINUX_2_1) || defined(LINUX_2_4) 
+                chan->common.usedby = API;
+                printk(KERN_INFO "%s: Running in API mode.\n",
 			wandev->name);
-                printk(KERN_INFO
-			"%s: API patch can be obtained from Sangoma Tech.\n",
-                        	wandev->name);
+              #else
+                printk(KERN_INFO "%s: The API Mode is not supported for"
+				 "kernels lower than 2.2.X !\n",
+					wandev->name);
+		printk(KERN_INFO "%s: Please upgrade to a 2.2.X kernel for the API support\n",
+					wandev->name);
                 err = -EINVAL;
-#endif
+              #endif
         }
 
 	if (err) {
@@ -737,8 +948,6 @@
 		return err;
 	}
 
-	card->u.f.dlci_to_dev_map[dlci] = dev;
- 
 	/* place cir,be,bc and other channel specific information into the
 	 * chan structure 
          */
@@ -774,20 +983,24 @@
 
 	chan->mc = conf->mc;
 
-	/* FIXME: ARP is not supported by this frame relay verson */
 	if (conf->inarp == WANOPT_YES){
-		printk(KERN_INFO "%s: ERROR - This version of WANPIPE doesn't support ARPs\n",
+	      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+		printk(KERN_INFO "%s: Inverse ARP Support Enabled\n",card->devname);
+		chan->inarp = conf->inarp ? INARP_REQUEST : INARP_NONE;
+		chan->inarp_interval = conf->inarp_interval ? conf->inarp_interval : 10;
+	      #else
+		printk(KERN_INFO "%s: Warning, Inverse ARP Support not available for 2.0.X kernels!\n",
 				card->devname);
-	
-		//chan->inarp = conf->inarp ? INARP_REQUEST : INARP_NONE;
-		//chan->inarp_interval = conf->inarp_interval ? conf->inarp_interval : 10;
-		kfree(chan);
-		return -EINVAL;
+		chan->inarp = INARP_NONE;
+		chan->inarp_interval = 10;
+              #endif
 	}else{
+		printk(KERN_INFO "%s: Inverse ARP Support Disabled\n",card->devname);
 		chan->inarp = INARP_NONE;
 		chan->inarp_interval = 10;
 	}
 
+
 	chan->dlci_configured = DLCI_NOT_CONFIGURED;	
 
 
@@ -814,64 +1027,102 @@
 	chan->transmit_length = 0;
 
 	/* prepare network device data space for registration */
-	strcpy(dev->name, chan->name);
+      #ifdef LINUX_2_4
+	strcpy(dev->name,chan->name);
+      #else
+	dev->name = (char *)kmalloc(strlen(chan->name) + 2, GFP_KERNEL); 
+	sprintf(dev->name, "%s", chan->name);
+      #endif
+	
 	dev->init = &if_init;
 	dev->priv = chan;
 
-
-	/* Enable Interrupts and Communications */
-	if (!wandev->new_if_cnt){
-		fr508_flags_t* flags = card->flags;
-
-		wandev->new_if_cnt++;
-
-		/* 
-		If you enable comms and then set ints, you get a Tx int as you
-		perform the SET_INT_TRIGGERS command. So, we only set int
-		triggers and then adjust the interrupt mask (to disable Tx ints)
-		before enabling comms. 
-		*/	
-                if (fr_set_intr_mode(card, (FR_INTR_RXRDY | FR_INTR_TXRDY |
-			FR_INTR_DLC | FR_INTR_TIMER | FR_INTR_TX_MULT_DLCIs) ,
-			card->wandev.mtu, 0)) {
-			kfree(chan);
-			return -EIO;
-		}
-
-		flags->imask &= ~(FR_INTR_TXRDY | FR_INTR_TIMER);
- 
-		if (fr_comm_enable(card)) {
-			kfree(chan);
-			return -EIO;
-		}	
-		wanpipe_set_state(card, WAN_CONNECTED);
+	/* Initialize FR Polling Task Queue
+         * We need a poll routine for each network
+         * interface. 
+         */
+      #ifndef LINUX_2_4
+	chan->fr_poll_task.next = NULL;
+      #endif
+	chan->fr_poll_task.sync = 0;
+	chan->fr_poll_task.routine = (void *)(void *)fr_poll;
+	chan->fr_poll_task.data = dev;
+
+	init_timer(&chan->fr_arp_timer);
+	chan->fr_arp_timer.data=(unsigned long)dev;
+	chan->fr_arp_timer.function = fr_arp;
+
+	wandev->new_if_cnt++;
+
+	/* Tells us that if this interface is a
+         * gateway or not */
+	if ((chan->gateway = conf->gateway) == WANOPT_YES){
+		printk(KERN_INFO "%s: Interface %s is set as a gateway.\n",
+			card->devname,dev->name);
+	}
+
+	/* M. Grant Patch Apr 28 2000 
+         * Disallow duplicate dlci configurations. */
+	if (card->u.f.dlci_to_dev_map[chan->dlci] != NULL) {
+		kfree(chan);
+		return -EBUSY;
 	}
 
+	/* Configure this dlci at a later date, when
+         * the interface comes up. i.e. when if_open() 
+         * executes */
+	set_bit(0,&chan->config_dlci);
+	
+	printk(KERN_INFO "\n");
+
 	return 0;
 }
 
 /*============================================================================
  * Delete logical channel.
  */
-static int del_if (wan_device_t* wandev, struct net_device* dev)
+static int del_if (wan_device_t* wandev, netdevice_t* dev)
 {
-	sdla_t *card = wandev->private;
+	fr_channel_t* chan = dev->priv;
+	unsigned long smp_flags=0;
 
-	/* Execute shutdown very first time we enter del_if */
+	/* This interface is dead, make sure the 
+	 * ARP timer is stopped */
+	del_timer(&chan->fr_arp_timer);
+	
+	/* If we are a NODE, we must unconfigure this DLCI
+	 * Trigger an unconfigure command that will
+	 * be executed in timer interrupt. We must wait
+	 * for the command to complete. */
+	trigger_unconfig_fr(dev);
+
+	lock_adapter_irq(&wandev->lock, &smp_flags);
+	wandev->new_if_cnt--;
+	unlock_adapter_irq(&wandev->lock, &smp_flags);
 
-	if (!wandev->del_if_cnt) {
-		wandev->del_if_cnt++;
-		wanpipe_set_state(card, WAN_DISCONNECTED);
-		fr_set_intr_mode(card, 0, 0, 0);
-		fr_comm_disable(card);
-	}
+	return 0;
+}
 
-	if (dev->priv) {
-		kfree(dev->priv);
-		dev->priv = NULL;
-	}
 
-	return 0;
+/*=====================================================================
+ * disable_comm
+ *
+ * Description:
+ *	Disable communications.
+ * 	This code runs in shutdown (sdlamain.c)
+ *      under critical flag. Therefore it is not
+ *      necessary to set a critical flag here 
+ *
+ * Usage:
+ * 	Commnunications are disabled only on a card
+ *      shutdown.
+ */
+
+static void disable_comm (sdla_t *card)
+{
+	printk(KERN_INFO "%s: Disabling Communications!\n",
+			card->devname);
+	fr_comm_disable(card);
 }
 
 /****** WANPIPE-specific entry points ***************************************/
@@ -886,6 +1137,8 @@
 	int err, len;
 	fr_cmd_t cmd;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
 	if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
 		return -EFAULT;
 	
@@ -916,6 +1169,43 @@
 		return -EFAULT;
 	return 0;
 
+#else
+        if (!u_cmd || verify_area(VERIFY_WRITE, u_cmd, sizeof(fr_cmd_t)))
+                return -EFAULT;
+
+        memcpy_fromfs((void*)&cmd, u_cmd, sizeof(cmd));
+
+        if (cmd.length) {
+
+                if (!u_data || verify_area(VERIFY_READ, u_data, cmd.length))
+                        return -EFAULT;
+        }
+
+        /* execute command */
+        do
+        {
+                memcpy(&mbox->cmd, &cmd, sizeof(cmd));
+
+                if (cmd.length)
+                        memcpy_fromfs((void*)&mbox->data, u_data, cmd.length);
+
+                if (sdla_exec(mbox))
+                        err = mbox->cmd.result;
+
+                else return -EIO;
+        } while (err && retry-- && fr_event(card, err, mbox));
+
+        /* return result */
+        memcpy_tofs(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t));
+        len = mbox->cmd.length;
+
+        if (len && u_data && !verify_area(VERIFY_WRITE, u_data, len))
+                memcpy_tofs(u_data, (void*)&mbox->data, len);
+
+        return 0;
+
+#endif
+
 }
 
 /****** Network Device Interface ********************************************/
@@ -927,44 +1217,83 @@
  * interface registration.  Returning anything but zero will fail interface
  * registration.
  */
-static int if_init (struct net_device* dev)
+static int if_init (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 	wan_device_t* wandev = &card->wandev;
+      #ifdef LINUX_2_0
+	int i;
+      #endif
 
 	/* Initialize device driver entry points */
 	dev->open		= &if_open;
 	dev->stop		= &if_close;
-	dev->hard_header	= &if_header;
+	dev->hard_header	= NULL;
 	dev->rebuild_header	= &if_rebuild_hdr;
 	dev->hard_start_xmit	= &if_send;
 	dev->get_stats		= &if_stats;
+      #ifdef LINUX_2_4
 	dev->tx_timeout		= &if_tx_timeout;
 	dev->watchdog_timeo	= TX_TIMEOUT;
+      #endif
+	
+	if (chan->common.usedby == WANPIPE || chan->common.usedby == API){
+		
+              #ifdef LINUX_2_0
+		dev->family = AF_INET;
+              #endif
+		/* Initialize media-specific parameters */
+		if (chan->true_if_encoding){
+			dev->type 		= ARPHRD_DLCI;  /* This breaks tcpdump */
+		}else{
+			dev->type		= ARPHRD_PPP; 	/* ARP h/w type */
+		}
+		
+		dev->flags		|= IFF_POINTOPOINT;
+		dev->flags		|= IFF_NOARP;
 
-	/* Initialize media-specific parameters */
-	dev->type		= ARPHRD_DLCI;	/* ARP h/w type */
-	dev->flags		|= IFF_POINTOPOINT;	
-
-	/* Enable Multicast addressing */
-	if (chan->mc == WANOPT_YES){
-		dev->flags 	|= IFF_MULTICAST;
-	}
+		/* Enable Multicast addressing */
+		if (chan->mc == WANOPT_YES){
+			dev->flags 	|= IFF_MULTICAST;
+		}
 
-	dev->mtu		= wandev->mtu - FR_HEADER_LEN;
-	/* For an API, the maximum number of bytes that the stack will pass
-	   to the driver is (dev->mtu + dev->hard_header_len). So, adjust the
-	   mtu so that a frame of maximum size can be transmitted by the API. 
-	*/
-	if(chan->usedby == API) {
-		dev->mtu += (sizeof(api_tx_hdr_t) - FR_HEADER_LEN);
-	}
-	
-	dev->hard_header_len	= FR_HEADER_LEN;/* media header length */
-	dev->addr_len		= 2; 		/* hardware address length */
-	*(unsigned short*)dev->dev_addr = htons(chan->dlci);
+		dev->mtu		= wandev->mtu - FR_HEADER_LEN;
+		/* For an API, the maximum number of bytes that the stack will pass
+		   to the driver is (dev->mtu + dev->hard_header_len). So, adjust the
+		   mtu so that a frame of maximum size can be transmitted by the API. 
+		*/
+		if(chan->common.usedby == API) {
+			dev->mtu += (sizeof(api_tx_hdr_t) - FR_HEADER_LEN);
+		}
+		
+		dev->hard_header_len	= FR_HEADER_LEN;/* media header length */
+		dev->addr_len		= 2; 		/* hardware address length */
+		*(unsigned short*)dev->dev_addr = htons(chan->dlci);
+
+		/* Set transmit buffer queue length */
+        	dev->tx_queue_len = 100;
+
+			/* Initialize socket buffers */
+	      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+		dev_init_buffers(dev);
+	      #else
+	        for (i = 0; i < DEV_NUMBUFFS; ++i)
+			skb_queue_head_init(&dev->buffs[i]);
+	      #endif
+ 
+	}else{
 
+		/* Setup the interface for Bridging */
+		int hw_addr=0;
+		ether_setup(dev);
+		
+		/* Use a random number to generate the MAC address */
+		memcpy(dev->dev_addr, "\xFE\xFC\x00\x00\x00\x00", 6);
+		get_random_bytes(&hw_addr, sizeof(hw_addr));
+		*(int *)(dev->dev_addr + 2) += hw_addr;
+	}
+		
 	/* Initialize hardware parameters (just for reference) */
 	dev->irq	= wandev->irq;
 	dev->dma	= wandev->dma;
@@ -972,13 +1301,6 @@
 	dev->mem_start	= wandev->maddr;
 	dev->mem_end	= wandev->maddr + wandev->msize - 1;
 
-        /* Set transmit buffer queue length */
-        dev->tx_queue_len = 100;
-   
-	/* Initialize socket buffers */
-	dev_init_buffers(dev);
-
-	set_chan_state(dev, WAN_DISCONNECTED);
 	return 0;
 }
 
@@ -989,60 +1311,51 @@
  *
  * Return 0 if O.k. or errno.
  */
-static int if_open (struct net_device* dev)
+static int if_open (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 	int err = 0;
 	struct timeval tv;
 
-	if (netif_running(dev))
-		return -EBUSY;		/* only one open is allowed */
-
-	if (test_and_set_bit(1, (void*)&card->wandev.critical))
-		return -EAGAIN;
-	
-
-	/* If signalling is set to NO, then setup 
-         * DLCI addresses right away.  Don't have to wait for
-	 * link to connect. 
-	 */
-	if (card->wandev.signalling == WANOPT_NO){
-		printk(KERN_INFO "%s: Signalling set to NO: Mapping DLCI's\n",
-					card->wandev.name);
-		if (fr_init_dlci(card,chan)){
-			return -EAGAIN;
-		}
-	}
-
-	if (card->wandev.station == WANOPT_CPE) {
-
-		/* CPE: issue full status enquiry */
-		fr_issue_isf(card, FR_ISF_FSE);
-
-	} else {	/* FR switch: activate DLCI(s) */
-		
-		/* For Switch emulation we have to ADD and ACTIVATE
-		 * the DLCI(s) that were configured with the SET_DLCI_
-		 * CONFIGURATION command. Add and Activate will fail if
-		 * DLCI specified is not included in the list.
-		 *
-		 * Also If_open is called once for each interface. But
-		 * it does not get in here for all the interface. So
-	 	 * we have to pass the entire list of DLCI(s) to add 
-		 * activate routines.  
-		 */ 
+	if (is_dev_running(dev))
+		return -EBUSY;
 	
-		fr_add_dlci(card, chan->dlci);
-		fr_activate_dlci(card, chan->dlci);
-	}
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	/* Initialize the task queue */
+	chan->tq_working=0;
+
+      #ifndef LINUX_2_4
+	chan->common.wanpipe_task.next = NULL;
+      #endif
+	chan->common.wanpipe_task.sync = 0;
+	chan->common.wanpipe_task.routine = (void *)(void *)fr_bh;
+	chan->common.wanpipe_task.data = dev;
+
+	/* Allocate and initialize BH circular buffer */
+	chan->bh_head = kmalloc((sizeof(bh_data_t)*MAX_BH_BUFF),GFP_ATOMIC);
+	memset(chan->bh_head,0,(sizeof(bh_data_t)*MAX_BH_BUFF));
+	atomic_set(&chan->bh_buff_used, 0);
+      #endif
 
+      #ifdef LINUX_2_4
 	netif_start_queue(dev);
+      #else	
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	dev->start = 1;
+      #endif
+
 	wanpipe_open(card);
-	update_chan_state(dev);
 	do_gettimeofday( &tv );
 	chan->router_start_time = tv.tv_sec;
-        clear_bit(1, (void*)&card->wandev.critical);
+	
+	if (test_bit(0,&chan->config_dlci)){
+		trigger_config_fr (card);
+	}else if (chan->inarp == INARP_REQUEST){
+		trigger_fr_arp(dev);
+	}
+	
 	return err;
 }
 
@@ -1051,61 +1364,42 @@
  * o if this is the last open, then disable communications and interrupts.
  * o reset flags.
  */
-static int if_close (struct net_device* dev)
+static int if_close (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 
-	if (test_and_set_bit(1, (void*)&card->wandev.critical))
-		return -EAGAIN;
+	if (chan->inarp == INARP_CONFIGURED) {
+		chan->inarp = INARP_REQUEST;
+	}
 
-	netif_stop_queue(dev);
+	stop_net_queue(dev);
+      #ifndef LINUX_2_4
+	dev->start=0;
+      #endif
 	wanpipe_close(card);
-	if (card->wandev.station == WANOPT_NODE) {
-		fr_delete_dlci (card,chan->dlci);
-	}
 
-        clear_bit(1, (void*)&card->wandev.critical);
 	return 0;
 }
 
 /*============================================================================
- * Build media header.
- * o encapsulate packet according to encapsulation type.
- *
- * The trick here is to put packet type (Ethertype) into 'protocol' field of
- * the socket buffer, so that we don't forget it.  If encapsulation fails,
- * set skb->protocol to 0 and discard packet later.
- *
- * Return:	media header length.
- */
-static int if_header (struct sk_buff* skb, struct net_device* dev,
-	unsigned short type, void* daddr, void* saddr, unsigned len)
-{
-	int hdr_len = 0;
-	
-	skb->protocol = type;
-	hdr_len = wanrouter_encapsulate(skb, dev);
-
-	if (hdr_len < 0) {
-		hdr_len = 0;
-		skb->protocol = 0;
-	}
-	skb_push(skb, 1);
-	skb->data[0] = Q922_UI;
-	++hdr_len;
-	return hdr_len;
-}
-
-/*============================================================================
  * Re-build media header.
  *
  * Return:	1	physical address resolved.
  *		0	physical address not resolved
  */
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 static int if_rebuild_hdr (struct sk_buff* skb)
 {
-	struct net_device *dev = skb->dev;
+#else
+static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
+                           struct sk_buff* skb)
+{
+#endif
+
+      #if defined(LINUX_2_1) || defined(LINUX_2_4) 
+	netdevice_t *dev = skb->dev;
+      #endif
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 
@@ -1114,13 +1408,14 @@
 	return 1;
 }
 
-
+#ifdef LINUX_2_4
 /*============================================================================
  * Handle transmit timeout event from netif watchdog
  */
-static void if_tx_timeout (struct net_device *dev)
+static void if_tx_timeout (netdevice_t *dev)
 {
     	fr_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
 
 	/* If our device stays busy for at least 5 seconds then we will
 	 * kick start the device by making dev->tbusy = 0.  We expect
@@ -1131,12 +1426,13 @@
 	chan->drvstats_if_send.if_send_tbusy++;
 	++chan->ifstats.collisions;
 
-	printk (KERN_INFO "%s: Transmit timed out\n", chan->name);
+	printk (KERN_INFO "%s: Transmit timed out on %s\n", 
+			card->devname, dev->name);
 	chan->drvstats_if_send.if_send_tbusy_timeout++;
 	netif_wake_queue (dev);
 
 }
-
+#endif
 
 /*============================================================================
  * Send a packet on a network interface.
@@ -1154,23 +1450,28 @@
  * Notes:
  * 1. This routine is called either by the protocol stack or by the "net
  *    bottom half" (with interrupts enabled).
- * 2. Setting tbusy flag will inhibit further transmit requests from the
- *    protocol stack and can be used for flow control with protocol layer.
+ * 
+ * 2. Using the start_net_queue() and stop_net_queue() MACROS
+ *    will inhibit further transmit requests from the protocol stack 
+ *    and can be used for flow control with protocol layer.
  */
-static int if_send (struct sk_buff* skb, struct net_device* dev)
+static int if_send (struct sk_buff* skb, netdevice_t* dev)
 {
     	fr_channel_t* chan = dev->priv;
     	sdla_t* card = chan->card;
         int err;
     	unsigned char *sendpacket;
     	fr508_flags_t* adptr_flags = card->flags;
-	int udp_type, send_data;
+	int udp_type, delay_tx_queued=0;
 	unsigned long smp_flags=0;
-	void* data;
-	unsigned len;
+	unsigned char attr = 0;
 
 	chan->drvstats_if_send.if_send_entry++;
 
+      #ifdef LINUX_2_4
+	netif_stop_queue(dev);
+      #endif
+	
         if (skb == NULL) {             
 		/* if we get here, some higher layer thinks we've missed an
 		 * tx-done interrupt.
@@ -1178,7 +1479,19 @@
 		printk(KERN_INFO "%s: interface %s got kicked!\n", 
 			card->devname, dev->name);
 		chan->drvstats_if_send.if_send_skb_null ++;
-		netif_wake_queue(dev);
+
+		wake_net_dev(dev);
+		return 0;
+	}
+
+	/* If a peripheral task is running just drop packets */
+	if (test_bit(PERI_CRIT, &card->wandev.critical)){
+		
+		printk(KERN_INFO "%s: Critical in if_send(): Peripheral running!\n",
+				card->devname);
+		
+		wan_dev_kfree_skb(skb,FREE_WRITE);
+		start_net_queue(dev);
 		return 0;
 	}
 
@@ -1187,23 +1500,58 @@
 	   ensure that the transmit interrupt does not reset the 'tbusy' flag
 	   just before we set it, as this will result in a "transmit timeout".
 	*/
-	set_bit(2, (void*)&card->wandev.critical);
+	set_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical);
         if(chan->transmit_length) {
-		netif_stop_queue(dev);
-	        chan->tick_counter = jiffies;
- 		clear_bit(2, (void*)&card->wandev.critical);
+		stop_net_queue(dev);
+		chan->tick_counter = jiffies;
+ 		clear_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical);
 		return 1;
 	}
-       	clear_bit(2, (void*)&card->wandev.critical);
-  
-	data = skb->data;
+       	clear_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical);
+ 
+      #ifndef LINUX_2_4
+    	if (dev->tbusy) {
+
+		/* If our device stays busy for at least 5 seconds then we will
+                 * kick start the device by making dev->tbusy = 0.  We expect
+                 * that our device never stays busy more than 5 seconds. So this                 
+		 * is only used as a last resort.
+                 */
+		
+		chan->drvstats_if_send.if_send_tbusy++;
+		++chan->ifstats.collisions;
+
+		if ((jiffies - chan->tick_counter) < (5 * HZ)) {
+			return 1;
+		}
+
+		printk(KERN_INFO "%s: Transmit timed out on %s\n", 
+				card->devname, chan->name);
+		chan->drvstats_if_send.if_send_tbusy_timeout ++;
+		dev->tbusy = 0;
+    	}
+      #endif
+
+	
+	/* Move the if_header() code to here. By inserting frame
+	 * relay header in if_header() we would break the
+	 * tcpdump and other packet sniffers */
+	chan->fr_header_len = setup_fr_header(&skb,dev,chan->common.usedby);
+	if (chan->fr_header_len < 0 ){
+		++chan->ifstats.tx_dropped;
+		++card->wandev.stats.tx_dropped;
+		
+		wan_dev_kfree_skb(skb,FREE_WRITE);
+		start_net_queue(dev);	
+		return 0;
+	}
+
 	sendpacket = skb->data;
-        len = skb->len;
 
 	udp_type = udp_pkt_type(skb, card);
 
         if(udp_type != UDP_INVALID_TYPE) {
-                if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, skb,
+		if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, skb,
                         chan->dlci)) {
                         adptr_flags->imask |= FR_INTR_TIMER;
                         if (udp_type == UDP_FPIPE_TYPE){
@@ -1211,61 +1559,92 @@
 					if_send_PIPE_request ++;
 			}
                 }
+		start_net_queue(dev);
 		return 0;
 	}
 
-	if((chan->usedby == API) && (len <= sizeof(api_tx_hdr_t))) {
-		//FIXME: increment some error statistic
-                dev_kfree_skb(skb);
-                return 0;
- 	}
- 
 	//FIXME: can we do better than sendpacket[2]?
-  	if ((chan->usedby == WANPIPE) && (sendpacket[2] == 0x45)) {
+  	if ((chan->common.usedby == WANPIPE) && (sendpacket[2] == 0x45)) {
+		
                	/* check to see if the source IP address is a broadcast or */
                 /* multicast IP address */
-                if(chk_bcast_mcast_addr(card, dev, skb))
-                        return 0;
+                if(chk_bcast_mcast_addr(card, dev, skb)){
+            		++chan->ifstats.tx_dropped;
+			++card->wandev.stats.tx_dropped;
+                	wan_dev_kfree_skb(skb, FREE_WRITE);
+			start_net_queue(dev);
+			return 0;
+		}
 	}
 
-	/* Lock the 508 card: SMP Supported */
+	
+	/* Lock the S514/S508 card: SMP Supported */
     	s508_s514_lock(card,&smp_flags);
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
+	if (test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
+		
 		chan->drvstats_if_send.if_send_critical_non_ISR ++;
 		chan->ifstats.tx_dropped ++;
-		printk(KERN_INFO "%s Critical in IF_SEND %02X\n", 
-				card->devname, card->wandev.critical);
-		dev_kfree_skb(skb);
-		/* Unlock the 508 card */
-               	s508_s514_unlock(card,&smp_flags);
-		return 0;
+		printk(KERN_INFO "%s Critical in IF_SEND: if_send() already running!\n", 
+				card->devname);
+		goto if_send_start_and_exit;
 	}
- 	
+	
+	/* API packet check: minimum packet size must be greater than 
+	 * 16 byte API header */
+	if((chan->common.usedby == API) && (skb->len <= sizeof(api_tx_hdr_t))) {
+		++chan->ifstats.tx_dropped;
+		++card->wandev.stats.tx_dropped;
+	    
+		
+		goto if_send_start_and_exit;
+
+ 	}else{
+		/* During API transmission, get rid of the API header */
+		if (chan->common.usedby == API) {
+			api_tx_hdr_t* api_tx_hdr;
+			api_tx_hdr = (api_tx_hdr_t*)&skb->data[0x00];
+			attr = api_tx_hdr->attr;
+			skb_pull(skb,sizeof(api_tx_hdr_t));
+		}
+	}
+
 	if (card->wandev.state != WAN_CONNECTED) {
 		chan->drvstats_if_send.if_send_wan_disconnected ++;
 		++chan->ifstats.tx_dropped;
         	++card->wandev.stats.tx_dropped;
 	
-	} else if (chan->state != WAN_CONNECTED) {
+	} else if (chan->common.state != WAN_CONNECTED) {
 		chan->drvstats_if_send.if_send_dlci_disconnected ++;
-		/* Critical area on 514, since disabl_irq is not used
-                 * thus, interrupt would execute a command at
-		 * the same time as if_send.
-                 */
-     		set_bit(1, (void*)&card->wandev.critical);
-		update_chan_state(dev);
-		clear_bit(1, (void*)&card->wandev.critical);
+
+		/* Update the DLCI state in timer interrupt */
+		card->u.f.timer_int_enabled |= TMR_INT_ENABLED_UPDATE_STATE;	
+		adptr_flags->imask |= FR_INTR_TIMER;
+
         	++chan->ifstats.tx_dropped;
         	++card->wandev.stats.tx_dropped;
-
+		
 	} else if (!is_tx_ready(card, chan)) {
-		setup_for_delayed_transmit(dev, data, len);
+		/* No tx buffers available, store for delayed transmit */
+		if (!setup_for_delayed_transmit(dev, skb)){
+			set_bit(1,&delay_tx_queued);
+		}
 		chan->drvstats_if_send.if_send_no_bfrs++;
- 	} else {
-		send_data = 1;
+		
+	} else if (!skb->protocol) {
+		/* No protocols drop packet */
+		chan->drvstats_if_send.if_send_protocol_error ++;
+		++card->wandev.stats.tx_errors;
+	
+	} else if (test_bit(ARP_CRIT,&card->wandev.critical)){
+		/* We are trying to send an ARP Packet, block IP data until
+		 * ARP is sent */
+		++chan->ifstats.tx_dropped;
+        	++card->wandev.stats.tx_dropped;
+		
+	} else {
 		//FIXME: IPX is not implemented in this version of Frame Relay ?
-		if((chan->usedby == WANPIPE) &&
+		if((chan->common.usedby == WANPIPE) &&
 		 	sendpacket[1] == 0x00 &&
 		    	sendpacket[2] == 0x80 &&
 		    	sendpacket[6] == 0x81 &&
@@ -1279,32 +1658,28 @@
 				printk(KERN_INFO 
 				"%s: WARNING: Unsupported IPX data in send, packet dropped\n",
 					card->devname);
-				send_data = 0;
 			}
-		}
-
-		if (send_data) {
-			unsigned char attr = 0;
-
-			/* For an API transmission, get rid of the API header */
-                        if (chan->usedby == API) {
-                                api_tx_hdr_t* api_tx_hdr;
-           			api_tx_hdr = (api_tx_hdr_t*)&skb->data[0x00];
-                      		attr = api_tx_hdr->attr;
-				data += sizeof(api_tx_hdr_t);
-				len -= sizeof(api_tx_hdr_t);
-			}
-
-        		err = fr_send(card, chan->dlci, attr, len, data);
+			
+		}else{
+        		err = fr_send_data_header(card, chan->dlci, attr, skb->len, skb->data, chan->fr_header_len);
 			if (err) {
 				switch(err) {
 				case FRRES_CIR_OVERFLOW:
 				case FRRES_BUFFER_OVERFLOW:
-                			setup_for_delayed_transmit(dev, data,
-						len);
+                			if (!setup_for_delayed_transmit(dev, skb)){
+						set_bit(1,&delay_tx_queued);
+					}
            				chan->drvstats_if_send.
 						if_send_adptr_bfrs_full ++;
-					break;	
+					break;
+					
+				case FRRES_TOO_LONG:
+					if (net_ratelimit()){
+						printk(KERN_INFO 
+						"%s: Error: Frame too long, transmission failed %i\n",
+						 card->devname, (unsigned int)skb->len);
+					}
+					/* Drop down to default */
 				default:
 					chan->drvstats_if_send.
 						if_send_dlci_disconnected ++;
@@ -1317,20 +1692,38 @@
 					if_send_bfr_passed_to_adptr++;
 				++chan->ifstats.tx_packets;
 				++card->wandev.stats.tx_packets;
-                                chan->ifstats.tx_bytes += len;
-                                card->wandev.stats.tx_bytes += len;
+				
+			      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+                                chan->ifstats.tx_bytes += skb->len;
+                                card->wandev.stats.tx_bytes += skb->len;
+			      #endif
+				
+			      #ifdef LINUX_2_4
+				dev->trans_start = jiffies;
+			      #endif
 			}
 		}
 	}
 
-        if (!netif_queue_stopped(dev))
-		dev_kfree_skb(skb);
+if_send_start_and_exit:
+
+	start_net_queue(dev);
+	
+	/* If we queued the packet for transmission, we must not
+	 * deallocate it. The packet is unlinked from the IP stack
+	 * not copied. Therefore, we must keep the original packet */
+	if (!test_bit(1,&delay_tx_queued)) {
+                wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+		adptr_flags->imask |= FR_INTR_TXRDY;
+		card->u.f.tx_interrupts_pending ++;
+	}
 
-        clear_bit(0, (void*)&card->wandev.critical);
+        clear_bit(SEND_CRIT, (void*)&card->wandev.critical);
 
 	s508_s514_unlock(card,&smp_flags);
 
-        return (netif_queue_stopped(dev));
+	return 0;
 }
 
 
@@ -1339,33 +1732,49 @@
  * Setup so that a frame can be transmitted on the occurence of a transmit
  * interrupt.
  */
-static void setup_for_delayed_transmit (struct net_device* dev, void* buf,
-	unsigned len)
+static int setup_for_delayed_transmit (netdevice_t* dev, struct sk_buff *skb)
 {
         fr_channel_t* chan = dev->priv;
         sdla_t* card = chan->card;
-        fr508_flags_t* adptr_flags = card->flags;
-        fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface;
+        fr_dlci_interface_t* dlci_interface;
+	int len = skb->len;
+
+	/* Check that the dlci is properly configured,
+         * before using tx interrupt */
+	if (!chan->dlci_int_interface){
+		if (net_ratelimit()){ 
+			printk(KERN_INFO 
+				"%s: ERROR on DLCI %i: Not configured properly !\n",
+					card->devname, chan->dlci);
+			printk(KERN_INFO "%s: Please contact Sangoma Technologies\n",
+					card->devname);
+		}
+		return 1;
+	}
+		
+	dlci_interface = chan->dlci_int_interface;
 
         if(chan->transmit_length) {
                 printk(KERN_INFO "%s: Big mess in setup_for_del...\n",
 				card->devname);
-                return;
+                return 1;
         }
 
 	if(len > FR_MAX_NO_DATA_BYTES_IN_FRAME) {
 		//FIXME: increment some statistic */
-		return;
+		return 1;
 	}
 
+	skb_unlink(skb);
+	
         chan->transmit_length = len;
-        memcpy(chan->transmit_buffer, buf, len);
-
+	chan->delay_skb = skb;
+        
         dlci_interface->gen_interrupt |= FR_INTR_TXRDY;
         dlci_interface->packet_length = len;
-        adptr_flags->imask |= FR_INTR_TXRDY;
 
-	card->u.f.tx_interrupts_pending ++;
+	/* Turn on TX interrupt at the end of if_send */
+	return 0;
 }
 
 
@@ -1375,18 +1784,21 @@
  * Return 0 if not broadcast/multicast address, otherwise return 1.
  */
 
-static int chk_bcast_mcast_addr(sdla_t *card, struct net_device* dev,
+static int chk_bcast_mcast_addr(sdla_t *card, netdevice_t* dev,
                                 struct sk_buff *skb)
 {
         u32 src_ip_addr;
         u32 broadcast_ip_addr = 0;
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
         struct in_device *in_dev;
+      #endif
         fr_channel_t* chan = dev->priv;
  
         /* read the IP source address from the outgoing packet */
         src_ip_addr = *(u32 *)(skb->data + 14);
 
         /* read the IP broadcast address for the device */
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
         in_dev = dev->ip_ptr;
         if(in_dev != NULL) {
                 struct in_ifaddr *ifa= in_dev->ifa_list;
@@ -1395,13 +1807,15 @@
                 else
                         return 0;
         }
+      #else
+        broadcast_ip_addr = dev->pa_brdaddr;
+      #endif
 
         /* check if the IP Source Address is a Broadcast address */
-        if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) {                printk(KERN_INFO
+        if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) {
+                printk(KERN_INFO
                         "%s: Broadcast Source Address silently discarded\n",
                         card->devname);
-                dev_kfree_skb(skb);
-                ++ chan->ifstats.tx_dropped;
                 return 1;
         }
 
@@ -1411,8 +1825,6 @@
                 printk(KERN_INFO
                         "%s: Multicast Source Address silently discarded\n",
                         card->devname);
-                dev_kfree_skb(skb);
-                ++ chan->ifstats.tx_dropped;
                 return 1;
         }
 
@@ -1433,7 +1845,7 @@
 	fr_udp_pkt_t *fr_udp_pkt = (fr_udp_pkt_t *)data; 
 
 	/* Set length of packet */
-	len = sizeof(fr_encap_hdr_t)+
+	len = //sizeof(fr_encap_hdr_t)+
 	      sizeof(ip_pkt_t)+ 
 	      sizeof(udp_pkt_t)+
 	      sizeof(wp_mgmt_t)+
@@ -1481,7 +1893,7 @@
 	fr_udp_pkt->udp_pkt.udp_checksum = 0;
 
 	fr_udp_pkt->udp_pkt.udp_checksum = 
-		calc_checksum(&data[UDP_OFFSET+sizeof(fr_encap_hdr_t)],
+		calc_checksum(&data[UDP_OFFSET/*+sizeof(fr_encap_hdr_t)*/],
 			      udp_length+UDP_OFFSET);
 
 	/* fill in IP length */
@@ -1499,7 +1911,7 @@
 	/* fill in IP checksum */
 	fr_udp_pkt->ip_pkt.hdr_checksum = 0;
 	fr_udp_pkt->ip_pkt.hdr_checksum = 
-		calc_checksum(&data[sizeof(fr_encap_hdr_t)],
+		calc_checksum(&data[/*sizeof(fr_encap_hdr_t)*/0],
 		      	      sizeof(ip_pkt_t));
 
 	return len;
@@ -1588,9 +2000,13 @@
 
 /*============================================================================
  * Get ethernet-style interface statistics.
- * Return a pointer to struct net_device_stats.
+ * Return a pointer to struct enet_statistics.
  */
-static struct net_device_stats* if_stats (struct net_device* dev)
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats *if_stats(netdevice_t *dev)
+#else
+static struct enet_statistics* if_stats (netdevice_t* dev)
+#endif
 {
 	fr_channel_t* chan = dev->priv;
 	
@@ -1603,7 +2019,12 @@
 /****** Interrupt Handlers **************************************************/
 
 /*============================================================================
- * S508 frame relay interrupt service routine.
+ * fr_isr:	S508 frame relay interrupt service routine.
+ *
+ * Description:
+ *	Frame relay main interrupt service route. This
+ *      function check the interrupt type and takes
+ *      the appropriate action.
  */
 static void fr_isr (sdla_t* card)
 {
@@ -1613,26 +2034,25 @@
 	fr_mbox_t* mbox = card->mbox;
 
 	/* This flag prevents nesting of interrupts.  See sdla_isr() routine
-         * in sdlamain.c. 
-	 */
+         * in sdlamain.c.  */
 	card->in_isr = 1;
 	
 	++card->statistics.isr_entry;
 
-	if(test_bit(1, (void*)&card->wandev.critical)) {
-		card->wandev.critical = 0;
-		flags->iflag = 0;
-		card->in_isr = 0;
-		return;
-	}
 
+	/* All peripheral (configuraiton, re-configuration) events
+	 * take presidence over the ISR.  Thus, retrigger */
+	if (test_bit(PERI_CRIT, (void*)&card->wandev.critical)) {
+		++card->statistics.isr_already_critical;
+		goto fr_isr_exit;
+	}
+	
         if(card->hw.type != SDLA_S514) {
-		if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
-                        printk(KERN_INFO "%s: Critical while in ISR (0x%02X)\n",
-                                card->devname, card->wandev.critical);
+		if (test_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
+                        printk(KERN_INFO "%s: Critical while in ISR: If Send Running!\n",
+                                card->devname);
 			++card->statistics.isr_already_critical;
-			card->in_isr = 0;
-			return;
+			goto fr_isr_exit;
 		}
 	}
 
@@ -1649,12 +2069,12 @@
 			tx_intr(card); 
             		break;
 
-                case FR_INTR_READY:
+                case FR_INTR_READY:  	
 	    		Intr_test_counter++;
 			++card->statistics.isr_intr_test;
 	    		break;	
 
-                case FR_INTR_DLC: /* Event interrupt occurred */
+                case FR_INTR_DLC: /* Event interrupt occured */
 			mbox->cmd.command = FR_READ_STATUS;
 			mbox->cmd.length = 0;
 			err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
@@ -1680,27 +2100,31 @@
 			break;
     	}
 
+fr_isr_exit:
+	
 	card->in_isr = 0;
 	flags->iflag = 0;
-        if(card->hw.type != SDLA_S514)
-                clear_bit(0, (void*)&card->wandev.critical);
+	return;
 }
 
 
 
-/*============================================================================
- * Receive interrupt handler.
- * When a receive interrupt occurs do the following:
- *	1- Find the structure for the dlci that the interrupt occurred on
- *      2- If it doesn't exist then print appropriate msg and goto step 8. 
- * 	3- If it exist then copy data to a skb.
- * 	4- If skb contains Sangoma UDP data then process them
- *	5- If skb contains IPXWAN data then send IPXWAN reply packets
- *	6- If skb contains Inverse Arp data then send Inv Arp replies
- *	7- If skb contains any other data then decapsulate the packet and
- *	   send it to the stack.
- * 	8- Release the receive element and update receive pointers on the board 
+/*===========================================================
+ * rx_intr	Receive interrupt handler.
+ *
+ * Description
+ * 	Upon receiveing an interrupt: 
+ *	1. Check that the firmware is in sync with 
+ *     	   the driver. 
+ *      2. Find an appropriate network interface
+ *         based on the received dlci number.
+ *	3. Check that the netowrk interface exists
+ *         and that it's setup properly.
+ *	4. Copy the data into an skb buffer.
+ *	5. Check the packet type and take
+ *         appropriate acton: UPD, API, ARP or Data.
  */
+
 static void rx_intr (sdla_t* card)
 {
 	fr_rx_buf_ctl_t* frbuf = card->rxmb;
@@ -1708,11 +2132,13 @@
 	fr_channel_t* chan;
 	char *ptr = &flags->iflag;
 	struct sk_buff* skb;
-	struct net_device* dev;
+	netdevice_t* dev;
 	void* buf;
 	unsigned dlci, len, offs, len_incl_hdr;
 	int i, udp_type;	
 
+
+	/* Check that firmware buffers are in sync */
 	if (frbuf->flag != 0x01) {
 
 		printk(KERN_INFO 
@@ -1725,6 +2151,16 @@
 		printk(KERN_INFO "\n");
 	
 		++card->statistics.rx_intr_corrupt_rx_bfr;
+
+		/* Bug Fix: Mar 6 2000
+                 * If we get a corrupted mailbox, it means that driver 
+                 * is out of sync with the firmware. There is no recovery.
+                 * If we don't turn off all interrupts for this card
+                 * the machine will crash. 
+                 */
+		printk(KERN_INFO "%s: Critical router failure ...!!!\n", card->devname);
+		printk(KERN_INFO "Please contact Sangoma Technologies !\n");
+		fr_set_intr_mode(card, 0, 0, 0);	
 		return;
 	}
 
@@ -1732,134 +2168,206 @@
 	dlci = frbuf->dlci;
 	offs = frbuf->offset;
 
-	/* Find network interface for this packet */
+	/* Find the network interface for this packet */
 	dev = find_channel(card, dlci);
-        
-	if (dev == NULL) {
+   
 
-		/* unconfigured DLCI, so discard packet */
-   		printk(KERN_INFO "%s: received data on unconfigured DLCI %d!\n",
+	/* Check that the network interface is active and
+         * properly setup */
+	if (dev == NULL) {
+   		if( net_ratelimit()) { 
+			printk(KERN_INFO "%s: received data on unconfigured DLCI %d!\n",
                                                 card->devname, dlci);
+		}
 		++card->statistics.rx_intr_on_orphaned_DLCI; 
+		++card->wandev.stats.rx_dropped;
+		goto rx_done;
+	}
 
-       	} else {
-		chan = dev->priv;
+	if ((chan = dev->priv) == NULL){
+		if( net_ratelimit()) { 
+			printk(KERN_INFO "%s: received data on unconfigured DLCI %d!\n",
+                                                card->devname, dlci);
+		}
+		++card->statistics.rx_intr_on_orphaned_DLCI; 
+		++card->wandev.stats.rx_dropped;
+		goto rx_done;
+	}
 
-   		skb = dev_alloc_skb(len); 
+	skb = dev_alloc_skb(len); 
 
-		if (!netif_running(dev) || (skb == NULL)) { 
-			++chan->ifstats.rx_dropped;
-		
-			if(netif_running(dev)) {
+	if (!is_dev_running(dev) || (skb == NULL)){
 
+		++chan->ifstats.rx_dropped;
+	
+		if(skb == NULL) {
+			if (net_ratelimit()) { 
 				printk(KERN_INFO 
-				"%s: no socket buffers available!\n", 
-				card->devname);
-	    			chan->drvstats_rx_intr.rx_intr_no_socket ++;
-                		
-			} else
-				chan->drvstats_rx_intr.
-					rx_intr_dev_not_started ++;
-		} else {
-			/* Copy data to the socket buffer */
-			if ((offs + len) > card->u.f.rx_top + 1) {
-				unsigned tmp = card->u.f.rx_top - offs + 1;
-
-				buf = skb_put(skb, tmp);
-				sdla_peek(&card->hw, offs, buf, tmp);
-				offs = card->u.f.rx_base;
-               			len -= tmp;
-               		}
-
-			buf = skb_put(skb, len);
-			sdla_peek(&card->hw, offs, buf, len);
-
-			udp_type = udp_pkt_type( skb, card );
-
-                        if(udp_type != UDP_INVALID_TYPE) {
-                                if(store_udp_mgmt_pkt(udp_type,
-                                        UDP_PKT_FRM_NETWORK, card, skb, dlci)) {
-                                        flags->imask |= FR_INTR_TIMER;
-                                        if (udp_type == UDP_FPIPE_TYPE){
-                                                chan->drvstats_rx_intr.
-						    rx_intr_PIPE_request ++;
-					}
-				}			
+					"%s: no socket buffers available!\n", 
+						card->devname);
 			}
+			chan->drvstats_rx_intr.rx_intr_no_socket ++;
+		} 
 
-			else if (chan->usedby == API) {
-				api_rx_hdr_t* api_rx_hdr;
-				chan->drvstats_rx_intr.
-                                	rx_intr_bfr_passed_to_stack ++;
-				chan->ifstats.rx_packets ++;
-                                card->wandev.stats.rx_packets ++;
-				chan->ifstats.rx_bytes += skb->len;
-				card->wandev.stats.rx_bytes += skb->len;
-
-				skb_push(skb, sizeof(api_rx_hdr_t));
-				api_rx_hdr = (api_rx_hdr_t*)&skb->data[0x00];
-				api_rx_hdr->attr = frbuf->attr;
-				api_rx_hdr->time_stamp = frbuf->tmstamp;
-				skb->protocol = htons(0x16);
-				skb->pkt_type = PACKET_HOST;
-                                /* Pass it up the protocol stack */
-                                skb->dev = dev;
-                                skb->mac.raw  = skb->data;
-                                netif_rx(skb);
-
-			} else if (handle_IPXWAN(skb->data,chan->name,
-				chan->enable_IPX, chan->network_number)) {
-				if (chan->enable_IPX) {
-                                        fr_send(card, dlci, 0, skb->len,
-						skb->data);
-				}
-				dev_kfree_skb(skb);
+		if (!is_dev_running(dev)){
+			chan->drvstats_rx_intr.
+				rx_intr_dev_not_started ++;
+			if (skb){
+				wan_dev_kfree_skb(skb, FREE_READ);
+			}
+		}
+		goto rx_done;
+	}
 
-/*FIXME: Fix the ARPS in next release
+	/* Copy data from the board into the socket buffer */
+	if ((offs + len) > card->u.f.rx_top + 1) {
+		unsigned tmp = card->u.f.rx_top - offs + 1;
 
-			} else if (is_arp(skb->data)) {
-				if (process_ARP((arphdr_1490_t *)skb->data, card, dev)) {
-					printk (KERN_INFO "%s: Error processing ARP Packet.\n", card->devname);
-				}
-				dev_kfree_skb(skb);
-*/
+		buf = skb_put(skb, tmp);
+		sdla_peek(&card->hw, offs, buf, tmp);
+		offs = card->u.f.rx_base;
+		len -= tmp;
+	}
 
-			} else if ( skb->data[0] != 0x03) {
-				printk(KERN_INFO "%s: Non IETF packet discarded.\n", card->devname);
-				dev_kfree_skb(skb);
+	buf = skb_put(skb, len);
+	sdla_peek(&card->hw, offs, buf, len);
 
-			} else {
 
-				len_incl_hdr = skb->len;
-                    		/* Decapsulate packet and pass it up the
-			   	   protocol stack */
-				skb->dev = dev;
+	/* We got the packet from the bard. 
+         * Check the packet type and take appropriate action */
 
-				/* remove hardware header */
-				buf = skb_pull(skb, 1); 
+	udp_type = udp_pkt_type( skb, card );
 
-				if (!wanrouter_type_trans(skb, dev)) {
-					
-					/* can't decapsulate packet */
-					dev_kfree_skb(skb);
-					chan->drvstats_rx_intr.
-					     rx_intr_bfr_not_passed_to_stack ++;
- 					++ chan->ifstats.rx_errors;
-		                       	++ card->wandev.stats.rx_errors;
-					
-				} else {
-					netif_rx(skb);
-					chan->drvstats_rx_intr.
-						rx_intr_bfr_passed_to_stack ++;
-					++ chan->ifstats.rx_packets;
-					++ card->wandev.stats.rx_packets;
-                                        chan->ifstats.rx_bytes += len_incl_hdr;
-                                        card->wandev.stats.rx_bytes += 
-						len_incl_hdr;
+	if(udp_type != UDP_INVALID_TYPE) {
+
+		/* UDP Debug packet received, store the
+		 * packet and handle it in timer interrupt */
+
+		skb_pull(skb, 1); 
+		if (wanrouter_type_trans(skb, dev)){ 
+			if(store_udp_mgmt_pkt(udp_type,UDP_PKT_FRM_NETWORK,card,skb,dlci)){
+
+				flags->imask |= FR_INTR_TIMER;
+
+				if (udp_type == UDP_FPIPE_TYPE){
+					++chan->drvstats_rx_intr.rx_intr_PIPE_request;
 				}
-                	}
-            	}
-       	}
+			}
+		}
+
+	#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	}else if (chan->common.usedby == API) {
+
+		/* We are in API mode. 
+                 * Add an API header to the RAW packet
+                 * and queue it into a circular buffer.
+                 * Then kick the fr_bh() bottom half handler */
+
+		api_rx_hdr_t* api_rx_hdr;
+		chan->drvstats_rx_intr.rx_intr_bfr_passed_to_stack ++;
+		chan->ifstats.rx_packets ++;
+		card->wandev.stats.rx_packets ++;
+
+		chan->ifstats.rx_bytes += skb->len;
+		card->wandev.stats.rx_bytes += skb->len;
+
+		skb_push(skb, sizeof(api_rx_hdr_t));
+		api_rx_hdr = (api_rx_hdr_t*)&skb->data[0x00];
+		api_rx_hdr->attr = frbuf->attr;
+		api_rx_hdr->time_stamp = frbuf->tmstamp;
+
+		skb->protocol = htons(ETH_P_IP);
+		skb->mac.raw  = skb->data;
+		skb->dev      = dev;
+		skb->pkt_type = WAN_PACKET_DATA;
+
+		bh_enqueue(dev, skb);
+
+		trigger_fr_bh(chan);
+	#endif
+
+	}else if (handle_IPXWAN(skb->data,chan->name,chan->enable_IPX, chan->network_number)){
+
+		//FIXME: Frame Relay IPX is not supported, Yet !
+		//if (chan->enable_IPX) {
+		//	fr_send(card, dlci, 0, skb->len,skb->data);
+		//}
+		wan_dev_kfree_skb(skb, FREE_READ);
+
+	} else if (is_arp(skb->data)) {
+
+		/* ARP support enabled Mar 16 2000 
+		 * Process incoming ARP reply/request, setup
+		 * dynamic routes. */ 
+
+		if (process_ARP((arphdr_1490_t *)skb->data, card, dev)) {
+			if (net_ratelimit()){  
+				printk (KERN_INFO 
+				   "%s: Error processing ARP Packet.\n", 
+					card->devname);
+			}
+		}
+		wan_dev_kfree_skb(skb, FREE_READ);
+
+	} else if (skb->data[0] != 0x03) {
+
+		if (net_ratelimit()) { 
+			printk(KERN_INFO "%s: Non IETF packet discarded.\n", 
+				card->devname);
+		}
+		wan_dev_kfree_skb(skb, FREE_READ);
+
+	} else {
+
+		len_incl_hdr = skb->len;
+		/* Decapsulate packet and pass it up the
+		   protocol stack */
+		skb->dev = dev;
+		
+		if (chan->common.usedby == BRIDGE || chan->common.usedby == BRIDGE_NODE){
+		
+			/* Make sure it's an Ethernet frame, otherwise drop it */
+			if (!memcmp(skb->data, "\x03\x00\x80\x00\x80\xC2\x00\x07", 8)) {
+				skb_pull(skb, 8);
+				skb->protocol=eth_type_trans(skb,dev);
+			}else{
+				++chan->drvstats_rx_intr.rx_intr_bfr_not_passed_to_stack;
+				++chan->ifstats.rx_errors;
+				++card->wandev.stats.rx_errors;
+				goto rx_done;
+			}
+		}else{
+		
+			/* remove hardware header */
+			buf = skb_pull(skb, 1); 
+			
+			if (!wanrouter_type_trans(skb, dev)) {
+				
+				/* can't decapsulate packet */
+				wan_dev_kfree_skb(skb, FREE_READ);
+
+				++chan->drvstats_rx_intr.rx_intr_bfr_not_passed_to_stack;
+				++chan->ifstats.rx_errors;
+				++card->wandev.stats.rx_errors;
+				goto rx_done;	
+			}
+			skb->mac.raw = skb->data;
+		} 
+		
+
+		/* Send a packed up the IP stack */
+		netif_rx(skb);
+		++chan->drvstats_rx_intr.rx_intr_bfr_passed_to_stack;
+		++chan->ifstats.rx_packets;
+		++card->wandev.stats.rx_packets;
+
+		#if defined(LINUX_2_1) || defined(LINUX_2_4)
+		chan->ifstats.rx_bytes += len_incl_hdr;
+		card->wandev.stats.rx_bytes += len_incl_hdr;
+		#endif
+	}
+
+rx_done:
 
        	/* Release buffer element and calculate a pointer to the next one */ 
        	frbuf->flag = 0;
@@ -1869,14 +2377,36 @@
 
 }
 
-/*============================================================================
- * Transmit interrupt handler.
+/*==================================================================
+ * tx_intr:	Transmit interrupt handler.
+ *
+ * Rationale:
+ *      If the board is busy transmitting, if_send() will
+ *      buffers a single packet and turn on
+ *      the tx interrupt. Tx interrupt will be called
+ *      by the board, once the firmware can send more
+ *      data. Thus, no polling is required.	 
+ *
+ * Description:
+ *	Tx interrupt is called for each 
+ *      configured dlci channel. Thus: 
+ * 	1. Obtain the netowrk interface based on the
+ *         dlci number.
+ *      2. Check that network interface is up and
+ *         properly setup.
+ * 	3. Check for a buffered packed.
+ *      4. Transmit the packed.
+ *	5. If we are in WANPIPE mode, mark the 
+ *         NET_BH handler. 
+ *      6. If we are in API mode, kick
+ *         the AF_WANPIPE socket for more data. 
+ *	   
  */
 static void tx_intr(sdla_t *card)
 {
         fr508_flags_t* flags = card->flags;
         fr_tx_buf_ctl_t* bctl;
-        struct net_device* dev = card->wandev.dev;
+        netdevice_t* dev;
         fr_channel_t* chan;
 
         if(card->hw.type == SDLA_S514){
@@ -1888,61 +2418,112 @@
 
         /* Find the structure and make it unbusy */
         dev = find_channel(card, flags->dlci);
-        chan = dev->priv;
+	if (dev == NULL){
+		printk(KERN_INFO "NO DEV IN TX Interrupt\n");	
+		goto end_of_tx_intr;
+	}
+
+        if ((chan = dev->priv) == NULL){
+		printk(KERN_INFO "NO CHAN IN TX Interrupt\n");	
+		goto end_of_tx_intr;
+	}
 
-        if(!chan->transmit_length) {
+        if(!chan->transmit_length || !chan->delay_skb) {
                 printk(KERN_INFO "%s: tx int error - transmit length zero\n",
 				card->wandev.name);
-                return;
+                goto end_of_tx_intr;
         }
 
 	/* If the 'if_send()' procedure is currently checking the 'tbusy'
 	   status, then we cannot transmit. Instead, we configure the microcode
 	   so as to re-issue this transmit interrupt at a later stage. 
 	*/
-	if (test_bit(2, (void*)&card->wandev.critical)) {
+	if (test_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical)) {
+
 		fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface;
 		bctl->flag = 0xA0;
 		dlci_interface->gen_interrupt |= FR_INTR_TXRDY;
-		printk(KERN_INFO "%s: TX Interrupt Detected busy if_send\n",card->devname);
+		return;
 
- 	} else {
+ 	}else{
         	bctl->dlci = flags->dlci;
-	        bctl->length = chan->transmit_length;
-        	sdla_poke(&card->hw, bctl->offset, chan->transmit_buffer,
-                	chan->transmit_length);
+	        bctl->length = chan->transmit_length+chan->fr_header_len;
+        	sdla_poke(&card->hw, 
+		          fr_send_hdr(card,bctl->dlci,bctl->offset), 
+			  chan->delay_skb->data,
+ 	              	  chan->delay_skb->len);
 	        bctl->flag = 0xC0;
 
 		++chan->ifstats.tx_packets;
 		++card->wandev.stats.tx_packets;
+              #if defined(LINUX_2_1) || defined(LINUX_2_4)
 		chan->ifstats.tx_bytes += chan->transmit_length;
 		card->wandev.stats.tx_bytes += chan->transmit_length;
-        	chan->transmit_length = 0;
+              #endif
 
-	        /* if any other interfaces have transmit interrupts pending, */
-		/* do not disable the global transmit interrupt */
-		if(!(-- card->u.f.tx_interrupts_pending))
-        	        flags->imask &= ~FR_INTR_TXRDY;
+		/* We must free an sk buffer, which we used
+		 * for delayed transmission; Otherwise, the sock
+		 * will run out of memory */
+                wan_dev_kfree_skb(chan->delay_skb, FREE_WRITE);
 
-		netif_wake_queue (dev);
+		chan->delay_skb = NULL;				
+        	chan->transmit_length = 0;
+
+	      #ifdef LINUX_2_4
+		dev->trans_start = jiffies;
+	      #endif
+
+	      #ifdef LINUX_2_0
+		wake_net_dev(dev);
+	      #else
+		if (is_queue_stopped(dev)){
+			/* If using API, than wakeup socket BH handler */
+			if (chan->common.usedby == API){
+				start_net_queue(dev);
+				wakeup_sk_bh(dev);
+			}else{
+				wake_net_dev(dev);
+			}
+		}
+	      #endif
 	}
+
+end_of_tx_intr:
+
+ 	/* if any other interfaces have transmit interrupts pending, 
+	 * do not disable the global transmit interrupt */
+	if(!(-- card->u.f.tx_interrupts_pending))
+       	        flags->imask &= ~FR_INTR_TXRDY;
+
+
 }
 
 
 /*============================================================================
- * Timer interrupt handler.
- FIXME: update comments as we modify the code
- * The timer interrupt is used for three purposes:
- *    1) Processing udp calls from 'fpipemon'.
- *    2) Processing update calls from /proc file system
- *    2) Reading board-level statistics for updating the proc file system.
- *    3) Sending inverse ARP request packets.
+ * timer_intr:	Timer interrupt handler.
+ *
+ * Rationale:
+ *	All commans must be executed within the timer
+ *      interrupt since no two commands should execute
+ *      at the same time.
+ *
+ * Description:
+ *	The timer interrupt is used to:
+ *    	1. Processing udp calls from 'fpipemon'.
+ *    	2. Processing update calls from /proc file system
+ *   	3. Reading board-level statistics for 
+ *         updating the proc file system.
+ *    	4. Sending inverse ARP request packets.
+ *	5. Configure a dlci/channel.
+ *	6. Unconfigure a dlci/channel. (Node only)
  */
+
 static void timer_intr(sdla_t *card)
 {
 	fr508_flags_t* flags = card->flags;
 
-        if(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UDP) {
+	/* UDP Debuging: fpipemon call */
+        if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UDP) {
 		if(card->u.f.udp_type == UDP_FPIPE_TYPE) {
                     	if(process_udp_mgmt_pkt(card)) {
 		                card->u.f.timer_int_enabled &=
@@ -1951,81 +2532,132 @@
 		}
         }
 
-	if(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UPDATE) {
+	/* /proc update call : triggered from update() */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UPDATE) {
 		fr_get_err_stats(card);
 		fr_get_stats(card);
 		card->u.f.update_comms_stats = 0;
 		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
 	}
 
+	/* Update the channel state call.  This is call is
+         * triggered by if_send() function */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UPDATE_STATE){
+		netdevice_t *dev;
+		if (card->wandev.state == WAN_CONNECTED){
+			for (dev=card->wandev.dev; dev; dev = *((netdevice_t **)dev->priv)){
+				fr_channel_t *chan = dev->priv;	
+				if (chan->common.state != WAN_CONNECTED){
+					update_chan_state(dev);
+				}
+			}
+		}
+		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE_STATE;
+	}
 
-//FIXME: Fix the dynamic IP addressing
-/*
-goto L4;
+	/* configure a dlci/channel */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_CONFIG){
+		config_fr(card);
+		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_CONFIG;
+	}
 
-	// Used to send inarp request at given interval 
-	if (card->wandev.state == WAN_CONNECTED) {
-        	int num_remaining = 0;
-
-		dev = card->wandev.dev;
-		while (dev) {
-                	fr_channel_t *chan = dev->priv;
-
-                        if (chan->inarp == INARP_REQUEST &&
-                        	chan->state == WAN_CONNECTED) {
-                                num_remaining++;
-
-                                if ((jiffies - chan->inarp_tick) > (chan->inarp_interval * HZ)) {
-                                	send_inarp_request(card,dev);
-                                	chan->inarp_tick = jiffies;
-                                }
+	/* unconfigure a dlci/channel */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_UNCONFIG){
+		unconfig_fr(card);
+		card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UNCONFIG;
+	}
+
+	
+	/* Transmit ARP packets */
+	if (card->u.f.timer_int_enabled & TMR_INT_ENABLED_ARP){
+		int i=0;
+		netdevice_t *dev;
+
+		if (card->u.f.arp_dev == NULL)
+			card->u.f.arp_dev = card->wandev.dev;
+
+		dev = card->u.f.arp_dev;
+
+		for (;;){ 
+
+			fr_channel_t *chan = dev->priv;
+
+			/* If the interface is brought down cancel sending In-ARPs */
+			if (!(dev->flags&IFF_UP)){
+				clear_bit(0,&chan->inarp_ready);	
+			}
+
+			if (test_bit(0,&chan->inarp_ready)){
+
+				if (check_tx_status(card,dev)){
+					set_bit(ARP_CRIT,&card->wandev.critical);
+					break;
+				}
+
+				if (!send_inarp_request(card,dev)){
+					trigger_fr_arp(dev);
+					chan->inarp_tick = jiffies;
+				}
+
+				clear_bit(0,&chan->inarp_ready);
+				dev = move_dev_to_next(card,dev);
+				break;
+			}
+			dev = move_dev_to_next(card,dev);
+
+			if (++i == card->wandev.new_if_cnt){
+				card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_ARP;
+				break;
 			}
-			dev = chan->slave;
-		}
-		if (!num_remaining) {   // no more to process 
-                        flags->imask &= ~FR_INTR_TIMER;
 		}
+		card->u.f.arp_dev = dev;
 	}
-L4:
-	;
-*/
+
         if(!card->u.f.timer_int_enabled)
                 flags->imask &= ~FR_INTR_TIMER;
 }
 
 
 /*============================================================================
- * Spurious interrupt handler.
- * o print a warning
- * o 
+ * spur_intr:	Spurious interrupt handler.
+ * 
+ * Description:
+ *  	We don't know this interrupt.
+ *      Print a warning.
  */
+
 static void spur_intr (sdla_t* card)
 {
-	printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
+	if (net_ratelimit()){ 
+		printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
+	}
 }
 
+
 //FIXME: Fix the IPX in next version
 /*===========================================================================
  *  Return 0 for non-IPXWAN packet
  *         1 for IPXWAN packet or IPX is not enabled!
  *  FIXME: Use a IPX structure here not offsets
  */
-static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number)
+static int handle_IPXWAN(unsigned char *sendpacket, 
+			 char *devname, unsigned char enable_IPX, 
+			 unsigned long network_number)
 {
 	int i;
 
-	if( sendpacket[1] == 0x00 &&
-	    sendpacket[2] == 0x80 &&
-	    sendpacket[6] == 0x81 &&
-	    sendpacket[7] == 0x37) { 
+	if( sendpacket[1] == 0x00 && sendpacket[2] == 0x80 &&
+	    sendpacket[6] == 0x81 && sendpacket[7] == 0x37) { 
 
 		/* It's an IPX packet */
-		if(!enable_IPX) {
+		if (!enable_IPX){
 			/* Return 1 so we don't pass it up the stack. */
 			//FIXME: Take this out when IPX is fixed
-			printk (KERN_INFO 
+			if (net_ratelimit()){ 
+				printk (KERN_INFO 
 				"%s: WARNING: Unsupported IPX packet received and dropped\n",
 					devname);
+			}
 			return 1;
 		}
 	} else {
@@ -2033,39 +2665,33 @@
 		return 0;
 	}
 
-	if( sendpacket[24] == 0x90 &&
-	    sendpacket[25] == 0x04)
-	{
+	if( sendpacket[24] == 0x90 && sendpacket[25] == 0x04){
 		/* It's IPXWAN */
 
-		if( sendpacket[10] == 0x02 &&
-		    sendpacket[42] == 0x00)
-		{
+		if( sendpacket[10] == 0x02 && sendpacket[42] == 0x00){
+
 			/* It's a timer request packet */
-			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
+			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",
+					devname);
 
 			/* Go through the routing options and answer no to every
 			 * option except Unnumbered RIP/SAP
 			 */
-			for(i = 49; sendpacket[i] == 0x00; i += 5)
-			{
+			for(i = 49; sendpacket[i] == 0x00; i += 5){
 				/* 0x02 is the option for Unnumbered RIP/SAP */
-				if( sendpacket[i + 4] != 0x02)
-				{
+				if( sendpacket[i + 4] != 0x02){
 					sendpacket[i + 1] = 0;
 				}
 			}
 
 			/* Skip over the extended Node ID option */
-			if( sendpacket[i] == 0x04 )
-			{
+			if( sendpacket[i] == 0x04 ){
 				i += 8;
 			}
 
 			/* We also want to turn off all header compression opt.
 			 */
-			for(; sendpacket[i] == 0x80 ;)
-			{
+			for(; sendpacket[i] == 0x80 ;){
 				sendpacket[i + 1] = 0;
 				i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
 			}
@@ -2073,12 +2699,15 @@
 			/* Set the packet type to timer response */
 			sendpacket[42] = 0x01;
 
-			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
-		}
-		else if( sendpacket[42] == 0x02 )
-		{
+			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",
+					devname);
+
+		} else if( sendpacket[42] == 0x02 ){
+
 			/* This is an information request packet */
-			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
+			printk(KERN_INFO 
+				"%s: Received IPXWAN Information Request packet\n",
+						devname);
 
 			/* Set the packet type to information response */
 			sendpacket[42] = 0x03;
@@ -2103,10 +2732,10 @@
 				sendpacket[i] = 0;
 			}
 
-			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
-		}
-		else
-		{
+			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",
+					devname);
+		} else {
+
 			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
 			return 0;
 		}
@@ -2128,86 +2757,166 @@
 	return 0;
 }
 /*============================================================================
- * Process Route.
- * This routine is called as a polling routine to dynamically add/delete routes
- * negotiated by inverse ARP.  It is in this "task" because we don't want routes
- * to be added while in interrupt context.
-*/
+ * process_route
+ * 
+ * Rationale:
+ *	If the interface goes down, or we receive an ARP request,
+ *      we have to change the network interface ip addresses.
+ * 	This cannot be done within the interrupt.
+ *
+ * Description:
+ *
+ * 	This routine is called as a polling routine to dynamically 
+ *	add/delete routes negotiated by inverse ARP.  It is in this 
+ *    	"task" because we don't want routes to be added while in 
+ *      interrupt context.
+ *
+ * Usage:
+ *	This function is called by fr_poll() polling funtion.
+ */
 
-static void process_route (sdla_t* card)
+static void process_route (netdevice_t *dev)
 {
-	struct net_device* dev;
-	struct in_device *in_dev;
-    	struct rtentry route;
-        int err = 0;
-        mm_segment_t fs;
+	fr_channel_t *chan = dev->priv;
+	sdla_t *card = chan->card;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 
-	/* Dynamic Route adding/removing */
-	dev = card->wandev.dev;
-	while (dev) {
-		fr_channel_t *chan = dev->priv;
-
-		if (chan->route_flag == ADD_ROUTE   ||
-		    chan->route_flag == REMOVE_ROUTE ) {
-			fs = get_fs();
-		
-			in_dev = dev->ip_ptr;
-
-			if( in_dev != NULL && in_dev->ifa_list != NULL) {
-				memset(&route, 0, sizeof(route));
-				route.rt_dev   = dev->name;
-				route.rt_flags = 0;
-
-				((struct sockaddr_in *) &(route.rt_dst)) ->
-					sin_addr.s_addr=in_dev->ifa_list->ifa_address;
-				((struct sockaddr_in *) &(route.rt_dst)) ->
-					sin_family = AF_INET;
-				((struct sockaddr_in *) &(route.rt_genmask)) ->
-					sin_addr.s_addr = 0xFFFFFFFF;
-				((struct sockaddr_in *) &(route.rt_genmask)) ->
-					sin_family = AF_INET;
-
-				switch(chan->route_flag) {
-
-				case ADD_ROUTE:
-					set_fs(get_ds());     /* get user space block */
-					err = ip_rt_ioctl( SIOCADDRT, &route);	
-					set_fs(fs);	      /* restore old block */
-
-					if (err) {
-						printk(KERN_INFO "%s: Adding of route failed.  Error: %d\n", card->devname,err);
-						printk(KERN_INFO "%s: Address: %s\n",
-						       chan->name,
-						       in_ntoa(in_dev->ifa_list->ifa_address) );
-					} else {
-						chan->route_flag = ROUTE_ADDED;
-					}
-					break;
+	struct ifreq if_info;
+	struct sockaddr_in *if_data;
+	mm_segment_t fs = get_fs();
+	u32 ip_tmp;
+	int err;
 
-				case REMOVE_ROUTE:
-					set_fs(get_ds());     /* get user space block */
-					err = ip_rt_ioctl( SIOCDELRT, &route);
-					set_fs(fs);	      /* restore old block */
-
-					if (err) {
-						printk(KERN_INFO "%s: Deleting of route failed.  Error: %d\n", card->devname,err);
-						printk(KERN_INFO "%s: Address: %s\n",
-						       dev->name,in_ntoa(in_dev->ifa_list->ifa_address) );
-					} else {
-						printk(KERN_INFO "%s: Removed route.\n",
-						       chan->name);
-						chan->route_flag = NO_ROUTE;
-					}
-					break;
-				} /* Case Statement */
+
+	switch(chan->route_flag){
+
+	case ADD_ROUTE:
+				
+		/* Set remote addresses */
+		memset(&if_info, 0, sizeof(if_info));
+		strcpy(if_info.ifr_name, dev->name);
+
+		set_fs(get_ds());     /* get user space block */ 
+		
+		if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
+		if_data->sin_addr.s_addr = chan->ip_remote;
+		if_data->sin_family = AF_INET;
+		err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
+
+		set_fs(fs);           /* restore old block */
+
+		if (err) {
+
+			printk(KERN_INFO 
+				"%s: Route Add failed.  Error: %d\n", 
+					card->devname,err);
+			printk(KERN_INFO "%s: Address: %s\n",
+				chan->name, in_ntoa(chan->ip_remote));
+
+		}else {
+			printk(KERN_INFO "%s: Route Added Successfully: %s\n",
+				card->devname,in_ntoa(chan->ip_remote));
+			chan->route_flag = ROUTE_ADDED;
+		}
+		break;
+
+	case REMOVE_ROUTE:
+
+		/* Set remote addresses */
+		memset(&if_info, 0, sizeof(if_info));
+		strcpy(if_info.ifr_name, dev->name);
+
+		ip_tmp = get_ip_address(dev,WAN_POINTOPOINT_IP);	
+
+		set_fs(get_ds());     /* get user space block */ 
+		
+		if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
+		if_data->sin_addr.s_addr = 0;
+		if_data->sin_family = AF_INET;
+		err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
+
+		set_fs(fs);    
+		
+		if (err) {
+
+			printk(KERN_INFO 
+				"%s: Deleting of route failed.  Error: %d\n", 
+					card->devname,err);
+			printk(KERN_INFO "%s: Address: %s\n",
+				dev->name,in_ntoa(chan->ip_remote) );
+
+		} else {
+
+			printk(KERN_INFO "%s: Route Removed Sucessfuly: %s\n", 
+				card->devname,in_ntoa(ip_tmp));
+			chan->route_flag = NO_ROUTE;
+		}
+		break;
+
+	} /* Case Statement */
+
+#else
+        /* Dynamic Route adding/removing */
+	struct rtentry route;
+        int err = 0;
+        unsigned long fs = get_fs();
+
+	memset(&route, 0, sizeof(route));
+	route.rt_dev   = dev->name;
+	route.rt_flags = 0;
+
+	((struct sockaddr_in *) &(route.rt_dst)) ->
+					sin_addr.s_addr=dev->pa_dstaddr;
+	((struct sockaddr_in *) &(route.rt_dst)) ->
+						sin_family = AF_INET;
+	((struct sockaddr_in *) &(route.rt_genmask)) ->
+				sin_addr.s_addr = 0xFFFFFFFF;
+	((struct sockaddr_in *) &(route.rt_genmask)) ->
+						sin_family = AF_INET;
+	switch(chan->route_flag) {
+
+	case ADD_ROUTE:
+
+		set_fs(get_ds());     /* get user space block */
+		err = ip_rt_new(&route);
+		set_fs(fs);           /* restore old block */
+
+		if (err) {
+			printk(KERN_INFO "%s: Adding of route failed.  Error: %d\n", 
+						card->devname,err);
+			printk(KERN_INFO "%s: Address: %s\n",
+				chan->name, in_ntoa(dev->pa_dstaddr) );
 			}
-		} /* If ADD/DELETE ROUTE */
+		else {
+			chan->route_flag = ROUTE_ADDED;
+		}
+		break;
+
+	case REMOVE_ROUTE:
+
+		set_fs(get_ds());     /* get user space block */
+		err = ip_rt_kill(&route);
+		set_fs(fs);           /* restore old block */
 
-		dev = chan->slave;
-	}  /* Device 'While' Loop */
+		if (err) {
+
+		     printk(KERN_INFO "%s: Deleting of route failed.  Error: %d\n", 
+				card->devname,err);
+		     printk(KERN_INFO "%s: Address: %s\n",
+				dev->name,in_ntoa(dev->pa_dstaddr) );
+		} else {
+
+		     printk(KERN_INFO "%s: Removed route.\n",
+			((fr_channel_t*)dev->priv)->name);
+		     chan->route_flag = NO_ROUTE;
+
+		}
+		break;
+	}
+
+#endif	
 
-	card->poll = NULL;
 }
 
 
@@ -2264,6 +2973,12 @@
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	
 	} while (err && retry-- && fr_event(card, err, mbox));
+
+	/*NC Oct 12 2000 */
+	if (err != CMD_OK){
+		printk(KERN_ERR "%s: Frame Relay Configuration Failed: rc=0x%x\n",
+				card->devname,err);
+	}
 	
 	return err;
 }
@@ -2338,32 +3053,38 @@
 }
 
 /*============================================================================
- * Disable communications. 
+ * fr_comm_disable 
+ *
+ * Warning: This functin is called by the shutdown() procedure. It is void
+ *          since dev->priv are has already been deallocated and no
+ *          error checking is possible using fr_event() function.
  */
-static int fr_comm_disable (sdla_t* card)
+static void fr_comm_disable (sdla_t* card)
 {
 	fr_mbox_t* mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
 
+	do {
+	mbox->cmd.command = FR_SET_MODEM_STATUS;
+	mbox->cmd.length = 1;
+	mbox->data[0] = 0;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	} while (err && retry--);
+	
+	retry = MAX_CMD_RETRY;
+	
 	do
 	{
 		mbox->cmd.command = FR_COMM_DISABLE;
 		mbox->cmd.length = 0;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- && fr_event(card, err, mbox));
+	} while (err && retry--);
+
+	return;
+}
 
-	retry = MAX_CMD_RETRY;
 
-	do {
-	mbox->cmd.command = FR_SET_MODEM_STATUS;
-	mbox->cmd.length = 1;
-	mbox->data[0] = 0;
-	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- && fr_event(card, err, mbox));
-	
-	return err;
-}
 
 /*============================================================================
  * Get communications error statistics. 
@@ -2518,9 +3239,58 @@
 	return err;
 }
 
+
+static unsigned int fr_send_hdr (sdla_t*card, int dlci, unsigned int offset)
+{
+	netdevice_t *dev = find_channel(card,dlci);	
+	fr_channel_t *chan;
+
+	if (!dev || !(chan=dev->priv))
+		return offset;
+	
+	if (chan->fr_header_len){
+		sdla_poke(&card->hw, offset, chan->fr_header, chan->fr_header_len);
+	}
+	
+	return offset+chan->fr_header_len;
+}
+
 /*============================================================================
  * Send a frame on a selected DLCI.  
  */
+static int fr_send_data_header (sdla_t* card, int dlci, unsigned char attr, int len,
+	void *buf, unsigned char hdr_len)
+{
+	fr_mbox_t* mbox = card->mbox + 0x800;
+	int retry = MAX_CMD_RETRY;
+	int err;
+
+	do
+	{
+		mbox->cmd.dlci    = dlci;
+		mbox->cmd.attr    = attr;
+		mbox->cmd.length  = len+hdr_len;
+		mbox->cmd.command = FR_WRITE;
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	} while (err && retry-- && fr_event(card, err, mbox));
+
+	if (!err) {
+		fr_tx_buf_ctl_t* frbuf;
+ 
+               	if(card->hw.type == SDLA_S514)
+			frbuf = (void*)(*(unsigned long*)mbox->data +
+                        	card->hw.dpmbase);
+		else
+			frbuf = (void*)(*(unsigned long*)mbox->data -
+                        	FR_MB_VECTOR + card->hw.dpmbase);
+
+		sdla_poke(&card->hw, fr_send_hdr(card,dlci,frbuf->offset), buf, len);
+		frbuf->flag = 0x01;
+	}
+
+	return err;
+}
+
 static int fr_send (sdla_t* card, int dlci, unsigned char attr, int len,
 	void *buf)
 {
@@ -2538,7 +3308,6 @@
 	} while (err && retry-- && fr_event(card, err, mbox));
 
 	if (!err) {
-
 		fr_tx_buf_ctl_t* frbuf;
  
                	if(card->hw.type == SDLA_S514)
@@ -2555,6 +3324,7 @@
 	return err;
 }
 
+
 /****** Firmware Asynchronous Event Handlers ********************************/
 
 /*============================================================================
@@ -2577,46 +3347,41 @@
 
 		case FRRES_CHANNEL_DOWN:
 			{
-			struct net_device *dev;
+			netdevice_t *dev;
 
 			/* Remove all routes from associated DLCI's */
-			dev = card->wandev.dev;
-			while (dev) {
+			for (dev = card->wandev.dev; dev; dev = *((netdevice_t **)dev->priv)) {
 				fr_channel_t *chan = dev->priv;
 				if (chan->route_flag == ROUTE_ADDED) {
 					chan->route_flag = REMOVE_ROUTE;
-					card->poll = &process_route;
 				}
 
 				if (chan->inarp == INARP_CONFIGURED) {
 					chan->inarp = INARP_REQUEST;
 				}
 
-				dev = chan->slave;
+				/* If the link becomes disconnected then,
+                                 * all channels will be disconnected
+                                 * as well.
+                                 */
+				set_chan_state(dev,WAN_DISCONNECTED);
 			}
-
+				
 			wanpipe_set_state(card, WAN_DISCONNECTED);
 			return 1;
 			}
 
 		case FRRES_CHANNEL_UP:
 			{
-			struct net_device *dev;
-			int num_requests = 0;
+			netdevice_t *dev;
 
-			/* Remove all routes from associated DLCI's */
-			dev = card->wandev.dev;
-			while (dev) {
-				fr_channel_t *chan = dev->priv;	
-				if( chan->inarp == INARP_REQUEST ){
-					num_requests++;
-					chan->inarp_tick = jiffies;
-				}
-				dev = chan->slave;
+			/* FIXME: Only startup devices that are on the list */
+			
+			for (dev = card->wandev.dev; dev; dev = *((netdevice_t **)dev->priv)) {
+				
+				set_chan_state(dev,WAN_CONNECTED);
 			}
 
-			/* Allow timer interrupts */
-			if (num_requests) flags->imask |= 0x20;	
 			wanpipe_set_state(card, WAN_CONNECTED);
 			return 1;
 			}
@@ -2644,8 +3409,10 @@
  
 		case FRRES_CIR_OVERFLOW:
 			break;
+			
 		case FRRES_BUFFER_OVERFLOW:
 			break; 
+			
 		default:
 			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n"
 				, card->devname, mbox->cmd.command, event);
@@ -2684,13 +3451,13 @@
 	dlci_status_t* status = (void*)mbox->data;
 	int cnt = mbox->cmd.length / sizeof(dlci_status_t);
 	fr_channel_t *chan;
-	struct net_device* dev2;
+	netdevice_t* dev2;
 	
 
 	for (; cnt; --cnt, ++status) {
 
 		unsigned short dlci= status->dlci;
-		struct net_device* dev = find_channel(card, dlci);
+		netdevice_t* dev = find_channel(card, dlci);
 		
 		if (dev == NULL){
 			printk(KERN_INFO 
@@ -2707,7 +3474,7 @@
 					"%s: DLCI %u is inactive!\n",
 					card->devname, dlci);
 
-				if (dev && netif_running(dev))
+				if (dev && is_dev_running(dev))
 					set_chan_state(dev, WAN_DISCONNECTED);
 			}
 	
@@ -2717,12 +3484,14 @@
 					"%s: DLCI %u has been deleted!\n",
 					card->devname, dlci);
 
-				if (dev && netif_running(dev)) {
+				if (dev && is_dev_running(dev)){
+
 					fr_channel_t *chan = dev->priv;
 
 					if (chan->route_flag == ROUTE_ADDED) {
 						chan->route_flag = REMOVE_ROUTE;
-						card->poll = &process_route;
+						/* The state change will trigger
+                                                 * the fr polling routine */
 					}
 
 					if (chan->inarp == INARP_CONFIGURED) {
@@ -2740,16 +3509,15 @@
 				   DLCI(s) when they become active.
 			 	*/ 
 				chan->dlci_configured = DLCI_CONFIG_PENDING;
-			
-				if (dev && netif_running(dev))
-					set_chan_state(dev, WAN_CONNECTED);
+	
+				set_chan_state(dev, WAN_CONNECTED);
 		
 			}
 		}
 	}
 	
-	dev2 = card->wandev.dev;
-	while (dev2) {
+	for (dev2 =card->wandev.dev; dev2; dev2 = *((netdevice_t **)dev2->priv)){
+		
 		chan = dev2->priv;
 	
 		if (chan->dlci_configured == DLCI_CONFIG_PENDING) {
@@ -2758,7 +3526,6 @@
 			}
 		}
 
-		dev2 = chan->slave;
 	}
 	return 1;
 }
@@ -2767,8 +3534,7 @@
 static int fr_init_dlci (sdla_t *card, fr_channel_t *chan)
 {
 	fr_dlc_conf_t cfg;
-	fr508_flags_t* flags = card->flags;
-
+	
 	memset(&cfg, 0, sizeof(cfg));
 
 	if ( chan->cir_status == CIR_DISABLED) {
@@ -2786,22 +3552,16 @@
 	}
 	
 	if (fr_dlci_configure( card, &cfg , chan->dlci)){
-	printk(KERN_INFO 
-		"%s: DLCI Configure failed for %d\n",
-			card->devname, chan->dlci);
+		printk(KERN_INFO 
+			"%s: DLCI Configure failed for %d\n",
+				card->devname, chan->dlci);
 		return 1;	
 	}
 	
 	chan->dlci_configured = DLCI_CONFIGURED;
 
-	/* Allow timer interrupts */
-	if( chan->inarp == INARP_REQUEST && card->wandev.state == WAN_CONNECTED) {
-		chan->inarp_tick = jiffies;
-		flags->imask |= 0x20;	
-	}	
-
 	/* Read the interface byte mapping into the channel 
-	   structure.
+	 * structure.
 	 */
 	read_DLCI_IB_mapping( card, chan );
 
@@ -2812,7 +3572,7 @@
 /*============================================================================
  * Update channel state. 
  */
-static int update_chan_state (struct net_device* dev)
+static int update_chan_state (netdevice_t* dev)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
@@ -2828,14 +3588,25 @@
 	} while (err && retry-- && fr_event(card, err, mbox));
 
 	if (!err) {
-
+		
 		unsigned short* list = (void*)mbox->data;
 		int cnt = mbox->cmd.length / sizeof(short);
-
+		
+		err=1;
+		
 		for (; cnt; --cnt, ++list) {
 
 			if (*list == chan->dlci) {
  				set_chan_state(dev, WAN_CONNECTED);
+
+
+				/* May 23 2000. NC
+				 * When a dlci is added or restarted,
+                                 * the dlci_int_interface pointer must
+				 * be reinitialized.  */
+				if (!chan->dlci_int_interface){
+					err=fr_init_dlci (card,chan);
+				}
 				break;
 			}
 		}
@@ -2847,16 +3618,12 @@
 /*============================================================================
  * Set channel state.
  */
-static void set_chan_state (struct net_device* dev, int state)
+static void set_chan_state (netdevice_t* dev, int state)
 {
 	fr_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
 
-	if (chan->state != state) {
+	if (chan->common.state != state) {
 
 		switch (state) {
 
@@ -2864,6 +3631,12 @@
 				printk(KERN_INFO
 					"%s: Interface %s: DLCI %d connected\n",
 					card->devname, dev->name, chan->dlci);
+
+				/* If the interface was previoulsy down,
+                                 * bring it up, since the channel is active */
+
+				trigger_fr_poll (dev);
+				trigger_fr_arp  (dev);
 				break;
 
 			case WAN_CONNECTING:
@@ -2876,20 +3649,29 @@
 				printk (KERN_INFO 
 				    "%s: Interface %s: DLCI %d disconnected!\n",
 					card->devname, dev->name, chan->dlci);
+			
+				/* If the interface is up, bring it down,
+                                 * since the channel is now disconnected */
+				trigger_fr_poll (dev);
 				break;
 		}
 
-		chan->state = state;
+		chan->common.state = state;
 	}
 
 	chan->state_tick = jiffies;
-	restore_flags(flags);
 }
 
 /*============================================================================
  * Find network device by its channel number.
+ *
+ * We need this critical flag because we change
+ * the dlci_to_dev_map outside the interrupt.
+ *
+ * NOTE: del_if() functions updates this array, it uses
+ *       the spin locks to avoid corruption.
  */
-static struct net_device* find_channel (sdla_t* card, unsigned dlci)
+static netdevice_t* find_channel (sdla_t* card, unsigned dlci)
 {
 	if(dlci > HIGHEST_VALID_DLCI)
 		return NULL;
@@ -2945,9 +3727,15 @@
                                 struct sk_buff *skb, int dlci)
 {
         int udp_pkt_stored = 0;
-
-        if(!card->u.f.udp_pkt_lgth &&(skb->len <= MAX_LGTH_UDP_MGNT_PKT)){
-                card->u.f.udp_pkt_lgth = skb->len;
+	
+	netdevice_t *dev=find_channel(card,dlci);
+	fr_channel_t *chan;
+	
+	if (!dev || !(chan=dev->priv))
+		return 1;
+	
+        if(!card->u.f.udp_pkt_lgth && (skb->len <= MAX_LGTH_UDP_MGNT_PKT)){
+                card->u.f.udp_pkt_lgth = skb->len + chan->fr_header_len;
                 card->u.f.udp_type = udp_type;
                 card->u.f.udp_pkt_src = udp_pkt_src;
                 card->u.f.udp_dlci = dlci;
@@ -2960,8 +3748,12 @@
 							dlci);
 	}
 
-        dev_kfree_skb(skb);
-
+        if(udp_pkt_src == UDP_PKT_FRM_STACK){
+                wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+                wan_dev_kfree_skb(skb, FREE_READ);
+	}
+		
         return(udp_pkt_stored);
 }
 
@@ -2982,7 +3774,7 @@
 	int err;
 	struct timeval tv;
 	int udp_mgmt_req_valid = 1;
-        struct net_device* dev;
+        netdevice_t* dev;
         fr_channel_t* chan;
         fr_udp_pkt_t *fr_udp_pkt;
 	unsigned short num_trc_els;
@@ -2995,7 +3787,14 @@
 
 	/* Find network interface for this packet */
 	dev = find_channel(card, dlci);
-        chan = dev->priv;
+	if (!dev){
+		card->u.f.udp_pkt_lgth = 0;
+		return 1;
+	}
+        if ((chan = dev->priv) == NULL){
+		card->u.f.udp_pkt_lgth = 0;
+		return 1;
+	}
 
 	/* If the UDP packet is from the network, we are going to have to 
 	   transmit a response. Before doing so, we must check to see that
@@ -3003,39 +3802,49 @@
 	   that we are not already in a 'delayed transmit' state.
 	*/
 	if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-		if (test_bit(0, (void*)&card->wandev.critical) ||
-			test_bit(2, (void*)&card->wandev.critical)) {
-			return 0;
-  		}
-		if((netif_queue_stopped(dev)) || (card->u.f.tx_interrupts_pending)) {
-			return 0;
+		if (check_tx_status(card,dev)){
+			card->u.f.udp_pkt_lgth = 0;
+			return 1;
 		}
         }
 
         fr_udp_pkt = (fr_udp_pkt_t *)card->u.f.udp_pkt_data;
 
-	switch(fr_udp_pkt->cblock.command) {
+	if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+	
+		switch(fr_udp_pkt->cblock.command) {
 
-		case FPIPE_ENABLE_TRACING:
-		case FPIPE_DISABLE_TRACING:
-		case FPIPE_GET_TRACE_INFO:
-		case FR_SET_FT1_MODE:
-			if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-                    		chan->drvstats_gen.
-					UDP_PIPE_mgmt_direction_err ++;
+			case FR_READ_MODEM_STATUS:
+			case FR_READ_STATUS:
+			case FPIPE_ROUTER_UP_TIME:
+			case FR_READ_ERROR_STATS:
+			case FPIPE_DRIVER_STAT_GEN:
+			case FR_READ_STATISTICS:
+			case FR_READ_ADD_DLC_STATS:
+			case FR_READ_CONFIG:
+			case FR_READ_CODE_VERSION:
+				udp_mgmt_req_valid = 1;
+				break;
+			default:
 				udp_mgmt_req_valid = 0;
 				break;
-			}
-
-		default:
-			break;
-       	}
+		}
+	}
 
 	if(!udp_mgmt_req_valid) {
 		/* set length to 0 */
 		fr_udp_pkt->cblock.length = 0;
 		/* set return code */
 		fr_udp_pkt->cblock.result = 0xCD; 
+		
+		chan->drvstats_gen.UDP_PIPE_mgmt_direction_err ++;
+
+		if (net_ratelimit()){	
+			printk(KERN_INFO 
+			"%s: Warning, Illegal UDP command attempted from network: %x\n",
+			card->devname,fr_udp_pkt->cblock.command);
+		}
+		
 	} else {   
            
 		switch(fr_udp_pkt->cblock.command) {
@@ -3074,7 +3883,7 @@
 				/* Calculate the maximum trace data area in */
 				/* the UDP packet */
 				card->u.f.trc_bfr_space=(MAX_LGTH_UDP_MGNT_PKT -
-					sizeof(fr_encap_hdr_t) -
+					//sizeof(fr_encap_hdr_t) -
 					sizeof(ip_pkt_t) -
 					sizeof(udp_pkt_t) -
 					sizeof(wp_mgmt_t) -
@@ -3204,7 +4013,7 @@
                 case FPIPE_FT1_READ_STATUS:
 			sdla_peek(&card->hw, 0xF020,
 				&fr_udp_pkt->data[0x00] , 2);
-			fr_udp_pkt->cblock.length = 2;
+			fr_udp_pkt->cblock.length = mbox->cmd.length = 2;
 			fr_udp_pkt->cblock.result = 0;
 			break;
 
@@ -3220,44 +4029,32 @@
 						chan->router_start_time;
     	                *(unsigned long *)&fr_udp_pkt->data =
     				chan->router_up_time;	
-			mbox->cmd.length = 4;
+			mbox->cmd.length = fr_udp_pkt->cblock.length = 4;
+			fr_udp_pkt->cblock.result = 0;
 			break;
 
-
-		case FR_FT1_STATUS_CTRL:
-			if(fr_udp_pkt->data[0] == 1) {
-				if(rCount++ != 0 ){
-					fr_udp_pkt->cblock.result = 0;
-					mbox->cmd.length = 1;
-					break;
-				} 
-			}
-           
-			/* Disable FT1 MONITOR STATUS */
-                        if(fr_udp_pkt->data[0] == 0) {
-				if( --rCount != 0) {
-                                        fr_udp_pkt->cblock.result = 0;
-					mbox->cmd.length = 1;
-					break;
-				} 
-			}  
-
 		case FPIPE_DRIVER_STAT_IFSEND:
 			memcpy(fr_udp_pkt->data,
 				&chan->drvstats_if_send.if_send_entry,
 				sizeof(if_send_stat_t));
-			mbox->cmd.length = sizeof(if_send_stat_t);	
+			mbox->cmd.length = fr_udp_pkt->cblock.length =sizeof(if_send_stat_t);	
+			fr_udp_pkt->cblock.result = 0;
 			break;
 	
 		case FPIPE_DRIVER_STAT_INTR:
+
 			memcpy(fr_udp_pkt->data,
                                 &card->statistics.isr_entry,
                                 sizeof(global_stats_t));
+
                         memcpy(&fr_udp_pkt->data[sizeof(global_stats_t)],
                                 &chan->drvstats_rx_intr.rx_intr_no_socket,
                                 sizeof(rx_intr_stat_t));
-			mbox->cmd.length = sizeof(global_stats_t) +
+
+			mbox->cmd.length = fr_udp_pkt->cblock.length = 
+					sizeof(global_stats_t) +
 					sizeof(rx_intr_stat_t);
+			fr_udp_pkt->cblock.result = 0;
 			break;
 
 		case FPIPE_DRIVER_STAT_GEN:
@@ -3268,13 +4065,34 @@
                         memcpy(&fr_udp_pkt->data[sizeof(pipe_mgmt_stat_t)],
                                &card->statistics, sizeof(global_stats_t));
 
-                        fr_udp_pkt->cblock.result = 0;
-                        fr_udp_pkt->cblock.length = sizeof(global_stats_t)+
+                        mbox->cmd.length = fr_udp_pkt->cblock.length = sizeof(global_stats_t)+
                                                      sizeof(rx_intr_stat_t);
-                        mbox->cmd.length = fr_udp_pkt->cblock.length;
+			fr_udp_pkt->cblock.result = 0;
                         break;
-		
+
+
+		case FR_FT1_STATUS_CTRL:
+			if(fr_udp_pkt->data[0] == 1) {
+				if(rCount++ != 0 ){
+					fr_udp_pkt->cblock.result = 0;
+					mbox->cmd.length = 1;
+					break;
+				} 
+			}
+           
+			/* Disable FT1 MONITOR STATUS */
+                        if(fr_udp_pkt->data[0] == 0) {
+				if( --rCount != 0) {
+                                        fr_udp_pkt->cblock.result = 0;
+					mbox->cmd.length = 1;
+					break;
+				} 
+			}  
+			goto udp_mgmt_dflt;
+
+			
 		default:
+udp_mgmt_dflt:
  			do {
 				memcpy(&mbox->cmd,
 					&fr_udp_pkt->cblock.command,
@@ -3312,12 +4130,19 @@
         len = reply_udp(card->u.f.udp_pkt_data, mbox->cmd.length);
 
         if(udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-		
-                err = fr_send(card, dlci, 0, len, card->u.f.udp_pkt_data);
-		if (err) 
+
+		chan->fr_header_len=2;
+		chan->fr_header[0]=Q922_UI;
+		chan->fr_header[1]=NLPID_IP;
+			
+		err = fr_send_data_header(card, dlci, 0, len, 
+			card->u.f.udp_pkt_data,chan->fr_header_len);
+		if (err){ 
 			chan->drvstats_gen.UDP_PIPE_mgmt_adptr_send_passed ++;
-		else
+		}else{
 			chan->drvstats_gen.UDP_PIPE_mgmt_adptr_send_failed ++;
+		}
+		
 	} else {
 		/* Allocate socket buffer */
 		if((new_skb = dev_alloc_skb(len)) != NULL) {
@@ -3326,22 +4151,12 @@
 			buf = skb_put(new_skb, len);
 			memcpy(buf, card->u.f.udp_pkt_data, len);
         
-			/* Decapsulate packet and pass it up the protocol 
-			   stack */
+			chan->drvstats_gen.
+				UDP_PIPE_mgmt_passed_to_stack ++;
 			new_skb->dev = dev;
-			buf = skb_pull(new_skb, 1);  /* remove hardware header*/
-			
-			if(!wanrouter_type_trans(new_skb, dev)) {
-
-				chan->drvstats_gen.
-					UDP_PIPE_mgmt_not_passed_to_stack ++;
-				/* can't decapsulate packet */
-				dev_kfree_skb(new_skb);
-			} else {
-				chan->drvstats_gen.
-					UDP_PIPE_mgmt_passed_to_stack ++;
-				netif_rx(new_skb);
-                	}
+			new_skb->protocol = htons(ETH_P_IP);
+			new_skb->mac.raw = new_skb->data;
+			netif_rx(new_skb);
             	
 		} else {
 			chan->drvstats_gen.UDP_PIPE_mgmt_no_socket ++;
@@ -3360,15 +4175,19 @@
  * Send Inverse ARP Request
  */
 
-int send_inarp_request(sdla_t *card, struct net_device *dev)
+int send_inarp_request(sdla_t *card, netdevice_t *dev)
 {
+	int err=0;
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
 	arphdr_1490_t *ArpPacket;
 	arphdr_fr_t *arphdr;
 	fr_channel_t *chan = dev->priv;
 	struct in_device *in_dev;
 
 	in_dev = dev->ip_ptr;
-	
+
 	if(in_dev != NULL ) {	
 
 		ArpPacket = kmalloc(sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t), GFP_ATOMIC);
@@ -3396,14 +4215,64 @@
 		arphdr->ar_tha = 0; 		/* dst HW DLCI - Doesn't matter */
 		arphdr->ar_tip = 0;		/* Remote Address -- what we want */
 
-		printk(KERN_INFO "%s: Sending InARP request on DLCI %d.\n", card->devname, chan->dlci);
-                fr_send(card, chan->dlci, 0,
-		   sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t),
-		   (void *)ArpPacket);
+		err = fr_send(card, chan->dlci, 0, sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t),
+		   			(void *)ArpPacket);
+
+		if (!err){
+			printk(KERN_INFO "\n%s: Sending InARP request on DLCI %d.\n", 
+				card->devname, chan->dlci);
+			clear_bit(ARP_CRIT,&card->wandev.critical);
+		}
+
 		kfree(ArpPacket);
+	}else{
+		printk(KERN_INFO "%s: INARP ERROR: %s doesn't have a local IP address!\n",
+				card->devname,dev->name);
+		return 1;
 	}
 
-	return 1;
+#else
+        arphdr_1490_t *ArpPacket;
+        arphdr_fr_t *arphdr;
+        fr_channel_t *chan = dev->priv;
+
+        ArpPacket = kmalloc(sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t), GFP_ATOMIC);
+        /* SNAP Header indicating ARP */
+        ArpPacket->control      = 0x03;
+        ArpPacket->pad          = 0x00;
+        ArpPacket->NLPID        = 0x80;
+        ArpPacket->OUI[0]       = 0;
+        ArpPacket->OUI[1]       = 0;
+        ArpPacket->OUI[2]       = 0;
+        ArpPacket->PID          = 0x0608;
+
+        arphdr = (arphdr_fr_t *)(ArpPacket + 1); // Go to ARP Packet
+       /* InARP request */
+        arphdr->ar_hrd = 0x0F00;        /* Frame Relay HW type */
+        arphdr->ar_pro = 0x0008;        /* IP Protocol         */
+        arphdr->ar_hln = 2;             /* HW addr length      */
+        arphdr->ar_pln = 4;             /* IP addr length      */
+        arphdr->ar_op = htons(0x08);    /* InARP Request       */
+        arphdr->ar_sha = 0;             /* src HW DLCI - Doesn't matter */
+        arphdr->ar_sip = dev->pa_addr;  /* Local Address       */
+        arphdr->ar_tha = 0;             /* dst HW DLCI - Doesn't matter */
+        arphdr->ar_tip = 0;             /* Remote Address -- what we want */
+
+        printk(KERN_INFO "%s: Sending InARP request on DLCI %d.\n", card->devname, chan->dlci);
+        err = fr_send(card, chan->dlci, 0,
+                   sizeof(arphdr_1490_t) + sizeof(arphdr_fr_t),
+                   (void *)ArpPacket);
+	
+	if (!err){
+         	printk(KERN_INFO "\n%s: Sending InARP request on DLCI %d.\n",
+                        card->devname, chan->dlci);
+                clear_bit(ARP_CRIT,&card->wandev.critical);
+        }
+
+        kfree(ArpPacket);
+#endif
+
+	return 0;
 }
 	
 
@@ -3426,151 +4295,312 @@
  * Process ARP Packet Type
  */
 
-int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, struct net_device* dev)
+int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, netdevice_t* dev)
 {
 
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	arphdr_fr_t *arphdr = (arphdr_fr_t *)(ArpPacket + 1); /* Skip header */
 	fr_rx_buf_ctl_t* frbuf = card->rxmb;
 	struct in_device *in_dev;
+	fr_channel_t *chan = dev->priv;		
+#else
+  	arphdr_fr_t *arphdr = (arphdr_fr_t *)(ArpPacket + 1); /* Skip header */
+        fr_rx_buf_ctl_t* frbuf = card->rxmb;
+#endif
+	
+	/* Before we transmit ARP packet, we must check 
+	 * to see that we are not currently transmitting a 
+	 * frame (in 'if_send()') and that we are not 
+	 * already in a 'delayed transmit' state. */
+	if (check_tx_status(card,dev)){
+		if (net_ratelimit()){ 	
+			printk(KERN_INFO "%s: Disabling comminication to process ARP\n",
+					card->devname);
+		}
+		set_bit(ARP_CRIT,&card->wandev.critical);
+		return 0;
+	}
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 
 	in_dev = dev->ip_ptr;
-	if( in_dev != NULL && in_dev->ifa_list != NULL) {
-		switch (ntohs(arphdr->ar_op)) {
 
-		case 0x08:  // Inverse ARP request  -- Send Reply, add route.
+	/* Check that IP addresses exist for our network address */
+	if (in_dev == NULL || in_dev->ifa_list == NULL) 
+		return -1;
+
+	switch (ntohs(arphdr->ar_op)) {
+
+	case 0x08:  // Inverse ARP request  -- Send Reply, add route.
 			
-			/* Check for valid Address */
-			printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Req\n", ((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
+		/* Check for valid Address */
+		printk(KERN_INFO "%s: Recvd PtP addr -InArp Req: %s\n", 
+			card->devname, in_ntoa(arphdr->ar_sip));
 
-			if ((in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) {
-				printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", card->devname);
-				printk(KERN_INFO "mask %X\n", in_dev->ifa_list->ifa_mask);
-				printk(KERN_INFO "local %X\n", in_dev->ifa_list->ifa_local);
-				return -1;
-			}
-		
-			if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) {
-				printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", card->devname);
-				return -1;
-			}
+
+		/* Check that the network address is the same as ours, only
+                 * if the netowrk mask is not 255.255.255.255. Otherwise
+                 * this check would not make sense */
+
+		if (in_dev->ifa_list->ifa_mask != 0xFFFFFFFF && 
+		    (in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != 
+		    (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)){
+
+			printk(KERN_INFO 
+				"%s: Invalid PtP address. %s  InARP ignored.\n", 
+					card->devname,in_ntoa(arphdr->ar_sip));
+
+			printk(KERN_INFO "%s: mask %s\n", 
+				card->devname, in_ntoa(in_dev->ifa_list->ifa_mask));
+				printk(KERN_INFO "%s: local %s\n", 
+				card->devname,in_ntoa(in_dev->ifa_list->ifa_local));
+			return -1;
+		}
+
+		if (in_dev->ifa_list->ifa_local == arphdr->ar_sip){
+			printk(KERN_INFO 
+				"%s: Local addr = PtP addr.  InARP ignored.\n", 
+					card->devname);
+			return -1;
+		}
 	
-			arphdr->ar_op = htons(0x09);	/* InARP Reply */
+		arphdr->ar_op = htons(0x09);	/* InARP Reply */
 
-			/* Set addresses */
-			arphdr->ar_tip = arphdr->ar_sip;
-			arphdr->ar_sip = in_dev->ifa_list->ifa_local;
+		/* Set addresses */
+		arphdr->ar_tip = arphdr->ar_sip;
+		arphdr->ar_sip = in_dev->ifa_list->ifa_local;
 
-                        fr_send(card, frbuf->dlci, 0, frbuf->length, (void *)ArpPacket);
+		chan->ip_local = in_dev->ifa_list->ifa_local;
+		chan->ip_remote = arphdr->ar_sip;
 
-			/* Modify Point-to-Point Address */
-			{
-			struct ifreq if_info;
-			struct sockaddr_in *if_data;
-			mm_segment_t fs = get_fs();
-			int err;
-
-			/* Set remote addresses */
-			memset(&if_info, 0, sizeof(if_info));
-			strcpy(if_info.ifr_name, dev->name);
-
-          		set_fs(get_ds());     /* get user space block */ 
-	
-			if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
-			if_data->sin_addr.s_addr = arphdr->ar_tip;
-			if_data->sin_family = AF_INET;
-			err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
+		fr_send(card, frbuf->dlci, 0, frbuf->length, (void *)ArpPacket);
 
-                	set_fs(fs);           /* restore old block */
+		if (test_bit(ARP_CRIT,&card->wandev.critical)){
+			if (net_ratelimit()){ 	
+				printk(KERN_INFO "%s: ARP Processed Enabling Communication!\n",
+					card->devname);
 			}
-				
-			/* Add Route Flag */
-			/* The route will be added in the polling routine so
-			   that it is not interrupt context. */
+		}
+		clear_bit(ARP_CRIT,&card->wandev.critical);
+		
+		chan->ip_local = in_dev->ifa_list->ifa_local;
+		chan->ip_remote = arphdr->ar_sip;
 
-			((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
-			card->poll	= &process_route;
+		/* Add Route Flag */
+		/* The route will be added in the polling routine so
+		   that it is not interrupt context. */
 
-			break;
+		chan->route_flag = ADD_ROUTE;
+		trigger_fr_poll (dev);
 
-		case 0x09:  // Inverse ARP reply
+		break;
 
-			/* Check for valid Address */
-			printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Reply\n", ((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
+	case 0x09:  // Inverse ARP reply
 
-			if ((in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != (in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) {
-				printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", card->devname);
-				return -1;
-			}
+		/* Check for valid Address */
+		printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Reply\n", 
+				card->devname, in_ntoa(arphdr->ar_sip));
 
-			if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) {
-				printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", card->devname);
-				return -1;
-			}			
 
-			/* Modify Point-to-Point Address */
-			{
-			struct ifreq if_info;
-			struct sockaddr_in *if_data;
-			mm_segment_t fs = get_fs();
-			int err;
+		/* Compare network addresses, only if network mask
+                 * is not 255.255.255.255  It would not make sense
+                 * to perform this test if the mask was all 1's */
 
-			/* Set remote addresses */
-			memset(&if_info, 0, sizeof(if_info));
-			strcpy(if_info.ifr_name, dev->name);
+		if (in_dev->ifa_list->ifa_mask != 0xffffffff &&
+		    (in_dev->ifa_list->ifa_mask & arphdr->ar_sip) != 
+			(in_dev->ifa_list->ifa_mask & in_dev->ifa_list->ifa_local)) {
 
-          		set_fs(get_ds());     /* get user space block */ 
-	
-			if_data = (struct sockaddr_in *)&if_info.ifr_dstaddr;
-			if_data->sin_addr.s_addr = arphdr->ar_sip;
-			if_data->sin_family = AF_INET;
-			err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
+			printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", 
+					card->devname);
+			return -1;
+		}
 
-                	set_fs(fs);           /* restore old block */
-			}
+		/* Make sure that the received IP address is not
+                 * the same as our own local address */
+		if (in_dev->ifa_list->ifa_local == arphdr->ar_sip) {
+			printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", 
+				card->devname);
+			return -1;
+		}			
 
-			/* Add Route Flag */
-			/* The route will be added in the polling routine so
-			   that it is not interrupt context. */
+		chan->ip_local  = in_dev->ifa_list->ifa_local;
+		chan->ip_remote = arphdr->ar_sip;
 
-			((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
-			((fr_channel_t *) dev->priv)->inarp = INARP_CONFIGURED;
-			card->poll	= &process_route;
-			
-			break;
-		default:  // ARP's and RARP's -- Shouldn't happen.
-		}
+		/* Add Route Flag */
+		/* The route will be added in the polling routine so
+		   that it is not interrupt context. */
+
+		chan->route_flag = ADD_ROUTE;
+		chan->inarp = INARP_CONFIGURED;
+		trigger_fr_poll(dev);
+		
+		break;
+	default:  // ARP's and RARP's -- Shouldn't happen.
 	}
 
 	return 0;	
-}
-	
+#else
 
-/*==============================================================================
- * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_
- * TEST_COUNTER times.
- */
-static int intr_test( sdla_t* card )
-{
-	fr_mbox_t* mb = card->mbox;
-	int err,i;
+        switch (ntohs(arphdr->ar_op)) {
 
-	/* The critical flag is unset here because we want to get into the
-	   ISR without the flag already set. The If_open sets the flag.
-	*/
-        clear_bit(1, (void*)&card->wandev.critical);
+                case 0x08:  // Inverse ARP request  -- Send Reply, add route.
 
-        err = fr_set_intr_mode(card, FR_INTR_READY, card->wandev.mtu, 0 );
-	
-	if (err == CMD_OK) {
+                        /* Check for valid Address */
+                        printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Req\n", 
+				((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
 
-		for ( i = 0; i < MAX_INTR_TEST_COUNTER; i++ ) {
- 			/* Run command READ_CODE_VERSION */
-			mb->cmd.length  = 0;
-			mb->cmd.command = FR_READ_CODE_VERSION;
-			err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-			if (err != CMD_OK) 
-				fr_event(card, err, mb);
+
+			if (dev->pa_mask != 0xFFFFFFFF){
+
+	                        if ((dev->pa_mask & arphdr->ar_sip) != (dev->pa_mask & dev->pa_addr)) {
+        	                        printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", 
+							card->devname);
+                        	        return -1;
+                        	}
+			}
+
+                        if (dev->pa_addr == arphdr->ar_sip) {
+                                printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", 
+						card->devname);
+                                return -1;
+                        }
+
+                        arphdr->ar_op = htons(0x09);    /* InARP Reply */
+
+                        /* Set addresses */
+                        arphdr->ar_tip = arphdr->ar_sip;
+                        arphdr->ar_sip = dev->pa_addr;
+
+                        fr_send(card, frbuf->dlci, 0, frbuf->length, (void *)ArpPacket);
+
+			clear_bit(ARP_CRIT,&card->wandev.critical);
+
+                        /* Modify Point-to-Point Address */
+                        dev->pa_dstaddr = arphdr->ar_tip;
+
+                        /* Add Route Flag */
+                        /* The route will be added in the polling routine so
+                           that it is not interrupt context. */
+                        ((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
+			trigger_fr_poll(dev);
+
+                        break;
+                case 0x09:  // Inverse ARP reply
+
+                        /* Check for valid Address */
+                        printk(KERN_INFO "%s: Recvd PtP addr %s -InArp Reply\n", 
+				((fr_channel_t *)dev->priv)->name, in_ntoa(arphdr->ar_sip));
+
+                        if ((dev->pa_mask & arphdr->ar_sip) != (dev->pa_mask & dev->pa_addr)) {
+                                printk(KERN_INFO "%s: Invalid PtP address.  InARP ignored.\n", 
+					card->devname);
+                                return -1;
+                        }
+
+                        if (dev->pa_addr == arphdr->ar_sip) {
+                                printk(KERN_INFO "%s: Local addr = PtP addr.  InARP ignored.\n", 
+					card->devname);
+                                return -1;
+                        }
+
+                        /* Modify Point-to-Point Address */
+                        dev->pa_dstaddr = arphdr->ar_sip;
+                        /* Add Route Flag */
+                        /* The route will be added in the polling routine so
+                           that it is not interrupt context. */
+
+                        ((fr_channel_t *) dev->priv)->route_flag = ADD_ROUTE;
+                        ((fr_channel_t *) dev->priv)->inarp = INARP_CONFIGURED;
+			trigger_fr_poll(dev);
+
+                        break;
+                default:  // ARP's and RARP's -- Shouldn't happen.
+        }
+
+        return 0;
+
+#endif
+}
+
+
+/*============================================================
+ * trigger_fr_arp
+ *
+ * Description:
+ * 	Add an fr_arp() task into a arp
+ *      timer handler for a specific dlci/interface.  
+ *      This will kick the fr_arp() routine 
+ *      within the specified time interval. 
+ *
+ * Usage:
+ * 	This timer is used to send ARP requests at
+ *      certain time intervals. 
+ * 	Called by an interrupt to request an action
+ *      at a later date.
+ */	
+
+static void trigger_fr_arp (netdevice_t *dev)
+{
+	fr_channel_t* chan = dev->priv;
+
+	del_timer(&chan->fr_arp_timer);
+	chan->fr_arp_timer.expires = jiffies + (chan->inarp_interval * HZ);
+	add_timer(&chan->fr_arp_timer);
+	return;
+}
+
+
+
+/*==============================================================================
+ * ARP Request Action
+ *
+ *	This funciton is called by timer interrupt to send an arp request
+ *      to the remote end.
+ */
+
+static void fr_arp (unsigned long data)
+{
+	netdevice_t *dev = (netdevice_t *)data;
+	fr_channel_t *chan = dev->priv;
+	volatile sdla_t *card = chan->card;
+	fr508_flags_t* flags = card->flags;
+
+	/* Send ARP packets for all devs' until
+         * ARP state changes to CONFIGURED */
+
+	if (chan->inarp == INARP_REQUEST &&
+	    chan->common.state == WAN_CONNECTED && 
+	    card->wandev.state == WAN_CONNECTED){
+		set_bit(0,&chan->inarp_ready);
+		card->u.f.timer_int_enabled |= TMR_INT_ENABLED_ARP;
+		flags->imask |= FR_INTR_TIMER;	
+	}
+ 
+	return;
+}
+	
+
+/*==============================================================================
+ * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_
+ * TEST_COUNTER times.
+ */
+static int intr_test( sdla_t* card )
+{
+	fr_mbox_t* mb = card->mbox;
+	int err,i;
+
+        err = fr_set_intr_mode(card, FR_INTR_READY, card->wandev.mtu, 0 );
+	
+	if (err == CMD_OK) {
+
+		for ( i = 0; i < MAX_INTR_TEST_COUNTER; i++ ) {
+ 			/* Run command READ_CODE_VERSION */
+			mb->cmd.length  = 0;
+			mb->cmd.command = FR_READ_CODE_VERSION;
+			err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+			if (err != CMD_OK) 
+				fr_event(card, err, mb);
 		}
 	
 	} else {
@@ -3582,7 +4612,6 @@
 	if( err != CMD_OK ) 
 		return err;
 
-	set_bit(1, (void*)&card->wandev.critical);
 	return 0;
 }
 
@@ -3593,6 +4622,9 @@
 {
 	fr_udp_pkt_t *fr_udp_pkt = (fr_udp_pkt_t *)skb->data;
 
+	/* Quick HACK */
+	
+	
         if((fr_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) &&
 		(fr_udp_pkt->ip_pkt.ver_inet_hdr_length == 0x45) &&
 		(fr_udp_pkt->udp_pkt.udp_dst_port == 
@@ -3600,10 +4632,10 @@
 		(fr_udp_pkt->wp_mgmt.request_reply == 
 		UDPMGMT_REQUEST)) {
                         if(!strncmp(fr_udp_pkt->wp_mgmt.signature,
-                                UDPMGMT_FPIPE_SIGNATURE, 8))
+                                UDPMGMT_FPIPE_SIGNATURE, 8)){
                                 return UDP_FPIPE_TYPE;
+			}
 	}
-
         return UDP_INVALID_TYPE;
 }
 
@@ -3674,38 +4706,772 @@
 		card->devname, chan->dlci);
 }
 
+
+
 void s508_s514_lock(sdla_t *card, unsigned long *smp_flags)
 {
-
 	if (card->hw.type != SDLA_S514){
-#ifdef CONFIG_SMP
-		spin_lock_irqsave(&card->lock, *smp_flags);
-#else
-    		disable_irq(card->hw.irq);
-#endif
+
+	      #if defined(__SMP__) || defined(LINUX_2_4)
+		spin_lock_irqsave(&card->wandev.lock, *smp_flags);
+	      #else
+          	disable_irq(card->hw.irq);
+	      #endif
+	}else{
+	      #if defined(__SMP__) || defined(LINUX_2_4)
+		spin_lock(&card->u.f.if_send_lock);
+	      #endif
 	}
-#ifdef CONFIG_SMP
-	else{
-		spin_lock(&card->lock);
-	}	
-#endif
+	return;
 }
 
+
 void s508_s514_unlock(sdla_t *card, unsigned long *smp_flags)
 {
 	if (card->hw.type != SDLA_S514){
-#ifdef CONFIG_SMP
-		spin_unlock_irqrestore(&card->lock, *smp_flags);
-#else
-    		enable_irq(card->hw.irq);
-#endif
+
+	      #if defined(__SMP__) || defined(LINUX_2_4)
+		spin_unlock_irqrestore (&card->wandev.lock, *smp_flags);
+	      #else
+          	enable_irq(card->hw.irq);
+	      #endif
+	}else{
+	      #if defined(__SMP__) || defined(LINUX_2_4)
+		spin_unlock(&card->u.f.if_send_lock);
+	      #endif
 	}
-#ifdef CONFIG_SMP
-	else{
-		spin_unlock(&card->lock);
+	return;
+}
+
+
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
+/*----------------------------------------------------------------------
+                  RECEIVE INTERRUPT: BOTTOM HALF HANDLERS 
+ ----------------------------------------------------------------------*/
+
+
+/*========================================================
+ * bh_enqueue
+ *
+ * Description:
+ *	Insert a received packed into a circular
+ *      rx queue.  This packed will be picked up 
+ *      by fr_bh() and sent up the stack to the
+ *      user.
+ *       	
+ * Usage: 
+ *	This function is called by rx interrupt,
+ *      in API mode.
+ *
+ */
+
+static int bh_enqueue (netdevice_t *dev, struct sk_buff *skb)
+{
+	/* Check for full */
+	fr_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+
+
+	if (atomic_read(&chan->bh_buff_used) == MAX_BH_BUFF){
+		++card->wandev.stats.rx_dropped;
+		wan_dev_kfree_skb(skb, FREE_READ);
+		return 1; 
+	}
+
+	((bh_data_t *)&chan->bh_head[chan->bh_write])->skb = skb;
+
+	if (chan->bh_write == (MAX_BH_BUFF-1)){
+		chan->bh_write=0;
+	}else{
+		++chan->bh_write;
+	}
+
+	atomic_inc(&chan->bh_buff_used);
+
+	return 0;
+}
+
+
+/*========================================================
+ * trigger_fr_bh
+ *
+ * Description:
+ * 	Kick the fr_bh() handler
+ *
+ * Usage:
+ *	rx interrupt calls this function during
+ *      the API mode. 
+ */
+
+static void trigger_fr_bh (fr_channel_t *chan)
+{
+	if (!test_and_set_bit(0,&chan->tq_working)){
+		wanpipe_queue_tq(&chan->common.wanpipe_task);
+		wanpipe_mark_bh();
+	}
+}
+
+
+/*========================================================
+ * fr_bh
+ *
+ * Description:
+ *	Frame relay receive BH handler. 
+ *	Dequeue data from the BH circular 
+ *	buffer and pass it up the API sock.
+ *       	
+ * Rationale: 
+ *	This fuction is used to offload the 
+ *	rx_interrupt during API operation mode.  
+ *	The fr_bh() function executes for each 
+ *	dlci/interface.  
+ * 
+ *      Once receive interrupt copies data from the
+ *      card into an skb buffer, the skb buffer
+ *  	is appended to a circular BH buffer.
+ *  	Then the interrupt kicks fr_bh() to finish the
+ *      job at a later time (no within the interrupt).
+ *       
+ * Usage:
+ * 	Interrupts use this to defer a taks to 
+ *      a polling routine.
+ *
+ */	
+
+static void fr_bh (netdevice_t * dev)
+{
+	fr_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	struct sk_buff *skb;
+
+	if (atomic_read(&chan->bh_buff_used) == 0){
+		clear_bit(0, &chan->tq_working);
+		return;
+	}
+
+	while (atomic_read(&chan->bh_buff_used)){
+
+		if (chan->common.sk == NULL || chan->common.func == NULL){
+			clear_bit(0, &chan->tq_working);
+			return;
+		}
+
+		skb  = ((bh_data_t *)&chan->bh_head[chan->bh_read])->skb;
+
+		if (skb != NULL){
+
+			if (chan->common.sk == NULL || chan->common.func == NULL){
+				++card->wandev.stats.rx_dropped;
+				++chan->ifstats.rx_dropped;
+				wan_dev_kfree_skb(skb, FREE_READ);
+				fr_bh_cleanup(dev);
+				continue;
+			}
+
+			if (chan->common.func(skb,dev,chan->common.sk) != 0){
+				/* Sock full cannot send, queue us for
+                                 * another try */
+				atomic_set(&chan->common.receive_block,1);
+				return;
+			}else{
+				fr_bh_cleanup(dev);
+			}
+		}else{
+			fr_bh_cleanup(dev);
+		}
 	}	
+	clear_bit(0, &chan->tq_working);
+
+	return;
+}
+
+static int fr_bh_cleanup (netdevice_t *dev)
+{
+	fr_channel_t* chan = dev->priv;
+
+	((bh_data_t *)&chan->bh_head[chan->bh_read])->skb = NULL;
+
+	if (chan->bh_read == (MAX_BH_BUFF-1)){
+		chan->bh_read=0;
+	}else{
+		++chan->bh_read;	
+	}
+
+	atomic_dec(&chan->bh_buff_used);
+	return 0;
+}
 #endif
 
+
+/*----------------------------------------------------------------------
+               POLL BH HANDLERS AND KICK ROUTINES 
+ ----------------------------------------------------------------------*/
+
+/*============================================================
+ * trigger_fr_poll
+ *
+ * Description:
+ * 	Add a fr_poll() task into a tq_scheduler bh handler
+ *      for a specific dlci/interface.  This will kick
+ *      the fr_poll() routine at a later time. 
+ *
+ * Usage:
+ * 	Interrupts use this to defer a taks to 
+ *      a polling routine.
+ *
+ */	
+static void trigger_fr_poll (netdevice_t *dev)
+{
+	fr_channel_t* chan = dev->priv;
+      #ifdef LINUX_2_4
+	schedule_task(&chan->fr_poll_task);
+      #else
+	queue_task(&chan->fr_poll_task, &tq_scheduler);
+      #endif
+	return;
 }
+
+
+/*============================================================
+ * fr_poll
+ *	
+ * Rationale:
+ * 	We cannot manipulate the routing tables, or
+ *      ip addresses withing the interrupt. Therefore
+ *      we must perform such actons outside an interrupt 
+ *      at a later time. 
+ *
+ * Description:	
+ *	Frame relay polling routine, responsible for 
+ *     	shutting down interfaces upon disconnect
+ *     	and adding/removing routes. 
+ *      
+ * Usage:        
+ * 	This function is executed for each frame relay
+ * 	dlci/interface through a tq_schedule bottom half.
+ *      
+ *      trigger_fr_poll() function is used to kick
+ *      the fr_poll routine.  
+ */
+
+static void fr_poll (netdevice_t *dev)
+{
+
+	fr_channel_t* chan;
+	sdla_t *card;
+	u8 check_gateway=0;
+
+	if (!dev || (chan = dev->priv) == NULL)
+		return;
+
+	card = chan->card;
+	
+	/* (Re)Configuraiton is in progress, stop what you are 
+	 * doing and get out */
+	if (test_bit(PERI_CRIT,&card->wandev.critical)){
+		return;
+	}
+
+	switch (chan->common.state){
+
+	case WAN_DISCONNECTED:
+
+		if (test_bit(DYN_OPT_ON,&chan->interface_down) &&
+		    !test_bit(DEV_DOWN, &chan->interface_down) &&
+		    dev->flags&IFF_UP){
+
+			printk(KERN_INFO "%s: Interface %s is Down.\n", 
+				card->devname,dev->name);
+			change_dev_flags(dev,dev->flags&~IFF_UP);
+			set_bit(DEV_DOWN, &chan->interface_down);
+			chan->route_flag = NO_ROUTE;
+			
+		}else{
+			if (chan->inarp != INARP_NONE)
+				process_route(dev);	
+		}
+		break;
+
+	case WAN_CONNECTED:
+
+		if (test_bit(DYN_OPT_ON,&chan->interface_down) &&
+		    test_bit(DEV_DOWN, &chan->interface_down) &&
+		    !(dev->flags&IFF_UP)){
+
+			printk(KERN_INFO "%s: Interface %s is Up.\n", 
+					card->devname,dev->name);
+
+			change_dev_flags(dev,dev->flags|IFF_UP);
+			clear_bit(DEV_DOWN, &chan->interface_down);
+			check_gateway=1;
+		}
+
+		if (chan->inarp != INARP_NONE){
+			process_route(dev);
+			check_gateway=1;
+		}
+
+		if (chan->gateway && check_gateway)
+			add_gateway(card,dev);
+
+		break;
+
+	}
+
+	return;	
+}
+
+/*==============================================================
+ * check_tx_status
+ *
+ * Rationale:
+ *	We cannot transmit from an interrupt while
+ *      the if_send is transmitting data.  Therefore,
+ *      we must check whether the tx buffers are
+ *      begin used, before we transmit from an
+ *      interrupt.	
+ * 
+ * Description:	
+ *	Checks whether it's safe to use the transmit 
+ *      buffers. 
+ *
+ * Usage:
+ * 	ARP and UDP handling routines use this function
+ *      because, they need to transmit data during
+ *      an interrupt.
+ */
+
+static int check_tx_status(sdla_t *card, netdevice_t *dev)
+{
+
+	if (card->hw.type == SDLA_S514){
+		if (test_bit(SEND_CRIT, (void*)&card->wandev.critical) ||
+			test_bit(SEND_TXIRQ_CRIT, (void*)&card->wandev.critical)) {
+			return 1;
+		}
+	}
+
+	if (is_queue_stopped(dev) || (card->u.f.tx_interrupts_pending))
+     		return 1; 
+
+	return 0;
+}
+
+/*===============================================================
+ * move_dev_to_next
+ *  
+ * Description:
+ *	Move the dev pointer to the next location in the
+ *      link list.  Check if we are at the end of the 
+ *      list, if so start from the begining.
+ *
+ * Usage:
+ * 	Timer interrupt uses this function to efficiently
+ *      step through the devices that need to send ARP data.
+ *
+ */
+
+netdevice_t * move_dev_to_next (sdla_t *card, netdevice_t *dev)
+{
+	if (card->wandev.new_if_cnt != 1){
+		if (*((netdevice_t **)dev->priv) == NULL){
+			return card->wandev.dev;
+		}else{
+			return *((netdevice_t **)dev->priv);
+		}
+	}
+	return dev;
+}
+
+/*==============================================================
+ * trigger_config_fr
+ *
+ * Rationale:
+ *	All commands must be performed inside of a  
+ *      interrupt.   
+ *
+ * Description:
+ *	Kick the config_fr() routine throught the
+ *      timer interrupt.
+ */
+
+
+static void trigger_config_fr (sdla_t *card)
+{
+	fr508_flags_t* flags = card->flags;
+
+	card->u.f.timer_int_enabled |= TMR_INT_ENABLED_CONFIG;
+	flags->imask |= FR_INTR_TIMER;
+}
+
+
+/*==============================================================
+ * config_fr
+ *
+ * Rationale:
+ * 	All commands must be performed inside of a  
+ *      interrupt.  
+ &
+ * Description:	
+ * 	Configure a DLCI. This function is executed
+ *      by a timer_interrupt.  The if_open() function
+ *      triggers it.
+ *
+ * Usage:
+ *	new_if() collects all data necessary to
+ *      configure the DLCI. It sets the chan->dlci_ready 
+ *      bit.  When the if_open() function is executed
+ *      it checks this bit, and if its set it triggers
+ *      the timer interrupt to execute the config_fr()
+ *      function.
+ */
+
+static void config_fr (sdla_t *card)
+{
+	netdevice_t *dev;
+	fr_channel_t *chan;
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t **)dev->priv)){
+	
+		if ((chan=dev->priv) == NULL)
+			continue;
+		
+		if (!test_bit(0,&chan->config_dlci))
+			continue;
+
+		clear_bit(0,&chan->config_dlci);
+
+		/* If signalling is set to NO, then setup 
+        	 * DLCI addresses right away.  Don't have to wait for
+		 * link to connect. 
+		 */
+		if (card->wandev.signalling == WANOPT_NO){
+			printk(KERN_INFO "%s: Signalling set to NO: Mapping DLCI's\n",
+					card->wandev.name);
+			if (fr_init_dlci(card,chan)){
+				printk(KERN_INFO "%s: ERROR: Failed to configure DLCI %i !\n",
+					card->devname, chan->dlci);
+				return;
+			}
+		}
+
+		if (card->wandev.station == WANOPT_CPE) {
+	
+			update_chan_state(dev);	
+			
+			/* CPE: issue full status enquiry */
+			fr_issue_isf(card, FR_ISF_FSE);
+
+		} else {	
+			/* FR switch: activate DLCI(s) */
+	
+			/* For Switch emulation we have to ADD and ACTIVATE
+			 * the DLCI(s) that were configured with the SET_DLCI_
+			 * CONFIGURATION command. Add and Activate will fail if
+			 * DLCI specified is not included in the list.
+			 *
+			 * Also If_open is called once for each interface. But
+			 * it does not get in here for all the interface. So
+		 	 * we have to pass the entire list of DLCI(s) to add 
+			 * activate routines.  
+			 */ 
+			
+			if (!check_dlci_config (card, chan)){
+				fr_add_dlci(card, chan->dlci);
+				fr_activate_dlci(card, chan->dlci);
+			}
+		}
+
+		card->u.f.dlci_to_dev_map[chan->dlci] = dev;
+	}
+	return;
+}
+
+
+/*==============================================================
+ * config_fr
+ *
+ * Rationale:
+ *	All commands must be executed during an interrupt.
+ * 
+ * Description:	
+ *	Trigger uncofig_fr() function through 
+ *      the timer interrupt.
+ *
+ */
+
+static void trigger_unconfig_fr (netdevice_t *dev)
+{
+	fr_channel_t *chan = dev->priv;
+	volatile sdla_t *card = chan->card;
+	u32 timeout;
+	fr508_flags_t* flags = card->flags;
+	int reset_critical=0;
+	
+	if (test_bit(PERI_CRIT,(void*)&card->wandev.critical)){
+		clear_bit(PERI_CRIT,(void*)&card->wandev.critical);
+		reset_critical=1;
+	}
+		
+	/* run unconfig_dlci() function 
+         * throught the timer interrupt */
+	set_bit(0,(void*)&chan->unconfig_dlci);
+	card->u.f.timer_int_enabled |= TMR_INT_ENABLED_UNCONFIG;
+	flags->imask |= FR_INTR_TIMER;
+
+	/* Wait for the command to complete */
+	timeout = jiffies;
+     	for(;;) {
+
+		if(!(card->u.f.timer_int_enabled & TMR_INT_ENABLED_UNCONFIG))
+			break;
+
+             	if ((jiffies - timeout) > (1 * HZ)){
+    			card->u.f.timer_int_enabled &= ~TMR_INT_ENABLED_UNCONFIG;
+			printk(KERN_INFO "%s: Failed to delete DLCI %i\n",
+				card->devname,chan->dlci);
+ 			break;
+		}
+	}
+
+	if (reset_critical){
+		set_bit(PERI_CRIT,(void*)&card->wandev.critical);
+	}
+}
+
+/*==============================================================
+ * unconfig_fr
+ *
+ * Rationale:
+ *	All commands must be executed during an interrupt.
+ * 
+ * Description:	
+ *	Remove the dlci from firmware.
+ *	This funciton is used in NODE shutdown.
+ */
+
+static void unconfig_fr (sdla_t *card)
+{
+	netdevice_t *dev;
+	fr_channel_t *chan;
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t **)dev->priv)){
+	
+		if ((chan=dev->priv) == NULL)
+			continue;
+		
+		if (!test_bit(0,&chan->unconfig_dlci))
+			continue;
+
+		clear_bit(0,&chan->unconfig_dlci);
+
+		if (card->wandev.station == WANOPT_NODE){
+			printk(KERN_INFO "%s: Unconfiguring DLCI %i\n",
+					card->devname,chan->dlci);
+			fr_delete_dlci(card,chan->dlci);
+		}
+		card->u.f.dlci_to_dev_map[chan->dlci] = NULL;
+	}
+}
+
+static int setup_fr_header(struct sk_buff ** skb_orig, netdevice_t* dev, char op_mode)
+{
+	struct sk_buff *skb = *skb_orig;
+	fr_channel_t *chan=dev->priv;
+
+	if (op_mode == WANPIPE){
+
+		chan->fr_header[0]=Q922_UI;
+		
+		switch (htons(skb->protocol)){
+			
+		case ETH_P_IP:
+			chan->fr_header[1]=NLPID_IP;
+			break;
+		default:
+			return -EINVAL;
+		}
+			
+		return 2;
+	}
+
+	/* If we are in bridging mode, we must apply
+	 * an Ethernet header */
+	if (op_mode == BRIDGE || op_mode == BRIDGE_NODE){
+
+	      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+
+		/* Encapsulate the packet as a bridged Ethernet frame. */
+              #ifdef DEBUG
+		printk(KERN_INFO "%s: encapsulating skb for frame relay\n", 
+			dev->name);
+              #endif
+		
+		chan->fr_header[0] = 0x03;
+		chan->fr_header[1] = 0x00;
+		chan->fr_header[2] = 0x80;
+		chan->fr_header[3] = 0x00;
+		chan->fr_header[4] = 0x80;
+		chan->fr_header[5] = 0xC2;
+		chan->fr_header[6] = 0x00;
+		chan->fr_header[7] = 0x07;
+
+		/* Yuck. */
+		skb->protocol = ETH_P_802_3;
+		return 8;
+
+	      #else
+
+		/* BRIDGING is not supported in 2.0.X */
+		return -EINVAL;
+
+	      #endif
+	}
+		
+	return 0;
+}
+
+
+static int check_dlci_config (sdla_t *card, fr_channel_t *chan)
+{
+	fr_mbox_t* mbox = card->mbox;
+	int err=0;
+	fr_conf_t *conf=NULL;
+	unsigned short dlci_num = chan->dlci;
+	int dlci_offset=0;
+	netdevice_t *dev=NULL;
+	
+	mbox->cmd.command = FR_READ_CONFIG;
+	mbox->cmd.length = 0;
+	mbox->cmd.dlci = dlci_num; 	
+
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	
+	if (err == CMD_OK){
+		return 0;
+	}
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t**)dev->priv)){
+		set_chan_state(dev,WAN_DISCONNECTED);
+	}
+	
+	printk(KERN_INFO "DLCI %i Not configured, configuring\n",dlci_num);
+	
+	mbox->cmd.command = FR_COMM_DISABLE;
+	mbox->cmd.length = 0;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	if (err != CMD_OK){
+		fr_event(card, err, mbox);
+		return 2;
+	}
+
+	printk(KERN_INFO "Disabled Communications \n");
+	
+	mbox->cmd.command = FR_READ_CONFIG;
+	mbox->cmd.length = 0;
+	mbox->cmd.dlci = 0; 	
+
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	
+	if (err != CMD_OK){
+		fr_event(card, err, mbox);
+		return 2;
+	}
+	
+	conf = (fr_conf_t *)mbox->data;
+
+	dlci_offset=0;
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t**)dev->priv)){
+		fr_channel_t *chan_tmp = dev->priv;
+		conf->dlci[dlci_offset] = chan_tmp->dlci;		
+		dlci_offset++;
+	}
+	
+	printk(KERN_INFO "Got Fr configuration Buffer Length is %x Dlci %i Dlci Off %i\n",
+		mbox->cmd.length,
+		mbox->cmd.length > 0x20 ? conf->dlci[0] : -1, 
+		dlci_offset );
+	
+	mbox->cmd.length = 0x20 + dlci_offset*2;
+
+	mbox->cmd.command = FR_SET_CONFIG;
+	mbox->cmd.dlci = 0; 
+
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+
+	if (err != CMD_OK){
+		fr_event(card, err, mbox);
+		return 2;
+	}
+
+	initialize_rx_tx_buffers (card);
+
+	
+	printk(KERN_INFO "Configuraiton Succeded for new DLCI %i\n",dlci_num);
+
+	if (fr_comm_enable (card)){
+		return 2;
+	}
+
+	printk(KERN_INFO "Enabling Communications \n");
+
+	for (dev=card->wandev.dev; dev; dev=*((netdevice_t**)dev->priv)){
+		fr_channel_t *chan_tmp = dev->priv;
+		fr_init_dlci(card,chan_tmp);
+		fr_add_dlci(card, chan_tmp->dlci);
+		fr_activate_dlci(card, chan_tmp->dlci);
+	}
+
+	printk(KERN_INFO "END OF CONFIGURAITON %i\n",dlci_num);
+	
+	return 1;
+}
+
+static void initialize_rx_tx_buffers (sdla_t *card)
+{
+	fr_buf_info_t* buf_info;
+	
+	if (card->hw.type == SDLA_S514) {
+	
+                buf_info = (void*)(card->hw.dpmbase + FR_MB_VECTOR +
+			FR508_RXBC_OFFS);
+
+                card->rxmb = (void*)(buf_info->rse_next + card->hw.dpmbase);
+
+                card->u.f.rxmb_base =
+                        (void*)(buf_info->rse_base + card->hw.dpmbase); 
+
+                card->u.f.rxmb_last =
+                        (void*)(buf_info->rse_base +
+                        (buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) +
+                        card->hw.dpmbase);
+	}else{	
+		buf_info = (void*)(card->hw.dpmbase + FR508_RXBC_OFFS);
+
+		card->rxmb = (void*)(buf_info->rse_next -
+			FR_MB_VECTOR + card->hw.dpmbase);
+		
+		card->u.f.rxmb_base =
+			(void*)(buf_info->rse_base -
+			FR_MB_VECTOR + card->hw.dpmbase);
+		
+		card->u.f.rxmb_last =
+			(void*)(buf_info->rse_base +
+			(buf_info->rse_num - 1) * sizeof(fr_rx_buf_ctl_t) -
+			FR_MB_VECTOR + card->hw.dpmbase);
+	}
+
+	card->u.f.rx_base = buf_info->buf_base;
+	card->u.f.rx_top  = buf_info->buf_top;
+
+	card->u.f.tx_interrupts_pending = 0;
+
+	return;
+}
+
+	
+
 
 /****** End *****************************************************************/
diff -urN linux-2.4.4-pre2/drivers/net/wan/sdla_ft1.c linux/drivers/net/wan/sdla_ft1.c
--- linux-2.4.4-pre2/drivers/net/wan/sdla_ft1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/sdla_ft1.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,386 @@
+/*****************************************************************************
+* sdla_chdlc.c	WANPIPE(tm) Multiprotocol WAN Link Driver. Cisco HDLC module.
+*
+* Authors: 	Nenad Corbic <ncorbic@sangoma.com>
+*		Gideon Hack  
+*
+* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+*
+*		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.
+* ============================================================================
+* Sep 30, 1999  Nenad Corbic    Fixed dynamic IP and route setup.
+* Sep 23, 1999  Nenad Corbic    Added SMP support, fixed tracing 
+* Sep 13, 1999  Nenad Corbic	Split up Port 0 and 1 into separate devices.
+* Jun 02, 1999  Gideon Hack     Added support for the S514 adapter.
+* Oct 30, 1998	Jaspreet Singh	Added Support for CHDLC API (HDLC STREAMING).
+* Oct 28, 1998	Jaspreet Singh	Added Support for Dual Port CHDLC.
+* Aug 07, 1998	David Fong	Initial version.
+*****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/kernel.h>	/* printk(), and other useful stuff */
+#include <linux/stddef.h>	/* offsetof(), etc. */
+#include <linux/errno.h>	/* return codes */
+#include <linux/string.h>	/* inline memset(), etc. */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
+#include <linux/wanrouter.h>	/* WAN router definitions */
+#include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
+#include <linux/if_arp.h>	/* ARPHRD_* defines */
+
+#if defined(LINUX_2_4)
+  #include <linux/inetdevice.h>
+  #include <asm/uaccess.h>
+
+#elif defined(LINUX_2_1)
+  #include <linux/inetdevice.h>
+  #include <asm/uaccess.h>
+
+#else
+  #include <net/route.h>          /* Adding new route entries */
+  #include <asm/segment.h>
+  #define test_and_set_bit set_bit
+#endif
+
+#include <linux/in.h>		/* sockaddr_in */
+#include <linux/inet.h>	
+#include <linux/if.h>
+#include <asm/byteorder.h>	/* htons(), etc. */
+#include <linux/sdlapci.h>
+#include <asm/io.h>
+
+#include <linux/sdla_chdlc.h>		/* CHDLC firmware API definitions */
+
+/****** Defines & Macros ****************************************************/
+
+/* reasons for enabling the timer interrupt on the adapter */
+#define TMR_INT_ENABLED_UDP   	0x0001
+#define TMR_INT_ENABLED_UPDATE	0x0002
+ 
+#define	CHDLC_DFLT_DATA_LEN	1500		/* default MTU */
+#define CHDLC_HDR_LEN		1
+
+#define IFF_POINTTOPOINT 0x10
+
+#define WANPIPE 0x00
+#define API	0x01
+#define CHDLC_API 0x01
+
+#define PORT(x)   (x == 0 ? "PRIMARY" : "SECONDARY" )
+
+ 
+/******Data Structures*****************************************************/
+
+/* This structure is placed in the private data area of the device structure.
+ * The card structure used to occupy the private area but now the following 
+ * structure will incorporate the card structure along with CHDLC specific data
+ */
+
+typedef struct chdlc_private_area
+{
+	netdevice_t 	*slave;
+	sdla_t		*card;
+	int 		TracingEnabled;		/* For enabling Tracing */
+	unsigned long 	curr_trace_addr;	/* Used for Tracing */
+	unsigned long 	start_trace_addr;
+	unsigned long 	end_trace_addr;
+	unsigned long 	base_addr_trace_buffer;
+	unsigned long 	end_addr_trace_buffer;
+	unsigned short 	number_trace_elements;
+	unsigned  	available_buffer_space;
+	unsigned long 	router_start_time;
+	unsigned char 	route_status;
+	unsigned char 	route_removed;
+	unsigned long 	tick_counter;		/* For 5s timeout counter */
+	unsigned long 	router_up_time;
+        u32             IP_address;		/* IP addressing */
+        u32             IP_netmask;
+	unsigned char  mc;			/* Mulitcast support on/off */
+	unsigned short udp_pkt_lgth;		/* udp packet processing */
+	char udp_pkt_src;
+	char udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT];
+	unsigned short timer_int_enabled;
+	char update_comms_stats;		/* updating comms stats */
+	//FIXME: add driver stats as per frame relay!
+
+} chdlc_private_area_t;
+
+/* Route Status options */
+#define NO_ROUTE	0x00
+#define ADD_ROUTE	0x01
+#define ROUTE_ADDED	0x02
+#define REMOVE_ROUTE	0x03
+
+
+/****** Function Prototypes *************************************************/
+/* WAN link driver entry points. These are called by the WAN router module. */
+static int wpft1_exec (struct sdla *card, void *u_cmd, void *u_data);
+static int chdlc_read_version (sdla_t* card, char* str);
+static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb);
+
+/****** Public Functions ****************************************************/
+
+/*============================================================================
+ * Cisco HDLC protocol initialization routine.
+ *
+ * This routine is called by the main WANPIPE module during setup.  At this
+ * point adapter is completely initialized and firmware is running.
+ *  o read firmware version (to make sure it's alive)
+ *  o configure adapter
+ *  o initialize protocol-specific fields of the adapter data space.
+ *
+ * Return:	0	o.k.
+ *		< 0	failure.
+ */
+int wpft1_init (sdla_t* card, wandev_conf_t* conf)
+{
+	unsigned char port_num;
+	int err;
+
+	union
+		{
+		char str[80];
+		} u;
+	volatile CHDLC_MAILBOX_STRUCT* mb;
+	CHDLC_MAILBOX_STRUCT* mb1;
+	unsigned long timeout;
+
+	/* Verify configuration ID */
+	if (conf->config_id != WANCONFIG_CHDLC) {
+		printk(KERN_INFO "%s: invalid configuration ID %u!\n",
+				  card->devname, conf->config_id);
+		return -EINVAL;
+	}
+
+	/* Use primary port */
+	card->u.c.comm_port = 0;
+	
+
+	/* Initialize protocol-specific fields */
+	if(card->hw.type != SDLA_S514){
+		card->mbox  = (void *) card->hw.dpmbase;
+	}else{ 
+		card->mbox = (void *) card->hw.dpmbase + PRI_BASE_ADDR_MB_STRUCT;
+	}
+
+	mb = mb1 = card->mbox;
+
+	if (!card->configured){
+
+		/* The board will place an 'I' in the return code to indicate that it is
+	   	ready to accept commands.  We expect this to be completed in less
+           	than 1 second. */
+
+		timeout = jiffies;
+		while (mb->return_code != 'I')	/* Wait 1s for board to initialize */
+			if ((jiffies - timeout) > 1*HZ) break;
+
+		if (mb->return_code != 'I') {
+			printk(KERN_INFO
+				"%s: Initialization not completed by adapter\n",
+				card->devname);
+			printk(KERN_INFO "Please contact Sangoma representative.\n");
+			return -EIO;
+		}
+	}
+
+	/* Read firmware version.  Note that when adapter initializes, it
+	 * clears the mailbox, so it may appear that the first command was
+	 * executed successfully when in fact it was merely erased. To work
+	 * around this, we execute the first command twice.
+	 */
+
+	if (chdlc_read_version(card, u.str))
+		return -EIO;
+
+	printk(KERN_INFO "%s: Running FT1 Configuration firmware v%s\n",
+		card->devname, u.str); 
+
+	card->isr			= NULL;
+	card->poll			= NULL;
+	card->exec			= &wpft1_exec;
+	card->wandev.update		= NULL;
+ 	card->wandev.new_if		= NULL;
+	card->wandev.del_if		= NULL;
+	card->wandev.state		= WAN_DUALPORT;
+	card->wandev.udp_port   	= conf->udp_port;
+
+	card->wandev.new_if_cnt = 0;
+
+	/* This is for the ports link state */
+	card->u.c.state = WAN_DISCONNECTED;
+	
+	/* reset the number of times the 'update()' proc has been called */
+	card->u.c.update_call_count = 0;
+	
+	card->wandev.ttl = 0x7F;
+	card->wandev.interface = 0; 
+
+	card->wandev.clocking = 0;
+
+	port_num = card->u.c.comm_port;
+
+	/* Setup Port Bps */
+
+       	card->wandev.bps = 0;
+
+	card->wandev.mtu = MIN_LGTH_CHDLC_DATA_CFG;
+
+	/* Set up the interrupt status area */
+	/* Read the CHDLC Configuration and obtain: 
+	 *	Ptr to shared memory infor struct
+         * Use this pointer to calculate the value of card->u.c.flags !
+ 	 */
+	mb1->buffer_length = 0;
+	mb1->command = READ_CHDLC_CONFIGURATION;
+	err = sdla_exec(mb1) ? mb1->return_code : CMD_TIMEOUT;
+	if(err != COMMAND_OK) {
+		chdlc_error(card, err, mb1);
+		return -EIO;
+	}
+
+	if(card->hw.type == SDLA_S514){
+               	card->u.c.flags = (void *)(card->hw.dpmbase +
+               		(((CHDLC_CONFIGURATION_STRUCT *)mb1->data)->
+			ptr_shared_mem_info_struct));
+        }else{
+                card->u.c.flags = (void *)(card->hw.dpmbase +
+                        (((CHDLC_CONFIGURATION_STRUCT *)mb1->data)->
+			ptr_shared_mem_info_struct % SDLA_WINDOWSIZE));
+	}
+
+	card->wandev.state = WAN_FT1_READY;
+	printk(KERN_INFO "%s: FT1 Config Ready !\n",card->devname);
+
+	return 0;
+}
+
+static int wpft1_exec(sdla_t *card, void *u_cmd, void *u_data)
+{
+	CHDLC_MAILBOX_STRUCT* mbox = card->mbox;
+	int len;
+
+	
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	if (copy_from_user((void*)&mbox->command, u_cmd, sizeof(ft1_exec_cmd_t))){
+		return -EFAULT;
+	}
+
+	len = mbox->buffer_length;
+
+	if (len) {
+		if( copy_from_user((void*)&mbox->data, u_data, len)){
+			return -EFAULT;
+		}
+	}
+
+	/* execute command */
+	if (!sdla_exec(mbox)){
+		return -EIO;
+	}
+
+	/* return result */
+	if( copy_to_user(u_cmd, (void*)&mbox->command, sizeof(ft1_exec_cmd_t))){
+		return -EFAULT;
+	}
+
+	len = mbox->buffer_length;
+
+	if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)){
+		return -EFAULT;
+	}
+
+#else
+
+        if (!u_cmd || verify_area(VERIFY_WRITE, u_cmd, sizeof(ft1_exec_cmd_t))){
+                return -EFAULT;
+	}
+
+        memcpy_fromfs((void*)&mbox->command, u_cmd, sizeof(ft1_exec_cmd_t));
+
+	len = mbox->buffer_length;
+
+        if (len) {
+                if (!u_data || verify_area(VERIFY_READ, u_data, len))
+                        return -EFAULT;
+		memcpy_fromfs((void*)&mbox->data, u_data, len);
+        }
+
+        /* execute command */
+        if (!sdla_exec(mbox))
+               	return -EIO;
+
+        /* return result */
+        memcpy_tofs(u_cmd, (void*)&mbox->command, sizeof(ft1_exec_cmd_t));
+        len = mbox->buffer_length;
+
+        if (len && u_data && !verify_area(VERIFY_WRITE, u_data, len)){
+                memcpy_tofs(u_data, (void*)&mbox->data, len);
+	}
+
+#endif
+
+	return 0;
+
+}
+
+/*============================================================================
+ * Read firmware code version.
+ *	Put code version as ASCII string in str. 
+ */
+static int chdlc_read_version (sdla_t* card, char* str)
+{
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+	int len;
+	char err;
+	mb->buffer_length = 0;
+	mb->command = READ_CHDLC_CODE_VERSION;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+
+	if(err != COMMAND_OK) {
+		chdlc_error(card,err,mb);
+	}
+	else if (str) {  /* is not null */
+		len = mb->buffer_length;
+		memcpy(str, mb->data, len);
+		str[len] = '\0';
+	}
+	return (err);
+}
+
+/*============================================================================
+ * Firmware error handler.
+ *	This routine is called whenever firmware command returns non-zero
+ *	return code.
+ *
+ * Return zero if previous command has to be cancelled.
+ */
+static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb)
+{
+	unsigned cmd = mb->command;
+
+	switch (err) {
+
+	case CMD_TIMEOUT:
+		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+			card->devname, cmd);
+		break;
+
+	case S514_BOTH_PORTS_SAME_CLK_MODE:
+		if(cmd == SET_CHDLC_CONFIGURATION) {
+			printk(KERN_INFO
+			 "%s: Configure both ports for the same clock source\n",
+				card->devname);
+			break;
+		}
+
+	default:
+		printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
+			card->devname, cmd, err);
+	}
+
+	return 0;
+}
+
diff -urN linux-2.4.4-pre2/drivers/net/wan/sdla_ppp.c linux/drivers/net/wan/sdla_ppp.c
--- linux-2.4.4-pre2/drivers/net/wan/sdla_ppp.c	Tue Mar  6 19:44:36 2001
+++ linux/drivers/net/wan/sdla_ppp.c	Mon Dec  3 22:34:50 2001
@@ -3,14 +3,30 @@
 *
 * Author: 	Nenad Corbic <ncorbic@sangoma.com>
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2001 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
-*
+* Feb 28, 2001  Nenad Corbic	o Updated if_tx_timeout() routine for 
+* 				  2.4.X kernels.
+* Nov 29, 2000  Nenad Corbic	o Added the 2.4.x kernel support:
+* 				  get_ip_address() function has moved
+* 				  into the ppp_poll() routine. It cannot
+* 				  be called from an interrupt.
+* Nov 07, 2000  Nenad Corbic	o Added security features for UDP debugging:
+*                                 Deny all and specify allowed requests.
+* May 02, 2000  Nenad Corbic	o Added the dynamic interface shutdown
+*                                 option. When the link goes down, the
+*                                 network interface IFF_UP flag is reset.
+* Mar 06, 2000  Nenad Corbic	o Bug Fix: corrupted mbox recovery.
+* Feb 25, 2000  Nenad Corbic    o Fixed the FT1 UDP debugger problem.
+* Feb 09, 2000  Nenad Coribc    o Shutdown bug fix. update() was called
+*                                 with NULL dev pointer: no check.
+* Jan 24, 2000  Nenad Corbic    o Disabled use of CMD complete inter.
+* Dev 15, 1999  Nenad Corbic    o Fixed up header files for 2.0.X kernels
 * Oct 25, 1999  Nenad Corbic    o Support for 2.0.X kernels
 *                                 Moved dynamic route processing into 
 *                                 a polling routine.
@@ -74,13 +90,12 @@
 * Jan 06, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
 
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <linux/if_arp.h>	/* ARPHRD_* defines */
@@ -88,24 +103,29 @@
 #include <linux/in.h>		/* sockaddr_in */
 #include <linux/inet.h>		/* in_aton(), in_ntoa() prototypes */
 
-#include <linux/inetdevice.h>
-#include <asm/uaccess.h>
+
+/* ---- 2.4.X KERNEL SUPPORT -----------------------*/
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ #include <asm/uaccess.h>
+ #include <linux/inetdevice.h>
+ #include <linux/netdevice.h>
+#else
+ #include <asm/segment.h>
+ #include <net/route.h>          /* Adding new route entries : 2.0.X kernels */
+#endif
 
 #include <linux/if.h>
+#include <linux/wanpipe.h>
 #include <linux/sdla_ppp.h>		/* PPP firmware API definitions */
 #include <linux/sdlasfm.h>		/* S514 Type Definition */
 /****** Defines & Macros ****************************************************/
 
-#ifdef	_DEBUG_
-#define	STATIC
-#else
-#define	STATIC		static
-#endif
-
 #define	PPP_DFLT_MTU	1500		/* default MTU */
 #define	PPP_MAX_MTU	4000		/* maximum MTU */
 #define PPP_HDR_LEN	1
 
+#define MAX_IP_ERRORS 100 
+
 #define	CONNECT_TIMEOUT	(90*HZ)		/* link connection timeout */
 #define	HOLD_DOWN_TIME	(5*HZ)		/* link hold down time : Changed from 30 to 5 */
 
@@ -125,9 +145,10 @@
 #define LCP(a)   ( a == 0x09 ? "OPEN" : "CLOSED" )
 #define IP(a)    ( a == 0x09 ? "ENABLED" : "DISABLED" )
 
-#define TMR_INT_ENABLED_UPDATE  	1
-#define TMR_INT_ENABLED_PPP_EVENT	2
-#define TMR_INT_ENABLED_UDP		4
+#define TMR_INT_ENABLED_UPDATE  	0x01
+#define TMR_INT_ENABLED_PPP_EVENT	0x02
+#define TMR_INT_ENABLED_UDP		0x04
+#define TMR_INT_ENABLED_CONFIG		0x20
 
 /* Set Configuraton Command Definitions */
 #define PERCENT_TX_BUFF			60
@@ -143,8 +164,11 @@
 #define NUM_AUTH_REQ_WITHOUT_REPLY      10
 
 #define END_OFFSET 0x1F0
+#if LINUX_VERSION_CODE < 0x020125
+#define test_and_set_bit set_bit
+#define net_ratelimit() 1
+#endif
 
-#define TX_TIMEOUT (5*HZ)
 
 /******Data Structures*****************************************************/
 
@@ -155,9 +179,7 @@
   
 typedef struct ppp_private_area
 {
-	/* This member must be first. */
-	struct net_device *slave;	/* WAN slave */
-
+	netdevice_t *slave;
 	sdla_t* card;	
 	unsigned long router_start_time;	/*router start time in sec */
 	unsigned long tick_counter;		/*used for 5 second counter*/
@@ -173,12 +195,20 @@
 	u32 ip_local;				/* Local IP Address */
 	u32 ip_remote;				/* remote IP Address */
 
+	u32 ip_local_tmp;
+	u32 ip_remote_tmp;
+	
 	unsigned char timer_int_enabled;	/* Who enabled the timer inter*/
 	unsigned char update_comms_stats;	/* Used by update function */
 	unsigned long curr_trace_addr;		/* Trace information */
 	unsigned long start_trace_addr;
 	unsigned long end_trace_addr;
 
+	unsigned char interface_down;		/* Brind down interface when channel 
+                                                   goes down */
+	unsigned long config_wait_timeout;	/* After if_open() if in dynamic if mode,
+						   wait a few seconds before configuring */
+	
 	unsigned short udp_pkt_lgth;
 	char  udp_pkt_src;
       	char  udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT];
@@ -191,6 +221,16 @@
 
 	unsigned long router_up_time; 
 
+	/* Polling task queue. Each interface
+         * has its own task queue, which is used
+         * to defer events from the interrupt */
+	struct tq_struct poll_task;
+	struct timer_list poll_delay_timer;
+
+	u8 gateway;
+	u8 config_ppp;
+	u8 ip_error;
+	
 }ppp_private_area_t;
 
 /* variable for keeping track of enabling/disabling FT1 monitor status */
@@ -203,22 +243,32 @@
 
 /* WAN link driver entry points. These are called by the WAN router module. */
 static int update(wan_device_t *wandev);
-static int new_if(wan_device_t *wandev, struct net_device *dev, wanif_conf_t *conf);
-static int del_if(wan_device_t *wandev, struct net_device *dev);
+static int new_if(wan_device_t *wandev, netdevice_t *dev, wanif_conf_t *conf);
+static int del_if(wan_device_t *wandev, netdevice_t *dev);
 
 /* WANPIPE-specific entry points */
 static int wpp_exec (struct sdla *card, void *u_cmd, void *u_data);
 
 /* Network device interface */
-static int if_init(struct net_device *dev);
-static int if_open(struct net_device *dev);
-static int if_close(struct net_device *dev);
-static void if_tx_timeout (struct net_device *dev);
-static int if_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, 
+static int if_init(netdevice_t *dev);
+static int if_open(netdevice_t *dev);
+static int if_close(netdevice_t *dev);
+static int if_header(struct sk_buff *skb, netdevice_t *dev, unsigned short type, 
 		     void *daddr, void *saddr, unsigned len);
+
+#ifdef LINUX_2_4
+static void if_tx_timeout (netdevice_t *dev);
+#endif
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 static int if_rebuild_hdr(struct sk_buff *skb);
-static struct net_device_stats *if_stats(struct net_device *dev);
-static int if_send(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *if_stats(netdevice_t *dev);
+#else
+static struct enet_statistics *if_stats(netdevice_t *dev);
+static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
+        struct sk_buff* skb);
+#endif
+static int if_send(struct sk_buff *skb, netdevice_t *dev);
 
 
 /* PPP firmware interface functions */
@@ -229,27 +279,28 @@
 static int ppp_set_intr_mode(sdla_t *card, unsigned char mode);
 static int ppp_comm_enable(sdla_t *card);
 static int ppp_comm_disable(sdla_t *card);
+static int ppp_comm_disable_shutdown(sdla_t *card);
 static int ppp_get_err_stats(sdla_t *card);
 static int ppp_send(sdla_t *card, void *data, unsigned len, unsigned proto);
 static int ppp_error(sdla_t *card, int err, ppp_mbox_t *mb);
 
-STATIC void wpp_isr(sdla_t *card);
+static void wpp_isr(sdla_t *card);
 static void rx_intr(sdla_t *card);
 static void event_intr(sdla_t *card);
 static void timer_intr(sdla_t *card);
 
 /* Background polling routines */
 static void process_route(sdla_t *card);
-static void poll_disconnected(sdla_t *card);
+static void retrigger_comm(sdla_t *card);
 
 /* Miscellaneous functions */
 static int read_info( sdla_t *card );
 static int read_connection_info (sdla_t *card);
-static int remove_route( sdla_t *card );
-static int config508(ppp_private_area_t *ppp_priv_area, sdla_t *card);
+static void remove_route( sdla_t *card );
+static int config508(netdevice_t *dev, sdla_t *card);
 static void show_disc_cause(sdla_t * card, unsigned cause);
 static int reply_udp( unsigned char *data, unsigned int mbox_len );
-static void process_udp_mgmt_pkt(sdla_t *card, struct net_device *dev, 
+static void process_udp_mgmt_pkt(sdla_t *card, netdevice_t *dev, 
 				ppp_private_area_t *ppp_priv_area);
 static void init_ppp_tx_rx_buff( sdla_t *card );
 static int intr_test( sdla_t *card );
@@ -258,12 +309,20 @@
 static void init_global_statistics( sdla_t *card );
 static int tokenize(char *str, char **tokens);
 static char* strstrip(char *str, char *s);
-static int chk_bcast_mcast_addr(sdla_t* card, struct net_device* dev,
+static int chk_bcast_mcast_addr(sdla_t* card, netdevice_t* dev,
 				struct sk_buff *skb);
+
+static int config_ppp (sdla_t *);
+static void ppp_poll(netdevice_t *);
+static void trigger_ppp_poll(netdevice_t *);
+static void ppp_poll_delay (unsigned long dev_ptr);
+
+
 static int Read_connection_info;
 static int Intr_test_counter;
 static unsigned short available_buffer_space;
 
+
 /* IPX functions */
 static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, 
 			       unsigned char incoming);
@@ -275,12 +334,11 @@
 static void s508_unlock (sdla_t *card, unsigned long *smp_flags);
 
 static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card,
-                                struct sk_buff *skb, struct net_device* dev,
+                                struct sk_buff *skb, netdevice_t* dev,
                                 ppp_private_area_t* ppp_priv_area );
 static unsigned short calc_checksum (char *data, int len);
-
-
-
+static void disable_comm (sdla_t *card);
+static int detect_and_fix_tx_bug (sdla_t *card);
 
 /****** Public Functions ****************************************************/
 
@@ -298,6 +356,7 @@
  */
 int wpp_init(sdla_t *card, wandev_conf_t *conf)
 {
+	ppp_flags_t *flags;
 	union
 	{
 		char str[80];
@@ -329,6 +388,7 @@
 			return -EINVAL;
 
 	}
+	flags = card->flags;
 
 	/* Read firmware version.  Note that when adapter initializes, it
 	 * clears the mailbox, so it may appear that the first command was
@@ -353,9 +413,10 @@
 	card->wandev.update	= &update;
 	card->wandev.new_if	= &new_if;
 	card->wandev.del_if	= &del_if;
-	card->wandev.state	= WAN_DISCONNECTED;
         card->wandev.udp_port   = conf->udp_port;
 	card->wandev.ttl	= conf->ttl;
+	card->wandev.state      = WAN_DISCONNECTED;
+	card->disable_comm	= &disable_comm;
 	card->irq_dis_if_send_count = 0;
         card->irq_dis_poll_count = 0;
 	card->u.p.authenticator = conf->u.ppp.authenticator;
@@ -367,6 +428,33 @@
 	/* initialize global statistics */
 	init_global_statistics( card );
 
+
+
+	if (!card->configured){
+		int err;
+
+		Intr_test_counter = 0;
+		err = intr_test(card);
+
+		if(err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) {
+			printk("%s: Interrupt Test Failed, Counter: %i\n", 
+				card->devname, Intr_test_counter);
+			printk( "%s: Please choose another interrupt\n",card->devname);
+			return -EIO;
+		}
+		
+		printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", 
+			card->devname, Intr_test_counter);
+		card->configured = 1;
+	}
+
+	ppp_set_intr_mode(card, PPP_INTR_TIMER); 
+
+	/* Turn off the transmit and timer interrupt */
+	flags->imask &= ~PPP_INTR_TIMER;
+
+	printk(KERN_INFO "\n");
+
 	return 0;
 }
 
@@ -378,8 +466,8 @@
 static int update(wan_device_t *wandev)
 {
 	sdla_t* card = wandev->private;
- 	struct net_device* dev = card->wandev.dev;
-        volatile ppp_private_area_t *ppp_priv_area = dev->priv;
+ 	netdevice_t* dev;
+        volatile ppp_private_area_t *ppp_priv_area;
 	ppp_flags_t *flags = card->flags;
 	unsigned long timeout;
 
@@ -390,9 +478,17 @@
 	if (wandev->state == WAN_UNCONFIGURED)
 		return -ENODEV;
 	
-	//FIXME: Do we need this
-	if (test_bit(0, (void*)&wandev->critical))
-		return -EAGAIN;
+	/* Shutdown bug fix. This function can be
+         * called with NULL dev pointer during
+         * shutdown 
+	 */
+	if ((dev=card->wandev.dev) == NULL){
+		return -ENODEV;
+	}
+
+	if ((ppp_priv_area=dev->priv) == NULL){
+		return -ENODEV;
+	}
 	
 	ppp_priv_area->update_comms_stats = 2;
 	ppp_priv_area->timer_int_enabled |= TMR_INT_ENABLED_UPDATE;
@@ -427,17 +523,21 @@
  * Return:	0	o.k.
  *		< 0	failure (channel will not be created)
  */
-static int new_if(wan_device_t *wandev, struct net_device *dev, wanif_conf_t *conf)
+static int new_if(wan_device_t *wandev, netdevice_t *dev, wanif_conf_t *conf)
 {
 	sdla_t *card = wandev->private;
 	ppp_private_area_t *ppp_priv_area;
-	
+
 	if (wandev->ndev)
 		return -EEXIST;
 	
+
+	printk(KERN_INFO "%s: Configuring Interface: %s\n",
+			card->devname, conf->name);
+
 	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
 
-		printk(KERN_INFO "%s: invalid interface name!\n",
+		printk(KERN_INFO "%s: Invalid interface name!\n",
 			card->devname);
 		return -EINVAL;
 
@@ -464,6 +564,14 @@
 	ppp_priv_area->pap = conf->pap;
 	ppp_priv_area->chap = conf->chap;
 
+	/* Option to bring down the interface when 
+         * the link goes down */
+	if (conf->if_down){
+		set_bit(DYN_OPT_ON,&ppp_priv_area->interface_down);
+		printk("%s: Dynamic interface configuration enabled\n",
+			card->devname);
+	} 
+
 	/* If no user ids are specified */
 	if(!strlen(conf->userid) && (ppp_priv_area->pap||ppp_priv_area->chap)){
 		kfree(ppp_priv_area);
@@ -494,16 +602,48 @@
 
 	
 	ppp_priv_area->enable_IPX = conf->enable_IPX;
-	if (conf->network_number)
+	if (conf->network_number){
 		ppp_priv_area->network_number = conf->network_number;
-	else
+	}else{
 		ppp_priv_area->network_number = 0xDEADBEEF;
+	}
 
+	/* Tells us that if this interface is a
+         * gateway or not */
+	if ((ppp_priv_area->gateway = conf->gateway) == WANOPT_YES){
+		printk(KERN_INFO "%s: Interface %s is set as a gateway.\n",
+			card->devname,card->u.p.if_name);
+	}
 
 	/* prepare network device data space for registration */
-	strcpy(dev->name, card->u.p.if_name);
+      #ifdef LINUX_2_4
+ 	strcpy(dev->name,card->u.p.if_name);
+      #else
+	dev->name = (char *)kmalloc(strlen(card->u.p.if_name) + 2, GFP_KERNEL); 
+	sprintf(dev->name, "%s", card->u.p.if_name);
+      #endif
+	
 	dev->init = &if_init;
 	dev->priv = ppp_priv_area;
+	dev->mtu = min(dev->mtu, card->wandev.mtu);
+
+	/* Initialize the polling task routine */
+      #ifndef LINUX_2_4
+	ppp_priv_area->poll_task.next = NULL;
+      #endif
+	ppp_priv_area->poll_task.sync=0;
+	ppp_priv_area->poll_task.routine = (void*)(void*)ppp_poll;
+	ppp_priv_area->poll_task.data = dev;
+
+	/* Initialize the polling delay timer */
+	init_timer(&ppp_priv_area->poll_delay_timer);
+	ppp_priv_area->poll_delay_timer.data = (unsigned long)dev;
+	ppp_priv_area->poll_delay_timer.function = ppp_poll_delay;
+	
+	
+	/* Since we start with dummy IP addresses we can say
+	 * that route exists */
+	printk(KERN_INFO "\n");
 
 	return 0;
 }
@@ -511,17 +651,17 @@
 /*============================================================================
  * Delete logical channel.
  */
-static int del_if(wan_device_t *wandev, struct net_device *dev)
+static int del_if(wan_device_t *wandev, netdevice_t *dev)
 {
-	if (dev->priv) {
-
-                kfree(dev->priv);
-                dev->priv = NULL;
-        }
-
 	return 0;
 }
 
+static void disable_comm (sdla_t *card)
+{
+	ppp_comm_disable_shutdown(card);
+	return;
+}
+
 /****** WANPIPE-specific entry points ***************************************/
 
 /*============================================================================
@@ -534,6 +674,7 @@
 	ppp_mbox_t *mbox = card->mbox;
 	int len;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	if (copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t)))
 		return -EFAULT;
 
@@ -558,6 +699,35 @@
 	if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
 		return -EFAULT;
 
+#else
+
+        if (!u_cmd || verify_area(VERIFY_WRITE, u_cmd, sizeof(ppp_cmd_t)))
+                return -EFAULT;
+
+        memcpy_fromfs((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t));
+
+	len = mbox->cmd.length;
+
+        if (len) {
+
+                if (!u_data || verify_area(VERIFY_READ, u_data, len))
+                        return -EFAULT;
+        }
+
+        /* execute command */
+        if (!sdla_exec(mbox))
+               	return -EIO;
+
+        /* return result */
+        memcpy_tofs(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t));
+        len = mbox->cmd.length;
+
+        if (len && u_data && !verify_area(VERIFY_WRITE, u_data, len))
+                memcpy_tofs(u_data, (void*)&mbox->data, len);
+
+
+#endif
+
 	return 0;
 }
 
@@ -570,14 +740,14 @@
  * interface registration.  Returning anything but zero will fail interface
  * registration.
  */
-static int if_init(struct net_device *dev)
+static int if_init(netdevice_t *dev)
 {
 	ppp_private_area_t *ppp_priv_area = dev->priv;
 	sdla_t *card = ppp_priv_area->card;
 	wan_device_t *wandev = &card->wandev;
-#ifndef LINUX_2_1
+      #ifdef LINUX_2_0
 	int i;
-#endif
+      #endif
 
 	/* Initialize device driver entry points */
 	dev->open		= &if_open;
@@ -586,21 +756,24 @@
 	dev->rebuild_header	= &if_rebuild_hdr;
 	dev->hard_start_xmit	= &if_send;
 	dev->get_stats		= &if_stats;
+      #ifdef LINUX_2_4
 	dev->tx_timeout		= &if_tx_timeout;
 	dev->watchdog_timeo	= TX_TIMEOUT;
+      #endif
 
 	/* Initialize media-specific parameters */
 	dev->type		= ARPHRD_PPP;	/* ARP h/w type */
-	dev->flags	       |= IFF_POINTOPOINT;
+	dev->flags		|= IFF_POINTOPOINT;
+	dev->flags		|= IFF_NOARP;
 
 	/* Enable Mulitcasting if specified by user*/
 	if (ppp_priv_area->mc == WANOPT_YES){
-		dev->flags     |= IFF_MULTICAST;
+		dev->flags	|= IFF_MULTICAST;
 	}
 
-#ifndef LINUX_2_1
+      #ifdef LINUX_2_0
 	dev->family		= AF_INET;
-#endif	
+      #endif	
 	dev->mtu		= wandev->mtu;
 	dev->hard_header_len	= PPP_HDR_LEN;	/* media header length */
 
@@ -615,7 +788,13 @@
         dev->tx_queue_len = 100;
    
 	/* Initialize socket buffers */
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 	dev_init_buffers(dev);
+      #else
+        for (i = 0; i < DEV_NUMBUFFS; ++i)
+                skb_queue_head_init(&dev->buffs[i]);
+      #endif
+
 	return 0;
 }
 
@@ -626,105 +805,44 @@
  *
  * Return 0 if O.k. or errno.
  */
-static int if_open(struct net_device *dev)
+static int if_open (netdevice_t *dev)
 {
 	ppp_private_area_t *ppp_priv_area = dev->priv;
 	sdla_t *card = ppp_priv_area->card;
-	ppp_flags_t *flags = card->flags;
 	struct timeval tv;
-	int err = 0;
-
-	if (netif_running(dev))
-		return -EBUSY;		/* only one open is allowed */
-	
-	if (test_and_set_bit(0, (void*)&card->wandev.critical))
-		return -EAGAIN;
-
-	if (!card->configured){
-
-		if (config508(ppp_priv_area, card)){
-
-			err = -EIO;
-			card->wandev.critical = 0;
-			return err;
-		}
-
-		Intr_test_counter = 0;
-		err = intr_test( card );
-
-		if(err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) {
-			printk("%s: Interrupt Test Failed, Counter: %i\n", 
-				card->devname, Intr_test_counter);
-			printk( "%s: Please choose another interrupt\n",card->devname);
-			err = -EIO;
-			card->wandev.critical = 0;
-			return err;
-		}
-		
-		printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", 
-			card->devname, Intr_test_counter);
-		card->configured = 1;
-
-	}
-
-	/* Initialize Rx/Tx buffer control fields */
-	init_ppp_tx_rx_buff( card );
-
-	if (ppp_set_intr_mode(card, PPP_INTR_RXRDY|
-			    		PPP_INTR_TXRDY|
-				    	PPP_INTR_MODEM|
-				    	PPP_INTR_CMD  |
-				    	PPP_INTR_DISC |
-				    	PPP_INTR_OPEN |
-				    	PPP_INTR_DROP_DTR |
-					PPP_INTR_TIMER)) {
-	
-		err = -EIO;
-		card->wandev.critical = 0;
-		return err;
+	//unsigned long smp_flags;
 
-	}
-
-        /* Turn off the transmit and timer interrupt */
-	flags->imask &= ~(PPP_INTR_TXRDY | PPP_INTR_TIMER) ;
+	if (is_dev_running(dev))
+		return -EBUSY;
 
-	/* If you are not the authenticator and any one of the protocol is 
-	 * enabled then we call the set_out_bound_authentication.
-	 */
-	if ( !card->u.p.authenticator  && (ppp_priv_area->pap || ppp_priv_area->chap)) {
-		if ( ppp_set_outbnd_auth(card, ppp_priv_area) ){
-			err = -EIO;
-			card->wandev.critical = 0;
-			return err;
-		}
-	} 
-	
-	/* If you are the authenticator and any one of the protocol is enabled
-	 * then we call the set_in_bound_authentication.
-	 */
-	if ( card->u.p.authenticator && (ppp_priv_area->pap || ppp_priv_area->chap)) {
-		if ( ppp_set_inbnd_auth(card, ppp_priv_area) ){
-			err = -EIO;
-			card->wandev.critical = 0;
-			return err;
-		}
-	}
+	wanpipe_open(card);
 
-	if (ppp_comm_enable(card)) {
-		err = -EIO;
-		card->wandev.critical = 0;
-		return err;
-	}
+      #ifdef LINUX_2_4
+	netif_start_queue(dev);
+      #else
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	dev->start = 1;
+      #endif
 	
-	wanpipe_set_state(card, WAN_CONNECTING);
-	wanpipe_open(card);
-	dev->mtu = min(dev->mtu, card->wandev.mtu);
-	netif_start_queue(dev);	
 	do_gettimeofday( &tv );
 	ppp_priv_area->router_start_time = tv.tv_sec;
-	card->wandev.critical = 0;
 
-	return err;
+	/* We cannot configure the card here because we don't
+	 * have access to the interface IP addresses.
+         * Once the interface initilization is complete, we will be
+         * able to access the IP addresses.  Therefore,
+         * configure the ppp link in the poll routine */
+	set_bit(0,&ppp_priv_area->config_ppp);
+	ppp_priv_area->config_wait_timeout=jiffies;
+
+	/* Start the PPP configuration after 1sec delay.
+	 * This will give the interface initilization time
+	 * to finish its configuration */
+	del_timer(&ppp_priv_area->poll_delay_timer);
+	ppp_priv_area->poll_delay_timer.expires = jiffies+HZ;
+	add_timer(&ppp_priv_area->poll_delay_timer);
+	return 0;
 }
 
 /*============================================================================
@@ -732,20 +850,18 @@
  * o if this is the last open, then disable communications and interrupts.
  * o reset flags.
  */
-static int if_close(struct net_device *dev)
+static int if_close(netdevice_t *dev)
 {
 	ppp_private_area_t *ppp_priv_area = dev->priv;
 	sdla_t *card = ppp_priv_area->card;
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical))
-		return -EAGAIN;
-	
-	netif_stop_queue(dev);
+	stop_net_queue(dev);
+      #ifndef LINUX_2_4
+	dev->start=0;
+      #endif
 	wanpipe_close(card);
-	wanpipe_set_state(card, WAN_DISCONNECTED);
-	ppp_set_intr_mode(card, 0);
-	ppp_comm_disable(card);
-	card->wandev.critical = 0;
+
+	del_timer (&ppp_priv_area->poll_delay_timer);
 	return 0;
 }
 
@@ -758,13 +874,12 @@
  *
  * Return:	media header length.
  */
-static int if_header(struct sk_buff *skb, struct net_device *dev,
+static int if_header(struct sk_buff *skb, netdevice_t *dev,
 	unsigned short type, void *daddr, void *saddr, unsigned len)
 {
 	switch (type)
 	{
 		case ETH_P_IP:
-	
 		case ETH_P_IPX:
 			skb->protocol = htons(type);
 			break;
@@ -782,10 +897,10 @@
  * Return:	1	physical address resolved.
  *		0	physical address not resolved
  */
-
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 static int if_rebuild_hdr (struct sk_buff *skb)
 {
-	struct net_device *dev = skb->dev;
+	netdevice_t *dev = skb->dev;
 	ppp_private_area_t *ppp_priv_area = dev->priv;
 	sdla_t *card = ppp_priv_area->card;
 
@@ -794,33 +909,39 @@
 	return 1;
 }
 
+#else
+static int if_rebuild_hdr (void* hdr, netdevice_t* dev, unsigned long raddr,
+                           struct sk_buff* skb)
+{
+	return 1;
+
+}
+#endif
 
+#ifdef LINUX_2_4
 /*============================================================================
- * Handle transmit timeout from netif watchdog
+ * Handle transmit timeout event from netif watchdog
  */
-static void if_tx_timeout (struct net_device *dev)
+static void if_tx_timeout (netdevice_t *dev)
 {
-	ppp_private_area_t *ppp_priv_area = dev->priv;
-	sdla_t *card = ppp_priv_area->card;
-
-
+    	ppp_private_area_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	
 	/* If our device stays busy for at least 5 seconds then we will
-	 * kick start the device by making dev->tbusy = 0.  We expect 
-	 * that our device never stays busy more than 5 seconds. So this
-	 * is only used as a last resort. 
+	 * kick start the device by making dev->tbusy = 0.  We expect
+	 * that our device never stays busy more than 5 seconds. So this                 
+	 * is only used as a last resort.
 	 */
 
-	++ppp_priv_area->if_send_stat.if_send_tbusy;
+	++ chan->if_send_stat.if_send_tbusy;
 	++card->wandev.stats.collisions;
 
-	printk (KERN_INFO "%s: Transmit times out\n", card->devname);
-
-	++ppp_priv_area->if_send_stat.if_send_tbusy_timeout;
-	++card->wandev.stats.collisions;
-
-	/* unbusy the card (because only one interface per card) */
-	netif_wake_queue(dev);
+	printk (KERN_INFO "%s: Transmit timed out on %s\n", card->devname,dev->name);
+	++chan->if_send_stat.if_send_tbusy_timeout;
+	netif_wake_queue (dev);
 }
+#endif
+
 
 
 /*============================================================================
@@ -840,19 +961,22 @@
  * 2. Setting tbusy flag will inhibit further transmit requests from the
  *    protocol stack and can be used for flow control with protocol layer.
  */
-static int if_send (struct sk_buff *skb, struct net_device *dev)
+static int if_send (struct sk_buff *skb, netdevice_t *dev)
 {
 	ppp_private_area_t *ppp_priv_area = dev->priv;
 	sdla_t *card = ppp_priv_area->card;
 	unsigned char *sendpacket;
 	unsigned long smp_flags;
 	ppp_flags_t *flags = card->flags;
-	int retry = 0;
 	int udp_type;
-	
+	int err=0;
 	
 	++ppp_priv_area->if_send_stat.if_send_entry;
 
+      #ifdef LINUX_2_4
+	netif_stop_queue(dev);
+      #endif
+	
 	if (skb == NULL) {
 
 		/* If we get here, some higher layer thinks we've missed an
@@ -862,12 +986,37 @@
 			card->devname, dev->name);
 		
 		++ppp_priv_area->if_send_stat.if_send_skb_null;
-		
-		netif_wake_queue(dev);
+	
+		wake_net_dev(dev);
 		return 0;
-
 	}
 
+      #ifndef LINUX_2_4
+	if (dev->tbusy) {
+
+		/* If our device stays busy for at least 5 seconds then we will
+		 * kick start the device by making dev->tbusy = 0.  We expect 
+		 * that our device never stays busy more than 5 seconds. So this
+		 * is only used as a last resort. 
+		 */
+              
+		++ppp_priv_area->if_send_stat.if_send_tbusy;
+        	++card->wandev.stats.collisions;
+
+		if ((jiffies - ppp_priv_area->tick_counter) < (5*HZ)) {
+			return 1;
+		}
+
+		printk (KERN_INFO "%s: Transmit times out on %s\n",card->devname,dev->name);
+	
+		++ppp_priv_area->if_send_stat.if_send_tbusy_timeout;
+		++card->wandev.stats.collisions;
+
+		/* unbusy the card (because only one interface per card)*/
+		dev->tbusy = 0;
+	}	
+      #endif
+	
 	sendpacket = skb->data;
 
 	udp_type = udp_pkt_type( skb, card );
@@ -879,15 +1028,18 @@
 	               	flags->imask |= PPP_INTR_TIMER;
 		}
 		++ppp_priv_area->if_send_stat.if_send_PIPE_request;
+		start_net_queue(dev);
 		return 0;
-
 	}
 
 	/* Check for broadcast and multicast addresses 
 	 * If found, drop (deallocate) a packet and return.
 	 */
 	if(chk_bcast_mcast_addr(card, dev, skb)){
-                        return 0;
+		++card->wandev.stats.tx_dropped;
+		wan_dev_kfree_skb(skb,FREE_WRITE);
+		start_net_queue(dev);
+		return 0;
 	}
 
 
@@ -895,31 +1047,28 @@
 		s508_lock(card,&smp_flags);
 	}
 
-    	if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
+    	if (test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
 
 		printk(KERN_INFO "%s: Critical in if_send: %lx\n",
 				card->wandev.name,card->wandev.critical);
-    		dev_kfree_skb(skb);
-
+		
 		++card->wandev.stats.tx_dropped;
 		++ppp_priv_area->if_send_stat.if_send_critical_non_ISR;
-	
-		if(card->hw.type != SDLA_S514){	
-			s508_unlock(card,&smp_flags);
-		}
-
-		return 0;
+		start_net_queue(dev);
+		goto if_send_exit_crit;
 	}
 
 	if (card->wandev.state != WAN_CONNECTED) {
 
 		++ppp_priv_area->if_send_stat.if_send_wan_disconnected;
         	++card->wandev.stats.tx_dropped;
-
+		start_net_queue(dev);
+		
      	} else if (!skb->protocol) {
 		++ppp_priv_area->if_send_stat.if_send_protocol_error;
         	++card->wandev.stats.tx_errors;
-
+		start_net_queue(dev);
+		
 	} else {
 
 		/*If it's IPX change the network numbers to 0 if they're ours.*/
@@ -929,37 +1078,45 @@
 					ppp_priv_area->network_number, 0);
 			} else {
 				++card->wandev.stats.tx_dropped;
-				goto tx_done;
+				start_net_queue(dev);
+				goto if_send_exit_crit;
 			}
 		}
 
 		if (ppp_send(card, skb->data, skb->len, skb->protocol)) {
-			retry = 1;
-			netif_stop_queue(dev);
+			stop_net_queue(dev);
 			++ppp_priv_area->if_send_stat.if_send_adptr_bfrs_full;
 			++ppp_priv_area->if_send_stat.if_send_tx_int_enabled;
-			ppp_priv_area->tick_counter = jiffies;
-			flags->imask |= 0x02;	/* unmask Tx interrupts */
 		} else {
 			++ppp_priv_area->if_send_stat.if_send_bfr_passed_to_adptr;
 			++card->wandev.stats.tx_packets;
+                      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 			card->wandev.stats.tx_bytes += skb->len;
+                      #endif
+			start_net_queue(dev);
+		      #ifdef LINUX_2_4
+			dev->trans_start = jiffies;
+		      #endif
+
+
 		}
     	}
 	
-tx_done:	
-	if (!retry){
-		dev_kfree_skb(skb);
+if_send_exit_crit:
+	
+	if (!(err=is_queue_stopped(dev))){
+      		wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+		ppp_priv_area->tick_counter = jiffies;
+		flags->imask |= PPP_INTR_TXRDY;	/* unmask Tx interrupts */
 	}
-
-	card->wandev.critical = 0;
 	
+	clear_bit(SEND_CRIT,&card->wandev.critical);
 	if(card->hw.type != SDLA_S514){	
 		s508_unlock(card,&smp_flags);
 	}
 
-	
-	return retry;
+	return err;
 }
 
 
@@ -968,7 +1125,7 @@
  */
 
 static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card,
-                                struct sk_buff *skb, struct net_device* dev,
+                                struct sk_buff *skb, netdevice_t* dev,
                                 ppp_private_area_t* ppp_priv_area )
 {
 	int udp_pkt_stored = 0;
@@ -982,8 +1139,13 @@
 		udp_pkt_stored = 1;
 	}else{
 		if (skb->len > MAX_LGTH_UDP_MGNT_PKT){
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 			printk(KERN_INFO "%s: PIPEMON UDP request too long : %i\n",
 				card->devname, skb->len);
+#else
+			printk(KERN_INFO "%s: PIPEMON UDP request too long : %li\n",
+				card->devname, skb->len);
+#endif
 		}else{
 			printk(KERN_INFO "%s: PIPEMON UPD request already pending\n",
 				card->devname);
@@ -991,7 +1153,12 @@
 		ppp_priv_area->udp_pkt_lgth = 0;
 	}
 
-	dev_kfree_skb(skb);
+	if(udp_pkt_src == UDP_PKT_FRM_STACK){
+		wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+                wan_dev_kfree_skb(skb, FREE_READ);
+	}
+
 	return(udp_pkt_stored);
 }
 
@@ -1157,7 +1324,11 @@
  * Get ethernet-style interface statistics.
  * Return a pointer to struct net_device_stats.
  */
-static struct net_device_stats *if_stats(struct net_device *dev)
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats *if_stats(netdevice_t *dev)
+#else
+static struct enet_statistics *if_stats(netdevice_t *dev)
+#endif
 {
 
 	ppp_private_area_t *ppp_priv_area = dev->priv;
@@ -1354,7 +1525,7 @@
 
        /* If timer has been enabled, set the timer delay to 1sec */
        if (mode & 0x80){
-       		ppp_intr_data->timer_len = 5;//100; //250;
+       		ppp_intr_data->timer_len = 250; //5;//100; //250;
                 mb->cmd.length = 4;
         }
 	
@@ -1382,7 +1553,9 @@
 	
 	if (err != CMD_OK) 
 		ppp_error(card, err, mb);
-	
+	else	
+		card->u.p.comm_enabled = 1;	
+
 	return err;
 }
 
@@ -1399,10 +1572,46 @@
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
 	if (err != CMD_OK) 
 		ppp_error(card, err, mb);
+	else
+		card->u.p.comm_enabled = 0;
 
 	return err;
 }
 
+static int ppp_comm_disable_shutdown(sdla_t *card)
+{
+	ppp_mbox_t *mb = card->mbox;
+	ppp_intr_info_t *ppp_intr_data;
+	int err;
+
+	if (!mb){
+		return 1;
+	}
+	
+	ppp_intr_data = (ppp_intr_info_t *) &mb->data[0];
+	
+	/* Disable all interrupts */
+	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
+	ppp_intr_data->i_enable = 0;
+
+	ppp_intr_data->irq = card->hw.irq;
+	mb->cmd.length = 2;
+
+	mb->cmd.command = PPP_SET_INTR_FLAGS;
+	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+
+	/* Disable communicatinons */
+	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
+	mb->cmd.command = PPP_COMM_DISABLE;
+	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+
+	card->u.p.comm_enabled = 0;
+
+	return 0;
+}
+
+
+
 /*============================================================================
  * Get communications error statistics.
  */
@@ -1495,36 +1704,40 @@
 /*============================================================================
  * PPP interrupt service routine.
  */
-STATIC void wpp_isr(sdla_t *card)
+static void wpp_isr (sdla_t *card)
 {
 	ppp_flags_t *flags = card->flags;
 	char *ptr = &flags->iflag;
-	struct net_device *dev = card->wandev.dev;
-
-
+	netdevice_t *dev = card->wandev.dev;
 	int i;
 
 	card->in_isr = 1;
-	
 	++card->statistics.isr_entry;
 
-	//FIXME: Do we need this
-	card->force_enable_irq = 0;
-
+	if (!dev && flags->iflag != PPP_INTR_CMD){
+		card->in_isr = 0;
+		flags->iflag = 0;
+		return;
+	}
+	
+	if (test_bit(PERI_CRIT, (void*)&card->wandev.critical)) {
+		card->in_isr = 0;
+		flags->iflag = 0;
+		return;
+	}
+	
+	
 	if(card->hw.type != SDLA_S514){
-		if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
-        	
+		if (test_bit(SEND_CRIT, (void*)&card->wandev.critical)) {
 			++card->statistics.isr_already_critical;
 			printk (KERN_INFO "%s: Critical while in ISR!\n",
 					card->devname);
 			card->in_isr = 0;
+			flags->iflag = 0;
 			return;
-	
 		}
 	}
 
-	card->buff_int_mode_unbusy = 0;
-
 	switch (flags->iflag) {
 
 		case PPP_INTR_RXRDY:	/* receive interrupt  0x01  (bit 0)*/
@@ -1535,8 +1748,7 @@
 		case PPP_INTR_TXRDY:	/* transmit interrupt  0x02 (bit 1)*/
 			++card->statistics.isr_tx;
 			flags->imask &= ~PPP_INTR_TXRDY;
-			netif_wake_queue (dev);
-			card->buff_int_mode_unbusy = 1;
+			wake_net_dev(dev);
 			break;
 
 		case PPP_INTR_CMD:      /* interface command completed */
@@ -1567,10 +1779,7 @@
 	
 	card->in_isr = 0;
 	flags->iflag = 0;
-	card->wandev.critical = 0;
-
-	if(card->buff_int_mode_unbusy)
-		netif_wake_queue(dev);
+	return;
 }
 
 /*============================================================================
@@ -1579,7 +1788,7 @@
 static void rx_intr(sdla_t *card)
 {
 	ppp_buf_ctl_t *rxbuf = card->rxmb;
-	struct net_device *dev = card->wandev.dev;
+	netdevice_t *dev = card->wandev.dev;
 	ppp_private_area_t *ppp_priv_area;
 	struct sk_buff *skb;
 	unsigned len;
@@ -1603,11 +1812,21 @@
 		printk(KERN_INFO "\n");	
 		
 		++card->statistics.rx_intr_corrupt_rx_bfr;
-		return;
 
-	}
 
-	if (dev && netif_running(dev)) {
+		/* Bug Fix: Mar 6 2000
+                 * If we get a corrupted mailbox, it means that driver 
+                 * is out of sync with the firmware. There is no recovery.
+                 * If we don't turn off all interrupts for this card
+                 * the machine will crash. 
+                 */
+		printk(KERN_INFO "%s: Critical router failure ...!!!\n", card->devname);
+		printk(KERN_INFO "Please contact Sangoma Technologies !\n");
+		ppp_set_intr_mode(card,0);
+		return;
+	}
+      
+	if (dev && is_dev_running(dev) && dev->priv){
 	
 		len  = rxbuf->length;
 		ppp_priv_area = dev->priv;
@@ -1662,9 +1881,12 @@
 			
 				/* Handle an IPXWAN packet */
 				if( ppp_priv_area->enable_IPX) {
-					ppp_send(card, skb->data, skb->len, htons(ETH_P_IPX));
-                        		dev_kfree_skb(skb);
-
+					
+					/* Make sure we are not already sending */
+					if (!test_bit(SEND_CRIT, &card->wandev.critical)){
+					 	ppp_send(card, skb->data, skb->len, htons(ETH_P_IPX));
+					}
+					wan_dev_kfree_skb(skb,FREE_READ);
 
 				} else {
 					++card->wandev.stats.rx_dropped;
@@ -1675,15 +1897,19 @@
 				skb->mac.raw  = skb->data;
 
 			    	++card->wandev.stats.rx_packets;
+ 			      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 				card->wandev.stats.rx_bytes += skb->len;
+			      #endif
 		    		++ppp_priv_area->rx_intr_stat.rx_intr_bfr_passed_to_stack;	
 				netif_rx(skb);
 			}
 
 		} else {
-		
-			printk(KERN_INFO "%s: no socket buffers available!\n",
-				card->devname);
+	
+			if (net_ratelimit()){
+				printk(KERN_INFO "%s: no socket buffers available!\n",
+					card->devname);
+			}
 			++card->wandev.stats.rx_dropped;
 			++ppp_priv_area->rx_intr_stat.rx_intr_no_socket;
 		}
@@ -1703,16 +1929,18 @@
 void event_intr (sdla_t *card)
 {
 
- 	struct net_device* dev = card->wandev.dev;
+ 	netdevice_t* dev = card->wandev.dev;
         ppp_private_area_t* ppp_priv_area = dev->priv;
 	volatile ppp_flags_t *flags = card->flags;
 
 	switch (flags->iflag){
 
 		case PPP_INTR_MODEM:    /* modem status change (DCD, CTS) 0x04  (bit 2)*/
-			printk (KERN_INFO "%s: Modem status: DCD=%s CTS=%s\n",
-				card->devname, DCD(flags->mstatus), CTS(flags->mstatus));
 
+			if (net_ratelimit()){
+				printk (KERN_INFO "%s: Modem status: DCD=%s CTS=%s\n",
+					card->devname, DCD(flags->mstatus), CTS(flags->mstatus));
+			}
 			break;
 
 		case PPP_INTR_DISC:  	/* Data link disconnected 0x10  (bit 4)*/	
@@ -1723,15 +1951,16 @@
 			if (flags->disc_cause &
 				(PPP_LOCAL_TERMINATION | PPP_DCD_CTS_DROP |
 				PPP_REMOTE_TERMINATION)) {
+
 				if (card->u.p.ip_mode == WANOPT_PPP_PEER) { 
-					Read_connection_info = 1;
-					remove_route (card);
+					set_bit(0,&Read_connection_info);
 				}
 				wanpipe_set_state(card, WAN_DISCONNECTED);
+
 				show_disc_cause(card, flags->disc_cause);
-				ppp_priv_area->timer_int_enabled |= 
-						TMR_INT_ENABLED_PPP_EVENT;
+				ppp_priv_area->timer_int_enabled |= TMR_INT_ENABLED_PPP_EVENT;
 				flags->imask |= PPP_INTR_TIMER;
+				trigger_ppp_poll(dev);
 			}
 			break;
 
@@ -1743,30 +1972,68 @@
 
 			if (flags->lcp_state == 0x09 && 
                            (flags->ip_state == 0x09 || flags->ipx_state == 0x09)){
+
                                 /* Initialize the polling timer and set the state
-                                 * to WAN_CONNNECTED 
-                                 */
-                                card->state_tick = jiffies;
-				wanpipe_set_state(card, WAN_CONNECTED);
-				ppp_priv_area->timer_int_enabled |= 
+                                 * to WAN_CONNNECTED */
+
+
+				/* BUG FIX: When the protocol restarts, during heavy 
+                                 * traffic, board tx buffers and driver tx buffers
+                                 * can go out of sync.  This checks the condition
+                                 * and if the tx buffers are out of sync, the 
+                                 * protocols are restarted. 
+                                 * I don't know why the board tx buffer is out
+                                 * of sync. It could be that a packets is tx
+                                 * while the link is down, but that is not 
+                                 * possible. The other possiblility is that the
+                                 * firmware doesn't reinitialize properly.
+                                 * FIXME: A better fix should be found.
+                                 */ 
+				if (detect_and_fix_tx_bug(card)){
+
+					ppp_comm_disable(card);
+
+					wanpipe_set_state(card, WAN_DISCONNECTED);
+
+					ppp_priv_area->timer_int_enabled |= 
 						TMR_INT_ENABLED_PPP_EVENT;
+					flags->imask |= PPP_INTR_TIMER;
+					break;	
+				}
+
+				card->state_tick = jiffies;
+				wanpipe_set_state(card, WAN_CONNECTED);
+
+				NEX_PRINTK(KERN_INFO "CON: L Tx: %lx  B Tx: %lx || L Rx %lx B Rx %lx\n",
+					(unsigned long)card->u.p.txbuf, *card->u.p.txbuf_next,
+					(unsigned long)card->rxmb, *card->u.p.rxbuf_next);
+
+				/* Tell timer interrupt that PPP event occured */
+				ppp_priv_area->timer_int_enabled |= TMR_INT_ENABLED_PPP_EVENT;
 				flags->imask |= PPP_INTR_TIMER;
 
+				/* If we are in PEER mode, we must first obtain the
+				 * IP information and then go into the poll routine */
+				if (card->u.p.ip_mode != WANOPT_PPP_PEER){	
+					trigger_ppp_poll(dev);
+				}
 			}
                    	break;
 
 		case PPP_INTR_DROP_DTR:		/* DTR drop timeout expired  0x40 bit 6 */
 
 			NEX_PRINTK(KERN_INFO "DTR Drop Timeout Interrrupt \n"); 
+
 			if (card->u.p.ip_mode == WANOPT_PPP_PEER) { 
-				Read_connection_info = 1;
-				remove_route (card);
+				set_bit(0,&Read_connection_info);
 			}
+		
 			wanpipe_set_state(card, WAN_DISCONNECTED);
+
 			show_disc_cause(card, flags->disc_cause);
-			ppp_priv_area->timer_int_enabled |= 
-						TMR_INT_ENABLED_PPP_EVENT;
+			ppp_priv_area->timer_int_enabled |= TMR_INT_ENABLED_PPP_EVENT;
 			flags->imask |= PPP_INTR_TIMER;
+			trigger_ppp_poll(dev);
 			break;
 		
 		default:
@@ -1781,10 +2048,18 @@
 void timer_intr (sdla_t *card)
 {
 
-        struct net_device* dev = card->wandev.dev;
+        netdevice_t* dev = card->wandev.dev;
         ppp_private_area_t* ppp_priv_area = dev->priv;
 	ppp_flags_t *flags = card->flags;
 
+
+	if (ppp_priv_area->timer_int_enabled & TMR_INT_ENABLED_CONFIG){
+		if (!config_ppp(card)){
+			ppp_priv_area->timer_int_enabled &= 
+					~TMR_INT_ENABLED_CONFIG;	
+		}
+	}
+
 	/* Update statistics */
 	if (ppp_priv_area->timer_int_enabled & TMR_INT_ENABLED_UPDATE){
 		ppp_get_err_stats(card);
@@ -1801,12 +2076,11 @@
 		ppp_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_UDP;
 	}
 
-
 	/* PPP Event */
 	if (ppp_priv_area->timer_int_enabled & TMR_INT_ENABLED_PPP_EVENT){
 
 		if (card->wandev.state == WAN_DISCONNECTED){
-			poll_disconnected(card);
+			retrigger_comm(card);
 		}
 
 		/* If the state is CONNECTING, it means that communicatins were
@@ -1823,21 +2097,22 @@
 	 	 * poll for an IP address which will be provided by remote end.
 	 	 */
 		else if ((card->wandev.state == WAN_CONNECTED && 
-		  	card->u.p.ip_mode == WANOPT_PPP_PEER) && 
-		  	Read_connection_info){
+		  	  card->u.p.ip_mode == WANOPT_PPP_PEER) && 
+		  	  test_bit(0,&Read_connection_info)){
 
 			card->state_tick = jiffies;
-			if (!read_connection_info (card)){
-				card->poll = &process_route;
+			if (read_connection_info (card)){
+				printk(KERN_INFO "%s: Failed to read PEER IP Addresses\n",
+					card->devname);
+			}else{
+				clear_bit(0,&Read_connection_info);
+				set_bit(1,&Read_connection_info);
+				trigger_ppp_poll(dev);
 			}
-
 		}else{
-			/* If we are using Static IP,no need to poll for
-			 * an IP address. 
-		 	 */
-			NEX_PRINTK(KERN_INFO "Turning off TIMER \n");
+			//FIXME Put the comment back int
 			ppp_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_PPP_EVENT;
-		}	
+		}
 
 	}/* End of PPP_EVENT */
 
@@ -1970,32 +2245,62 @@
 static void process_route (sdla_t *card)
 {
 	ppp_flags_t *flags = card->flags;
-	struct net_device *dev = card->wandev.dev;
-	struct in_device *in_dev = dev->ip_ptr;
+	netdevice_t *dev = card->wandev.dev;
+	ppp_private_area_t *ppp_priv_area = dev->priv;
 	
-	if (in_dev != NULL ) {
-		if ((card->u.p.ip_mode == WANOPT_PPP_PEER) &&
-		    (Read_connection_info && flags->ip_state == 0x09)){ 
-		
-			printk(KERN_INFO "%s: IPCP State Opened.\n", card->devname);
-			if (read_info( card )) {
-   				printk(KERN_INFO 
-					"%s: An error occurred in IP assignment.\n", 
-					card->devname);
-			} else {
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
+	if ((card->u.p.ip_mode == WANOPT_PPP_PEER) &&
+	    (flags->ip_state == 0x09)){ 
+
+		/* We get ip_local from the firmware in PEER mode.
+	         * Therefore, if ip_local is 0, we failed to obtain
+         	 * the remote IP address. */
+		if (ppp_priv_area->ip_local == 0) 
+			return;
+		
+		printk(KERN_INFO "%s: IPCP State Opened.\n", card->devname);
+		if (read_info( card )) {
+   			printk(KERN_INFO 
+				"%s: An error occurred in IP assignment.\n", 
+				card->devname);
+		} else {
+			struct in_device *in_dev = dev->ip_ptr;
+			if (in_dev != NULL ) {
 				struct in_ifaddr *ifa = in_dev->ifa_list;
 				printk(KERN_INFO "%s: Assigned Lcl. Addr: %s\n", 
-						card->devname, in_ntoa(ifa->ifa_local));
+					card->devname, in_ntoa(ifa->ifa_local));
 				printk(KERN_INFO "%s: Assigned Rmt. Addr: %s\n", 
 						card->devname, in_ntoa(ifa->ifa_address));
+			}else{
+				printk(KERN_INFO 
+				"%s: Error: Failed to add a route for PPP interface %s\n",
+					card->devname,dev->name);	
 			}
-			Read_connection_info = 0;
 		}
-	}else{
-		printk(KERN_INFO "%s: Error: Null pointer in Poll Active\n",
+	}
+#else
+
+	if ((card->u.p.ip_mode == WANOPT_PPP_PEER) &&
+	    (flags->ip_state == 0x09)){ 
+	
+		if (ppp_priv_area->ip_local == 0)
+			return;
+	
+		printk(KERN_INFO "%s: IPCP State Opened.\n", card->devname);
+		if (read_info( card )) {
+			printk(KERN_INFO 
+				"%s: An error occurred in IP assignment.\n", 
 				card->devname);
+		} else {
+			printk(KERN_INFO "%s: Assigned Lcl. Addr: %s\n", 
+					card->devname, in_ntoa(dev->pa_addr));
+			printk(KERN_INFO "%s: Assigned Rmt. Addr: %s\n", 
+					card->devname, in_ntoa(dev->pa_dstaddr));
+		}
 	}
-	card->poll = NULL;
+
+#endif
 
 }
 
@@ -2004,19 +2309,17 @@
  *  o if interface is up and the hold-down timeout has expired, then retry
  *    connection.
  */
-static void poll_disconnected(sdla_t *card)
+static void retrigger_comm(sdla_t *card)
 {
-	struct net_device *dev = card->wandev.dev;
+	netdevice_t *dev = card->wandev.dev;
+
+	if (dev && ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) {
 
-	if (dev && netif_running(dev) &&
-	    ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) {
-	
 		wanpipe_set_state(card, WAN_CONNECTING);
-		
+
 		if(ppp_comm_enable(card) == CMD_OK){
 			init_ppp_tx_rx_buff( card );
 		}	         
-		
 	}
 }
 
@@ -2025,12 +2328,14 @@
 /*============================================================================
  * Configure S508 adapter.
  */
-static int config508(ppp_private_area_t *ppp_priv_area, sdla_t *card)
+static int config508(netdevice_t *dev, sdla_t *card)
 {
 	ppp508_conf_t cfg;
-	struct net_device *dev = card->wandev.dev;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct in_device *in_dev = dev->ip_ptr;
-	
+#endif
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+
 	/* Prepare PPP configuration structure */
 	memset(&cfg, 0, sizeof(ppp508_conf_t));
 
@@ -2040,6 +2345,7 @@
 	if (card->wandev.interface == WANOPT_RS232)
 		cfg.conf_flags |= INTERFACE_LEVEL_RS232;
 
+
         cfg.conf_flags 	|= DONT_TERMINATE_LNK_MAX_CONFIG; /*send Configure-Request packets forever*/
 	cfg.txbuf_percent	= PERCENT_TX_BUFF;	/* % of Tx bufs */
 	cfg.mtu_local		= card->wandev.mtu;
@@ -2065,6 +2371,7 @@
 				card->devname);
 		cfg.auth_options = INBOUND_AUTH;
 	}
+
 	if( ppp_priv_area->pap == WANOPT_YES){
 		cfg.auth_options |=PAP_AUTH;
 		printk(KERN_INFO "%s: Pap enabled\n", card->devname);
@@ -2076,6 +2383,7 @@
 
 
 	if (ppp_priv_area->enable_IPX == WANOPT_YES){
+		printk(KERN_INFO "%s: Enabling IPX Protocol\n",card->devname);
 		cfg.ipx_options		= ENABLE_IPX | ROUTING_PROT_DEFAULT;
 	}else{
 		cfg.ipx_options 	= DISABLE_IPX;
@@ -2085,25 +2393,92 @@
 	
 		case WANOPT_PPP_STATIC:
 
+			printk(KERN_INFO "%s: PPP IP Mode: STATIC\n",card->devname);
 			cfg.ip_options		= L_AND_R_IP_NO_ASSIG | 
 							    ENABLE_IP;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 			cfg.ip_local		= in_dev->ifa_list->ifa_local;
 			cfg.ip_remote		= in_dev->ifa_list->ifa_address;
+#else
+			cfg.ip_local		= dev->pa_addr;
+			cfg.ip_remote		= dev->pa_dstaddr;
+#endif
+
+			/* Debugging code used to check that IP addresses
+                         * obtained from the kernel are correct */
+
+			{
+			char laddr[20];
+			char raddr[20];
+			strcpy(laddr,in_ntoa(cfg.ip_local));
+			strcpy(raddr,in_ntoa(cfg.ip_remote));
+
                         NEX_PRINTK(KERN_INFO "Local %s Remote %s Name %s\n",
-					in_ntoa(cfg.ip_local), 
-					in_ntoa(cfg.ip_remote), 
-					dev->name);
+					laddr,raddr, dev->name);
+			
+			}	
+			break;
+
+		case WANOPT_PPP_HOST:
+
+			printk(KERN_INFO "%s: PPP IP Mode: HOST\n",card->devname);
+			cfg.ip_options		= L_IP_LOCAL_ASSIG |
+						  R_IP_LOCAL_ASSIG | 
+						  ENABLE_IP;
+                      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+			cfg.ip_local		= in_dev->ifa_list->ifa_local;
+			cfg.ip_remote		= in_dev->ifa_list->ifa_address;
+		      #else
+			cfg.ip_local		= dev->pa_addr;
+			cfg.ip_remote		= dev->pa_dstaddr;
+                      #endif
+
+			/* Debugging code used to check that IP addresses
+                         * obtained from the kernel are correct */
+			{
+			char laddr[20];
+			char raddr[20];
+			strcpy(laddr,in_ntoa(cfg.ip_local));
+			strcpy(raddr,in_ntoa(cfg.ip_remote));
+
+                        NEX_PRINTK (KERN_INFO "Local %s Remote %s Name %s\n",
+					laddr,raddr, dev->name);
+			
+			}	
+
+
 			break;
 	
 		case WANOPT_PPP_PEER:
+
+			#if defined(LINUX_2_1) || defined(LINUX_2_4)
+
+			printk(KERN_INFO "%s: PPP IP Mode: PEER\n",card->devname);
 			cfg.ip_options		= L_IP_REMOTE_ASSIG | 
 						  R_IP_REMOTE_ASSIG | 
 							  ENABLE_IP;
 			cfg.ip_local		= 0x00;
 			cfg.ip_remote		= 0x00;
 			break;
-	
+
+			#else
+
+			/* No PEER support for 2.0.X kernels, drop down to default
+   			 * condition */
+
+			printk(KERN_INFO "%s: ERROR, PEER mode is not supported in 2.0.X kernels\n",
+						card->devname);
+			
+			#endif
+
+		default:
+			printk(KERN_INFO "%s: ERROR: Unsuported PPP Mode Selected\n",
+					card->devname);
+			printk(KERN_INFO "%s:        PPP IP Modes: STATIC, PEER or HOST\n",
+					card->devname);	
+			return 1;
 	}
+
 	return ppp_configure(card, &cfg);
 }
 
@@ -2168,7 +2543,7 @@
 /*=============================================================================
  * Process UDP call of type PTPIPEAB.
  */
-static void process_udp_mgmt_pkt(sdla_t *card, struct net_device *dev, 
+static void process_udp_mgmt_pkt(sdla_t *card, netdevice_t *dev, 
 				 ppp_private_area_t *ppp_priv_area ) 
 {
 	unsigned char buf2[5];
@@ -2186,33 +2561,31 @@
 	memcpy(&buf2, &card->wandev.udp_port, 2 );
 
 
-	switch(ppp_udp_pkt->cblock.command) {
+	if(ppp_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) {
 
-		/* FT1 MONITOR STATUS */
-		case FT1_MONITOR_STATUS_CTRL:
-		
-		/* PPIPE_ENABLE_TRACING */
-		case PPIPE_ENABLE_TRACING:
-		   
-		/* PPIPE_DISABLE_TRACING */
-		case PPIPE_DISABLE_TRACING:
-		   
-		/* PPIPE_GET_TRACE_INFO */
-		case PPIPE_GET_TRACE_INFO:
-		   
-		/* SET FT1 MODE */
-		case SET_FT1_MODE:
-			if(ppp_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-			
-				++ppp_priv_area->pipe_mgmt_stat.
-						UDP_PIPE_mgmt_direction_err;
+		switch(ppp_udp_pkt->cblock.command) {
+
+			case PPIPE_GET_IBA_DATA:
+			case PPP_READ_CONFIG:
+			case PPP_GET_CONNECTION_INFO:
+			case PPIPE_ROUTER_UP_TIME:
+			case PPP_READ_STATISTICS:
+			case PPP_READ_ERROR_STATS:
+			case PPP_READ_PACKET_STATS:
+			case PPP_READ_LCP_STATS:
+			case PPP_READ_IPCP_STATS:
+			case PPP_READ_IPXCP_STATS:
+			case PPP_READ_PAP_STATS:
+			case PPP_READ_CHAP_STATS:
+			case PPP_READ_CODE_VERSION:
+				udp_mgmt_req_valid = 1;
+				break;
+			   
+			default:
 				udp_mgmt_req_valid = 0;
-			}	 
-			break;
-		   
-		default:
-			break;
-	} 
+				break;
+		} 
+	}
 	
   	if(!udp_mgmt_req_valid) {
 	    
@@ -2221,7 +2594,13 @@
 
     		/* set return code */
     		ppp_udp_pkt->cblock.result = 0xCD; 
-
+		++ppp_priv_area->pipe_mgmt_stat.UDP_PIPE_mgmt_direction_err;
+	
+		if (net_ratelimit()){	
+			printk(KERN_INFO 
+			"%s: Warning, Illegal UDP command attempted from network: %x\n",
+			card->devname,ppp_udp_pkt->cblock.command);
+		}
    	} else {
 		/* Initialize the trace element */
 		trace_element_t trace_element;		    
@@ -2422,27 +2801,24 @@
 
 			/* set return code */
 			ppp_udp_pkt->cblock.result = 0x00;
-
-			break;
-
-		/* PPIPE_KILL_BOARD */
-		case PPIPE_KILL_BOARD:
+			ppp_udp_pkt->cblock.result = 0;
 			break;
 
 		/* PPIPE_FT1_READ_STATUS */
 		case PPIPE_FT1_READ_STATUS:
-			sdla_peek(&card->hw, 0xF020, &ppp_udp_pkt->data, 2);
-			ppp_udp_pkt->cblock.length = 2;
+			sdla_peek(&card->hw, 0xF020, &ppp_udp_pkt->data[0], 2);
+			ppp_udp_pkt->cblock.length = mbox->cmd.length = 2;
 			ppp_udp_pkt->cblock.result = 0;
-			mbox->cmd.length = 2;
 			break;
 		
 		case PPIPE_FLUSH_DRIVER_STATS:   
 			init_ppp_priv_struct( ppp_priv_area );
 			init_global_statistics( card );
 			mbox->cmd.length = 0;
+			ppp_udp_pkt->cblock.result = 0;
 			break;
 
+		
 		case PPIPE_ROUTER_UP_TIME:
 
 			do_gettimeofday( &tv );
@@ -2450,34 +2826,11 @@
 					ppp_priv_area->router_start_time;
 			*(unsigned long *)&ppp_udp_pkt->data = ppp_priv_area->router_up_time;
 			mbox->cmd.length = 4;
+			ppp_udp_pkt->cblock.result = 0;
 			break;
 
-		/* FT1 MONITOR STATUS */
-   		case FT1_MONITOR_STATUS_CTRL:
-	
-			/* Enable FT1 MONITOR STATUS */
-	        	if( ppp_udp_pkt->data[0] == 1) {
-			
-				if( rCount++ != 0 ) {
-		        		ppp_udp_pkt->cblock.result = 0;
-	          			mbox->cmd.length = 1;
-		  			break;
-		    		}	
-	      		}
-
-	      		/* Disable FT1 MONITOR STATUS */
-	      		if( ppp_udp_pkt->data[0] == 0) {
-
-	      	   		if( --rCount != 0) {
-		  			ppp_udp_pkt->cblock.result = 0;
-		  			mbox->cmd.length = 1;
-		  			break;
-	   	    		} 
-	      		} 	
-
-		/* PPIPE_DRIVER_STATISTICS */   
+				/* PPIPE_DRIVER_STATISTICS */   
 		case PPIPE_DRIVER_STAT_IFSEND:
-			printk(KERN_INFO "Getting IF_SEND Drivers Statistics\n");
 			memcpy(&ppp_udp_pkt->data, &ppp_priv_area->if_send_stat, 
 				sizeof(if_send_stat_t));
 
@@ -2515,8 +2868,38 @@
 			mbox->cmd.length = ppp_udp_pkt->cblock.length;
 			break;
 
+
+		/* FT1 MONITOR STATUS */
+   		case FT1_MONITOR_STATUS_CTRL:
 	
+			/* Enable FT1 MONITOR STATUS */
+	        	if( ppp_udp_pkt->data[0] == 1) {
+			
+				if( rCount++ != 0 ) {
+		        		ppp_udp_pkt->cblock.result = 0;
+	          			mbox->cmd.length = 1;
+		  			break;
+		    		}	
+	      		}
+
+	      		/* Disable FT1 MONITOR STATUS */
+	      		if( ppp_udp_pkt->data[0] == 0) {
+
+	      	   		if( --rCount != 0) {
+		  			ppp_udp_pkt->cblock.result = 0;
+		  			mbox->cmd.length = 1;
+		  			break;
+	   	    		} 
+	      		} 	
+			goto udp_dflt_cmd;
+			
+		/* WARNING: FIXME: This should be fixed.
+		 * The FT1 Status Ctrl doesn't have a break
+                 * statment.  Thus, no code must be inserted
+                 * HERE: between default and above case statement */
+
 		default:
+udp_dflt_cmd:
 	        
 			/* it's a board command */
 			mbox->cmd.command = ppp_udp_pkt->cblock.command;
@@ -2555,9 +2938,12 @@
      	len = reply_udp(ppp_priv_area->udp_pkt_data, mbox->cmd.length);
 
      	if (ppp_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) {
-	
-		++ppp_priv_area->pipe_mgmt_stat.UDP_PIPE_mgmt_passed_to_adptr;
-		ppp_send(card,ppp_priv_area->udp_pkt_data,len,ppp_priv_area->protocol);
+
+		/* Make sure we are not already sending */
+		if (!test_bit(SEND_CRIT,&card->wandev.critical)){
+			++ppp_priv_area->pipe_mgmt_stat.UDP_PIPE_mgmt_passed_to_adptr;
+			ppp_send(card,ppp_priv_area->udp_pkt_data,len,ppp_priv_area->protocol);
+		}
 
 	} else {	
 	
@@ -2587,6 +2973,8 @@
     	}	
 
 	ppp_priv_area->udp_pkt_lgth = 0;
+	
+	return; 
 }
 
 /*=============================================================================
@@ -2648,9 +3036,12 @@
 			(info->rxb_num - 1);
 	}
 
+	card->u.p.txbuf_next = (unsigned long*)&info->txb_nxt; 
+	card->u.p.rxbuf_next = (unsigned long*)&info->rxb1_ptr;
+
 	card->u.p.rx_base = info->rxb_base;
         card->u.p.rx_top  = info->rxb_end;
-       
+      
 	card->u.p.txbuf = card->u.p.txbuf_base;
 	card->rxmb = card->u.p.rxbuf_base;
 
@@ -2662,21 +3053,32 @@
  */
 static int read_info( sdla_t *card )
 {
-	struct net_device *dev = card->wandev.dev;
+	netdevice_t *dev = card->wandev.dev;
 	ppp_private_area_t *ppp_priv_area = dev->priv;
 	int err;
+
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct ifreq if_info;
 	struct sockaddr_in *if_data1, *if_data2;
 	mm_segment_t fs;
+      #else
+	#ifdef _DYNAMIC_ROUTE_20X_SUPPORT_
+        struct rtentry route;
+	#endif
+      #endif
+
+
+	
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 
 	/* Set Local and remote addresses */
 	memset(&if_info, 0, sizeof(if_info));
 	strcpy(if_info.ifr_name, dev->name);
 
+
 	fs = get_fs();
 	set_fs(get_ds());     /* get user space block */ 
 
-	
 	/* Change the local and remote ip address of the interface.
 	 * This will also add in the destination route.
 	 */	
@@ -2690,11 +3092,43 @@
 	err = devinet_ioctl( SIOCSIFDSTADDR, &if_info );
 
 	set_fs(fs);           /* restore old block */
+	
+#else
+	/* FIXME: Dynamic Routing in 2.0.X kernels is not
+         * supported. Sorry ! I'll come back to it when I get
+         * a chance. */
+	
+	printk(KERN_INFO "%s: ERROR, Dynamic routing is not supported in 2.0.X kernels\n",
+				card->devname);
+	printk(KERN_INFO "%s:        Please use the STATIC IP mode!\n",
+				card->devname);
 
+	err = 0;
+
+      #ifdef _DYNAMIC_ROUTE_20X_SUPPORT_
+	dev->pa_dstaddr = ppp_priv_area->ip_remote;
+	dev->pa_addr = ppp_priv_area->ip_local;
+
+	memset(&route, 0, sizeof(route));
+	route.rt_dev = dev->name;
+	route.rt_flags = 0;
+	((struct sockaddr_in *)&(route.rt_dst))->sin_addr.s_addr =
+		dev->pa_dstaddr;
+	((struct sockaddr_in *)&(route.rt_dst))->sin_family = AF_INET;
+	((struct sockaddr_in *)&(route.rt_genmask))->sin_addr.s_addr =
+			0xFFFFFFFF;
+	((struct sockaddr_in *)&(route.rt_genmask))->sin_family =
+			AF_INET;
+	
+	err = ip_rt_new(&route);
+
+      #endif
+
+#endif
 
 	if (err) {
-		printk (KERN_INFO "%s: Adding of route failed:\n",
-			card->devname);
+		printk (KERN_INFO "%s: Adding of route failed: %i\n",
+			card->devname,err);
 		printk (KERN_INFO "%s:	Local : %s\n",
 			card->devname,in_ntoa(ppp_priv_area->ip_local));
 		printk (KERN_INFO "%s:	Remote: %s\n",
@@ -2708,31 +3142,39 @@
  * Called when ppp interface disconnected.
  */
 
-static int remove_route( sdla_t *card )
+static void remove_route( sdla_t *card )
 {
 
-	struct net_device *dev = card->wandev.dev;
+	netdevice_t *dev = card->wandev.dev;
 	long ip_addr;
 	int err;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
         mm_segment_t fs;
 	struct ifreq if_info;
 	struct sockaddr_in *if_data1;
         struct in_device *in_dev = dev->ip_ptr;
         struct in_ifaddr *ifa = in_dev->ifa_list;	
+#else
+	unsigned long fs = 0;
+        struct rtentry route;
+#endif
 
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	ip_addr = ifa->ifa_local;
 
 	/* Set Local and remote addresses */
 	memset(&if_info, 0, sizeof(if_info));
 	strcpy(if_info.ifr_name, dev->name);
+#endif
 
 
 	fs = get_fs();
        	set_fs(get_ds());     /* get user space block */ 
 
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)	
 	/* Change the local ip address of the interface to 0.
 	 * This will also delete the destination route.
 	 */	
@@ -2740,6 +3182,26 @@
 	if_data1->sin_addr.s_addr = 0;
 	if_data1->sin_family = AF_INET;
 	err = devinet_ioctl( SIOCSIFADDR, &if_info );
+#else
+
+	ip_addr = dev->pa_addr;
+	dev->pa_dstaddr = 0;
+
+	memset(&route, 0, sizeof(route));
+	route.rt_dev = dev->name;
+	route.rt_flags = 0;
+	((struct sockaddr_in *)&(route.rt_dst))->sin_addr.s_addr =
+			dev->pa_dstaddr;
+	((struct sockaddr_in *)&(route.rt_dst))->sin_family = AF_INET;
+	((struct sockaddr_in *)&(route.rt_genmask))->sin_addr.s_addr =
+			0xFFFFFFFF;
+        ((struct sockaddr_in *)&(route.rt_genmask))->sin_family =
+			AF_INET;
+
+
+	err = ip_rt_kill(&route);
+
+#endif
 
         set_fs(fs);           /* restore old block */
 
@@ -2747,13 +3209,12 @@
 	if (err) {
 		printk (KERN_INFO "%s: Deleting dynamic route failed %d!\n",
 			 card->devname, err);
-		return err;
-	}else
-		printk (KERN_INFO "%s: PPP Deleting dynamic route %s successfully\n",
+		return;
+	}else{
+		printk (KERN_INFO "%s: PPP Deleting dynamic route %s successfuly\n",
 			card->devname, in_ntoa(ip_addr));
-
-	
-	return 0;
+	}
+	return;
 }
 
 /*=============================================================================
@@ -2765,13 +3226,6 @@
 	ppp_mbox_t *mb = card->mbox;
 	int err,i;
 
-	/* The critical flag is unset because during intialization (if_open) 
-	 * we want the interrupts to be enabled so that when the wpp_isr is
-	 * called it does not exit due to critical flag set.
-	 */ 
-	 
-	card->wandev.critical = 0;
-
 	err = ppp_set_intr_mode( card, 0x08 );
 	
 	if (err == CMD_OK) { 
@@ -2792,7 +3246,6 @@
 	if (err != CMD_OK) 
 		return err;
 
-	card->wandev.critical = 1;
 	return 0;
 }
 
@@ -2803,7 +3256,6 @@
 {
 	unsigned char *sendpacket;
 	unsigned char buf2[5]; 
-	//FIXME: Use the structure
 	ppp_udp_pkt_t *ppp_udp_pkt = (ppp_udp_pkt_t *)skb->data; 
 	
 	sendpacket = skb->data;
@@ -2850,16 +3302,19 @@
  * multicast source IP address.
  */
 
-static int chk_bcast_mcast_addr(sdla_t *card, struct net_device* dev,
+static int chk_bcast_mcast_addr(sdla_t *card, netdevice_t* dev,
 				struct sk_buff *skb)
 {
 	u32 src_ip_addr;
         u32 broadcast_ip_addr = 0;
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
         struct in_device *in_dev;
+      #endif
         /* read the IP source address from the outgoing packet */
         src_ip_addr = *(u32 *)(skb->data + 12);
 
 	/* read the IP broadcast address for the device */
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
         in_dev = dev->ip_ptr;
         if(in_dev != NULL) {
                 struct in_ifaddr *ifa= in_dev->ifa_list;
@@ -2868,13 +3323,14 @@
                 else
                         return 0;
         }
+      #else
+        broadcast_ip_addr = dev->pa_brdaddr;
+      #endif
  
         /* check if the IP Source Address is a Broadcast address */
         if((dev->flags & IFF_BROADCAST) && (src_ip_addr == broadcast_ip_addr)) {
                 printk(KERN_INFO "%s: Broadcast Source Address silently discarded\n",
 				card->devname);
-                dev_kfree_skb(skb);
-                ++card->wandev.stats.tx_dropped;
                 return 1;
         } 
 
@@ -2883,8 +3339,6 @@
 		(ntohl(src_ip_addr) <= 0xFFFFFFFE)) {
                 printk(KERN_INFO "%s: Multicast Source Address silently discarded\n",
 				card->devname);
-                dev_kfree_skb(skb);
-                ++card->wandev.stats.tx_dropped;
                 return 1;
         }
 
@@ -2893,26 +3347,26 @@
 
 void s508_lock (sdla_t *card, unsigned long *smp_flags)
 {
-#ifdef CONFIG_SMP
-	spin_lock_irqsave(&card->lock, *smp_flags);
-#else
+      #if defined(__SMP__) || defined(LINUX_2_4)
+	spin_lock_irqsave(&card->wandev.lock, *smp_flags);
+      #else
 	disable_irq(card->hw.irq);
-#endif                                                                     
+      #endif                                                                     
 }
 
 void s508_unlock (sdla_t *card, unsigned long *smp_flags)
 {
-#ifdef CONFIG_SMP
-        spin_unlock_irqrestore(&card->lock, *smp_flags);
-#else
+      #if defined(__SMP__) || defined(LINUX_2_4)
+        spin_unlock_irqrestore(&card->wandev.lock, *smp_flags);
+      #else
 	enable_irq(card->hw.irq);
-#endif           
+      #endif           
 }
 
 static int read_connection_info (sdla_t *card)
 {
 	ppp_mbox_t *mb = card->mbox;
-	struct net_device *dev = card->wandev.dev;
+	netdevice_t *dev = card->wandev.dev;
 	ppp_private_area_t *ppp_priv_area = dev->priv;
 	ppp508_connect_info_t *ppp508_connect_info;
 	int err;
@@ -2924,15 +3378,347 @@
 
 	if (err != CMD_OK) { 
 		ppp_error(card, err, mb);
+		ppp_priv_area->ip_remote = 0;
+		ppp_priv_area->ip_local = 0;
 	}
 	else {
 		ppp508_connect_info = (ppp508_connect_info_t *)mb->data;
-
 		ppp_priv_area->ip_remote = ppp508_connect_info->ip_remote;
 		ppp_priv_area->ip_local = ppp508_connect_info->ip_local;
+
+		NEX_PRINTK(KERN_INFO "READ CONNECTION GOT IP ADDRESS %x, %x\n",
+				ppp_priv_area->ip_remote,
+				ppp_priv_area->ip_local);
 	}
 
 	return err;
 }
+
+/*===============================================================================
+ * config_ppp
+ *
+ *	Configure the ppp protocol and enable communications.		
+ *
+ *   	The if_open function binds this function to the poll routine.
+ *      Therefore, this function will run every time the ppp interface
+ *      is brought up.  
+ *      
+ *	If the communications are not enabled, proceed to configure
+ *      the card and enable communications.
+ *
+ *      If the communications are enabled, it means that the interface
+ *      was shutdown by ether the user or driver. In this case, we 
+ *      have to check that the IP addresses have not changed.  If
+ *      the IP addresses changed, we have to reconfigure the firmware
+ *      and update the changed IP addresses.  Otherwise, just exit.
+ */
+static int config_ppp (sdla_t *card)
+{
+
+	netdevice_t *dev = card->wandev.dev;
+	ppp_flags_t *flags = card->flags;
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+
+	if (card->u.p.comm_enabled){
+
+		if (ppp_priv_area->ip_local_tmp != ppp_priv_area->ip_local ||
+		    ppp_priv_area->ip_remote_tmp != ppp_priv_area->ip_remote){
+			
+			/* The IP addersses have changed, we must
+                         * stop the communications and reconfigure
+                         * the card. Reason: the firmware must know
+                         * the local and remote IP addresses. */
+			disable_comm(card);
+			wanpipe_set_state(card, WAN_DISCONNECTED);
+			printk(KERN_INFO 
+				"%s: IP addresses changed!\n",
+					card->devname);
+			printk(KERN_INFO "%s: Restarting communications ...\n",
+					card->devname);
+		}else{ 
+			/* IP addresses are the same and the link is up, 
+                         * we dont have to do anything here. Therefore, exit */
+			return 0;
+		}
+	}
+
+	/* Record the new IP addreses */
+	ppp_priv_area->ip_local = ppp_priv_area->ip_local_tmp;
+	ppp_priv_area->ip_remote = ppp_priv_area->ip_remote_tmp;
+
+	if (config508(dev, card)){
+		printk(KERN_INFO "%s: Failed to configure PPP device\n",
+			card->devname);
+		return 0;
+	}
+
+	if (ppp_set_intr_mode(card, PPP_INTR_RXRDY|
+			    		PPP_INTR_TXRDY|
+				    	PPP_INTR_MODEM|
+				    	PPP_INTR_DISC |
+				    	PPP_INTR_OPEN |
+				    	PPP_INTR_DROP_DTR |
+					PPP_INTR_TIMER)) {
+
+		printk(KERN_INFO "%s: Failed to configure board interrupts !\n", 
+			card->devname);
+		return 0;
+	}
+
+        /* Turn off the transmit and timer interrupt */
+	flags->imask &= ~(PPP_INTR_TXRDY | PPP_INTR_TIMER) ;
+
+
+	/* If you are not the authenticator and any one of the protocol is 
+	 * enabled then we call the set_out_bound_authentication.
+	 */
+	if ( !card->u.p.authenticator  && (ppp_priv_area->pap || ppp_priv_area->chap)) {
+		if ( ppp_set_outbnd_auth(card, ppp_priv_area) ){
+			printk(KERN_INFO "%s: Outbound authentication failed !\n",
+				card->devname);
+			return 0;
+		}
+	} 
+	
+	/* If you are the authenticator and any one of the protocol is enabled
+	 * then we call the set_in_bound_authentication.
+	 */
+	if (card->u.p.authenticator && (ppp_priv_area->pap || ppp_priv_area->chap)){
+		if (ppp_set_inbnd_auth(card, ppp_priv_area)){
+			printk(KERN_INFO "%s: Inbound authentication failed !\n",
+				card->devname);	
+			return 0;
+		}
+	}
+
+	/* If we fail to enable communications here it's OK,
+	 * since the DTR timer will cause a disconnected, which
+	 * will retrigger communication in timer_intr() */
+	if (ppp_comm_enable(card) == CMD_OK) {
+		wanpipe_set_state(card, WAN_CONNECTING);
+		init_ppp_tx_rx_buff(card);
+	}
+
+	return 0; 
+}
+
+/*============================================================
+ * ppp_poll
+ *	
+ * Rationale:
+ * 	We cannot manipulate the routing tables, or
+ *      ip addresses withing the interrupt. Therefore
+ *      we must perform such actons outside an interrupt 
+ *      at a later time. 
+ *
+ * Description:	
+ *	PPP polling routine, responsible for 
+ *     	shutting down interfaces upon disconnect
+ *     	and adding/removing routes. 
+ *      
+ * Usage:        
+ * 	This function is executed for each ppp  
+ * 	interface through a tq_schedule bottom half.
+ *      
+ *      trigger_ppp_poll() function is used to kick
+ *      the ppp_poll routine.  
+ */
+static void ppp_poll (netdevice_t *dev)
+{
+	ppp_private_area_t *ppp_priv_area; 	
+	sdla_t *card;
+	u8 check_gateway=0;
+	ppp_flags_t *flags;
+
+	if (!dev || (ppp_priv_area = dev->priv) == NULL)
+		return;
+
+	card = ppp_priv_area->card;
+	flags = card->flags;
+
+	/* Shutdown is in progress, stop what you are 
+	 * doing and get out */
+	if (test_bit(PERI_CRIT,&card->wandev.critical)){
+		clear_bit(POLL_CRIT,&card->wandev.critical);
+		return;
+	}
+
+	/* if_open() function has triggered the polling routine
+	 * to determine the configured IP addresses.  Once the
+	 * addresses are found, trigger the chdlc configuration */
+	if (test_bit(0,&ppp_priv_area->config_ppp)){
+
+		ppp_priv_area->ip_local_tmp  = get_ip_address(dev,WAN_LOCAL_IP);
+		ppp_priv_area->ip_remote_tmp = get_ip_address(dev,WAN_POINTOPOINT_IP);
+
+		if (ppp_priv_area->ip_local_tmp == ppp_priv_area->ip_remote_tmp && 
+	            card->u.p.ip_mode == WANOPT_PPP_HOST){
+			
+			if (++ppp_priv_area->ip_error > MAX_IP_ERRORS){
+				printk(KERN_INFO "\n%s: --- WARNING ---\n",
+						card->devname);
+				printk(KERN_INFO "%s: The local IP address is the same as the\n",
+						card->devname);
+				printk(KERN_INFO "%s: Point-to-Point IP address.\n",
+						card->devname);
+				printk(KERN_INFO "%s: --- WARNING ---\n\n",
+						card->devname);
+			}else{
+				clear_bit(POLL_CRIT,&card->wandev.critical);
+				ppp_priv_area->poll_delay_timer.expires = jiffies+HZ;
+				add_timer(&ppp_priv_area->poll_delay_timer);
+				return;
+			}
+		}
+
+		ppp_priv_area->timer_int_enabled |= TMR_INT_ENABLED_CONFIG;
+		flags->imask |= PPP_INTR_TIMER;	
+		ppp_priv_area->ip_error=0;	
+		
+		clear_bit(0,&ppp_priv_area->config_ppp);
+		clear_bit(POLL_CRIT,&card->wandev.critical);
+		return;
+	}
+
+	/* Dynamic interface implementation, as well as dynamic
+	 * routing.  */
+	
+	switch (card->wandev.state) {
+	
+	case WAN_DISCONNECTED:
+
+		/* If the dynamic interface configuration is on, and interface 
+		 * is up, then bring down the netowrk interface */
+
+		if (test_bit(DYN_OPT_ON,&ppp_priv_area->interface_down) &&
+		    !test_bit(DEV_DOWN,&ppp_priv_area->interface_down)	&&	
+		    card->wandev.dev->flags & IFF_UP){	
+
+			printk(KERN_INFO "%s: Interface %s down.\n",
+				card->devname,card->wandev.dev->name);
+			change_dev_flags(card->wandev.dev,
+					(card->wandev.dev->flags&~IFF_UP));
+			set_bit(DEV_DOWN,&ppp_priv_area->interface_down);
+		}else{
+			/* We need to check if the local IP address is
+               	   	 * zero. If it is, we shouldn't try to remove it.
+                 	 * For some reason the kernel crashes badly if 
+                 	 * we try to remove the route twice */
+
+			if (card->wandev.dev->flags & IFF_UP && 
+		    	    get_ip_address(card->wandev.dev,WAN_LOCAL_IP) &&
+		    	    card->u.p.ip_mode == WANOPT_PPP_PEER){
+
+				remove_route(card);
+			}
+		}
+		break;
+
+	case WAN_CONNECTED:
+		
+		/* In SMP machine this code can execute before the interface
+		 * comes up.  In this case, we must make sure that we do not
+		 * try to bring up the interface before dev_open() is finished */
+
+
+		/* DEV_DOWN will be set only when we bring down the interface
+		 * for the very first time. This way we know that it was us
+		 * that brought the interface down */
+		
+		if (test_bit(DYN_OPT_ON,&ppp_priv_area->interface_down) &&
+	            test_bit(DEV_DOWN,  &ppp_priv_area->interface_down) &&
+ 		    !(card->wandev.dev->flags & IFF_UP)){
+			
+			printk(KERN_INFO "%s: Interface %s up.\n",
+				card->devname,card->wandev.dev->name);
+			
+			change_dev_flags(card->wandev.dev,(card->wandev.dev->flags|IFF_UP));
+			clear_bit(DEV_DOWN,&ppp_priv_area->interface_down);
+			check_gateway=1;
+		}
+
+		if ((card->u.p.ip_mode == WANOPT_PPP_PEER) && 
+		    test_bit(1,&Read_connection_info)) { 
+			
+			process_route(card);
+			clear_bit(1,&Read_connection_info);
+			check_gateway=1;
+		}
+
+		if (ppp_priv_area->gateway && check_gateway)
+			add_gateway(card,dev);
+
+		break;
+	}
+	clear_bit(POLL_CRIT,&card->wandev.critical);
+	return;
+}
+
+/*============================================================
+ * trigger_ppp_poll
+ *
+ * Description:
+ * 	Add a ppp_poll() task into a tq_scheduler bh handler
+ *      for a specific interface.  This will kick
+ *      the ppp_poll() routine at a later time. 
+ *
+ * Usage:
+ * 	Interrupts use this to defer a taks to 
+ *      a polling routine.
+ *
+ */	
+
+static void trigger_ppp_poll (netdevice_t *dev)
+{
+	ppp_private_area_t *ppp_priv_area;
+	if ((ppp_priv_area=dev->priv) != NULL){ 	
+		
+		sdla_t *card = ppp_priv_area->card;
+
+		if (test_bit(PERI_CRIT,&card->wandev.critical)){
+			return;
+		}
+		
+		if (test_and_set_bit(POLL_CRIT,&card->wandev.critical)){
+			return;
+		}
+
+	      #ifdef LINUX_2_4
+		schedule_task(&ppp_priv_area->poll_task);
+	      #else
+		queue_task(&ppp_priv_area->poll_task, &tq_scheduler);
+	      #endif
+	}
+	return;
+}
+
+static void ppp_poll_delay (unsigned long dev_ptr)
+{
+	netdevice_t *dev = (netdevice_t *)dev_ptr;
+	trigger_ppp_poll(dev);
+}
+
+/*============================================================
+ * detect_and_fix_tx_bug
+ *
+ * Description:
+ *	On connect, if the board tx buffer ptr is not the same
+ *      as the driver tx buffer ptr, we found a firmware bug.
+ *      Report the bug to the above layer.  To fix the
+ *      error restart communications again.
+ *
+ * Usage:
+ *
+ */	
+
+static int detect_and_fix_tx_bug (sdla_t *card)
+{
+	if (((unsigned long)card->u.p.txbuf_base&0xFFF) != ((*card->u.p.txbuf_next)&0xFFF)){
+		NEX_PRINTK(KERN_INFO "Major Error, Fix the bug\n");
+		return 1;
+	}
+	return 0;
+}
+
 
 /****** End *****************************************************************/
diff -urN linux-2.4.4-pre2/drivers/net/wan/sdla_x25.c linux/drivers/net/wan/sdla_x25.c
--- linux-2.4.4-pre2/drivers/net/wan/sdla_x25.c	Tue Mar  6 19:44:36 2001
+++ linux/drivers/net/wan/sdla_x25.c	Mon Dec  3 22:34:50 2001
@@ -1,16 +1,55 @@
 /*****************************************************************************
 * sdla_x25.c	WANPIPE(tm) Multiprotocol WAN Link Driver.  X.25 module.
 *
-* Author:	Gene Kozin	<genek@compuserve.com>
+* Author:	Nenad Corbic	<ncorbic@sangoma.com>
 *
-* Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2001 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
-* Mar 15, 1998  Alan Cox	 o 2.1.x porting
+* Apr 03, 2001  Nenad Corbic	 o Fixed the rx_skb=NULL bug in x25 in rx_intr().
+* Dec 26, 2000  Nenad Corbic	 o Added a new polling routine, that uses
+*                                  a kernel timer (more efficient).
+* Dec 25, 2000  Nenad Corbic	 o Updated for 2.4.X kernel
+* Jul 26, 2000  Nenad Corbic	 o Increased the local packet buffering
+* 				   for API to 4096+header_size. 
+* Jul 17, 2000  Nenad Corbic	 o Fixed the x25 startup bug. Enable 
+* 				   communications only after all interfaces
+* 				   come up.  HIGH SVC/PVC is used to calculate
+* 				   the number of channels.
+*                                  Enable protocol only after all interfaces
+*                                  are enabled.
+* Jul 10, 2000	Nenad Corbic	 o Fixed the M_BIT bug. 
+* Apr 25, 2000  Nenad Corbic	 o Pass Modem messages to the API.
+*                                  Disable idle timeout in X25 API.
+* Apr 14, 2000  Nenad Corbic	 o Fixed: Large LCN number support.
+*                                  Maximum LCN number is 4095.
+*                                  Maximum number of X25 channels is 255.
+* Apr 06, 2000  Nenad Corbic	 o Added SMP Support.
+* Mar 29, 2000  Nenad Corbic	 o Added support for S514 PCI Card
+* Mar 23, 2000  Nenad Corbic	 o Improved task queue, BH handling.
+* Mar 14, 2000  Nenad Corbic  	 o Updated Protocol Violation handling
+*                                  routines.  Bug Fix.
+* Mar 10, 2000  Nenad Corbic	 o Bug Fix: corrupted mbox recovery.
+* Mar 09, 2000  Nenad Corbic     o Fixed the auto HDLC bug.
+* Mar 08, 2000	Nenad Corbic     o Fixed LAPB HDLC startup problems.
+*                                  Application must bring the link up 
+*                                  before tx/rx, and bring the 
+*                                  link down on close().
+* Mar 06, 2000	Nenad Corbic	 o Added an option for logging call setup 
+*                                  information. 
+* Feb 29, 2000  Nenad Corbic 	 o Added support for LAPB HDLC API
+* Feb 25, 2000  Nenad Corbic     o Fixed the modem failure handling.
+*                                  No Modem OOB message will be passed 
+*                                  to the user.
+* Feb 21, 2000  Nenad Corbic 	 o Added Xpipemon Debug Support
+* Dec 30, 1999 	Nenad Corbic	 o Socket based X25API 
+* Sep 17, 1998	Jaspreet Singh	 o Updates for 2.2.X  kernel
+* Mar 15, 1998	Alan Cox	 o 2.1.x porting
+* Dec 19, 1997	Jaspreet Singh	 o Added multi-channel IPX support
 * Nov 27, 1997	Jaspreet Singh	 o Added protection against enabling of irqs
 *				   when they are disabled.
 * Nov 17, 1997  Farhan Thawar    o Added IPX support
@@ -38,21 +77,41 @@
 * Jan 07, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
 
+/*======================================================
+ * 	Includes 
+ *=====================================================*/
 
+#include <linux/version.h>
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/ctype.h>
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <asm/byteorder.h>	/* htons(), etc. */
-#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/delay.h>	/* Experimental delay */
 
-#define	_GNUC_
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ #include <asm/uaccess.h>
+#else
+ #include <asm/segment.h>
+ #include <net/route.h>
+#endif
+
+#include <linux/if.h>
+#include <linux/if_arp.h>
 #include <linux/sdla_x25.h>	/* X.25 firmware API definitions */
+#include <linux/if_wanpipe_common.h>
+#include <linux/if_wanpipe.h>
+
+
+/*======================================================
+ * 	Defines & Macros 
+ *=====================================================*/
 
-/****** Defines & Macros ****************************************************/
 
 #define	CMD_OK		0		/* normal firmware return code */
 #define	CMD_TIMEOUT	0xFF		/* firmware command timed out */
@@ -64,27 +123,141 @@
 #define	X25_RECON_TMOUT	(10*HZ)		/* link connection timeout */
 #define	CONNECT_TIMEOUT	(90*HZ)		/* link connection timeout */
 #define	HOLD_DOWN_TIME	(30*HZ)		/* link hold down time */
+#define MAX_BH_BUFF	10
+#define M_BIT		0x01	
+
+//#define PRINT_DEBUG 1
+#ifdef PRINT_DEBUG
+#define DBG_PRINTK(format, a...) printk(format, ## a)
+#else
+#define DBG_PRINTK(format, a...)
+#endif  
+
+#define TMR_INT_ENABLED_POLL_ACTIVE      0x01
+#define TMR_INT_ENABLED_POLL_CONNECT_ON  0x02
+#define TMR_INT_ENABLED_POLL_CONNECT_OFF 0x04
+#define TMR_INT_ENABLED_POLL_DISCONNECT  0x08
+#define TMR_INT_ENABLED_CMD_EXEC	 0x10
+#define TMR_INT_ENABLED_UPDATE		 0x20
+#define TMR_INT_ENABLED_UDP_PKT		 0x40
+
+#define MAX_X25_ADDR_SIZE	16
+#define MAX_X25_DATA_SIZE 	129
+#define MAX_X25_FACL_SIZE	110
+
+#define TRY_CMD_AGAIN	2
+#define DELAY_RESULT    1
+#define RETURN_RESULT   0
+
+#define DCD(x) (x & 0x03 ? "HIGH" : "LOW")
+#define CTS(x) (x & 0x05 ? "HIGH" : "LOW")
+
+
+/* Driver will not write log messages about 
+ * modem status if defined.*/
+#define MODEM_NOT_LOG 1
+
+/*==================================================== 
+ * 	For IPXWAN 
+ *===================================================*/
 
-/* For IPXWAN */
 #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
 
-/****** Data Structures *****************************************************/
 
-/* This is an extention of the 'struct net_device' we create for each network
- * interface to keep the rest of X.25 channel-specific data.
+/*====================================================
+ *           MEMORY DEBUGGING FUNCTION
+ *====================================================
+
+#define KMEM_SAFETYZONE 8
+
+static void * dbg_kmalloc(unsigned int size, int prio, int line) {
+	int i = 0;
+	void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio);
+	char * c1 = v;	
+	c1 += sizeof(unsigned int);
+	*((unsigned int *)v) = size;
+
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D';
+		c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F';
+		c1 += 8;
+	}
+	c1 += size;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G';
+		c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L';
+		c1 += 8;
+	}
+	v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8;
+	printk(KERN_INFO "line %d  kmalloc(%d,%d) = %p\n",line,size,prio,v);
+	return v;
+}
+static void dbg_kfree(void * v, int line) {
+	unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8));
+	unsigned int size = *sp;
+	char * c1 = ((char *)v) - KMEM_SAFETYZONE*8;
+	int i = 0;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		if (   c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D'
+		    || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') {
+			printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v);
+			printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
+			                c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
+		}
+		c1 += 8;
+	}
+	c1 += size;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		if (   c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G'
+		    || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L'
+		   ) {
+			printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v);
+			printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
+			                c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
+		}
+		c1 += 8;
+	}
+	printk(KERN_INFO "line %d  kfree(%p)\n",line,v);
+	v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8);
+	kfree(v);
+}
+
+#define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__)
+#define kfree(x) dbg_kfree(x,__LINE__)
+
+==============================================================*/
+
+
+
+/*===============================================
+ * 	Data Structures 
+ *===============================================*/
+
+
+/*========================================================
+ * Name: 	x25_channel
+ *
+ * Purpose:	To hold private informaton for each  
+ *              logical channel.
+ *		
+ * Rationale:  	Per-channel debugging is possible if each 
+ *              channel has its own private area.
+ *	
+ * Assumptions:
+ *
+ * Description:	This is an extention of the 'netdevice_t' 
+ *              we create for each network interface to keep 
+ *              the rest of X.25 channel-specific data. 
+ *
+ * Construct:	Typedef
  */
 typedef struct x25_channel
 {
-	/* This member must be first. */
-	struct net_device *slave;	/* WAN slave */
-
+	wanpipe_common_t common;	/* common area for x25api and socket */
 	char name[WAN_IFNAME_SZ+1];	/* interface name, ASCIIZ */
 	char addr[WAN_ADDRESS_SZ+1];	/* media address, ASCIIZ */
-	unsigned lcn;			/* logical channel number */
 	unsigned tx_pkt_size;
 	unsigned short protocol;	/* ethertype, 0 - multiplexed */
-	char svc;			/* 0 - permanent, 1 - switched */
-	char state;			/* channel state */
 	char drop_sequence;		/* mark sequence for dropping */
 	unsigned long state_tick;	/* time of the last state change */
 	unsigned idle_timeout;		/* sec, before disconnecting */
@@ -94,63 +267,144 @@
 	char devtint;			/* Weather we should dev_tint() */
 	struct sk_buff* rx_skb;		/* receive socket buffer */
 	struct sk_buff* tx_skb;		/* transmit socket buffer */
+
+	bh_data_t *bh_head;	  	  /* Circular buffer for x25api_bh */
+	unsigned long  tq_working;
+	volatile int  bh_write;
+	volatile int  bh_read;
+	atomic_t  bh_buff_used;
+
 	sdla_t* card;			/* -> owner */
+	netdevice_t *dev;		/* -> bound devce */
+
 	int ch_idx;
+	unsigned char enable_IPX;
+	unsigned long network_number;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct net_device_stats ifstats;	/* interface statistics */
+#else
+	struct enet_statistics ifstats;
+#endif	
+	unsigned short transmit_length;
+	unsigned short tx_offset;
+	char transmit_buffer[X25_CHAN_MTU+sizeof(x25api_hdr_t)];
+
+	if_send_stat_t   if_send_stat;
+        rx_intr_stat_t   rx_intr_stat;
+        pipe_mgmt_stat_t pipe_mgmt_stat;    
+
+	unsigned long router_start_time; /* Router start time in seconds */
+	unsigned long router_up_time;
+	
 } x25_channel_t;
 
+/* FIXME Take this out */
+
+#ifdef NEX_OLD_CALL_INFO
 typedef struct x25_call_info
 {
-	char dest[17];			/* ASCIIZ destination address */
-	char src[17];			/* ASCIIZ source address */
-	char nuser;			/* number of user data bytes */
-	unsigned char user[127];	/* user data */
-	char nfacil;			/* number of facilities */
+	char dest[17];			PACKED;/* ASCIIZ destination address */
+	char src[17];			PACKED;/* ASCIIZ source address */
+	char nuser;			PACKED;/* number of user data bytes */
+	unsigned char user[127];	PACKED;/* user data */
+	char nfacil;			PACKED;/* number of facilities */
 	struct
 	{
-		unsigned char code;
-		unsigned char parm;
-	} facil[64];			/* facilities */
+		unsigned char code;     PACKED;
+		unsigned char parm;     PACKED;
+	} facil[64];			        /* facilities */
+} x25_call_info_t;
+#else
+typedef struct x25_call_info
+{
+	char dest[MAX_X25_ADDR_SIZE]		PACKED;/* ASCIIZ destination address */
+	char src[MAX_X25_ADDR_SIZE]		PACKED;/* ASCIIZ source address */
+	unsigned char nuser			PACKED;
+	unsigned char user[MAX_X25_DATA_SIZE]	PACKED;/* user data */
+	unsigned char nfacil			PACKED;
+	unsigned char facil[MAX_X25_FACL_SIZE]	PACKED;
+	unsigned short lcn             		PACKED;
 } x25_call_info_t;
+#endif
+
 
-/****** Function Prototypes *************************************************/
+  
+/*===============================================
+ *	Private Function Prototypes
+ *==============================================*/
 
-/* WAN link driver entry points. These are called by the WAN router module. */
+
+/*================================================= 
+ * WAN link driver entry points. These are 
+ * called by the WAN router module.
+ */
 static int update (wan_device_t* wandev);
-static int new_if (wan_device_t* wandev, struct net_device* dev,
+static int new_if (wan_device_t* wandev, netdevice_t* dev,
 	wanif_conf_t* conf);
-static int del_if (wan_device_t* wandev, struct net_device* dev);
+static int del_if (wan_device_t* wandev, netdevice_t* dev);
+static void disable_comm (sdla_t* card);
+static void disable_comm_shutdown(sdla_t *card);
 
-/* WANPIPE-specific entry points */
+
+
+/*================================================= 
+ *	WANPIPE-specific entry points 
+ */
 static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data);
+static void x25api_bh (netdevice_t *);
+static int x25api_bh_cleanup (netdevice_t *);
+static int bh_enqueue (netdevice_t *, struct sk_buff *);
+
 
-/* Network device interface */
-static int if_init   (struct net_device* dev);
-static int if_open   (struct net_device* dev);
-static int if_close  (struct net_device* dev);
-static int if_header (struct sk_buff* skb, struct net_device* dev,
+/*=================================================  
+ * 	Network device interface 
+ */
+static int if_init   (netdevice_t* dev);
+static int if_open   (netdevice_t* dev);
+static int if_close  (netdevice_t* dev);
+static int if_header (struct sk_buff* skb, netdevice_t* dev,
 	unsigned short type, void* daddr, void* saddr, unsigned len);
 static int if_rebuild_hdr (struct sk_buff* skb);
-static int if_send (struct sk_buff* skb, struct net_device* dev);
-static struct net_device_stats * if_stats (struct net_device* dev);
+static int if_send (struct sk_buff* skb, netdevice_t* dev);
+static struct net_device_stats *if_stats (netdevice_t* dev);
 
-/* Interrupt handlers */
-static void wpx_isr	(sdla_t* card);
-static void rx_intr	(sdla_t* card);
-static void tx_intr	(sdla_t* card);
-static void status_intr	(sdla_t* card);
-static void event_intr	(sdla_t* card);
-static void spur_intr	(sdla_t* card);
+#ifdef LINUX_2_4
+static void if_tx_timeout (netdevice_t *dev);
+#endif
+
+/*=================================================  
+ * 	Interrupt handlers 
+ */
+static void wpx_isr	(sdla_t *);
+static void rx_intr	(sdla_t *);
+static void tx_intr	(sdla_t *);
+static void status_intr	(sdla_t *);
+static void event_intr	(sdla_t *);
+static void spur_intr	(sdla_t *);
+static void timer_intr  (sdla_t *);
 
-/* Background polling routines */
+static int tx_intr_send(sdla_t *, netdevice_t *);
+static netdevice_t * move_dev_to_next (sdla_t *, netdevice_t *);
+
+/*=================================================  
+ *	Background polling routines 
+ */
 static void wpx_poll (sdla_t* card);
 static void poll_disconnected (sdla_t* card);
 static void poll_connecting (sdla_t* card);
 static void poll_active (sdla_t* card);
+static void trigger_x25_poll(sdla_t *card);
+static void x25_timer_routine(unsigned long data);
+
+
 
-/* X.25 firmware interface functions */
+/*=================================================  
+ *	X.25 firmware interface functions 
+ */
 static int x25_get_version (sdla_t* card, char* str);
 static int x25_configure (sdla_t* card, TX25Config* conf);
+static int hdlc_configure (sdla_t* card, TX25Config* conf);
+static int set_hdlc_level (sdla_t* card);
 static int x25_get_err_stats (sdla_t* card);
 static int x25_get_stats (sdla_t* card);
 static int x25_set_intr_mode (sdla_t* card, int mode);
@@ -166,62 +420,136 @@
 static int x25_fetch_events (sdla_t* card);
 static int x25_error (sdla_t* card, int err, int cmd, int lcn);
 
-/* X.25 asynchronous event handlers */
+/*=================================================  
+ *	X.25 asynchronous event handlers 
+ */
 static int incoming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
 static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
 static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
 static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
 static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
 
-/* Miscellaneous functions */
+
+/*=================================================  
+ *	Miscellaneous functions 
+ */
 static int connect (sdla_t* card);
 static int disconnect (sdla_t* card);
-static struct net_device* get_dev_by_lcn(wan_device_t* wandev, unsigned lcn);
-static int chan_connect (struct net_device* dev);
-static int chan_disc (struct net_device* dev);
-static void set_chan_state (struct net_device* dev, int state);
-static int chan_send (struct net_device* dev, struct sk_buff* skb);
+static netdevice_t* get_dev_by_lcn(wan_device_t* wandev, unsigned lcn);
+static int chan_connect (netdevice_t* dev);
+static int chan_disc (netdevice_t* dev);
+static void set_chan_state (netdevice_t* dev, int state);
+static int chan_send (netdevice_t* , void* , unsigned, unsigned char);
 static unsigned char bps_to_speed_code (unsigned long bps);
 static unsigned int dec_to_uint (unsigned char* str, int len);
-static unsigned int hex_to_uint (unsigned char* str, int len);
-static void parse_call_info (unsigned char* str, x25_call_info_t* info);
+static unsigned int hex_to_uint (unsigned char*, int);
+static void parse_call_info (unsigned char*, x25_call_info_t*);
+static netdevice_t * find_channel(sdla_t *, unsigned);
+static void bind_lcn_to_dev (sdla_t *, netdevice_t *,unsigned);
+static void setup_for_delayed_transmit (netdevice_t*, void*, unsigned);
+
+
+/*=================================================  
+ *      X25 API Functions 
+ */
+static int wanpipe_pull_data_in_skb (sdla_t *, netdevice_t *, struct sk_buff **);
+static void timer_intr_exec(sdla_t *, unsigned char);
+static int execute_delayed_cmd (sdla_t*, netdevice_t *, mbox_cmd_t *,char);
+static int api_incoming_call (sdla_t*, TX25Mbox *, int);
+static int alloc_and_init_skb_buf (sdla_t *,struct sk_buff **, int);
+static void send_delayed_cmd_result(sdla_t *, netdevice_t *dev, TX25Mbox*);
+static int clear_confirm_event (sdla_t *, TX25Mbox*);
+static void send_oob_msg (sdla_t *, netdevice_t *, TX25Mbox *);
+static int timer_intr_cmd_exec(sdla_t *card);
+static void api_oob_event (sdla_t *card,TX25Mbox *mbox);
+static int check_bad_command (sdla_t *, netdevice_t *);
+static int channel_disconnect (sdla_t*, netdevice_t *);
+static void hdlc_link_down (sdla_t*);
+
+/*=================================================
+ *     XPIPEMON Functions
+ */
+static int process_udp_mgmt_pkt(sdla_t *);
+static int udp_pkt_type( struct sk_buff *, sdla_t*);
+static int reply_udp( unsigned char *, unsigned int); 
+static void init_x25_channel_struct( x25_channel_t *);
+static void init_global_statistics( sdla_t *);
+static int store_udp_mgmt_pkt(int, char, sdla_t*, netdevice_t *, struct sk_buff *, int);
+static unsigned short calc_checksum (char *, int);
 
-/* IPX functions */
-static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming);
-static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto);
+
+
+/*================================================= 
+ *	IPX functions 
+ */
+static void switch_net_numbers(unsigned char *, unsigned long, unsigned char);
+static int handle_IPXWAN(unsigned char *, char *, unsigned char , 
+			 unsigned long , unsigned short );
 
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
-/****** Global Data **********************************************************
- * Note: All data must be explicitly initialized!!!
- */
+static void S508_S514_lock(sdla_t *, unsigned long *);
+static void S508_S514_unlock(sdla_t *, unsigned long *);
 
-/****** Public Functions ****************************************************/
 
-/*============================================================================
- * X.25 Protocol Initialization routine.
+/*=================================================  
+ * 	Global Variables 
+ *=================================================*/
+
+
+
+/*================================================= 
+ *	Public Functions 
+ *=================================================*/
+
+
+
+
+/*===================================================================
+ * wpx_init:	X.25 Protocol Initialization routine.
+ *
+ * Purpose:	To initialize the protocol/firmware.
+ * 
+ * Rationale:	This function is called by setup() function, in
+ *              sdlamain.c, to dynamically setup the x25 protocol.
+ *		This is the first protocol specific function, which
+ *              executes once on startup.
+ *                
+ * Description:	This procedure initializes the x25 firmware and
+ *    		sets up the mailbox, transmit and receive buffer
+ *              pointers. It also initializes all debugging structures
+ *              and sets up the X25 environment.
  *
- * This routine is called by the main WANPIPE module during setup.  At this
- * point adapter is completely initialized and X.25 firmware is running.
- *  o read firmware version (to make sure it's alive)
- *  o configure adapter
- *  o initialize protocol-specific fields of the adapter data space.
+ *		Sets up hardware options defined by user in [wanpipe#] 
+ *		section of wanpipe#.conf configuration file. 
  *
- * Return:	0	o.k.
- *		< 0	failure.
+ * 		At this point adapter is completely initialized 
+ *      	and X.25 firmware is running.
+ *  		o read firmware version (to make sure it's alive)
+ *  		o configure adapter
+ *  		o initialize protocol-specific fields of the 
+ *                adapter data space.
+ *
+ * Called by:	setup() function in sdlamain.c
+ *
+ * Assumptions:	None
+ *
+ * Warnings:	None
+ *
+ * Return: 	0	o.k.
+ *	 	< 0	failure.
  */
+
 int wpx_init (sdla_t* card, wandev_conf_t* conf)
 {
-	union
-	{
+	union{
 		char str[80];
 		TX25Config cfg;
 	} u;
 
 	/* Verify configuration ID */
-	if (conf->config_id != WANCONFIG_X25)
-	{
+	if (conf->config_id != WANCONFIG_X25){
 		printk(KERN_INFO "%s: invalid configuration ID %u!\n",
 			card->devname, conf->config_id)
 		;
@@ -233,23 +561,43 @@
 	card->rxmb  = (void*)(card->hw.dpmbase + X25_RXMBOX_OFFS);
 	card->flags = (void*)(card->hw.dpmbase + X25_STATUS_OFFS);
 
+	/* Initialize for S514 Card */
+	if(card->hw.type == SDLA_S514) {
+		card->mbox += X25_MB_VECTOR;
+		card->flags += X25_MB_VECTOR;
+		card->rxmb += X25_MB_VECTOR;
+	}
+
+
 	/* Read firmware version.  Note that when adapter initializes, it
 	 * clears the mailbox, so it may appear that the first command was
 	 * executed successfully when in fact it was merely erased. To work
 	 * around this, we execute the first command twice.
 	 */
 	if (x25_get_version(card, NULL) || x25_get_version(card, u.str))
-		return -EIO
-	;
-	printk(KERN_INFO "%s: running X.25 firmware v%s\n",
-		card->devname, u.str)
-	;
+		return -EIO;
+
+
+	/* X25 firmware can run ether in X25 or LAPB HDLC mode.
+         * Check the user defined option and configure accordingly */
+	if (conf->u.x25.LAPB_hdlc_only == WANOPT_YES){
+		if (set_hdlc_level(card) != CMD_OK){
+			return -EIO;	
+		}else{
+			printk(KERN_INFO "%s: running LAP_B HDLC firmware v%s\n",
+				card->devname, u.str);
+		}
+		card->u.x.LAPB_hdlc = 1;
+	}else{
+		printk(KERN_INFO "%s: running X.25 firmware v%s\n",
+				card->devname, u.str);
+		card->u.x.LAPB_hdlc = 0;
+	}
 
 	/* Configure adapter. Here we set resonable defaults, then parse
 	 * device configuration structure and set configuration options.
 	 * Most configuration options are verified and corrected (if
-	 * necessary) since we can't rely on the adapter to do so and don't
-	 * want it to fail either.
+	 * necessary) since we can't rely on the adapter to do so.
 	 */
 	memset(&u.cfg, 0, sizeof(u.cfg));
 	u.cfg.t1		= 3;
@@ -258,7 +606,7 @@
 	u.cfg.hdlcWindow	= 7;
 	u.cfg.pktWindow		= 2;
 	u.cfg.station		= 1;		/* DTE */
-	u.cfg.options		= 0x00B0;	/* disable D-bit pragmatics */
+	u.cfg.options		= 0x0090;	/* disable D-bit pragmatics */
 	u.cfg.ccittCompat	= 1988;
 	u.cfg.t10t20		= 30;
 	u.cfg.t11t21		= 30;
@@ -271,71 +619,120 @@
 	u.cfg.r13r23		= 5;
 	u.cfg.responseOpt	= 1;		/* RR's after every packet */
 
+	if (card->u.x.LAPB_hdlc){
+		u.cfg.hdlcMTU = 1027;
+	}
+
+	if (conf->u.x25.x25_conf_opt){
+		u.cfg.options = conf->u.x25.x25_conf_opt;
+	}
+
 	if (conf->clocking != WANOPT_EXTERNAL)
-		u.cfg.baudRate = bps_to_speed_code(conf->bps)
-	;
-	if (conf->station != WANOPT_DTE)
-	{
+		u.cfg.baudRate = bps_to_speed_code(conf->bps);
+
+	if (conf->station != WANOPT_DTE){
 		u.cfg.station = 0;		/* DCE mode */
 	}
-        if (conf->interface != WANOPT_RS232 ) {
+
+        if (conf->interface != WANOPT_RS232 ){
 	        u.cfg.hdlcOptions |= 0x80;      /* V35 mode */
 	} 
+
 	/* adjust MTU */
 	if (!conf->mtu || (conf->mtu >= 1024))
-		card->wandev.mtu = 1024
-	;
+		card->wandev.mtu = 1024;
 	else if (conf->mtu >= 512)
-		card->wandev.mtu = 512
-	;
+		card->wandev.mtu = 512;
 	else if (conf->mtu >= 256)
-		card->wandev.mtu = 256
-	;
+		card->wandev.mtu = 256;
 	else if (conf->mtu >= 128)
-		card->wandev.mtu = 128
-	;
-	else card->wandev.mtu = 64;
+		card->wandev.mtu = 128;
+	else 
+		card->wandev.mtu = 64;
+
 	u.cfg.defPktSize = u.cfg.pktMTU = card->wandev.mtu;
 
-	if (conf->u.x25.hi_pvc)
-	{
-		card->u.x.hi_pvc = min(conf->u.x25.hi_pvc, 4095);
+	if (conf->u.x25.hi_pvc){
+		card->u.x.hi_pvc = min(conf->u.x25.hi_pvc, MAX_LCN_NUM);
 		card->u.x.lo_pvc = min(conf->u.x25.lo_pvc, card->u.x.hi_pvc);
 	}
-	if (conf->u.x25.hi_svc)
-	{
-		card->u.x.hi_svc = min(conf->u.x25.hi_svc, 4095);
+
+	if (conf->u.x25.hi_svc){
+		card->u.x.hi_svc = min(conf->u.x25.hi_svc, MAX_LCN_NUM);
 		card->u.x.lo_svc = min(conf->u.x25.lo_svc, card->u.x.hi_svc);
 	}
-	u.cfg.loPVC	  = card->u.x.lo_pvc;
-	u.cfg.hiPVC	  = card->u.x.hi_pvc;
+
+	/* Figure out the total number of channels to configure */
+	card->u.x.num_of_ch = 0;
+	if (card->u.x.hi_svc != 0){
+		card->u.x.num_of_ch = (card->u.x.hi_svc - card->u.x.lo_svc) + 1;
+	}
+	if (card->u.x.hi_pvc != 0){
+		card->u.x.num_of_ch += (card->u.x.hi_pvc - card->u.x.lo_pvc) + 1;
+	}
+
+	if (card->u.x.num_of_ch == 0){
+		printk(KERN_INFO "%s: ERROR, Minimum number of PVC/SVC channels is 1 !\n"
+				 "%s: Please set the Lowest/Highest PVC/SVC values !\n",
+				 card->devname,card->devname);
+		return -ECHRNG;
+	}
+	
+	u.cfg.loPVC = card->u.x.lo_pvc;
+	u.cfg.hiPVC = card->u.x.hi_pvc;
 	u.cfg.loTwoWaySVC = card->u.x.lo_svc;
 	u.cfg.hiTwoWaySVC = card->u.x.hi_svc;
 
 	if (conf->u.x25.hdlc_window)
-		u.cfg.hdlcWindow = min(conf->u.x25.hdlc_window, 7)
-	;
+		u.cfg.hdlcWindow = min(conf->u.x25.hdlc_window, 7);
 	if (conf->u.x25.pkt_window)
-		u.cfg.pktWindow = min(conf->u.x25.pkt_window, 7)
-	;
+		u.cfg.pktWindow = min(conf->u.x25.pkt_window, 7);
+
 	if (conf->u.x25.t1)
-		u.cfg.t1 = min(conf->u.x25.t1, 30)
-	;
-	u.cfg.t2 = min(conf->u.x25.t2, 29);
-	u.cfg.t4 = min(conf->u.x25.t4, 240);
+		u.cfg.t1 = min(conf->u.x25.t1, 30);
+	if (conf->u.x25.t2)
+		u.cfg.t2 = min(conf->u.x25.t2, 29);
+	if (conf->u.x25.t4)
+		u.cfg.t4 = min(conf->u.x25.t4, 240);
 	if (conf->u.x25.n2)
-		u.cfg.n2 = min(conf->u.x25.n2, 30)
-	;
+		u.cfg.n2 = min(conf->u.x25.n2, 30);
+
+	if (conf->u.x25.t10_t20)
+		u.cfg.t10t20 = min(conf->u.x25.t10_t20,255);
+	if (conf->u.x25.t11_t21)
+		u.cfg.t11t21 = min(conf->u.x25.t11_t21,255);
+	if (conf->u.x25.t12_t22)
+		u.cfg.t12t22 = min(conf->u.x25.t12_t22,255);
+	if (conf->u.x25.t13_t23)	
+		u.cfg.t13t23 = min(conf->u.x25.t13_t23,255);
+	if (conf->u.x25.t16_t26)
+		u.cfg.t16t26 = min(conf->u.x25.t16_t26, 255);
+	if (conf->u.x25.t28)
+		u.cfg.t28 = min(conf->u.x25.t28, 255);
+
+	if (conf->u.x25.r10_r20)
+		u.cfg.r10r20 = min(conf->u.x25.r10_r20,250);
+	if (conf->u.x25.r12_r22)
+		u.cfg.r12r22 = min(conf->u.x25.r12_r22,250);
+	if (conf->u.x25.r13_r23)
+		u.cfg.r13r23 = min(conf->u.x25.r13_r23,250);
+
+
 	if (conf->u.x25.ccitt_compat)
-		u.cfg.ccittCompat = conf->u.x25.ccitt_compat
-	;
+		u.cfg.ccittCompat = conf->u.x25.ccitt_compat;
 
 	/* initialize adapter */
-	if ((x25_configure(card, &u.cfg) != CMD_OK) ||
-	    (x25_close_hdlc(card) != CMD_OK) ||		/* close HDLC link */
+	if (card->u.x.LAPB_hdlc){
+		if (hdlc_configure(card, &u.cfg) != CMD_OK)
+			return -EIO;
+	}else{
+		if (x25_configure(card, &u.cfg) != CMD_OK)
+			return -EIO;
+	}
+
+	if ((x25_close_hdlc(card) != CMD_OK) ||		/* close HDLC link */
 	    (x25_set_dtr(card, 0) != CMD_OK))		/* drop DTR */
-		return -EIO
-	;
+		return -EIO;
 
 	/* Initialize protocol-specific fields of adapter data space */
 	card->wandev.bps	= conf->bps;
@@ -343,202 +740,386 @@
 	card->wandev.clocking	= conf->clocking;
 	card->wandev.station	= conf->station;
 	card->isr		= &wpx_isr;
-	card->poll		= &wpx_poll;
+	card->poll		= NULL; //&wpx_poll;
+	card->disable_comm	= &disable_comm;
 	card->exec		= &wpx_exec;
 	card->wandev.update	= &update;
 	card->wandev.new_if	= &new_if;
 	card->wandev.del_if	= &del_if;
+
+	/* WARNING: This function cannot exit with an error
+	 *          after the change of state */
 	card->wandev.state	= WAN_DISCONNECTED;
+	
 	card->wandev.enable_tx_int = 0;
 	card->irq_dis_if_send_count = 0;
         card->irq_dis_poll_count = 0;
-	card->wandev.enable_IPX = conf->enable_IPX;
+	card->u.x.tx_dev = NULL;
+	card->u.x.no_dev = 0;
+
+
+	/* Configure for S514 PCI Card */
+	if (card->hw.type == SDLA_S514) {
+		card->u.x.hdlc_buf_status = 
+			(volatile unsigned char *)
+				(card->hw.dpmbase + X25_MB_VECTOR+ X25_MISC_HDLC_BITS);
+	}else{
+		card->u.x.hdlc_buf_status = 
+			(volatile unsigned char *)(card->hw.dpmbase + X25_MISC_HDLC_BITS); 
+	}
+
+	card->u.x.poll_device=NULL;
+	card->wandev.udp_port = conf->udp_port;
+
+	/* Enable or disable call setup logging */
+	if (conf->u.x25.logging == WANOPT_YES){
+		printk(KERN_INFO "%s: Enabling Call Logging.\n",
+			card->devname);
+		card->u.x.logging = 1;
+	}else{	
+		card->u.x.logging = 0;
+	}
+
+	/* Enable or disable modem status reporting */
+	if (conf->u.x25.oob_on_modem == WANOPT_YES){
+		printk(KERN_INFO "%s: Enabling OOB on Modem change.\n",
+			card->devname);
+		card->u.x.oob_on_modem = 1;
+	}else{
+		card->u.x.oob_on_modem = 0;
+	}
+	
+	init_global_statistics(card);	
+
+      #ifndef LINUX_2_4
+	card->u.x.x25_poll_task.next = NULL;
+      #endif
+	card->u.x.x25_poll_task.sync=0;
+	card->u.x.x25_poll_task.routine = (void*)(void*)wpx_poll;
+	card->u.x.x25_poll_task.data = card;
+
+	init_timer(&card->u.x.x25_timer);
+	card->u.x.x25_timer.data = (unsigned long)card;
+	card->u.x.x25_timer.function = x25_timer_routine;
 	
-	if (conf->network_number)
-		card->wandev.network_number = conf->network_number;
-	else
-		card->wandev.network_number = 0xDEADBEEF;
 	return 0;
 }
 
-/******* WAN Device Driver Entry Points *************************************/
+/*=========================================================
+ *	WAN Device Driver Entry Points 
+ *========================================================*/
 
-/*============================================================================
- * Update device status & statistics.
+/*============================================================
+ * Name:	update(),  Update device status & statistics.
+ *
+ * Purpose:	To provide debugging and statitical
+ *              information to the /proc file system.
+ *              /proc/net/wanrouter/wanpipe#
+ *              	
+ * Rationale:	The /proc file system is used to collect
+ *              information about the kernel and drivers.
+ *              Using the /proc file system the user
+ *              can see exactly what the sangoma drivers are
+ *              doing. And in what state they are in. 
+ *                
+ * Description: Collect all driver statistical information
+ *              and pass it to the top laywer. 
+ *		
+ *		Since we have to execute a debugging command, 
+ *              to obtain firmware statitics, we trigger a 
+ *              UPDATE function within the timer interrtup.
+ *              We wait until the timer update is complete.
+ *              Once complete return the appropriate return
+ *              code to indicate that the update was successful.
+ *              
+ * Called by:	device_stat() in wanmain.c
+ *
+ * Assumptions:	
+ *
+ * Warnings:	This function will degrade the performance
+ *              of the router, since it uses the mailbox. 
+ *
+ * Return: 	0 	OK
+ * 		<0	Failed (or busy).
  */
+
 static int update (wan_device_t* wandev)
 {
-	sdla_t* card;
+	volatile sdla_t* card;
+	TX25Status* status;
+	unsigned long timeout;
 
 	/* sanity checks */
 	if ((wandev == NULL) || (wandev->private == NULL))
 		return -EFAULT;
+
 	if (wandev->state == WAN_UNCONFIGURED)
 		return -ENODEV;
-	if (test_and_set_bit(0, (void*)&wandev->critical))
+
+	if (test_bit(SEND_CRIT, (void*)&wandev->critical))
 		return -EAGAIN;
+
+	if (!wandev->dev)
+		return -ENODEV;
+	
 	card = wandev->private;
+	status = card->flags;
 
-	x25_get_err_stats(card);
-	x25_get_stats(card);
-	wandev->critical = 0;
+	card->u.x.timer_int_enabled |= TMR_INT_ENABLED_UPDATE;
+	status->imask |= INTR_ON_TIMER;
+	timeout = jiffies;	
+
+	for (;;){
+		if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_UPDATE)){	
+			break;
+		}
+		if ((jiffies-timeout) > 1*HZ){
+			card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
+			return -EAGAIN;
+		}
+	}
 	return 0;
 }
 
-/*============================================================================
- * Create new logical channel.
- * This routine is called by the router when ROUTER_IFNEW IOCTL is being
- * handled.
- * o parse media- and hardware-specific configuration
- * o make sure that a new channel can be created
- * o allocate resources, if necessary
- * o prepare network device structure for registaration.
+
+/*===================================================================
+ * Name:	new_if
+ *
+ * Purpose:	To allocate and initialize resources for a 
+ *              new logical channel.  
+ * 
+ * Rationale:	A new channel can be added dynamically via
+ *              ioctl call.
+ *                
+ * Description:	Allocate a private channel structure, x25_channel_t.
+ *		Parse the user interface options from wanpipe#.conf 
+ *		configuration file. 
+ *		Bind the private are into the network device private
+ *              area pointer (dev->priv).
+ *		Prepare the network device structure for registration.
+ *
+ * Called by:	ROUTER_IFNEW Ioctl call, from wanrouter_ioctl() 
+ *              (wanmain.c)
+ *
+ * Assumptions: None
  *
- * Return:	0	o.k.
- *		< 0	failure (channel will not be created)
+ * Warnings:	None
+ *
+ * Return: 	0 	Ok
+ *		<0 	Failed (channel will not be created)
  */
-static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* conf)
+static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf)
 {
 	sdla_t* card = wandev->private;
 	x25_channel_t* chan;
 	int err = 0;
 
-	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ))
-	{
+	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)){
 		printk(KERN_INFO "%s: invalid interface name!\n",
-			card->devname)
-		;
+			card->devname);
 		return -EINVAL;
 	}
 
+	if(card->wandev.new_if_cnt++ > 0 && card->u.x.LAPB_hdlc) {
+		printk(KERN_INFO "%s: Error: Running LAPB HDLC Mode !\n",
+						card->devname);
+		printk(KERN_INFO 
+			"%s: Maximum number of network interfaces must be one !\n",
+						card->devname);
+		return -EEXIST;
+	}
+
 	/* allocate and initialize private data */
-	chan = kmalloc(sizeof(x25_channel_t), GFP_KERNEL);
-	if (chan == NULL)
-		return -ENOMEM
-	;
+	chan = kmalloc(sizeof(x25_channel_t), GFP_ATOMIC);
+	if (chan == NULL){
+		return -ENOMEM;
+	}
+	
 	memset(chan, 0, sizeof(x25_channel_t));
+
+	/* Bug Fix: Seg Err on PVC startup
+	 * It must be here since bind_lcn_to_dev expects 
+	 * it bellow */
+	dev->priv = chan;
+	
 	strcpy(chan->name, conf->name);
 	chan->card = card;
-	chan->protocol = ETH_P_IP;
+	chan->dev = dev;
+	chan->common.sk = NULL;
+	chan->common.func = NULL;
+	chan->common.rw_bind = 0;
 	chan->tx_skb = chan->rx_skb = NULL;
 
 	/* verify media address */
-	if (conf->addr[0] == '@')		/* SVC */
-	{
-		chan->svc = 1;
+	if (conf->addr[0] == '@'){		/* SVC */
+		chan->common.svc = 1;
 		strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ);
 
 		/* Set channel timeouts (default if not specified) */
-		chan->idle_timeout = (conf->idle_timeout) ? conf->idle_timeout : 					90;
-		chan->hold_timeout = (conf->hold_timeout) ? conf->hold_timeout :					10;
-	}
-	else if (is_digit(conf->addr[0]))	/* PVC */
-	{
+		chan->idle_timeout = (conf->idle_timeout) ? 
+					conf->idle_timeout : 90;
+		chan->hold_timeout = (conf->hold_timeout) ? 
+					conf->hold_timeout : 10;
+
+	}else if (is_digit(conf->addr[0])){	/* PVC */
 		int lcn = dec_to_uint(conf->addr, 0);
 
-		if ((lcn >= card->u.x.lo_pvc) && (lcn <= card->u.x.hi_pvc))
-		{
-			chan->lcn = lcn;
-		}
-		else
-		{
+		if ((lcn >= card->u.x.lo_pvc) && (lcn <= card->u.x.hi_pvc)){
+			bind_lcn_to_dev (card, dev, lcn);
+		}else{
 			printk(KERN_ERR
 				"%s: PVC %u is out of range on interface %s!\n",
-				wandev->name, lcn, chan->name)
-			;
+				wandev->name, lcn, chan->name);
 			err = -EINVAL;
 		}
-	}
-	else
-	{
+	}else{
 		printk(KERN_ERR
 			"%s: invalid media address on interface %s!\n",
-			wandev->name, chan->name)
-		;
+			wandev->name, chan->name);
 		err = -EINVAL;
 	}
-	if (err)
-	{
+
+	if(strcmp(conf->usedby, "WANPIPE") == 0){
+                printk(KERN_INFO "%s: Running in WANPIPE mode %s\n",
+			wandev->name, chan->name);
+                chan->common.usedby = WANPIPE;
+		chan->protocol = htons(ETH_P_IP);
+
+        }else if(strcmp(conf->usedby, "API") == 0){
+		chan->common.usedby = API;
+                printk(KERN_INFO "%s: Running in API mode %s\n",
+			wandev->name, chan->name);
+		chan->protocol = htons(X25_PROT);
+	}
+
+
+	if (err){
 		kfree(chan);
+		dev->priv = NULL;
 		return err;
 	}
+	
+	chan->enable_IPX = conf->enable_IPX;
+	
+	if (chan->enable_IPX)
+		chan->protocol = htons(ETH_P_IPX);
+	
+	if (conf->network_number)
+		chan->network_number = conf->network_number;
+	else
+		chan->network_number = 0xDEADBEEF;
 
 	/* prepare network device data space for registration */
-	strcpy(dev->name, chan->name);
+      #ifdef LINUX_2_4
+	strcpy(dev->name,chan->name);
+      #else
+	dev->name = (char *)kmalloc(strlen(chan->name) + 2, GFP_KERNEL); 
+	sprintf(dev->name, "%s", chan->name);
+      #endif
 	dev->init = &if_init;
-	dev->priv = chan;
+
+	init_x25_channel_struct(chan);
+
 	return 0;
 }
 
-/*============================================================================
- * Delete logical channel.
+/*===================================================================
+ * Name:	del_if(),  Remove a logical channel.	 
+ *
+ * Purpose:	To dynamically remove a logical channel.
+ * 
+ * Rationale:	Each logical channel should be dynamically
+ *              removable. This functin is called by an 
+ *              IOCTL_IFDEL ioctl call or shutdown(). 
+ *                
+ * Description: Do nothing.
+ *
+ * Called by:	IOCTL_IFDEL : wanrouter_ioctl() from wanmain.c
+ *              shutdown() from sdlamain.c
+ *
+ * Assumptions: 
+ *
+ * Warnings:
+ *
+ * Return: 	0 Ok. Void function.
  */
-static int del_if (wan_device_t* wandev, struct net_device* dev)
+
+//FIXME Del IF Should be taken out now.
+
+static int del_if (wan_device_t* wandev, netdevice_t* dev)
 {
-	if (dev->priv)
-	{
-		kfree(dev->priv);
-		dev->priv = NULL;
-	}
 	return 0;
 }
 
-/****** WANPIPE-specific entry points ***************************************/
 
-/*============================================================================
- * Execute adapter interface command.
- */
+/*============================================================
+ * Name:	wpx_exec
+ *
+ * Description:	Execute adapter interface command.
+ * 		This option is currently dissabled.
+ *===========================================================*/
 
 static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data)
 {
-	TX25Mbox* mbox = card->mbox;
-	int retry = MAX_CMD_RETRY;
-	int err, len;
-	TX25Cmd cmd;
-
-	if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
-		return -EFAULT;
-		
-	/* execute command */
+        return 0;
+}
 
-	do
-	{
-		memcpy(&mbox->cmd, &cmd, sizeof(cmd));
-		if (cmd.length)
-		{
-			if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
-				return-EFAULT;
-		}
-		if (sdla_exec(mbox))
-			err = mbox->cmd.result
-		;
-		else return -EIO;
-	}
-	while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn));
+/*============================================================
+ * Name:	disable_comm	
+ *
+ * Description:	Disable communications during shutdown.
+ *              Dont check return code because there is 
+ *              nothing we can do about it.  
+ *
+ * Warning:	Dev and private areas are gone at this point.
+ *===========================================================*/
 
-	/* return result */
-	if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd)))
-		return -EFAULT;
-	len = mbox->cmd.length;
-	if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
-		return -EFAULT;
-	return 0;
+static void disable_comm(sdla_t* card)
+{
+	disable_comm_shutdown(card);
+	del_timer(&card->u.x.x25_timer);
+	return;
 }
 
-/****** Network Device Interface ********************************************/
 
-/*============================================================================
- * Initialize Linux network interface.
+/*============================================================
+ *	Network Device Interface 
+ *===========================================================*/
+
+/*===================================================================
+ * Name:	if_init(),   Netowrk Interface Initialization 	 
  *
- * This routine is called only once for each interface, during Linux network
- * interface registration.  Returning anything but zero will fail interface
- * registration.
+ * Purpose:	To initialize a network interface device structure.
+ * 
+ * Rationale:	During network interface startup, the if_init
+ *              is called by the kernel to initialize the
+ *              netowrk device structure.  Thus a driver
+ *              can customze a network device. 
+ *                
+ * Description:	Initialize the netowrk device call back
+ *              routines.  This is where we tell the kernel
+ *              which function to use when it wants to send
+ *              via our interface. 
+ *		Furthermore, we initialize the device flags, 
+ *              MTU and physical address of the board.
+ *
+ * Called by:	Kernel (/usr/src/linux/net/core/dev.c)
+ * 		(dev->init())
+ *
+ * Assumptions: None
+ *	
+ * Warnings:	None
+ *
+ * Return: 	0 	Ok : Void function.
  */
-static int if_init (struct net_device* dev)
+static int if_init (netdevice_t* dev)
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 	wan_device_t* wandev = &card->wandev;
+      #ifdef LINUX_2_0
+	int i;
+      #endif
 
 	/* Initialize device driver entry points */
 	dev->open		= &if_open;
@@ -548,127 +1129,270 @@
 	dev->hard_start_xmit	= &if_send;
 	dev->get_stats		= &if_stats;
 
+      #ifdef LINUX_2_4
+	dev->tx_timeout		= &if_tx_timeout;
+	dev->watchdog_timeo	= TX_TIMEOUT;
+      #endif
+
 	/* Initialize media-specific parameters */
-	dev->type		= 30;		/* ARP h/w type */
-	dev->mtu		= X25_CHAN_MTU;
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	dev->type		= ARPHRD_PPP;		/* ARP h/w type */
+      #else
+        dev->family             = AF_INET;      /* address family */
+        dev->type               = ARPHRD_PPP;   /* no x25 type */
+      #endif
+	dev->flags		|= IFF_POINTOPOINT;
+	dev->flags		|= IFF_NOARP;
+
+	if (chan->common.usedby == API){
+		dev->mtu	= X25_CHAN_MTU+sizeof(x25api_hdr_t);
+	}else{
+		dev->mtu	= card->wandev.mtu; 	
+	}
+	
 	dev->hard_header_len	= X25_HRDHDR_SZ; /* media header length */
 	dev->addr_len		= 2;		/* hardware address length */
-	if (!chan->svc)
-		*(unsigned short*)dev->dev_addr = htons(chan->lcn);
-
+	
+	if (!chan->common.svc){
+		*(unsigned short*)dev->dev_addr = htons(chan->common.lcn);
+	}
+	
 	/* Initialize hardware parameters (just for reference) */
 	dev->irq	= wandev->irq;
 	dev->dma	= wandev->dma;
 	dev->base_addr	= wandev->ioport;
 	dev->mem_start	= (unsigned long)wandev->maddr;
-	dev->mem_end	= dev->mem_end + wandev->msize - 1;
+	dev->mem_end	= wandev->maddr + wandev->msize - 1;
 
         /* Set transmit buffer queue length */
-        dev->tx_queue_len = 10;
+        dev->tx_queue_len = 100;
 
 	/* Initialize socket buffers */
-	
-	dev_init_buffers(dev);
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	dev_init_buffers(dev);	
+      #else
+        for (i = 0; i < DEV_NUMBUFFS; ++i)
+                skb_queue_head_init(&dev->buffs[i]);
+      #endif
+
+	/* FIXME Why are we doing this */
 	set_chan_state(dev, WAN_DISCONNECTED);
 	return 0;
 }
 
-/*============================================================================
- * Open network interface.
- * o prevent module from unloading by incrementing use count
- * o if link is disconnected then initiate connection
+
+/*===================================================================
+ * Name:	if_open(),   Open/Bring up the Netowrk Interface 
+ *
+ * Purpose:	To bring up a network interface.
+ * 
+ * Rationale:	
+ *                
+ * Description:	Open network interface.
+ * 		o prevent module from unloading by incrementing use count
+ * 		o if link is disconnected then initiate connection
  *
- * Return 0 if O.k. or errno.
+ * Called by:	Kernel (/usr/src/linux/net/core/dev.c)
+ * 		(dev->open())
+ *
+ * Assumptions: None
+ *	
+ * Warnings:	None
+ *
+ * Return: 	0 	Ok
+ * 		<0 	Failur: Interface will not come up.
  */
-static int if_open (struct net_device* dev)
+
+static int if_open (netdevice_t* dev)
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
+	struct timeval tv;
+	unsigned long smp_flags;
+	
+	if (is_dev_running(dev))
+		return -EBUSY;
 
-	if (dev->start)
-		return -EBUSY;		/* only one open is allowed */
+	chan->tq_working = 0;
+
+	/* Initialize the task queue */
+      #ifndef LINUX_2_4
+	chan->common.wanpipe_task.next = NULL;
+      #endif
+	chan->common.wanpipe_task.sync = 0;
+	chan->common.wanpipe_task.routine = (void *)(void *)x25api_bh;
+	chan->common.wanpipe_task.data = dev;
+
+	/* Allocate and initialize BH circular buffer */
+	/* Add 1 to MAX_BH_BUFF so we don't have test with (MAX_BH_BUFF-1) */
+	chan->bh_head = kmalloc((sizeof(bh_data_t)*(MAX_BH_BUFF+1)),GFP_ATOMIC);
+
+	if (chan->bh_head == NULL){
+		printk(KERN_INFO "%s: ERROR, failed to allocate memory ! BH_BUFFERS !\n",
+				card->devname);
+
+		return -ENOBUFS;
+	}
+	memset(chan->bh_head,0,(sizeof(bh_data_t)*(MAX_BH_BUFF+1)));
+	atomic_set(&chan->bh_buff_used, 0);
+
+	/* Increment the number of interfaces */
+	++card->u.x.no_dev;
 	
-	if (test_and_set_bit(0, (void*)&card->wandev.critical))
-		return -EAGAIN;
+	wanpipe_open(card);
 
+	/* LAPB protocol only uses one interface, thus
+	 * start the protocol after it comes up. */
+	if (card->u.x.LAPB_hdlc){
+		if (card->open_cnt == 1){
+			TX25Status* status = card->flags;
+			S508_S514_lock(card, &smp_flags);
+			x25_set_intr_mode(card, INTR_ON_TIMER); 
+			status->imask &= ~INTR_ON_TIMER;
+			S508_S514_unlock(card, &smp_flags);
+		}
+	}else{
+		/* X25 can have multiple interfaces thus, start the 
+		 * protocol once all interfaces are up */
+
+		//FIXME: There is a bug here. If interface is
+		//brought down and up, it will try to enable comm.
+		if (card->open_cnt == card->u.x.num_of_ch){
+
+			S508_S514_lock(card, &smp_flags);
+			connect(card);
+			S508_S514_unlock(card, &smp_flags);
+
+			del_timer(&card->u.x.x25_timer);
+			card->u.x.x25_timer.expires=jiffies+HZ;
+			add_timer(&card->u.x.x25_timer);
+		}
+	}
+	/* Device is not up untill the we are in connected state */
+	do_gettimeofday( &tv );
+	chan->router_start_time = tv.tv_sec;
+
+      #ifdef LINUX_2_4
+	netif_start_queue(dev);
+      #else	
 	dev->interrupt = 0;
 	dev->tbusy = 0;
 	dev->start = 1;
-	wanpipe_open(card);
+      #endif
 
-	/* If this is the first open, initiate physical connection */
-	if (card->open_cnt == 1)
-		connect(card);
-	card->wandev.critical = 0;
 	return 0;
 }
 
-/*============================================================================
- * Close network interface.
- * o reset flags.
- * o if there's no more open channels then disconnect physical link.
+/*===================================================================
+ * Name:	if_close(),   Close/Bring down the Netowrk Interface 
+ *
+ * Purpose:	To bring down a network interface.
+ * 
+ * Rationale:	
+ *                
+ * Description:	Close network interface.
+ * 		o decrement use module use count
+ *
+ * Called by:	Kernel (/usr/src/linux/net/core/dev.c)
+ * 		(dev->close())
+ *		ifconfig <name> down: will trigger the kernel
+ *              which will call this function.
+ *
+ * Assumptions: None
+ *	
+ * Warnings:	None
+ *
+ * Return: 	0 	Ok
+ * 		<0 	Failure: Interface will not exit properly.
  */
-static int if_close (struct net_device* dev)
+static int if_close (netdevice_t* dev)
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
-
-	if (test_and_set_bit(0, (void*)&card->wandev.critical))
-		return -EAGAIN;
-
-	dev->start = 0;
-	if ((chan->state == WAN_CONNECTED) || (chan->state == WAN_CONNECTING))
+	unsigned long smp_flags;
+	
+	stop_net_queue(dev);
+      #ifndef LINUX_2_4
+	dev->start=0;
+      #endif
+	
+	if ((chan->common.state == WAN_CONNECTED) || 
+	    (chan->common.state == WAN_CONNECTING)){
+		S508_S514_lock(card, &smp_flags);
 		chan_disc(dev);
-		
+		S508_S514_unlock(card, &smp_flags);
+	}
+
 	wanpipe_close(card);
 
+	S508_S514_lock(card, &smp_flags);
+	if (chan->bh_head){
+		int i;
+		struct sk_buff *skb;
+	
+		for (i=0; i<(MAX_BH_BUFF+1); i++){
+			skb = ((bh_data_t *)&chan->bh_head[i])->skb;
+			if (skb != NULL){
+                		wan_dev_kfree_skb(skb, FREE_READ);
+			}
+		}
+		kfree(chan->bh_head);
+		chan->bh_head=NULL;
+	}
+	S508_S514_unlock(card, &smp_flags);
+
 	/* If this is the last close, disconnect physical link */
-	if (!card->open_cnt)
+	if (!card->open_cnt){
+		S508_S514_lock(card, &smp_flags);
 		disconnect(card);
-		
-	card->wandev.critical = 0;
+		x25_set_intr_mode(card, 0);
+		S508_S514_unlock(card, &smp_flags);
+	}
+	
+	/* Decrement the number of interfaces */
+	--card->u.x.no_dev;
 	return 0;
 }
 
-/*============================================================================
- * Build media header.
- * o encapsulate packet according to encapsulation type.
+/*======================================================================
+ * 	Build media header.
+ * 	o encapsulate packet according to encapsulation type.
  *
- * The trick here is to put packet type (Ethertype) into 'protocol' field of
- * the socket buffer, so that we don't forget it.  If encapsulation fails,
- * set skb->protocol to 0 and discard packet later.
+ * 	The trick here is to put packet type (Ethertype) into 'protocol' 
+ *      field of the socket buffer, so that we don't forget it.  
+ *      If encapsulation fails, set skb->protocol to 0 and discard 
+ *      packet later.
  *
- * Return:	media header length.
- */
-static int if_header (struct sk_buff* skb, struct net_device* dev,
+ * 	Return:		media header length.
+ *======================================================================*/
+
+static int if_header (struct sk_buff* skb, netdevice_t* dev,
 	unsigned short type, void* daddr, void* saddr, unsigned len)
 {
 	x25_channel_t* chan = dev->priv;
 	int hdr_len = dev->hard_header_len;
-
-	skb->protocol = type;
-	if (!chan->protocol)
-	{
-		hdr_len = wanrouter_encapsulate(skb, dev);
-		if (hdr_len < 0)
-		{
+	
+	skb->protocol = htons(type);
+	if (!chan->protocol){
+		hdr_len = wanrouter_encapsulate(skb, dev, type);
+		if (hdr_len < 0){
 			hdr_len = 0;
-			skb->protocol = 0;
+			skb->protocol = htons(0);
 		}
 	}
 	return hdr_len;
 }
 
-/*============================================================================
- * Re-build media header.
+/*===============================================================
+ * 	Re-build media header.
  *
- * Return:	1	physical address resolved.
- *		0	physical address not resolved
- */
- 
+ * 	Return:		1	physical address resolved.
+ *			0	physical address not resolved
+ *==============================================================*/
+
 static int if_rebuild_hdr (struct sk_buff* skb)
 {
-	struct net_device *dev=skb->dev;
+	netdevice_t *dev = skb->dev; 
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 
@@ -677,185 +1401,294 @@
 	return 1;
 }
 
+
+#ifdef LINUX_2_4
 /*============================================================================
- * Send a packet on a network interface.
- * o set tbusy flag (marks start of the transmission).
- * o check link state. If link is not up, then drop the packet.
- * o check channel status. If it's down then initiate a call.
- * o pass a packet to corresponding WAN device.
- * o free socket buffer
+ * Handle transmit timeout event from netif watchdog
+ */
+static void if_tx_timeout (netdevice_t *dev)
+{
+    	x25_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+
+	/* If our device stays busy for at least 5 seconds then we will
+	 * kick start the device by making dev->tbusy = 0.  We expect
+	 * that our device never stays busy more than 5 seconds. So this                 
+	 * is only used as a last resort.
+	 */
+
+	++chan->if_send_stat.if_send_tbusy_timeout;
+	printk (KERN_INFO "%s: Transmit timed out on %s\n", 
+			card->devname, dev->name);
+	netif_wake_queue (dev);
+}
+#endif
+
+
+/*=========================================================================
+ * 	Send a packet on a network interface.
+ * 	o set tbusy flag (marks start of the transmission).
+ * 	o check link state. If link is not up, then drop the packet.
+ * 	o check channel status. If it's down then initiate a call.
+ * 	o pass a packet to corresponding WAN device.
+ * 	o free socket buffer
  *
- * Return:	0	complete (socket buffer must be freed)
+ * 	Return:	0	complete (socket buffer must be freed)
  *		non-0	packet may be re-transmitted (tbusy must be set)
  *
- * Notes:
- * 1. This routine is called either by the protocol stack or by the "net
- *    bottom half" (with interrupts enabled).
- * 2. Setting tbusy flag will inhibit further transmit requests from the
- *    protocol stack and can be used for flow control with protocol layer.
- */
+ * 	Notes:
+ * 	1. This routine is called either by the protocol stack or by the "net
+ *    	bottom half" (with interrupts enabled).
+ * 	2. Setting tbusy flag will inhibit further transmit requests from the
+ *    	protocol stack and can be used for flow control with protocol layer.
+ *
+ *========================================================================*/
 
-static int if_send (struct sk_buff* skb, struct net_device* dev)
+static int if_send (struct sk_buff* skb, netdevice_t* dev)
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
-	struct net_device *dev2;
 	TX25Status* status = card->flags;
-	unsigned long host_cpu_flags;
+	int udp_type;
+	unsigned long smp_flags=0;
 
-	if (dev->tbusy)
-	{
-		++chan->ifstats.rx_dropped;	
-		if ((jiffies - chan->tick_counter) < (5*HZ))
-		{
-			return dev->tbusy;
+	++chan->if_send_stat.if_send_entry;
+
+      #ifdef LINUX_2_4
+	netif_stop_queue(dev);
+      #endif
+
+	/* No need to check frame length, since socket code
+         * will perform the check for us */
+
+      #ifndef LINUX_2_4
+	if (dev->tbusy){
+		netdevice_t *dev2;
+		
+		++chan->if_send_stat.if_send_tbusy;
+		if ((jiffies - chan->tick_counter) < (5*HZ)){
+			return 1;
 		}
 		printk(KERN_INFO "%s: Transmit time out %s!\n",
-		       card->devname, dev->name);
+			card->devname, dev->name);
+		
+		for( dev2 = card->wandev.dev; dev2; 
+		     dev2 = *((netdevice_t**)dev2->priv)){
 
-		dev2 = card->wandev.dev;
-		while (dev2) {
-			x25_channel_t *chan2 = dev2->priv;
 	        	dev2->tbusy = 0;
-			dev2 = chan2->slave;
 		}
+		++chan->if_send_stat.if_send_tbusy_timeout;
 	}
+      #endif
+
 	chan->tick_counter = jiffies;
+	
+	/* Critical region starts here */
+	S508_S514_lock(card, &smp_flags);
+	
+	if (test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)){
+		printk(KERN_INFO "Hit critical in if_send()! %lx\n",card->wandev.critical);
+		goto if_send_crit_exit;
+	}
+	
+	udp_type = udp_pkt_type(skb, card);
 
-	disable_irq(card->hw.irq);
-	++card->irq_dis_if_send_count;
+        if(udp_type != UDP_INVALID_TYPE) {
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
-	{
-		printk(KERN_INFO "Hit critical in if_send()!\n");
-		if (card->wandev.critical == CRITICAL_IN_ISR) 
-		{
-			card->wandev.enable_tx_int = 1;
-			dev->tbusy = 1;
-			
-			save_flags(host_cpu_flags);
-                        cli();
-                        if ((!(--card->irq_dis_if_send_count)) &&
-                                        (!card->irq_dis_poll_count))
-                                enable_irq(card->hw.irq);
-                        restore_flags(host_cpu_flags);
-			
-			return dev->tbusy;
-		}
-		dev_kfree_skb(skb);
-		
-		save_flags(host_cpu_flags);
-                cli();
-                if ((!(--card->irq_dis_if_send_count)) &&
-                                         (!card->irq_dis_poll_count))
-                        enable_irq(card->hw.irq);
-                restore_flags(host_cpu_flags);
+                if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, dev, skb,
+                        chan->common.lcn)) {
 
-		return dev->tbusy;
+                        status->imask |= INTR_ON_TIMER;
+                        if (udp_type == UDP_XPIPE_TYPE){
+                                chan->if_send_stat.if_send_PIPE_request++;
+			}
+               	}
+		start_net_queue(dev);
+		clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+		S508_S514_unlock(card, &smp_flags);
+		return 0;
 	}
 
-	/* Below is only until we have per-channel IPX going.... */
-	if(!(chan->svc))
-		chan->protocol = skb->protocol;
+	if (chan->transmit_length){
+		//FIXME: This check doesn't make sense any more
+		if (chan->common.state != WAN_CONNECTED){
+			chan->transmit_length=0;
+			atomic_set(&chan->common.driver_busy,0);
+		}else{
+			stop_net_queue(dev);
+			++card->u.x.tx_interrupts_pending;
+		        status->imask |= INTR_ON_TX_FRAME;
+			clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+			S508_S514_unlock(card, &smp_flags);
+			return 1;
+		}
+	}
 
-	if (card->wandev.state != WAN_CONNECTED)
+	if (card->wandev.state != WAN_CONNECTED){
 		++chan->ifstats.tx_dropped;
-
-	/* Below is only until we have per-channel IPX going.... */
-	else if ( (chan->svc) && (chan->protocol && (chan->protocol != skb->protocol)))
-	{
+		++card->wandev.stats.tx_dropped;
+		++chan->if_send_stat.if_send_wan_disconnected;	
+		
+	}else if ( chan->protocol && (chan->protocol != skb->protocol)){
 		printk(KERN_INFO
 			"%s: unsupported Ethertype 0x%04X on interface %s!\n",
-			card->devname, skb->protocol, dev->name);
+			chan->name, htons(skb->protocol), dev->name);
+		
+		printk(KERN_INFO "PROTO %Xn", htons(chan->protocol));
 		++chan->ifstats.tx_errors;
-	}
-	else switch (chan->state)
-	{
+		++chan->ifstats.tx_dropped;
+		++card->wandev.stats.tx_dropped;
+		++chan->if_send_stat.if_send_protocol_error;
+		
+	}else switch (chan->common.state){
+
 		case WAN_DISCONNECTED:
 			/* Try to establish connection. If succeded, then start
 			 * transmission, else drop a packet.
 			 */
-			if (chan_connect(dev) != 0)
-			{
+			if (chan->common.usedby == API){
 				++chan->ifstats.tx_dropped;
 				++card->wandev.stats.tx_dropped;
 				break;
+			}else{
+				if (chan_connect(dev) != 0){
+					++chan->ifstats.tx_dropped;
+					++card->wandev.stats.tx_dropped;
+					break;
+				}
 			}
 			/* fall through */
 
 		case WAN_CONNECTED:
-			if( skb->protocol == ETH_P_IPX ) 
-			{
-				if(card->wandev.enable_IPX) 
-				{
+			if( skb->protocol == htons(ETH_P_IPX)) {
+				if(chan->enable_IPX) {
 					switch_net_numbers( skb->data, 
-						card->wandev.network_number, 0);
-				}
-				else 
-				{
+						chan->network_number, 0);
+				} else {
 					++card->wandev.stats.tx_dropped;
 					++chan->ifstats.tx_dropped;
-					goto tx_done;
+					++chan->if_send_stat.if_send_protocol_error;
+					goto if_send_crit_exit;
 				}
 			}
-			dev->trans_start = jiffies;
-			if(chan_send(dev, skb))
-			{
-				dev->tbusy = 1;
-				status->imask |= 0x2;
-			}
+			/* We never drop here, if cannot send than, copy
+	                 * a packet into a transmit buffer 
+                         */
+			chan_send(dev, skb->data, skb->len, 0);
 			break;
 
 		default:
 			++chan->ifstats.tx_dropped;	
 			++card->wandev.stats.tx_dropped;
+			break;
 	}
-tx_done:
-	if (!dev->tbusy)
-		dev_kfree_skb(skb);
-
-	card->wandev.critical = 0;
-	save_flags(host_cpu_flags);
-        cli();
-        if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
-                enable_irq(card->hw.irq);
-        restore_flags(host_cpu_flags);
-	return dev->tbusy;
+
+
+if_send_crit_exit:
+	
+       	wan_dev_kfree_skb(skb, FREE_WRITE);
+
+	start_net_queue(dev);
+	clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+	S508_S514_unlock(card, &smp_flags);
+	return 0;
 }
 
 /*============================================================================
- * Get Ethernet-style interface statistics.
- * Return a pointer to struct net_device_stats
- */
- 
-static struct net_device_stats* if_stats (struct net_device* dev)
+ * Setup so that a frame can be transmitted on the occurence of a transmit
+ * interrupt.
+ *===========================================================================*/
+
+static void setup_for_delayed_transmit (netdevice_t* dev, void* buf,
+	unsigned len)
 {
-	x25_channel_t* chan = dev->priv;
-	if(chan==NULL)
+        x25_channel_t* chan = dev->priv;
+        sdla_t* card = chan->card;
+	TX25Status* status = card->flags;
+
+	++chan->if_send_stat.if_send_adptr_bfrs_full;
+
+        if(chan->transmit_length) {
+                printk(KERN_INFO "%s: Error, transmit length set in delayed transmit!\n",
+				card->devname);
+                return;
+        }
+
+	if (chan->common.usedby == API){
+		if (len > X25_CHAN_MTU+sizeof(x25api_hdr_t)) {
+			++chan->ifstats.tx_dropped;	
+			++card->wandev.stats.tx_dropped;
+			printk(KERN_INFO "%s: Length is too big for delayed transmit\n",
+				card->devname);
+			return;
+		}
+	}else{
+		if (len > X25_MAX_DATA) {
+			++chan->ifstats.tx_dropped;	
+			++card->wandev.stats.tx_dropped;
+			printk(KERN_INFO "%s: Length is too big for delayed transmit\n",
+				card->devname);
+			return;
+		}
+	}
+
+        chan->transmit_length = len;
+	atomic_set(&chan->common.driver_busy,1);
+        memcpy(chan->transmit_buffer, buf, len);
+
+	++chan->if_send_stat.if_send_tx_int_enabled;
+
+	/* Enable Transmit Interrupt */
+	++card->u.x.tx_interrupts_pending;
+        status->imask |= INTR_ON_TX_FRAME;
+}
+
+
+/*===============================================================
+ * net_device_stats
+ *
+ * 	Get ethernet-style interface statistics.
+ * 	Return a pointer to struct enet_statistics.
+ *
+ *==============================================================*/
+static struct net_device_stats *if_stats (netdevice_t* dev)
+{
+	x25_channel_t *chan = dev->priv;
+
+	if(chan == NULL)
 		return NULL;
+
 	return &chan->ifstats;
 }
 
-/****** Interrupt Handlers **************************************************/
 
-/*============================================================================
+/*
+ *	Interrupt Handlers 
+ */
+
+/*
  * X.25 Interrupt Service Routine.
  */
- 
+
 static void wpx_isr (sdla_t* card)
 {
 	TX25Status* status = card->flags;
-	struct net_device *dev;
-	unsigned long host_cpu_flags;
 
 	card->in_isr = 1;
-	card->buff_int_mode_unbusy = 0;
+	++card->statistics.isr_entry;
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
-	{
+	if (test_bit(PERI_CRIT,(void*)&card->wandev.critical)){
+		card->in_isr=0;
+		status->iflags = 0;
+		return;
+	}
+	
+	if (test_bit(SEND_CRIT, (void*)&card->wandev.critical)){
 
- 		printk(KERN_INFO "wpx_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, status->iflags);
+ 		printk(KERN_INFO "%s: wpx_isr: wandev.critical set to 0x%02lx, int type = 0x%02x\n", 
+			card->devname, card->wandev.critical, status->iflags);
 		card->in_isr = 0;
+		status->iflags = 0;
 		return;
 	}
 
@@ -863,92 +1696,62 @@
          * If the if_send routine is called with this flag set it will set
          * the enable transmit flag to 1. (for a delayed interrupt)
          */
-	card->wandev.critical = CRITICAL_IN_ISR;
+	switch (status->iflags){
 
-	switch (status->iflags)
-	{
-		case 0x01:		/* receive interrupt */
+		case RX_INTR_PENDING:		/* receive interrupt */
 			rx_intr(card);
 			break;
 
-		case 0x02:		/* transmit interrupt */
+		case TX_INTR_PENDING:		/* transmit interrupt */
 			tx_intr(card);
-			card->buff_int_mode_unbusy = 1;
-			status->imask &= ~0x2;
 			break;
 
-		case 0x04:		/* modem status interrupt */
+		case MODEM_INTR_PENDING:	/* modem status interrupt */
 			status_intr(card);
 			break;
 
-		case 0x10:		/* network event interrupt */
+		case X25_ASY_TRANS_INTR_PENDING:	/* network event interrupt */
 			event_intr(card);
 			break;
 
+		case TIMER_INTR_PENDING:
+			timer_intr(card);
+			break;
+
 		default:		/* unwanted interrupt */
 			spur_intr(card);
 	}
-	card->wandev.critical = CRITICAL_INTR_HANDLED;
-	if( card->wandev.enable_tx_int)
-	{
-		card->wandev.enable_tx_int = 0;
-		status->imask |= 0x2;
-	}
-	save_flags(host_cpu_flags);
-	cli();
+
 	card->in_isr = 0;
 	status->iflags = 0;	/* clear interrupt condition */
-	card->wandev.critical = 0;
-	restore_flags(host_cpu_flags);
-
-	if(card->buff_int_mode_unbusy)
-	{
-		x25_channel_t *chan;
-
-		dev = card->wandev.dev;
-		while (dev) {
-			chan = dev->priv;
-			if(chan->devtint) {
-				mark_bh(NET_BH);
-				return;
-			}	
-
-			dev = chan->slave;
-		}
-	}
 }
 
-/*============================================================================
- * Receive interrupt handler.
- * This routine handles fragmented IP packets using M-bit according to the
- * RFC1356.
- * o map ligical channel number to network interface.
- * o allocate socket buffer or append received packet to the existing one.
- * o if M-bit is reset (i.e. it's the last packet in a sequence) then 
- *   decapsulate packet and pass socket buffer to the protocol stack.
- *
- * Notes:
- * 1. When allocating a socket buffer, if M-bit is set then more data is
- *    coming and we have to allocate buffer for the maximum IP packet size
- *    expected on this channel.
- * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no
- *    socket buffers available) the whole packet sequence must be discarded.
+/*
+ * 	Receive interrupt handler.
+ * 	This routine handles fragmented IP packets using M-bit according to the
+ * 	RFC1356.
+ * 	o map ligical channel number to network interface.
+ * 	o allocate socket buffer or append received packet to the existing one.
+ * 	o if M-bit is reset (i.e. it's the last packet in a sequence) then 
+ *   	decapsulate packet and pass socket buffer to the protocol stack.
+ *
+ * 	Notes:
+ * 	1. When allocating a socket buffer, if M-bit is set then more data is
+ *    	coming and we have to allocate buffer for the maximum IP packet size
+ *    	expected on this channel.
+ * 	2. If something goes wrong and X.25 packet has to be dropped (e.g. no
+ *    	socket buffers available) the whole packet sequence must be discarded.
  */
 
 static void rx_intr (sdla_t* card)
 {
 	TX25Mbox* rxmb = card->rxmb;
-	unsigned lcn = rxmb->cmd.lcn;		/* logical channel number */
-	unsigned len = rxmb->cmd.length;	/* packet length */
-	unsigned qdm = rxmb->cmd.qdm;		/* Q,D and M bits */
-	wan_device_t* wandev = &card->wandev;
-	struct net_device* dev = get_dev_by_lcn(wandev, lcn);
+	unsigned lcn = rxmb->cmd.lcn;
+	netdevice_t* dev = find_channel(card,lcn);
 	x25_channel_t* chan;
-	struct sk_buff* skb;
-	void* bufptr;
+	struct sk_buff* skb=NULL;
 
-	if (dev == NULL)
-	{
+	if (dev == NULL){
 		/* Invalid channel, discard packet */
 		printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
 			card->devname, lcn);
@@ -957,279 +1760,741 @@
 
 	chan = dev->priv;
 	chan->i_timeout_sofar = jiffies;
-	if (chan->drop_sequence)
-	{
-		if (!(qdm & 0x01)) chan->drop_sequence = 0;
+
+
+	/* Copy the data from the board, into an
+         * skb buffer 
+	 */
+	if (wanpipe_pull_data_in_skb(card,dev,&skb)){
+		++chan->ifstats.rx_dropped;
+		++card->wandev.stats.rx_dropped;
+		++chan->rx_intr_stat.rx_intr_no_socket;
+		++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
 		return;
 	}
 
-	skb = chan->rx_skb;
-	if (skb == NULL)
-	{
-		/* Allocate new socket buffer */
-		int bufsize = (qdm & 0x01) ? dev->mtu : len;
+	dev->last_rx = jiffies;		/* timestamp */
 
-		skb = dev_alloc_skb(bufsize + dev->hard_header_len);
-		if (skb == NULL)
-		{
-			printk(KERN_INFO "%s: no socket buffers available!\n",
-				card->devname);
-			chan->drop_sequence = 1;	/* set flag */
+
+	/* ------------ API ----------------*/
+
+	if (chan->common.usedby == API){
+
+		if (bh_enqueue(dev, skb)){
 			++chan->ifstats.rx_dropped;
+			++card->wandev.stats.rx_dropped;
+			++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
+			wan_dev_kfree_skb(skb, FREE_READ);
 			return;
-		}
-		skb->dev = dev;
-		skb->protocol = htons(chan->protocol);
-		chan->rx_skb = skb;
-	}
+		}		
 
-	if (skb_tailroom(skb) < len)
-	{
-		/* No room for the packet. Call off the whole thing! */
-		dev_kfree_skb(skb);
-		chan->rx_skb = NULL;
-		if (qdm & 0x01) chan->drop_sequence = 1;
+		++chan->ifstats.rx_packets;
+	      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+		chan->ifstats.rx_bytes += skb->len;
+	      #endif
+		
 
-		printk(KERN_INFO "%s: unexpectedly long packet sequence "
-			"on interface %s!\n", card->devname, dev->name);
-		++chan->ifstats.rx_length_errors;
+		chan->rx_skb = NULL;
+		if (!test_and_set_bit(0, &chan->tq_working)){
+			wanpipe_queue_tq(&chan->common.wanpipe_task);
+			wanpipe_mark_bh();
+		}
 		return;
 	}
 
-	/* Append packet to the socket buffer */
-	bufptr = skb_put(skb, len);
-	memcpy(bufptr, rxmb->data, len);
-
-	if (qdm & 0x01)
-		return;		/* more data is coming */
-
-	dev->last_rx = jiffies;		/* timestamp */
-	chan->rx_skb = NULL;		/* dequeue packet */
 
+	/* ------------- WANPIPE -------------------*/
+	
+	/* set rx_skb to NULL so we won't access it later when kernel already owns it */
+	chan->rx_skb=NULL;
+	
 	/* Decapsulate packet, if necessary */
-	if (!skb->protocol && !wanrouter_type_trans(skb, dev))
-	{
+	if (!skb->protocol && !wanrouter_type_trans(skb, dev)){
 		/* can't decapsulate packet */
-		dev_kfree_skb(skb);
+                wan_dev_kfree_skb(skb, FREE_READ);
 		++chan->ifstats.rx_errors;
-	}
-	else
-	{
-		if( handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol))
-		{
-			if( card->wandev.enable_IPX )
-			{
-				if(chan_send(dev, skb))
-				{
+		++chan->ifstats.rx_dropped;
+		++card->wandev.stats.rx_dropped;
+		++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
+
+	}else{
+		if( handle_IPXWAN(skb->data, chan->name, 
+				  chan->enable_IPX, chan->network_number, 
+				  skb->protocol)){
+
+			if( chan->enable_IPX ){
+				if(chan_send(dev, skb->data, skb->len,0)){
 					chan->tx_skb = skb;
+				}else{
+                                        wan_dev_kfree_skb(skb, FREE_WRITE);
+					++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
 				}
-				else
-				{
-					dev_kfree_skb(skb);
-				}
-			}
-			else
-			{
-				/* FIXME: increment IPX packet dropped statistic */
+			}else{
+				/* increment IPX packet dropped statistic */
+				++chan->ifstats.rx_dropped;
+				++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
 			}
-		}
-		else
-		{
-			netif_rx(skb);
-			++chan->ifstats.rx_packets;
+		}else{
+			skb->mac.raw = skb->data;
+		      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 			chan->ifstats.rx_bytes += skb->len;
+		      #endif
+			++chan->ifstats.rx_packets;
+			++chan->rx_intr_stat.rx_intr_bfr_passed_to_stack;
+			netif_rx(skb);
 		}
 	}
+	
+	return;
 }
 
-/*============================================================================
- * Transmit interrupt handler.
- *	o Release socket buffer
- *	o Clear 'tbusy' flag
- */
 
-static void tx_intr (sdla_t* card)
+static int wanpipe_pull_data_in_skb (sdla_t *card, netdevice_t *dev, struct sk_buff **skb)
 {
-	struct net_device *dev;
-	x25_channel_t *chan;
+	void *bufptr;
+	TX25Mbox* rxmb = card->rxmb;
+	unsigned len = rxmb->cmd.length;	/* packet length */
+	unsigned qdm = rxmb->cmd.qdm;		/* Q,D and M bits */
+	x25_channel_t *chan = dev->priv;
+	struct sk_buff *new_skb = *skb;
+
+	if (chan->common.usedby == WANPIPE){
+		if (chan->drop_sequence){
+			if (!(qdm & 0x01)){ 
+				chan->drop_sequence = 0;
+			}
+			return 1;
+		}
+		new_skb = chan->rx_skb;
+	}else{
+		/* Add on the API header to the received
+                 * data 
+		 */
+		len += sizeof(x25api_hdr_t);
+	}
+
+	if (new_skb == NULL){
+		int bufsize;
 
-	/* unbusy all devices and then dev_tint(); */
-	dev = card->wandev.dev;
-	while (dev) {
-		chan->devtint = dev->tbusy; 
-		dev->tbusy = 0;
+		if (chan->common.usedby == WANPIPE){
+			bufsize = (qdm & 0x01) ? dev->mtu : len;
+		}else{
+			bufsize = len;
+		}
 
-		dev = chan->slave;
+		/* Allocate new socket buffer */
+		new_skb = dev_alloc_skb(bufsize + dev->hard_header_len);
+		if (new_skb == NULL){
+			printk(KERN_INFO "%s: no socket buffers available!\n",
+				card->devname);
+			chan->drop_sequence = 1;	/* set flag */
+			++chan->ifstats.rx_dropped;
+			return 1;
+		}
 	}
 
-}
+	if (skb_tailroom(new_skb) < len){
+		/* No room for the packet. Call off the whole thing! */
+                wan_dev_kfree_skb(new_skb, FREE_READ);
+		if (chan->common.usedby == WANPIPE){
+			chan->rx_skb = NULL;
+			if (qdm & 0x01){ 
+				chan->drop_sequence = 1;
+			}
+		}
 
-/*============================================================================
- * Modem status interrupt handler.
- */
-static void status_intr (sdla_t* card)
-{
-}
+		printk(KERN_INFO "%s: unexpectedly long packet sequence "
+			"on interface %s!\n", card->devname, dev->name);
+		++chan->ifstats.rx_length_errors;
+		return 1;
+	}
 
-/*============================================================================
- * Network event interrupt handler.
- */
-static void event_intr (sdla_t* card)
-{
-}
+	bufptr = skb_put(new_skb,len);
 
-/*============================================================================
- * Spurious interrupt handler.
- * o print a warning
- * o 
- * If number of spurious interrupts exceeded some limit, then ???
- */
-static void spur_intr (sdla_t* card)
-{
-	printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
-}
 
-/****** Background Polling Routines  ****************************************/
+	if (chan->common.usedby == API){
+		/* Fill in the x25api header 
+		 */
+		x25api_t * api_data = (x25api_t*)bufptr;
+		api_data->hdr.qdm = rxmb->cmd.qdm;
+		api_data->hdr.cause = rxmb->cmd.cause;
+		api_data->hdr.diagn = rxmb->cmd.diagn;
+		api_data->hdr.length = rxmb->cmd.length;
+		memcpy(api_data->data, rxmb->data, rxmb->cmd.length);
+	}else{
+		memcpy(bufptr, rxmb->data, len);
+	}
+
+	new_skb->dev = dev;
+
+	if (chan->common.usedby == API){
+		new_skb->mac.raw = new_skb->data;
+		new_skb->protocol = htons(X25_PROT);
+		new_skb->pkt_type = WAN_PACKET_DATA;
+	}else{
+		new_skb->protocol = chan->protocol;
+		chan->rx_skb = new_skb;
+	}
+
+	/* If qdm bit is set, more data is coming 
+         * thus, exit and wait for more data before
+         * sending the packet up. (Used by router only) 
+	 */
+	if ((qdm & 0x01) && (chan->common.usedby == WANPIPE)) 
+		return 1;	
+
+	*skb = new_skb; 
 
-/*============================================================================
- * Main polling routine.
- * This routine is repeatedly called by the WANPIPE 'thread' to allow for
- * time-dependent housekeeping work.
+	return 0;
+}
+
+/*===============================================================
+ * tx_intr
+ *  
+ * 	Transmit interrupt handler.
+ *	For each dev, check that there is something to send.
+ *	If data available, transmit. 	
  *
- * Notes:
- * 1. This routine may be called on interrupt context with all interrupts
- *    enabled. Beware!
- */
+ *===============================================================*/
 
-static void wpx_poll (sdla_t* card)
+static void tx_intr (sdla_t* card)
 {
-	unsigned long host_cpu_flags;
-
-	disable_irq(card->hw.irq);
-	++card->irq_dis_poll_count;
+	netdevice_t *dev;
+	TX25Status* status = card->flags;
+	unsigned char more_to_tx=0;
+	x25_channel_t *chan=NULL;
+	int i=0;	
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
-	{
- 		printk(KERN_INFO "%s: critical in polling!\n",card->devname);	
-		save_flags(host_cpu_flags);
-                cli();
-		if ((!card->irq_dis_if_send_count) &&
-                                (!(--card->irq_dis_poll_count)))
-                        enable_irq(card->hw.irq);
-                restore_flags(host_cpu_flags);
-		return;
+	if (card->u.x.tx_dev == NULL){
+		card->u.x.tx_dev = card->wandev.dev;
 	}
 
-	switch(card->wandev.state)
-	{
-		case WAN_CONNECTED:
-			poll_active(card);
-			break;
+	dev = card->u.x.tx_dev;
 
-		case WAN_CONNECTING:
-			poll_connecting(card);
-			break;
+	for (;;){
 
-		case WAN_DISCONNECTED:
-			poll_disconnected(card);
+		chan = dev->priv;
+		if (chan->transmit_length){
+			/* Device was set to transmit, check if the TX
+                         * buffers are available 
+			 */		
+			if (chan->common.state != WAN_CONNECTED){
+				chan->transmit_length = 0;
+				atomic_set(&chan->common.driver_busy,0);
+				chan->tx_offset=0;
+				if (is_queue_stopped(dev)){
+					if (chan->common.usedby == API){
+						start_net_queue(dev);
+						wakeup_sk_bh(dev);
+					}else{
+						wake_net_dev(dev);
+					}
+				}
+				dev = move_dev_to_next(card,dev);
+				break;
+			}				
+
+			if ((status->cflags[chan->ch_idx] & 0x40 || card->u.x.LAPB_hdlc) && 
+			     (*card->u.x.hdlc_buf_status & 0x40) ){
+				/* Tx buffer available, we can send */
+				
+				if (tx_intr_send(card, dev)){
+					more_to_tx=1;
+				}
+
+				/* If more than one interface present, move the
+                                 * device pointer to the next interface, so on the 
+                                 * next TX interrupt we will try sending from it. 
+                                 */
+				dev = move_dev_to_next(card,dev);
+				break;
+			}else{
+				/* Tx buffers not available, but device set
+                                 * the TX interrupt.  Set more_to_tx and try  
+                                 * to transmit for other devices.
+				 */
+				more_to_tx=1;
+				dev = move_dev_to_next(card,dev);
+			}
+
+		}else{
+			/* This device was not set to transmit,
+                         * go to next 
+			 */
+			dev = move_dev_to_next(card,dev);
+		}	
+
+		if (++i == card->u.x.no_dev){
+			if (!more_to_tx){
+				DBG_PRINTK(KERN_INFO "%s: Nothing to Send in TX INTR\n",
+					card->devname);
+			}
+			break;
+		}
+
+	} //End of FOR
+
+	card->u.x.tx_dev = dev;
+	
+	if (!more_to_tx){
+		/* if any other interfaces have transmit interrupts pending, */
+		/* do not disable the global transmit interrupt */
+		if (!(--card->u.x.tx_interrupts_pending)){
+			status->imask &= ~INTR_ON_TX_FRAME;
+		}
 	}
-	card->wandev.critical = 0;
-	save_flags(host_cpu_flags);
-        cli();
-        if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
-                enable_irq(card->hw.irq);
-        restore_flags(host_cpu_flags);
+	return;
 }
 
-/*============================================================================
- * Handle physical link establishment phase.
- * o if connection timed out, disconnect the link.
- */
-static void poll_connecting (sdla_t* card)
+/*===============================================================
+ * move_dev_to_next
+ *  
+ *
+ *===============================================================*/
+
+
+netdevice_t * move_dev_to_next (sdla_t *card, netdevice_t *dev)
+{
+	if (card->u.x.no_dev != 1){
+		if (*((netdevice_t**)dev->priv) == NULL){
+			return card->wandev.dev;
+		}else{
+			return *((netdevice_t**)dev->priv);
+		}
+	}
+	return dev;
+}
+
+/*===============================================================
+ *  tx_intr_send
+ *  
+ *
+ *===============================================================*/
+
+static int tx_intr_send(sdla_t *card, netdevice_t *dev)
+{
+	x25_channel_t* chan = dev->priv; 
+
+	if (chan_send (dev,chan->transmit_buffer,chan->transmit_length,1)){
+		 
+                /* Packet was split up due to its size, do not disable
+                 * tx_intr 
+                 */
+		return 1;
+	}
+
+	chan->transmit_length=0;
+	atomic_set(&chan->common.driver_busy,0);
+	chan->tx_offset=0;
+
+	/* If we are in API mode, wakeup the 
+         * sock BH handler, not the NET_BH */
+	if (is_queue_stopped(dev)){
+		if (chan->common.usedby == API){
+			start_net_queue(dev);
+			wakeup_sk_bh(dev);
+		}else{
+			wake_net_dev(dev);
+		}
+	}
+	return 0;
+}
+
+
+/*===============================================================
+ * timer_intr
+ *  
+ * 	Timer interrupt handler.
+ *	Check who called the timer interrupt and perform
+ *      action accordingly.
+ *
+ *===============================================================*/
+
+static void timer_intr (sdla_t *card)
 {
 	TX25Status* status = card->flags;
 
-	if (status->gflags & X25_HDLC_ABM)
-	{
+	if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC){
+
+		if (timer_intr_cmd_exec(card) == 0){
+			card->u.x.timer_int_enabled &=
+				~TMR_INT_ENABLED_CMD_EXEC;
+		}
+
+	}else  if(card->u.x.timer_int_enabled & TMR_INT_ENABLED_UDP_PKT) {
+
+		if ((*card->u.x.hdlc_buf_status & 0x40) && 
+		    card->u.x.udp_type == UDP_XPIPE_TYPE){
+
+                    	if(process_udp_mgmt_pkt(card)) {
+		                card->u.x.timer_int_enabled &= 
+					~TMR_INT_ENABLED_UDP_PKT;
+			}
+		}
+
+	}else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_ACTIVE) {
+
+		netdevice_t *dev = card->u.x.poll_device;
+		x25_channel_t *chan = NULL;
+
+		if (!dev){
+			card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_ACTIVE;
+			return;
+		}
+		chan = dev->priv;
+
+		printk(KERN_INFO 
+			"%s: Closing down Idle link %s on LCN %d\n",
+					card->devname,chan->name,chan->common.lcn); 
+		chan->i_timeout_sofar = jiffies;
+		chan_disc(dev);	
+         	card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_ACTIVE;
+		card->u.x.poll_device=NULL;
+
+	}else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_ON) {
+
 		wanpipe_set_state(card, WAN_CONNECTED);
-		x25_set_intr_mode(card, 0x83);	/* enable Rx interrupts */
-		status->imask &= ~0x2;		/* mask Tx interrupts */
+		if (card->u.x.LAPB_hdlc){
+			netdevice_t *dev = card->wandev.dev;
+			set_chan_state(dev,WAN_CONNECTED);
+			send_delayed_cmd_result(card,dev,card->mbox);	
+		}
+
+		/* 0x8F enable all interrupts */
+		x25_set_intr_mode(card, INTR_ON_RX_FRAME|	
+					INTR_ON_TX_FRAME|
+					INTR_ON_MODEM_STATUS_CHANGE|
+					//INTR_ON_COMMAND_COMPLETE|
+					X25_ASY_TRANS_INTR_PENDING |
+					INTR_ON_TIMER |
+					DIRECT_RX_INTR_USAGE
+				); 
+
+		status->imask &= ~INTR_ON_TX_FRAME;	/* mask Tx interrupts */
+		card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_CONNECT_ON;
+
+	}else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_OFF) {
+
+		//printk(KERN_INFO "Poll connect, Turning OFF\n");
+		disconnect(card);
+		card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_CONNECT_OFF;
+
+	}else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_DISCONNECT) {
+
+		//printk(KERN_INFO "POll disconnect, trying to connect\n");
+		connect(card);
+		card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_DISCONNECT;
+
+	}else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_UPDATE){
+
+		if (*card->u.x.hdlc_buf_status & 0x40){
+			x25_get_err_stats(card);
+			x25_get_stats(card);
+			card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
+		}
+	}
+
+	if(!card->u.x.timer_int_enabled){
+		//printk(KERN_INFO "Turning Timer Off \n");
+                status->imask &= ~INTR_ON_TIMER;	
 	}
-	else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT)
-	    disconnect(card);
 }
 
-/*============================================================================
- * Handle physical link disconnected phase.
- * o if hold-down timeout has expired and there are open interfaces, connect
- *   link.
+/*====================================================================
+ * 	Modem status interrupt handler.
+ *===================================================================*/
+static void status_intr (sdla_t* card)
+{
+
+	/* Added to avoid Modem status message flooding */
+	static TX25ModemStatus last_stat;
+
+	TX25Mbox* mbox = card->mbox;
+	TX25ModemStatus *modem_status;
+	netdevice_t *dev;
+	x25_channel_t *chan;
+	int err;
+
+	memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+	mbox->cmd.command = X25_READ_MODEM_STATUS;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	if (err){ 
+		x25_error(card, err, X25_READ_MODEM_STATUS, 0);
+	}else{
+	
+		modem_status = (TX25ModemStatus*)mbox->data;	
+	
+           	/* Check if the last status was the same
+           	 * if it was, do NOT print message again */
+	
+		if (last_stat.status != modem_status->status){
+
+	     		printk(KERN_INFO "%s: Modem Status Change: DCD=%s, CTS=%s\n",
+				card->devname,DCD(modem_status->status),CTS(modem_status->status));
+
+			last_stat.status = modem_status->status;
+		
+			if (card->u.x.oob_on_modem){
+
+				mbox->cmd.pktType = mbox->cmd.command;
+				mbox->cmd.result = 0x08;
+
+				/* Send a OOB to all connected sockets */
+				for (dev = card->wandev.dev; dev; dev = *((netdevice_t**)dev->priv)){
+					chan=dev->priv;
+					if (chan->common.usedby == API){
+						send_oob_msg(card,dev,mbox);				
+					}
+				}
+
+				/* The modem OOB message will probably kill the
+				 * the link. If we don't clear the flag here,
+				 * a deadlock could occur */ 
+				if (atomic_read(&card->u.x.command_busy)){
+					atomic_set(&card->u.x.command_busy,0);
+				}
+			}
+		}
+	}
+
+	memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+	mbox->cmd.command = X25_HDLC_LINK_STATUS;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	if (err){ 
+		x25_error(card, err, X25_HDLC_LINK_STATUS, 0);
+	}
+
+}
+
+/*====================================================================
+ * 	Network event interrupt handler.
+ *===================================================================*/
+static void event_intr (sdla_t* card)
+{
+	x25_fetch_events(card);
+}
+
+/*====================================================================
+ * 	Spurious interrupt handler.
+ * 	o print a warning
+ * 	o	 
+ *====================================================================*/
+
+static void spur_intr (sdla_t* card)
+{
+	printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
+}
+
+
+/*
+ *	Background Polling Routines  
  */
+
+/*====================================================================
+ * 	Main polling routine.
+ * 	This routine is repeatedly called by the WANPIPE 'thead' to allow for
+ * 	time-dependent housekeeping work.
+ *
+ * 	Notes:
+ * 	1. This routine may be called on interrupt context with all interrupts
+ *    	enabled. Beware!
+ *====================================================================*/
+
+static void wpx_poll (sdla_t *card)
+{
+	if (!card->wandev.dev){
+		goto wpx_poll_exit;
+	}
+
+	if (card->open_cnt != card->u.x.num_of_ch){
+		goto wpx_poll_exit;
+	}
+	
+	if (test_bit(PERI_CRIT,&card->wandev.critical)){
+		goto wpx_poll_exit;
+	}
+
+	if (test_bit(SEND_CRIT,&card->wandev.critical)){
+		goto wpx_poll_exit;
+	}
+
+	switch(card->wandev.state){
+		case WAN_CONNECTED:
+			poll_active(card);
+			break;
+
+		case WAN_CONNECTING:
+			poll_connecting(card);
+			break;
+
+		case WAN_DISCONNECTED:
+			poll_disconnected(card);
+			break;
+	}
+
+wpx_poll_exit:
+	clear_bit(POLL_CRIT,&card->wandev.critical);
+	return;
+}
+
+static void trigger_x25_poll(sdla_t *card)
+{
+      #ifdef LINUX_2_4
+	schedule_task(&card->u.x.x25_poll_task);
+      #else
+	queue_task(&card->u.x.x25_poll_task, &tq_scheduler);
+      #endif
+}
+
+/*====================================================================
+ * 	Handle physical link establishment phase.
+ * 	o if connection timed out, disconnect the link.
+ *===================================================================*/
+
+static void poll_connecting (sdla_t* card)
+{
+	volatile TX25Status* status = card->flags;
+
+	if (status->gflags & X25_HDLC_ABM){
+
+		timer_intr_exec (card, TMR_INT_ENABLED_POLL_CONNECT_ON);
+
+	}else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT){
+
+		timer_intr_exec (card, TMR_INT_ENABLED_POLL_CONNECT_OFF);
+
+	}
+}
+
+/*====================================================================
+ * 	Handle physical link disconnected phase.
+ * 	o if hold-down timeout has expired and there are open interfaces, 
+ *	connect link.
+ *===================================================================*/
+
 static void poll_disconnected (sdla_t* card)
 {
-	if (card->open_cnt && ((jiffies - card->state_tick) > HOLD_DOWN_TIME))
-		connect(card);
+	netdevice_t *dev; 
+	x25_channel_t *chan;
+	TX25Status* status = card->flags;
+
+	if (!card->u.x.LAPB_hdlc && card->open_cnt && 
+	    ((jiffies - card->state_tick) > HOLD_DOWN_TIME)){
+		timer_intr_exec(card, TMR_INT_ENABLED_POLL_DISCONNECT);
+	}
+
+
+	if ((dev=card->wandev.dev) == NULL)
+		return;
+
+	if ((chan=dev->priv) == NULL)
+		return;
+
+	if (chan->common.usedby == API && 
+	    atomic_read(&chan->common.command) && 
+	    card->u.x.LAPB_hdlc){
+
+		if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC)) 
+			card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;
+
+		if (!(status->imask & INTR_ON_TIMER))
+			status->imask |= INTR_ON_TIMER;
+	}	
+
 }
 
-/*============================================================================
- * Handle active link phase.
- * o fetch X.25 asynchronous events.
- * o kick off transmission on all interfaces.
- */
+/*====================================================================
+ * 	Handle active link phase.
+ * 	o fetch X.25 asynchronous events.
+ * 	o kick off transmission on all interfaces.
+ *===================================================================*/
+
 static void poll_active (sdla_t* card)
 {
-	struct net_device* dev;
-
-	/* Fetch X.25 asynchronous events */
-	x25_fetch_events(card);
+	netdevice_t* dev;
+	TX25Status* status = card->flags;
 
-	dev = card->wandev.dev;
-	while (dev) {
+	for (dev = card->wandev.dev; dev; dev = *((netdevice_t**)dev->priv)){
 		x25_channel_t* chan = dev->priv;
-		struct sk_buff* skb = chan->tx_skb;
-
-		/* If there is a packet queued for transmission then kick
-		 * the channel's send routine. When transmission is complete
-		 * or if error has occurred, release socket buffer and reset
-		 * 'tbusy' flag.
-		 */
-		if (skb && (chan_send(dev, skb) == 0))
-		{
-			chan->tx_skb = NULL;
-			dev->tbusy = 0;
-			dev_kfree_skb(skb);
-		}
 
 		/* If SVC has been idle long enough, close virtual circuit */
-
-		if(( chan->svc )&&( chan->state == WAN_CONNECTED ))
-		{
-			if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout )
-			{
+		if ( chan->common.svc && 
+		     chan->common.state == WAN_CONNECTED &&
+		     chan->common.usedby == WANPIPE ){
+		
+			if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout ){
 				/* Close svc */
-				printk(KERN_INFO "%s: Closing down Idle link %s on LCN %d\n",card->devname,chan->name,chan->lcn); 
-				chan->i_timeout_sofar = jiffies;
-				chan_disc(dev);
+				card->u.x.poll_device=dev;
+				timer_intr_exec	(card, TMR_INT_ENABLED_POLL_ACTIVE);
 			}
 		}
 
-		dev = chan->slave;
+#ifdef PRINT_DEBUG
+		chan->ifstats.tx_compressed = atomic_read(&chan->common.command);
+		chan->ifstats.tx_errors = chan->common.state;
+		chan->ifstats.rx_fifo_errors = atomic_read(&card->u.x.command_busy);
+		++chan->ifstats.tx_bytes;
+
+		chan->ifstats.rx_fifo_errors=atomic_read(&chan->common.disconnect);
+		chan->ifstats.multicast=atomic_read(&chan->bh_buff_used);
+		chan->ifstats.rx_length_errors=*card->u.x.hdlc_buf_status;
+#endif	
+
+		if (chan->common.usedby == API && 
+		    atomic_read(&chan->common.command) && 
+	            !card->u.x.LAPB_hdlc){
+
+			if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC)) 
+				card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;
+
+			if (!(status->imask & INTR_ON_TIMER))
+				status->imask |= INTR_ON_TIMER;
+		}	
+
+		if ((chan->common.usedby == API) && 
+		     atomic_read(&chan->common.disconnect)){
+
+			if (chan->common.state == WAN_DISCONNECTED){
+				atomic_set(&chan->common.disconnect,0);
+				return;
+			}
+
+			atomic_set(&chan->common.command,X25_CLEAR_CALL);
+			if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC)) 
+				card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;
+
+			if (!(status->imask & INTR_ON_TIMER))
+				status->imask |= INTR_ON_TIMER;
+		}
 	}
 }
 
-/****** SDLA Firmware-Specific Functions *************************************
- * Almost all X.25 commands can unexpetedly fail due to so called 'X.25
- * asynchronous events' such as restart, interrupt, incoming call request,
- * call clear request, etc.  They can't be ignored and have to be dealt with
- * immediately.  To tackle with this problem we execute each interface command
- * in a loop until good return code is received or maximum number of retries
- * is reached.  Each interface command returns non-zero return code, an
- * asynchronous event/error handler x25_error() is called.
- */
+static void timer_intr_exec(sdla_t *card, unsigned char TYPE)
+{
+	TX25Status* status = card->flags;
+	card->u.x.timer_int_enabled |= TYPE;
+	if (!(status->imask & INTR_ON_TIMER))
+		status->imask |= INTR_ON_TIMER;
+}
+
+
+/*==================================================================== 
+ * SDLA Firmware-Specific Functions 
+ *
+ *  Almost all X.25 commands can unexpetedly fail due to so called 'X.25
+ *  asynchronous events' such as restart, interrupt, incoming call request,
+ *  call clear request, etc.  They can't be ignored and have to be delt with
+ *  immediately.  To tackle with this problem we execute each interface 
+ *  command in a loop until good return code is received or maximum number 
+ *  of retries is reached.  Each interface command returns non-zero return 
+ *  code, an asynchronous event/error handler x25_error() is called.
+ *====================================================================*/
+
+/*====================================================================
+ * 	Read X.25 firmware version.
+ *		Put code version as ASCII string in str. 
+ *===================================================================*/
 
-/*============================================================================
- * Read X.25 firmware version.
- *	Put code version as ASCII string in str. 
- */
 static int x25_get_version (sdla_t* card, char* str)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1247,15 +2512,16 @@
 	if (!err && str)
 	{
 		int len = mbox->cmd.length;
+
 		memcpy(str, mbox->data, len);
 		str[len] = '\0';
 	}
 	return err;
 }
 
-/*============================================================================
- * Configure adapter.
- */
+/*====================================================================
+ * 	Configure adapter.
+ *===================================================================*/
 
 static int x25_configure (sdla_t* card, TX25Config* conf)
 {
@@ -1263,8 +2529,7 @@
   	int retry = MAX_CMD_RETRY;
 	int err;
 
-	do
-	{
+	do{
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		memcpy(mbox->data, (void*)conf, sizeof(TX25Config));
 		mbox->cmd.length  = sizeof(TX25Config);
@@ -1274,9 +2539,51 @@
 	return err;
 }
 
-/*============================================================================
+/*====================================================================
+ * 	Configure adapter for HDLC only.
+ *===================================================================*/
+
+static int hdlc_configure (sdla_t* card, TX25Config* conf)
+{
+	TX25Mbox* mbox = card->mbox;
+  	int retry = MAX_CMD_RETRY;
+	int err;
+
+	do{
+		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+		memcpy(mbox->data, (void*)conf, sizeof(TX25Config));
+		mbox->cmd.length  = sizeof(TX25Config);
+		mbox->cmd.command = X25_HDLC_SET_CONFIG;
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	} while (err && retry-- && x25_error(card, err, X25_SET_CONFIGURATION, 0));
+
+	return err;
+}
+
+static int set_hdlc_level (sdla_t* card)
+{
+
+	TX25Mbox* mbox = card->mbox;
+  	int retry = MAX_CMD_RETRY;
+	int err;
+
+	do{
+		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+		mbox->cmd.command = SET_PROTOCOL_LEVEL;
+		mbox->cmd.length = 1;
+		mbox->data[0] = HDLC_LEVEL; //| DO_HDLC_LEVEL_ERROR_CHECKING; 	
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	} while (err && retry-- && x25_error(card, err, SET_PROTOCOL_LEVEL, 0));
+
+	return err;
+}
+
+
+
+/*====================================================================
  * Get communications error statistics.
- */
+ *====================================================================*/
+
 static int x25_get_err_stats (sdla_t* card)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1289,7 +2596,7 @@
 		mbox->cmd.command = X25_HDLC_READ_COMM_ERR;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- && x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0));
-
+	
 	if (!err)
 	{
 		THdlcCommErr* stats = (void*)mbox->data;
@@ -1302,9 +2609,10 @@
 	return err;
 }
 
-/*============================================================================
- * Get protocol statistics.
- */
+/*====================================================================
+ * 	Get protocol statistics.
+ *===================================================================*/
+
 static int x25_get_stats (sdla_t* card)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1316,21 +2624,22 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_READ_STATISTICS;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- && x25_error(card, err, X25_READ_STATISTICS, 0));
+	} while (err && retry-- && x25_error(card, err, X25_READ_STATISTICS, 0)) ;
 	
 	if (!err)
 	{
 		TX25Stats* stats = (void*)mbox->data;
 
 		card->wandev.stats.rx_packets = stats->rxData;
-		card->wandev.stats.tx_packets = stats->rxData;
+		card->wandev.stats.tx_packets = stats->txData;
 	}
 	return err;
 }
 
-/*============================================================================
- * Close HDLC link.
- */
+/*====================================================================
+ * 	Close HDLC link.
+ *===================================================================*/
+
 static int x25_close_hdlc (sdla_t* card)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1343,13 +2652,15 @@
 		mbox->cmd.command = X25_HDLC_LINK_CLOSE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_CLOSE, 0));
-
+	
 	return err;
 }
 
-/*============================================================================
- * Open HDLC link.
- */
+
+/*====================================================================
+ * 	Open HDLC link.
+ *===================================================================*/
+
 static int x25_open_hdlc (sdla_t* card)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1362,13 +2673,13 @@
 		mbox->cmd.command = X25_HDLC_LINK_OPEN;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_OPEN, 0));
-	
+
 	return err;
 }
 
-/*============================================================================
+/*=====================================================================
  * Setup HDLC link.
- */
+ *====================================================================*/
 static int x25_setup_hdlc (sdla_t* card)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1385,9 +2696,10 @@
 	return err;
 }
 
-/*============================================================================
+/*====================================================================
  * Set (raise/drop) DTR.
- */
+ *===================================================================*/
+
 static int x25_set_dtr (sdla_t* card, int dtr)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1404,13 +2716,14 @@
 		mbox->cmd.command = X25_SET_GLOBAL_VARS;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- && x25_error(card, err, X25_SET_GLOBAL_VARS, 0));
-
+	
 	return err;
 }
 
-/*============================================================================
- * Set interrupt mode.
- */
+/*====================================================================
+ * 	Set interrupt mode.
+ *===================================================================*/
+
 static int x25_set_intr_mode (sdla_t* card, int mode)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1421,30 +2734,32 @@
 	{
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->data[0] = mode;
-		if (card->hw.fwid == SFID_X25_508)
-		{
+		if (card->hw.fwid == SFID_X25_508){
 			mbox->data[1] = card->hw.irq;
-			mbox->cmd.length = 2;
+			mbox->data[2] = 2;
+			mbox->cmd.length = 3;
+		}else {
+		 	mbox->cmd.length  = 1;
 		}
-		else mbox->cmd.length  = 1;
 		mbox->cmd.command = X25_SET_INTERRUPT_MODE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- && x25_error(card, err, X25_SET_INTERRUPT_MODE, 0)) ;
+	} while (err && retry-- && x25_error(card, err, X25_SET_INTERRUPT_MODE, 0));
+	
 	return err;
 }
 
-/*============================================================================
- * Read X.25 channel configuration.
- */
+/*====================================================================
+ * 	Read X.25 channel configuration.
+ *===================================================================*/
+
 static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan)
 {
 	TX25Mbox* mbox = card->mbox;
   	int retry = MAX_CMD_RETRY;
-	int lcn = chan->lcn;
+	int lcn = chan->common.lcn;
 	int err;
 
-	do
-	{
+	do{
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.lcn     = lcn;
 		mbox->cmd.command = X25_READ_CHANNEL_CONFIG;
@@ -1456,19 +2771,25 @@
 		TX25Status* status = card->flags;
 
 		/* calculate an offset into the array of status bytes */
-		if (card->u.x.hi_svc <= 255) 
+		if (card->u.x.hi_svc <= X25_MAX_CHAN){ 
+
 			chan->ch_idx = lcn - 1;
-		else
-		{
+
+		}else{
 			int offset;
 
-			switch (mbox->data[0] && 0x1F)
+			/* FIX: Apr 14 2000 : Nenad Corbic
+			 * The data field was being compared to 0x1F using
+                         * '&&' instead of '&'. 
+			 * This caused X25API to fail for LCNs greater than 255.
+			 */
+			switch (mbox->data[0] & 0x1F)
 			{
-				case 0x01:
+				case 0x01: 
 					offset = status->pvc_map; break;
-				case 0x03:
+				case 0x03: 
 					offset = status->icc_map; break;
-				case 0x07:
+				case 0x07: 
 					offset = status->twc_map; break;
 				case 0x0B: 
 					offset = status->ogc_map; break;
@@ -1481,37 +2802,38 @@
 		/* get actual transmit packet size on this channel */
 		switch(mbox->data[1] & 0x38)
 		{
-			case 0x00:
-				chan->tx_pkt_size = 16;
+			case 0x00: 
+				chan->tx_pkt_size = 16; 
 				break;
-			case 0x08:
-				chan->tx_pkt_size = 32;
+			case 0x08: 
+				chan->tx_pkt_size = 32; 
 				break;
-			case 0x10:
-				chan->tx_pkt_size = 64;
+			case 0x10: 
+				chan->tx_pkt_size = 64; 
 				break;
-			case 0x18:
-				chan->tx_pkt_size = 128;
+			case 0x18: 
+				chan->tx_pkt_size = 128; 
 				break;
-			case 0x20:
-				chan->tx_pkt_size = 256;
+			case 0x20: 
+				chan->tx_pkt_size = 256; 
 				break;
-			case 0x28:
-				chan->tx_pkt_size = 512;
+			case 0x28: 
+				chan->tx_pkt_size = 512; 
 				break;
-			case 0x30:
-				chan->tx_pkt_size = 1024;
+			case 0x30: 
+				chan->tx_pkt_size = 1024; 
 				break;
 		}
-		printk(KERN_INFO "%s: X.25 packet size on LCN %d is %d.\n",
-			card->devname, lcn, chan->tx_pkt_size);
+		if (card->u.x.logging)
+			printk(KERN_INFO "%s: X.25 packet size on LCN %d is %d.\n",
+				card->devname, lcn, chan->tx_pkt_size);
 	}
 	return err;
 }
 
-/*============================================================================
- * Place X.25 call.
- */
+/*====================================================================
+ * 	Place X.25 call.
+ *====================================================================*/
 
 static int x25_place_call (sdla_t* card, x25_channel_t* chan)
 {
@@ -1520,7 +2842,15 @@
 	int err;
 	char str[64];
 
-	sprintf(str, "-d%s -uCC", chan->addr);
+
+	if (chan->protocol == htons(ETH_P_IP)){
+		sprintf(str, "-d%s -uCC", chan->addr);
+	
+	}else if (chan->protocol == htons(ETH_P_IPX)){
+		sprintf(str, "-d%s -u800000008137", chan->addr);
+	
+	}
+	
 	do
 	{
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
@@ -1530,17 +2860,15 @@
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- && x25_error(card, err, X25_PLACE_CALL, 0));
 
-	if (!err)
-	{
-		chan->lcn = mbox->cmd.lcn;
-		chan->protocol = ETH_P_IP;
+	if (!err){
+		bind_lcn_to_dev (card, chan->dev, mbox->cmd.lcn);
 	}
 	return err;
 }
 
-/*============================================================================
- * Accept X.25 call.
- */
+/*====================================================================
+ * 	Accept X.25 call.
+ *====================================================================*/
 
 static int x25_accept_call (sdla_t* card, int lcn, int qdm)
 {
@@ -1556,13 +2884,14 @@
 		mbox->cmd.command = X25_ACCEPT_CALL;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- && x25_error(card, err, X25_ACCEPT_CALL, lcn));
-
+	
 	return err;
 }
 
-/*============================================================================
- * Clear X.25 call.
- */
+/*====================================================================
+ * 	Clear X.25 call.
+ *====================================================================*/
+
 static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1578,35 +2907,59 @@
 		mbox->cmd.command = X25_CLEAR_CALL;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- && x25_error(card, err, X25_CLEAR_CALL, lcn));
-
+	
 	return err;
 }
 
-/*============================================================================
- * Send X.25 data packet.
- */
+/*====================================================================
+ * 	Send X.25 data packet.
+ *====================================================================*/
+
 static int x25_send (sdla_t* card, int lcn, int qdm, int len, void* buf)
 {
 	TX25Mbox* mbox = card->mbox;
   	int retry = MAX_CMD_RETRY;
 	int err;
-	
+	unsigned char cmd;
+		
+	if (card->u.x.LAPB_hdlc)
+		cmd = X25_HDLC_WRITE;
+	else
+		cmd = X25_WRITE;
+
 	do
 	{
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		memcpy(mbox->data, buf, len);
 		mbox->cmd.length  = len;
 		mbox->cmd.lcn     = lcn;
-		mbox->cmd.qdm     = qdm;
-		mbox->cmd.command = X25_WRITE;
+
+		if (card->u.x.LAPB_hdlc){
+			mbox->cmd.pf = qdm;
+		}else{			
+			mbox->cmd.qdm = qdm;
+		}
+
+		mbox->cmd.command = cmd;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- && x25_error(card, err, X25_WRITE, lcn));
+	} while (err && retry-- && x25_error(card, err, cmd , lcn));
+
+
+	/* If buffers are busy the return code for LAPB HDLC is
+         * 1. The above functions are looking for return code
+         * of X25RES_NOT_READY if busy. */
+
+	if (card->u.x.LAPB_hdlc && err == 1){
+		err = X25RES_NOT_READY;
+	}
+
 	return err;
 }
 
-/*============================================================================
- * Fetch X.25 asynchronous events.
- */
+/*====================================================================
+ * 	Fetch X.25 asynchronous events.
+ *===================================================================*/
+
 static int x25_fetch_events (sdla_t* card)
 {
 	TX25Status* status = card->flags;
@@ -1618,25 +2971,26 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_IS_DATA_AVAILABLE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
- 		if (err)
- 			x25_error(card, err, X25_IS_DATA_AVAILABLE, 0);
+ 		if (err) x25_error(card, err, X25_IS_DATA_AVAILABLE, 0);
 	}
 	return err;
 }
 
-/*============================================================================
- * X.25 asynchronous event/error handler.
- *	This routine is called each time interface command returns non-zero
- *	return code to handle X.25 asynchronous events and common errors.
- *	Return non-zero to repeat command or zero to cancel it.
- *
- * Notes:
- * 1. This function may be called recursively, as handling some of the
- *    asynchronous events (e.g. call request) requires execution of the
- *    interface command(s) that, in turn, may also return asynchronous
- *    events.  To avoid re-entrancy problems we copy mailbox to dynamically
- *    allocated memory before processing events.
- */
+/*====================================================================
+ * 	X.25 asynchronous event/error handler.
+ *		This routine is called each time interface command returns 
+ *		non-zero return code to handle X.25 asynchronous events and 
+ *		common errors. Return non-zero to repeat command or zero to 
+ *		cancel it.
+ *
+ * 	Notes:
+ * 	1. This function may be called recursively, as handling some of the
+ *    	asynchronous events (e.g. call request) requires execution of the
+ *    	interface command(s) that, in turn, may also return asynchronous
+ *    	events.  To avoid re-entrancy problems we copy mailbox to dynamically
+ *    	allocated memory before processing events.
+ *====================================================================*/
+
 static int x25_error (sdla_t* card, int err, int cmd, int lcn)
 {
 	int retry = 1;
@@ -1651,138 +3005,212 @@
 		return 0;
 	}
 	memcpy(mb, card->mbox, sizeof(TX25Mbox) + dlen);
-	switch (err)
-	{
-		case 0x40:	/* X.25 asynchronous packet was received */
-			mb->data[dlen] = '\0';
-			switch (mb->cmd.pktType & 0x7F)
-			{
-				case 0x30:		/* incoming call */
-					retry = incoming_call(card, cmd, lcn, mb);
-					break;
+	switch (err){
 
-				case 0x31:		/* connected */
-					retry = call_accepted(card, cmd, lcn, mb);
-					break;
-
-				case 0x02:		/* call clear request */
-					retry = call_cleared(card, cmd, lcn, mb);
-					break;
+	case X25RES_ASYNC_PACKET:	/* X.25 asynchronous packet was received */
 
-				case 0x04:		/* reset request */
-					printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
-						"Cause:0x%02X Diagn:0x%02X\n",
-						card->devname, mb->cmd.lcn, mb->cmd.cause,
-						mb->cmd.diagn);
-					break;
+		mb->data[dlen] = '\0';
 
-				case 0x08:		/* restart request */
-					retry = restart_event(card, cmd, lcn, mb);
-					break;
+		switch (mb->cmd.pktType & 0x7F){
 
-				default:
-					printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! "
-						"Cause:0x%02X Diagn:0x%02X\n",
-						card->devname, mb->cmd.pktType,
-						mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn);
-			}
+		case ASE_CALL_RQST:		/* incoming call */
+			retry = incoming_call(card, cmd, lcn, mb);
 			break;
 
-		case 0x41:	/* X.25 protocol violation indication */
-			printk(KERN_INFO
-				"%s: X.25 protocol violation on LCN %d! "
-				"Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n",
-				card->devname, mb->cmd.lcn,
-				mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn);
+		case ASE_CALL_ACCEPTED:		/* connected */
+			retry = call_accepted(card, cmd, lcn, mb);
 			break;
 
-		case 0x42:	/* X.25 timeout */
-			retry = timeout_event(card, cmd, lcn, mb);
+		case ASE_CLEAR_RQST:		/* call clear request */
+			retry = call_cleared(card, cmd, lcn, mb);
 			break;
 
-		case 0x43:	/* X.25 retry limit exceeded */
-			printk(KERN_INFO
-				"%s: exceeded X.25 retry limit on LCN %d! "
-				"Packet:0x%02X Diagn:0x%02X\n", card->devname,
-				mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn);
+		case ASE_RESET_RQST:		/* reset request */
+			printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
+				"Cause:0x%02X Diagn:0x%02X\n",
+				card->devname, mb->cmd.lcn, mb->cmd.cause,
+				mb->cmd.diagn);
+			api_oob_event (card,mb);
 			break;
 
-		case 0x08:	/* modem failure */
-			printk(KERN_INFO "%s: modem failure!\n", card->devname);
+		case ASE_RESTART_RQST:		/* restart request */
+			retry = restart_event(card, cmd, lcn, mb);
 			break;
 
-		case 0x09:	/* N2 retry limit */
-			printk(KERN_INFO "%s: exceeded HDLC retry limit!\n",
-				card->devname);
+		case ASE_CLEAR_CONFRM:
+			if (clear_confirm_event (card,mb))
+				break;
+
+			/* I use the goto statement here so if 
+	                 * somebody inserts code between the
+        	         * case and default, we will not have
+                	 * ghost problems */
+
+			goto dflt_1;
+
+		default:
+dflt_1:
+			printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! "
+				"Cause:0x%02X Diagn:0x%02X\n",
+				card->devname, mb->cmd.pktType,
+				mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn);
+		}
+		break;
+
+	case X25RES_PROTO_VIOLATION:	/* X.25 protocol violation indication */
+
+		/* Bug Fix: Mar 14 2000
+                 * The Protocol violation error conditions were  
+                 * not handeled previously */
+
+		switch (mb->cmd.pktType & 0x7F){
+
+		case PVE_CLEAR_RQST:	/* Clear request */		
+			retry = call_cleared(card, cmd, lcn, mb);
+			break;	
+
+		case PVE_RESET_RQST:	/* Reset request */
+			printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
+				"Cause:0x%02X Diagn:0x%02X\n",
+				card->devname, mb->cmd.lcn, mb->cmd.cause,
+				mb->cmd.diagn);
+			api_oob_event (card,mb);
 			break;
 
-		case 0x06:	/* unnumbered frame was received while in ABM */
-			printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n",
-				card->devname, mb->data[0]);
+		case PVE_RESTART_RQST:	/* Restart request */
+			retry = restart_event(card, cmd, lcn, mb);
 			break;
 
-		case CMD_TIMEOUT:
-			printk(KERN_ERR "%s: command 0x%02X timed out!\n",
-				card->devname, cmd);
-			retry = 0;	/* abort command */
+		default :
+			printk(KERN_INFO
+				"%s: X.25 protocol violation on LCN %d! "
+				"Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n",
+				card->devname, mb->cmd.lcn,
+				mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn);
+			api_oob_event(card,mb);
+		}
+		break;
+
+	case 0x42:	/* X.25 timeout */
+		retry = timeout_event(card, cmd, lcn, mb);
+		break;
+
+	case 0x43:	/* X.25 retry limit exceeded */
+		printk(KERN_INFO
+			"%s: exceeded X.25 retry limit on LCN %d! "
+			"Packet:0x%02X Diagn:0x%02X\n", card->devname,
+			mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn)
+		;
+		break;
+
+	case 0x08:	/* modem failure */
+	#ifndef MODEM_NOT_LOG
+		printk(KERN_INFO "%s: modem failure!\n", card->devname);
+	#endif MODEM_NOT_LOG
+		api_oob_event(card,mb);
+		break;
+
+	case 0x09:	/* N2 retry limit */
+		printk(KERN_INFO "%s: exceeded HDLC retry limit!\n",
+			card->devname);
+		api_oob_event(card,mb);
+		break;
+
+	case 0x06:	/* unnumbered frame was received while in ABM */
+		printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n",
+			card->devname, mb->data[0]);
+		api_oob_event(card,mb);
+		break;
+
+	case CMD_TIMEOUT:
+		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+			card->devname, cmd)
+		;
+		retry = 0;	/* abort command */
+		break;
+
+	case X25RES_NOT_READY:
+		retry = 1;
+		break;
+
+	case 0x01:
+		if (card->u.x.LAPB_hdlc)
 			break;
 
-		default:
-			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
-				card->devname, cmd, err);
-			retry = 0;	/* abort command */
+		if (mb->cmd.command == 0x16)
+			break;
+		/* I use the goto statement here so if 
+                 * somebody inserts code between the
+                 * case and default, we will not have
+                 * ghost problems */
+		goto dflt_2;
+
+	default:
+dflt_2:
+		printk(KERN_INFO "%s: command 0x%02X returned 0x%02X! Lcn %i\n",
+			card->devname, cmd, err, mb->cmd.lcn)
+		;
+		retry = 0;	/* abort command */
 	}
 	kfree(mb);
 	return retry;
 }
 
-/****** X.25 Asynchronous Event Handlers *************************************
- * These functions are called by the x25_error() and should return 0, if
- * the command resulting in the asynchronous event must be aborted.
- */
+/*==================================================================== 
+ *	X.25 Asynchronous Event Handlers
+ * 	These functions are called by the x25_error() and should return 0, if
+ * 	the command resulting in the asynchronous event must be aborted.
+ *====================================================================*/
 
-/*============================================================================
- * Handle X.25 incoming call request.
+
+
+/*====================================================================
+ *Handle X.25 incoming call request.
  *	RFC 1356 establishes the following rules:
  *	1. The first octet in the Call User Data (CUD) field of the call
- *	   request packet contains NLPID identifying protocol encapsulation.
- *	2. Calls MUST NOT be accepted unless router supports requested
- *	   protocol encapsulation.
- *	3. A diagnostic code 249 defined by ISO/IEC 8208 may be used when
- *	   clearing a call because protocol encapsulation is not supported.
- *	4. If an incoming call is received while a call request is pending
- *	   (i.e. call collision has occurred), the incoming call shall be
- *	   rejected and call request shall be retried.
- */
+ *     	   request packet contains NLPID identifying protocol encapsulation
+ * 	2. Calls MUST NOT be accepted unless router supports requested
+ *   	   protocol encapsulation.
+ *	3. A diagnostic code 249 defined by ISO/IEC 8208 may be used 
+ *	   when clearing a call because protocol encapsulation is not 
+ *	   supported.
+ *	4. If an incoming call is received while a call request is 
+ *	   pending (i.e. call collision has occured), the incoming call 
+ *	   shall be rejected and call request shall be retried.
+ *====================================================================*/
 
 static int incoming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	wan_device_t* wandev = &card->wandev;
 	int new_lcn = mb->cmd.lcn;
-	struct net_device* dev = get_dev_by_lcn(wandev, new_lcn);
+	netdevice_t* dev = get_dev_by_lcn(wandev, new_lcn);
 	x25_channel_t* chan = NULL;
 	int accept = 0;		/* set to '1' if o.k. to accept call */
+	unsigned int user_data;
 	x25_call_info_t* info;
-
+	
 	/* Make sure there is no call collision */
 	if (dev != NULL)
 	{
 		printk(KERN_INFO
 			"%s: X.25 incoming call collision on LCN %d!\n",
 			card->devname, new_lcn);
+
 		x25_clear_call(card, new_lcn, 0, 0);
 		return 1;
 	}
 
 	/* Make sure D bit is not set in call request */
-	if (mb->cmd.qdm & 0x02)
-	{
-		printk(KERN_INFO
-			"%s: X.25 incoming call on LCN %d with D-bit set!\n",
-			card->devname, new_lcn);
-		x25_clear_call(card, new_lcn, 0, 0);
-		return 1;
-	}
+//FIXME: THIS IS NOT TURE !!!! TAKE IT OUT
+//	if (mb->cmd.qdm & 0x02)
+//	{
+//		printk(KERN_INFO
+//			"%s: X.25 incoming call on LCN %d with D-bit set!\n",
+//			card->devname, new_lcn);
+//
+//		x25_clear_call(card, new_lcn, 0, 0);
+//		return 1;
+//	}
 
 	/* Parse call request data */
 	info = kmalloc(sizeof(x25_call_info_t), GFP_ATOMIC);
@@ -1794,90 +3222,120 @@
 		x25_clear_call(card, new_lcn, 0, 0);
 		return 1;
 	}
+ 
 	parse_call_info(mb->data, info);
-	printk(KERN_INFO "%s: X.25 incoming call on LCN %d! Call data: %s\n",
-		card->devname, new_lcn, mb->data);
+
+	if (card->u.x.logging)
+		printk(KERN_INFO "\n%s: X.25 incoming call on LCN %d!\n",
+			card->devname, new_lcn);
+
+	/* Conver the first two ASCII characters into an
+         * interger. Used to check the incoming protocol 
+         */
+	user_data = hex_to_uint(info->user,2);
 
 	/* Find available channel */
-	dev = wandev->dev;
-	while (dev) {
+	for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv)){
 		chan = dev->priv;
 
-		if (!chan->svc || (chan->state != WAN_DISCONNECTED))
+		if (chan->common.usedby == API)
 			continue;
+
+		if (!chan->common.svc || (chan->common.state != WAN_DISCONNECTED))
+			continue;
+
+		if (user_data == NLPID_IP && chan->protocol != htons(ETH_P_IP)){
+			printk(KERN_INFO "IP packet but configured for IPX : %x, %x\n",
+				       htons(chan->protocol), info->user[0]);
+			continue;
+		}
+	
+		if (user_data == NLPID_SNAP && chan->protocol != htons(ETH_P_IPX)){
+			printk(KERN_INFO "IPX packet but configured for IP: %x\n",
+				       htons(chan->protocol));
+			continue;
+		}
 		if (strcmp(info->src, chan->addr) == 0)
 			break;
+
 	        /* If just an '@' is specified, accept all incoming calls */
 	        if (strcmp(chan->addr, "") == 0)
 	                break;
-
-		dev = chan->slave;
 	}
 
-	if (dev == NULL)
-	{
-		printk(KERN_INFO "%s: no channels available!\n",
-			card->devname);
-		x25_clear_call(card, new_lcn, 0, 0);
-	}
+	if (dev == NULL){
+
+		/* If the call is not for any WANPIPE interfaces
+                 * check to see if there is an API listening queue
+                 * waiting for data. If there is send the packet
+                 * up the stack.
+                 */
+		if (card->sk != NULL && card->func != NULL){
+			if (api_incoming_call(card,mb,new_lcn)){
+				x25_clear_call(card, new_lcn, 0, 0);
+			}
+			accept = 0;
+		}else{
+			printk(KERN_INFO "%s: no channels available!\n",
+				card->devname);
+			
+			x25_clear_call(card, new_lcn, 0, 0);
+		}
+
+	}else if (info->nuser == 0){
 
-	/* Check requested protocol encapsulation */
-	else if (info->nuser == 0)
-	{
 		printk(KERN_INFO
 			"%s: no user data in incoming call on LCN %d!\n",
-			card->devname, new_lcn);
+			card->devname, new_lcn)
+		;
 		x25_clear_call(card, new_lcn, 0, 0);
-	}
-	else switch (info->user[0])
-	{
+
+	}else switch (info->user[0]){
+
 		case 0:		/* multiplexed */
-			chan->protocol = 0;
+			chan->protocol = htons(0);
 			accept = 1;
 			break;
 
 		case NLPID_IP:	/* IP datagrams */
-			chan->protocol = ETH_P_IP;
 			accept = 1;
 			break;
 
 		case NLPID_SNAP: /* IPX datagrams */
-			chan->protocol = ETH_P_IPX;
 			accept = 1;
 			break;
+
 		default:
 			printk(KERN_INFO
 				"%s: unsupported NLPID 0x%02X in incoming call "
 				"on LCN %d!\n", card->devname, info->user[0], new_lcn);
 			x25_clear_call(card, new_lcn, 0, 249);
 	}
+	
+	if (accept && (x25_accept_call(card, new_lcn, 0) == CMD_OK)){
 
-	if (accept && (x25_accept_call(card, new_lcn, 0) == CMD_OK))
-	{
-		chan->lcn = new_lcn;
+		bind_lcn_to_dev (card, chan->dev, new_lcn);
+		
 		if (x25_get_chan_conf(card, chan) == CMD_OK)
 			set_chan_state(dev, WAN_CONNECTED);
-		else
+		else 
 			x25_clear_call(card, new_lcn, 0, 0);
 	}
 	kfree(info);
 	return 1;
 }
 
-/*============================================================================
- * Handle accepted call.
- */
+/*====================================================================
+ * 	Handle accepted call.
+ *====================================================================*/
 
 static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	unsigned new_lcn = mb->cmd.lcn;
-	struct net_device* dev = get_dev_by_lcn(&card->wandev, new_lcn);
+	netdevice_t* dev = find_channel(card, new_lcn);
 	x25_channel_t* chan;
 
-	printk(KERN_INFO "%s: X.25 call accepted on LCN %d!\n",
-		card->devname, new_lcn);
-	if (dev == NULL)
-	{
+	if (dev == NULL){
 		printk(KERN_INFO
 			"%s: clearing orphaned connection on LCN %d!\n",
 			card->devname, new_lcn);
@@ -1885,6 +3343,10 @@
 		return 1;
 	}
 
+	if (card->u.x.logging)	
+		printk(KERN_INFO "%s: X.25 call accepted on Dev %s and LCN %d!\n",
+			card->devname, dev->name, new_lcn);
+
 	/* Get channel configuration and notify router */
 	chan = dev->priv;
 	if (x25_get_chan_conf(card, chan) != CMD_OK)
@@ -1892,178 +3354,256 @@
 		x25_clear_call(card, new_lcn, 0, 0);
 		return 1;
 	}
+
 	set_chan_state(dev, WAN_CONNECTED);
+
+	if (chan->common.usedby == API){
+		send_delayed_cmd_result(card,dev,mb);
+		bind_lcn_to_dev (card, dev, new_lcn);
+	}
+
 	return 1;
 }
 
-/*============================================================================
- * Handle cleared call.
- */
+/*====================================================================
+ * 	Handle cleared call.
+ *====================================================================*/
 
 static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	unsigned new_lcn = mb->cmd.lcn;
-	struct net_device* dev = get_dev_by_lcn(&card->wandev, new_lcn);
+	netdevice_t* dev = find_channel(card, new_lcn);
+	x25_channel_t *chan;
+	unsigned char old_state;
 
-	printk(KERN_INFO "%s: X.25 clear request on LCN %d! Cause:0x%02X "
+	if (card->u.x.logging){
+		printk(KERN_INFO "%s: X.25 clear request on LCN %d! Cause:0x%02X "
 		"Diagn:0x%02X\n",
 		card->devname, new_lcn, mb->cmd.cause, mb->cmd.diagn);
-	if (dev == NULL)
+	}
+
+	if (dev == NULL){ 
+		printk(KERN_INFO "%s: X.25 clear request : No device for clear\n",
+				card->devname);
 		return 1;
+	}
+
+	chan=dev->priv;
+
+	old_state = chan->common.state;
+
 	set_chan_state(dev, WAN_DISCONNECTED);
+
+	if (chan->common.usedby == API){
+
+		switch (old_state){
+		
+		case WAN_CONNECTING:
+			send_delayed_cmd_result(card,dev,mb);
+			break;
+		case WAN_CONNECTED:
+			send_oob_msg(card,dev,mb);				
+			break;
+		}
+	}
+	
 	return ((cmd == X25_WRITE) && (lcn == new_lcn)) ? 0 : 1;
 }
 
-/*============================================================================
- * Handle X.25 restart event.
- */
- 
+/*====================================================================
+ * 	Handle X.25 restart event.
+ *====================================================================*/
+
 static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	wan_device_t* wandev = &card->wandev;
-	struct net_device* dev;
+	netdevice_t* dev;
+	x25_channel_t *chan;
+	unsigned char old_state;
 
 	printk(KERN_INFO
 		"%s: X.25 restart request! Cause:0x%02X Diagn:0x%02X\n",
 		card->devname, mb->cmd.cause, mb->cmd.diagn);
 
 	/* down all logical channels */
-	dev = wandev->dev;
-	while (dev) {
-		x25_channel_t *chan = dev->priv;
+	for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv)){
+		chan=dev->priv;
+		old_state = chan->common.state;
 
 		set_chan_state(dev, WAN_DISCONNECTED);
-		dev = chan->slave;
-	}
 
+		if (chan->common.usedby == API){
+			switch (old_state){
+		
+			case WAN_CONNECTING:
+				send_delayed_cmd_result(card,dev,mb);
+				break;
+			case WAN_CONNECTED:
+				send_oob_msg(card,dev,mb);				
+				break;
+			}
+		}
+	}
 	return (cmd == X25_WRITE) ? 0 : 1;
 }
 
-/*============================================================================
+/*====================================================================
  * Handle timeout event.
- */
+ *====================================================================*/
+
 static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	unsigned new_lcn = mb->cmd.lcn;
 
 	if (mb->cmd.pktType == 0x05)	/* call request time out */
 	{
-		struct net_device* dev = get_dev_by_lcn(&card->wandev, new_lcn);
+		netdevice_t* dev = find_channel(card,new_lcn);
 
 		printk(KERN_INFO "%s: X.25 call timed timeout on LCN %d!\n",
 			card->devname, new_lcn);
-		if (dev)
+
+		if (dev){
+			x25_channel_t *chan = dev->priv;
 			set_chan_state(dev, WAN_DISCONNECTED);
-	}
-	else printk(KERN_INFO "%s: X.25 packet 0x%02X timeout on LCN %d!\n",
+
+			if (chan->common.usedby == API){
+				send_delayed_cmd_result(card,dev,card->mbox);
+			}
+		}
+	}else{ 
+		printk(KERN_INFO "%s: X.25 packet 0x%02X timeout on LCN %d!\n",
 		card->devname, mb->cmd.pktType, new_lcn);
+	}
 	return 1;
 }
 
-/******* Miscellaneous ******************************************************/
+/* 
+ *	Miscellaneous 
+ */
 
-/*============================================================================
- * Establish physical connection.
- * o open HDLC and raise DTR
+/*====================================================================
+ * 	Establish physical connection.
+ * 	o open HDLC and raise DTR
  *
- * Return:	0	connection established
- *		1	connection is in progress
- *		<0	error
- */
+ * 	Return:		0	connection established
+ *			1	connection is in progress
+ *			<0	error
+ *===================================================================*/
+
 static int connect (sdla_t* card)
 {
+	TX25Status* status = card->flags;
+
 	if (x25_open_hdlc(card) || x25_setup_hdlc(card))
 		return -EIO;
+
 	wanpipe_set_state(card, WAN_CONNECTING);
+
+	x25_set_intr_mode(card, INTR_ON_TIMER); 
+	status->imask &= ~INTR_ON_TIMER;
+
 	return 1;
 }
 
-/*============================================================================
- * Tear down physical connection.
- * o close HDLC link
- * o drop DTR
+/*
+ * 	Tear down physical connection.
+ * 	o close HDLC link
+ * 	o drop DTR
  *
- * Return:	0
- *		<0	error
+ * 	Return:		0
+ *			<0	error
  */
+
 static int disconnect (sdla_t* card)
 {
 	wanpipe_set_state(card, WAN_DISCONNECTED);
-	x25_set_intr_mode(card, 0);	/* disable interrupt generation */
-	x25_close_hdlc(card);		/* close HDLC link */
-	x25_set_dtr(card, 0);		/* drop DTR */
+	x25_set_intr_mode(card, INTR_ON_TIMER);	/* disable all interrupt except timer */
+	x25_close_hdlc(card);			/* close HDLC link */
+	x25_set_dtr(card, 0);			/* drop DTR */
 	return 0;
 }
 
-/*============================================================================
+/*
  * Find network device by its channel number.
  */
-static struct net_device* get_dev_by_lcn (wan_device_t* wandev, unsigned lcn)
+
+static netdevice_t* get_dev_by_lcn (wan_device_t* wandev, unsigned lcn)
 {
-	struct net_device* dev;
-	x25_channel_t *chan;
+	netdevice_t* dev;
 
-	dev = wandev->dev;
-	while (dev) {
-		if (chan->lcn == lcn)
+	for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv))
+		if (((x25_channel_t*)dev->priv)->common.lcn == lcn) 
 			break;
-		dev = chan->slave;
-	}
 	return dev;
 }
 
-/*============================================================================
- * Initiate connection on the logical channel.
- * o for PVC we just get channel configuration
- * o for SVCs place an X.25 call
- *
- * Return:	0	connected
- *		>0	connection in progress
- *		<0	failure
+/*
+ * 	Initiate connection on the logical channel.
+ * 	o for PVC we just get channel configuration
+ * 	o for SVCs place an X.25 call
+ *
+ * 	Return:		0	connected
+ *			>0	connection in progress
+ *			<0	failure
  */
-static int chan_connect (struct net_device* dev)
+
+static int chan_connect (netdevice_t* dev)
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 
-	if (chan->svc)
-	{
-		if (!chan->addr[0])
+	if (chan->common.svc && chan->common.usedby == WANPIPE){
+		if (!chan->addr[0]){
+			printk(KERN_INFO "%s: No Destination Address\n",
+					card->devname);
 			return -EINVAL;	/* no destination address */
+		}
 		printk(KERN_INFO "%s: placing X.25 call to %s ...\n",
 			card->devname, chan->addr);
+
 		if (x25_place_call(card, chan) != CMD_OK)
 			return -EIO;
+
 		set_chan_state(dev, WAN_CONNECTING);
 		return 1;
-	}
-	else
-	{
+	}else{
 		if (x25_get_chan_conf(card, chan) != CMD_OK)
 			return -EIO;
+
 		set_chan_state(dev, WAN_CONNECTED);
 	}
 	return 0;
 }
 
-/*============================================================================
- * Disconnect logical channel.
- * o if SVC then clear X.25 call
+/*
+ * 	Disconnect logical channel.
+ * 	o if SVC then clear X.25 call
  */
-static int chan_disc (struct net_device* dev)
+
+static int chan_disc (netdevice_t* dev)
 {
 	x25_channel_t* chan = dev->priv;
 
-	if (chan->svc)
-		x25_clear_call(chan->card, chan->lcn, 0, 0);
+	if (chan->common.svc){ 
+		x25_clear_call(chan->card, chan->common.lcn, 0, 0);
+
+		/* For API we disconnect on clear
+                 * confirmation. 
+                 */
+		if (chan->common.usedby == API)
+			return 0;
+	}
+
 	set_chan_state(dev, WAN_DISCONNECTED);
+	
 	return 0;
 }
 
-/*============================================================================
- * Set logical channel state.
+/*
+ * 	Set logical channel state.
  */
-static void set_chan_state (struct net_device* dev, int state)
+
+static void set_chan_state (netdevice_t* dev, int state)
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
@@ -2071,99 +3611,257 @@
 
 	save_flags(flags);
 	cli();
-	if (chan->state != state)
+	if (chan->common.state != state)
 	{
 		switch (state)
 		{
 			case WAN_CONNECTED:
-				printk (KERN_INFO "%s: interface %s connected!\n",
-					card->devname, dev->name);
-				*(unsigned short*)dev->dev_addr = htons(chan->lcn);
+				if (card->u.x.logging){
+					printk (KERN_INFO 
+						"%s: interface %s connected, lcn %i !\n", 
+						card->devname, dev->name,chan->common.lcn);
+				}
+				*(unsigned short*)dev->dev_addr = htons(chan->common.lcn);
 				chan->i_timeout_sofar = jiffies;
+
+				/* LAPB is PVC Based */
+				if (card->u.x.LAPB_hdlc)
+					chan->common.svc=0;
 				break;
 
 			case WAN_CONNECTING:
-				printk (KERN_INFO "%s: interface %s connecting...\n",
-					card->devname, dev->name);
+				if (card->u.x.logging){
+					printk (KERN_INFO 
+						"%s: interface %s connecting, lcn %i ...\n", 
+						card->devname, dev->name, chan->common.lcn);
+				}
 				break;
 
 			case WAN_DISCONNECTED:
-				printk (KERN_INFO "%s: interface %s disconnected!\n",
-					card->devname, dev->name);
-				if (chan->svc) 
-				{
+				if (card->u.x.logging){
+					printk (KERN_INFO 
+						"%s: interface %s disconnected, lcn %i !\n", 
+						card->devname, dev->name,chan->common.lcn);
+				}
+				atomic_set(&chan->common.disconnect,0);
+				
+				if (chan->common.svc) {
 					*(unsigned short*)dev->dev_addr = 0;
-		                	chan->lcn = 0;
+					card->u.x.svc_to_dev_map[(chan->common.lcn%X25_MAX_CHAN)]=NULL;
+		                	chan->common.lcn = 0;
+				}
+
+				if (chan->transmit_length){
+					chan->transmit_length=0;
+					atomic_set(&chan->common.driver_busy,0);
+					chan->tx_offset=0;
+					if (is_queue_stopped(dev)){
+						wake_net_dev(dev);
+					}
+				}
+				atomic_set(&chan->common.command,0);
+				break;
+
+			case WAN_DISCONNECTING:
+				if (card->u.x.logging){
+					printk (KERN_INFO 
+					"\n%s: interface %s disconnecting, lcn %i ...\n", 
+					card->devname, dev->name,chan->common.lcn);
 				}
+				atomic_set(&chan->common.disconnect,0);
 				break;
 		}
-		chan->state = state;
+		chan->common.state = state;
 	}
 	chan->state_tick = jiffies;
 	restore_flags(flags);
 }
 
-/*============================================================================
- * Send packet on a logical channel.
- *	When this function is called, tx_skb field of the channel data space
- *	points to the transmit socket buffer.  When transmission is complete,
- *	release socket buffer and reset 'tbusy' flag.
- *
- * Return:	0	- transmission complete
- *		1	- busy
- *
- * Notes:
- * 1. If packet length is greater than MTU for this channel, we'll fragment
- *    the packet into 'complete sequence' using M-bit.
- * 2. When transmission is complete, an event notification should be issued
- *    to the router.
+/*
+ * 	Send packet on a logical channel.
+ *		When this function is called, tx_skb field of the channel data 
+ *		space points to the transmit socket buffer.  When transmission 
+ *		is complete, release socket buffer and reset 'tbusy' flag.
+ *
+ * 	Return:		0	- transmission complete
+ *			1	- busy
+ *
+ * 	Notes:
+ * 	1. If packet length is greater than MTU for this channel, we'll fragment
+ *    	the packet into 'complete sequence' using M-bit.
+ * 	2. When transmission is complete, an event notification should be issued
+ *    	to the router.
  */
-static int chan_send (struct net_device* dev, struct sk_buff* skb)
+
+static int chan_send (netdevice_t* dev, void* buff, unsigned data_len, unsigned char tx_intr)
 {
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 	TX25Status* status = card->flags;
-	unsigned len, qdm;
+	unsigned len=0, qdm=0, res=0, orig_len = 0;
+	void *data;
 
 	/* Check to see if channel is ready */
-	if (!(status->cflags[chan->ch_idx] & 0x40))
-		return 1;
-
-	if (skb->len > chan->tx_pkt_size)
-	{
-		len = chan->tx_pkt_size;
-		qdm = 0x01;		/* set M-bit (more data) */
+	if ((!(status->cflags[chan->ch_idx] & 0x40) && !card->u.x.LAPB_hdlc)  || 
+             !(*card->u.x.hdlc_buf_status & 0x40)){ 
+            
+		if (!tx_intr){
+			setup_for_delayed_transmit (dev, buff, data_len);
+			return 0;
+		}else{
+			/* By returning 0 to tx_intr the packet will be dropped */
+			++card->wandev.stats.tx_dropped;
+			++chan->ifstats.tx_dropped;
+			printk(KERN_INFO "%s: ERROR, Tx intr could not send, dropping %s:\n", 
+				card->devname,dev->name);
+			++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
+			return 0;
+		}
 	}
-	else	/* final packet */
-	{
-		len = skb->len;
-		qdm = 0;
+
+	if (chan->common.usedby == API){
+		/* Remove the API Header */
+		x25api_hdr_t *api_data = (x25api_hdr_t *)buff;
+
+		/* Set the qdm bits from the packet header 
+                 * User has the option to set the qdm bits
+                 */
+		qdm = api_data->qdm;
+
+		orig_len = len = data_len - sizeof(x25api_hdr_t);
+		data = (unsigned char*)buff + sizeof(x25api_hdr_t);
+	}else{
+		data = buff;
+		orig_len = len = data_len;
+	}	
+
+	if (tx_intr){
+		/* We are in tx_intr, minus the tx_offset from 
+                 * the total length. The tx_offset part of the
+		 * data has already been sent. Also, move the 
+		 * data pointer to proper offset location.
+                 */
+		len -= chan->tx_offset;
+		data = (unsigned char*)data + chan->tx_offset;
 	}
-	switch(x25_send(card, chan->lcn, qdm, len, skb->data))
-	{
+		
+	/* Check if the packet length is greater than MTU
+         * If YES: Cut the len to MTU and set the M bit 
+         */
+	if (len > chan->tx_pkt_size && !card->u.x.LAPB_hdlc){
+		len = chan->tx_pkt_size;
+		qdm |= M_BIT;		
+	} 
+
+
+	/* Pass only first three bits of the qdm byte to the send
+         * routine. In case user sets any other bit which might
+         * cause errors. 
+         */
+
+	switch(x25_send(card, chan->common.lcn, (qdm&0x07), len, data)){
 		case 0x00:	/* success */
 			chan->i_timeout_sofar = jiffies;
-			if (qdm)
-			{
-				skb_pull(skb, len);
-				return 1;
+
+		      #ifdef LINUX_2_4
+			dev->trans_start=jiffies;
+		      #endif
+			
+			if ((qdm & M_BIT) && !card->u.x.LAPB_hdlc){
+				if (!tx_intr){
+					/* The M bit was set, which means that part of the
+                                         * packet has been sent. Copy the packet into a buffer
+				         * and set the offset to len, so on next tx_inter 
+					 * the packet will be sent using the below offset.
+					 */
+					chan->tx_offset += len;
+
+					++chan->ifstats.tx_packets;
+				      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+					chan->ifstats.tx_bytes += len;
+				      #endif
+					
+					if (chan->tx_offset < orig_len){
+						setup_for_delayed_transmit (dev, buff, data_len);
+					}
+					res=0;
+				}else{
+					/* We are already in tx_inter, thus data is already
+                                         * in the buffer. Update the offset and wait for
+                                         * next tx_intr. We add on to the offset, since data can
+                                         * be X number of times larger than max data size.
+					 */
+					++chan->ifstats.tx_packets;
+				      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+					chan->ifstats.tx_bytes += len;
+				      #endif
+					
+					++chan->if_send_stat.if_send_bfr_passed_to_adptr;
+					chan->tx_offset += len;
+
+					/* The user can set the qdm bit as well.
+                                         * If the entire packet was sent and qdm is still
+                                         * set, than it's the user who has set the M bit. In that,
+                                         * case indicate that the packet was send by returning 
+					 * 0 and wait for a new packet. Otherwise, wait for next
+                                         * tx interrupt to send the rest of the packet */
+
+					if (chan->tx_offset < orig_len){
+						res=1;
+					}else{	
+						res=0;
+					}
+				}
+			}else{
+				++chan->ifstats.tx_packets;
+			      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+				chan->ifstats.tx_bytes += len;
+			      #endif
+				++chan->if_send_stat.if_send_bfr_passed_to_adptr;
+				res=0;
 			}
-			++chan->ifstats.tx_packets;
-			chan->ifstats.tx_bytes += skb->len;
 			break;
 
 		case 0x33:	/* Tx busy */
-			return 1;
+			if (tx_intr){
+				printk(KERN_INFO "%s: Tx_intr: Big Error dropping packet %s\n",
+						card->devname,dev->name);
+				++chan->ifstats.tx_dropped;
+				++card->wandev.stats.tx_dropped;
+				++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
+				res=0;
+			}else{
+				DBG_PRINTK(KERN_INFO 
+					"%s: Send: Big Error should have tx: storring %s\n",
+						card->devname,dev->name);
+				setup_for_delayed_transmit (dev, buff, data_len);	
+				res=1;
+			}
+			break;
 
 		default:	/* failure */
 			++chan->ifstats.tx_errors;
-/*			return 1; */
+			if (tx_intr){
+				printk(KERN_INFO "%s: Tx_intr: Failure to send, dropping %s\n",
+					card->devname,dev->name);
+				++chan->ifstats.tx_dropped;
+				++card->wandev.stats.tx_dropped;
+				++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
+				res=0;
+			}else{
+				DBG_PRINTK(KERN_INFO "%s: Send: Failure to send !!!, storing %s\n",
+					card->devname,dev->name);			
+				setup_for_delayed_transmit (dev, buff, data_len);
+				res=1;
+			}
+			break;	
 	}
-	return 0;
+	return res;
 }
 
-/*============================================================================
- * Parse X.25 call request data and fill x25_call_info_t structure.
+
+/*
+ * 	Parse X.25 call request data and fill x25_call_info_t structure.
  */
 
 static void parse_call_info (unsigned char* str, x25_call_info_t* info)
@@ -2172,54 +3870,45 @@
 	for (; *str; ++str)
 	{
 		int i;
-		unsigned ch;
+		unsigned char ch;
+
+		if (*str == '-') switch (str[1]) {
+
+			/* Take minus 2 off the maximum size so that 
+                         * last byte is 0. This way we can use string
+                         * manipulaton functions on call information.
+                         */
 
-		if (*str == '-') switch (str[1])
-		{
 			case 'd':	/* destination address */
-				for (i = 0; i < 16; ++i)
-				{
+				for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
 					ch = str[2+i];
-					if (!is_digit(ch)) 
-						break;
+					if (isspace(ch)) break;
 					info->dest[i] = ch;
 				}
 				break;
-	
+
 			case 's':	/* source address */
-				for (i = 0; i < 16; ++i)
-				{
+				for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
 					ch = str[2+i];
-					if (!is_digit(ch))
-						break;
+					if (isspace(ch)) break;
 					info->src[i] = ch;
 				}
 				break;
 
 			case 'u':	/* user data */
-				for (i = 0; i < 127; ++i)
-				{
-					ch = str[2+2*i];
-					if (!is_hex_digit(ch)) 
-						break;
-					info->user[i] = hex_to_uint(&str[2+2*i], 2);
+				for (i = 0; i < (MAX_X25_DATA_SIZE-2); ++i){
+					ch = str[2+i];
+					if (isspace(ch)) break;
+					info->user[i] = ch; 
 				}
 				info->nuser = i;
 				break;
 
 			case 'f':	/* facilities */
-				for (i = 0; i < 64; ++i)
-				{
-					ch = str[2+4*i];
-					if (!is_hex_digit(ch))
-						break;
-					info->facil[i].code =
-						hex_to_uint(&str[2+4*i], 2);
-					ch = str[4+4*i];
-					if (!is_hex_digit(ch))
-						break;
-					info->facil[i].parm =
-						hex_to_uint(&str[4+4*i], 2);
+				for (i = 0; i < (MAX_X25_FACL_SIZE-2); ++i){
+					ch = str[2+i];
+					if (isspace(ch)) break;
+					info->facil[i] = ch;
 				}
 				info->nfacil = i;
 				break;
@@ -2227,14 +3916,15 @@
 	}
 }
 
-/*============================================================================
- * Convert line speed in bps to a number used by S502 code.
+/*
+ * 	Convert line speed in bps to a number used by S502 code.
  */
+
 static unsigned char bps_to_speed_code (unsigned long bps)
 {
 	unsigned char	number;
 
-	if (bps <= 1200)        number = 0x01 ;
+	if (bps <= 1200)        number = 0x01;
 	else if (bps <= 2400)   number = 0x02;
 	else if (bps <= 4800)   number = 0x03;
 	else if (bps <= 9600)   number = 0x04;
@@ -2251,29 +3941,36 @@
 	return number;
 }
 
-/*============================================================================
- * Convert decimal string to unsigned integer.
- * If len != 0 then only 'len' characters of the string are converted.
+/*
+ * 	Convert decimal string to unsigned integer.
+ * 	If len != 0 then only 'len' characters of the string are converted.
  */
+
 static unsigned int dec_to_uint (unsigned char* str, int len)
 {
 	unsigned val;
 
-	if (!len) len = strlen(str);
+	if (!len) 
+		len = strlen(str);
+
 	for (val = 0; len && is_digit(*str); ++str, --len)
 		val = (val * 10) + (*str - (unsigned)'0');
+	
 	return val;
 }
 
-/*============================================================================
- * Convert hex string to unsigned integer.
- * If len != 0 then only 'len' characters of the string are conferted.
+/*
+ * 	Convert hex string to unsigned integer.
+ * 	If len != 0 then only 'len' characters of the string are conferted.
  */
+
 static unsigned int hex_to_uint (unsigned char* str, int len)
 {
 	unsigned val, ch;
 
-	if (!len) len = strlen(str);
+	if (!len) 
+		len = strlen(str);
+
 	for (val = 0; len; ++str, --len)
 	{
 		ch = *str;
@@ -2281,8 +3978,7 @@
 			val = (val << 4) + (ch - (unsigned)'0');
 		else if (is_hex_digit(ch))
 			val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10);
-		else
-			break;
+		else break;
 	}
 	return val;
 }
@@ -2292,21 +3988,21 @@
 {
 	int i;
 
-	if( proto == htons(ETH_P_IPX) ) {
+	if( proto == ETH_P_IPX) {
 		/* It's an IPX packet */
 		if(!enable_IPX) {
 			/* Return 1 so we don't pass it up the stack. */
 			return 1;
 		}
 	} else {
-		/* It's not IPX so pass it up the stack. */
+		/* It's not IPX so pass it up the stack.*/ 
 		return 0;
 	}
 
 	if( sendpacket[16] == 0x90 &&
 	    sendpacket[17] == 0x04)
 	{
-		/* It's IPXWAN */
+		/* It's IPXWAN  */
 
 		if( sendpacket[2] == 0x02 &&
 		    sendpacket[34] == 0x00)
@@ -2315,19 +4011,24 @@
 			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
 
 			/* Go through the routing options and answer no to every
-			 * option except Unnumbered RIP/SAP */
+			 * option except Unnumbered RIP/SAP
+			 */
 			for(i = 41; sendpacket[i] == 0x00; i += 5)
 			{
 				/* 0x02 is the option for Unnumbered RIP/SAP */
 				if( sendpacket[i + 4] != 0x02)
+				{
 					sendpacket[i + 1] = 0;
+				}
 			}
 
 			/* Skip over the extended Node ID option */
 			if( sendpacket[i] == 0x04 )
+			{
 				i += 8;
+			}
 
-			/* We also want to turn off all header compression opt. */
+			/* We also want to turn off all header compression opt. 			 */ 
 			for(; sendpacket[i] == 0x80 ;)
 			{
 				sendpacket[i + 1] = 0;
@@ -2364,7 +4065,9 @@
 			sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4);
 			sendpacket[65] = CVHexToAscii(network_number & 0x0000000F);
 			for(i = 66; i < 99; i+= 1)
+			{
 				sendpacket[i] = 0;
+			}
 
 			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
 		}
@@ -2382,18 +4085,18 @@
 
 		return 1;
 	} else {
-		/* If we get here its an IPX-data packet, so it'll get passed up the stack.
-		   switch the network numbers */
+		/*If we get here its an IPX-data packet, so it'll get passed up the stack.
+		 */
+		/* switch the network numbers */
 		switch_net_numbers(sendpacket, network_number, 1);	
 		return 0;
 	}
 }
 
 /*
-   If incoming is 0 (outgoing)- if the net numbers is ours make it 0
-   if incoming is 1 - if the net number is 0 make it ours 
-
-*/
+ *  	If incoming is 0 (outgoing)- if the net numbers is ours make it 0
+ *  	if incoming is 1 - if the net number is 0 make it ours 
+ */
 
 static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
 {
@@ -2402,21 +4105,17 @@
 	pnetwork_number = (unsigned long)((sendpacket[6] << 24) + 
 			  (sendpacket[7] << 16) + (sendpacket[8] << 8) + 
 			  sendpacket[9]);
+	
 
-	if (!incoming) 
-	{
-		/* If the destination network number is ours, make it 0 */
-		if( pnetwork_number == network_number) 
-		{
+	if (!incoming) {
+		/*If the destination network number is ours, make it 0 */
+		if( pnetwork_number == network_number) {
 			sendpacket[6] = sendpacket[7] = sendpacket[8] = 
 					 sendpacket[9] = 0x00;
 		}
-	} 
-	else 
-	{
+	} else {
 		/* If the incoming network is 0, make it ours */
-		if( pnetwork_number == 0) 
-		{
+		if( pnetwork_number == 0) {
 			sendpacket[6] = (unsigned char)(network_number >> 24);
 			sendpacket[7] = (unsigned char)((network_number & 
 					 0x00FF0000) >> 16);
@@ -2431,21 +4130,17 @@
 	pnetwork_number = (unsigned long)((sendpacket[18] << 24) + 
 			  (sendpacket[19] << 16) + (sendpacket[20] << 8) + 
 			  sendpacket[21]);
-
-	if( !incoming ) 
-	{
+	
+	
+	if( !incoming ) {
 		/* If the source network is ours, make it 0 */
-		if( pnetwork_number == network_number) 
-		{
+		if( pnetwork_number == network_number) {
 			sendpacket[18] = sendpacket[19] = sendpacket[20] = 
-					 sendpacket[21] = 0x00;
+				 sendpacket[21] = 0x00;
 		}
-	}
-	else
-	{
+	} else {
 		/* If the source network is 0, make it ours */
-		if( pnetwork_number == 0 ) 
-		{
+		if( pnetwork_number == 0 ) {
 			sendpacket[18] = (unsigned char)(network_number >> 24);
 			sendpacket[19] = (unsigned char)((network_number & 
 					 0x00FF0000) >> 16);
@@ -2457,5 +4152,1419 @@
 	}
 } /* switch_net_numbers */
 
+
+
+
+/********************* X25API SPECIFIC FUNCTIONS ****************/
+
+
+/*===============================================================
+ *  find_channel
+ *
+ *	Manages the lcn to device map. It increases performance
+ *      because it eliminates the need to search through the link  
+ *      list for a device which is bounded to a specific lcn.
+ *
+ *===============================================================*/
+
+
+netdevice_t * find_channel(sdla_t *card, unsigned lcn)
+{
+	if (card->u.x.LAPB_hdlc){
+
+		return card->wandev.dev;
+
+	}else{
+		/* We don't know whether the incoming lcn
+                 * is a PVC or an SVC channel. But we do know that
+                 * the lcn cannot be for both the PVC and the SVC
+                 * channel.
+
+		 * If the lcn number is greater or equal to 255, 
+                 * take the modulo 255 of that number. We only have
+                 * 255 locations, thus higher numbers must be mapped
+                 * to a number between 0 and 245. 
+
+		 * We must separate pvc's and svc's since two don't
+                 * have to be contiguous.  Meaning pvc's can start
+                 * from 1 to 10 and svc's can start from 256 to 266.
+                 * But 256%255 is 1, i.e. CONFLICT.
+		 */
+
+
+		/* Highest LCN number must be less or equal to 4096 */
+		if ((lcn <= MAX_LCN_NUM) && (lcn > 0)){
+
+			if (lcn < X25_MAX_CHAN){
+				if (card->u.x.svc_to_dev_map[lcn])
+					return card->u.x.svc_to_dev_map[lcn];
+
+				if (card->u.x.pvc_to_dev_map[lcn])
+					return card->u.x.pvc_to_dev_map[lcn];
+			
+			}else{
+				int new_lcn = lcn%X25_MAX_CHAN;
+				if (card->u.x.svc_to_dev_map[new_lcn])
+					return card->u.x.svc_to_dev_map[new_lcn];
+
+				if (card->u.x.pvc_to_dev_map[new_lcn])
+					return card->u.x.pvc_to_dev_map[new_lcn];
+			}
+		}
+		return NULL;
+	}
+}
+
+void bind_lcn_to_dev (sdla_t *card, netdevice_t *dev,unsigned lcn)
+{
+	x25_channel_t *chan = dev->priv;
+
+	/* Modulo the lcn number by X25_MAX_CHAN (255)
+	 * because the lcn number can be greater than 255 
+         *
+	 * We need to split svc and pvc since they don't have
+         * to be contigous. 
+	 */
+
+	if (chan->common.svc){
+		card->u.x.svc_to_dev_map[(lcn % X25_MAX_CHAN)] = dev;
+	}else{
+		card->u.x.pvc_to_dev_map[(lcn % X25_MAX_CHAN)] = dev;
+	}
+	chan->common.lcn = lcn;
+}
+
+
+
+/*===============================================================
+ * x25api_bh 
+ *
+ *
+ *==============================================================*/
+
+static void x25api_bh (netdevice_t * dev)
+{
+	x25_channel_t* chan = dev->priv;
+	sdla_t* card = chan->card;
+	struct sk_buff *skb;
+
+	if (atomic_read(&chan->bh_buff_used) == 0){
+		printk(KERN_INFO "%s: BH Buffer Empty in BH\n",
+				card->devname);
+		clear_bit(0, &chan->tq_working);
+		return;
+	}
+
+	while (atomic_read(&chan->bh_buff_used)){
+
+		/* If the sock is in the process of unlinking the
+		 * driver from the socket, we must get out. 
+		 * This never happends but is a sanity check. */
+		if (test_bit(0,&chan->common.common_critical)){
+			clear_bit(0, &chan->tq_working);
+			return;
+		}
+		
+		/* If LAPB HDLC, do not drop packets if socket is
+                 * not connected.  Let the buffer fill up and
+                 * turn off rx interrupt */
+		if (card->u.x.LAPB_hdlc){
+			if (chan->common.sk == NULL || chan->common.func == NULL){
+				clear_bit(0, &chan->tq_working);			
+				return;
+			}
+		}
+
+		skb  = ((bh_data_t *)&chan->bh_head[chan->bh_read])->skb;
+
+		if (skb == NULL){
+			printk(KERN_INFO "%s: BH Skb empty for read %i\n",
+					card->devname,chan->bh_read);
+		}else{
+			
+			if (chan->common.sk == NULL || chan->common.func == NULL){
+				printk(KERN_INFO "%s: BH: Socket disconnected, dropping\n",
+						card->devname);
+				wan_dev_kfree_skb(skb, FREE_READ);
+				x25api_bh_cleanup(dev);
+				++chan->ifstats.rx_dropped;
+				++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
+				continue;
+			}
+
+
+			if (chan->common.func(skb,dev,chan->common.sk) != 0){
+				/* Sock full cannot send, queue us for another
+                                 * try 
+				 */
+				printk(KERN_INFO "%s: BH: !!! Packet failed to send !!!!! \n",
+						card->devname);
+				atomic_set(&chan->common.receive_block,1);
+				return;
+			}else{
+				x25api_bh_cleanup(dev);
+				++chan->rx_intr_stat.rx_intr_bfr_passed_to_stack;
+			}
+		}
+	}	
+	clear_bit(0, &chan->tq_working);
+
+	return;
+}
+
+/*===============================================================
+ * x25api_bh_cleanup 
+ *
+ *
+ *==============================================================*/
+
+static int x25api_bh_cleanup (netdevice_t *dev)
+{
+	x25_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	TX25Status* status = card->flags;
+
+
+	((bh_data_t *)&chan->bh_head[chan->bh_read])->skb = NULL;
+
+	if (chan->bh_read == MAX_BH_BUFF){
+		chan->bh_read=0;
+	}else{
+		++chan->bh_read;	
+	}
+
+	/* If the Receive interrupt was off, it means
+         * that we filled up our circular buffer. Check    
+         * that we have space in the buffer. If so 
+         * turn the RX interrupt back on. 
+	 */
+	if (!(status->imask & INTR_ON_RX_FRAME)){
+		if (atomic_read(&chan->bh_buff_used) < (MAX_BH_BUFF+1)){
+			printk(KERN_INFO "%s: BH: Turning on the interrupt\n",
+					card->devname);
+			status->imask |= INTR_ON_RX_FRAME;
+		}
+	}	
+
+	atomic_dec(&chan->bh_buff_used);
+	return 0;
+}
+
+
+/*===============================================================
+ * bh_enqueue 
+ *
+ *
+ *==============================================================*/
+
+static int bh_enqueue (netdevice_t *dev, struct sk_buff *skb)
+{
+	x25_channel_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	TX25Status* status = card->flags;
+
+	if (atomic_read(&chan->bh_buff_used) == (MAX_BH_BUFF+1)){
+		printk(KERN_INFO "%s: Bottom half buffer FULL\n",
+				card->devname);
+		return 1; 
+	}
+
+	((bh_data_t *)&chan->bh_head[chan->bh_write])->skb = skb;
+
+	if (chan->bh_write == MAX_BH_BUFF){
+		chan->bh_write=0;
+	}else{
+		++chan->bh_write;
+	}
+
+	atomic_inc(&chan->bh_buff_used);
+
+	if (atomic_read(&chan->bh_buff_used) == (MAX_BH_BUFF+1)){
+		printk(KERN_INFO "%s: Buffer is now full, Turning off RX Intr\n",
+				card->devname);
+		status->imask &= ~INTR_ON_RX_FRAME;
+	}
+
+	return 0;
+}
+
+
+/*===============================================================
+ * timer_intr_cmd_exec
+ *  
+ *	Called by timer interrupt to execute a command
+ *===============================================================*/
+
+static int timer_intr_cmd_exec (sdla_t* card)
+{
+	netdevice_t *dev;
+	unsigned char more_to_exec=0;
+	volatile x25_channel_t *chan=NULL;
+	int i=0,bad_cmd=0,err=0;	
+
+	if (card->u.x.cmd_dev == NULL){
+		card->u.x.cmd_dev = card->wandev.dev;
+	}
+
+	dev = card->u.x.cmd_dev;
+
+	for (;;){
+
+		chan = dev->priv;
+		
+		if (atomic_read(&chan->common.command)){ 
+
+			bad_cmd = check_bad_command(card,dev);
+
+			if ((!chan->common.mbox || atomic_read(&chan->common.disconnect)) && 
+			     !bad_cmd){
+
+				/* Socket has died or exited, We must bring the
+                                 * channel down before anybody else tries to 
+                                 * use it */
+				err = channel_disconnect(card,dev);
+			}else{
+			        err = execute_delayed_cmd(card, dev,
+							 (mbox_cmd_t*)chan->common.mbox,
+							  bad_cmd);
+			}
+
+			switch (err){
+
+			case RETURN_RESULT:
+
+				/* Return the result to the socket without
+                                 * delay. NO_WAIT Command */	
+				atomic_set(&chan->common.command,0);
+				if (atomic_read(&card->u.x.command_busy))
+					atomic_set(&card->u.x.command_busy,0);
+
+				send_delayed_cmd_result(card,dev,card->mbox);
+
+				more_to_exec=0;
+				break;
+			case DELAY_RESULT:
+		
+				/* Wait for the remote to respond, before
+                                 * sending the result up to the socket.
+                                 * WAIT command */
+				if (atomic_read(&card->u.x.command_busy))
+					atomic_set(&card->u.x.command_busy,0);
+				
+				atomic_set(&chan->common.command,0);
+				more_to_exec=0;
+				break;
+			default:
+
+				/* If command could not be executed for
+                                 * some reason (i.e return code 0x33 busy)
+                                 * set the more_to_exec bit which will
+                                 * indicate that this command must be exectued
+                                 * again during next timer interrupt 
+				 */
+				more_to_exec=1;
+				if (atomic_read(&card->u.x.command_busy) == 0)
+					atomic_set(&card->u.x.command_busy,1);
+				break;
+			}
+
+			bad_cmd=0;
+
+			/* If flags is set, there are no hdlc buffers,
+                         * thus, wait for the next pass and try the
+                         * same command again. Otherwise, start searching 
+                         * from next device on the next pass. 
+			 */
+			if (!more_to_exec){
+				dev = move_dev_to_next(card,dev);
+			}
+			break;
+		}else{
+			/* This device has nothing to execute,
+                         * go to next. 
+			 */
+			if (atomic_read(&card->u.x.command_busy))
+					atomic_set(&card->u.x.command_busy,0);
+			dev = move_dev_to_next(card,dev);
+		}	
+
+		if (++i == card->u.x.no_dev){
+			if (!more_to_exec){
+				DBG_PRINTK(KERN_INFO "%s: Nothing to execute in Timer\n",
+					card->devname);
+				if (atomic_read(&card->u.x.command_busy)){
+					atomic_set(&card->u.x.command_busy,0);
+				}
+			}
+			break;
+		}
+
+	} //End of FOR
+
+	card->u.x.cmd_dev = dev;
+	
+	if (more_to_exec){
+		/* If more commands are pending, do not turn off timer 
+                 * interrupt */
+		return 1;
+	}else{
+		/* No more commands, turn off timer interrupt */
+		return 0;
+	}	
+}
+
+/*===============================================================
+ * execute_delayed_cmd 
+ *
+ *	Execute an API command which was passed down from the
+ *      sock.  Sock is very limited in which commands it can
+ *      execute.  Wait and No Wait commands are supported.  
+ *      Place Call, Clear Call and Reset wait commands, where
+ *      Accept Call is a no_wait command.
+ *
+ *===============================================================*/
+
+static int execute_delayed_cmd (sdla_t* card, netdevice_t *dev, mbox_cmd_t *usr_cmd,char bad_cmd)
+{
+	TX25Mbox* mbox = card->mbox;
+	int err;
+	x25_channel_t *chan = dev->priv;
+	int delay=RETURN_RESULT;
+
+	if (!(*card->u.x.hdlc_buf_status & 0x40) && !bad_cmd){
+		return TRY_CMD_AGAIN;
+	}
+
+	/* This way a command is guaranteed to be executed for
+         * a specific lcn, the network interface is bound to. */
+	usr_cmd->cmd.lcn = chan->common.lcn;
+	
+
+	/* If channel is pvc, instead of place call
+         * run x25_channel configuration. If running LAPB HDLC
+         * enable communications. 
+         */
+	if ((!chan->common.svc) && (usr_cmd->cmd.command == X25_PLACE_CALL)){
+
+		if (card->u.x.LAPB_hdlc){
+			DBG_PRINTK(KERN_INFO "LAPB: Connecting\n");
+			connect(card);
+			set_chan_state(dev,WAN_CONNECTING);
+			return DELAY_RESULT;
+		}else{
+			DBG_PRINTK(KERN_INFO "%s: PVC is CONNECTING\n",card->devname);
+			if (x25_get_chan_conf(card, chan) == CMD_OK){
+				set_chan_state(dev, WAN_CONNECTED);
+			}else{ 
+				set_chan_state(dev, WAN_DISCONNECTED);
+			}
+			return RETURN_RESULT;
+		}
+	}
+
+	/* Copy the socket mbox command onto the board */
+
+	memcpy(&mbox->cmd, &usr_cmd->cmd, sizeof(TX25Cmd));
+	if (usr_cmd->cmd.length){
+		memcpy(mbox->data, usr_cmd->data, usr_cmd->cmd.length);
+	}
+
+	/* Check if command is bad. We need to copy the cmd into
+         * the buffer regardless since we return the, mbox to
+         * the user */
+	if (bad_cmd){
+		mbox->cmd.result=0x01;
+		return RETURN_RESULT;
+	}
+
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+
+	if (err != CMD_OK && err != X25RES_NOT_READY)
+		x25_error(card, err, usr_cmd->cmd.command, usr_cmd->cmd.lcn);
+
+	if (mbox->cmd.result == X25RES_NOT_READY){
+		return TRY_CMD_AGAIN;
+	}
+
+	switch (mbox->cmd.command){
+
+	case X25_PLACE_CALL:
+		
+		switch (mbox->cmd.result){
+
+		case CMD_OK:
+
+			/* Check if Place call is a wait command or a 
+               	  	 * no wait command */
+			if (atomic_read(&chan->common.command) & 0x80)
+ 				delay=RETURN_RESULT;
+			else
+				delay=DELAY_RESULT;
+		
+
+			DBG_PRINTK(KERN_INFO "\n%s: PLACE CALL Binding dev %s to lcn %i\n",
+					card->devname,dev->name, mbox->cmd.lcn);
+		
+			bind_lcn_to_dev (card, dev, mbox->cmd.lcn);
+			set_chan_state(dev, WAN_CONNECTING);
+			break;
+
+
+		default:
+			delay=RETURN_RESULT;
+			set_chan_state(dev, WAN_DISCONNECTED);
+			break;
+		}
+		break;
+
+	case X25_ACCEPT_CALL: 
+		
+		switch (mbox->cmd.result){
+
+		case CMD_OK:
+
+			DBG_PRINTK(KERN_INFO "\n%s: ACCEPT Binding dev %s to lcn %i\n",
+				card->devname,dev->name,mbox->cmd.lcn);
+
+			bind_lcn_to_dev (card, dev, mbox->cmd.lcn);
+
+			if (x25_get_chan_conf(card, chan) == CMD_OK){
+
+				set_chan_state(dev, WAN_CONNECTED);
+				delay=RETURN_RESULT;
+
+			}else{ 
+				if (x25_clear_call(card, usr_cmd->cmd.lcn, 0, 0) == CMD_OK){
+					/* if clear is successful, wait for clear confirm 
+					 */ 
+					delay=DELAY_RESULT;
+				}else{
+					/* Do not change the state here. If we fail 
+					 * the accept the return code is send up 
+					 *the stack, which will ether retry
+                               	  	 * or clear the call 
+					 */
+					DBG_PRINTK(KERN_INFO 
+						"%s: ACCEPT: STATE MAY BE CURRUPTED 2 !!!!!\n",
+						card->devname);
+					delay=RETURN_RESULT;
+				}
+			}
+			break;
+
+
+		case X25RES_ASYNC_PACKET:
+			delay=TRY_CMD_AGAIN;
+			break;
+
+		default: 
+			DBG_PRINTK(KERN_INFO "%s: ACCEPT FAILED\n",card->devname);
+			if (x25_clear_call(card, usr_cmd->cmd.lcn, 0, 0) == CMD_OK){
+				delay=DELAY_RESULT;
+			}else{
+				/* Do not change the state here. If we fail the accept. The
+                                 * return code is send up the stack, which will ether retry
+                                 * or clear the call */
+				DBG_PRINTK(KERN_INFO 
+					"%s: ACCEPT: STATE MAY BE CORRUPTED 1 !!!!!\n",
+						card->devname);
+				delay=RETURN_RESULT;
+			}
+		}
+		break;
+
+	case X25_CLEAR_CALL:
+
+		switch (mbox->cmd.result){
+
+		case CMD_OK:
+			DBG_PRINTK(KERN_INFO 
+					"CALL CLEAR OK: Dev %s Mbox Lcn %i  Chan Lcn %i\n",
+					dev->name,mbox->cmd.lcn,chan->common.lcn);
+			set_chan_state(dev, WAN_DISCONNECTING);
+			delay = DELAY_RESULT;
+			break;
+
+		case X25RES_CHANNEL_IN_USE:
+		case X25RES_ASYNC_PACKET:
+			delay = TRY_CMD_AGAIN;
+			break;
+			
+		case X25RES_LINK_NOT_IN_ABM:
+		case X25RES_INVAL_LCN:
+		case X25RES_INVAL_STATE:
+			set_chan_state(dev, WAN_DISCONNECTED);
+			delay = RETURN_RESULT;
+			break;
+		
+		default:
+			/* If command did not execute because of user
+                         * fault, do not change the state. This will
+                         * signal the socket that clear command failed.
+                         * User can retry or close the socket.
+                         * When socket gets killed, it will set the 
+                         * chan->disconnect which will signal
+                         * driver to clear the call */
+			printk(KERN_INFO "%s: Clear Command Failed, Rc %x\n",
+				card->devname,mbox->cmd.command); 
+			delay = RETURN_RESULT;
+		}
+		break;
+	}	
+
+	return delay;
+}
+
+/*===============================================================
+ * api_incoming_call 
+ *
+ *	Pass an incoming call request up the the listening
+ *      sock.  If the API sock is not listening reject the
+ *      call.
+ *
+ *===============================================================*/
+
+static int api_incoming_call (sdla_t* card, TX25Mbox *mbox, int lcn)
+{
+	struct sk_buff *skb;
+	int len = sizeof(TX25Cmd)+mbox->cmd.length;
+
+	if (alloc_and_init_skb_buf(card, &skb, len)){
+		printk(KERN_INFO "%s: API incoming call, no memory\n",card->devname);
+		return 1;
+	}
+
+	memcpy(skb_put(skb,len),&mbox->cmd,len);
+
+	skb->mac.raw = skb->data;
+	skb->protocol = htons(X25_PROT);
+	skb->pkt_type = WAN_PACKET_ASYNC;
+
+	if (card->func(skb,card->sk) < 0){
+		printk(KERN_INFO "%s: MAJOR ERROR: Failed to send up place call \n",card->devname);
+                wan_dev_kfree_skb(skb, FREE_READ);
+		return 1;
+	}
+
+	return 0;
+}
+
+/*===============================================================
+ * send_delayed_cmd_result
+ *
+ *	Wait commands like PLEACE CALL or CLEAR CALL must wait
+ *      untill the result arrivers. This function passes
+ *      the result to a waiting sock. 
+ *
+ *===============================================================*/
+static void send_delayed_cmd_result(sdla_t *card, netdevice_t *dev, TX25Mbox* mbox)
+{
+	x25_channel_t *chan = dev->priv;
+	mbox_cmd_t *usr_cmd = (mbox_cmd_t *)chan->common.mbox;
+	struct sk_buff *skb;
+	int len=sizeof(unsigned char);
+
+	atomic_set(&chan->common.command,0);
+
+	/* If the sock is in the process of unlinking the
+	 * driver from the socket, we must get out. 
+	 * This never happends but is a sanity check. */
+	if (test_bit(0,&chan->common.common_critical)){
+		return;
+	}
+
+	if (!usr_cmd || !chan->common.sk || !chan->common.func){
+		DBG_PRINTK(KERN_INFO "Delay result: Sock not bounded sk: %u, func: %u, mbox: %u\n",
+			(unsigned int)chan->common.sk,
+			(unsigned int)chan->common.func,
+			(unsigned int)usr_cmd); 
+		return;
+	}
+
+	memcpy(&usr_cmd->cmd, &mbox->cmd, sizeof(TX25Cmd)); 
+	if (mbox->cmd.length > 0){
+		memcpy(usr_cmd->data, mbox->data, mbox->cmd.length);
+	}
+
+	if (alloc_and_init_skb_buf(card,&skb,len)){
+		printk(KERN_INFO "Delay result: No sock buffers\n");
+		return;
+	}
+
+	memcpy(skb_put(skb,len),&mbox->cmd.command,len);
+	
+	skb->mac.raw = skb->data;
+	skb->pkt_type = WAN_PACKET_CMD;
+			
+	chan->common.func(skb,dev,chan->common.sk);
+}
+
+/*===============================================================
+ * clear_confirm_event
+ *
+ * 	Pass the clear confirmation event up the sock. The
+ *      API will disconnect only after the clear confirmation
+ *      has been received. 
+ *
+ *      Depending on the state, clear confirmation could 
+ *      be an OOB event, or a result of an API command.
+ *===============================================================*/
+
+static int clear_confirm_event (sdla_t *card, TX25Mbox* mb)
+{
+	netdevice_t *dev;
+	x25_channel_t *chan;
+	unsigned char old_state;	
+
+	dev = find_channel(card,mb->cmd.lcn);
+	if (!dev){
+		DBG_PRINTK(KERN_INFO "%s: *** GOT CLEAR BUT NO DEV %i\n",
+				card->devname,mb->cmd.lcn);
+		return 0;
+	}
+
+	chan=dev->priv;
+	DBG_PRINTK(KERN_INFO "%s: GOT CLEAR CONFIRM %s:  Mbox lcn %i  Chan lcn %i\n",
+			card->devname, dev->name, mb->cmd.lcn, chan->common.lcn);
+
+	/* If not API fall through to default. 
+	 * If API, send the result to a waiting
+         * socket.
+	 */
+	
+	old_state = chan->common.state;
+	set_chan_state(dev, WAN_DISCONNECTED);
+
+	if (chan->common.usedby == API){
+		switch (old_state) {
+
+		case WAN_DISCONNECTING:
+		case WAN_CONNECTING:
+			send_delayed_cmd_result(card,dev,mb);
+			break;
+		case WAN_CONNECTED:
+			send_oob_msg(card,dev,mb);
+			break;
+		}
+		return 1;
+	}
+
+	return 0;
+}
+
+/*===============================================================
+ * send_oob_msg
+ *
+ *    Construct an NEM Message and pass it up the connected
+ *    sock. If the sock is not bounded discard the NEM.
+ *
+ *===============================================================*/
+
+static void send_oob_msg (sdla_t *card, netdevice_t *dev, TX25Mbox *mbox)
+{
+	x25_channel_t *chan = dev->priv;
+	mbox_cmd_t *usr_cmd = (mbox_cmd_t *)chan->common.mbox;
+	struct sk_buff *skb;
+	int len=sizeof(x25api_hdr_t)+mbox->cmd.length;
+	x25api_t *api_hdr;
+
+	/* If the sock is in the process of unlinking the
+	 * driver from the socket, we must get out. 
+	 * This never happends but is a sanity check. */
+	if (test_bit(0,&chan->common.common_critical)){
+		return;
+	}
+
+	if (!usr_cmd || !chan->common.sk || !chan->common.func){
+		DBG_PRINTK(KERN_INFO "OOB MSG: Sock not bounded\n"); 
+		return;
+	}
+
+	memcpy(&usr_cmd->cmd, &mbox->cmd, sizeof(TX25Cmd)); 
+	if (mbox->cmd.length > 0){
+		memcpy(usr_cmd->data, mbox->data, mbox->cmd.length);
+	}
+
+	if (alloc_and_init_skb_buf(card,&skb,len)){
+		printk(KERN_INFO "%s: OOB MSG: No sock buffers\n",card->devname);
+		return;
+	}
+
+	api_hdr = (x25api_t*)skb_put(skb,len); 
+	api_hdr->hdr.pktType = mbox->cmd.pktType & 0x7F;
+	api_hdr->hdr.qdm     = mbox->cmd.qdm;
+	api_hdr->hdr.cause   = mbox->cmd.cause;
+	api_hdr->hdr.diagn   = mbox->cmd.diagn;
+	api_hdr->hdr.length  = mbox->cmd.length;
+	api_hdr->hdr.result  = mbox->cmd.result;
+	api_hdr->hdr.lcn     = mbox->cmd.lcn;
+
+	if (mbox->cmd.length > 0){
+		memcpy(api_hdr->data,mbox->data,mbox->cmd.length);
+	}
+	
+	skb->mac.raw = skb->data;
+	skb->pkt_type = WAN_PACKET_ERR;
+			
+	if (chan->common.func(skb,dev,chan->common.sk) < 0){
+		if (bh_enqueue(dev,skb)){
+			printk(KERN_INFO "%s: Dropping OOB MSG\n",card->devname);
+                	wan_dev_kfree_skb(skb, FREE_READ);
+		}
+	}
+
+	DBG_PRINTK(KERN_INFO "%s: OOB MSG OK, %s, lcn %i\n",
+			card->devname, dev->name, mbox->cmd.lcn);
+}	
+
+/*===============================================================
+ *  alloc_and_init_skb_buf 
+ *
+ *	Allocate and initialize an skb buffer. 
+ *
+ *===============================================================*/
+
+static int alloc_and_init_skb_buf (sdla_t *card, struct sk_buff **skb, int len)
+{
+	struct sk_buff *new_skb = *skb;
+
+	new_skb = dev_alloc_skb(len + X25_HRDHDR_SZ);
+	if (new_skb == NULL){
+		printk(KERN_INFO "%s: no socket buffers available!\n",
+			card->devname);
+		return 1;
+	}
+
+	if (skb_tailroom(new_skb) < len){
+		/* No room for the packet. Call off the whole thing! */
+                wan_dev_kfree_skb(new_skb, FREE_READ);
+		printk(KERN_INFO "%s: Listen: unexpectedly long packet sequence\n"
+			,card->devname);
+		*skb = NULL;
+		return 1;
+	}
+
+	*skb = new_skb;
+	return 0;
+
+}
+
+/*===============================================================
+ *  api_oob_event 
+ *
+ *	Send an OOB event up to the sock 
+ *
+ *===============================================================*/
+
+static void api_oob_event (sdla_t *card,TX25Mbox *mbox)
+{
+	netdevice_t *dev = find_channel(card,mbox->cmd.lcn);
+	x25_channel_t *chan;
+
+	if (!dev)
+		return;
+
+	chan=dev->priv;
+
+	if (chan->common.usedby == API)
+		send_oob_msg(card,dev,mbox);
+	
+}
+
+
+
+
+static int channel_disconnect (sdla_t* card, netdevice_t *dev)
+{
+
+	int err;
+	x25_channel_t *chan = dev->priv;
+
+	DBG_PRINTK(KERN_INFO "%s: TIMER: %s, Device down disconnecting\n",
+				card->devname,dev->name);
+
+	if (chan->common.svc){
+		err = x25_clear_call(card,chan->common.lcn,0,0);
+	}else{
+		/* If channel is PVC or LAPB HDLC, there is no call
+                 * to be cleared, thus drop down to the default
+                 * area 
+	         */
+		err = 1;
+	}
+
+	switch (err){
+	
+		case X25RES_CHANNEL_IN_USE:	
+		case X25RES_NOT_READY:
+			err = TRY_CMD_AGAIN;
+			break;
+		case CMD_OK:
+			DBG_PRINTK(KERN_INFO "CALL CLEAR OK: Dev %s Chan Lcn %i\n",
+						dev->name,chan->common.lcn);
+
+			set_chan_state(dev,WAN_DISCONNECTING);
+			atomic_set(&chan->common.command,0);
+			err = DELAY_RESULT;
+			break;
+		default:
+			/* If LAPB HDLC protocol, bring the whole link down
+                         * once the application terminates 
+			 */
+
+			set_chan_state(dev,WAN_DISCONNECTED);
+
+			if (card->u.x.LAPB_hdlc){
+				DBG_PRINTK(KERN_INFO "LAPB: Disconnecting Link\n");
+				hdlc_link_down (card);
+			}
+			atomic_set(&chan->common.command,0);
+			err = RETURN_RESULT;
+			break;
+	}
+
+	return err;
+}
+
+static void hdlc_link_down (sdla_t *card)
+{
+	TX25Mbox* mbox = card->mbox;
+	int retry = 5;
+	int err=0;
+
+	do {
+		memset(mbox,0,sizeof(TX25Mbox));
+		mbox->cmd.command = X25_HDLC_LINK_DISC;
+		mbox->cmd.length = 1;
+		mbox->data[0]=0;
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+
+	} while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_DISC, 0));
+
+	if (err)
+		printk(KERN_INFO "%s: Hdlc Link Down Failed %x\n",card->devname,err);
+
+	disconnect (card);
+	
+}
+
+static int check_bad_command (sdla_t* card, netdevice_t *dev)
+{
+	x25_channel_t *chan = dev->priv;
+	int bad_cmd = 0;
+
+	switch (atomic_read(&chan->common.command)&0x7F){
+
+		case X25_PLACE_CALL:
+			if (chan->common.state != WAN_DISCONNECTED)
+				bad_cmd=1;
+			break;
+		case X25_CLEAR_CALL:
+			if (chan->common.state == WAN_DISCONNECTED)
+				bad_cmd=1;
+			break;
+		case X25_ACCEPT_CALL:
+			if (chan->common.state != WAN_CONNECTING)
+				bad_cmd=1;
+			break;
+		case X25_RESET:
+			if (chan->common.state != WAN_CONNECTED)
+				bad_cmd=1;
+			break;
+		default:
+			bad_cmd=1;
+			break;
+	}
+
+	if (bad_cmd){
+		printk(KERN_INFO "%s: Invalid State, BAD Command %x, dev %s, lcn %i, st %i\n", 
+			card->devname,atomic_read(&chan->common.command),dev->name, 
+			chan->common.lcn, chan->common.state);
+	}
+
+	return bad_cmd;
+}
+
+
+
+/*************************** XPIPEMON FUNCTIONS **************************/
+
+/*==============================================================================
+ * Process UDP call of type XPIPE
+ */
+
+static int process_udp_mgmt_pkt(sdla_t *card)
+{
+	int            c_retry = MAX_CMD_RETRY;
+	unsigned int   len;
+	struct sk_buff *new_skb;
+	TX25Mbox       *mbox = card->mbox;
+	int            err;
+	int            udp_mgmt_req_valid = 1;
+	netdevice_t  *dev;
+        x25_channel_t  *chan;
+	unsigned short lcn;
+	struct timeval tv;
+	
+
+	x25_udp_pkt_t *x25_udp_pkt;
+	x25_udp_pkt = (x25_udp_pkt_t *)card->u.x.udp_pkt_data;
+
+	dev = card->u.x.udp_dev;
+	chan = dev->priv;
+	lcn = chan->common.lcn;
+
+	switch(x25_udp_pkt->cblock.command) {
+            
+		/* XPIPE_ENABLE_TRACE */
+		case XPIPE_ENABLE_TRACING:
+
+		/* XPIPE_GET_TRACE_INFO */
+		case XPIPE_GET_TRACE_INFO:
+ 
+		/* SET FT1 MODE */
+		case XPIPE_SET_FT1_MODE:
+           
+			if(card->u.x.udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+                    		++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_direction_err;
+				udp_mgmt_req_valid = 0;
+				break;
+			}
+
+		/* XPIPE_FT1_READ_STATUS */
+		case XPIPE_FT1_READ_STATUS:
+
+		/* FT1 MONITOR STATUS */
+		case XPIPE_FT1_STATUS_CTRL:
+			if(card->hw.fwid !=  SFID_X25_508) {
+				++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_type_err;
+				udp_mgmt_req_valid = 0;
+				break;
+			}
+		default:
+			break;
+       	}
+
+	if(!udp_mgmt_req_valid) {
+           	/* set length to 0 */
+		x25_udp_pkt->cblock.length = 0;
+		/* set return code */
+		x25_udp_pkt->cblock.result = (card->hw.fwid != SFID_X25_508) ? 0x1F : 0xCD;
+		
+	} else {   
+        
+		switch (x25_udp_pkt->cblock.command) {
+    
+	
+		case XPIPE_FLUSH_DRIVER_STATS:
+			init_x25_channel_struct(chan);
+			init_global_statistics(card);
+			mbox->cmd.length = 0;
+			break;
+
+
+		case XPIPE_DRIVER_STAT_IFSEND:
+			memcpy(x25_udp_pkt->data, &chan->if_send_stat, sizeof(if_send_stat_t));
+			mbox->cmd.length = sizeof(if_send_stat_t);
+			x25_udp_pkt->cblock.length =  mbox->cmd.length;	
+			break;
+	
+		case XPIPE_DRIVER_STAT_INTR:
+			memcpy(&x25_udp_pkt->data[0], &card->statistics, sizeof(global_stats_t));
+                        memcpy(&x25_udp_pkt->data[sizeof(global_stats_t)],
+                                &chan->rx_intr_stat, sizeof(rx_intr_stat_t));
+			
+			mbox->cmd.length = sizeof(global_stats_t) +
+					sizeof(rx_intr_stat_t);
+			x25_udp_pkt->cblock.length =  mbox->cmd.length;
+			break;
+
+		case XPIPE_DRIVER_STAT_GEN:
+                        memcpy(x25_udp_pkt->data,
+                                &chan->pipe_mgmt_stat.UDP_PIPE_mgmt_kmalloc_err,
+                                sizeof(pipe_mgmt_stat_t));
+
+                        memcpy(&x25_udp_pkt->data[sizeof(pipe_mgmt_stat_t)],
+                               &card->statistics, sizeof(global_stats_t));
+
+                        x25_udp_pkt->cblock.result = 0;
+                        x25_udp_pkt->cblock.length = sizeof(global_stats_t)+
+                                                     sizeof(rx_intr_stat_t);
+                        mbox->cmd.length = x25_udp_pkt->cblock.length;
+                        break;
+
+		case XPIPE_ROUTER_UP_TIME:
+			do_gettimeofday(&tv);
+			chan->router_up_time = tv.tv_sec - chan->router_start_time;
+    	                *(unsigned long *)&x25_udp_pkt->data = chan->router_up_time;	
+			x25_udp_pkt->cblock.length = mbox->cmd.length = 4;
+			x25_udp_pkt->cblock.result = 0;
+			break;
+	
+		default :
+
+			do {
+				memcpy(&mbox->cmd, &x25_udp_pkt->cblock.command, sizeof(TX25Cmd));
+				if(mbox->cmd.length){ 
+					memcpy(&mbox->data, 
+					       (char *)x25_udp_pkt->data, 
+					       mbox->cmd.length);
+				}	
+		
+				err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+			} while (err && c_retry-- && x25_error(card, err, mbox->cmd.command, 0));
+
+
+			if ( err == CMD_OK || 
+			    (err == 1 && 
+			     (mbox->cmd.command == 0x06 || 
+			      mbox->cmd.command == 0x16)  ) ){
+
+				++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_cmnd_OK;
+			} else {
+				++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_cmnd_timeout;
+			}
+
+			  /* copy the result back to our buffer */
+			memcpy(&x25_udp_pkt->cblock.command, &mbox->cmd, sizeof(TX25Cmd));
+
+      	         	if(mbox->cmd.length) {
+        	               memcpy(&x25_udp_pkt->data, &mbox->data, mbox->cmd.length);
+			}
+			break;
+
+		} //switch
+
+        }
+    
+        /* Fill UDP TTL */
+
+	x25_udp_pkt->ip_pkt.ttl = card->wandev.ttl;
+        len = reply_udp(card->u.x.udp_pkt_data, mbox->cmd.length);
+
+
+        if(card->u.x.udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+		
+		err = x25_send(card, lcn, 0, len, card->u.x.udp_pkt_data);
+		if (!err) 
+			++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_send_passed;
+		else
+			++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_send_failed;
+	
+	} else {
+
+		/* Allocate socket buffer */
+		if((new_skb = dev_alloc_skb(len)) != NULL) {
+			void *buf;
+
+			/* copy data into new_skb */
+			buf = skb_put(new_skb, len);
+			memcpy(buf, card->u.x.udp_pkt_data, len);
+        
+			/* Decapsulate packet and pass it up the protocol 
+			   stack */
+			new_skb->dev = dev;
+	
+			if (chan->common.usedby == API)
+                        	new_skb->protocol = htons(X25_PROT);
+			else 
+				new_skb->protocol = htons(ETH_P_IP);
+	
+                        new_skb->mac.raw = new_skb->data;
+
+			netif_rx(new_skb);
+			++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_passed_to_stack;
+            	
+		} else {
+			++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_no_socket;
+			printk(KERN_INFO 
+			"%s: UDP mgmt cmnd, no socket buffers available!\n", 
+			card->devname);
+            	}
+        }
+
+	card->u.x.udp_pkt_lgth = 0;
+
+	return 1;
+}
+
+
+/*==============================================================================
+ * Determine what type of UDP call it is. DRVSTATS or XPIPE8ND ?
+ */
+static int udp_pkt_type( struct sk_buff *skb, sdla_t* card )
+{
+	x25_udp_pkt_t *x25_udp_pkt = (x25_udp_pkt_t *)skb->data;
+
+        if((x25_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) &&
+		(x25_udp_pkt->ip_pkt.ver_inet_hdr_length == 0x45) &&
+		(x25_udp_pkt->udp_pkt.udp_dst_port == ntohs(card->wandev.udp_port)) &&
+		(x25_udp_pkt->wp_mgmt.request_reply == UDPMGMT_REQUEST)) {
+
+                        if(!strncmp(x25_udp_pkt->wp_mgmt.signature,
+                                UDPMGMT_XPIPE_SIGNATURE, 8)){
+                                return UDP_XPIPE_TYPE;
+			}else{
+				printk(KERN_INFO "%s: UDP Packet, Failed Signature !\n",
+					card->devname);
+			}
+	}
+
+        return UDP_INVALID_TYPE;
+}
+
+
+/*============================================================================
+ * Reply to UDP Management system.
+ * Return nothing.
+ */
+static int reply_udp( unsigned char *data, unsigned int mbox_len ) 
+{
+	unsigned short len, udp_length, temp, ip_length;
+	unsigned long ip_temp;
+	int even_bound = 0;
+
+  
+	x25_udp_pkt_t *x25_udp_pkt = (x25_udp_pkt_t *)data; 
+
+	/* Set length of packet */
+	len = sizeof(ip_pkt_t)+ 
+	      sizeof(udp_pkt_t)+
+	      sizeof(wp_mgmt_t)+
+	      sizeof(cblock_t)+
+	      mbox_len;
+ 
+
+	/* fill in UDP reply */
+	x25_udp_pkt->wp_mgmt.request_reply = UDPMGMT_REPLY;
+  
+	/* fill in UDP length */
+	udp_length = sizeof(udp_pkt_t)+ 
+		     sizeof(wp_mgmt_t)+
+		     sizeof(cblock_t)+
+		     mbox_len; 
+
+
+	/* put it on an even boundary */
+	if ( udp_length & 0x0001 ) {
+		udp_length += 1;
+		len += 1;
+		even_bound = 1;
+	}
+
+	temp = (udp_length<<8)|(udp_length>>8);
+	x25_udp_pkt->udp_pkt.udp_length = temp;
+	 
+	/* swap UDP ports */
+	temp = x25_udp_pkt->udp_pkt.udp_src_port;
+	x25_udp_pkt->udp_pkt.udp_src_port = 
+			x25_udp_pkt->udp_pkt.udp_dst_port; 
+	x25_udp_pkt->udp_pkt.udp_dst_port = temp;
+
+
+
+	/* add UDP pseudo header */
+	temp = 0x1100;
+	*((unsigned short *)
+		(x25_udp_pkt->data+mbox_len+even_bound)) = temp;	
+	temp = (udp_length<<8)|(udp_length>>8);
+	*((unsigned short *)
+		(x25_udp_pkt->data+mbox_len+even_bound+2)) = temp;
+		 
+	/* calculate UDP checksum */
+	x25_udp_pkt->udp_pkt.udp_checksum = 0;
+
+	x25_udp_pkt->udp_pkt.udp_checksum = 
+		calc_checksum(&data[UDP_OFFSET], udp_length+UDP_OFFSET);
+
+	/* fill in IP length */
+	ip_length = len;
+	temp = (ip_length<<8)|(ip_length>>8);
+	x25_udp_pkt->ip_pkt.total_length = temp;
+  
+	/* swap IP addresses */
+	ip_temp = x25_udp_pkt->ip_pkt.ip_src_address;
+	x25_udp_pkt->ip_pkt.ip_src_address = 
+				x25_udp_pkt->ip_pkt.ip_dst_address;
+	x25_udp_pkt->ip_pkt.ip_dst_address = ip_temp;
+
+		 
+	/* fill in IP checksum */
+	x25_udp_pkt->ip_pkt.hdr_checksum = 0;
+	x25_udp_pkt->ip_pkt.hdr_checksum = calc_checksum(data, sizeof(ip_pkt_t));
+
+	return len;
+} /* reply_udp */
+
+unsigned short calc_checksum (char *data, int len)
+{
+	unsigned short temp; 
+	unsigned long sum=0;
+	int i;
+
+	for( i = 0; i <len; i+=2 ) {
+		memcpy(&temp,&data[i],2);
+		sum += (unsigned long)temp;
+	}
+
+	while (sum >> 16 ) {
+		sum = (sum & 0xffffUL) + (sum >> 16);
+	}
+
+	temp = (unsigned short)sum;
+	temp = ~temp;
+
+	if( temp == 0 ) 
+		temp = 0xffff;
+
+	return temp;	
+}
+
+/*=============================================================================
+ * Store a UDP management packet for later processing.
+ */
+
+static int store_udp_mgmt_pkt(int udp_type, char udp_pkt_src, sdla_t* card,
+                                netdevice_t *dev, struct sk_buff *skb, int lcn)
+{
+        int udp_pkt_stored = 0;
+
+        if(!card->u.x.udp_pkt_lgth && (skb->len <= MAX_LGTH_UDP_MGNT_PKT)){
+                card->u.x.udp_pkt_lgth = skb->len;
+                card->u.x.udp_type = udp_type;
+                card->u.x.udp_pkt_src = udp_pkt_src;
+                card->u.x.udp_lcn = lcn;
+		card->u.x.udp_dev = dev;
+                memcpy(card->u.x.udp_pkt_data, skb->data, skb->len);
+                card->u.x.timer_int_enabled |= TMR_INT_ENABLED_UDP_PKT;
+                udp_pkt_stored = 1;
+
+        }else{
+                printk(KERN_INFO "%s: ERROR: UDP packet not stored for LCN %d\n", 
+							card->devname,lcn);
+	}
+
+        if(udp_pkt_src == UDP_PKT_FRM_STACK){
+                wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+                wan_dev_kfree_skb(skb, FREE_READ);
+	}
+
+        return(udp_pkt_stored);
+}
+
+
+
+/*=============================================================================
+ * Initial the ppp_private_area structure.
+ */
+static void init_x25_channel_struct( x25_channel_t *chan )
+{
+	memset(&chan->if_send_stat.if_send_entry,0,sizeof(if_send_stat_t));
+	memset(&chan->rx_intr_stat.rx_intr_no_socket,0,sizeof(rx_intr_stat_t));
+	memset(&chan->pipe_mgmt_stat.UDP_PIPE_mgmt_kmalloc_err,0,sizeof(pipe_mgmt_stat_t));
+}
+
+/*============================================================================
+ * Initialize Global Statistics
+ */
+static void init_global_statistics( sdla_t *card )
+{
+	memset(&card->statistics.isr_entry,0,sizeof(global_stats_t));
+}
+
+
+/*===============================================================
+ * SMP Support
+ * ==============================================================*/
+
+static void S508_S514_lock(sdla_t *card, unsigned long *smp_flags)
+{
+	spin_lock_irqsave(&card->wandev.lock, *smp_flags);
+}
+static void S508_S514_unlock(sdla_t *card, unsigned long *smp_flags)
+{
+	spin_unlock_irqrestore(&card->wandev.lock, *smp_flags);
+}
+
+/*===============================================================
+ * x25_timer_routine
+ *
+ * 	A more efficient polling routine.  Each half a second
+ * 	queue a polling task. We want to do the polling in a 
+ * 	task not timer, because timer runs in interrupt time.
+ *
+ * 	FIXME Polling should be rethinked.
+ *==============================================================*/
+
+static void x25_timer_routine(unsigned long data)
+{
+	sdla_t *card = (sdla_t*)data;
+
+	if (!card->wandev.dev){
+		printk(KERN_INFO "%s: Stopping the X25 Poll Timer: No Dev.\n",
+				card->devname);
+		return;
+	}
+
+	if (card->open_cnt != card->u.x.num_of_ch){
+		printk(KERN_INFO "%s: Stopping the X25 Poll Timer: Interface down.\n",
+				card->devname);
+		return;
+	}
+
+	if (test_bit(PERI_CRIT,&card->wandev.critical)){
+		printk(KERN_INFO "%s: Stopping the X25 Poll Timer: Shutting down.\n",
+				card->devname);
+		return;
+	}
+	
+	if (!test_and_set_bit(POLL_CRIT,&card->wandev.critical)){
+		trigger_x25_poll(card);
+	}
+	
+	card->u.x.x25_timer.expires=jiffies+(HZ>>1);
+	add_timer(&card->u.x.x25_timer);
+	return;
+}
+
+void disable_comm_shutdown(sdla_t *card)
+{
+	TX25Mbox* mbox = card->mbox;
+	int err;
+
+	/* Turn of interrutps */
+	mbox->data[0] = 0;
+	if (card->hw.fwid == SFID_X25_508){
+		mbox->data[1] = card->hw.irq;
+		mbox->data[2] = 2;
+		mbox->cmd.length = 3;
+	}else {
+	 	mbox->cmd.length  = 1;
+	}
+	mbox->cmd.command = X25_SET_INTERRUPT_MODE;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	if (err)
+		printk(KERN_INFO "INTERRUPT OFF FAIED %x\n",err);
+
+	/* Bring down HDLC */
+	mbox->cmd.command = X25_HDLC_LINK_CLOSE;
+	mbox->cmd.length  = 0;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	if (err)
+		printk(KERN_INFO "LINK CLOSED FAILED %x\n",err);
+
+
+	/* Brind down DTR */
+	mbox->data[0] = 0;
+	mbox->data[2] = 0;
+	mbox->data[1] = 0x01;
+	mbox->cmd.length  = 3;
+	mbox->cmd.command = X25_SET_GLOBAL_VARS;
+	err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	if (err)
+		printk(KERN_INFO "DTR DOWN FAILED %x\n",err);
+
+}
 
 /****** End *****************************************************************/
diff -urN linux-2.4.4-pre2/drivers/net/wan/sdladrv.c linux/drivers/net/wan/sdladrv.c
--- linux-2.4.4-pre2/drivers/net/wan/sdladrv.c	Tue Mar  6 19:44:37 2001
+++ linux/drivers/net/wan/sdladrv.c	Mon Dec  3 22:34:50 2001
@@ -6,13 +6,19 @@
 *
 * Author:	Gideon Hack	
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
+* Mar 20, 2001  Nenad Corbic	Added the auto_pci_cfg filed, to support
+*                               the PCISLOT #0. 
+* Apr 04, 2000  Nenad Corbic	Fixed the auto memory detection code.
+*                               The memory test at address 0xC8000.
+* Mar 09, 2000  Nenad Corbic 	Added Gideon's Bug Fix: clear pci
+*                               interrupt flags on initial load.
 * Jun 02, 1999  Gideon Hack     Added support for the S514 adapter.
 *				Updates for Linux 2.2.X kernels.	
 * Sep 17, 1998	Jaspreet Singh	Updates for linux 2.2.X kernels
@@ -84,7 +90,6 @@
 
 #if	defined(_LINUX_)	/****** Linux *******************************/
 
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
@@ -102,7 +107,16 @@
 #define _OUTB(port, byte)	(outb((byte),(port)))
 #define	SYSTEM_TICK		jiffies
 
-#include <linux/init.h>
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4) 
+ #include <linux/init.h>
+#else
+ #include <linux/bios32.h>/* BIOS32, PCI BIOS functions and definitions */
+ #define ioremap vremap
+ #define iounmap vfree
+ extern void * vremap (unsigned long offset, unsigned long size);
+ extern void vfree (void *addr);
+#endif
 
 #elif	defined(_SCO_UNIX_)	/****** SCO Unix ****************************/
 
@@ -190,16 +204,21 @@
 static unsigned check_memregion (void* ptr, unsigned len);
 static unsigned	test_memregion (void* ptr, unsigned len);
 static unsigned short checksum (unsigned char* buf, unsigned len);
+static int init_pci_slot(sdlahw_t *);
+
+static int pci_probe(sdlahw_t *hw);
 
 /****** Global Data **********************************************************
  * Note: All data must be explicitly initialized!!!
  */
 
+#ifdef LINUX_2_4
 static struct pci_device_id sdladrv_pci_tbl[] __initdata = {
 	{ V3_VENDOR_ID, V3_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, },
 	{ }			/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(pci, sdladrv_pci_tbl);
+#endif
 
 /* private data */
 static char modname[]	= "sdladrv";
@@ -298,6 +317,8 @@
 	0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0
 };
 
+static int pci_slot_ar[MAX_S514_CARDS];
+
 /******* Kernel Loadable Module Entry Points ********************************/
 
 /*============================================================================
@@ -314,15 +335,24 @@
 #ifdef MODULE
 int init_module (void)
 #else
-int __init wanpipe_init(void)
+int sdladrv_init(void)
 #endif
 {
+	int i=0;
+
 	printk(KERN_INFO "%s v%u.%u %s\n",
 		fullname, MOD_VERSION, MOD_RELEASE, copyright);
 	exec_idle = calibrate_delay(EXEC_DELAY);
 #ifdef WANDEBUG	
 	printk(KERN_DEBUG "%s: exec_idle = %d\n", modname, exec_idle);
 #endif	
+
+	/* Initialize the PCI Card array, which
+         * will store flags, used to mark 
+         * card initialization state */
+	for (i=0; i<MAX_S514_CARDS; i++)
+		pci_slot_ar[i] = 0xFF;
+
 	return 0;
 }
 
@@ -350,7 +380,9 @@
  *		< 0	error
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4) 
 EXPORT_SYMBOL(sdla_setup);
+#endif
 
 int sdla_setup (sdlahw_t* hw, void* sfm, unsigned len)
 {
@@ -361,7 +393,7 @@
 
 	if (sdla_detect(hw)) {
                 if(hw->type != SDLA_S514)
-                        printk(KERN_ERR "%s: no SDLA card found at port 0x%X\n",
+                        printk(KERN_INFO "%s: no SDLA card found at port 0x%X\n",
                         modname, hw->port);
 		return -EINVAL;
 	}
@@ -410,7 +442,7 @@
 
                 /* Verify IRQ configuration options */
                 if (!get_option_index(irq_opt, hw->irq)) {
-                        printk(KERN_ERR "%s: IRQ %d is illegal!\n",
+                        printk(KERN_INFO "%s: IRQ %d is illegal!\n",
                         	modname, hw->irq);
                       return -EINVAL;
                 } 
@@ -420,7 +452,7 @@
                         hw->pclk = pclk_opt[1];  /* use default */
         
                 else if (!get_option_index(pclk_opt, hw->pclk)) {
-                        printk(KERN_ERR "%s: CPU clock %u is illegal!\n",
+                        printk(KERN_INFO "%s: CPU clock %u is illegal!\n",
 				modname, hw->pclk);
                         return -EINVAL;
                 } 
@@ -431,7 +463,7 @@
                 if (hw->dpmbase == 0) {
                         err = sdla_autodpm(hw);
                         if (err) {
-                                printk(KERN_ERR
+                                printk(KERN_INFO
 				"%s: can't find available memory region!\n",
 					modname);
                                 return err;
@@ -439,13 +471,13 @@
                 }
                 else if (!get_option_index(dpmbase_opt,
 			virt_to_phys(hw->dpmbase))) {
-                        printk(KERN_ERR
+                        printk(KERN_INFO
 				"%s: memory address 0x%lX is illegal!\n",
 				modname, virt_to_phys(hw->dpmbase));
                         return -EINVAL;
                 }               
                 else if (sdla_setdpm(hw)) {
-                        printk(KERN_ERR
+                        printk(KERN_INFO
 			"%s: 8K memory region at 0x%lX is not available!\n",
 				modname, virt_to_phys(hw->dpmbase));
                         return -EINVAL;
@@ -453,13 +485,27 @@
                 printk(KERN_INFO
 			"%s: dual-port memory window is set at 0x%lX.\n",
 				modname, virt_to_phys(hw->dpmbase));
+
+
+		/* If we find memory in 0xE**** Memory region, 
+                 * warn the user to disable the SHADOW RAM.  
+                 * Since memory corruption can occur if SHADOW is
+                 * enabled. This can causes random crashes ! */
+		if (virt_to_phys(hw->dpmbase) >= 0xE0000){
+			printk(KERN_WARNING "\n%s: !!!!!!!!  WARNING !!!!!!!!\n",modname);
+			printk(KERN_WARNING "%s: WANPIPE is using 0x%lX memory region !!!\n",
+						modname, virt_to_phys(hw->dpmbase));
+			printk(KERN_WARNING "         Please disable the SHADOW RAM, otherwise\n");
+			printk(KERN_WARNING "         your system might crash randomly from time to time !\n");
+			printk(KERN_WARNING "%s: !!!!!!!!  WARNING !!!!!!!!\n\n",modname);
+		}
         }
 
 	else {
 		hw->memory = test_memregion((void*)hw->dpmbase, 
 			MAX_SIZEOF_S514_MEMORY);
 		if(hw->memory < (256 * 1024)) {
-			printk(KERN_ERR
+			printk(KERN_INFO
 				"%s: error in testing S514 memory (0x%lX)\n",
 				modname, hw->memory);
 			sdla_down(hw);
@@ -481,7 +527,9 @@
  * Shut down SDLA: disable shared memory access and interrupts, stop CPU, etc.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_down);
+#endif
 
 int sdla_down (sdlahw_t* hw)
 {
@@ -523,9 +571,10 @@
                 *(char *)hw->vector = S514_CPU_HALT;
         	CPU_no = hw->S514_cpu_no[0];
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 		/* disable the PCI IRQ and disable memory access */
                 pci_read_config_dword(hw->pci_dev, PCI_INT_CONFIG, &int_config);
-	        int_config &= (CPU_no == S514_CPU_A) ? ~PCI_DISABLE_IRQ_CPU_A : 			~PCI_DISABLE_IRQ_CPU_B;
+	        int_config &= (CPU_no == S514_CPU_A) ? ~PCI_DISABLE_IRQ_CPU_A :	~PCI_DISABLE_IRQ_CPU_B;
                 pci_write_config_dword(hw->pci_dev, PCI_INT_CONFIG, int_config);
 		read_S514_int_stat(hw, &int_status);
 		S514_intack(hw, int_status);
@@ -535,6 +584,22 @@
 		else
                         pci_write_config_dword(hw->pci_dev, PCI_MAP1_DWORD,
 				PCI_CPU_B_MEM_DISABLE);
+#else
+                /* disable the PCI IRQ and disable memory access */
+             	pcibios_read_config_dword(hw->pci_bus, hw->pci_dev_func,
+			PCI_INT_CONFIG, &int_config);
+	        int_config &= (CPU_no == S514_CPU_A) ? ~PCI_DISABLE_IRQ_CPU_A :	~PCI_DISABLE_IRQ_CPU_B;
+        	pcibios_write_config_dword(hw->pci_bus, hw->pci_dev_func,
+			PCI_INT_CONFIG, int_config);
+                read_S514_int_stat(hw, &int_status);
+                S514_intack(hw, int_status);
+     		// disable PCI memory access
+		if(CPU_no == S514_CPU_A)
+	     		pcibios_write_config_dword(hw->pci_bus,hw->pci_dev_func,
+				PCI_MAP0_DWORD, PCI_CPU_A_MEM_DISABLE);
+		else
+                        pcibios_write_config_dword(hw->pci_bus,hw->pci_dev_func,							PCI_MAP1_DWORD, PCI_CPU_B_MEM_DISABLE);
+#endif
 
 		/* free up the allocated virtual memory */
  		iounmap((void *)hw->dpmbase);
@@ -552,7 +617,9 @@
  * Map shared memory window into SDLA address space.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_mapmem);
+#endif
 
 int sdla_mapmem (sdlahw_t* hw, unsigned long addr)
 {
@@ -613,7 +680,9 @@
  * Enable interrupt generation.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_inten);
+#endif
 
 int sdla_inten (sdlahw_t* hw)
 {
@@ -669,7 +738,9 @@
  * Disable interrupt generation.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_intde);
+#endif
 
 int sdla_intde (sdlahw_t* hw)
 {
@@ -724,7 +795,9 @@
  * Acknowledge SDLA hardware interrupt.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_intack);
+#endif
 
 int sdla_intack (sdlahw_t* hw)
 {
@@ -774,11 +847,18 @@
  * Acknowledge S514 hardware interrupt.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(S514_intack);
+#endif
 
 void S514_intack (sdlahw_t* hw, u32 int_status)
 {
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
         pci_write_config_dword(hw->pci_dev, PCI_INT_STATUS, int_status);
+#else
+	pcibios_write_config_dword(hw->pci_bus, hw->pci_dev_func,
+                PCI_INT_STATUS, int_status);
+#endif
 }
 
 
@@ -786,11 +866,18 @@
  * Read the S514 hardware interrupt status.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(read_S514_int_stat);
+#endif
 
 void read_S514_int_stat (sdlahw_t* hw, u32* int_status)
 {
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	pci_read_config_dword(hw->pci_dev, PCI_INT_STATUS, int_status);
+#else
+        pcibios_read_config_dword(hw->pci_bus, hw->pci_dev_func, PCI_INT_STATUS,
+		int_status);
+#endif
 }
 
 
@@ -798,7 +885,9 @@
  * Generate an interrupt to adapter's CPU.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_intr);
+#endif
 
 int sdla_intr (sdlahw_t* hw)
 {
@@ -842,7 +931,9 @@
  * o Return number of loops made, or 0 if command timed out.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_exec);
+#endif
 
 int sdla_exec (void* opflag)
 {
@@ -878,7 +969,9 @@
  * interrupt routines are accessing adapter shared memory.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_peek);
+#endif
 
 int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len)
 {
@@ -960,7 +1053,9 @@
  * interrupt routines are accessing adapter shared memory.
  */
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 EXPORT_SYMBOL(sdla_poke);
+#endif
  
 int sdla_poke (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len)
 {
@@ -1124,7 +1219,11 @@
 		return -EINVAL;
 	}
 
-	for (i = opt[0]; i && err; --i) {
+	/* Start testing from 8th position, address
+         * 0xC8000 from the 508 address table. 
+         * We don't want to test A**** addresses, since
+         * they are usually used for Video */
+	for (i = 8; i <= opt[0] && err; i++) {
 		hw->dpmbase = phys_to_virt(opt[i]);
 		err = sdla_setdpm(hw);
 	}
@@ -1176,14 +1275,14 @@
 
 	/* Verify firmware signature */
 	if (strcmp(sfm->signature, SFM_SIGNATURE)) {
-		printk(KERN_ERR "%s: not SDLA firmware!\n",
+		printk(KERN_INFO "%s: not SDLA firmware!\n",
 			modname);
 		return -EINVAL;
 	}
 
 	/* Verify firmware module format version */
 	if (sfm->version != SFM_VERSION) {
-		printk(KERN_ERR
+		printk(KERN_INFO
 			"%s: firmware format %u rejected! Expecting %u.\n",
 			modname, sfm->version, SFM_VERSION);
 		return -EINVAL;
@@ -1193,7 +1292,7 @@
 	if ((len - offsetof(sfm_t, image) != sfm->info.codesize) ||
 		(checksum((void*)&sfm->info,
 		sizeof(sfm_info_t) + sfm->info.codesize) != sfm->checksum)) {
-		printk(KERN_ERR "%s: firmware corrupted!\n", modname);
+		printk(KERN_INFO "%s: firmware corrupted!\n", modname);
 		return -EINVAL;
 	}
 
@@ -1211,19 +1310,18 @@
 	 */
 	for (i = 0;
 	     (i < SFM_MAX_SDLA) && (sfm->info.adapter[i] != hw->type);
-	     ++i)
-	;
+	     ++i);
+	
 	if (i == SFM_MAX_SDLA) {
-		printk(KERN_ERR "%s: firmware is not compatible with S%u!\n",
+		printk(KERN_INFO "%s: firmware is not compatible with S%u!\n",
 			modname, hw->type);
-		;
 		return -EINVAL;
 	}
 
 
 	/* Make sure there is enough on-board memory */
 	if (hw->memory < sfm->info.memsize) {
-		printk(KERN_ERR
+		printk(KERN_INFO
 			"%s: firmware needs %lu bytes of on-board memory!\n",
 			modname, sfm->info.memsize);
 		return -EINVAL;
@@ -1231,7 +1329,7 @@
 
 	/* Move code onto adapter */
 	if (sdla_poke(hw, sfm->info.codeoffs, sfm->image, sfm->info.codesize)) {
-		printk(KERN_ERR "%s: failed to load code segment!\n",
+		printk(KERN_INFO "%s: failed to load code segment!\n",
 			modname);
 		return -EIO;
 	}
@@ -1239,14 +1337,14 @@
 	/* Prepare boot-time configuration data and kick-off CPU */
 	sdla_bootcfg(hw, &sfm->info);
 	if (sdla_start(hw, sfm->info.startoffs)) {
-		printk(KERN_ERR "%s: Damn... Adapter won't start!\n",
+		printk(KERN_INFO "%s: Damn... Adapter won't start!\n",
 			modname);
 		return -EIO;
 	}
 
 	/* position DPM window over the mailbox and enable interrupts */
         if (sdla_mapmem(hw, sfm->info.winoffs) || sdla_inten(hw)) {
-		printk(KERN_ERR "%s: adapter hardware failure!\n",
+		printk(KERN_INFO "%s: adapter hardware failure!\n",
 			modname);
 		return -EIO;
 	}
@@ -1858,22 +1956,30 @@
  */
 static int detect_s514 (sdlahw_t* hw)
 {
-	unsigned char CPU_no, slot_no;
+	unsigned char CPU_no, slot_no, auto_slot_cfg;
 	int number_S514_cards = 0;
 	u32 S514_mem_base_addr = 0;
 	u32 ut_u32;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct pci_dev *pci_dev;
+#else
+	u8 ut_u8;
+#endif
 
 
 #ifdef CONFIG_PCI
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
         if(!pci_present())
+#else
+	if(!pcibios_present())
+#endif
         {
-                printk(KERN_ERR "%s: PCI BIOS not present!\n", modname);
+                printk(KERN_INFO "%s: PCI BIOS not present!\n", modname);
                 return 0;
         }
 #else
-        printk(KERN_ERR "%s: Linux not compiled for PCI usage!\n", modname);
+        printk(KERN_INFO "%s: Linux not compiled for PCI usage!\n", modname);
         return 0;
 #endif
 
@@ -1883,10 +1989,17 @@
 	*/
 	CPU_no = hw->S514_cpu_no[0];
 	slot_no = hw->S514_slot_no;
-	
-	printk(KERN_INFO "%s: detecting S514 card, CPU %c, slot #%d\n",
-		modname, CPU_no, slot_no);
+	auto_slot_cfg = hw->auto_pci_cfg;
 
+	if (auto_slot_cfg){
+		printk(KERN_INFO "%s: srch... S514 card, CPU %c, Slot=Auto\n",
+		modname, CPU_no);
+
+	}else{
+		printk(KERN_INFO "%s: srch... S514 card, CPU %c, Slot #%d\n",
+		modname, CPU_no, slot_no);
+	}
+	
 	/* check to see that CPU A or B has been selected in 'router.conf' */
 	switch(CPU_no) {
 		case S514_CPU_A:
@@ -1894,9 +2007,9 @@
 			break;
 	
 		default:
-			printk(KERN_ERR "%s: S514 CPU definition invalid.\n", 
+			printk(KERN_INFO "%s: S514 CPU definition invalid.\n", 
 				modname);
-			printk(KERN_ERR "Must be 'A' or 'B'\n");
+			printk(KERN_INFO "Must be 'A' or 'B'\n");
 			return 0;
 	}
 
@@ -1904,50 +2017,110 @@
 	if(!number_S514_cards)
 		return 0;
 
-	/* we are using a single S514 adapter with a slot of 0 so re-read the */	/* location of this adapter */
-	if((number_S514_cards == 1) && !slot_no) {	
+	/* we are using a single S514 adapter with a slot of 0 so re-read the */	
+	/* location of this adapter */
+	if((number_S514_cards == 1) && auto_slot_cfg) {	
         	number_S514_cards = find_s514_adapter(hw, 1);
 		if(!number_S514_cards) {
-			printk(KERN_ERR "%s: Error finding PCI card\n",
+			printk(KERN_INFO "%s: Error finding PCI card\n",
 				modname);
 			return 0;
 		}
 	}
 
+      #if defined(LINUX_2_4)
 	pci_dev = hw->pci_dev;
 	/* read the physical memory base address */
 	S514_mem_base_addr = (CPU_no == S514_CPU_A) ? 
 		(pci_dev->resource[1].start) :
 		(pci_dev->resource[2].start);
+	
+      #elif defined (LINUX_2_1)
+	pci_dev = hw->pci_dev;
+	/* read the physical memory base address */
+	S514_mem_base_addr = (CPU_no == S514_CPU_A) ? 
+		(pci_dev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK) :
+		(pci_dev->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK);
+	
+      #else
+	pcibios_read_config_dword(hw->pci_bus, hw->pci_dev_func,
+		(CPU_no == S514_CPU_A) ? PCI_MEM_BASE0_DWORD :
+		PCI_MEM_BASE1_DWORD, &S514_mem_base_addr);
+      #endif
 
 	printk(KERN_INFO "%s: S514 PCI memory at 0x%X\n",
 		modname, S514_mem_base_addr);
 	if(!S514_mem_base_addr) {
 		if(CPU_no == S514_CPU_B)
-			printk(KERN_ERR "%s: CPU #B not present on the card\n", 				modname);
+			printk(KERN_INFO "%s: CPU #B not present on the card\n", 				modname);
 		else
-			printk(KERN_ERR "%s: No PCI memory allocated to card\n",				modname);
+			printk(KERN_INFO "%s: No PCI memory allocated to card\n",				modname);
 		return 0;
 	}
 
 	/* enable the PCI memory */
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	pci_read_config_dword(pci_dev, 
 		(CPU_no == S514_CPU_A) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD,
 		&ut_u32);
 	pci_write_config_dword(pci_dev,
 		(CPU_no == S514_CPU_A) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD,
 		(ut_u32 | PCI_MEMORY_ENABLE));
+#else
+        pcibios_read_config_dword(hw->pci_bus, hw->pci_dev_func,
+		(CPU_no == S514_CPU_A) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD,
+		&ut_u32); 
+        pcibios_write_config_dword(hw->pci_bus, hw->pci_dev_func,
+		(CPU_no == S514_CPU_A) ? PCI_MAP0_DWORD : PCI_MAP1_DWORD,
+		(ut_u32 | PCI_MEMORY_ENABLE));
+#endif
 
 	/* check the IRQ allocated and enable IRQ usage */
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	if(!(hw->irq = pci_dev->irq)) {
-		printk(KERN_ERR "%s: IRQ not allocated to S514 adapter\n",
+		printk(KERN_INFO "%s: IRQ not allocated to S514 adapter\n",
 			modname);
                 return 0;
 	}
+
+	/* BUG FIX : Mar 6 2000
+ 	 * On a initial loading of the card, we must check
+         * and clear PCI interrupt bits, due to a reset
+         * problem on some other boards.  i.e. An interrupt
+         * might be pending, even after system bootup, 
+         * in which case, when starting wanrouter the machine
+         * would crash. 
+	 */
+	if (init_pci_slot(hw))
+		return 0;
+
         pci_read_config_dword(pci_dev, PCI_INT_CONFIG, &ut_u32);
         ut_u32 |= (CPU_no == S514_CPU_A) ?
                 PCI_ENABLE_IRQ_CPU_A : PCI_ENABLE_IRQ_CPU_B;
         pci_write_config_dword(pci_dev, PCI_INT_CONFIG, ut_u32);
+#else
+	/* the INTPIN must not be 0 - if it is, then the S514 adapter is not */
+	/* configured for IRQ usage */
+	pcibios_read_config_byte(hw->pci_bus, hw->pci_dev_func,
+		PCI_INT_PIN_BYTE, &ut_u8);
+        if(!ut_u8) {
+                printk(KERN_INFO "%s: invalid setting for INTPIN on S514 card\n", modname);
+                printk(KERN_INFO "Please contact your Sangoma representative\n");
+                return 0;
+        }
+        pcibios_read_config_byte(hw->pci_bus, hw->pci_dev_func,
+		PCI_INT_LINE_BYTE, (unsigned char *)&hw->irq);
+        if(hw->irq == PCI_IRQ_NOT_ALLOCATED) {
+                printk(KERN_INFO "%s: IRQ not allocated to S514 adapter\n",
+			modname);
+                return 0;
+        }
+	pcibios_read_config_dword(hw->pci_bus, hw->pci_dev_func, PCI_INT_CONFIG, &ut_u32);
+	ut_u32 |= (CPU_no == S514_CPU_A) ?
+		PCI_ENABLE_IRQ_CPU_A : PCI_ENABLE_IRQ_CPU_B;
+        pcibios_write_config_dword(hw->pci_bus, hw->pci_dev_func, 
+		PCI_INT_CONFIG, ut_u32);
+#endif
 
 	printk(KERN_INFO "%s: IRQ %d allocated to the S514 card\n",
 		modname, hw->irq);
@@ -1961,7 +2134,7 @@
 		(unsigned long)16);
      
         if(!hw->dpmbase || !hw->vector) {
-		printk(KERN_ERR "%s: PCI virtual memory allocation failed\n",
+		printk(KERN_INFO "%s: PCI virtual memory allocation failed\n",
 			modname);
                 return 0;
 	}
@@ -1983,49 +2156,99 @@
 	char S514_found_in_slot = 0;
         u16 PCI_subsys_vendor;
 
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
         struct pci_dev *pci_dev = NULL;
+#else
+        int pci_index;
+#endif
  
        slot_no = hw->S514_slot_no;
   
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	while ((pci_dev = pci_find_device(V3_VENDOR_ID, V3_DEVICE_ID, pci_dev))
         	!= NULL) {
-		if (pci_enable_device(pci_dev))
-			continue;
-		PCI_subsys_vendor = pci_dev->subsystem_vendor;
-                if(PCI_subsys_vendor != SANGOMA_SUBSYS_VENDOR)
+                
+		pci_read_config_word(pci_dev, PCI_SUBSYS_VENDOR_WORD,
+                        &PCI_subsys_vendor);
+                
+		if(PCI_subsys_vendor != SANGOMA_SUBSYS_VENDOR)
                 	continue;
-        	hw->pci_dev = pci_dev;
+        	
+		hw->pci_dev = pci_dev;
+		
 		if(find_first_S514_card)
 			return(1);
+		
                 number_S514_cards ++;
-                printk(KERN_INFO
+                
+		printk(KERN_INFO
 			"%s: S514 card found, slot #%d (devfn 0x%X)\n",
                         modname, ((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK),
 			pci_dev->devfn);
-                if(slot_no && (((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK) ==
-                        slot_no)) {
+		
+		if (hw->auto_pci_cfg){
+			hw->S514_slot_no = ((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK);
+			slot_no = hw->S514_slot_no;
+			
+		}else if (((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK) == slot_no){
+                        S514_found_in_slot = 1;
+                        break;
+                }
+        }
+
+#else
+	//LINUX VERSION 2.0.X 
+        for (pci_index = 0; pci_index < MAX_S514_CARDS; pci_index ++) {
+                if (pcibios_find_device(V3_VENDOR_ID, V3_DEVICE_ID, pci_index,
+                        &hw->pci_bus, &hw->pci_dev_func)!=PCIBIOS_SUCCESSFUL) {
+                                break;
+                }
+                
+		pcibios_read_config_word(hw->pci_bus, hw->pci_dev_func,
+                        PCI_SUBSYS_VENDOR_WORD, &PCI_subsys_vendor);
+                
+		if (PCI_subsys_vendor != SANGOMA_SUBSYS_VENDOR)
+                        continue;
+                
+		if (find_first_S514_card)
+                        return(1);
+           	
+		number_S514_cards ++;
+		
+                printk(KERN_INFO "%s: S514 card found, bus #%d, slot #%d\n",
+                        modname, hw->pci_bus,
+                        ((hw->pci_dev_func >> 3) & PCI_DEV_SLOT_MASK));
+
+		if (hw->auto_pci_cfg){
+			hw->S514_slot_no = ((hw->pci_dev_func >> 3) & PCI_DEV_SLOT_MASK)
+			slot_no = hw->S514_slot_no;
+				
+		}else if (((hw->pci_dev_func >> 3) & PCI_DEV_SLOT_MASK) == slot_no) {
                         S514_found_in_slot = 1;
                         break;
                 }
         }
+#endif
 
 	/* if no S514 adapter has been found, then exit */
-        if(!number_S514_cards) {
-                printk(KERN_ERR "%s: no S514 adapters found\n", modname);
+        if (!number_S514_cards) {
+                printk(KERN_INFO "%s: Error, no S514 adapters found\n", modname);
                 return 0;
         }
         /* if more than one S514 card has been found, then the user must have */        /* defined a slot number so that the correct adapter is used */
-        else if((number_S514_cards > 1) && !slot_no) {
-                printk(KERN_ERR "%s: More than one S514 adapter found\n",
-                        modname);
-                printk(KERN_ERR "Define a PCI slot number for this adapter\n");
+        else if ((number_S514_cards > 1) && hw->auto_pci_cfg) {
+                printk(KERN_INFO "%s: Error, PCI Slot autodetect Failed! \n"
+				 "%s:        More than one S514 adapter found.\n"
+				 "%s:        Disable the Autodetect feature and supply\n"
+				 "%s:        the PCISLOT numbers for each card.\n",
+                        modname,modname,modname,modname);
                 return 0;
         }
         /* if the user has specified a slot number and the S514 adapter has */
         /* not been found in that slot, then exit */
-        else if (slot_no && !S514_found_in_slot) {
-                printk(KERN_ERR
-			"%s: S514 card not found in specified slot #%d\n",
+        else if (!hw->auto_pci_cfg && !S514_found_in_slot) {
+                printk(KERN_INFO
+			"%s: Error, S514 card not found in specified slot #%d\n",
                         modname, slot_no);
                 return 0;
         }
@@ -2138,5 +2361,170 @@
 	return crc;
 }
 
+static int init_pci_slot(sdlahw_t *hw)
+{
+
+	u32 int_status;
+	int volatile found=0;
+	int i=0;
+
+	/* Check if this is a very first load for a specific
+         * pci card. If it is, clear the interrput bits, and
+         * set the flag indicating that this card was initialized.
+	 */
+	
+	for (i=0; (i<MAX_S514_CARDS) && !found; i++){
+		if (pci_slot_ar[i] == hw->S514_slot_no){
+			found=1;
+			break;
+		}
+		if (pci_slot_ar[i] == 0xFF){
+			break;
+		}
+	}
+
+	if (!found){
+		read_S514_int_stat(hw,&int_status);
+		S514_intack(hw,int_status);
+		if (i == MAX_S514_CARDS){
+			printk(KERN_INFO "%s: Critical Error !!!\n",modname);
+			printk(KERN_INFO 
+				"%s: Number of Sangoma PCI cards exceeded maximum limit.\n",
+					modname);
+			printk(KERN_INFO "Please contact Sangoma Technologies\n");
+			return 1;
+		}
+		pci_slot_ar[i] = hw->S514_slot_no;
+	}
+	return 0;
+}
+
+static int pci_probe(sdlahw_t *hw)
+{
+
+        unsigned char slot_no;
+        int number_S514_cards = 0;
+        u16 PCI_subsys_vendor;
+	u16 PCI_card_type;
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+        struct pci_dev *pci_dev = NULL;
+	struct pci_bus *bus = NULL;
+#else
+        int pci_index;
+	u8 irq;
+#endif
+ 
+       slot_no = 0;
+  
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	while ((pci_dev = pci_find_device(V3_VENDOR_ID, V3_DEVICE_ID, pci_dev))
+        	!= NULL) {
+		
+                pci_read_config_word(pci_dev, PCI_SUBSYS_VENDOR_WORD,
+                        &PCI_subsys_vendor);
+		
+                if(PCI_subsys_vendor != SANGOMA_SUBSYS_VENDOR)
+                	continue;
+
+		pci_read_config_word(pci_dev, PCI_CARD_TYPE,
+                        &PCI_card_type);
+	
+		bus = pci_dev->bus;
+		
+		/* A dual cpu card can support up to 4 physical connections,
+		 * where a single cpu card can support up to 2 physical
+		 * connections.  The FT1 card can only support a single 
+		 * connection, however we cannot distinguish between a Single
+		 * CPU card and an FT1 card. */
+		if (PCI_card_type == S514_DUAL_CPU){
+                	number_S514_cards += 4;
+			 printk(KERN_INFO
+				"wanpipe: S514-PCI card found, cpu(s) 2, bus #%d, slot #%d, irq #%d\n",
+                        	bus->number,((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK),
+				pci_dev->irq);
+		}else{
+			number_S514_cards += 2;
+			printk(KERN_INFO
+				"wanpipe: S514-PCI card found, cpu(s) 1, bus #%d, slot #%d, irq #%d\n",
+                        	bus->number,((pci_dev->devfn >> 3) & PCI_DEV_SLOT_MASK),
+				pci_dev->irq);
+		}
+        }
+
+#else
+        for (pci_index = 0; pci_index < MAX_S514_CARDS; pci_index ++) {
+		
+                if(pcibios_find_device(V3_VENDOR_ID, V3_DEVICE_ID, pci_index,
+                        &hw->pci_bus, &hw->pci_dev_func)!=PCIBIOS_SUCCESSFUL) {
+                                break;
+                }
+                pcibios_read_config_word(hw->pci_bus, hw->pci_dev_func,
+                        PCI_SUBSYS_VENDOR_WORD, &PCI_subsys_vendor);
+                if(PCI_subsys_vendor != SANGOMA_SUBSYS_VENDOR)
+                        continue;
+		
+		pcibios_read_config_word(hw->pci_bus,hw->pci_dev_func,PCI_CARD_TYPE,
+                        &PCI_card_type);
+
+		pcibios_read_config_byte(hw->pci_bus, hw->pci_dev_func,
+				PCI_INT_LINE_BYTE, &irq);
+
+		/* A dual cpu card can support up to 4 physical connections,
+		 * where a single cpu card can support up to 2 physical
+		 * connections.  The FT1 card can only support a single 
+		 * connection, however we cannot distinguish between a Single
+		 * CPU card and an FT1 card. */
+		if (PCI_card_type == S514_DUAL_CPU){
+                	number_S514_cards += 4;
+			 printk(KERN_INFO "%s: S514-PCI card found, cpu(s) 2, bus #%d, slot #%d, irq #%d\n",
+                        	modname, hw->pci_bus,
+                        	((hw->pci_dev_func >> 3) & PCI_DEV_SLOT_MASK),irq);
+		}else{
+			printk(KERN_INFO "%s: S514-PCI card found, cpu(s) 1, bus #%d, slot #%d, irq #%d\n",
+                        	modname, hw->pci_bus,
+                        	((hw->pci_dev_func >> 3) & PCI_DEV_SLOT_MASK),irq);
+			number_S514_cards += 2;
+		}
+        }
+#endif
+
+	return number_S514_cards;
+
+}
+
+
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+EXPORT_SYMBOL(wanpipe_hw_probe);
+#endif
+
+unsigned wanpipe_hw_probe(void)
+{
+	sdlahw_t hw;
+	unsigned* opt = s508_port_options; 
+	unsigned cardno=0;
+	int i;
+	
+	memset(&hw, 0, sizeof(hw));
+	
+	for (i = 1; i <= opt[0]; i++) {
+		if (detect_s508(opt[i])){
+			/* S508 card can support up to two physical links */
+			cardno+=2;
+			printk(KERN_INFO "wanpipe: S508-ISA card found, port 0x%x\n",opt[i]);
+		}
+	}
+
+      #ifdef CONFIG_PCI
+	hw.S514_slot_no = 0;
+	cardno += pci_probe(&hw);
+      #else
+	printk(KERN_INFO "wanpipe: Warning, Kernel not compiled for PCI support!\n");
+	printk(KERN_INFO "wanpipe: PCI Hardware Probe Failed!\n");
+      #endif
+
+	return cardno;
+}
 
 /****** End *****************************************************************/
diff -urN linux-2.4.4-pre2/drivers/net/wan/sdlamain.c linux/drivers/net/wan/sdlamain.c
--- linux-2.4.4-pre2/drivers/net/wan/sdlamain.c	Tue Mar  6 19:44:36 2001
+++ linux/drivers/net/wan/sdlamain.c	Mon Dec  3 22:34:50 2001
@@ -1,16 +1,28 @@
-/*****************************************************************************
+/****************************************************************************
 * sdlamain.c	WANPIPE(tm) Multiprotocol WAN Link Driver.  Main module.
 *
 * Author:	Nenad Corbic	<ncorbic@sangoma.com>
 *		Gideon Hack	
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
+* Dec 22, 2000  Nenad Corbic	Updated for 2.4.X kernels.
+* 				Removed the polling routine.
+* Nov 13, 2000  Nenad Corbic	Added hw probing on module load and dynamic
+* 				device allocation. 
+* Nov 7,  2000  Nenad Corbic	Fixed the Multi-Port PPP for kernels
+*                               2.2.16 and above.
+* Aug 2,  2000  Nenad Corbic	Block the Multi-Port PPP from running on
+*  			        kernels 2.2.16 or greater.  The SyncPPP 
+*  			        has changed.
+* Jul 25, 2000  Nenad Corbic	Updated the Piggiback support for MultPPPP.
+* Jul 13, 2000	Nenad Corbic	Added Multi-PPP support.
+* Feb 02, 2000  Nenad Corbic    Fixed up piggyback probing and selection.
 * Sep 23, 1999  Nenad Corbic    Added support for SMP
 * Sep 13, 1999  Nenad Corbic	Each port is treated as a separate device.
 * Jun 02, 1999  Gideon Hack     Added support for the S514 adapter.
@@ -39,17 +51,125 @@
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/module.h>	/* support for loadable modules */
 #include <linux/ioport.h>	/* request_region(), release_region() */
-#include <linux/tqueue.h>	/* for kernel task queues */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
-#include <asm/uaccess.h>	/* kernel <-> user copy */
+
+#include <linux/in.h>
 #include <asm/io.h>		/* phys_to_virt() */
 #include <linux/pci.h>
 #include <linux/sdlapci.h>
+#include <linux/if_wanpipe_common.h>
+
+#if defined(LINUX_2_4)
+
+ #include <asm/uaccess.h>	/* kernel <-> user copy */
+ #include <linux/inetdevice.h>
+ #define netdevice_t struct net_device 
+
+#elif defined(LINUX_2_1)
+
+ #include <asm/uaccess.h>	/* kernel <-> user copy */
+ #include <linux/inetdevice.h>
+ #define netdevice_t struct device 
+
+#else
+
+ #include <asm/segment.h>
+ #define devinet_ioctl(x,y) dev_ioctl(x,y)
+ #define netdevice_t struct device 
+ #define test_and_set_bit set_bit
+ typedef unsigned long mm_segment_t; 
+#endif
+
+#include <linux/ip.h>
+#include <net/route.h>
+ 
+#define KMEM_SAFETYZONE 8
+
+
+#ifndef CONFIG_WANPIPE_FR
+  #define wpf_init(a,b) (-EPROTONOSUPPORT) 
+#endif
+
+#ifndef CONFIG_WANPIPE_CHDLC
+ #define wpc_init(a,b) (-EPROTONOSUPPORT) 
+#endif
+
+#ifndef CONFIG_WANPIPE_X25
+ #define wpx_init(a,b) (-EPROTONOSUPPORT) 
+#endif
+ 
+#ifndef CONFIG_WANPIPE_PPP
+ #define wpp_init(a,b) (-EPROTONOSUPPORT) 
+#endif
+
+#ifndef CONFIG_WANPIPE_MULTPPP 
+ #define wsppp_init(a,b) (-EPROTONOSUPPORT) 
+#endif
+ 
+ 
+/***********FOR DEBUGGING PURPOSES*********************************************
+static void * dbg_kmalloc(unsigned int size, int prio, int line) {
+	int i = 0;
+	void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio);
+	char * c1 = v;	
+	c1 += sizeof(unsigned int);
+	*((unsigned int *)v) = size;
+
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D';
+		c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F';
+		c1 += 8;
+	}
+	c1 += size;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G';
+		c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L';
+		c1 += 8;
+	}
+	v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8;
+	printk(KERN_INFO "line %d  kmalloc(%d,%d) = %p\n",line,size,prio,v);
+	return v;
+}
+static void dbg_kfree(void * v, int line) {
+	unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8));
+	unsigned int size = *sp;
+	char * c1 = ((char *)v) - KMEM_SAFETYZONE*8;
+	int i = 0;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		if (   c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D'
+		    || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') {
+			printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v);
+			printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
+			                c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
+		}
+		c1 += 8;
+	}
+	c1 += size;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		if (   c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G'
+		    || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L'
+		   ) {
+			printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v);
+			printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
+			                c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
+		}
+		c1 += 8;
+	}
+	printk(KERN_INFO "line %d  kfree(%p)\n",line,v);
+	v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8);
+	kfree(v);
+}
+
+#define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__)
+#define kfree(x) dbg_kfree(x,__LINE__)
+******************************************************************************/
+
+
 
 /****** Defines & Macros ****************************************************/
 
@@ -61,7 +181,7 @@
 
 #define	DRV_VERSION	5		/* version number */
 #define	DRV_RELEASE	0		/* release (minor version) number */
-#define	MAX_CARDS	8		/* max number of adapters */
+#define	MAX_CARDS	16		/* max number of adapters */
 
 #ifndef	CONFIG_WANPIPE_CARDS		/* configurable option */
 #define	CONFIG_WANPIPE_CARDS 1
@@ -86,12 +206,16 @@
 
 /* IOCTL handlers */
 static int ioctl_dump	(sdla_t* card, sdla_dump_t* u_dump);
-static int ioctl_exec	(sdla_t* card, sdla_exec_t* u_exec);
+static int ioctl_exec	(sdla_t* card, sdla_exec_t* u_exec, int);
 
 /* Miscellaneous functions */
 STATIC void sdla_isr	(int irq, void* dev_id, struct pt_regs *regs);
-STATIC void sdla_poll	(void* data);
 static void release_hw  (sdla_t *card);
+static void run_wanpipe_tq (unsigned long);
+
+static int check_s508_conflicts (sdla_t* card,wandev_conf_t* conf, int*);
+static int check_s514_conflicts (sdla_t* card,wandev_conf_t* conf, int*);
+
 
 /****** Global Data **********************************************************
  * Note: All data must be explicitly initialized!!!
@@ -100,13 +224,41 @@
 /* private data */
 static char drvname[]	= "wanpipe";
 static char fullname[]	= "WANPIPE(tm) Multiprotocol Driver";
-static char copyright[]	= "(c) 1995-1999 Sangoma Technologies Inc.";
-static int ncards = CONFIG_WANPIPE_CARDS;
-static int active;			/* number of active cards */
-static sdla_t* card_array;	/* adapter data space */
+static char copyright[]	= "(c) 1995-2000 Sangoma Technologies Inc.";
+static int ncards = 0; 
+static sdla_t* card_array = NULL;	/* adapter data space */
+
+/* Wanpipe's own task queue, used for all API's.
+ * All protocol specific tasks will be instered
+ * into "wanpipe_tq_custom" task_queue. 
+
+ * On each rx_interrupt, the whole task queue
+ * (wanpipe_tq_custom) will be queued into 
+ * IMMEDIATE_BH via wanpipe_mark_bh() call. 
+ 
+ * The IMMEDIATE_BH will execute run_wanpipe_tq() 
+ * function, which will execute all pending,
+ * tasks in wanpipe_tq_custom queue */
+
+#ifdef LINUX_2_4
+DECLARE_TASK_QUEUE(wanpipe_tq_custom);
+static struct tq_struct wanpipe_tq_task = 
+{
+	routine: (void (*)(void *)) run_wanpipe_tq,
+	data: &wanpipe_tq_custom
+};
+#else
+static struct tq_struct *wanpipe_tq_custom = NULL;
+static struct tq_struct wanpipe_tq_task = 
+{
+	NULL,
+	0,
+	(void *)(void *) run_wanpipe_tq,
+	&wanpipe_tq_custom
+};
+#endif
 
-/* Task queue element for creating a 'thread' */
-static struct tq_struct sdla_tq = { routine: sdla_poll };
+static int wanpipe_bh_critical=0;
 
 /******* Kernel Loadable Module Entry Points ********************************/
 
@@ -126,7 +278,7 @@
 #ifdef MODULE
 int init_module (void)
 #else
-int wanpipe_init2(void)
+int wanpipe_init(void)
 #endif
 {
 	int cnt, err = 0;
@@ -134,9 +286,17 @@
 	printk(KERN_INFO "%s v%u.%u %s\n",
 		fullname, DRV_VERSION, DRV_RELEASE, copyright);
 
+	/* Probe for wanpipe cards and return the number found */
+	printk(KERN_INFO "wanpipe: Probing for WANPIPE hardware.\n");
+	ncards = wanpipe_hw_probe();
+	if (ncards){
+		printk(KERN_INFO "wanpipe: Allocating maximum %i devices: wanpipe%i - wanpipe%i.\n",ncards,1,ncards);
+	}else{
+		printk(KERN_INFO "wanpipe: No S514/S508 cards found, unloading modules!\n");
+		return -ENODEV;
+	}
+	
 	/* Verify number of cards and allocate adapter data space */
-	ncards = min(ncards, MAX_CARDS);
-	ncards = max(ncards, 1);
 	card_array = kmalloc(sizeof(sdla_t) * ncards, GFP_KERNEL);
 	if (card_array == NULL)
 		return -ENOMEM;
@@ -159,7 +319,7 @@
 		wandev->ioctl    = &ioctl;
 		err = register_wan_device(wandev);
 		if (err) {
-			printk(KERN_ERR
+			printk(KERN_INFO
 				"%s: %s registration failed with error %d!\n",
 				drvname, card->devname, err);
 			break;
@@ -172,6 +332,7 @@
 		printk(KERN_INFO "IN Init Module: NO Cards registered\n");
 		err = -ENODEV;
 	}
+
 	return err;
 }
 
@@ -185,11 +346,16 @@
 {
 	int i;
 
+	if (!ncards)
+		return;
+		
 	for (i = 0; i < ncards; ++i) {
 		sdla_t* card = &card_array[i];
 		unregister_wan_device(card->devname);
 	}
 	kfree(card_array);
+
+	printk(KERN_INFO "\nwanpipe: WANPIPE Modules Unloaded.\n");
 }
 
 #endif
@@ -218,15 +384,25 @@
 	sdla_t* card;
 	int err = 0;
 	int irq=0;
-	int i;
 
 	/* Sanity checks */
-	if ((wandev == NULL) || (wandev->private == NULL) || (conf == NULL))
+	if ((wandev == NULL) || (wandev->private == NULL) || (conf == NULL)){
+		printk(KERN_INFO 
+		      "%s: Failed Sdlamain Setup wandev %u, card %u, conf %u !\n",
+		      wandev->name,
+		      (unsigned int)wandev,(unsigned int)wandev->private,
+		      (unsigned int)conf); 
 		return -EFAULT;
-		
+	}
+
+	printk(KERN_INFO "%s: Starting WAN Setup\n", wandev->name);
+
 	card = wandev->private;
-	if (wandev->state != WAN_UNCONFIGURED)
+	if (wandev->state != WAN_UNCONFIGURED){
+		printk(KERN_INFO "%s: failed sdlamain setup, busy!\n",
+			wandev->name);
 		return -EBUSY;		/* already configured */
+	}
 
 	printk(KERN_INFO "\nProcessing WAN device %s...\n", wandev->name);
 
@@ -239,84 +415,21 @@
 	wandev->config_id  = conf->config_id;
 
 	if (!conf->data_size || (conf->data == NULL)) {
-		printk(KERN_ERR
+		printk(KERN_INFO
 			"%s: firmware not found in configuration data!\n",
 			wandev->name);
 		return -EINVAL;
 	}
 
-	/* only check I/O port and IRQ if not an S514 adapter */
+	/* Check for resource conflicts and setup the
+	 * card for piggibacking if necessary */
 	if(!conf->S514_CPU_no[0]) {
-
-                if (conf->ioport <= 0) {
-                        printk(KERN_ERR
-			"%s: can't configure without I/O port address!\n",
-			wandev->name);
-                        return -EINVAL;
-                }
-	
-                if (conf->irq <= 0) {
-                        printk(KERN_ERR "%s: can't configure without IRQ!\n",
-			wandev->name);
-                        return -EINVAL;
-                }
-
-		/* Check for already loaded card with the same IO port and IRQ 
-		 * If found, copy its hardware configuration and use its
-		 * resources (i.e. piggybacking)
-		 */
-		if (!card->configured){
-			for (i = 0; i < ncards; i ++) {
-				sdla_t *nxt_card = &card_array[i];
-				if (nxt_card->hw.port == conf->ioport && 
-				    nxt_card != card && 
-				    conf->config_id == WANCONFIG_CHDLC &&
-				    nxt_card->wandev.config_id == WANCONFIG_CHDLC){
-					irq = nxt_card->hw.irq;
-					memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t));
-					nxt_card->next = card;
-					card->next = nxt_card;
-					card->wandev.piggyback = WANOPT_YES;
-				}
-			}
-			
-
-			/* Make sure I/O port region is available */
-			if (check_region(conf->ioport, SDLA_MAXIORANGE) && 
-			    !card->wandev.piggyback) {
-				printk(KERN_ERR
-					"%s: I/O region 0x%X - 0x%X is in use!\n",
-					wandev->name, conf->ioport,
-					conf->ioport + SDLA_MAXIORANGE);
-				return -EINVAL;
-			}
+		if ((err=check_s508_conflicts(card,conf,&irq)) != 0){
+			return err;
 		}
-	}
-
-	/* 
-	For a S514 adapter, check for a possible configuration error in that
-	we are loading an adapter in the same slot as a previously loaded S514
-	card.
-	*/ 
-	else {
-		if (!card->configured){
-			for (i = 0; i < ncards; i ++) {
-				sdla_t* nxt_card = &card_array[i];
-				if(nxt_card == card)
-					continue;
-				if((nxt_card->hw.type == SDLA_S514) &&
-				   (nxt_card->hw.S514_slot_no == conf->PCI_slot_no) &&
-				   (nxt_card->hw.S514_cpu_no[0] == conf->S514_CPU_no[0])&&
-				   (conf->config_id == WANCONFIG_CHDLC)&&
-				   (nxt_card->wandev.config_id == WANCONFIG_CHDLC)){
-
-					irq = nxt_card->hw.irq;
-					memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t));
-					nxt_card->next = card;
-					card->next = nxt_card;
-					card->wandev.piggyback = WANOPT_YES;
-				}
-			}
+	}else {
+		if ((err=check_s514_conflicts(card,conf,&irq)) != 0){
+			return err;
 		}
 	}
 
@@ -336,8 +449,15 @@
 
 			card->hw.S514_cpu_no[0] = conf->S514_CPU_no[0];
 			card->hw.S514_slot_no = conf->PCI_slot_no;
-			printk(KERN_INFO "Setting CPU to %c and Slot to %i\n",
-				card->hw.S514_cpu_no[0], card->hw.S514_slot_no);
+			card->hw.auto_pci_cfg = conf->auto_pci_cfg;
+
+			if (card->hw.auto_pci_cfg == WANOPT_YES){
+				printk(KERN_INFO "%s: Setting CPU to %c and Slot to Auto\n",
+				card->devname, card->hw.S514_cpu_no[0]);
+			}else{
+				printk(KERN_INFO "%s: Setting CPU to %c and Slot to %i\n",
+				card->devname, card->hw.S514_cpu_no[0], card->hw.S514_slot_no);
+			}
 
 		}else{
 			/* 508 Card io port and irq initialization */
@@ -347,18 +467,22 @@
 
 
 		/* Compute the virtual address of the card in kernel space */
-		if(conf->maddr)
+		if(conf->maddr){
 			card->hw.dpmbase = phys_to_virt(conf->maddr);
-		else	/* But 0 means NULL */
+		}else{	
 			card->hw.dpmbase = (void *)conf->maddr;
-
+		}
+			
 		card->hw.dpmsize = SDLA_WINDOWSIZE;
+		
 		/* set the adapter type if using an S514 adapter */
 		card->hw.type = (conf->S514_CPU_no[0]) ? SDLA_S514 : conf->hw_opt[0]; 
 		card->hw.pclk = conf->hw_opt[1];
 
 		err = sdla_setup(&card->hw, conf->data, conf->data_size);
 		if (err){
+			printk(KERN_INFO "%s: Hardware setup Failed %i\n",
+					card->devname,err);
 			return err;
 		}
 
@@ -369,10 +493,12 @@
 
 		/* request an interrupt vector - note that interrupts may be shared */
 		/* when using the S514 PCI adapter */
+		
        		if(request_irq(irq, sdla_isr, 
-		      (card->hw.type == SDLA_S514) ? SA_SHIRQ : 0, wandev->name, card)){
+		      (card->hw.type == SDLA_S514) ? SA_SHIRQ : 0, 
+		       wandev->name, card)){
 
-			printk(KERN_ERR "%s: Can't reserve IRQ %d!\n", wandev->name, irq);
+			printk(KERN_INFO "%s: Can't reserve IRQ %d!\n", wandev->name, irq);
 			return -EINVAL;
 		}
 
@@ -384,11 +510,16 @@
 
 	if (!card->configured){
 
-	#ifdef CONFIG_SMP
 		/* Initialize the Spin lock */
-		printk(KERN_INFO "%s: Initializing SMP\n",wandev->name);
-		spin_lock_init(&card->lock);
-	#endif
+	      #if defined(__SMP__) || defined(LINUX_2_4) 
+		printk(KERN_INFO "%s: Initializing for SMP\n",wandev->name);
+	      #endif
+
+		/* Piggiback spin lock has already been initialized,
+		 * in check_s514/s508_conflicts() */
+		if (!card->wandev.piggyback){
+			spin_lock_init(&card->wandev.lock);
+		}
 		
 		/* Intialize WAN device data space */
 		wandev->irq       = irq;
@@ -409,63 +540,65 @@
 
 	/* Protocol-specific initialization */
 	switch (card->hw.fwid) {
-#ifdef	CONFIG_WANPIPE_X25
+
 	case SFID_X25_502:
 	case SFID_X25_508:
+		printk(KERN_INFO "%s: Starting x25 Protocol Init.\n",
+				card->devname);
 		err = wpx_init(card, conf);
 		break;
-#endif
-
-#ifdef	CONFIG_WANPIPE_FR
 	case SFID_FR502:
 	case SFID_FR508:
+		printk(KERN_INFO "%s: Starting Frame Relay Protocol Init.\n",
+				card->devname);
 		err = wpf_init(card, conf);
 		break;
-#endif
-
-#ifdef	CONFIG_WANPIPE_PPP
 	case SFID_PPP502:
 	case SFID_PPP508:
+		printk(KERN_INFO "%s: Starting PPP Protocol Init.\n",
+				card->devname);
 		err = wpp_init(card, conf);
 		break;
-#endif
-
-#ifdef CONFIG_WANPIPE_CHDLC
+		
 	case SFID_CHDLC508:
 	case SFID_CHDLC514:
-//		if (conf->ft1){		
-//			printk(KERN_INFO "%s: Starting FT1 Configurator\n",
-//				card->devname);
-//			err = wpft1_init(card, conf);
-//		}else{
-			err = wpc_init(card, conf);
-//		}
-		break;
-#endif
-
-#ifdef CONFIG_WANPIPE_BSTRM
-	case SFID_BSC502:
-		err = bsc_init(card, conf);
-		break;
-#endif
-
-#ifdef CONFIG_WANPIPE_HDLC
-	case SFID_HDLC508:
-		err = hdlc_init(card, conf);
-		break;
-#endif
+		if (conf->ft1){		
+			printk(KERN_INFO "%s: Starting FT1 CSU/DSU Config Driver.\n",
+				card->devname);
+			err = wpft1_init(card, conf);
+			break;
+			
+		}else if (conf->config_id == WANCONFIG_MPPP){
+			printk(KERN_INFO "%s: Starting Multi-Port PPP Protocol Init.\n",
+					card->devname);
+			err = wsppp_init(card,conf);
+			break;
 
+		}else{
+			printk(KERN_INFO "%s: Starting CHDLC Protocol Init.\n",
+					card->devname);
+			err = wpc_init(card, conf);
+			break;
+		}
 	default:
-		printk(KERN_ERR "%s: this firmware is not supported %X %X!\n",
+		printk(KERN_INFO "%s: Error, Firmware is not supported %X %X!\n",
 			wandev->name,card->hw.fwid,SFID_CHDLC508);
-		err = -EINVAL;
+		err = -EPROTONOSUPPORT;
 	}
 
-
-	if (err){
+	if (err != 0){
+		if (err == -EPROTONOSUPPORT){
+			printk(KERN_INFO 
+				"%s: Error, Protocol selected has not been compiled!\n",
+					card->devname);
+			printk(KERN_INFO 
+				"%s:        Re-configure the kernel and re-build the modules!\n",
+					card->devname);
+		}
+		
 		release_hw(card);
+		wandev->state = WAN_UNCONFIGURED;
 		return err;
-		
 	}
 
 
@@ -473,16 +606,194 @@
         if(card->hw.type != SDLA_S514 && !card->wandev.piggyback)
                 request_region(card->hw.port, card->hw.io_range, wandev->name);
 
-	if (++active == 1) {
-		MOD_INC_USE_COUNT;
-		if (schedule_task(&sdla_tq) == 0)
-			MOD_DEC_USE_COUNT;
+	/* Only use the polling routine for the X25 protocol */
+	
+	card->wandev.critical=0;
+	return 0;
+}
+
+/*================================================================== 
+ * configure_s508_card
+ * 
+ * For a S508 adapter, check for a possible configuration error in that
+ * we are loading an adapter in the same IO port as a previously loaded S508
+ * card.
+ */ 
+
+static int check_s508_conflicts (sdla_t* card,wandev_conf_t* conf, int *irq)
+{
+	unsigned long smp_flags;
+	int i;
+	
+	if (conf->ioport <= 0) {
+		printk(KERN_INFO
+		"%s: can't configure without I/O port address!\n",
+		card->wandev.name);
+		return -EINVAL;
+	}
+
+	if (conf->irq <= 0) {
+		printk(KERN_INFO "%s: can't configure without IRQ!\n",
+		card->wandev.name);
+		return -EINVAL;
 	}
+
+	if (test_bit(0,&card->configured))
+		return 0;
+
+
+	/* Check for already loaded card with the same IO port and IRQ 
+	 * If found, copy its hardware configuration and use its
+	 * resources (i.e. piggybacking)
+	 */
+	
+	for (i = 0; i < ncards; i++) {
+		sdla_t *nxt_card = &card_array[i];
+
+		/* Skip the current card ptr */
+		if (nxt_card == card)	
+			continue;
+
+
+		/* Find a card that is already configured with the
+		 * same IO Port */
+		if ((nxt_card->hw.type == SDLA_S508) &&
+		    (nxt_card->hw.port == conf->ioport) && 
+		    (nxt_card->next == NULL)){
+			
+			/* We found a card the card that has same configuration
+			 * as us. This means, that we must setup this card in 
+			 * piggibacking mode. However, only CHDLC and MPPP protocol
+			 * support this setup */
+		
+			if ((conf->config_id == WANCONFIG_CHDLC || 
+			     conf->config_id == WANCONFIG_MPPP) &&
+			    (nxt_card->wandev.config_id == WANCONFIG_CHDLC || 
+			     nxt_card->wandev.config_id == WANCONFIG_MPPP)){ 
+				
+				*irq = nxt_card->hw.irq;
+				memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t));
+			
+				/* The master could already be running, we must
+				 * set this as a critical area */
+				lock_adapter_irq(&nxt_card->wandev.lock, &smp_flags);
+
+				nxt_card->next = card;
+				card->next = nxt_card;
+
+				card->wandev.piggyback = WANOPT_YES;
+
+				/* We must initialise the piggiback spin lock here
+				 * since isr will try to lock card->next if it
+				 * exists */
+				spin_lock_init(&card->wandev.lock);
+				
+				unlock_adapter_irq(&nxt_card->wandev.lock, &smp_flags);
+				break;
+			}else{
+				/* Trying to run piggibacking with a wrong protocol */
+				printk(KERN_INFO "%s: ERROR: Resource busy, ioport: 0x%x\n"
+						 "%s:        This protocol doesn't support\n"
+						 "%s:        multi-port operation!\n",
+						 card->devname,nxt_card->hw.port,
+						 card->devname,card->devname);
+				return -EEXIST;
+			}
+		}
+	}
+	
+
+	/* Make sure I/O port region is available only if we are the
+	 * master device.  If we are running in piggibacking mode, 
+	 * we will use the resources of the master card */
+	if (check_region(conf->ioport, SDLA_MAXIORANGE) && 
+	    !card->wandev.piggyback) {
+		printk(KERN_INFO
+			"%s: I/O region 0x%X - 0x%X is in use!\n",
+			card->wandev.name, conf->ioport,
+			conf->ioport + SDLA_MAXIORANGE);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*================================================================== 
+ * configure_s514_card
+ * 
+ * For a S514 adapter, check for a possible configuration error in that
+ * we are loading an adapter in the same slot as a previously loaded S514
+ * card.
+ */ 
+
+
+static int check_s514_conflicts(sdla_t* card,wandev_conf_t* conf, int *irq)
+{
+	unsigned long smp_flags;
+	int i;
+	
+	if (test_bit(0,&card->configured))
+		return 0;
+
+	
+	/* Check for already loaded card with the same IO port and IRQ 
+	 * If found, copy its hardware configuration and use its
+	 * resources (i.e. piggybacking)
+	 */
+
+	for (i = 0; i < ncards; i ++) {
+	
+		sdla_t* nxt_card = &card_array[i];
+		if(nxt_card == card)
+			continue;
 		
-	wandev->critical = 0;
+		if((nxt_card->hw.type == SDLA_S514) &&
+		   (nxt_card->hw.S514_slot_no == conf->PCI_slot_no) &&
+		   (nxt_card->hw.S514_cpu_no[0] == conf->S514_CPU_no[0])&&
+		   (nxt_card->next == NULL)){
+
+
+			if ((conf->config_id == WANCONFIG_CHDLC || 
+			     conf->config_id == WANCONFIG_MPPP) &&
+			    (nxt_card->wandev.config_id == WANCONFIG_CHDLC || 
+			     nxt_card->wandev.config_id == WANCONFIG_MPPP)){ 
+				
+				*irq = nxt_card->hw.irq;
+				memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t));
+	
+				/* The master could already be running, we must
+				 * set this as a critical area */
+				lock_adapter_irq(&nxt_card->wandev.lock,&smp_flags);
+				nxt_card->next = card;
+				card->next = nxt_card;
+
+				card->wandev.piggyback = WANOPT_YES;
+
+				/* We must initialise the piggiback spin lock here
+				 * since isr will try to lock card->next if it
+				 * exists */
+				spin_lock_init(&card->wandev.lock);
+
+				unlock_adapter_irq(&nxt_card->wandev.lock,&smp_flags);
+
+			}else{
+				/* Trying to run piggibacking with a wrong protocol */
+				printk(KERN_INFO "%s: ERROR: Resource busy: CPU %c PCISLOT %i\n"
+						 "%s:        This protocol doesn't support\n"
+						 "%s:        multi-port operation!\n",
+						 card->devname,
+						 conf->S514_CPU_no[0],conf->PCI_slot_no,
+						 card->devname,card->devname);
+				return -EEXIST;
+			}
+		}
+	}
+
 	return 0;
 }
 
+
+
 /*============================================================================
  * Shut down WAN link driver. 
  * o shut down adapter hardware
@@ -494,23 +805,39 @@
 static int shutdown (wan_device_t* wandev)
 {
 	sdla_t *card;
-
+	int err=0;
+	
 	/* sanity checks */
-	if ((wandev == NULL) || (wandev->private == NULL))
+	if ((wandev == NULL) || (wandev->private == NULL)){
 		return -EFAULT;
+	}
 		
-	if (wandev->state == WAN_UNCONFIGURED)
+	if (wandev->state == WAN_UNCONFIGURED){
 		return 0;
-		
-	/* If we are in a critical section we lose */
-	if (test_and_set_bit(0, (void*)&wandev->critical))
-		return -EAGAIN;
-		
+	}
+
 	card = wandev->private;
+
+	if (card->tty_opt){
+		if (card->tty_open){
+			printk(KERN_INFO 
+				"%s: Shutdown Failed: TTY is still open\n",
+				  card->devname);
+			return -EBUSY;
+		}
+	}
+	
 	wandev->state = WAN_UNCONFIGURED;
 
-	if (--active == 0)
-		schedule();	/* stop background thread */
+	set_bit(PERI_CRIT,(void*)&wandev->critical);
+	
+	/* In case of piggibacking, make sure that 
+         * we never try to shutdown both devices at the same
+         * time, because they depend on one another */
+	
+	if (card->disable_comm){
+		card->disable_comm(card);
+	}
 
 	/* Release Resources */
 	release_hw(card);
@@ -526,15 +853,17 @@
 			memset(&card->next->hw, 0, sizeof(sdlahw_t));
 		}
 	}
+	
 
-	wandev->critical = 0;
-	return 0;
+	clear_bit(PERI_CRIT,(void*)&wandev->critical);
+	return err;
 }
 
 static void release_hw (sdla_t *card)
 {
 	sdla_t *nxt_card;
 
+	
 	/* Check if next device exists */
 	if (card->next){
 		nxt_card = card->next;
@@ -559,8 +888,9 @@
 				card->next->configured = 0;
 			}
 		}else{
-			printk(KERN_INFO "%s: Device still running\n",
-				nxt_card->devname);
+			printk(KERN_INFO "%s: Device still running %i\n",
+				nxt_card->devname,nxt_card->wandev.state);
+
 			card->configured = 1;
 		}
 	}else{
@@ -569,6 +899,7 @@
        		free_irq(card->wandev.irq, card);
 		card->configured = 0;
 	}
+	return;
 }
 
 
@@ -597,10 +928,7 @@
 		disable_irq(card->hw.irq);
 	}
 
-	if (test_and_set_bit(0, (void*)&wandev->critical)) {
-		if(card->hw.type != SDLA_S514){
-			enable_irq(card->hw.irq);
-		}
+	if (test_bit(SEND_CRIT, (void*)&wandev->critical)) {
 		return -EAGAIN;
 	}
 	
@@ -610,18 +938,12 @@
 		break;
 
 	case WANPIPE_EXEC:
-		err = ioctl_exec(wandev->private, (void*)arg);
+		err = ioctl_exec(wandev->private, (void*)arg, cmd);
 		break;
-
 	default:
 		err = -EINVAL;
 	}
  
-	clear_bit(0, (void*)&wandev->critical);
-        if(card->hw.type != SDLA_S514){
-		enable_irq(card->hw.irq);
-	}
- 
 	return err;
 }
 
@@ -635,7 +957,7 @@
  * o verify user buffer
  * o copy adapter memory image to user buffer
  *
- * Note: when dumping memory, this routine switches current dual-port memory
+ * Note: when dumping memory, this routine switches curent dual-port memory
  *	 vector, so care must be taken to avoid racing conditions.
  */
 static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump)
@@ -643,21 +965,35 @@
 	sdla_dump_t dump;
 	unsigned winsize;
 	unsigned long oldvec;	/* DPM window vector */
-	unsigned long flags;
+	unsigned long smp_flags;
 	int err = 0;
 
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 	if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t)))
 		return -EFAULT;
+      #else
+        if ((u_dump == NULL) ||
+            verify_area(VERIFY_READ, u_dump, sizeof(sdla_dump_t)))
+                return -EFAULT;
+        memcpy_fromfs((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t));
+      #endif
 		
 	if ((dump.magic != WANPIPE_MAGIC) ||
 	    (dump.offset + dump.length > card->hw.memory))
 		return -EINVAL;
 	
+      #ifdef LINUX_2_0
+        if ((dump.ptr == NULL) ||
+            verify_area(VERIFY_WRITE, dump.ptr, dump.length))
+                return -EFAULT;
+      #endif	
+
 	winsize = card->hw.dpmsize;
-	save_flags(flags);
-        cli();				/* >>> critical section start <<< */
 
 	if(card->hw.type != SDLA_S514) {
+
+		lock_adapter_irq(&card->wandev.lock, &smp_flags);
+		
                 oldvec = card->hw.vector;
                 while (dump.length) {
 			/* current offset */				
@@ -671,29 +1007,39 @@
                                 err = -EIO;
                                 break;
                         }
-                        /* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */
-                        sti();  /* Not ideal but tough we have to do this */
+			
+                      #if defined(LINUX_2_1) || defined(LINUX_2_4)
                         if(copy_to_user((void *)dump.ptr,
-                                (u8 *)card->hw.dpmbase + pos, len)) 
-                                return -EFAULT;
-                        cli();
+                                (u8 *)card->hw.dpmbase + pos, len)){ 
+				
+				unlock_adapter_irq(&card->wandev.lock, &smp_flags);
+				return -EFAULT;
+			}
+                      #else
+			memcpy_tofs((void*)(dump.ptr),
+                        	(void*)(card->hw.dpmbase + pos), len);
+                      #endif
                         dump.length     -= len;
                         dump.offset     += len;
                         (char*)dump.ptr += len;
                 }
+		
                 sdla_mapmem(&card->hw, oldvec);/* restore DPM window position */
-        }
-	
-	else {
-		/* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */
-                sti();  /* Not ideal but tough we have to do this */
+		unlock_adapter_irq(&card->wandev.lock, &smp_flags);
+        
+	}else {
+
+	     #if defined(LINUX_2_1) || defined(LINUX_2_4) 
                if(copy_to_user((void *)dump.ptr,
-	                (u8 *)card->hw.dpmbase + dump.offset, dump.length))
-                	return -EFAULT;
-               cli();
+			       (u8 *)card->hw.dpmbase + dump.offset, dump.length)){
+			return -EFAULT;
+		}
+             #else
+                memcpy_tofs((void*)(dump.ptr),
+                (void*)(card->hw.dpmbase + dump.offset), dump.length);
+             #endif
 	}
 
-	restore_flags(flags);		/* >>> critical section end <<< */
 	return err;
 }
 
@@ -703,19 +1049,34 @@
  * o copy request structure to kernel data space
  * o call protocol-specific 'exec' function
  */
-static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec)
+static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec, int cmd)
 {
 	sdla_exec_t exec;
+	int err=0;
 
-	if (card->exec == NULL)
+	if (card->exec == NULL && cmd == WANPIPE_EXEC){
 		return -ENODEV;
-	
+	}
+
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)	
 	if(copy_from_user((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t)))
 		return -EFAULT;
+      #else
+        if ((u_exec == NULL) ||
+            verify_area(VERIFY_READ, u_exec, sizeof(sdla_exec_t)))
+                return -EFAULT;
+        memcpy_fromfs((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t));
+      #endif
 
 	if ((exec.magic != WANPIPE_MAGIC) || (exec.cmd == NULL))
 		return -EINVAL;
-	return card->exec(card, exec.cmd, exec.data);
+
+	switch (cmd) {
+		case WANPIPE_EXEC:	
+			err = card->exec(card, exec.cmd, exec.data);
+			break;
+	}	
+	return err;
 }
 
 /******* Miscellaneous ******************************************************/
@@ -795,11 +1156,20 @@
  				return;
        			}
 
+			spin_lock(&card->wandev.lock);
+			if (card->next){
+				spin_lock(&card->next->wandev.lock);
+			}
+				
 	               	S514_intack(&card->hw, int_status);
- 
        			if (card->isr)
 				card->isr(card);
 
+			if (card->next){
+				spin_unlock(&card->next->wandev.lock);
+			}
+			spin_unlock(&card->wandev.lock);
+
 			/* handle a maximum of two interrupts (one for each */
 			/* CPU on the adapter) before returning */  
 			if((++ IRQ_count) == 2)
@@ -819,17 +1189,19 @@
 			return;
 		}
 
-		/* Use spin lock only for S508 */
-
-#ifdef CONFIG_SMP
-		spin_lock(&card->lock);
-#endif
+		spin_lock(&card->wandev.lock);
+		if (card->next){
+			spin_lock(&card->next->wandev.lock);
+		}
+	
 		sdla_intack(&card->hw);
 		if (card->isr)
 			card->isr(card);
-#ifdef CONFIG_SMP
-		spin_unlock(&card->lock);
-#endif
+		
+		if (card->next){
+			spin_unlock(&card->next->wandev.lock);
+		}
+		spin_unlock(&card->wandev.lock);
 
 	}
                 
@@ -837,33 +1209,6 @@
 }
 
 /*============================================================================
- * SDLA polling routine.
- * This routine simulates kernel thread to perform various housekeeping job.
- *
- * o for each configured device call its poll() routine
- * o if there is at least one active card, then reschedule itself once again
- */
-STATIC void sdla_poll (void* data)
-{
-	int i;
-
-	for (i = 0; i < ncards; ++i) {
-		sdla_t* card = &card_array[i];
-
-		if ((card->wandev.state != WAN_UNCONFIGURED) && card->poll &&
-			!card->wandev.critical) {
-			card->poll(card);
-		}
-	}
-	if (active) {
-		MOD_INC_USE_COUNT;
-		if (schedule_task(&sdla_tq) == 0)	/* Surely not? */
-			MOD_DEC_USE_COUNT;
-	}
-	MOD_DEC_USE_COUNT;
-}
-
-/*============================================================================
  * This routine is called by the protocol-specific modules when network
  * interface is being open.  The only reason we need this, is because we
  * have to call MOD_INC_USE_COUNT, but cannot include 'module.h' where it's
@@ -892,10 +1237,6 @@
  */
 void wanpipe_set_state (sdla_t* card, int state)
 {
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
 	if (card->wandev.state != state) {
 		switch (state) {
 		case WAN_CONNECTED:
@@ -916,7 +1257,187 @@
 		card->wandev.state = state;
 	}
 	card->state_tick = jiffies;
-	restore_flags(flags);
 }
 
-/****** End *****************************************************************/
+sdla_t * wanpipe_find_card (char *name)
+{
+	int cnt;
+	for (cnt = 0; cnt < ncards; ++ cnt) {
+		sdla_t* card = &card_array[cnt];
+		if (!strcmp(card->devname,name))
+			return card;
+	}
+	return NULL;
+}
+sdla_t * wanpipe_find_card_num (int num)
+{
+	if (num < 1 || num > ncards)
+		return NULL;	
+	num--;
+	return &card_array[num];
+}
+
+
+static void run_wanpipe_tq (unsigned long data)
+{
+	task_queue *tq_queue = (task_queue *)data;
+	if (test_and_set_bit(2,(void*)&wanpipe_bh_critical))
+		printk(KERN_INFO "CRITICAL IN RUNNING TASK QUEUE\n");
+	run_task_queue (tq_queue);
+	clear_bit(2,(void*)&wanpipe_bh_critical);
+
+}
+
+void wanpipe_queue_tq (struct tq_struct *bh_pointer)
+{
+	if (test_and_set_bit(1,(void*)&wanpipe_bh_critical))
+		printk(KERN_INFO "CRITICAL IN QUEUING TASK\n");
+
+	queue_task(bh_pointer,&wanpipe_tq_custom);
+	clear_bit(1,(void*)&wanpipe_bh_critical);
+}
+
+void wanpipe_mark_bh (void)
+{
+	if (!test_and_set_bit(0,(void*)&wanpipe_bh_critical)){
+		queue_task(&wanpipe_tq_task,&tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+		clear_bit(0,(void*)&wanpipe_bh_critical);
+	}
+} 
+
+void wakeup_sk_bh (netdevice_t *dev)
+{
+	wanpipe_common_t *chan = dev->priv;
+
+	if (test_bit(0,&chan->common_critical))
+		return;
+	
+	if (chan->sk && chan->tx_timer){
+		chan->tx_timer->expires=jiffies+1;
+		add_timer(chan->tx_timer);
+	}
+}
+
+int change_dev_flags (netdevice_t *dev, unsigned flags)
+{
+	struct ifreq if_info;
+	mm_segment_t fs = get_fs();
+	int err;
+
+	memset(&if_info, 0, sizeof(if_info));
+	strcpy(if_info.ifr_name, dev->name);
+	if_info.ifr_flags = flags;	
+
+	set_fs(get_ds());     /* get user space block */ 
+	err = devinet_ioctl(SIOCSIFFLAGS, &if_info);
+	set_fs(fs);
+
+	return err;
+}
+
+unsigned long get_ip_address (netdevice_t *dev, int option)
+{
+	
+      #ifdef LINUX_2_4
+	struct in_ifaddr *ifaddr;
+	struct in_device *in_dev;
+
+	if ((in_dev = __in_dev_get(dev)) == NULL){
+		return 0;
+	}
+      #elif defined(LINUX_2_1)
+	struct in_ifaddr *ifaddr;
+	struct in_device *in_dev;
+	
+	if ((in_dev = dev->ip_ptr) == NULL){
+		return 0;
+	}
+      #endif
+
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	if ((ifaddr = in_dev->ifa_list)== NULL ){
+		return 0;
+	}
+      #endif
+	
+	switch (option){
+
+	case WAN_LOCAL_IP:
+	      #ifdef LINUX_2_0
+		return dev->pa_addr;
+	      #else	
+		return ifaddr->ifa_local;
+	      #endif	
+		break;
+	
+	case WAN_POINTOPOINT_IP:
+	      #ifdef LINUX_2_0
+		return dev->pa_dstaddr;
+	      #else	
+		return ifaddr->ifa_address;
+	      #endif	
+		break;	
+
+	case WAN_NETMASK_IP:
+	      #ifdef LINUX_2_0
+		return dev->pa_mask;
+	      #else	
+		return ifaddr->ifa_mask;
+	      #endif	
+		break;
+
+	case WAN_BROADCAST_IP:
+	      #ifdef LINUX_2_0
+		return dev->pa_brdaddr;
+	      #else	
+		return ifaddr->ifa_broadcast;
+	      #endif	
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}	
+
+void add_gateway(sdla_t *card, netdevice_t *dev)
+{
+	mm_segment_t oldfs;
+	struct rtentry route;
+	int res;
+
+	memset((char*)&route,0,sizeof(struct rtentry));
+
+	((struct sockaddr_in *)
+		&(route.rt_dst))->sin_addr.s_addr = 0;
+	((struct sockaddr_in *)
+		&(route.rt_dst))->sin_family = AF_INET;
+
+	((struct sockaddr_in *)
+		&(route.rt_genmask))->sin_addr.s_addr = 0;
+	((struct sockaddr_in *) 
+		&(route.rt_genmask)) ->sin_family = AF_INET;
+
+
+	route.rt_flags = 0;  
+	route.rt_dev = dev->name;
+
+	oldfs = get_fs();
+	set_fs(get_ds());
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	res = ip_rt_ioctl(SIOCADDRT,&route);
+      #else
+	res = ip_rt_new(&route);
+      #endif
+	set_fs(oldfs);
+
+	if (res == 0){
+		printk(KERN_INFO "%s: Gateway added for %s\n",
+			card->devname,dev->name);
+	}
+
+	return;
+}
+
+/****** End *********************************************************/
diff -urN linux-2.4.4-pre2/drivers/net/wan/wanpipe_multppp.c linux/drivers/net/wan/wanpipe_multppp.c
--- linux-2.4.4-pre2/drivers/net/wan/wanpipe_multppp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/wan/wanpipe_multppp.c	Mon Dec  3 22:34:50 2001
@@ -0,0 +1,2473 @@
+/*****************************************************************************
+* wanpipe_multppp.c Multi-Port PPP driver module.
+*
+* Authors: 	Nenad Corbic <ncorbic@sangoma.com>
+*
+* Copyright:	(c) 1995-2001 Sangoma Technologies Inc.
+*
+*		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.
+* ============================================================================
+* Dec 15 2000   Updated for 2.4.X kernel
+* Nov 15 2000   Fixed the SyncPPP support for kernels 2.2.16 and higher.
+*   		The pppstruct has changed.
+* Jul 13 2000	Using the kernel Syncppp module on top of RAW Wanpipe CHDLC
+*  		module.
+*****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/kernel.h>	/* printk(), and other useful stuff */
+#include <linux/stddef.h>	/* offsetof(), etc. */
+#include <linux/errno.h>	/* return codes */
+#include <linux/string.h>	/* inline memset(), etc. */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
+#include <linux/wanrouter.h>	/* WAN router definitions */
+#include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
+#include <linux/if_arp.h>	/* ARPHRD_* defines */
+
+#include <linux/in.h>		/* sockaddr_in */
+#include <linux/inet.h>	
+#include <linux/if.h>
+#include <asm/byteorder.h>	/* htons(), etc. */
+#include <linux/sdlapci.h>
+#include <asm/io.h>
+
+#include <linux/sdla_chdlc.h>		/* CHDLC firmware API definitions */
+#include <linux/sdla_asy.h>           	/* CHDLC (async) API definitions */
+
+#include <linux/if_wanpipe_common.h>    /* Socket Driver common area */
+#include <linux/if_wanpipe.h>		
+
+
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+  #include <linux/inetdevice.h>
+  #include <asm/uaccess.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+ #include <net/syncppp.h>
+#else
+ #include "syncppp.h"
+#endif
+
+#else
+  #include <net/route.h>          /* Adding new route entries */
+#endif
+
+/****** Defines & Macros ****************************************************/
+
+#ifdef	_DEBUG_
+#define	STATIC
+#else
+#define	STATIC		static
+#endif
+
+/* reasons for enabling the timer interrupt on the adapter */
+#define TMR_INT_ENABLED_UDP   	0x01
+#define TMR_INT_ENABLED_UPDATE	0x02
+#define TMR_INT_ENABLED_CONFIG  0x04
+ 
+#define	CHDLC_DFLT_DATA_LEN	1500		/* default MTU */
+#define CHDLC_HDR_LEN		1
+
+#define IFF_POINTTOPOINT 0x10
+
+#define CHDLC_API 0x01
+
+#define PORT(x)   (x == 0 ? "PRIMARY" : "SECONDARY" )
+#define MAX_BH_BUFF	10
+
+#define CRC_LENGTH 	2 
+#define PPP_HEADER_LEN 	4
+ 
+/******Data Structures*****************************************************/
+
+/* This structure is placed in the private data area of the device structure.
+ * The card structure used to occupy the private area but now the following 
+ * structure will incorporate the card structure along with CHDLC specific data
+ */
+
+typedef struct chdlc_private_area
+{
+	void *if_ptr;				/* General Pointer used by SPPP */
+	wanpipe_common_t common;
+	sdla_t		*card;
+	int 		TracingEnabled;		/* For enabling Tracing */
+	unsigned long 	curr_trace_addr;	/* Used for Tracing */
+	unsigned long 	start_trace_addr;
+	unsigned long 	end_trace_addr;
+	unsigned long 	base_addr_trace_buffer;
+	unsigned long 	end_addr_trace_buffer;
+	unsigned short 	number_trace_elements;
+	unsigned  	available_buffer_space;
+	unsigned long 	router_start_time;
+	unsigned char 	route_status;
+	unsigned char 	route_removed;
+	unsigned long 	tick_counter;		/* For 5s timeout counter */
+	unsigned long 	router_up_time;
+        u32             IP_address;		/* IP addressing */
+        u32             IP_netmask;
+	unsigned char  mc;			/* Mulitcast support on/off */
+	unsigned short udp_pkt_lgth;		/* udp packet processing */
+	char udp_pkt_src;
+	char udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT];
+	unsigned short timer_int_enabled;
+	char update_comms_stats;		/* updating comms stats */
+
+	//FIXME: add driver stats as per frame relay!
+
+} chdlc_private_area_t;
+
+/* Route Status options */
+#define NO_ROUTE	0x00
+#define ADD_ROUTE	0x01
+#define ROUTE_ADDED	0x02
+#define REMOVE_ROUTE	0x03
+
+
+/* variable for keeping track of enabling/disabling FT1 monitor status */
+static int rCount = 0;
+
+/* variable for tracking how many interfaces to open for WANPIPE on the
+   two ports */
+
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
+
+/****** Function Prototypes *************************************************/
+/* WAN link driver entry points. These are called by the WAN router module. */
+static int update (wan_device_t* wandev);
+static int new_if (wan_device_t* wandev, netdevice_t* dev,
+	wanif_conf_t* conf);
+static int del_if (wan_device_t* wandev, netdevice_t* dev);
+
+/* Network device interface */
+static int if_init   (netdevice_t* dev);
+static int if_open   (netdevice_t* dev);
+static int if_close  (netdevice_t* dev);
+static int if_send (struct sk_buff* skb, netdevice_t* dev);
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats* if_stats (netdevice_t* dev);
+#else
+static struct enet_statistics* if_stats (netdevice_t* dev);
+#endif
+
+#ifdef LINUX_2_4
+static void if_tx_timeout (netdevice_t *dev);
+#endif
+
+/* CHDLC Firmware interface functions */
+static int chdlc_configure 	(sdla_t* card, void* data);
+static int chdlc_comm_enable 	(sdla_t* card);
+static int chdlc_comm_disable 	(sdla_t* card);
+static int chdlc_read_version 	(sdla_t* card, char* str);
+static int chdlc_set_intr_mode 	(sdla_t* card, unsigned mode);
+static int chdlc_send (sdla_t* card, void* data, unsigned len);
+static int chdlc_read_comm_err_stats (sdla_t* card);
+static int chdlc_read_op_stats (sdla_t* card);
+static int config_chdlc (sdla_t *card);
+
+
+/* Miscellaneous CHDLC Functions */
+static int set_chdlc_config (sdla_t* card);
+static void init_chdlc_tx_rx_buff( sdla_t* card, netdevice_t *dev );
+static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb);
+static int process_chdlc_exception(sdla_t *card);
+static int process_global_exception(sdla_t *card);
+static int update_comms_stats(sdla_t* card,
+        chdlc_private_area_t* chdlc_priv_area);
+static void port_set_state (sdla_t *card, int);
+
+/* Interrupt handlers */
+static void wsppp_isr (sdla_t* card);
+static void rx_intr (sdla_t* card);
+static void timer_intr(sdla_t *);
+
+/* Miscellaneous functions */
+static int reply_udp( unsigned char *data, unsigned int mbox_len );
+static int intr_test( sdla_t* card);
+static int udp_pkt_type( struct sk_buff *skb , sdla_t* card);
+static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card,
+                                struct sk_buff *skb, netdevice_t* dev,
+                                chdlc_private_area_t* chdlc_priv_area);
+static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev,  
+				chdlc_private_area_t* chdlc_priv_area);
+static unsigned short calc_checksum (char *, int);
+static void s508_lock (sdla_t *card, unsigned long *smp_flags);
+static void s508_unlock (sdla_t *card, unsigned long *smp_flags);
+static void send_ppp_term_request (netdevice_t*);
+
+
+static int  Intr_test_counter;
+/****** Public Functions ****************************************************/
+
+/*============================================================================
+ * Cisco HDLC protocol initialization routine.
+ *
+ * This routine is called by the main WANPIPE module during setup.  At this
+ * point adapter is completely initialized and firmware is running.
+ *  o read firmware version (to make sure it's alive)
+ *  o configure adapter
+ *  o initialize protocol-specific fields of the adapter data space.
+ *
+ * Return:	0	o.k.
+ *		< 0	failure.
+ */
+int wsppp_init (sdla_t* card, wandev_conf_t* conf)
+{
+	unsigned char port_num;
+	int err;
+	unsigned long max_permitted_baud = 0;
+	SHARED_MEMORY_INFO_STRUCT *flags;
+
+	union
+		{
+		char str[80];
+		} u;
+	volatile CHDLC_MAILBOX_STRUCT* mb;
+	CHDLC_MAILBOX_STRUCT* mb1;
+	unsigned long timeout;
+
+	/* Verify configuration ID */
+	if (conf->config_id != WANCONFIG_MPPP) {
+		printk(KERN_INFO "%s: invalid configuration ID %u!\n",
+				  card->devname, conf->config_id);
+		return -EINVAL;
+	}
+
+	/* Find out which Port to use */
+	if ((conf->comm_port == WANOPT_PRI) || (conf->comm_port == WANOPT_SEC)){
+		if (card->next){
+
+			if (conf->comm_port != card->next->u.c.comm_port){
+				card->u.c.comm_port = conf->comm_port;
+			}else{
+				printk(KERN_ERR "%s: ERROR - %s port used!\n",
+        		        	card->wandev.name, PORT(conf->comm_port));
+				return -EINVAL;
+			}
+		}else{
+			card->u.c.comm_port = conf->comm_port;
+		}
+	}else{
+		printk(KERN_ERR "%s: ERROR - Invalid Port Selected!\n",
+                			card->wandev.name);
+		return -EINVAL;
+	}
+	
+
+	/* Initialize protocol-specific fields */
+	if(card->hw.type != SDLA_S514){
+
+		if (card->u.c.comm_port == WANOPT_PRI){	
+			card->mbox  = (void *) card->hw.dpmbase;
+		}else{
+			card->mbox  = (void *) card->hw.dpmbase + 
+				SEC_BASE_ADDR_MB_STRUCT - PRI_BASE_ADDR_MB_STRUCT;
+		}	
+	}else{ 
+		/* for a S514 adapter, set a pointer to the actual mailbox in the */
+		/* allocated virtual memory area */
+		if (card->u.c.comm_port == WANOPT_PRI){
+			card->mbox = (void *) card->hw.dpmbase + PRI_BASE_ADDR_MB_STRUCT;
+		}else{
+			card->mbox = (void *) card->hw.dpmbase + SEC_BASE_ADDR_MB_STRUCT;
+		}	
+	}
+
+	mb = mb1 = card->mbox;
+
+	if (!card->configured){
+
+		/* The board will place an 'I' in the return code to indicate that it is
+	   	ready to accept commands.  We expect this to be completed in less
+           	than 1 second. */
+
+		timeout = jiffies;
+		while (mb->return_code != 'I')	/* Wait 1s for board to initialize */
+			if ((jiffies - timeout) > 1*HZ) break;
+
+		if (mb->return_code != 'I') {
+			printk(KERN_INFO
+				"%s: Initialization not completed by adapter\n",
+				card->devname);
+			printk(KERN_INFO "Please contact Sangoma representative.\n");
+			return -EIO;
+		}
+	}
+
+	/* Read firmware version.  Note that when adapter initializes, it
+	 * clears the mailbox, so it may appear that the first command was
+	 * executed successfully when in fact it was merely erased. To work
+	 * around this, we execute the first command twice.
+	 */
+
+	if (chdlc_read_version(card, u.str))
+		return -EIO;
+
+	printk(KERN_INFO "%s: Running Raw CHDLC firmware v%s\n" 
+			 "%s: for Multi-Port PPP protocol.\n",
+			card->devname,u.str,card->devname); 
+
+	card->isr			= &wsppp_isr;
+	card->poll			= NULL;
+	card->exec			= NULL;
+	card->wandev.update		= &update;
+ 	card->wandev.new_if		= &new_if;
+	card->wandev.del_if		= &del_if;
+	card->wandev.udp_port   	= conf->udp_port;
+
+	card->wandev.new_if_cnt = 0;
+
+	/* reset the number of times the 'update()' proc has been called */
+	card->u.c.update_call_count = 0;
+	
+	card->wandev.ttl = conf->ttl;
+	card->wandev.interface = conf->interface; 
+
+	if ((card->u.c.comm_port == WANOPT_SEC && conf->interface == WANOPT_V35)&&
+	    card->hw.type != SDLA_S514){
+		printk(KERN_INFO "%s: ERROR - V35 Interface not supported on S508 %s port \n",
+			card->devname, PORT(card->u.c.comm_port));
+		return -EIO;
+	}
+
+
+	card->wandev.clocking = conf->clocking;
+
+	port_num = card->u.c.comm_port;
+
+	/* Setup Port Bps */
+
+	if(card->wandev.clocking) {
+		if((port_num == WANOPT_PRI) || card->u.c.receive_only) {
+			/* For Primary Port 0 */
+               		max_permitted_baud =
+				(card->hw.type == SDLA_S514) ?
+				PRI_MAX_BAUD_RATE_S514 : 
+				PRI_MAX_BAUD_RATE_S508;
+		}
+		else if(port_num == WANOPT_SEC) {
+			/* For Secondary Port 1 */
+                        max_permitted_baud =
+                               (card->hw.type == SDLA_S514) ?
+                                SEC_MAX_BAUD_RATE_S514 :
+                                SEC_MAX_BAUD_RATE_S508;
+                        }
+  
+			if(conf->bps > max_permitted_baud) {
+				conf->bps = max_permitted_baud;
+				printk(KERN_INFO "%s: Baud too high!\n",
+					card->wandev.name);
+ 				printk(KERN_INFO "%s: Baud rate set to %lu bps\n", 
+					card->wandev.name, max_permitted_baud);
+			}
+                             
+			card->wandev.bps = conf->bps;
+	}else{
+        	card->wandev.bps = 0;
+  	}
+
+	/* Setup the Port MTU */
+	if((port_num == WANOPT_PRI) || card->u.c.receive_only) {
+
+		/* For Primary Port 0 */
+		card->wandev.mtu =
+			(conf->mtu >= MIN_LGTH_CHDLC_DATA_CFG) ?
+			min(conf->mtu, PRI_MAX_NO_DATA_BYTES_IN_FRAME) :
+			CHDLC_DFLT_DATA_LEN;
+	} else if(port_num == WANOPT_SEC) { 
+		/* For Secondary Port 1 */
+		card->wandev.mtu =
+			(conf->mtu >= MIN_LGTH_CHDLC_DATA_CFG) ?
+			min(conf->mtu, SEC_MAX_NO_DATA_BYTES_IN_FRAME) :
+			CHDLC_DFLT_DATA_LEN;
+	}
+
+	/* Add on a PPP Header */
+	card->wandev.mtu += PPP_HEADER_LEN;
+
+	/* Set up the interrupt status area */
+	/* Read the CHDLC Configuration and obtain: 
+	 *	Ptr to shared memory infor struct
+         * Use this pointer to calculate the value of card->u.c.flags !
+ 	 */
+	mb1->buffer_length = 0;
+	mb1->command = READ_CHDLC_CONFIGURATION;
+	err = sdla_exec(mb1) ? mb1->return_code : CMD_TIMEOUT;
+	if(err != COMMAND_OK) {
+		clear_bit(1, (void*)&card->wandev.critical);
+
+                if(card->hw.type != SDLA_S514)
+                	enable_irq(card->hw.irq);
+
+		chdlc_error(card, err, mb1);
+		return -EIO;
+	}
+
+	if(card->hw.type == SDLA_S514){
+               	card->u.c.flags = (void *)(card->hw.dpmbase +
+               		(((CHDLC_CONFIGURATION_STRUCT *)mb1->data)->
+			ptr_shared_mem_info_struct));
+        }else{
+                card->u.c.flags = (void *)(card->hw.dpmbase +
+                        (((CHDLC_CONFIGURATION_STRUCT *)mb1->data)->
+			ptr_shared_mem_info_struct % SDLA_WINDOWSIZE));
+	}
+	
+	flags = card->u.c.flags;
+	
+	/* This is for the ports link state */
+	card->wandev.state = WAN_DUALPORT;
+	card->u.c.state = WAN_DISCONNECTED;
+
+
+	if (!card->wandev.piggyback){
+		err = intr_test(card);
+
+		if(err || (Intr_test_counter < MAX_INTR_TEST_COUNTER)) { 
+			printk(KERN_ERR "%s: Interrupt test failed (%i)\n",
+					card->devname, Intr_test_counter);
+			printk(KERN_ERR "%s: Please choose another interrupt\n",
+					card->devname);
+			return  -EIO;
+		}
+			
+		printk(KERN_INFO "%s: Interrupt test passed (%i)\n", 
+				card->devname, Intr_test_counter);
+	}
+
+
+	if (chdlc_set_intr_mode(card, APP_INT_ON_TIMER)){
+		printk (KERN_INFO "%s: Failed to set interrupt triggers!\n",
+				card->devname);
+		return -EIO;	
+        }
+	
+	/* Mask the Timer interrupt */
+	flags->interrupt_info_struct.interrupt_permission &= 
+		~APP_INT_ON_TIMER;
+
+	printk(KERN_INFO "\n");
+
+	return 0;
+}
+
+/******* WAN Device Driver Entry Points *************************************/
+
+/*============================================================================
+ * Update device status & statistics
+ * This procedure is called when updating the PROC file system and returns
+ * various communications statistics. These statistics are accumulated from 3 
+ * different locations:
+ * 	1) The 'if_stats' recorded for the device.
+ * 	2) Communication error statistics on the adapter.
+ *      3) CHDLC operational statistics on the adapter.
+ * The board level statistics are read during a timer interrupt. Note that we 
+ * read the error and operational statistics during consecitive timer ticks so
+ * as to minimize the time that we are inside the interrupt handler.
+ *
+ */
+static int update (wan_device_t* wandev)
+{
+	sdla_t* card = wandev->private;
+ 	netdevice_t* dev;
+        volatile chdlc_private_area_t* chdlc_priv_area;
+        SHARED_MEMORY_INFO_STRUCT *flags;
+	unsigned long timeout;
+
+	/* sanity checks */
+	if((wandev == NULL) || (wandev->private == NULL))
+		return -EFAULT;
+	
+	if(wandev->state == WAN_UNCONFIGURED)
+		return -ENODEV;
+
+	/* more sanity checks */
+        if(!card->u.c.flags)
+                return -ENODEV;
+
+	if((dev=card->wandev.dev) == NULL)
+		return -ENODEV;
+
+	if((chdlc_priv_area=dev->priv) == NULL)
+		return -ENODEV;
+
+      	flags = card->u.c.flags;
+
+       	if(chdlc_priv_area->update_comms_stats){
+		return -EAGAIN;
+	}
+			
+	/* we will need 2 timer interrupts to complete the */
+	/* reading of the statistics */
+	chdlc_priv_area->update_comms_stats = 2;
+       	flags->interrupt_info_struct.interrupt_permission |= APP_INT_ON_TIMER;
+	chdlc_priv_area->timer_int_enabled = TMR_INT_ENABLED_UPDATE;
+  
+	/* wait a maximum of 1 second for the statistics to be updated */ 
+        timeout = jiffies;
+        for(;;) {
+		if(chdlc_priv_area->update_comms_stats == 0)
+			break;
+                if ((jiffies - timeout) > (1 * HZ)){
+    			chdlc_priv_area->update_comms_stats = 0;
+ 			chdlc_priv_area->timer_int_enabled &=
+				~TMR_INT_ENABLED_UPDATE; 
+ 			return -EAGAIN;
+		}
+        }
+
+	return 0;
+}
+
+
+/*============================================================================
+ * Create new logical channel.
+ * This routine is called by the router when ROUTER_IFNEW IOCTL is being
+ * handled.
+ * o parse media- and hardware-specific configuration
+ * o make sure that a new channel can be created
+ * o allocate resources, if necessary
+ * o prepare network device structure for registaration.
+ *
+ * Return:	0	o.k.
+ *		< 0	failure (channel will not be created)
+ */
+static int new_if (wan_device_t* wandev, netdevice_t* pdev, wanif_conf_t* conf)
+{
+
+	struct ppp_device *pppdev = (struct ppp_device *)pdev;
+	netdevice_t *dev=NULL;
+	struct sppp *sp;
+	sdla_t* card = wandev->private;
+	chdlc_private_area_t* chdlc_priv_area;
+	
+	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
+		printk(KERN_INFO "%s: invalid interface name!\n",
+			card->devname);
+		return -EINVAL;
+	}
+		
+	/* allocate and initialize private data */
+	chdlc_priv_area = kmalloc(sizeof(chdlc_private_area_t), GFP_KERNEL);
+	
+	if(chdlc_priv_area == NULL) 
+		return -ENOMEM;
+
+	memset(chdlc_priv_area, 0, sizeof(chdlc_private_area_t));
+
+	chdlc_priv_area->card = card; 
+
+	/* initialize data */
+	strcpy(card->u.c.if_name, conf->name);
+
+	if(card->wandev.new_if_cnt > 0) {
+                kfree(chdlc_priv_area);
+		return -EEXIST;
+	}
+
+	card->wandev.new_if_cnt++;
+
+	chdlc_priv_area->TracingEnabled = 0;
+
+	//We don't need this any more
+	chdlc_priv_area->route_status = NO_ROUTE;
+	chdlc_priv_area->route_removed = 0;
+
+	printk(KERN_INFO "%s: Firmware running in HDLC STREAMING Mode\n",
+		wandev->name);
+	
+	/* Setup wanpipe as a router (WANPIPE) or as an API */
+	if( strcmp(conf->usedby, "WANPIPE") == 0) {
+		printk(KERN_INFO "%s: Driver running in WANPIPE mode!\n",
+			wandev->name);
+		card->u.c.usedby = WANPIPE;
+	} else {
+		printk(KERN_INFO 
+			"%s: API Mode is not supported for SyncPPP!\n",
+			wandev->name);
+		kfree(chdlc_priv_area);
+		return -EINVAL;
+	}
+
+	/* Get Multicast Information */
+	chdlc_priv_area->mc = conf->mc;
+
+
+	chdlc_priv_area->if_ptr = pppdev;
+
+	/* prepare network device data space for registration */
+	
+	/* Attach PPP protocol layer to pppdev
+	 * The sppp_attach() will initilize the dev structure
+         * and setup ppp layer protocols.
+         * All we have to do is to bind in:
+         *        if_open(), if_close(), if_send() and get_stats() functions.
+         */
+	sppp_attach(pppdev);
+      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16)
+	dev = pppdev->dev;
+      #else
+	dev = &pppdev->dev;
+      #endif
+	sp = &pppdev->sppp;
+	
+	/* Enable PPP Debugging */
+	// FIXME Fix this up somehow
+	//sp->pp_flags |= PP_DEBUG; 	
+	sp->pp_flags &= ~PP_CISCO;
+	
+      #ifdef LINUX_2_4
+	strcpy(dev->name,card->u.c.if_name);
+      #else
+	dev->name = (char *)kmalloc(strlen(card->u.c.if_name) + 2, GFP_KERNEL); 
+	sprintf(dev->name, "%s", card->u.c.if_name);
+      #endif
+
+	dev->init = &if_init;
+	dev->priv = chdlc_priv_area;
+	
+	return 0;
+}
+
+
+
+
+/*============================================================================
+ * Delete logical channel.
+ */
+static int del_if (wan_device_t* wandev, netdevice_t* dev)
+{
+	chdlc_private_area_t *chdlc_priv_area = dev->priv;
+	sdla_t *card = chdlc_priv_area->card;
+	unsigned long smp_lock;
+	
+	/* Detach the PPP layer */
+	printk(KERN_INFO "%s: Detaching SyncPPP Module from %s\n",
+			wandev->name,dev->name);
+
+	lock_adapter_irq(&wandev->lock,&smp_lock);
+
+	sppp_detach(dev);
+	chdlc_priv_area->if_ptr=NULL;
+	
+	chdlc_set_intr_mode(card, 0);
+	if (card->u.c.comm_enabled)
+		chdlc_comm_disable(card);
+	unlock_adapter_irq(&wandev->lock,&smp_lock);
+	
+	port_set_state(card, WAN_DISCONNECTED);
+
+	return 0;
+}
+
+
+/****** Network Device Interface ********************************************/
+
+/*============================================================================
+ * Initialize Linux network interface.
+ *
+ * This routine is called only once for each interface, during Linux network
+ * interface registration.  Returning anything but zero will fail interface
+ * registration.
+ */
+static int if_init (netdevice_t* dev)
+	{
+	chdlc_private_area_t* chdlc_priv_area = dev->priv;
+	sdla_t* card = chdlc_priv_area->card;
+	wan_device_t* wandev = &card->wandev;
+      #ifdef LINUX_2_0
+	int i;
+      #endif
+	
+	/* NOTE: Most of the dev initialization was
+         *       done in sppp_attach(), called by new_if() 
+         *       function. All we have to do here is
+         *       to link four major routines below. 
+         */
+
+	/* Initialize device driver entry points */
+	dev->open		= &if_open;
+	dev->stop		= &if_close;
+	dev->hard_start_xmit	= &if_send;
+	dev->get_stats		= &if_stats;
+      #ifdef LINUX_2_4
+	dev->tx_timeout		= &if_tx_timeout;
+	dev->watchdog_timeo	= TX_TIMEOUT;
+      #endif
+
+
+      #ifdef LINUX_2_0
+	dev->family		= AF_INET;
+      #endif	
+ 
+	/* Initialize hardware parameters */
+	dev->irq	= wandev->irq;
+	dev->dma	= wandev->dma;
+	dev->base_addr	= wandev->ioport;
+	dev->mem_start	= wandev->maddr;
+	dev->mem_end	= wandev->maddr + wandev->msize - 1;
+
+	/* Set transmit buffer queue length 
+         * If we over fill this queue the packets will
+         * be droped by the kernel.
+         * sppp_attach() sets this to 10, but
+         * 100 will give us more room at low speeds.
+	 */
+        dev->tx_queue_len = 100;
+   
+	/* Initialize socket buffers */
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
+	dev_init_buffers(dev);
+      #else
+        for (i = 0; i < DEV_NUMBUFFS; ++i)
+                skb_queue_head_init(&dev->buffs[i]);
+      #endif
+
+	return 0;
+}
+
+
+#ifdef LINUX_2_4
+/*============================================================================
+ * Handle transmit timeout event from netif watchdog
+ */
+static void if_tx_timeout (netdevice_t *dev)
+{
+    	chdlc_private_area_t* chan = dev->priv;
+	sdla_t *card = chan->card;
+	
+	/* If our device stays busy for at least 5 seconds then we will
+	 * kick start the device by making dev->tbusy = 0.  We expect
+	 * that our device never stays busy more than 5 seconds. So this                 
+	 * is only used as a last resort.
+	 */
+
+	++card->wandev.stats.collisions;
+
+	printk (KERN_INFO "%s: Transmit timed out on %s\n", card->devname,dev->name);
+	netif_wake_queue (dev);
+}
+#endif
+
+
+
+/*============================================================================
+ * Open network interface.
+ * o enable communications and interrupts.
+ * o prevent module from unloading by incrementing use count
+ *
+ * Return 0 if O.k. or errno.
+ */
+static int if_open (netdevice_t* dev)
+{
+	chdlc_private_area_t* chdlc_priv_area = dev->priv;
+	sdla_t* card = chdlc_priv_area->card;
+	struct timeval tv;
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+
+	/* Only one open per interface is allowed */
+
+      #ifdef LINUX_2_4
+	if (netif_running(dev))
+		return -EBUSY;
+      #else
+	if (dev->start)
+		return -EBUSY;		/* only one open is allowed */
+      #endif
+
+	/* Start PPP Layer */
+	if (sppp_open(dev)){
+		return -EIO;
+	}
+
+	do_gettimeofday(&tv);
+	chdlc_priv_area->router_start_time = tv.tv_sec;
+ 
+      #ifdef LINUX_2_4
+	netif_start_queue(dev);
+      #else
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	dev->start = 1;
+      #endif
+	
+	wanpipe_open(card);
+
+	chdlc_priv_area->timer_int_enabled |= TMR_INT_ENABLED_CONFIG;
+	flags->interrupt_info_struct.interrupt_permission |= APP_INT_ON_TIMER;
+	return 0;
+}
+
+/*============================================================================
+ * Close network interface.
+ * o if this is the last close, then disable communications and interrupts.
+ * o reset flags.
+ */
+static int if_close (netdevice_t* dev)
+{
+	chdlc_private_area_t* chdlc_priv_area = dev->priv;
+	sdla_t* card = chdlc_priv_area->card;
+
+	/* Stop the PPP Layer */
+	sppp_close(dev);
+	stop_net_queue(dev);
+
+      #ifndef LINUX_2_4
+	dev->start=0;
+      #endif
+
+	wanpipe_close(card);
+	
+	return 0;
+}
+
+/*============================================================================
+ * Send a packet on a network interface.
+ * o set tbusy flag (marks start of the transmission) to block a timer-based
+ *   transmit from overlapping.
+ * o check link state. If link is not up, then drop the packet.
+ * o execute adapter send command.
+ * o free socket buffer
+ *
+ * Return:	0	complete (socket buffer must be freed)
+ *		non-0	packet may be re-transmitted (tbusy must be set)
+ *
+ * Notes:
+ * 1. This routine is called either by the protocol stack or by the "net
+ *    bottom half" (with interrupts enabled).
+ * 2. Setting tbusy flag will inhibit further transmit requests from the
+ *    protocol stack and can be used for flow control with protocol layer.
+ */
+static int if_send (struct sk_buff* skb, netdevice_t* dev)
+{
+	chdlc_private_area_t *chdlc_priv_area = dev->priv;
+	sdla_t *card = chdlc_priv_area->card;
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+	INTERRUPT_INFORMATION_STRUCT *chdlc_int = &flags->interrupt_info_struct;
+	int udp_type = 0;
+	unsigned long smp_flags;
+	int err=0;
+
+      #ifdef LINUX_2_4
+	netif_stop_queue(dev);
+      #endif
+
+	
+	if (skb == NULL){
+		/* If we get here, some higher layer thinks we've missed an
+		 * tx-done interrupt.
+		 */
+		printk(KERN_INFO "%s: Received NULL skb buffer! interface %s got kicked!\n",
+			card->devname, dev->name);
+
+		wake_net_dev(dev);
+		return 0;
+	}
+
+      #ifndef LINUX_2_4
+	if (dev->tbusy){
+
+		/* If our device stays busy for at least 5 seconds then we will
+		 * kick start the device by making dev->tbusy = 0.  We expect 
+		 * that our device never stays busy more than 5 seconds. So this
+		 * is only used as a last resort. 
+		 */
+                ++card->wandev.stats.collisions;
+
+		if((jiffies - chdlc_priv_area->tick_counter) < (5 * HZ)) {
+			return 1;
+		}
+
+		printk (KERN_INFO "%s: Transmit (tbusy) timeout !\n",
+			card->devname);
+
+		/* unbusy the interface */
+		dev->tbusy = 0;
+	}
+      #endif
+
+   	if (ntohs(skb->protocol) != htons(PVC_PROT)){
+		/* check the udp packet type */
+		
+		udp_type = udp_pkt_type(skb, card);
+		if (udp_type == UDP_CPIPE_TYPE){
+                        if(store_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb, dev,
+                                chdlc_priv_area)){
+				chdlc_int->interrupt_permission |=
+					APP_INT_ON_TIMER;
+			}
+			start_net_queue(dev);
+			return 0;
+		}
+        }
+
+	/* Lock the 508 Card: SMP is supported */
+      	if(card->hw.type != SDLA_S514){
+		s508_lock(card,&smp_flags);
+	} 
+
+    	if (test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)){
+	
+		printk(KERN_INFO "%s: Critical in if_send: %lx\n",
+					card->wandev.name,card->wandev.critical);
+                ++card->wandev.stats.tx_dropped;
+		start_net_queue(dev);
+		goto if_send_crit_exit;
+	}
+
+	if (card->wandev.state != WAN_CONNECTED){
+		++card->wandev.stats.tx_dropped;
+		start_net_queue(dev);
+		goto if_send_crit_exit;
+	}
+	
+	if (chdlc_send(card, skb->data, skb->len)){
+		stop_net_queue(dev);
+
+	}else{
+		++card->wandev.stats.tx_packets;
+              #if defined(LINUX_2_1) || defined(LINUX_2_4)
+       		card->wandev.stats.tx_bytes += skb->len;
+              #endif
+	      #ifdef LINUX_2_4
+		dev->trans_start = jiffies;
+	      #endif
+		start_net_queue(dev);
+	}	
+
+if_send_crit_exit:
+	if (!(err=is_queue_stopped(dev))){
+                wan_dev_kfree_skb(skb, FREE_WRITE);
+	}else{
+		chdlc_priv_area->tick_counter = jiffies;
+		chdlc_int->interrupt_permission |= APP_INT_ON_TX_FRAME;
+	}
+
+	clear_bit(SEND_CRIT, (void*)&card->wandev.critical);
+	if(card->hw.type != SDLA_S514){
+		s508_unlock(card,&smp_flags);
+	}
+
+	return err;
+}
+
+
+/*============================================================================
+ * Reply to UDP Management system.
+ * Return length of reply.
+ */
+static int reply_udp( unsigned char *data, unsigned int mbox_len )
+{
+
+	unsigned short len, udp_length, temp, ip_length;
+	unsigned long ip_temp;
+	int even_bound = 0;
+  	chdlc_udp_pkt_t *c_udp_pkt = (chdlc_udp_pkt_t *)data;
+	 
+	/* Set length of packet */
+	len = sizeof(ip_pkt_t)+ 
+	      sizeof(udp_pkt_t)+
+	      sizeof(wp_mgmt_t)+
+	      sizeof(cblock_t)+
+	      sizeof(trace_info_t)+ 
+	      mbox_len;
+
+	/* fill in UDP reply */
+	c_udp_pkt->wp_mgmt.request_reply = UDPMGMT_REPLY;
+   
+	/* fill in UDP length */
+	udp_length = sizeof(udp_pkt_t)+ 
+		     sizeof(wp_mgmt_t)+
+		     sizeof(cblock_t)+
+	             sizeof(trace_info_t)+
+		     mbox_len; 
+
+ 	/* put it on an even boundary */
+	if ( udp_length & 0x0001 ) {
+		udp_length += 1;
+		len += 1;
+		even_bound = 1;
+	}  
+
+	temp = (udp_length<<8)|(udp_length>>8);
+	c_udp_pkt->udp_pkt.udp_length = temp;
+		 
+	/* swap UDP ports */
+	temp = c_udp_pkt->udp_pkt.udp_src_port;
+	c_udp_pkt->udp_pkt.udp_src_port = 
+			c_udp_pkt->udp_pkt.udp_dst_port; 
+	c_udp_pkt->udp_pkt.udp_dst_port = temp;
+
+	/* add UDP pseudo header */
+	temp = 0x1100;
+	*((unsigned short *)(c_udp_pkt->data+mbox_len+even_bound)) = temp;	
+	temp = (udp_length<<8)|(udp_length>>8);
+	*((unsigned short *)(c_udp_pkt->data+mbox_len+even_bound+2)) = temp;
+
+		 
+	/* calculate UDP checksum */
+	c_udp_pkt->udp_pkt.udp_checksum = 0;
+	c_udp_pkt->udp_pkt.udp_checksum = calc_checksum(&data[UDP_OFFSET],udp_length+UDP_OFFSET);
+
+	/* fill in IP length */
+	ip_length = len;
+	temp = (ip_length<<8)|(ip_length>>8);
+	c_udp_pkt->ip_pkt.total_length = temp;
+  
+	/* swap IP addresses */
+	ip_temp = c_udp_pkt->ip_pkt.ip_src_address;
+	c_udp_pkt->ip_pkt.ip_src_address = c_udp_pkt->ip_pkt.ip_dst_address;
+	c_udp_pkt->ip_pkt.ip_dst_address = ip_temp;
+
+	/* fill in IP checksum */
+	c_udp_pkt->ip_pkt.hdr_checksum = 0;
+	c_udp_pkt->ip_pkt.hdr_checksum = calc_checksum(data,sizeof(ip_pkt_t));
+
+	return len;
+
+} /* reply_udp */
+
+unsigned short calc_checksum (char *data, int len)
+{
+	unsigned short temp; 
+	unsigned long sum=0;
+	int i;
+
+	for( i = 0; i <len; i+=2 ) {
+		memcpy(&temp,&data[i],2);
+		sum += (unsigned long)temp;
+	}
+
+	while (sum >> 16 ) {
+		sum = (sum & 0xffffUL) + (sum >> 16);
+	}
+
+	temp = (unsigned short)sum;
+	temp = ~temp;
+
+	if( temp == 0 ) 
+		temp = 0xffff;
+
+	return temp;	
+}
+
+
+/*============================================================================
+ * Get ethernet-style interface statistics.
+ * Return a pointer to struct enet_statistics.
+ */
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+static struct net_device_stats* if_stats (netdevice_t* dev)
+{
+	sdla_t *my_card;
+	chdlc_private_area_t* chdlc_priv_area;
+
+	/* Shutdown bug fix. In del_if() we kill
+         * dev->priv pointer. This function, gets
+         * called after del_if(), thus check
+         * if pointer has been deleted */
+	if ((chdlc_priv_area=dev->priv) == NULL)
+		return NULL;
+
+	my_card = chdlc_priv_area->card;
+	return &my_card->wandev.stats; 
+}
+#else
+static struct enet_statistics* if_stats (netdevice_t* dev)
+{
+        sdla_t *my_card;
+        chdlc_private_area_t* chdlc_priv_area = dev->priv;
+
+	/* Shutdown bug fix. In del_if() we kill
+         * dev->priv pointer. This function, gets
+         * called after del_if(), thus check
+         * if pointer has been deleted */
+	if ((chdlc_priv_area=dev->priv) == NULL)
+		return NULL;
+
+        my_card = chdlc_priv_area->card;
+        return &my_card->wandev.stats;
+}
+#endif
+
+/****** Cisco HDLC Firmware Interface Functions *******************************/
+
+/*============================================================================
+ * Read firmware code version.
+ *	Put code version as ASCII string in str. 
+ */
+static int chdlc_read_version (sdla_t* card, char* str)
+{
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+	int len;
+	char err;
+	mb->buffer_length = 0;
+	mb->command = READ_CHDLC_CODE_VERSION;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+
+	if(err != COMMAND_OK) {
+		chdlc_error(card,err,mb);
+	}
+	else if (str) {  /* is not null */
+		len = mb->buffer_length;
+		memcpy(str, mb->data, len);
+		str[len] = '\0';
+	}
+	return (err);
+}
+
+/*-----------------------------------------------------------------------------
+ *  Configure CHDLC firmware.
+ */
+static int chdlc_configure (sdla_t* card, void* data)
+{
+	int err;
+	CHDLC_MAILBOX_STRUCT *mailbox = card->mbox;
+	int data_length = sizeof(CHDLC_CONFIGURATION_STRUCT);
+	
+	mailbox->buffer_length = data_length;  
+	memcpy(mailbox->data, data, data_length);
+	mailbox->command = SET_CHDLC_CONFIGURATION;
+	err = sdla_exec(mailbox) ? mailbox->return_code : CMD_TIMEOUT;
+	
+	if (err != COMMAND_OK) chdlc_error (card, err, mailbox);
+                           
+	return err;
+}
+
+
+/*============================================================================
+ * Set interrupt mode -- HDLC Version.
+ */
+
+static int chdlc_set_intr_mode (sdla_t* card, unsigned mode)
+{
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+	CHDLC_INT_TRIGGERS_STRUCT* int_data =
+		 (CHDLC_INT_TRIGGERS_STRUCT *)mb->data;
+	int err;
+
+	int_data->CHDLC_interrupt_triggers 	= mode;
+	int_data->IRQ				= card->hw.irq;
+	int_data->interrupt_timer               = 1;
+   
+	mb->buffer_length = sizeof(CHDLC_INT_TRIGGERS_STRUCT);
+	mb->command = SET_CHDLC_INTERRUPT_TRIGGERS;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+	if (err != COMMAND_OK)
+		chdlc_error (card, err, mb);
+	return err;
+}
+
+
+/*============================================================================
+ * Enable communications.
+ */
+
+static int chdlc_comm_enable (sdla_t* card)
+{
+	int err;
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+
+	mb->buffer_length = 0;
+	mb->command = ENABLE_CHDLC_COMMUNICATIONS;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+	if (err != COMMAND_OK)
+		chdlc_error(card, err, mb);
+	else
+		card->u.c.comm_enabled=1;
+
+	return err;
+}
+
+/*============================================================================
+ * Disable communications and Drop the Modem lines (DCD and RTS).
+ */
+static int chdlc_comm_disable (sdla_t* card)
+{
+	int err;
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+
+	mb->buffer_length = 0;
+	mb->command = DISABLE_CHDLC_COMMUNICATIONS;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+	if (err != COMMAND_OK)
+		chdlc_error(card,err,mb);
+
+	return err;
+}
+
+/*============================================================================
+ * Read communication error statistics.
+ */
+static int chdlc_read_comm_err_stats (sdla_t* card)
+{
+        int err;
+        CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+
+        mb->buffer_length = 0;
+        mb->command = READ_COMMS_ERROR_STATS;
+        err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+        if (err != COMMAND_OK)
+                chdlc_error(card,err,mb);
+        return err;
+}
+
+
+/*============================================================================
+ * Read CHDLC operational statistics.
+ */
+static int chdlc_read_op_stats (sdla_t* card)
+{
+        int err;
+        CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+
+        mb->buffer_length = 0;
+        mb->command = READ_CHDLC_OPERATIONAL_STATS;
+        err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+        if (err != COMMAND_OK)
+                chdlc_error(card,err,mb);
+        return err;
+}
+
+
+/*============================================================================
+ * Update communications error and general packet statistics.
+ */
+static int update_comms_stats(sdla_t* card,
+	chdlc_private_area_t* chdlc_priv_area)
+{
+        CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+  	COMMS_ERROR_STATS_STRUCT* err_stats;
+        CHDLC_OPERATIONAL_STATS_STRUCT *op_stats;
+
+	/* on the first timer interrupt, read the comms error statistics */
+	if(chdlc_priv_area->update_comms_stats == 2) {
+		if(chdlc_read_comm_err_stats(card))
+			return 1;
+		err_stats = (COMMS_ERROR_STATS_STRUCT *)mb->data;
+		card->wandev.stats.rx_over_errors = 
+				err_stats->Rx_overrun_err_count;
+		card->wandev.stats.rx_crc_errors = 
+				err_stats->CRC_err_count;
+		card->wandev.stats.rx_frame_errors = 
+				err_stats->Rx_abort_count;
+		card->wandev.stats.rx_fifo_errors = 
+				err_stats->Rx_dis_pri_bfrs_full_count; 
+		card->wandev.stats.rx_missed_errors =
+				card->wandev.stats.rx_fifo_errors;
+		card->wandev.stats.tx_aborted_errors =
+				err_stats->sec_Tx_abort_count;
+	}
+
+        /* on the second timer interrupt, read the operational statistics */
+	else {
+        	if(chdlc_read_op_stats(card))
+                	return 1;
+		op_stats = (CHDLC_OPERATIONAL_STATS_STRUCT *)mb->data;
+		card->wandev.stats.rx_length_errors =
+			(op_stats->Rx_Data_discard_short_count +
+			op_stats->Rx_Data_discard_long_count);
+	}
+
+	return 0;
+}
+
+/*============================================================================
+ * Send packet.
+ *	Return:	0 - o.k.
+ *		1 - no transmit buffers available
+ */
+static int chdlc_send (sdla_t* card, void* data, unsigned len)
+{
+	CHDLC_DATA_TX_STATUS_EL_STRUCT *txbuf = card->u.c.txbuf;
+
+	if (txbuf->opp_flag)
+		return 1;
+	
+	sdla_poke(&card->hw, txbuf->ptr_data_bfr, data, len);
+
+	txbuf->frame_length = len;
+	txbuf->opp_flag = 1;		/* start transmission */
+	
+	/* Update transmit buffer control fields */
+	card->u.c.txbuf = ++txbuf;
+
+	if ((void*)txbuf > card->u.c.txbuf_last)
+		card->u.c.txbuf = card->u.c.txbuf_base;
+
+	return 0;
+}
+
+/****** Firmware Error Handler **********************************************/
+
+/*============================================================================
+ * Firmware error handler.
+ *	This routine is called whenever firmware command returns non-zero
+ *	return code.
+ *
+ * Return zero if previous command has to be cancelled.
+ */
+static int chdlc_error (sdla_t *card, int err, CHDLC_MAILBOX_STRUCT *mb)
+{
+	unsigned cmd = mb->command;
+
+	switch (err) {
+
+	case CMD_TIMEOUT:
+		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+			card->devname, cmd);
+		break;
+
+	case S514_BOTH_PORTS_SAME_CLK_MODE:
+		if(cmd == SET_CHDLC_CONFIGURATION) {
+			printk(KERN_INFO
+			 "%s: Configure both ports for the same clock source\n",
+				card->devname);
+			break;
+		}
+
+	default:
+		printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
+			card->devname, cmd, err);
+	}
+
+	return 0;
+}
+
+/****** Interrupt Handlers **************************************************/
+
+/*============================================================================
+ * Cisco HDLC interrupt service routine.
+ */
+STATIC void wsppp_isr (sdla_t* card)
+{
+	netdevice_t* dev;
+	SHARED_MEMORY_INFO_STRUCT* flags = NULL;
+	int i;
+	sdla_t *my_card;
+
+
+	/* Check for which port the interrupt has been generated
+	 * Since Secondary Port is piggybacking on the Primary
+         * the check must be done here. 
+	 */
+
+	flags = card->u.c.flags;
+	if (!flags->interrupt_info_struct.interrupt_type){
+		/* Check for a second port (piggybacking) */
+		if((my_card = card->next)){
+			flags = my_card->u.c.flags;
+			if (flags->interrupt_info_struct.interrupt_type){
+				card = my_card;
+				card->isr(card);
+				return;
+			}
+		}
+	}
+
+	dev = card->wandev.dev;
+	card->in_isr = 1;
+	flags = card->u.c.flags;
+		
+	/* If we get an interrupt with no network device, stop the interrupts
+	 * and issue an error */
+	if ((!dev || !dev->priv) && flags->interrupt_info_struct.interrupt_type != 
+	    	COMMAND_COMPLETE_APP_INT_PEND){
+		goto isr_done;
+	}
+
+	
+	/* if critical due to peripheral operations
+	 * ie. update() or getstats() then reset the interrupt and
+	 * wait for the board to retrigger.
+	 */
+	if(test_bit(PERI_CRIT, (void*)&card->wandev.critical)) {
+		flags->interrupt_info_struct.
+					interrupt_type = 0;
+		goto isr_done;
+	}
+
+
+	/* On a 508 Card, if critical due to if_send 
+         * Major Error !!!
+	 */
+	if(card->hw.type != SDLA_S514) {
+		if(test_bit(0, (void*)&card->wandev.critical)) {
+			printk(KERN_INFO "%s: Critical while in ISR: %lx\n",
+				card->devname, card->wandev.critical);
+			goto isr_done;
+		}
+	}
+
+	switch(flags->interrupt_info_struct.interrupt_type) {
+
+		case RX_APP_INT_PEND:	/* 0x01: receive interrupt */
+			rx_intr(card);
+			break;
+
+		case TX_APP_INT_PEND:	/* 0x02: transmit interrupt */
+			flags->interrupt_info_struct.interrupt_permission &=
+				 ~APP_INT_ON_TX_FRAME;
+
+			wake_net_dev(dev);
+			break;
+
+		case COMMAND_COMPLETE_APP_INT_PEND:/* 0x04: cmd cplt */
+			++ Intr_test_counter;
+			break;
+
+		case CHDLC_EXCEP_COND_APP_INT_PEND:	/* 0x20 */
+			process_chdlc_exception(card);
+			break;
+
+		case GLOBAL_EXCEP_COND_APP_INT_PEND:
+			process_global_exception(card);
+			break;
+
+		case TIMER_APP_INT_PEND:
+			timer_intr(card);
+			break;
+
+		default:
+			printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", 
+				card->devname,
+				flags->interrupt_info_struct.interrupt_type);
+			printk(KERN_INFO "Code name: ");
+			for(i = 0; i < 4; i ++)
+				printk(KERN_INFO "%c",
+					flags->global_info_struct.codename[i]); 
+			printk(KERN_INFO "\nCode version: ");
+			for(i = 0; i < 4; i ++)
+				printk(KERN_INFO "%c", 
+					flags->global_info_struct.codeversion[i]); 
+			printk(KERN_INFO "\n");	
+			break;
+	}
+
+isr_done:
+	card->in_isr = 0;
+	flags->interrupt_info_struct.interrupt_type = 0;
+}
+
+/*============================================================================
+ * Receive interrupt handler.
+ */
+static void rx_intr (sdla_t* card)
+{
+	netdevice_t *dev;
+	chdlc_private_area_t *chdlc_priv_area;
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+	CHDLC_DATA_RX_STATUS_EL_STRUCT *rxbuf = card->u.c.rxmb;
+	struct sk_buff *skb;
+	unsigned len;
+	unsigned addr = rxbuf->ptr_data_bfr;
+	void *buf;
+	int i,udp_type;
+	
+	if (rxbuf->opp_flag != 0x01) {
+		printk(KERN_INFO 
+			"%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", 
+			card->devname, (unsigned)rxbuf, rxbuf->opp_flag);
+                printk(KERN_INFO "Code name: ");
+                for(i = 0; i < 4; i ++)
+                        printk(KERN_INFO "%c",
+                                flags->global_info_struct.codename[i]);
+                printk(KERN_INFO "\nCode version: ");
+                for(i = 0; i < 4; i ++)
+                        printk(KERN_INFO "%c",
+                                flags->global_info_struct.codeversion[i]);
+                printk(KERN_INFO "\n");
+
+
+		/* Bug Fix: Mar 6 2000
+                 * If we get a corrupted mailbox, it measn that driver 
+                 * is out of sync with the firmware. There is no recovery.
+                 * If we don't turn off all interrupts for this card
+                 * the machine will crash. 
+                 */
+		printk(KERN_INFO "%s: Critical router failure ...!!!\n", card->devname);
+		printk(KERN_INFO "Please contact Sangoma Technologies !\n");
+		chdlc_set_intr_mode(card,0);	
+		return;
+	}
+
+	dev = card->wandev.dev;
+
+	if (!dev){ 
+		goto rx_exit;
+	}
+	
+      #ifdef LINUX_2_4
+	if (!netif_running(dev)){
+		goto rx_exit;
+	}
+      #else
+	if (!dev->start){ 
+		goto rx_exit;
+	}
+      #endif
+
+	chdlc_priv_area = dev->priv;
+
+	if (rxbuf->error_flag){	
+		goto rx_exit;
+	}
+	/* Take off two CRC bytes */
+
+	if (rxbuf->frame_length < 7 || rxbuf->frame_length > 1506 ){
+		goto rx_exit;
+	}	
+
+	len = rxbuf->frame_length - CRC_LENGTH;
+
+	/* Allocate socket buffer */
+	skb = dev_alloc_skb(len);
+
+	if (skb == NULL) {
+		if (net_ratelimit()){
+			printk(KERN_INFO "%s: no socket buffers available!\n",
+						card->devname);
+		}
+		++card->wandev.stats.rx_dropped;
+		goto rx_exit;
+	}
+
+	/* Copy data to the socket buffer */
+	if((addr + len) > card->u.c.rx_top + 1) {
+		unsigned tmp = card->u.c.rx_top - addr + 1;
+		buf = skb_put(skb, tmp);
+		sdla_peek(&card->hw, addr, buf, tmp);
+		addr = card->u.c.rx_base;
+		len -= tmp;
+	}
+		
+	buf = skb_put(skb, len);
+	sdla_peek(&card->hw, addr, buf, len);
+
+	skb->protocol = htons(ETH_P_WAN_PPP);
+
+	card->wandev.stats.rx_packets ++;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+	card->wandev.stats.rx_bytes += skb->len;
+#endif
+	udp_type = udp_pkt_type( skb, card );
+
+	if(udp_type == UDP_CPIPE_TYPE) {
+		if(store_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK,
+   				      card, skb, dev, chdlc_priv_area)) {
+     		        flags->interrupt_info_struct.
+						interrupt_permission |= 
+							APP_INT_ON_TIMER; 
+		}
+	}else{
+               	/* Pass it up the protocol stack */
+                skb->dev = dev;
+                skb->mac.raw  = skb->data;
+                netif_rx(skb);
+	}
+
+rx_exit:
+	/* Release buffer element and calculate a pointer to the next one */
+	rxbuf->opp_flag = 0x00;
+	card->u.c.rxmb = ++ rxbuf;
+	if((void*)rxbuf > card->u.c.rxbuf_last){
+		card->u.c.rxmb = card->u.c.rxbuf_base;
+	}
+}
+
+/*============================================================================
+ * Timer interrupt handler.
+ * The timer interrupt is used for two purposes:
+ *    1) Processing udp calls from 'cpipemon'.
+ *    2) Reading board-level statistics for updating the proc file system.
+ */
+void timer_intr(sdla_t *card)
+{
+        netdevice_t* dev;
+        chdlc_private_area_t* chdlc_priv_area = NULL;
+        SHARED_MEMORY_INFO_STRUCT* flags = NULL;
+
+        dev = card->wandev.dev; 
+        chdlc_priv_area = dev->priv;
+
+	if (chdlc_priv_area->timer_int_enabled & TMR_INT_ENABLED_CONFIG) {
+		if (!config_chdlc(card)){
+			chdlc_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_CONFIG;
+		}
+	}
+	
+	/* process a udp call if pending */
+       	if(chdlc_priv_area->timer_int_enabled & TMR_INT_ENABLED_UDP) {
+               	process_udp_mgmt_pkt(card, dev,
+                       chdlc_priv_area);
+		chdlc_priv_area->timer_int_enabled &= ~TMR_INT_ENABLED_UDP;
+        }
+	
+
+	/* read the communications statistics if required */
+	if(chdlc_priv_area->timer_int_enabled & TMR_INT_ENABLED_UPDATE) {
+		update_comms_stats(card, chdlc_priv_area);
+                if(!(-- chdlc_priv_area->update_comms_stats)) {
+			chdlc_priv_area->timer_int_enabled &= 
+				~TMR_INT_ENABLED_UPDATE;
+		}
+        }
+
+	/* only disable the timer interrupt if there are no udp or statistic */
+	/* updates pending */
+        if(!chdlc_priv_area->timer_int_enabled) {
+                flags = card->u.c.flags;
+                flags->interrupt_info_struct.interrupt_permission &=
+                        ~APP_INT_ON_TIMER;
+        }
+}
+
+/*------------------------------------------------------------------------------
+  Miscellaneous Functions
+	- set_chdlc_config() used to set configuration options on the board
+------------------------------------------------------------------------------*/
+
+static int set_chdlc_config(sdla_t* card)
+{
+
+	CHDLC_CONFIGURATION_STRUCT cfg;
+
+	memset(&cfg, 0, sizeof(CHDLC_CONFIGURATION_STRUCT));
+
+	if(card->wandev.clocking)
+		cfg.baud_rate = card->wandev.bps;
+
+	cfg.line_config_options = (card->wandev.interface == WANOPT_RS232) ?
+		INTERFACE_LEVEL_RS232 : INTERFACE_LEVEL_V35;
+
+	cfg.modem_config_options	= 0;
+	//API OPTIONS
+	cfg.CHDLC_API_options		= DISCARD_RX_ERROR_FRAMES;
+	cfg.modem_status_timer		= 100;
+	cfg.CHDLC_protocol_options	= HDLC_STREAMING_MODE;
+	cfg.percent_data_buffer_for_Tx  = 50;
+	cfg.CHDLC_statistics_options	= (CHDLC_TX_DATA_BYTE_COUNT_STAT |
+		CHDLC_RX_DATA_BYTE_COUNT_STAT);
+	cfg.max_CHDLC_data_field_length	= card->wandev.mtu;
+
+	cfg.transmit_keepalive_timer	= 0;
+	cfg.receive_keepalive_timer	= 0;
+	cfg.keepalive_error_tolerance	= 0;
+	cfg.SLARP_request_timer		= 0;
+
+	cfg.IP_address		= 0;
+	cfg.IP_netmask		= 0;
+	
+	return chdlc_configure(card, &cfg);
+}
+
+/*============================================================================
+ * Process global exception condition
+ */
+static int process_global_exception(sdla_t *card)
+{
+	CHDLC_MAILBOX_STRUCT* mbox = card->mbox;
+	int err;
+
+	mbox->buffer_length = 0;
+	mbox->command = READ_GLOBAL_EXCEPTION_CONDITION;
+	err = sdla_exec(mbox) ? mbox->return_code : CMD_TIMEOUT;
+
+	if(err != CMD_TIMEOUT ){
+	
+		switch(mbox->return_code) {
+         
+	      	case EXCEP_MODEM_STATUS_CHANGE:
+
+			printk(KERN_INFO "%s: Modem status change\n",
+				card->devname);
+
+			switch(mbox->data[0] & (DCD_HIGH | CTS_HIGH)) {
+				case (DCD_HIGH):
+					printk(KERN_INFO "%s: DCD high, CTS low\n",card->devname);
+					break;
+				case (CTS_HIGH):
+                                        printk(KERN_INFO "%s: DCD low, CTS high\n",card->devname);
+                                        break;
+                                case ((DCD_HIGH | CTS_HIGH)):
+                                        printk(KERN_INFO "%s: DCD high, CTS high\n",card->devname);
+                                        break;
+				default:
+                                        printk(KERN_INFO "%s: DCD low, CTS low\n",card->devname);
+                                        break;
+			}
+
+			if (!(mbox->data[0] & DCD_HIGH) || !(mbox->data[0] & DCD_HIGH)){
+				//printk(KERN_INFO "Sending TERM Request Manually !\n");
+				send_ppp_term_request(card->wandev.dev);
+			}	
+			break;
+
+                case EXCEP_TRC_DISABLED:
+                        printk(KERN_INFO "%s: Line trace disabled\n",
+				card->devname);
+                        break;
+
+		case EXCEP_IRQ_TIMEOUT:
+			printk(KERN_INFO "%s: IRQ timeout occurred\n",
+				card->devname); 
+			break;
+
+                default:
+                        printk(KERN_INFO "%s: Global exception %x\n",
+				card->devname, mbox->return_code);
+                        break;
+                }
+	}
+	return 0;
+}
+
+
+/*============================================================================
+ * Process chdlc exception condition
+ */
+static int process_chdlc_exception(sdla_t *card)
+{
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+	int err;
+
+	mb->buffer_length = 0;
+	mb->command = READ_CHDLC_EXCEPTION_CONDITION;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+	if(err != CMD_TIMEOUT) {
+	
+		switch (err) {
+
+		case EXCEP_LINK_ACTIVE:
+			port_set_state(card, WAN_CONNECTED);
+			break;
+
+		case EXCEP_LINK_INACTIVE_MODEM:
+			port_set_state(card, WAN_DISCONNECTED);
+			break;
+
+		case EXCEP_LOOPBACK_CONDITION:
+			printk(KERN_INFO "%s: Loopback Condition Detected.\n",
+						card->devname);
+			break;
+
+		case NO_CHDLC_EXCEP_COND_TO_REPORT:
+			printk(KERN_INFO "%s: No exceptions reported.\n",
+						card->devname);
+			break;
+		default:
+			printk(KERN_INFO "%s: Exception Condition %x!\n",
+					card->devname,err);
+			break;
+		}
+
+	}
+	return 0;
+}
+
+
+/*=============================================================================
+ * Store a UDP management packet for later processing.
+ */
+
+static int store_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card,
+                                struct sk_buff *skb, netdevice_t* dev,
+                                chdlc_private_area_t* chdlc_priv_area )
+{
+	int udp_pkt_stored = 0;
+
+	if(!chdlc_priv_area->udp_pkt_lgth &&
+	  (skb->len <= MAX_LGTH_UDP_MGNT_PKT)) {
+        	chdlc_priv_area->udp_pkt_lgth = skb->len;
+		chdlc_priv_area->udp_pkt_src = udp_pkt_src;
+       		memcpy(chdlc_priv_area->udp_pkt_data, skb->data, skb->len);
+		chdlc_priv_area->timer_int_enabled = TMR_INT_ENABLED_UDP;
+		udp_pkt_stored = 1;
+	}
+
+	if(udp_pkt_src == UDP_PKT_FRM_STACK)
+		wan_dev_kfree_skb(skb, FREE_WRITE);
+	else
+                wan_dev_kfree_skb(skb, FREE_READ);
+	
+	return(udp_pkt_stored);
+}
+
+
+/*=============================================================================
+ * Process UDP management packet.
+ */
+
+static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev,
+				chdlc_private_area_t* chdlc_priv_area ) 
+{
+	unsigned char *buf;
+	unsigned int frames, len;
+	struct sk_buff *new_skb;
+	unsigned short buffer_length, real_len;
+	unsigned long data_ptr;
+	unsigned data_length;
+	int udp_mgmt_req_valid = 1;
+	CHDLC_MAILBOX_STRUCT *mb = card->mbox;
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+	chdlc_udp_pkt_t *chdlc_udp_pkt;
+	struct timeval tv;
+	int err;
+	char ut_char;
+
+	chdlc_udp_pkt = (chdlc_udp_pkt_t *) chdlc_priv_area->udp_pkt_data;
+
+	if(chdlc_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+
+		switch(chdlc_udp_pkt->cblock.command) {
+			case READ_GLOBAL_STATISTICS:
+			case READ_MODEM_STATUS:  
+			case READ_CHDLC_LINK_STATUS:
+			case CPIPE_ROUTER_UP_TIME:
+			case READ_COMMS_ERROR_STATS:
+			case READ_CHDLC_OPERATIONAL_STATS:
+
+			/* These two commands are executed for
+			 * each request */
+			case READ_CHDLC_CONFIGURATION:
+			case READ_CHDLC_CODE_VERSION:
+				udp_mgmt_req_valid = 1;
+				break;
+			default:
+				udp_mgmt_req_valid = 0;
+				break;
+		} 
+	}
+	
+  	if(!udp_mgmt_req_valid) {
+
+		/* set length to 0 */
+		chdlc_udp_pkt->cblock.buffer_length = 0;
+
+    		/* set return code */
+		chdlc_udp_pkt->cblock.return_code = 0xCD;
+
+		if (net_ratelimit()){	
+			printk(KERN_INFO 
+			"%s: Warning, Illegal UDP command attempted from network: %x\n",
+			card->devname,chdlc_udp_pkt->cblock.command);
+		}
+
+   	} else {
+	   	unsigned long trace_status_cfg_addr = 0;
+		TRACE_STATUS_EL_CFG_STRUCT trace_cfg_struct;
+		TRACE_STATUS_ELEMENT_STRUCT trace_element_struct;
+
+		switch(chdlc_udp_pkt->cblock.command) {
+
+		case CPIPE_ENABLE_TRACING:
+		     if (!chdlc_priv_area->TracingEnabled) {
+
+			/* OPERATE_DATALINE_MONITOR */
+
+			mb->buffer_length = sizeof(LINE_TRACE_CONFIG_STRUCT);
+			mb->command = SET_TRACE_CONFIGURATION;
+
+    			((LINE_TRACE_CONFIG_STRUCT *)mb->data)->
+				trace_config = TRACE_ACTIVE;
+			/* Trace delay mode is not used because it slows
+			   down transfer and results in a standoff situation
+			   when there is a lot of data */
+
+			/* Configure the Trace based on user inputs */
+			((LINE_TRACE_CONFIG_STRUCT *)mb->data)->trace_config |= 
+					chdlc_udp_pkt->data[0];
+
+			((LINE_TRACE_CONFIG_STRUCT *)mb->data)->
+			   trace_deactivation_timer = 4000;
+
+
+			err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+			if (err != COMMAND_OK) {
+				chdlc_error(card,err,mb);
+				card->TracingEnabled = 0;
+				chdlc_udp_pkt->cblock.return_code = err;
+				mb->buffer_length = 0;
+				break;
+	    		} 
+
+			/* Get the base address of the trace element list */
+			mb->buffer_length = 0;
+			mb->command = READ_TRACE_CONFIGURATION;
+			err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+
+			if (err != COMMAND_OK) {
+				chdlc_error(card,err,mb);
+				chdlc_priv_area->TracingEnabled = 0;
+				chdlc_udp_pkt->cblock.return_code = err;
+				mb->buffer_length = 0;
+				break;
+	    		} 	
+
+	   		trace_status_cfg_addr =((LINE_TRACE_CONFIG_STRUCT *)
+				mb->data) -> ptr_trace_stat_el_cfg_struct;
+
+			sdla_peek(&card->hw, trace_status_cfg_addr,
+				 &trace_cfg_struct, sizeof(trace_cfg_struct));
+		    
+			chdlc_priv_area->start_trace_addr = trace_cfg_struct.
+				base_addr_trace_status_elements;
+
+			chdlc_priv_area->number_trace_elements = 
+					trace_cfg_struct.number_trace_status_elements;
+
+			chdlc_priv_area->end_trace_addr = (unsigned long)
+					((TRACE_STATUS_ELEMENT_STRUCT *)
+					 chdlc_priv_area->start_trace_addr + 
+					 (chdlc_priv_area->number_trace_elements - 1));
+
+			chdlc_priv_area->base_addr_trace_buffer = 
+					trace_cfg_struct.base_addr_trace_buffer;
+
+			chdlc_priv_area->end_addr_trace_buffer = 
+					trace_cfg_struct.end_addr_trace_buffer;
+
+		    	chdlc_priv_area->curr_trace_addr = 
+					trace_cfg_struct.next_trace_element_to_use;
+
+	    		chdlc_priv_area->available_buffer_space = 2000 - 
+								  sizeof(ip_pkt_t) -
+								  sizeof(udp_pkt_t) -
+							      	  sizeof(wp_mgmt_t) -
+								  sizeof(cblock_t) -
+							          sizeof(trace_info_t);	
+	       	     }
+		     chdlc_udp_pkt->cblock.return_code = COMMAND_OK;
+		     mb->buffer_length = 0;
+	       	     chdlc_priv_area->TracingEnabled = 1;
+	       	     break;
+	   
+
+		case CPIPE_DISABLE_TRACING:
+		     if (chdlc_priv_area->TracingEnabled) {
+
+			/* OPERATE_DATALINE_MONITOR */
+			mb->buffer_length = sizeof(LINE_TRACE_CONFIG_STRUCT);
+			mb->command = SET_TRACE_CONFIGURATION;
+    			((LINE_TRACE_CONFIG_STRUCT *)mb->data)->
+				trace_config = TRACE_INACTIVE;
+			err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+		     }		
+
+		     chdlc_priv_area->TracingEnabled = 0;
+		     chdlc_udp_pkt->cblock.return_code = COMMAND_OK;
+		     mb->buffer_length = 0;
+		     break;
+	   
+
+		case CPIPE_GET_TRACE_INFO:
+
+		     if (!chdlc_priv_area->TracingEnabled) {
+			chdlc_udp_pkt->cblock.return_code = 1;
+			mb->buffer_length = 0;
+			break;
+		     }
+
+  		     chdlc_udp_pkt->trace_info.ismoredata = 0x00;
+		     buffer_length = 0;	/* offset of packet already occupied */
+
+		     for (frames=0; frames < chdlc_priv_area->number_trace_elements; frames++){
+
+			trace_pkt_t *trace_pkt = (trace_pkt_t *)
+				&chdlc_udp_pkt->data[buffer_length];
+
+			sdla_peek(&card->hw, chdlc_priv_area->curr_trace_addr,
+			   	  (unsigned char *)&trace_element_struct,
+			   	  sizeof(TRACE_STATUS_ELEMENT_STRUCT));
+
+     			if (trace_element_struct.opp_flag == 0x00) {
+			 	break;
+			}
+
+			/* get pointer to real data */
+			data_ptr = trace_element_struct.ptr_data_bfr;
+
+			/* See if there is actual data on the trace buffer */
+			if (data_ptr){
+				data_length = trace_element_struct.trace_length;
+			}else{
+				data_length = 0;
+				chdlc_udp_pkt->trace_info.ismoredata = 0x01;
+			}
+	
+   			if( (chdlc_priv_area->available_buffer_space - buffer_length)
+				< ( sizeof(trace_pkt_t) + data_length) ) {
+
+                            /* indicate there are more frames on board & exit */
+				chdlc_udp_pkt->trace_info.ismoredata = 0x01;
+                               	break;
+                         }
+
+			trace_pkt->status = trace_element_struct.trace_type;
+
+			trace_pkt->time_stamp =
+				trace_element_struct.trace_time_stamp;
+
+			trace_pkt->real_length =
+				trace_element_struct.trace_length;
+
+			/* see if we can fit the frame into the user buffer */
+			real_len = trace_pkt->real_length;
+
+			if (data_ptr == 0) {
+			     	trace_pkt->data_avail = 0x00;
+			} else {
+				unsigned tmp = 0;
+
+				/* get the data from circular buffer
+				    must check for end of buffer */
+			        trace_pkt->data_avail = 0x01;
+
+				if ((data_ptr + real_len) >
+					     chdlc_priv_area->end_addr_trace_buffer + 1){
+
+				    	tmp = chdlc_priv_area->end_addr_trace_buffer - data_ptr + 1;
+				    	sdla_peek(&card->hw, data_ptr,
+					       	  trace_pkt->data,tmp);
+				    	data_ptr = chdlc_priv_area->base_addr_trace_buffer;
+				}
+	
+		        	sdla_peek(&card->hw, data_ptr,
+					  &trace_pkt->data[tmp], real_len - tmp);
+			}	
+
+			/* zero the opp flag to show we got the frame */
+			ut_char = 0x00;
+			sdla_poke(&card->hw, chdlc_priv_area->curr_trace_addr, &ut_char, 1);
+
+       			/* now move onto the next frame */
+       			chdlc_priv_area->curr_trace_addr += sizeof(TRACE_STATUS_ELEMENT_STRUCT);
+
+       			/* check if we went over the last address */
+			if ( chdlc_priv_area->curr_trace_addr > chdlc_priv_area->end_trace_addr ) {
+				chdlc_priv_area->curr_trace_addr = chdlc_priv_area->start_trace_addr;
+       			}
+
+            		if(trace_pkt->data_avail == 0x01) {
+				buffer_length += real_len - 1;
+			}
+	 
+	       	    	/* for the header */
+	            	buffer_length += sizeof(trace_pkt_t);
+
+		     }  /* For Loop */
+
+		     if (frames == chdlc_priv_area->number_trace_elements){
+			chdlc_udp_pkt->trace_info.ismoredata = 0x01;
+	             }
+ 		     chdlc_udp_pkt->trace_info.num_frames = frames;
+		 
+    		     mb->buffer_length = buffer_length;
+		     chdlc_udp_pkt->cblock.buffer_length = buffer_length; 
+		 
+		     chdlc_udp_pkt->cblock.return_code = COMMAND_OK; 
+		     
+		     break;
+
+
+		case CPIPE_FT1_READ_STATUS:
+			((unsigned char *)chdlc_udp_pkt->data )[0] =
+				flags->FT1_info_struct.parallel_port_A_input;
+
+			((unsigned char *)chdlc_udp_pkt->data )[1] =
+				flags->FT1_info_struct.parallel_port_B_input;
+				 
+			chdlc_udp_pkt->cblock.return_code = COMMAND_OK;
+			mb->buffer_length = 2;
+			break;
+		
+		case CPIPE_ROUTER_UP_TIME:
+			do_gettimeofday( &tv );
+			chdlc_priv_area->router_up_time = tv.tv_sec - 
+					chdlc_priv_area->router_start_time;
+			*(unsigned long *)&chdlc_udp_pkt->data = 
+					chdlc_priv_area->router_up_time;	
+			mb->buffer_length = sizeof(unsigned long);
+			break;
+
+   		case FT1_MONITOR_STATUS_CTRL:
+			/* Enable FT1 MONITOR STATUS */
+	        	if ((chdlc_udp_pkt->data[0] & ENABLE_READ_FT1_STATUS) ||  
+				(chdlc_udp_pkt->data[0] & ENABLE_READ_FT1_OP_STATS)) {
+			
+			     	if( rCount++ != 0 ) {
+					chdlc_udp_pkt->cblock.
+					return_code = COMMAND_OK;
+					mb->buffer_length = 1;
+		  			break;
+		    	     	}
+	      		}
+
+	      		/* Disable FT1 MONITOR STATUS */
+	      		if( chdlc_udp_pkt->data[0] == 0) {
+
+	      	   	     	if( --rCount != 0) {
+		  			chdlc_udp_pkt->cblock.
+					return_code = COMMAND_OK;
+					mb->buffer_length = 1;
+		  			break;
+	   	    	     	} 
+	      		} 	
+	
+		default:
+			/* it's a board command */
+			mb->command = chdlc_udp_pkt->cblock.command;
+			mb->buffer_length = chdlc_udp_pkt->cblock.buffer_length;
+			if (mb->buffer_length) {
+				memcpy(&mb->data, (unsigned char *) chdlc_udp_pkt->
+							data, mb->buffer_length);
+	      		} 
+			/* run the command on the board */
+			err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+			if (err != COMMAND_OK) {
+				break;
+			}
+
+			/* copy the result back to our buffer */
+	         	memcpy(&chdlc_udp_pkt->cblock, mb, sizeof(cblock_t)); 
+			
+			if (mb->buffer_length) {
+	         		memcpy(&chdlc_udp_pkt->data, &mb->data, 
+								mb->buffer_length); 
+	      		}
+
+		} /* end of switch */
+     	} /* end of else */
+
+     	/* Fill UDP TTL */
+	chdlc_udp_pkt->ip_pkt.ttl = card->wandev.ttl; 
+
+     	len = reply_udp(chdlc_priv_area->udp_pkt_data, mb->buffer_length);
+	
+     	if(chdlc_priv_area->udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+		if(!chdlc_send(card, chdlc_priv_area->udp_pkt_data, len)) {
+			++ card->wandev.stats.tx_packets;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+			card->wandev.stats.tx_bytes += len;
+#endif
+		}
+	} else {	
+	
+		/* Pass it up the stack
+    		   Allocate socket buffer */
+		if ((new_skb = dev_alloc_skb(len)) != NULL) {
+			/* copy data into new_skb */
+
+ 	    		buf = skb_put(new_skb, len);
+  	    		memcpy(buf, chdlc_priv_area->udp_pkt_data, len);
+
+            		/* Decapsulate pkt and pass it up the protocol stack */
+	    		new_skb->protocol = htons(ETH_P_IP);
+            		new_skb->dev = dev;
+	    		new_skb->mac.raw  = new_skb->data;
+	
+			netif_rx(new_skb);
+		} else {
+	    	
+			printk(KERN_INFO "%s: no socket buffers available!\n",
+					card->devname);
+  		}
+    	}
+ 
+	chdlc_priv_area->udp_pkt_lgth = 0;
+ 	
+	return 0;
+}
+
+/*============================================================================
+ * Initialize Receive and Transmit Buffers.
+ */
+
+static void init_chdlc_tx_rx_buff( sdla_t* card, netdevice_t *dev )
+{
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+	CHDLC_TX_STATUS_EL_CFG_STRUCT *tx_config;
+	CHDLC_RX_STATUS_EL_CFG_STRUCT *rx_config;
+	char err;
+	
+	mb->buffer_length = 0;
+	mb->command = READ_CHDLC_CONFIGURATION;
+	err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+
+	if(err != COMMAND_OK) {
+		chdlc_error(card,err,mb);
+		return;
+	}
+
+	if(card->hw.type == SDLA_S514) {
+		tx_config = (CHDLC_TX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase +
+                (((CHDLC_CONFIGURATION_STRUCT *)mb->data)->
+                            ptr_CHDLC_Tx_stat_el_cfg_struct));
+        	rx_config = (CHDLC_RX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase +
+                (((CHDLC_CONFIGURATION_STRUCT *)mb->data)->
+                            ptr_CHDLC_Rx_stat_el_cfg_struct));
+
+       		/* Setup Head and Tails for buffers */
+        	card->u.c.txbuf_base = (void *)(card->hw.dpmbase +
+                tx_config->base_addr_Tx_status_elements);
+        	card->u.c.txbuf_last = 
+		(CHDLC_DATA_TX_STATUS_EL_STRUCT *)  
+                card->u.c.txbuf_base +
+		(tx_config->number_Tx_status_elements - 1);
+
+        	card->u.c.rxbuf_base = (void *)(card->hw.dpmbase +
+                rx_config->base_addr_Rx_status_elements);
+        	card->u.c.rxbuf_last =
+		(CHDLC_DATA_RX_STATUS_EL_STRUCT *)
+                card->u.c.rxbuf_base +
+		(rx_config->number_Rx_status_elements - 1);
+
+ 		/* Set up next pointer to be used */
+        	card->u.c.txbuf = (void *)(card->hw.dpmbase +
+                tx_config->next_Tx_status_element_to_use);
+        	card->u.c.rxmb = (void *)(card->hw.dpmbase +
+                rx_config->next_Rx_status_element_to_use);
+	}
+        else {
+                tx_config = (CHDLC_TX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase +
+			(((CHDLC_CONFIGURATION_STRUCT *)mb->data)->
+			ptr_CHDLC_Tx_stat_el_cfg_struct % SDLA_WINDOWSIZE));
+
+                rx_config = (CHDLC_RX_STATUS_EL_CFG_STRUCT *)(card->hw.dpmbase +
+			(((CHDLC_CONFIGURATION_STRUCT *)mb->data)->
+			ptr_CHDLC_Rx_stat_el_cfg_struct % SDLA_WINDOWSIZE));
+
+                /* Setup Head and Tails for buffers */
+                card->u.c.txbuf_base = (void *)(card->hw.dpmbase +
+		(tx_config->base_addr_Tx_status_elements % SDLA_WINDOWSIZE));
+                card->u.c.txbuf_last =
+		(CHDLC_DATA_TX_STATUS_EL_STRUCT *)card->u.c.txbuf_base
+		+ (tx_config->number_Tx_status_elements - 1);
+                card->u.c.rxbuf_base = (void *)(card->hw.dpmbase +
+		(rx_config->base_addr_Rx_status_elements % SDLA_WINDOWSIZE));
+                card->u.c.rxbuf_last = 
+		(CHDLC_DATA_RX_STATUS_EL_STRUCT *)card->u.c.rxbuf_base
+		+ (rx_config->number_Rx_status_elements - 1);
+
+                 /* Set up next pointer to be used */
+                card->u.c.txbuf = (void *)(card->hw.dpmbase +
+		(tx_config->next_Tx_status_element_to_use % SDLA_WINDOWSIZE));
+                card->u.c.rxmb = (void *)(card->hw.dpmbase +
+		(rx_config->next_Rx_status_element_to_use % SDLA_WINDOWSIZE));
+        }
+
+        /* Setup Actual Buffer Start and end addresses */
+        card->u.c.rx_base = rx_config->base_addr_Rx_buffer;
+        card->u.c.rx_top  = rx_config->end_addr_Rx_buffer;
+
+}
+
+/*=============================================================================
+ * Perform Interrupt Test by running READ_CHDLC_CODE_VERSION command MAX_INTR
+ * _TEST_COUNTER times.
+ */
+static int intr_test( sdla_t* card)
+{
+	CHDLC_MAILBOX_STRUCT* mb = card->mbox;
+	int err,i;
+
+	Intr_test_counter = 0;
+
+	/* The critical flag is unset because during intialization (if_open) 
+	 * we want the interrupts to be enabled so that when the wpc_isr is
+	 * called it does not exit due to critical flag set.
+	 */ 
+
+	err = chdlc_set_intr_mode(card, APP_INT_ON_COMMAND_COMPLETE);
+
+	if (err == CMD_OK) { 
+		for (i = 0; i < MAX_INTR_TEST_COUNTER; i ++) {	
+			mb->buffer_length  = 0;
+			mb->command = READ_CHDLC_CODE_VERSION;
+			err = sdla_exec(mb) ? mb->return_code : CMD_TIMEOUT;
+		}
+	}
+	else {
+		return err;
+	}
+
+	err = chdlc_set_intr_mode(card, 0);
+
+	if (err != CMD_OK)
+		return err;
+
+	return 0;
+}
+
+/*==============================================================================
+ * Determine what type of UDP call it is. CPIPEAB ?
+ */
+static int udp_pkt_type(struct sk_buff *skb, sdla_t* card)
+{
+	 chdlc_udp_pkt_t *chdlc_udp_pkt = (chdlc_udp_pkt_t *)skb->data;
+
+	if (!strncmp(chdlc_udp_pkt->wp_mgmt.signature,UDPMGMT_SIGNATURE,8) &&
+	   (chdlc_udp_pkt->udp_pkt.udp_dst_port == ntohs(card->wandev.udp_port)) &&
+	   (chdlc_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) &&
+	   (chdlc_udp_pkt->wp_mgmt.request_reply == UDPMGMT_REQUEST)) {
+		return UDP_CPIPE_TYPE;
+	}
+	else return UDP_INVALID_TYPE;
+}
+
+/*============================================================================
+ * Set PORT state.
+ */
+static void port_set_state (sdla_t *card, int state)
+{
+	netdevice_t *dev = card->wandev.dev;
+	chdlc_private_area_t *chdlc_priv_area = dev->priv;
+
+        if (card->u.c.state != state)
+        {
+                switch (state)
+                {
+                case WAN_CONNECTED:
+                        printk (KERN_INFO "%s: HDLC link connected!\n",
+                                card->devname);
+                      break;
+
+                case WAN_CONNECTING:
+                        printk (KERN_INFO "%s: HDLC link connecting...\n",
+                                card->devname);
+                        break;
+
+                case WAN_DISCONNECTED:
+                        printk (KERN_INFO "%s: HDLC link disconnected!\n",
+                                card->devname);
+                        break;
+                }
+
+                card->wandev.state = card->u.c.state = state;
+		chdlc_priv_area->common.state = state;
+        }
+}
+
+void s508_lock (sdla_t *card, unsigned long *smp_flags)
+{
+      #if defined(__SMP__) || defined(LINUX_2_4)
+	spin_lock_irqsave(&card->wandev.lock, *smp_flags);
+        if (card->next){
+		/* It is ok to use spin_lock here, since we
+		 * already turned off interrupts */
+        	spin_lock(&card->next->wandev.lock);
+	}
+      #else
+	disable_irq(card->hw.irq);
+      #endif
+}
+
+void s508_unlock (sdla_t *card, unsigned long *smp_flags)
+{
+      #if defined(__SMP__) || defined(LINUX_2_4)
+	if (card->next){
+		spin_unlock(&card->next->wandev.lock);
+	}
+	spin_unlock_irqrestore(&card->wandev.lock, *smp_flags);
+      #else
+	enable_irq(card->hw.irq);
+      #endif           
+}
+
+
+
+/*===========================================================================
+ * config_chdlc
+ *
+ *	Configure the chdlc protocol and enable communications.		
+ *
+ *   	The if_open() function binds this function to the poll routine.
+ *      Therefore, this function will run every time the chdlc interface
+ *      is brought up. We cannot run this function from the if_open 
+ *      because if_open does not have access to the remote IP address.
+ *      
+ *	If the communications are not enabled, proceed to configure
+ *      the card and enable communications.
+ *
+ *      If the communications are enabled, it means that the interface
+ *      was shutdown by ether the user or driver. In this case, we 
+ *      have to check that the IP addresses have not changed.  If
+ *      the IP addresses have changed, we have to reconfigure the firmware
+ *      and update the changed IP addresses.  Otherwise, just exit.
+ *
+ */
+
+static int config_chdlc (sdla_t *card)
+{
+	netdevice_t *dev = card->wandev.dev;
+	SHARED_MEMORY_INFO_STRUCT *flags = card->u.c.flags;
+
+	if (card->u.c.comm_enabled){
+		chdlc_comm_disable(card);
+		port_set_state(card, WAN_DISCONNECTED);
+	}
+
+	if (set_chdlc_config(card)) {
+		printk(KERN_INFO "%s: CHDLC Configuration Failed!\n",
+				card->devname);
+		return 0;
+	}
+	init_chdlc_tx_rx_buff(card, dev);
+
+	/* Set interrupt mode and mask */
+        if (chdlc_set_intr_mode(card, APP_INT_ON_RX_FRAME |
+                		APP_INT_ON_GLOBAL_EXCEP_COND |
+                		APP_INT_ON_TX_FRAME |
+                		APP_INT_ON_CHDLC_EXCEP_COND | APP_INT_ON_TIMER)){
+		printk (KERN_INFO "%s: Failed to set interrupt triggers!\n",
+				card->devname);
+		return 0;	
+        }
+	
+
+	/* Mask the Transmit and Timer interrupt */
+	flags->interrupt_info_struct.interrupt_permission &= 
+		~(APP_INT_ON_TX_FRAME | APP_INT_ON_TIMER);
+
+
+	if (chdlc_comm_enable(card) != 0) {
+		printk(KERN_INFO "%s: Failed to enable chdlc communications!\n",
+				card->devname);
+		flags->interrupt_info_struct.interrupt_permission = 0;
+		card->u.c.comm_enabled=0;
+		chdlc_set_intr_mode(card,0);
+		return 0;
+	}
+
+	/* Initialize Rx/Tx buffer control fields */
+	port_set_state(card, WAN_CONNECTING);
+	return 0; 
+}
+
+
+static void send_ppp_term_request (netdevice_t *dev)
+{
+	struct sk_buff *new_skb;
+	unsigned char *buf;
+
+	if ((new_skb = dev_alloc_skb(8)) != NULL) {
+		/* copy data into new_skb */
+
+		buf = skb_put(new_skb, 8);
+		sprintf(buf,"%c%c%c%c%c%c%c%c", 0xFF,0x03,0xC0,0x21,0x05,0x98,0x00,0x07);
+
+		/* Decapsulate pkt and pass it up the protocol stack */
+		new_skb->protocol = htons(ETH_P_WAN_PPP);
+		new_skb->dev = dev;
+		new_skb->mac.raw  = new_skb->data;
+
+		netif_rx(new_skb);
+	}
+}
+
+
+/****** End ****************************************************************/
diff -urN linux-2.4.4-pre2/drivers/parport/ChangeLog linux/drivers/parport/ChangeLog
--- linux-2.4.4-pre2/drivers/parport/ChangeLog	Sun Mar 25 18:22:01 2001
+++ linux/drivers/parport/ChangeLog	Mon Dec  3 22:34:50 2001
@@ -1,3 +1,20 @@
+2001-04-08  Tim Waugh  <twaugh@redhat.com>
+
+      * parport_pc.c (parport_pc_save_state): Read from the soft copy of
+      the control port.
+      (parport_pc_restore_state): Update the soft copy of the control
+      port.
+
+2001-03-26  Tim Waugh  <twaugh@redhat.com>
+
+	* share.c (parport_find_number, parport_find_base): Trigger
+	a lowlevel driver load if there are no ports yet.
+
+2001-03-26  Tim Waugh  <twaugh@redhat.com>
+
+	* parport_pc.c (parport_ECP_supported): Remove the IRQ conflict
+	check since it seems totally unreliable.
+
 2001-03-02  Tim Waugh  <twaugh@redhat.com>
 
 	* ieee1284_ops.c (parport_ieee1284_read_nibble): Reset nAutoFd
diff -urN linux-2.4.4-pre2/drivers/parport/parport_pc.c linux/drivers/parport/parport_pc.c
--- linux-2.4.4-pre2/drivers/parport/parport_pc.c	Mon Mar 26 15:41:19 2001
+++ linux/drivers/parport/parport_pc.c	Mon Dec  3 22:34:50 2001
@@ -347,15 +347,16 @@
 void parport_pc_save_state(struct parport *p, struct parport_state *s)
 {
 	const struct parport_pc_private *priv = p->physport->private_data;
-	s->u.pc.ctr = inb (CONTROL (p));
+	s->u.pc.ctr = priv->ctr;
 	if (priv->ecr)
 		s->u.pc.ecr = inb (ECONTROL (p));
 }
 
 void parport_pc_restore_state(struct parport *p, struct parport_state *s)
 {
-	const struct parport_pc_private *priv = p->physport->private_data;
+	struct parport_pc_private *priv = p->physport->private_data;
 	outb (s->u.pc.ctr, CONTROL (p));
+	priv->ctr = s->u.pc.ctr;
 	if (priv->ecr)
 		outb (s->u.pc.ecr, ECONTROL (p));
 }
diff -urN linux-2.4.4-pre2/drivers/pcmcia/hd64465_ss.c linux/drivers/pcmcia/hd64465_ss.c
--- linux-2.4.4-pre2/drivers/pcmcia/hd64465_ss.c	Fri Feb 16 16:02:36 2001
+++ linux/drivers/pcmcia/hd64465_ss.c	Mon Dec  3 22:34:50 2001
@@ -30,7 +30,6 @@
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
diff -urN linux-2.4.4-pre2/drivers/s390/char/ctrlchar.h linux/drivers/s390/char/ctrlchar.h
--- linux-2.4.4-pre2/drivers/s390/char/ctrlchar.h	Mon Dec  3 22:34:41 2001
+++ linux/drivers/s390/char/ctrlchar.h	Mon Dec  3 22:34:50 2001
@@ -7,7 +7,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/tty.h>
 
diff -urN linux-2.4.4-pre2/drivers/s390/char/hwc_tty.c linux/drivers/s390/char/hwc_tty.c
--- linux-2.4.4-pre2/drivers/s390/char/hwc_tty.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/s390/char/hwc_tty.c	Mon Dec  3 22:34:50 2001
@@ -9,6 +9,7 @@
  *  Thanks to Martin Schwidefsky.
  */
 
+#include <linux/config.h>
 #include <linux/major.h>
 #include <linux/termios.h>
 #include <linux/tty.h>
diff -urN linux-2.4.4-pre2/drivers/s390/char/tapeblock.c linux/drivers/s390/char/tapeblock.c
--- linux-2.4.4-pre2/drivers/s390/char/tapeblock.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/s390/char/tapeblock.c	Mon Dec  3 22:34:50 2001
@@ -14,6 +14,7 @@
  */
 
 #include "tapedefs.h"
+#include <linux/config.h>
 #include <linux/blkdev.h>
 #include <linux/blk.h>
 #include <linux/version.h>
diff -urN linux-2.4.4-pre2/drivers/s390/char/tapeblock.h linux/drivers/s390/char/tapeblock.h
--- linux-2.4.4-pre2/drivers/s390/char/tapeblock.h	Mon Dec  3 22:34:41 2001
+++ linux/drivers/s390/char/tapeblock.h	Mon Dec  3 22:34:50 2001
@@ -15,6 +15,7 @@
 
 #ifndef TAPEBLOCK_H
 #define TAPEBLOCK_H
+#include <linux/config.h>
 #define PARTN_BITS 0
 
 #define TAPEBLOCK_READAHEAD 30
diff -urN linux-2.4.4-pre2/drivers/s390/char/tapechar.c linux/drivers/s390/char/tapechar.c
--- linux-2.4.4-pre2/drivers/s390/char/tapechar.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/s390/char/tapechar.c	Mon Dec  3 22:34:50 2001
@@ -14,6 +14,7 @@
  */
 
 #include "tapedefs.h"
+#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/types.h>
 #include <linux/proc_fs.h>
diff -urN linux-2.4.4-pre2/drivers/s390/char/tapechar.h linux/drivers/s390/char/tapechar.h
--- linux-2.4.4-pre2/drivers/s390/char/tapechar.h	Mon Dec  3 22:34:41 2001
+++ linux/drivers/s390/char/tapechar.h	Mon Dec  3 22:34:50 2001
@@ -15,6 +15,7 @@
 
 #ifndef TAPECHAR_H
 #define TAPECHAR_H
+#include <linux/config.h>
 #define TAPECHAR_DEFAULTMODE 0020644
 #define  TAPE_MAJOR                    0        /* get dynamic major since no major officialy defined for tape */
 /*
diff -urN linux-2.4.4-pre2/drivers/s390/net/iucv.c linux/drivers/s390/net/iucv.c
--- linux-2.4.4-pre2/drivers/s390/net/iucv.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/s390/net/iucv.c	Mon Dec  3 22:34:50 2001
@@ -10,7 +10,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/spinlock.h>
 #include <linux/kernel.h>
diff -urN linux-2.4.4-pre2/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile
--- linux-2.4.4-pre2/drivers/sbus/char/Makefile	Sun Mar 25 18:14:20 2001
+++ linux/drivers/sbus/char/Makefile	Mon Dec  3 22:34:51 2001
@@ -9,10 +9,11 @@
 
 O_TARGET := sunchar.o
 
-export-objs := su.o
+export-objs := su.o bbc_i2c.o
 obj-y := sunkbd.o sunkbdmap.o sunmouse.o sunserial.o zs.o
 
 vfc-objs := vfc_dev.o vfc_i2c.o
+bbc-objs := bbc_i2c.o bbc_envctrl.o
 
 obj-$(CONFIG_PCI) += su.o pcikbd.o
 
@@ -29,6 +30,7 @@
 obj-$(CONFIG_SUN_AURORA)		+= aurora.o
 obj-$(CONFIG_TADPOLE_TS102_UCTRL)	+= uctrl.o
 obj-$(CONFIG_SUN_JSFLASH)		+= jsflash.o
+obj-$(CONFIG_BBC_I2C)			+= bbc.o
 
 include $(TOPDIR)/Rules.make
 
@@ -36,3 +38,6 @@
 
 vfc.o: $(vfc-objs)
 	$(LD) -r -o $@ $(vfc-objs)
+
+bbc.o: $(bbc-objs)
+	$(LD) -r -o $@ $(bbc-objs)
diff -urN linux-2.4.4-pre2/drivers/sbus/char/bbc_envctrl.c linux/drivers/sbus/char/bbc_envctrl.c
--- linux-2.4.4-pre2/drivers/sbus/char/bbc_envctrl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/bbc_envctrl.c	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,646 @@
+/* $Id: bbc_envctrl.c,v 1.4 2001/04/06 16:48:08 davem Exp $
+ * bbc_envctrl.c: UltraSPARC-III environment control driver.
+ *
+ * Copyright (C) 2001 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <asm/oplib.h>
+#include <asm/ebus.h>
+#define __KERNEL_SYSCALLS__
+static int errno;
+#include <asm/unistd.h>
+
+#include "bbc_i2c.h"
+#include "max1617.h"
+
+#undef ENVCTRL_TRACE
+
+/* WARNING: Making changes to this driver is very dangerous.
+ *          If you misprogram the sensor chips they can
+ *          cut the power on you instantly.
+ */
+
+/* Two temperature sensors exist in the SunBLADE-1000 enclosure.
+ * Both are implemented using max1617 i2c devices.  Each max1617
+ * monitors 2 temperatures, one for one of the cpu dies and the other
+ * for the ambient temperature.
+ *
+ * The max1617 is capable of being programmed with power-off
+ * temperature values, one low limit and one high limit.  These
+ * can be controlled independantly for the cpu or ambient temperature.
+ * If a limit is violated, the power is simply shut off.  The frequency
+ * with which the max1617 does temperature sampling can be controlled
+ * as well.
+ *
+ * Three fans exist inside the machine, all three are controlled with
+ * an i2c digital to analog converter.  There is a fan directed at the
+ * two processor slots, another for the rest of the enclosure, and the
+ * third is for the power supply.  The first two fans may be speed
+ * controlled by changing the voltage fed to them.  The third fan may
+ * only be completely off or on.  The third fan is meant to only be
+ * disabled/enabled when entering/exiting the lowest power-saving
+ * mode of the machine.
+ *
+ * An environmental control kernel thread periodically monitors all
+ * temperature sensors.  Based upon the samples it will adjust the
+ * fan speeds to try and keep the system within a certain temperature
+ * range (the goal being to make the fans as quiet as possible without
+ * allowing the system to get too hot).
+ *
+ * If the temperature begins to rise/fall outside of the acceptable
+ * operating range, a periodic warning will be sent to the kernel log.
+ * The fans will be put on full blast to attempt to deal with this
+ * situation.  After exceeding the acceptable operating range by a
+ * certain threshold, the kernel thread will shut down the system.
+ * Here, the thread is attempting to shut the machine down cleanly
+ * before the hardware based power-off event is triggered.
+ */
+
+/* These settings are in celcius.  We use these defaults only
+ * if we cannot interrogate the cpu-fru SEEPROM.
+ */
+struct temp_limits {
+	s8 high_pwroff, high_shutdown, high_warn;
+	s8 low_warn, low_shutdown, low_pwroff;
+};
+
+static struct temp_limits cpu_temp_limits[2] = {
+	{ 100, 85, 80, 5, -5, -10 },
+	{ 100, 85, 80, 5, -5, -10 },
+};
+
+static struct temp_limits amb_temp_limits[2] = {
+	{ 65, 55, 40, 5, -5, -10 },
+	{ 65, 55, 40, 5, -5, -10 },
+};
+
+enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX };
+
+struct bbc_cpu_temperature {
+	struct bbc_cpu_temperature	*next;
+
+	struct bbc_i2c_client		*client;
+	int				index;
+
+	/* Current readings, and history. */
+	s8				curr_cpu_temp;
+	s8				curr_amb_temp;
+	s8				prev_cpu_temp;
+	s8				prev_amb_temp;
+	s8				avg_cpu_temp;
+	s8				avg_amb_temp;
+
+	int				sample_tick;
+
+	enum fan_action			fan_todo[2];
+#define FAN_AMBIENT	0
+#define FAN_CPU		1
+};
+
+struct bbc_cpu_temperature *all_bbc_temps;
+
+struct bbc_fan_control {
+	struct bbc_fan_control 	*next;
+
+	struct bbc_i2c_client 	*client;
+	int 			index;
+
+	int			psupply_fan_on;
+	int			cpu_fan_speed;
+	int			system_fan_speed;
+};
+
+struct bbc_fan_control *all_bbc_fans;
+
+#define CPU_FAN_REG	0xf0
+#define SYS_FAN_REG	0xf2
+#define PSUPPLY_FAN_REG	0xf4
+
+#define FAN_SPEED_MIN	0x0c
+#define FAN_SPEED_MAX	0x3f
+
+#define PSUPPLY_FAN_ON	0x1f
+#define PSUPPLY_FAN_OFF	0x00
+
+static void set_fan_speeds(struct bbc_fan_control *fp)
+{
+	/* Put temperatures into range so we don't mis-program
+	 * the hardware.
+	 */
+	if (fp->cpu_fan_speed < FAN_SPEED_MIN)
+		fp->cpu_fan_speed = FAN_SPEED_MIN;
+	if (fp->cpu_fan_speed > FAN_SPEED_MAX)
+		fp->cpu_fan_speed = FAN_SPEED_MAX;
+	if (fp->system_fan_speed < FAN_SPEED_MIN)
+		fp->system_fan_speed = FAN_SPEED_MIN;
+	if (fp->system_fan_speed > FAN_SPEED_MAX)
+		fp->system_fan_speed = FAN_SPEED_MAX;
+#ifdef ENVCTRL_TRACE
+	printk("fan%d: Changed fan speed to cpu(%02x) sys(%02x)\n",
+	       fp->index,
+	       fp->cpu_fan_speed, fp->system_fan_speed);
+#endif
+
+	bbc_i2c_writeb(fp->client, fp->cpu_fan_speed, CPU_FAN_REG);
+	bbc_i2c_writeb(fp->client, fp->system_fan_speed, SYS_FAN_REG);
+	bbc_i2c_writeb(fp->client,
+		       (fp->psupply_fan_on ?
+			PSUPPLY_FAN_ON : PSUPPLY_FAN_OFF),
+		       PSUPPLY_FAN_REG);
+}
+
+static void get_current_temps(struct bbc_cpu_temperature *tp)
+{
+	tp->prev_amb_temp = tp->curr_amb_temp;
+	bbc_i2c_readb(tp->client,
+		      (unsigned char *) &tp->curr_amb_temp,
+		      MAX1617_AMB_TEMP);
+	tp->prev_cpu_temp = tp->curr_cpu_temp;
+	bbc_i2c_readb(tp->client,
+		      (unsigned char *) &tp->curr_cpu_temp,
+		      MAX1617_CPU_TEMP);
+#ifdef ENVCTRL_TRACE
+	printk("temp%d: cpu(%d C) amb(%d C)\n",
+	       tp->index,
+	       (int) tp->curr_cpu_temp, (int) tp->curr_amb_temp);
+#endif
+}
+
+
+static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp)
+{
+	static int shutting_down = 0;
+	static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+	char *argv[] = { "/sbin/shutdown", "-h", "now", NULL };
+	char *type = "???";
+	s8 val = -1;
+
+	if (shutting_down != 0)
+		return;
+
+	if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
+	    tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
+		type = "ambient";
+		val = tp->curr_amb_temp;
+	} else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
+		   tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
+		type = "CPU";
+		val = tp->curr_cpu_temp;
+	}
+
+	printk(KERN_CRIT "temp%d: Outside of safe %s "
+	       "operating temperature, %d C.\n",
+	       tp->index, type, val);
+
+	printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n");
+
+	shutting_down = 1;
+	if (execve("/sbin/shutdown", argv, envp) < 0)
+		printk(KERN_CRIT "envctrl: shutdown execution failed\n");
+}
+
+#define WARN_INTERVAL	(30 * HZ)
+
+static void analyze_ambient_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
+{
+	int ret = 0;
+
+	if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
+		if (tp->curr_amb_temp >=
+		    amb_temp_limits[tp->index].high_warn) {
+			printk(KERN_WARNING "temp%d: "
+			       "Above safe ambient operating temperature, %d C.\n",
+			       tp->index, (int) tp->curr_amb_temp);
+			ret = 1;
+		} else if (tp->curr_amb_temp <
+			   amb_temp_limits[tp->index].low_warn) {
+			printk(KERN_WARNING "temp%d: "
+			       "Below safe ambient operating temperature, %d C.\n",
+			       tp->index, (int) tp->curr_amb_temp);
+			ret = 1;
+		}
+		if (ret)
+			*last_warn = jiffies;
+	} else if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_warn ||
+		   tp->curr_amb_temp < amb_temp_limits[tp->index].low_warn)
+		ret = 1;
+
+	/* Now check the shutdown limits. */
+	if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
+	    tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
+		do_envctrl_shutdown(tp);
+		ret = 1;
+	}
+
+	if (ret) {
+		tp->fan_todo[FAN_AMBIENT] = FAN_FULLBLAST;
+	} else if ((tick & (8 - 1)) == 0) {
+		s8 amb_goal_hi = amb_temp_limits[tp->index].high_warn - 10;
+		s8 amb_goal_lo;
+
+		amb_goal_lo = amb_goal_hi - 3;
+
+		/* We do not try to avoid 'too cold' events.  Basically we
+		 * only try to deal with over-heating and fan noise reduction.
+		 */
+		if (tp->avg_amb_temp < amb_goal_hi) {
+			if (tp->avg_amb_temp >= amb_goal_lo)
+				tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
+			else
+				tp->fan_todo[FAN_AMBIENT] = FAN_SLOWER;
+		} else {
+			tp->fan_todo[FAN_AMBIENT] = FAN_FASTER;
+		}
+	} else {
+		tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
+	}
+}
+
+static void analyze_cpu_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
+{
+	int ret = 0;
+
+	if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
+		if (tp->curr_cpu_temp >=
+		    cpu_temp_limits[tp->index].high_warn) {
+			printk(KERN_WARNING "temp%d: "
+			       "Above safe CPU operating temperature, %d C.\n",
+			       tp->index, (int) tp->curr_cpu_temp);
+			ret = 1;
+		} else if (tp->curr_cpu_temp <
+			   cpu_temp_limits[tp->index].low_warn) {
+			printk(KERN_WARNING "temp%d: "
+			       "Below safe CPU operating temperature, %d C.\n",
+			       tp->index, (int) tp->curr_cpu_temp);
+			ret = 1;
+		}
+		if (ret)
+			*last_warn = jiffies;
+	} else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_warn ||
+		   tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_warn)
+		ret = 1;
+
+	/* Now check the shutdown limits. */
+	if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
+	    tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
+		do_envctrl_shutdown(tp);
+		ret = 1;
+	}
+
+	if (ret) {
+		tp->fan_todo[FAN_CPU] = FAN_FULLBLAST;
+	} else if ((tick & (8 - 1)) == 0) {
+		s8 cpu_goal_hi = cpu_temp_limits[tp->index].high_warn - 10;
+		s8 cpu_goal_lo;
+
+		cpu_goal_lo = cpu_goal_hi - 3;
+
+		/* We do not try to avoid 'too cold' events.  Basically we
+		 * only try to deal with over-heating and fan noise reduction.
+		 */
+		if (tp->avg_cpu_temp < cpu_goal_hi) {
+			if (tp->avg_cpu_temp >= cpu_goal_lo)
+				tp->fan_todo[FAN_CPU] = FAN_SAME;
+			else
+				tp->fan_todo[FAN_CPU] = FAN_SLOWER;
+		} else {
+			tp->fan_todo[FAN_CPU] = FAN_FASTER;
+		}
+	} else {
+		tp->fan_todo[FAN_CPU] = FAN_SAME;
+	}
+}
+
+static void analyze_temps(struct bbc_cpu_temperature *tp, unsigned long *last_warn)
+{
+	tp->avg_amb_temp = (s8)((int)((int)tp->avg_amb_temp + (int)tp->curr_amb_temp) / 2);
+	tp->avg_cpu_temp = (s8)((int)((int)tp->avg_cpu_temp + (int)tp->curr_cpu_temp) / 2);
+
+	analyze_ambient_temp(tp, last_warn, tp->sample_tick);
+	analyze_cpu_temp(tp, last_warn, tp->sample_tick);
+
+	tp->sample_tick++;
+}
+
+static enum fan_action prioritize_fan_action(int which_fan)
+{
+	struct bbc_cpu_temperature *tp;
+	enum fan_action decision = FAN_STATE_MAX;
+
+	/* Basically, prioritize what the temperature sensors
+	 * recommend we do, and perform that action on all the
+	 * fans.
+	 */
+	for (tp = all_bbc_temps; tp; tp = tp->next) {
+		if (tp->fan_todo[which_fan] == FAN_FULLBLAST) {
+			decision = FAN_FULLBLAST;
+			break;
+		}
+		if (tp->fan_todo[which_fan] == FAN_SAME &&
+		    decision != FAN_FASTER)
+			decision = FAN_SAME;
+		else if (tp->fan_todo[which_fan] == FAN_FASTER)
+			decision = FAN_FASTER;
+		else if (decision != FAN_FASTER &&
+			 decision != FAN_SAME &&
+			 tp->fan_todo[which_fan] == FAN_SLOWER)
+			decision = FAN_SLOWER;
+	}
+	if (decision == FAN_STATE_MAX)
+		decision = FAN_SAME;
+
+	return decision;
+}
+
+static int maybe_new_ambient_fan_speed(struct bbc_fan_control *fp)
+{
+	enum fan_action decision = prioritize_fan_action(FAN_AMBIENT);
+	int ret;
+
+	if (decision == FAN_SAME)
+		return 0;
+
+	ret = 1;
+	if (decision == FAN_FULLBLAST) {
+		if (fp->system_fan_speed >= FAN_SPEED_MAX)
+			ret = 0;
+		else
+			fp->system_fan_speed = FAN_SPEED_MAX;
+	} else {
+		if (decision == FAN_FASTER) {
+			if (fp->system_fan_speed >= FAN_SPEED_MAX)
+				ret = 0;
+			else
+				fp->system_fan_speed += 2;
+		} else {
+			int orig_speed = fp->system_fan_speed;
+
+			if (orig_speed <= FAN_SPEED_MIN ||
+			    orig_speed <= (fp->cpu_fan_speed - 3))
+				ret = 0;
+			else
+				fp->system_fan_speed -= 1;
+		}
+	}
+
+	return ret;
+}
+
+static int maybe_new_cpu_fan_speed(struct bbc_fan_control *fp)
+{
+	enum fan_action decision = prioritize_fan_action(FAN_CPU);
+	int ret;
+
+	if (decision == FAN_SAME)
+		return 0;
+
+	ret = 1;
+	if (decision == FAN_FULLBLAST) {
+		if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
+			ret = 0;
+		else
+			fp->cpu_fan_speed = FAN_SPEED_MAX;
+	} else {
+		if (decision == FAN_FASTER) {
+			if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
+				ret = 0;
+			else {
+				fp->cpu_fan_speed += 2;
+				if (fp->system_fan_speed <
+				    (fp->cpu_fan_speed - 3))
+					fp->system_fan_speed =
+						fp->cpu_fan_speed - 3;
+			}
+		} else {
+			if (fp->cpu_fan_speed <= FAN_SPEED_MIN)
+				ret = 0;
+			else
+				fp->cpu_fan_speed -= 1;
+		}
+	}
+
+	return ret;
+}
+
+static void maybe_new_fan_speeds(struct bbc_fan_control *fp)
+{
+	int new;
+
+	new  = maybe_new_ambient_fan_speed(fp);
+	new |= maybe_new_cpu_fan_speed(fp);
+
+	if (new)
+		set_fan_speeds(fp);
+}
+
+static void fans_full_blast(void)
+{
+	struct bbc_fan_control *fp;
+
+	/* Since we will not be monitoring things anymore, put
+	 * the fans on full blast.
+	 */
+	for (fp = all_bbc_fans; fp; fp = fp->next) {
+		fp->cpu_fan_speed = FAN_SPEED_MAX;
+		fp->system_fan_speed = FAN_SPEED_MAX;
+		fp->psupply_fan_on = 1;
+		set_fan_speeds(fp);
+	}
+}
+
+#define POLL_INTERVAL	(5 * HZ)
+static unsigned long last_warning_jiffies;
+static struct task_struct *kenvctrld_task;
+
+static int kenvctrld(void *__unused)
+{
+	daemonize();
+	strcpy(current->comm, "kenvctrld");
+	kenvctrld_task = current;
+
+	printk(KERN_INFO "bbc_envctrl: kenvctrld starting...\n");
+	last_warning_jiffies = jiffies - WARN_INTERVAL;
+	for (;;) {
+		struct bbc_cpu_temperature *tp;
+		struct bbc_fan_control *fp;
+
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(POLL_INTERVAL);
+		current->state = TASK_RUNNING;
+		if (signal_pending(current))
+			break;
+
+		for (tp = all_bbc_temps; tp; tp = tp->next) {
+			get_current_temps(tp);
+			analyze_temps(tp, &last_warning_jiffies);
+		}
+		for (fp = all_bbc_fans; fp; fp = fp->next)
+			maybe_new_fan_speeds(fp);
+	}
+	printk(KERN_INFO "bbc_envctrl: kenvctrld exiting...\n");
+
+	fans_full_blast();
+
+	return 0;
+}
+
+static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx)
+{
+	struct bbc_cpu_temperature *tp = kmalloc(sizeof(*tp), GFP_KERNEL);
+
+	if (!tp)
+		return;
+	memset(tp, 0, sizeof(*tp));
+	tp->client = bbc_i2c_attach(echild);
+	if (!tp->client) {
+		kfree(tp);
+		return;
+	}
+
+	tp->index = temp_idx;
+	{
+		struct bbc_cpu_temperature **tpp = &all_bbc_temps;
+		while (*tpp)
+			tpp = &((*tpp)->next);
+		tp->next = NULL;
+		*tpp = tp;
+	}
+
+	/* Tell it to convert once every 5 seconds, clear all cfg
+	 * bits.
+	 */
+	bbc_i2c_writeb(tp->client, 0x00, MAX1617_WR_CFG_BYTE);
+	bbc_i2c_writeb(tp->client, 0x02, MAX1617_WR_CVRATE_BYTE);
+
+	/* Program the hard temperature limits into the chip. */
+	bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].high_pwroff,
+		       MAX1617_WR_AMB_HIGHLIM);
+	bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].low_pwroff,
+		       MAX1617_WR_AMB_LOWLIM);
+	bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].high_pwroff,
+		       MAX1617_WR_CPU_HIGHLIM);
+	bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].low_pwroff,
+		       MAX1617_WR_CPU_LOWLIM);
+
+	get_current_temps(tp);
+	tp->prev_cpu_temp = tp->avg_cpu_temp = tp->curr_cpu_temp;
+	tp->prev_amb_temp = tp->avg_amb_temp = tp->curr_amb_temp;
+
+	tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
+	tp->fan_todo[FAN_CPU] = FAN_SAME;
+}
+
+static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx)
+{
+	struct bbc_fan_control *fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+
+	if (!fp)
+		return;
+	memset(fp, 0, sizeof(*fp));
+	fp->client = bbc_i2c_attach(echild);
+	if (!fp->client) {
+		kfree(fp);
+		return;
+	}
+
+	fp->index = fan_idx;
+
+	{
+		struct bbc_fan_control **fpp = &all_bbc_fans;
+		while (*fpp)
+			fpp = &((*fpp)->next);
+		fp->next = NULL;
+		*fpp = fp;
+	}
+
+	/* The i2c device controlling the fans is write-only.
+	 * So the only way to keep track of the current power
+	 * level fed to the fans is via software.  Choose half
+	 * power for cpu/system and 'on' fo the powersupply fan
+	 * and set it now.
+	 */
+	fp->psupply_fan_on = 1;
+	fp->cpu_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
+	fp->cpu_fan_speed += FAN_SPEED_MIN;
+	fp->system_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
+	fp->system_fan_speed += FAN_SPEED_MIN;
+
+	set_fan_speeds(fp);
+}
+
+void bbc_envctrl_init(void)
+{
+	struct linux_ebus_child *echild;
+	int temp_index = 0;
+	int fan_index = 0;
+	int devidx = 0;
+
+	while ((echild = bbc_i2c_getdev(devidx++)) != NULL) {
+		if (!strcmp(echild->prom_name, "temperature"))
+			attach_one_temp(echild, temp_index++);
+		if (!strcmp(echild->prom_name, "fan-control"))
+			attach_one_fan(echild, fan_index++);
+	}
+	if (temp_index != 0 && fan_index != 0)
+		kernel_thread(kenvctrld, NULL, CLONE_FS | CLONE_FILES);
+}
+
+static void destroy_one_temp(struct bbc_cpu_temperature *tp)
+{
+	bbc_i2c_detach(tp->client);
+	kfree(tp);
+}
+
+static void destroy_one_fan(struct bbc_fan_control *fp)
+{
+	bbc_i2c_detach(fp->client);
+	kfree(fp);
+}
+
+void bbc_envctrl_cleanup(void)
+{
+	struct bbc_cpu_temperature *tp;
+	struct bbc_fan_control *fp;
+
+	if (kenvctrld_task != NULL) {
+		force_sig(SIGKILL, kenvctrld_task);
+		for (;;) {
+			struct task_struct *p;
+			int found = 0;
+
+			read_lock(&tasklist_lock);
+			for_each_task(p) {
+				if (p == kenvctrld_task) {
+					found = 1;
+					break;
+				}
+			}
+			read_unlock(&tasklist_lock);
+			if (!found)
+				break;
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(HZ);
+			current->state = TASK_RUNNING;
+		}
+		kenvctrld_task = NULL;
+	}
+
+	tp = all_bbc_temps;
+	while (tp != NULL) {
+		struct bbc_cpu_temperature *next = tp->next;
+		destroy_one_temp(tp);
+		tp = next;
+	}
+	all_bbc_temps = NULL;
+
+	fp = all_bbc_fans;
+	while (fp != NULL) {
+		struct bbc_fan_control *next = fp->next;
+		destroy_one_fan(fp);
+		fp = next;
+	}
+	all_bbc_fans = NULL;
+}
diff -urN linux-2.4.4-pre2/drivers/sbus/char/bbc_i2c.c linux/drivers/sbus/char/bbc_i2c.c
--- linux-2.4.4-pre2/drivers/sbus/char/bbc_i2c.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/bbc_i2c.c	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,482 @@
+/* $Id: bbc_i2c.c,v 1.2 2001/04/02 09:59:08 davem Exp $
+ * bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III
+ *            platforms.
+ *
+ * Copyright (C) 2001 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <asm/oplib.h>
+#include <asm/ebus.h>
+#include <asm/spitfire.h>
+#include <asm/bbc.h>
+
+#include "bbc_i2c.h"
+
+/* Convert this driver to use i2c bus layer someday... */
+#define I2C_PCF_PIN	0x80
+#define I2C_PCF_ESO	0x40
+#define I2C_PCF_ES1	0x20
+#define I2C_PCF_ES2	0x10
+#define I2C_PCF_ENI	0x08
+#define I2C_PCF_STA	0x04
+#define I2C_PCF_STO	0x02
+#define I2C_PCF_ACK	0x01
+
+#define I2C_PCF_START    (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ENI | I2C_PCF_STA | I2C_PCF_ACK)
+#define I2C_PCF_STOP     (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK)
+#define I2C_PCF_REPSTART (              I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)
+#define I2C_PCF_IDLE     (I2C_PCF_PIN | I2C_PCF_ESO               | I2C_PCF_ACK)
+
+#define I2C_PCF_INI 0x40   /* 1 if not initialized */
+#define I2C_PCF_STS 0x20
+#define I2C_PCF_BER 0x10
+#define I2C_PCF_AD0 0x08
+#define I2C_PCF_LRB 0x08
+#define I2C_PCF_AAS 0x04
+#define I2C_PCF_LAB 0x02
+#define I2C_PCF_BB  0x01
+
+/* The BBC devices have two I2C controllers.  The first I2C controller
+ * connects mainly to configuration proms (NVRAM, cpu configuration,
+ * dimm types, etc.).  Whereas the second I2C controller connects to
+ * environmental control devices such as fans and temperature sensors.
+ * The second controller also connects to the smartcard reader, if present.
+ */
+
+#define NUM_CHILDREN	8
+struct bbc_i2c_bus {
+	struct bbc_i2c_bus		*next;
+	int				index;
+	spinlock_t			lock;
+	void				*i2c_bussel_reg;
+	void				*i2c_control_regs;
+	unsigned char			own, clock;
+
+	wait_queue_head_t		wq;
+	volatile int			waiting;
+
+	struct linux_ebus_device	*bus_edev;
+	struct {
+		struct linux_ebus_child	*device;
+		int			client_claimed;
+	} devs[NUM_CHILDREN];
+};
+
+static struct bbc_i2c_bus *all_bbc_i2c;
+
+struct bbc_i2c_client {
+	struct bbc_i2c_bus	*bp;
+	struct linux_ebus_child	*echild;
+	int			bus;
+	int			address;
+};
+
+static int find_device(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild)
+{
+	int i;
+
+	for (i = 0; i < NUM_CHILDREN; i++) {
+		if (bp->devs[i].device == echild) {
+			if (bp->devs[i].client_claimed)
+				return 0;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild, int val)
+{
+	int i;
+
+	for (i = 0; i < NUM_CHILDREN; i++) {
+		if (bp->devs[i].device == echild) {
+			bp->devs[i].client_claimed = val;
+			return;
+		}
+	}
+}
+
+#define claim_device(BP,ECHILD)		set_device_claimage(BP,ECHILD,1)
+#define release_device(BP,ECHILD)	set_device_claimage(BP,ECHILD,0)
+
+static struct bbc_i2c_bus *find_bus_for_device(struct linux_ebus_child *echild)
+{
+	struct bbc_i2c_bus *bp = all_bbc_i2c;
+
+	while (bp != NULL) {
+		if (find_device(bp, echild) != 0)
+			break;
+		bp = bp->next;
+	}
+
+	return bp;
+}
+
+struct linux_ebus_child *bbc_i2c_getdev(int index)
+{
+	struct bbc_i2c_bus *bp = all_bbc_i2c;
+	struct linux_ebus_child *echild = NULL;
+	int curidx = 0;
+
+	while (bp != NULL) {
+		struct bbc_i2c_bus *next = bp->next;
+		int i;
+
+		for (i = 0; i < NUM_CHILDREN; i++) {
+			if (!(echild = bp->devs[i].device))
+				break;
+			if (curidx == index)
+				goto out;
+			echild = NULL;
+			curidx++;
+		}
+		bp = next;
+	}
+out:
+	if (curidx == index)
+		return echild;
+	return NULL;
+}
+
+struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild)
+{
+	struct bbc_i2c_bus *bp = find_bus_for_device(echild);
+	struct bbc_i2c_client *client;
+
+	if (!bp)
+		return NULL;
+	client = kmalloc(sizeof(*client), GFP_KERNEL);
+	if (!client)
+		return NULL;
+	memset(client, 0, sizeof(*client));
+	client->bp = bp;
+	client->echild = echild;
+	client->bus = echild->resource[0].start;
+	client->address = echild->resource[1].start;
+
+	claim_device(bp, echild);
+
+	return client;
+}
+
+void bbc_i2c_detach(struct bbc_i2c_client *client)
+{
+	struct bbc_i2c_bus *bp = client->bp;
+	struct linux_ebus_child *echild = client->echild;
+
+	release_device(bp, echild);
+	kfree(client);
+}
+
+static int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int limit = 32;
+	int ret = 1;
+
+	bp->waiting = 1;
+	add_wait_queue(&bp->wq, &wait);
+	while (limit-- > 0) {
+		u8 val;
+
+		current->state = TASK_INTERRUPTIBLE;
+		*status = val = readb(bp->i2c_control_regs + 0);
+		if ((val & I2C_PCF_PIN) == 0) {
+			ret = 0;
+			break;
+		}
+		schedule_timeout(HZ/4);
+	}
+	remove_wait_queue(&bp->wq, &wait);
+	bp->waiting = 0;
+	current->state = TASK_RUNNING;
+
+	return ret;
+}
+
+int bbc_i2c_writeb(struct bbc_i2c_client *client, unsigned char val, int off)
+{
+	struct bbc_i2c_bus *bp = client->bp;
+	int address = client->address;
+	u8 status;
+	int ret = -1;
+
+	if (bp->i2c_bussel_reg != NULL)
+		writeb(client->bus, bp->i2c_bussel_reg);
+
+	writeb(address, bp->i2c_control_regs + 0x1);
+	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
+	if (wait_for_pin(bp, &status))
+		goto out;
+
+	writeb(off, bp->i2c_control_regs + 0x1);
+	if (wait_for_pin(bp, &status) ||
+	    (status & I2C_PCF_LRB) != 0)
+		goto out;
+
+	writeb(val, bp->i2c_control_regs + 0x1);
+	if (wait_for_pin(bp, &status))
+		goto out;
+
+	ret = 0;
+
+out:
+	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
+	return ret;
+}
+
+int bbc_i2c_readb(struct bbc_i2c_client *client, unsigned char *byte, int off)
+{
+	struct bbc_i2c_bus *bp = client->bp;
+	unsigned char address = client->address, status;
+	int ret = -1;
+
+	if (bp->i2c_bussel_reg != NULL)
+		writeb(client->bus, bp->i2c_bussel_reg);
+
+	writeb(address, bp->i2c_control_regs + 0x1);
+	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
+	if (wait_for_pin(bp, &status))
+		goto out;
+
+	writeb(off, bp->i2c_control_regs + 0x1);
+	if (wait_for_pin(bp, &status) ||
+	    (status & I2C_PCF_LRB) != 0)
+		goto out;
+
+	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
+
+	address |= 0x1; /* READ */
+
+	writeb(address, bp->i2c_control_regs + 0x1);
+	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
+	if (wait_for_pin(bp, &status))
+		goto out;
+
+	/* Set PIN back to one so the device sends the first
+	 * byte.
+	 */
+	(void) readb(bp->i2c_control_regs + 0x1);
+	if (wait_for_pin(bp, &status))
+		goto out;
+
+	writeb(I2C_PCF_ESO | I2C_PCF_ENI, bp->i2c_control_regs + 0x0);
+	*byte = readb(bp->i2c_control_regs + 0x1);
+	if (wait_for_pin(bp, &status))
+		goto out;
+
+	ret = 0;
+
+out:
+	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
+	(void) readb(bp->i2c_control_regs + 0x1);
+
+	return ret;
+}
+
+int bbc_i2c_write_buf(struct bbc_i2c_client *client,
+		      char *buf, int len, int off)
+{
+	int ret = 0;
+
+	while (len > 0) {
+		int err = bbc_i2c_writeb(client, *buf, off);
+
+		if (err < 0) {
+			ret = err;
+			break;
+		}
+
+		len--;
+		buf++;
+		off++;
+	}
+	return ret;
+}
+
+int bbc_i2c_read_buf(struct bbc_i2c_client *client,
+		     char *buf, int len, int off)
+{
+	int ret = 0;
+
+	while (len > 0) {
+		int err = bbc_i2c_readb(client, buf, off);
+		if (err < 0) {
+			ret = err;
+			break;
+		}
+		len--;
+		buf++;
+		off++;
+	}
+
+	return ret;
+}
+
+EXPORT_SYMBOL(bbc_i2c_getdev);
+EXPORT_SYMBOL(bbc_i2c_attach);
+EXPORT_SYMBOL(bbc_i2c_detach);
+EXPORT_SYMBOL(bbc_i2c_writeb);
+EXPORT_SYMBOL(bbc_i2c_readb);
+EXPORT_SYMBOL(bbc_i2c_write_buf);
+EXPORT_SYMBOL(bbc_i2c_read_buf);
+
+static void bbc_i2c_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct bbc_i2c_bus *bp = dev_id;
+
+	/* PIN going from set to clear is the only event which
+	 * makes the i2c assert an interrupt.
+	 */
+	if (bp->waiting &&
+	    !(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN))
+		wake_up(&bp->wq);
+}
+
+static void __init reset_one_i2c(struct bbc_i2c_bus *bp)
+{
+	writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);
+	writeb(bp->own, bp->i2c_control_regs + 0x1);
+	writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);
+	writeb(bp->clock, bp->i2c_control_regs + 0x1);
+	writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0);
+}
+
+static int __init attach_one_i2c(struct linux_ebus_device *edev, int index)
+{
+	struct bbc_i2c_bus *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
+	struct linux_ebus_child *echild;
+	int entry;
+
+	if (!bp)
+		return -ENOMEM;
+	memset(bp, 0, sizeof(*bp));
+
+	bp->i2c_control_regs = ioremap(edev->resource[0].start, 0x2);
+	if (!bp->i2c_control_regs)
+		goto fail;
+
+	if (edev->num_addrs == 2) {
+		bp->i2c_bussel_reg = ioremap(edev->resource[1].start, 0x1);
+		if (!bp->i2c_bussel_reg)
+			goto fail;
+	}
+
+	bp->waiting = 0;
+	init_waitqueue_head(&bp->wq);
+	if (request_irq(edev->irqs[0], bbc_i2c_interrupt,
+			SA_SHIRQ, "bbc_i2c", bp))
+		goto fail;
+
+	bp->index = index;
+	bp->bus_edev = edev;
+
+	spin_lock_init(&bp->lock);
+	bp->next = all_bbc_i2c;
+	all_bbc_i2c = bp;
+
+	entry = 0;
+	for (echild = edev->children;
+	     echild && entry < 8;
+	     echild = echild->next, entry++) {
+		bp->devs[entry].device = echild;
+		bp->devs[entry].client_claimed = 0;
+	}
+
+	writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);
+	bp->own = readb(bp->i2c_control_regs + 0x01);
+	writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);
+	bp->clock = readb(bp->i2c_control_regs + 0x01);
+
+	printk(KERN_INFO "i2c-%d: Regs at %p, %d devices, own %02x, clock %02x.\n",
+	       bp->index, bp->i2c_control_regs, entry, bp->own, bp->clock);
+
+	reset_one_i2c(bp);
+
+	return 0;
+
+fail:
+	if (bp->i2c_bussel_reg)
+		iounmap(bp->i2c_bussel_reg);
+	if (bp->i2c_control_regs)
+		iounmap(bp->i2c_control_regs);
+	kfree(bp);
+	return -EINVAL;
+}
+
+static int __init bbc_present(void)
+{
+	struct linux_ebus *ebus = NULL;
+	struct linux_ebus_device *edev = NULL;
+
+	for_each_ebus(ebus) {
+		for_each_ebusdev(edev, ebus) {
+			if (!strcmp(edev->prom_name, "bbc"))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+extern void bbc_envctrl_init(void);
+extern void bbc_envctrl_cleanup(void);
+
+static int __init bbc_i2c_init(void)
+{
+	struct linux_ebus *ebus = NULL;
+	struct linux_ebus_device *edev = NULL;
+	int index = 0;
+
+	if (tlb_type != cheetah || !bbc_present())
+		return -ENODEV;
+
+	for_each_ebus(ebus) {
+		for_each_ebusdev(edev, ebus) {
+			if (!strcmp(edev->prom_name, "i2c")) {
+				if (!attach_one_i2c(edev, index))
+					index++;
+			}
+		}
+	}
+
+	if (!index)
+		return -ENODEV;
+
+	bbc_envctrl_init();
+	return 0;
+}
+
+static void __exit bbc_i2c_cleanup(void)
+{
+	struct bbc_i2c_bus *bp = all_bbc_i2c;
+
+	bbc_envctrl_cleanup();
+
+	while (bp != NULL) {
+		struct bbc_i2c_bus *next = bp->next;
+
+		free_irq(bp->bus_edev->irqs[0], bp);
+
+		if (bp->i2c_bussel_reg)
+			iounmap(bp->i2c_bussel_reg);
+		if (bp->i2c_control_regs)
+			iounmap(bp->i2c_control_regs);
+
+		kfree(bp);
+
+		bp = next;
+	}
+	all_bbc_i2c = NULL;
+}
+
+module_init(bbc_i2c_init);
+module_exit(bbc_i2c_cleanup);
diff -urN linux-2.4.4-pre2/drivers/sbus/char/bbc_i2c.h linux/drivers/sbus/char/bbc_i2c.h
--- linux-2.4.4-pre2/drivers/sbus/char/bbc_i2c.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/bbc_i2c.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,20 @@
+/* $Id: bbc_i2c.h,v 1.2 2001/04/02 09:59:25 davem Exp $ */
+#ifndef _BBC_I2C_H
+#define _BBC_I2C_H
+
+#include <asm/ebus.h>
+
+struct bbc_i2c_client;
+
+/* Probing and attachment. */
+extern struct linux_ebus_child *bbc_i2c_getdev(int);
+extern struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *);
+extern void bbc_i2c_detach(struct bbc_i2c_client *);
+
+/* Register read/write.  NOTE: Blocking! */
+extern int bbc_i2c_writeb(struct bbc_i2c_client *, unsigned char val, int off);
+extern int bbc_i2c_readb(struct bbc_i2c_client *, unsigned char *byte, int off);
+extern int bbc_i2c_write_buf(struct bbc_i2c_client *, char *buf, int len, int off);
+extern int bbc_i2c_read_buf(struct bbc_i2c_client *, char *buf, int len, int off);
+
+#endif /* _BBC_I2C_H */
diff -urN linux-2.4.4-pre2/drivers/sbus/char/cpwatchdog.c linux/drivers/sbus/char/cpwatchdog.c
--- linux-2.4.4-pre2/drivers/sbus/char/cpwatchdog.c	Sun Mar 25 18:14:20 2001
+++ linux/drivers/sbus/char/cpwatchdog.c	Mon Dec  3 22:34:51 2001
@@ -199,7 +199,9 @@
 
 /* Forward declarations of internal methods
  */
+#ifdef WD_DEBUG
 static void wd_dumpregs(void);
+#endif
 static void wd_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void wd_toggleintr(struct wd_timer* pTimer, int enable);
 static void wd_pingtimer(struct wd_timer* pTimer);
@@ -471,6 +473,7 @@
 static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops };
 static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops };
 
+#ifdef WD_DEBUG
 static void wd_dumpregs(void)
 {
 	/* Reading from downcounters initiates watchdog countdown--
@@ -507,6 +510,7 @@
 		(unsigned long)(&wd_dev.regs->pld_regs.status), 
 		readb(&wd_dev.regs->pld_regs.status));
 }
+#endif
 
 /* Enable or disable watchdog interrupts
  * Because of the CP1400 defect this should only be
diff -urN linux-2.4.4-pre2/drivers/sbus/char/max1617.h linux/drivers/sbus/char/max1617.h
--- linux-2.4.4-pre2/drivers/sbus/char/max1617.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/max1617.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,27 @@
+/* $Id: max1617.h,v 1.1 2001/04/02 09:59:08 davem Exp $ */
+#ifndef _MAX1617_H
+#define _MAX1617_H
+
+#define MAX1617_AMB_TEMP	0x00 /* Ambient temp in C	*/
+#define MAX1617_CPU_TEMP	0x01 /* Processor die temp in C	*/
+#define MAX1617_STATUS		0x02 /* Chip status bits	*/
+
+/* Read-only versions of changable registers. */
+#define MAX1617_RD_CFG_BYTE	0x03 /* Config register		*/
+#define MAX1617_RD_CVRATE_BYTE	0x04 /* Temp conversion rate	*/
+#define MAX1617_RD_AMB_HIGHLIM	0x05 /* Ambient high limit	*/
+#define MAX1617_RD_AMB_LOWLIM	0x06 /* Ambient low limit	*/
+#define MAX1617_RD_CPU_HIGHLIM	0x07 /* Processor high limit	*/
+#define MAX1617_RD_CPU_LOWLIM	0x08 /* Processor low limit	*/
+
+/* Write-only versions of the same. */
+#define MAX1617_WR_CFG_BYTE	0x09
+#define MAX1617_WR_CVRATE_BYTE	0x0a
+#define MAX1617_WR_AMB_HIGHLIM	0x0b
+#define MAX1617_WR_AMB_LOWLIM	0x0c
+#define MAX1617_WR_CPU_HIGHLIM	0x0d
+#define MAX1617_WR_CPU_LOWLIM	0x0e
+
+#define MAX1617_ONESHOT		0x0f
+
+#endif /* _MAX1617_H */
diff -urN linux-2.4.4-pre2/drivers/sbus/char/riowatchdog.c linux/drivers/sbus/char/riowatchdog.c
--- linux-2.4.4-pre2/drivers/sbus/char/riowatchdog.c	Sun Mar 25 18:14:20 2001
+++ linux/drivers/sbus/char/riowatchdog.c	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: riowatchdog.c,v 1.1 2001/03/24 06:04:24 davem Exp $
+/* $Id: riowatchdog.c,v 1.2 2001/03/26 23:47:18 davem Exp $
  * riowatchdog.c - driver for hw watchdog inside Super I/O of RIO
  *
  * Copyright (C) 2001 David S. Miller (davem@redhat.com)
@@ -24,10 +24,9 @@
  * as its' watchdog.
  *
  * When the watchdog triggers, it asserts a line to the BBC (Boot Bus
- * Controller) of the machine.  The BBC can be configured to treat the
- * assertion of this signal in different ways.  It can trigger an XIR
- * (external CPU reset) to all the processors or it can trigger a true
- * power-on reset which triggers the RST signal of all devices in the machine.
+ * Controller) of the machine.  The BBC can only be configured to
+ * trigger a power-on reset when the signal is asserted.  The BBC
+ * can be configured to ignore the signal entirely as well.
  *
  * The only Super I/O device register we care about is at index
  * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
@@ -54,15 +53,13 @@
 
 static spinlock_t riowd_lock = SPIN_LOCK_UNLOCKED;
 
+static void *bbc_regs;
 static void *riowd_regs;
 #define WDTO_INDEX	0x05
 
 static int riowd_timeout = 1;		/* in minutes */
-static int riowd_xir = 1;		/* watchdog generates XIR? */
 MODULE_PARM(riowd_timeout,"i");
 MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");
-MODULE_PARM(riowd_xir,"i");
-MODULE_PARM_DESC(riowd_xir, "Watchdog generates XIR reset if non-zero");
 
 #if 0 /* Currently unused. */
 static u8 riowd_readreg(int index)
@@ -96,12 +93,24 @@
 
 static void riowd_stoptimer(void)
 {
+	u8 val;
+
 	riowd_writereg(0, WDTO_INDEX);
+
+	val = readb(bbc_regs + BBC_WDACTION);
+	val &= ~BBC_WDACTION_RST;
+	writeb(val, bbc_regs + BBC_WDACTION);
 }
 
 static void riowd_starttimer(void)
 {
+	u8 val;
+
 	riowd_writereg(riowd_timeout, WDTO_INDEX);
+
+	val = readb(bbc_regs + BBC_WDACTION);
+	val |= BBC_WDACTION_RST;
+	writeb(val, bbc_regs + BBC_WDACTION);
 }
 
 static int riowd_open(struct inode *inode, struct file *filp)
@@ -189,7 +198,6 @@
 {
 	struct 	linux_ebus *ebus = NULL;
 	struct 	linux_ebus_device *edev = NULL;
-	void *bbc_regs;
 	u8 val;
 
 	for_each_ebus(ebus) {
@@ -206,14 +214,11 @@
 	if (!bbc_regs)
 		return -ENODEV;
 
+	/* Turn it off. */
 	val = readb(bbc_regs + BBC_WDACTION);
-	if (riowd_xir != 0)
-		val &= ~BBC_WDACTION_RST;
-	else
-		val |= BBC_WDACTION_RST;
+	val &= ~BBC_WDACTION_RST;
 	writeb(val, bbc_regs + BBC_WDACTION);
 
-	iounmap(bbc_regs);
 	return 0;
 }
 
@@ -249,10 +254,8 @@
 		goto fail;
 	}
 
-	printk(KERN_INFO "pmc: Hardware watchdog [%i minutes, %s reset], "
-	       "regs at %p\n",
-	       riowd_timeout, (riowd_xir ? "XIR" : "POR"),
-	       riowd_regs);
+	printk(KERN_INFO "pmc: Hardware watchdog [%i minutes], "
+	       "regs at %p\n", riowd_timeout, riowd_regs);
 
 	return 0;
 
@@ -261,6 +264,10 @@
 		iounmap(riowd_regs);
 		riowd_regs = NULL;
 	}
+	if (bbc_regs) {
+		iounmap(bbc_regs);
+		bbc_regs = NULL;
+	}
 	return -ENODEV;
 }
 
@@ -269,6 +276,8 @@
 	misc_deregister(&riowd_miscdev);
 	iounmap(riowd_regs);
 	riowd_regs = NULL;
+	iounmap(bbc_regs);
+	bbc_regs = NULL;
 }
 
 module_init(riowd_init);
diff -urN linux-2.4.4-pre2/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c
--- linux-2.4.4-pre2/drivers/scsi/advansys.c	Mon Dec  3 22:34:41 2001
+++ linux/drivers/scsi/advansys.c	Mon Dec  3 22:34:51 2001
@@ -6306,13 +6306,11 @@
  */
 
 #if ASC_LINUX_KERNEL24
-static Scsi_Host_Template driver_template = ADVANSYS;
-# include "scsi_module.c"
-#elif ASC_LINUX_KERNEL22
-#ifdef MODULE
+static
+#endif
+#if ASC_LINUX_KERNEL24 || (ASC_LINUX_KERNEL22 && defined(MODULE))
 Scsi_Host_Template driver_template = ADVANSYS;
 # include "scsi_module.c"
-#endif /* MODULE */
 #endif
 
 
diff -urN linux-2.4.4-pre2/drivers/scsi/advansys.h linux/drivers/scsi/advansys.h
--- linux-2.4.4-pre2/drivers/scsi/advansys.h	Mon Dec  3 22:34:41 2001
+++ linux/drivers/scsi/advansys.h	Mon Dec  3 22:34:51 2001
@@ -28,6 +28,7 @@
 #ifndef _ADVANSYS_H
 #define _ADVANSYS_H
 
+#include <linux/config.h>
 #ifndef LINUX_VERSION_CODE
 #include <linux/version.h>
 #endif /* LINUX_VERSION_CODE */
diff -urN linux-2.4.4-pre2/drivers/scsi/aic7xxx/aic7xxx_linux.c linux/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- linux-2.4.4-pre2/drivers/scsi/aic7xxx/aic7xxx_linux.c	Sun Mar  4 14:30:18 2001
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux.c	Mon Dec  3 22:34:51 2001
@@ -112,6 +112,7 @@
  *  8: SMP friendliness has been improved
  *
  */
+#include <linux/config.h>
 
 /*
  * The next three defines are user configurable.  These should be the only
diff -urN linux-2.4.4-pre2/drivers/scsi/aic7xxx/aic7xxx_osm.h linux/drivers/scsi/aic7xxx/aic7xxx_osm.h
--- linux-2.4.4-pre2/drivers/scsi/aic7xxx/aic7xxx_osm.h	Tue Mar  6 22:44:16 2001
+++ linux/drivers/scsi/aic7xxx/aic7xxx_osm.h	Mon Dec  3 22:34:51 2001
@@ -355,10 +355,6 @@
 
 #define AIC7XXX_DRIVER_VERSION  "6.1.5"
 
-#ifndef LINUX_VERSION_CODE
-#include <linux/version.h>
-#endif
-
 #ifndef KERNEL_VERSION
 #define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
 #endif
diff -urN linux-2.4.4-pre2/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c
--- linux-2.4.4-pre2/drivers/scsi/megaraid.c	Sun Mar 25 18:24:31 2001
+++ linux/drivers/scsi/megaraid.c	Mon Dec  3 22:34:51 2001
@@ -391,42 +391,14 @@
 	writel (value, megaCfg->base + 0x2C);
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)	/* 0x020100 */
-
-/*
- *	Linux 2.4 and higher
- *
- *	No driver private lock
- *	Use the io_request_lock not cli/sti
- *	queue task is a simple api without irq forms
- */
-
-#include <linux/smp.h>
-#define cpuid smp_processor_id()
-
-MODULE_AUTHOR ("American Megatrends Inc.");
-MODULE_DESCRIPTION ("AMI MegaRAID driver");
-
-#define DRIVER_LOCK_T
-#define DRIVER_LOCK_INIT(p)
-#define DRIVER_LOCK(p)
-#define DRIVER_UNLOCK(p)
-#define IO_LOCK_T unsigned long io_flags = 0;
-#define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags);
-#define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags);
-
-#define queue_task_irq(a,b)     queue_task(a,b)
-#define queue_task_irq_off(a,b) queue_task(a,b)
-
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)	/* 0x020200 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)	/* 0x020200 */
 
 /*
  *	Linux 2.2 and higher
  *
  *	No driver private lock
  *	Use the io_request_lock not cli/sti
- *	No pci region api
- *	queue_task is now a single simple API
+ *	queue task is a simple api without irq forms
  */
  
 #include <linux/smp.h>
@@ -443,16 +415,18 @@
 #define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags);
 #define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags);
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)	/* 0x020400 */
 #define pci_free_consistent(a,b,c,d)
 #define pci_unmap_single(a,b,c,d,e)
 
 #define init_MUTEX_LOCKED(x)	((x)=MUTEX_LOCKED)
 #define init_MUTEX(x)		((x)=MUTEX)
 
+#define DECLARE_WAIT_QUEUE_HEAD(x)	struct wait_queue *x = NULL
+#endif
+
 #define queue_task_irq(a,b)     queue_task(a,b)
 #define queue_task_irq_off(a,b) queue_task(a,b)
-
-#define DECLARE_WAIT_QUEUE_HEAD(x)	struct wait_queue *x = NULL
 #else
 
 /*
@@ -4299,12 +4273,9 @@
 }
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-static Scsi_Host_Template driver_template = MEGARAID;
-#include "scsi_module.c"
-#else
-#ifdef MODULE
+static
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) || defined(MODULE)
 Scsi_Host_Template driver_template = MEGARAID;
-
 #include "scsi_module.c"
-#endif				/* MODULE */
-#endif				/* LINUX VERSION 2.4.XX  test */
+#endif
diff -urN linux-2.4.4-pre2/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- linux-2.4.4-pre2/drivers/scsi/ncr53c8xx.c	Tue Mar  6 19:34:25 2001
+++ linux/drivers/scsi/ncr53c8xx.c	Mon Dec  3 22:34:51 2001
@@ -9271,9 +9271,9 @@
 */
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
-static Scsi_Host_Template driver_template = NCR53C8XX;
-#include "scsi_module.c"
-#elif defined(MODULE)
+static
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) || defined(MODULE)
 Scsi_Host_Template driver_template = NCR53C8XX;
 #include "scsi_module.c"
 #endif
diff -urN linux-2.4.4-pre2/drivers/scsi/qlogicfc.c linux/drivers/scsi/qlogicfc.c
--- linux-2.4.4-pre2/drivers/scsi/qlogicfc.c	Tue Mar 20 15:05:46 2001
+++ linux/drivers/scsi/qlogicfc.c	Mon Dec  3 22:34:51 2001
@@ -1866,7 +1866,9 @@
 	hostdata = (struct isp2x00_hostdata *) host->hostdata;
 
 	outw(0x01, host->io_port + ISP_CTRL_STATUS);
+	udelay(100);
 	outw(HCCR_RESET, host->io_port + HOST_HCCR);
+	udelay(100);
 	outw(HCCR_RELEASE, host->io_port + HOST_HCCR);
 	outw(HCCR_BIOS_DISABLE, host->io_port + HOST_HCCR);
 
diff -urN linux-2.4.4-pre2/drivers/scsi/sym53c8xx.c linux/drivers/scsi/sym53c8xx.c
--- linux-2.4.4-pre2/drivers/scsi/sym53c8xx.c	Tue Mar 20 12:04:58 2001
+++ linux/drivers/scsi/sym53c8xx.c	Mon Dec  3 22:34:51 2001
@@ -14672,9 +14672,9 @@
 */
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
-static Scsi_Host_Template driver_template = SYM53C8XX;
-#include "scsi_module.c"
-#elif defined(MODULE)
+static
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) || defined(MODULE)
 Scsi_Host_Template driver_template = SYM53C8XX;
 #include "scsi_module.c"
 #endif
diff -urN linux-2.4.4-pre2/drivers/sound/cs46xx.c linux/drivers/sound/cs46xx.c
--- linux-2.4.4-pre2/drivers/sound/cs46xx.c	Mon Dec  3 22:34:42 2001
+++ linux/drivers/sound/cs46xx.c	Mon Dec  3 22:34:51 2001
@@ -67,7 +67,6 @@
  */
  
 #include <linux/list.h>
-#include <linux/config.h>
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/string.h>
diff -urN linux-2.4.4-pre2/drivers/usb/serial/io_edgeport.c linux/drivers/usb/serial/io_edgeport.c
--- linux-2.4.4-pre2/drivers/usb/serial/io_edgeport.c	Mon Dec  3 22:34:42 2001
+++ linux/drivers/usb/serial/io_edgeport.c	Mon Dec  3 22:34:51 2001
@@ -223,6 +223,7 @@
  * 
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
diff -urN linux-2.4.4-pre2/drivers/usb/serial/keyspan.c linux/drivers/usb/serial/keyspan.c
--- linux-2.4.4-pre2/drivers/usb/serial/keyspan.c	Mon Dec  3 22:34:42 2001
+++ linux/drivers/usb/serial/keyspan.c	Mon Dec  3 22:34:51 2001
@@ -49,6 +49,7 @@
 */
 
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
diff -urN linux-2.4.4-pre2/drivers/video/atyfb.c linux/drivers/video/atyfb.c
--- linux-2.4.4-pre2/drivers/video/atyfb.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/video/atyfb.c	Mon Dec  3 22:34:51 2001
@@ -3144,51 +3144,6 @@
 
 	vma->vm_pgoff = off >> PAGE_SHIFT;	/* propagate off changes */
 
-#ifdef __sparc_v9__
-	/* Align it as much as desirable */
-	{
-		unsigned long j, align;
-		int max = -1;
-
-		map_offset = off + size;
-		for (i = 0; fb->mmap_map[i].size; i++) {
-			if (fb->mmap_map[i].voff < off)
-				continue;
-			if (fb->mmap_map[i].voff >= map_offset)
-				break;
-			if (max < 0 ||
-			    fb->mmap_map[i].size > fb->mmap_map[max].size)
-				max = i;
-		}
-		if (max >= 0) {
-			j = fb->mmap_map[max].size;
-			if (fb->mmap_map[max].voff + j > map_offset)
-				j = map_offset - fb->mmap_map[max].voff;
-			for (align = 0x400000; align > PAGE_SIZE; align >>= 3)
-				if (j >= align &&
-				    !(fb->mmap_map[max].poff & (align - 1)))
-					break;
-			if (align > PAGE_SIZE) {
-				j = align;
-				align = j - ((vma->vm_start
-					      + fb->mmap_map[max].voff
-					      - off) & (j - 1));
-				if (align != j) {
-					struct vm_area_struct *vmm;
-
-					vmm = find_vma(current->mm,
-						       vma->vm_start);
-					if (!vmm || vmm->vm_start
-						    >= vma->vm_end + align) {
-						vma->vm_start += align;
-						vma->vm_end += align;
-					}
-				}
-			}
-		}
-	}
-#endif
-
 	/* Each page, see which map applies */
 	for (page = 0; page < size; ) {
 		map_size = 0;
diff -urN linux-2.4.4-pre2/drivers/video/fbmem.c linux/drivers/video/fbmem.c
--- linux-2.4.4-pre2/drivers/video/fbmem.c	Fri Mar  2 18:38:39 2001
+++ linux/drivers/video/fbmem.c	Mon Dec  3 22:34:51 2001
@@ -562,22 +562,6 @@
 	vma->vm_pgoff = off >> PAGE_SHIFT;
 #if defined(__sparc_v9__)
 	vma->vm_flags |= (VM_SHM | VM_LOCKED);
-	{
-		unsigned long align, j;
-		for (align = 0x400000; align > PAGE_SIZE; align >>= 3)
-			if (len >= align && !((start & ~PAGE_MASK) & (align - 1)))
-				break;
-		if (align > PAGE_SIZE && vma->vm_start & (align - 1)) {
-			/* align as much as possible */
-			struct vm_area_struct *vmm;
-			j = (-vma->vm_start) & (align - 1);
-			vmm = find_vma(current->mm, vma->vm_start);
-			if (!vmm || vmm->vm_start >= vma->vm_end + j) {
-				vma->vm_start += j;
-				vma->vm_end += j;
-			}
-		}
-	}
 	if (io_remap_page_range(vma->vm_start, off,
 				vma->vm_end - vma->vm_start, vma->vm_page_prot, 0))
 		return -EAGAIN;
@@ -687,6 +671,9 @@
 	mmap:		fb_mmap,
 	open:		fb_open,
 	release:	fb_release,
+#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
+	get_unmapped_area: get_fb_unmapped_area,
+#endif
 };
 
 static devfs_handle_t devfs_handle;
diff -urN linux-2.4.4-pre2/drivers/video/hitfb.c linux/drivers/video/hitfb.c
--- linux-2.4.4-pre2/drivers/video/hitfb.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/video/hitfb.c	Mon Dec  3 22:34:51 2001
@@ -22,9 +22,11 @@
 #include <linux/nubus.h>
 #include <linux/init.h>
 
+#include <asm/machvec.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
+#include <asm/hd64461.h>
 
 #include <linux/fb.h>
 
@@ -32,20 +34,6 @@
 #include <video/fbcon-cfb8.h>
 #include <video/fbcon-cfb16.h>
 
-#include <asm/hd64461.h>
-
-#define CONFIG_SH_LCD_VIDEOBASE		CONFIG_HD64461_IOBASE+0x2000000
-
-/* These are for HP Jornada 680/690.
-   It is desired that they are configurable...  */
-#define CONFIG_SH_LCD_VIDEOSIZE		1024*1024
-#define CONFIG_SH_LCD_HORZ		640
-#define CONFIG_SH_LCD_VERT		240
-#define CONFIG_SH_LCD_DEFAULTBPP	16
-
-struct hitfb_info {
-    struct fb_info_gen gen;
-};
 
 struct hitfb_par
 {
@@ -53,55 +41,79 @@
     int bpp;
 };
 
-static struct hitfb_info fb_info;
-static struct hitfb_par current_par;
-static int current_par_valid = 0;
-static struct display disp;
 
-static union {
+struct hitfb_info {
+    struct fb_info_gen gen;
+    struct display disp;
+    struct hitfb_par current_par;
+    struct fb_var_screeninfo default_var;
+    int current_par_valid;
+    unsigned long hit_videobase, hit_videosize;
+    union {
 #ifdef FBCON_HAS_CFB16
-    u16 cfb16[16];
+	u16 cfb16[16];
 #endif
-} fbcon_cmap;
-
-unsigned long hit_videobase, hit_videosize;
-static struct fb_var_screeninfo default_var;
+    } fbcon_cmap;
+} fb_info = {
+    {},
+    {},
+    {},
+    {},
+    0, 0, 0,
+    {},
+};
 
-int hitfb_init(void);
 
-static void hitfb_set_par(struct hitfb_par *par, const struct fb_info *info);
-static void hitfb_encode_var(struct fb_var_screeninfo *var, 
-			     struct hitfb_par *par,
-			     const struct fb_info *info);
+static void hitfb_set_par(const void *fb_par, struct fb_info_gen *info);
+static int hitfb_encode_var(struct fb_var_screeninfo *var, const void *fb_par,
+			    struct fb_info_gen *info);
 
 
 static void hitfb_detect(void)
 {
     struct hitfb_par par;
+    unsigned short lcdclor, ldr3, ldvntr;
+
+    fb_info.hit_videobase = CONFIG_HD64461_IOBASE + 0x02000000;
+    fb_info.hit_videosize = (MACH_HP680 || MACH_HP690) ? 1024*1024 : 512*1024;
 
-    hit_videobase = CONFIG_SH_LCD_VIDEOBASE;
-    hit_videosize = CONFIG_SH_LCD_VIDEOSIZE;
+    lcdclor = inw(HD64461_LCDCLOR);
+    ldvntr = inw(HD64461_LDVNTR);
+    ldr3 = inw(HD64461_LDR3);
+
+    switch(ldr3&15) {
+    default:
+    case 4:
+        par.bpp = 8;
+	par.x = lcdclor;
+	break;
+    case 8:
+        par.bpp = 16;
+	par.x = lcdclor/2;
+	break;
+    }
 
-    par.x = CONFIG_SH_LCD_HORZ;
-    par.y = CONFIG_SH_LCD_VERT;
-    par.bpp = CONFIG_SH_LCD_DEFAULTBPP;
+    par.y = ldvntr+1;
 
     hitfb_set_par(&par, NULL);
-    hitfb_encode_var(&default_var, &par, NULL);
+    hitfb_encode_var(&fb_info.default_var, &par, NULL);
 }
 
-static int hitfb_encode_fix(struct fb_fix_screeninfo *fix,
-			    struct hitfb_par *par,
-			    const struct fb_info *info)
+
+static int hitfb_encode_fix(struct fb_fix_screeninfo *fix, const void *fb_par,
+			     struct fb_info_gen *info)
 {
+    const struct hitfb_par *par = fb_par;
+
     memset(fix, 0, sizeof(struct fb_fix_screeninfo));
 
     strcpy(fix->id, "Hitachi HD64461");
-    fix->smem_start = hit_videobase;
-    fix->smem_len = hit_videosize;
+    fix->smem_start = fb_info.hit_videobase;
+    fix->smem_len = fb_info.hit_videosize;
     fix->type = FB_TYPE_PACKED_PIXELS;
     fix->type_aux = 0;
-    fix->visual = FB_VISUAL_TRUECOLOR;
+    fix->visual = (par->bpp == 8) ?
+	FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
     fix->xpanstep = 0;
     fix->ypanstep = 0;
     fix->ywrapstep = 0;
@@ -120,10 +132,11 @@
 }
 
 
-static int hitfb_decode_var(struct fb_var_screeninfo *var,
-			    struct hitfb_par *par,
-			    const struct fb_info *info)
+static int hitfb_decode_var(const struct fb_var_screeninfo *var, void *fb_par,
+			     struct fb_info_gen *info)
 {
+    struct hitfb_par *par = fb_par;
+
     par->x = var->xres;
     par->y = var->yres;
     par->bpp = var->bits_per_pixel;
@@ -131,10 +144,11 @@
 }
 
 
-static void hitfb_encode_var(struct fb_var_screeninfo *var, 
-			     struct hitfb_par *par,
-			     const struct fb_info *info)
+static int hitfb_encode_var(struct fb_var_screeninfo *var, const void *fb_par,
+			     struct fb_info_gen *info)
 {
+    const struct hitfb_par *par = fb_par;
+
     memset(var, 0, sizeof(*var));
 
     var->xres = par->x;
@@ -191,25 +205,28 @@
     var->green.msb_right = 0;
     var->blue.msb_right = 0;
     var->transp.msb_right = 0;
+
+    return 0;
 }
 
 
-static void hitfb_get_par(struct hitfb_par *par, const struct fb_info *info)
+static void hitfb_get_par(void *par, struct fb_info_gen *info)
 {
-    *par = current_par;
+    *(struct hitfb_par *)par = fb_info.current_par;
 }
 
 
-static void hitfb_set_par(struct hitfb_par *par, const struct fb_info *info)
+static void hitfb_set_par(const void *fb_par, struct fb_info_gen *info)
 {
-    /* Set the hardware according to 'par'. */
-    current_par = *par;
-    current_par_valid = 1;
+    const struct hitfb_par *par = fb_par;
+    fb_info.current_par = *par;
+    fb_info.current_par_valid = 1;
 }
 
 
-static int hitfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp, struct fb_info *info)
+static int hitfb_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			   unsigned *blue, unsigned *transp,
+			   struct fb_info *info)
 {
     if (regno > 255)
 	return 1;	
@@ -224,8 +241,9 @@
 }
 
 
-static int hitfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp, struct fb_info *info)
+static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   struct fb_info *info)
 {
     if (regno > 255)
 	return 1;
@@ -236,10 +254,10 @@
     outw(blue>>10, HD64461_CPTWDR);
     
     if(regno<16) {
-	switch(current_par.bpp) {
+	switch(fb_info.current_par.bpp) {
 #ifdef FBCON_HAS_CFB16
 	case 16:
-	    fbcon_cmap.cfb16[regno] =
+	    fb_info.fbcon_cmap.cfb16[regno] =
 		((red   & 0xf800)      ) |
 		((green & 0xfc00) >>  5) |
 		((blue  & 0xf800) >> 11);
@@ -251,16 +269,34 @@
     return 0;
 }
 
-static int hitfb_blank(int blank_mode, const struct fb_info *info)
+
+static int hitfb_pan_display(const struct fb_var_screeninfo *var,
+			     struct fb_info_gen *info)
 {
+    if (!fb_info.current_par_valid)
+	return -EINVAL;
+
     return 0;
 }
 
 
-static void hitfb_set_disp(const void *par, struct display *disp,
-			   struct fb_info_gen *info)
+static int hitfb_blank(int blank_mode, struct fb_info_gen *info)
 {
-    disp->screen_base = (void *)hit_videobase;
+    if (!fb_info.current_par_valid)
+	return 1;
+
+    return 0;
+}
+
+
+static void hitfb_set_disp(const void *fb_par, struct display *disp,
+			    struct fb_info_gen *info)
+{
+    const struct hitfb_par *par = fb_par;
+
+    disp->screen_base = (void *)fb_info.hit_videobase;
+    disp->scrollmode = SCROLL_YREDRAW;
+
     switch(((struct hitfb_par *)par)->bpp) {
 #ifdef FBCON_HAS_CFB8
     case 8:
@@ -270,7 +306,7 @@
 #ifdef FBCON_HAS_CFB16
     case 16:
 	disp->dispsw = &fbcon_cfb16;
-	disp->dispsw_data = fbcon_cmap.cfb16;
+	disp->dispsw_data = fb_info.fbcon_cmap.cfb16;
 	break;
 #endif
     default:
@@ -288,18 +324,20 @@
     hitfb_set_par,
     hitfb_getcolreg,
     hitfb_setcolreg,
-    NULL,
+    hitfb_pan_display,
     hitfb_blank,
     hitfb_set_disp
 };
 
+
 static struct fb_ops hitfb_ops = {
-	owner:		THIS_MODULE,
-	fb_get_fix:	fbgen_get_fix,
-	fb_get_var:	fbgen_get_var,
-	fb_set_var:	fbgen_set_var,
-	fb_get_cmap:	fbgen_get_cmap,
-	fb_set_cmap:	fbgen_set_cmap,
+    owner:		THIS_MODULE,
+    fb_get_fix:		fbgen_get_fix,
+    fb_get_var:		fbgen_get_var,
+    fb_set_var:		fbgen_set_var,
+    fb_get_cmap:	fbgen_get_cmap,
+    fb_set_cmap:	fbgen_set_cmap,
+    fb_pan_display:	fbgen_pan_display,
 };
 
 
@@ -309,7 +347,7 @@
     fb_info.gen.info.node = -1;
     fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
     fb_info.gen.info.fbops = &hitfb_ops;
-    fb_info.gen.info.disp = &disp;
+    fb_info.gen.info.disp = &fb_info.disp;
     fb_info.gen.info.changevar = NULL;
     fb_info.gen.info.switch_con = &fbgen_switch;
     fb_info.gen.info.updatevar = &fbgen_update_var;
@@ -318,9 +356,9 @@
     fb_info.gen.fbhw = &hitfb_switch;
     fb_info.gen.fbhw->detect();
     
-    fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
-    disp.var.activate = FB_ACTIVATE_NOW;
-    fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
+    fbgen_get_var(&fb_info.disp.var, -1, &fb_info.gen.info);
+    fb_info.disp.var.activate = FB_ACTIVATE_NOW;
+    fbgen_do_set_var(&fb_info.disp.var, 1, &fb_info.gen);
     fbgen_set_disp(-1, &fb_info.gen);
     fbgen_install_cmap(0, &fb_info.gen);
     
@@ -347,6 +385,13 @@
 
 void cleanup_module(void)
 {
-    hitfb_cleanup(void);
+  hitfb_cleanup(void);
 }
 #endif
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -urN linux-2.4.4-pre2/drivers/video/sbusfb.c linux/drivers/video/sbusfb.c
--- linux-2.4.4-pre2/drivers/video/sbusfb.c	Sun Mar 25 18:14:20 2001
+++ linux/drivers/video/sbusfb.c	Mon Dec  3 22:34:51 2001
@@ -193,44 +193,6 @@
 	/* To stop the swapper from even considering these pages */
 	vma->vm_flags |= (VM_SHM| VM_LOCKED);
 	
-#ifdef __sparc_v9__
-	/* Align it as much as desirable */
-	{
-		unsigned long j, alignment, s = 0;
-		int max = -1;
-		
-		map_offset = (vma->vm_pgoff << PAGE_SHIFT) + size;
-		for (i = 0; fb->mmap_map[i].size; i++) {
-			if (fb->mmap_map[i].voff < off)
-				continue;
-			if (fb->mmap_map[i].voff >= map_offset)
-				break;
-			if (max < 0 || sbusfb_mmapsize(fb,fb->mmap_map[i].size) > s) {
-				max = i;
-				s = sbusfb_mmapsize(fb,fb->mmap_map[max].size);
-			}
-		}
-		if (max >= 0) {
-			j = s;
-			if (fb->mmap_map[max].voff + j > map_offset)
-				j = map_offset - fb->mmap_map[max].voff;
-			for (alignment = 0x400000; alignment > PAGE_SIZE; alignment >>= 3)
-				if (j >= alignment && !(fb->mmap_map[max].poff & (alignment - 1)))
-					break;
-			if (alignment > PAGE_SIZE) {
-				j = alignment;
-				alignment = j - ((vma->vm_start + fb->mmap_map[max].voff - off) & (j - 1));
-				if (alignment != j) {
-					struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
-					if (!vmm || vmm->vm_start >= vma->vm_end + alignment) {
-						vma->vm_start += alignment;
-						vma->vm_end += alignment;
-					}
-				}
-			}
-		}
-	}
-#endif	
 
 	/* Each page, see which map applies */
 	for (page = 0; page < size; ){
diff -urN linux-2.4.4-pre2/fs/Config.in linux/fs/Config.in
--- linux-2.4.4-pre2/fs/Config.in	Mon Jan 15 12:42:32 2001
+++ linux/fs/Config.in	Mon Dec  3 22:34:51 2001
@@ -31,6 +31,7 @@
 	int 'JFFS debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_JFFS_FS_VERBOSE 0
 fi
 tristate 'Compressed ROM file system support' CONFIG_CRAMFS
+bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
 tristate 'Simple RAM-based file system support' CONFIG_RAMFS
 
 tristate 'ISO 9660 CDROM file system support' CONFIG_ISO9660_FS
diff -urN linux-2.4.4-pre2/fs/buffer.c linux/fs/buffer.c
--- linux-2.4.4-pre2/fs/buffer.c	Sat Mar 24 19:31:58 2001
+++ linux/fs/buffer.c	Mon Dec  3 22:34:51 2001
@@ -1367,11 +1367,12 @@
 {
 	if (buffer_mapped(bh)) {
 		mark_buffer_clean(bh);
-		wait_on_buffer(bh);
+		lock_buffer(bh);
 		clear_bit(BH_Uptodate, &bh->b_state);
 		clear_bit(BH_Mapped, &bh->b_state);
 		clear_bit(BH_Req, &bh->b_state);
 		clear_bit(BH_New, &bh->b_state);
+		unlock_buffer(bh);
 	}
 }
 
diff -urN linux-2.4.4-pre2/fs/dcache.c linux/fs/dcache.c
--- linux-2.4.4-pre2/fs/dcache.c	Wed Mar  7 16:53:48 2001
+++ linux/fs/dcache.c	Mon Dec  3 22:34:51 2001
@@ -340,7 +340,7 @@
 		if (dentry->d_flags & DCACHE_REFERENCED) {
 			dentry->d_flags &= ~DCACHE_REFERENCED;
 			list_add(&dentry->d_lru, &dentry_unused);
-			goto next;
+			continue;
 		}
 		dentry_stat.nr_unused--;
 
@@ -349,7 +349,6 @@
 			BUG();
 
 		prune_one_dentry(dentry);
-	next:
 		if (!--count)
 			break;
 	}
diff -urN linux-2.4.4-pre2/fs/super.c linux/fs/super.c
--- linux-2.4.4-pre2/fs/super.c	Sun Mar 25 18:17:00 2001
+++ linux/fs/super.c	Mon Dec  3 22:34:51 2001
@@ -898,9 +898,9 @@
 	/* Need to clean after the sucker */
 	if (fs->fs_flags & FS_LITTER)
 		d_genocide(root);
-	if (fs->fs_flags & (FS_SINGLE|FS_LITTER))
-		shrink_dcache_parent(root);
+	shrink_dcache_parent(root);
 	dput(root);
+	fsync_dev(sb->s_dev);
 	lock_super(sb);
 	if (sop) {
 		if (sop->write_super && sb->s_dirt)
@@ -1073,20 +1073,6 @@
 
 	if( (flags&MNT_FORCE) && sb->s_op->umount_begin)
 		sb->s_op->umount_begin(sb);
-
-	/*
-	 * Shrink dcache, then fsync. This guarantees that if the
-	 * filesystem is quiescent at this point, then (a) only the
-	 * root entry should be in use and (b) that root entry is
-	 * clean.
-	 */
-	shrink_dcache_sb(sb);
-	fsync_dev(sb->s_dev);
-
-	if (sb->s_root->d_inode->i_state) {
-		mntput(mnt);
-		return -EBUSY;
-	}
 
 	/* Something might grab it again - redo checks */
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-arc/hardware.h linux/include/asm-arm/arch-arc/hardware.h
--- linux-2.4.4-pre2/include/asm-arm/arch-arc/hardware.h	Mon Sep 18 15:15:22 2000
+++ linux/include/asm-arm/arch-arc/hardware.h	Mon Dec  3 22:34:51 2001
@@ -46,21 +46,28 @@
 #define SCREEN_BASE		0x02000000
 
 
+#define EXPMASK_BASE		0x03360000
+#define IOEB_BASE		0x03350000
+#define VIDC_BASE		0x03400000
+#define LATCHA_BASE		0x03250040
+#define LATCHB_BASE		0x03250018
+#define IOC_BASE		0x03200000
+#define FLOPPYDMA_BASE		0x0302a000
+#define PCIO_BASE		0x03010000
+
+#define vidc_writel(val)	__raw_writel(val, VIDC_BASE)
+
 #ifndef __ASSEMBLY__
 
 /*
  * for use with inb/outb
  */
-#define IO_VIDC_BASE		0x80100000
 #ifdef CONFIG_ARCH_A5K
-#define IOEB_VID_CTL		0x800d4012
-#define IOEB_PRESENT		0x800d4014
-#define IOEB_PSCLR		0x800d4016
-#define IOEB_MONTYPE		0x800d401c
+#define IOEB_VID_CTL		(IOEB_BASE + 0x48)
+#define IOEB_PRESENT		(IOEB_BASE + 0x50)
+#define IOEB_PSCLR		(IOEB_BASE + 0x58)
+#define IOEB_MONTYPE		(IOEB_BASE + 0x70)
 #endif
-#define LATCHAADDR		0x80094010
-#define LATCHBADDR		0x80094006
-#define IOC_BASE		0x80080000
 
 #define IO_EC_IOC4_BASE		0x8009c000
 #define IO_EC_IOC_BASE		0x80090000
@@ -74,36 +81,9 @@
 #define SCSI_BASE		0x03100000
 #endif
 
-/*
- * IO definitions
- */
-#define EXPMASK_BASE		((volatile unsigned char *)0x03360000)
-#define IOEB_BASE		((volatile unsigned char *)0x03350050)
-#define PCIO_FLOPPYDMABASE	((volatile unsigned char *)0x0302a000)
-#define PCIO_BASE		0x03010000
-
-/*
- * RAM definitions
- */
-#define GET_MEMORY_END(p)	(PAGE_OFFSET + (p->u1.s.page_size) * (p->u1.s.nr_pages))
-#define PARAMS_OFFSET		0x7c000
-
-#else
-
-#define IOEB_BASE		0x03350050
-#define IOC_BASE		0x03200000
-#define PCIO_FLOPPYDMABASE	0x0302a000
-#define PCIO_BASE		0x03010000
-
-#endif
-
-#ifndef __ASSEMBLY__
-#define __EXPMASK(offset)	(((volatile unsigned char *)EXPMASK_BASE)[offset])
-#else
-#define __EXPMASK(offset)	offset
 #endif
 
-#define	EXPMASK_STATUS	__EXPMASK(0x00)
-#define EXPMASK_ENABLE	__EXPMASK(0x04)
+#define	EXPMASK_STATUS		(EXPMASK_BASE + 0x00)
+#define EXPMASK_ENABLE		(EXPMASK_BASE + 0x04)
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-arc/irq.h linux/include/asm-arm/arch-arc/irq.h
--- linux-2.4.4-pre2/include/asm-arm/arch-arc/irq.h	Mon Sep 18 15:15:22 2000
+++ linux/include/asm-arm/arch-arc/irq.h	Mon Dec  3 22:34:51 2001
@@ -16,6 +16,7 @@
  */
 #include <linux/config.h>
 #include <asm/hardware/ioc.h>
+#include <asm/io.h>
 
 #ifdef CONFIG_ARCH_ARC
 #define a_clf()	clf()
@@ -29,105 +30,81 @@
 
 static void arc_mask_irq_ack_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
+	mask = 1 << irq;
 	a_clf();
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]\n"
-"	strb	%1, [%3]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOC_IRQMASKA)),
-	  "r" (ioaddr(IOC_IRQCLRA)));
+	val = ioc_readb(IOC_IRQMASKA);
+	ioc_writeb(val & ~mask, IOC_IRQMASKA);
+	ioc_writeb(mask, IOC_IRQCLRA);
 	a_stf();
 }
 
 static void arc_mask_irq_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
+	mask = 1 << irq;
 	a_clf();
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOC_IRQMASKA)));
+	val = ioc_readb(IOC_IRQMASKA);
+	ioc_writeb(val & ~mask, IOC_IRQMASKA);
 	a_stf();
 }
 
 static void arc_unmask_irq_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
+	mask = 1 << irq;
 	a_clf();
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOC_IRQMASKA)));
+	val = ioc_readb(IOC_IRQMASKA);
+	ioc_writeb(val | mask, IOC_IRQMASKA);
 	a_stf();
 }
 
 static void arc_mask_irq_b(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOC_IRQMASKB)));
+	mask = 1 << (irq & 7);
+	val = ioc_readb(IOC_IRQMASKB);
+	ioc_writeb(val & ~mask, IOC_IRQMASKB);
 }
 
 static void arc_unmask_irq_b(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOC_IRQMASKB)));
+	mask = 1 << (irq & 7);
+	val = ioc_readb(IOC_IRQMASKB);
+	ioc_writeb(val | mask, IOC_IRQMASKB);
 }
 
 static void arc_mask_irq_fiq(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOC_FIQMASK)));
+	mask = 1 << (irq & 7);
+	val = ioc_readb(IOC_FIQMASK);
+	ioc_writeb(val & ~mask, IOC_FIQMASK);
 }
 
 static void arc_unmask_irq_fiq(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOC_FIQMASK)));
+	mask = 1 << (irq & 7);
+	val = ioc_readb(IOC_FIQMASK);
+	ioc_writeb(val | mask, IOC_FIQMASK);
 }
 
 static __inline__ void irq_init_irq(void)
 {
-	extern void ecard_disableirq(unsigned int irq);
-	extern void ecard_enableirq(unsigned int irq);
 	int irq;
 
-	outb(0, IOC_IRQMASKA);
-	outb(0, IOC_IRQMASKB);
-	outb(0, IOC_FIQMASK);
+	ioc_writeb(0, IOC_IRQMASKA);
+	ioc_writeb(0, IOC_IRQMASKB);
+	ioc_writeb(0, IOC_FIQMASK);
 
 	for (irq = 0; irq < NR_IRQS; irq++) {
 		switch (irq) {
@@ -154,13 +131,6 @@
 			irq_desc[irq].mask_ack = arc_mask_irq_b;
 			irq_desc[irq].mask     = arc_mask_irq_b;
 			irq_desc[irq].unmask   = arc_unmask_irq_b;
-			break;
-
-		case 32 ... 40:
-			irq_desc[irq].valid    = 1;
-			irq_desc[irq].mask_ack = ecard_disableirq;
-			irq_desc[irq].mask     = ecard_disableirq;
-			irq_desc[irq].unmask   = ecard_enableirq;
 			break;
 
 		case 64 ... 72:
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-arc/system.h linux/include/asm-arm/arch-arc/system.h
--- linux-2.4.4-pre2/include/asm-arm/arch-arc/system.h	Mon Sep 18 15:15:22 2000
+++ linux/include/asm-arm/arch-arc/system.h	Mon Dec  3 22:34:51 2001
@@ -15,13 +15,6 @@
 
 extern __inline__ void arch_reset(char mode)
 {
-	extern void ecard_reset(int card);
-
-	/*
-	 * Reset all expansion cards.
-	 */
-	ecard_reset(-1);
-
 	/*
 	 * copy branch instruction to reset location and call it
 	 */
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-cl7500/hardware.h linux/include/asm-arm/arch-cl7500/hardware.h
--- linux-2.4.4-pre2/include/asm-arm/arch-cl7500/hardware.h	Mon Sep 18 15:15:22 2000
+++ linux/include/asm-arm/arch-cl7500/hardware.h	Mon Dec  3 22:34:51 2001
@@ -49,47 +49,18 @@
 
 #define FLUSH_BASE		0xdf000000
 
-
-#ifndef __ASSEMBLY__
-
-/*
- * for use with inb/outb
- */
-#define IO_VIDC_AUDIO_BASE	0x80140000
-#define IO_VIDC_BASE		0x80100000
-#define IO_IOMD_BASE		0x80080000
-#define IOC_BASE		0x80080000
-
-/*
- * IO definitions
- */
-#define EXPMASK_BASE		((volatile unsigned char *)0xe0360000)
-#define IOEB_BASE		((volatile unsigned char *)0xe0350050)
-#define PCIO_FLOPPYDMABASE	((volatile unsigned char *)0xe002a000)
+#define VIDC_BASE		0xe0400000
+#define IOMD_BASE		0xe0200000
+#define IOC_BASE		0xe0200000
+#define FLOPPYDMA_BASE		0xe002a000
 #define PCIO_BASE		0xe0010000
-/* in/out bias for the ISA slot region */
-#define ISASLOT_IO		0x80400000
-
-/*
- * RAM definitions
- */
-#define GET_MEMORY_END(p)	(PAGE_OFFSET + p->u1.s.page_size * \
-						(p->u1.s.pages_in_bank[0] + \
-						 p->u1.s.pages_in_bank[1] + \
-						 p->u1.s.pages_in_bank[2] + \
-						 p->u1.s.pages_in_bank[3]))
 
 #define FLUSH_BASE_PHYS		0x00000000	/* ROM */
 
-#else
+#define vidc_writel(val)	__raw_writel(val, VIDC_BASE)
 
-#define VIDC_SND_BASE		0xe0500000
-#define VIDC_BASE		0xe0400000
-#define IOMD_BASE		0xe0200000
-#define IOC_BASE		0xe0200000
-#define PCIO_FLOPPYDMABASE	0xe002a000
-#define PCIO_BASE		0xe0010000
+/* in/out bias for the ISA slot region */
+#define ISASLOT_IO		0x80400000
 
-#endif
 #endif
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-cl7500/irq.h linux/include/asm-arm/arch-cl7500/irq.h
--- linux-2.4.4-pre2/include/asm-arm/arch-cl7500/irq.h	Mon Sep 18 15:15:22 2000
+++ linux/include/asm-arm/arch-cl7500/irq.h	Mon Dec  3 22:34:51 2001
@@ -2,14 +2,16 @@
  * include/asm-arm/arch-cl7500/irq.h
  *
  * Copyright (C) 1996 Russell King
- * Copyright (C) 1999 Nexus Electronics Ltd.
+ * Copyright (C) 1999, 2001 Nexus Electronics Ltd.
  *
  * Changelog:
  *   10-10-1996	RMK	Brought up to date with arch-sa110eval
  *   22-08-1998	RMK	Restructured IRQ routines
  *   11-08-1999	PJB	Created ARM7500 version, derived from RiscPC code
  */
+
 #include <asm/hardware/iomd.h>
+#include <asm/io.h>
 
 static inline int fixup_irq(unsigned int irq)
 {
@@ -19,7 +21,7 @@
 			printk("Spurious ISA IRQ!\n");
 			return irq;
 		}
-		irq = 40;
+		irq = IRQ_ISA_BASE;
 		while (!(isabits & 1)) {
 			irq++;
 			isabits >>= 1;
@@ -31,161 +33,121 @@
 
 static void cl7500_mask_irq_ack_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]\n"
-"	strb	%1, [%3]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKA)),
-	  "r" (ioaddr(IOMD_IRQCLRA)));
+	mask = 1 << irq;
+	val = iomd_readb(IOMD_IRQMASKA);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKA);
+	iomd_writeb(mask, IOMD_IRQCLRA);
 }
 
 static void cl7500_mask_irq_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKA)));
+	mask = 1 << irq;
+	val = iomd_readb(IOMD_IRQMASKA);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKA);
 }
 
 static void cl7500_unmask_irq_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKA)));
+	mask = 1 << irq;
+	val = iomd_readb(IOMD_IRQMASKA);
+	iomd_writeb(val | mask, IOMD_IRQMASKA);
 }
 
 static void cl7500_mask_irq_b(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKB)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKB);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKB);
 }
 
 static void cl7500_unmask_irq_b(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKB)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKB);
+	iomd_writeb(val | mask, IOMD_IRQMASKB);
 }
 
 static void cl7500_mask_irq_c(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKC)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKC);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKC);
 }
 
 static void cl7500_unmask_irq_c(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKC)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKC);
+	iomd_writeb(val | mask, IOMD_IRQMASKC);
 }
 
 
 static void cl7500_mask_irq_d(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKD)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKD);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKD);
 }
 
 static void cl7500_unmask_irq_d(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKD)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKD);
+	iomd_writeb(val | mask, IOMD_IRQMASKD);
 }
 
 static void cl7500_mask_irq_dma(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_DMAMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_DMAMASK);
+	iomd_writeb(val & ~mask, IOMD_DMAMASK);
 }
 
 static void cl7500_unmask_irq_dma(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_DMAMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_DMAMASK);
+	iomd_writeb(val | mask, IOMD_DMAMASK);
 }
 
 static void cl7500_mask_irq_fiq(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_FIQMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_FIQMASK);
+	iomd_writeb(val & ~mask, IOMD_FIQMASK);
 }
 
 static void cl7500_unmask_irq_fiq(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_FIQMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_FIQMASK);
+	iomd_writeb(val | mask, IOMD_FIQMASK);
 }
 
 static void no_action(int cpl, void *dev_id, struct pt_regs *regs)
@@ -196,14 +158,12 @@
 
 static __inline__ void irq_init_irq(void)
 {
-	extern void ecard_disableirq(unsigned int irq);
-	extern void ecard_enableirq(unsigned int irq);
 	int irq;
 
-	outb(0, IOMD_IRQMASKA);
-	outb(0, IOMD_IRQMASKB);
-	outb(0, IOMD_FIQMASK);
-	outb(0, IOMD_DMAMASK);
+	iomd_writeb(0, IOMD_IRQMASKA);
+	iomd_writeb(0, IOMD_IRQMASKB);
+	iomd_writeb(0, IOMD_FIQMASK);
+	iomd_writeb(0, IOMD_DMAMASK);
 
 	for (irq = 0; irq < NR_IRQS; irq++) {
 		switch (irq) {
@@ -239,14 +199,14 @@
 			irq_desc[irq].unmask   = cl7500_unmask_irq_c;
 			break;
 
-		case 32 ... 39:
+		case 40 ... 47:
 			irq_desc[irq].valid    = 1;
 			irq_desc[irq].mask_ack = cl7500_mask_irq_d;
 			irq_desc[irq].mask     = cl7500_mask_irq_d;
 			irq_desc[irq].unmask   = cl7500_unmask_irq_d;
 			break;
 
-		case 40 ... 47:
+		case 48 ... 55:
 			irq_desc[irq].valid      = 1;
 			irq_desc[irq].probe_ok   = 1;
 			irq_desc[irq].mask_ack   = no_action;
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-cl7500/irqs.h linux/include/asm-arm/arch-cl7500/irqs.h
--- linux-2.4.4-pre2/include/asm-arm/arch-cl7500/irqs.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/arch-cl7500/irqs.h	Mon Dec  3 22:34:51 2001
@@ -35,20 +35,22 @@
 #define IRQ_IOP5		29
 #define IRQ_IOP6		30
 #define IRQ_IOP7		31
-#define IRQ_MOUSERX		32
-#define IRQ_MOUSETX		33
-#define IRQ_ADC			34
-#define IRQ_EVENT1		35
-#define IRQ_EVENT2		36
 
-#define IRQ_ISA_3		40
-#define IRQ_ISA_4		41
-#define IRQ_ISA_5		42
-#define IRQ_ISA_7		43
-#define IRQ_ISA_9		44
-#define IRQ_ISA_10		45
-#define IRQ_ISA_11		46
-#define IRQ_ISA_14		47	
+#define IRQ_MOUSERX		40
+#define IRQ_MOUSETX		41
+#define IRQ_ADC			42
+#define IRQ_EVENT1		43
+#define IRQ_EVENT2		44
+
+#define IRQ_ISA_BASE		48
+#define IRQ_ISA_3		48
+#define IRQ_ISA_4		49
+#define IRQ_ISA_5		50
+#define IRQ_ISA_7		51
+#define IRQ_ISA_9		52
+#define IRQ_ISA_10		53
+#define IRQ_ISA_11		54
+#define IRQ_ISA_14		55	
 
 #define FIQ_INT9		0
 #define FIQ_INT5		1
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-cl7500/shmparam.h linux/include/asm-arm/arch-cl7500/shmparam.h
--- linux-2.4.4-pre2/include/asm-arm/arch-cl7500/shmparam.h	Tue Nov 23 22:23:11 1999
+++ linux/include/asm-arm/arch-cl7500/shmparam.h	Wed Dec 31 16:00:00 1969
@@ -1,8 +0,0 @@
-/*
- * linux/include/asm-arm/arch-cl7500/shmparam.h
- *
- * Copyright (c) 1999 Nexus Electronics Ltd
- */
-
-/* The processor-centric definitions are OK.  */
-
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-cl7500/system.h linux/include/asm-arm/arch-cl7500/system.h
--- linux-2.4.4-pre2/include/asm-arm/arch-cl7500/system.h	Mon Sep 18 15:15:22 2000
+++ linux/include/asm-arm/arch-cl7500/system.h	Mon Dec  3 22:34:51 2001
@@ -11,12 +11,12 @@
 static void arch_idle(void)
 {
 	while (!current->need_resched && !hlt_counter)
-		outb(0, IOMD_SUSMODE);
+		iomd_writeb(0, IOMD_SUSMODE);
 }
 
 #define arch_reset(mode)			\
 	do {					\
-		outb (0, IOMD_ROMCR0);		\
+		iomd_writeb(0, IOMD_ROMCR0);	\
 		cpu_reset(0);			\
 	} while (0);
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-cl7500/time.h linux/include/asm-arm/arch-cl7500/time.h
--- linux-2.4.4-pre2/include/asm-arm/arch-cl7500/time.h	Sun Aug 13 09:54:15 2000
+++ linux/include/asm-arm/arch-cl7500/time.h	Mon Dec  3 22:34:51 2001
@@ -13,18 +13,17 @@
 {
 	do_timer(regs);
 	do_set_rtc();
+	do_profile(regs);
 
 	{
 		/* Twinkle the lights. */
-		static int count, state = 0xff;
+		static int count, state = 0xff00;
 		if (count-- == 0) {
-			state ^= 1;
+			state ^= 0x100;
 			count = 25;
-			*((volatile unsigned int *)(0xe002ba00)) = state;
+			*((volatile unsigned int *)LED_ADDRESS) = state;
 		}
 	}
-
-	do_profile(regs);
 }
 
 /*
@@ -32,6 +31,9 @@
  */
 extern __inline__ void setup_timer(void)
 {
+	extern void ioctime_init(void);
+	ioctime_init();
+
 	timer_irq.handler = timer_interrupt;
 
 	setup_arm_irq(IRQ_TIMER, &timer_irq);
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-cl7500/uncompress.h linux/include/asm-arm/arch-cl7500/uncompress.h
--- linux-2.4.4-pre2/include/asm-arm/arch-cl7500/uncompress.h	Sun Aug 13 09:54:15 2000
+++ linux/include/asm-arm/arch-cl7500/uncompress.h	Mon Dec  3 22:34:51 2001
@@ -1,11 +1,11 @@
 /*
  * linux/include/asm-arm/arch-cl7500/uncompress.h
  *
- * Copyright (C) 1999 Nexus Electronics Ltd.
+ * Copyright (C) 1999, 2000 Nexus Electronics Ltd.
  */
 
 #define BASE 0x03010000
-#define SERBASE (BASE + (0x3f8 << 2))
+#define SERBASE (BASE + (0x2f8 << 2))
 
 static __inline__ void putc(char c)
 {
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/hardware.h linux/include/asm-arm/arch-ebsa110/hardware.h
--- linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/hardware.h	Mon Sep 18 15:15:22 2000
+++ linux/include/asm-arm/arch-ebsa110/hardware.h	Mon Dec  3 22:34:51 2001
@@ -12,41 +12,55 @@
 #ifndef __ASM_ARCH_HARDWARE_H
 #define __ASM_ARCH_HARDWARE_H
 
-#ifndef __ASSEMBLY__
-
 /*
- * IO definitions
+ * The EBSA110 has a weird "ISA IO" region:
+ *
+ * Region 0 (addr = 0xf0000000 + io << 2)
+ * --------------------------------------------------------
+ * Physical region	IO region
+ * f0000fe0 - f0000ffc	3f8 - 3ff  ttyS0
+ * f0000e60 - f0000e64	398 - 399
+ * f0000de0 - f0000dfc	378 - 37f  lp0
+ * f0000be0 - f0000bfc	2f8 - 2ff  ttyS1
+ *
+ * Region 1 (addr = 0xf0000000 + (io & ~1) << 1 + (io & 1))
+ * --------------------------------------------------------
+ * Physical region	IO region
+ * f00014f1             a79        pnp write data
+ * f00007c0 - f00007c1	3e0 - 3e1  pcmcia
+ * f00004f1		279        pnp address
+ * f0000440 - f000046c  220 - 236  eth0
+ * f0000405		203        pnp read data
  */
-#define PIT_CTRL		((volatile unsigned char *)0xf200000d)
-#define PIT_T2			((volatile unsigned char *)0xf2000009)
-#define PIT_T1			((volatile unsigned char *)0xf2000005)
-#define PIT_T0			((volatile unsigned char *)0xf2000001)
 
-/*
- * Mapping areas
- */
-#define IO_BASE			0xe0000000
+#define ISAMEM_PHYS		0xe0000000
+#define ISAMEM_SIZE		0x10000000
 
-/*
- * RAM definitions
- */
-#define FLUSH_BASE_PHYS		0x40000000
+#define ISAIO_PHYS		0xf0000000
+#define ISAIO_SIZE		PGDIR_SIZE
 
-#else	/* __ASSEMBLY__ */
+#define TRICK0_PHYS		0xf2000000
+#define TRICK1_PHYS		0xf2400000
+#define TRICK2_PHYS		0xf2800000
+#define TRICK3_PHYS		0xf2c00000
+#define TRICK4_PHYS		0xf3000000
+#define TRICK5_PHYS		0xf3400000
+#define TRICK6_PHYS		0xf3800000
+#define TRICK7_PHYS		0xf3c00000
 
-#define IO_BASE			0
+#define ISAMEM_BASE		0xe0000000
+#define ISAIO_BASE		0xf0000000
 
-#endif	/* __ASSEMBLY__ */
-
-#define IO_SIZE			0x20000000
-#define IO_START		0xe0000000
+#define PIT_BASE		0xfc000000
+#define SOFT_BASE		0xfd000000
 
+/*
+ * RAM definitions
+ */
+#define FLUSH_BASE_PHYS		0x40000000
 #define FLUSH_BASE		0xdf000000
-#define PCIO_BASE		0xf0000000
-
-#define UNCACHEABLE_ADDR	0xf3000000
 
-#define PARAMS_OFFSET		0x400
+#define UNCACHEABLE_ADDR	0xff000000	/* IRQ_STAT */
 
 #endif
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/io.h linux/include/asm-arm/arch-ebsa110/io.h
--- linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/io.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/arch-ebsa110/io.h	Mon Dec  3 22:34:51 2001
@@ -13,7 +13,7 @@
 #ifndef __ASM_ARM_ARCH_IO_H
 #define __ASM_ARM_ARCH_IO_H
 
-#define IO_SPACE_LIMIT 0xffffffff
+#define IO_SPACE_LIMIT 0xffff
 
 /*
  * Generic virtual read/write
@@ -21,6 +21,39 @@
 #define __arch_getw(a)		(*(volatile unsigned short *)(a))
 #define __arch_putw(v,a)	(*(volatile unsigned short *)(a) = (v))
 
-#define __io(p)		(ISAIO_BASE + ((p) << 2))
+u8  __inb(int port);
+u16 __inw(int port);
+u32 __inl(int port);
+
+#define inb(p)			__inb(p)
+#define inw(p)			__inw(p)
+#define inl(p)			__inl(p)
+
+void __outb(u8  val, int port);
+void __outw(u16 val, int port);
+void __outl(u32 val, int port);
+
+#define outb(v,p)		__outb(v,p)
+#define outw(v,p)		__outw(v,p)
+#define outl(v,p)		__outl(v,p)
+
+u8  __readb(void *addr);
+u16 __readw(void *addr);
+u32 __readl(void *addr);
+
+#define readb(b)		__readb(b)
+#define readw(b)		__readw(b)
+#define readl(b)		__readl(b)
+
+void __writeb(u8  val, void *addr);
+void __writew(u16 val, void *addr);
+void __writel(u32 val, void *addr);
+
+#define writeb(v,b)		__writeb(v,b)
+#define writew(v,b)		__writew(v,b)
+#define writel(v,b)		__writel(v,b)
+
+#define __arch_ioremap(off,sz,c)	((void *)(off))
+#define __arch_iounmap(virt)		do { } while (0)
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/irq.h linux/include/asm-arm/arch-ebsa110/irq.h
--- linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/irq.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-ebsa110/irq.h	Mon Dec  3 22:34:51 2001
@@ -10,47 +10,4 @@
  *  Changelog:
  *   22-08-1998	RMK	Restructured IRQ routines
  */
-
-#define IRQ_MCLR	((volatile unsigned char *)0xf3000000)
-#define IRQ_MSET	((volatile unsigned char *)0xf2c00000)
-#define IRQ_MASK	((volatile unsigned char *)0xf2c00000)
-
-#define fixup_irq(x) (x)
-
-static void ebsa110_mask_and_ack_irq(unsigned int irq)
-{
-	*IRQ_MCLR = 1 << irq;
-}
-
-static void ebsa110_mask_irq(unsigned int irq)
-{
-	*IRQ_MCLR = 1 << irq;
-}
-
-static void ebsa110_unmask_irq(unsigned int irq)
-{
-	*IRQ_MSET = 1 << irq;
-}
- 
-static __inline__ void irq_init_irq(void)
-{
-	unsigned long flags;
-	int irq;
-
-	save_flags_cli (flags);
-	*IRQ_MCLR = 0xff;
-	*IRQ_MSET = 0x55;
-	*IRQ_MSET = 0x00;
-	if (*IRQ_MASK != 0x55)
-		while (1);
-	*IRQ_MCLR = 0xff;		/* clear all interrupt enables */
-	restore_flags (flags);
-
-	for (irq = 0; irq < NR_IRQS; irq++) {
-		irq_desc[irq].valid	= 1;
-		irq_desc[irq].probe_ok	= 1;
-		irq_desc[irq].mask_ack	= ebsa110_mask_and_ack_irq;
-		irq_desc[irq].mask	= ebsa110_mask_irq;
-		irq_desc[irq].unmask	= ebsa110_unmask_irq;
-	}
-}
+#define fixup_irq(i)	(i)
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/memory.h linux/include/asm-arm/arch-ebsa110/memory.h
--- linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/memory.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/arch-ebsa110/memory.h	Mon Dec  3 22:34:51 2001
@@ -35,13 +35,17 @@
 #define PHYS_OFFSET	(0x00000000UL)
 
 #define __virt_to_phys__is_a_macro
-#define __virt_to_phys(vpage) ((vpage) - PAGE_OFFSET)
+#define __virt_to_phys(vpage)	((vpage) - PAGE_OFFSET)
 #define __phys_to_virt__is_a_macro
-#define __phys_to_virt(ppage) ((ppage) + PAGE_OFFSET)
+#define __phys_to_virt(ppage)	((ppage) + PAGE_OFFSET)
 
+/*
+ * We keep this 1:1 so that we don't interfere
+ * with the PCMCIA memory regions
+ */
 #define __virt_to_bus__is_a_macro
-#define __virt_to_bus(x)	__virt_to_phys(x)
+#define __virt_to_bus(x)	(x)
 #define __bus_to_virt__is_a_macro
-#define __bus_to_virt(x)	__phys_to_virt(x)
+#define __bus_to_virt(x)	(x)
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/time.h linux/include/asm-arm/arch-ebsa110/time.h
--- linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/time.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-ebsa110/time.h	Mon Dec  3 22:34:51 2001
@@ -18,39 +18,16 @@
 
 #include <asm/leds.h>
 
-#define MCLK_47_8
-
-#if defined(MCLK_42_3)
-#define PIT1_COUNT 0xecbe
-#elif defined(MCLK_47_8)
-/*
- * This should be 0x10B43, but that doesn't exactly fit.
- * We run the timer interrupt at 5ms, and then divide it by
- * two in software...  This is so that the user processes
- * see exactly the same model whichever ARM processor they're
- * running on.
- */
-#define PIT1_COUNT 0x85A1
-#define DIVISOR 2
-#endif
+extern int  ebsa110_reset_timer(void);
+extern void ebsa110_setup_timer(void);
 
 static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	*PIT_T1 = (PIT1_COUNT) & 0xff;
-	*PIT_T1 = (PIT1_COUNT) >> 8;
-
-#ifdef DIVISOR
-	{
-		static unsigned int divisor;
-
-		if (divisor--)
-			return;
-		divisor = DIVISOR - 1;
+	if (ebsa110_reset_timer()) {
+		do_leds();
+		do_timer(regs);
+		do_profile(regs);
 	}
-#endif
-	do_leds();
-	do_timer(regs);
-	do_profile(regs);
 }
 
 /*
@@ -58,17 +35,7 @@
  */
 extern __inline__ void setup_timer(void)
 {
-	/*
-	 * Timer 1, mode 0, 16-bit, autoreload
-	 */
-	*PIT_CTRL = 0x70;
-
-	/*
-	 * Refresh counter clocked at 47.8MHz/7 = 146.4ns
-	 * We want centi-second interrupts
-	 */
-	*PIT_T1 = (PIT1_COUNT) & 0xff;
-	*PIT_T1 = (PIT1_COUNT) >> 8;
+	ebsa110_setup_timer();
 
 	timer_irq.handler = timer_interrupt;
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/uncompress.h linux/include/asm-arm/arch-ebsa110/uncompress.h
--- linux-2.4.4-pre2/include/asm-arm/arch-ebsa110/uncompress.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-ebsa110/uncompress.h	Mon Dec  3 22:34:51 2001
@@ -28,7 +28,11 @@
 	ldrb	%0, [%2], #1
 	teq	%0, #0
 	bne	1b
-3:	" : : "r" (0), "r" (0), "r" (s), "r" (0xf0000be0) : "cc");
+3:	ldrb	%1, [%3, #0x14]
+	and	%1, %1, #0x60
+	teq	%1, #0x60
+	bne	3b
+	" : : "r" (0), "r" (0), "r" (s), "r" (0xf0000be0) : "cc");
 }
 
 /*
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-ebsa285/irq.h linux/include/asm-arm/arch-ebsa285/irq.h
--- linux-2.4.4-pre2/include/asm-arm/arch-ebsa285/irq.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-ebsa285/irq.h	Mon Dec  3 22:34:51 2001
@@ -19,34 +19,7 @@
 #include <asm/irq.h>
 #include <asm/mach-types.h>
 
-/*
- * Footbridge IRQ translation table
- *  Converts from our IRQ numbers into FootBridge masks
- */
-static const int dc21285_irq_mask[] = {
-	IRQ_MASK_UART_RX,	/*  0 */
-	IRQ_MASK_UART_TX,	/*  1 */
-	IRQ_MASK_TIMER1,	/*  2 */
-	IRQ_MASK_TIMER2,	/*  3 */
-	IRQ_MASK_TIMER3,	/*  4 */
-	IRQ_MASK_IN0,		/*  5 */
-	IRQ_MASK_IN1,		/*  6 */
-	IRQ_MASK_IN2,		/*  7 */
-	IRQ_MASK_IN3,		/*  8 */
-	IRQ_MASK_DOORBELLHOST,	/*  9 */
-	IRQ_MASK_DMA1,		/* 10 */
-	IRQ_MASK_DMA2,		/* 11 */
-	IRQ_MASK_PCI,		/* 12 */
-	IRQ_MASK_SDRAMPARITY,	/* 13 */
-	IRQ_MASK_I2OINPOST,	/* 14 */
-	IRQ_MASK_PCI_ABORT,	/* 15 */
-	IRQ_MASK_PCI_SERR,	/* 16 */
-	IRQ_MASK_DISCARD_TIMER,	/* 17 */
-	IRQ_MASK_PCI_DPERR,	/* 18 */
-	IRQ_MASK_PCI_PERR,	/* 19 */
-};
-
-static int isa_irq = -1;
+int isa_irq = -1;
 
 static inline int fixup_irq(unsigned int irq)
 {
@@ -58,166 +31,3 @@
 	return irq;
 }
 
-static void dc21285_mask_irq(unsigned int irq)
-{
-	*CSR_IRQ_DISABLE = dc21285_irq_mask[_DC21285_INR(irq)];
-}
-
-static void dc21285_unmask_irq(unsigned int irq)
-{
-	*CSR_IRQ_ENABLE = dc21285_irq_mask[_DC21285_INR(irq)];
-}
-
-static void isa_mask_pic_lo_irq(unsigned int irq)
-{
-	unsigned int mask = 1 << (irq & 7);
-
-	outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO);
-}
-
-static void isa_mask_ack_pic_lo_irq(unsigned int irq)
-{
-	unsigned int mask = 1 << (irq & 7);
-
-	outb(inb(PIC_MASK_LO) | mask, PIC_MASK_LO);
-	outb(0x20, PIC_LO);
-}
-
-static void isa_unmask_pic_lo_irq(unsigned int irq)
-{
-	unsigned int mask = 1 << (irq & 7);
-
-	outb(inb(PIC_MASK_LO) & ~mask, PIC_MASK_LO);
-}
-
-static void isa_mask_pic_hi_irq(unsigned int irq)
-{
-	unsigned int mask = 1 << (irq & 7);
-
-	outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI);
-}
-
-static void isa_mask_ack_pic_hi_irq(unsigned int irq)
-{
-	unsigned int mask = 1 << (irq & 7);
-
-	outb(inb(PIC_MASK_HI) | mask, PIC_MASK_HI);
-	outb(0x62, PIC_LO);
-	outb(0x20, PIC_HI);
-}
-
-static void isa_unmask_pic_hi_irq(unsigned int irq)
-{
-	unsigned int mask = 1 << (irq & 7);
-
-	outb(inb(PIC_MASK_HI) & ~mask, PIC_MASK_HI);
-}
-
-static void no_action(int cpl, void *dev_id, struct pt_regs *regs)
-{
-}
-
-static struct irqaction irq_cascade = { no_action, 0, 0, "cascade", NULL, NULL };
-static struct resource pic1_resource = { "pic1", 0x20, 0x3f };
-static struct resource pic2_resource = { "pic2", 0xa0, 0xbf };
-
-static __inline__ void irq_init_irq(void)
-{
-	int irq;
-
-	/*
-	 * setup DC21285 IRQs
-	 */
-	*CSR_IRQ_DISABLE = -1;
-	*CSR_FIQ_DISABLE = -1;
-
-	for (irq = _DC21285_IRQ(0); irq < _DC21285_IRQ(20); irq++) {
-		irq_desc[irq].valid	= 1;
-		irq_desc[irq].probe_ok	= 1;
-		irq_desc[irq].mask_ack	= dc21285_mask_irq;
-		irq_desc[irq].mask	= dc21285_mask_irq;
-		irq_desc[irq].unmask	= dc21285_unmask_irq;
-	}
-
-	/*
-	 * Determine the ISA settings for
-	 * the machine we're running on.
-	 */
-	isa_irq = -1;
-
-	if (footbridge_cfn_mode()) {
-		if (machine_is_ebsa285())
-			/* The following is dependent on which slot
-			 * you plug the Southbridge card into.  We
-			 * currently assume that you plug it into
-			 * the right-hand most slot.
-			 */
-			isa_irq = IRQ_PCI;
-
-		if (machine_is_cats())
-			isa_irq = IRQ_IN2;
-
-		if (machine_is_netwinder())
-			isa_irq = IRQ_IN3;
-	}
-
-	if (isa_irq != -1) {
-		/*
-		 * Setup, and then probe for an ISA PIC
-		 * If the PIC is not there, then we
-		 * ignore the PIC.
-		 */
-		outb(0x11, PIC_LO);
-		outb(_ISA_IRQ(0), PIC_MASK_LO);	/* IRQ number		*/
-		outb(0x04, PIC_MASK_LO);	/* Slave on Ch2		*/
-		outb(0x01, PIC_MASK_LO);	/* x86			*/
-		outb(0xf5, PIC_MASK_LO);	/* pattern: 11110101	*/
-
-		outb(0x11, PIC_HI);
-		outb(_ISA_IRQ(8), PIC_MASK_HI);	/* IRQ number		*/
-		outb(0x02, PIC_MASK_HI);	/* Slave on Ch1		*/
-		outb(0x01, PIC_MASK_HI);	/* x86			*/
-		outb(0xfa, PIC_MASK_HI);	/* pattern: 11111010	*/
-
-		outb(0x0b, PIC_LO);
-		outb(0x0b, PIC_HI);
-
-		if (inb(PIC_MASK_LO) == 0xf5 && inb(PIC_MASK_HI) == 0xfa) {
-			outb(0xff, PIC_MASK_LO);/* mask all IRQs	*/
-			outb(0xff, PIC_MASK_HI);/* mask all IRQs	*/
-		} else
-			isa_irq = -1;
-	}
-
-	if (isa_irq != -1) {
-		for (irq = _ISA_IRQ(0); irq < _ISA_IRQ(8); irq++) {
-			irq_desc[irq].valid	= 1;
-			irq_desc[irq].probe_ok	= 1;
-			irq_desc[irq].mask_ack	= isa_mask_ack_pic_lo_irq;
-			irq_desc[irq].mask	= isa_mask_pic_lo_irq;
-			irq_desc[irq].unmask	= isa_unmask_pic_lo_irq;
-		}
-
-		for (irq = _ISA_IRQ(8); irq < _ISA_IRQ(16); irq++) {
-			irq_desc[irq].valid	= 1;
-			irq_desc[irq].probe_ok	= 1;
-			irq_desc[irq].mask_ack	= isa_mask_ack_pic_hi_irq;
-			irq_desc[irq].mask	= isa_mask_pic_hi_irq;
-			irq_desc[irq].unmask	= isa_unmask_pic_hi_irq;
-		}
-
-		request_resource(&ioport_resource, &pic1_resource);
-		request_resource(&ioport_resource, &pic2_resource);
-		setup_arm_irq(IRQ_ISA_CASCADE, &irq_cascade);
-		setup_arm_irq(isa_irq, &irq_cascade);
-
-		/*
-		 * On the NetWinder, don't automatically
-		 * enable ISA IRQ11 when it is requested.
-		 * There appears to be a missing pull-up
-		 * resistor on this line.
-		 */
-		if (machine_is_netwinder())
-			irq_desc[_ISA_IRQ(11)].noautoenable = 1;
-	}
-}
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/aux_reg.h linux/include/asm-arm/arch-l7200/aux_reg.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/aux_reg.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/aux_reg.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,28 @@
+/*
+ * linux/include/asm-arm/arch-l7200/aux_reg.h
+ *
+ * Copyright (C) 2000 Steve Hill (sjhill@cotw.com)
+ *
+ * Changelog:
+ *   08-02-2000	SJH	Created file
+ */
+#ifndef _ASM_ARCH_AUXREG_H
+#define _ASM_ARCH_AUXREG_H
+
+#include <asm/arch/hardware.h>
+
+#define l7200aux_reg	*((volatile unsigned int *) (AUX_BASE))
+
+/*
+ * Auxillary register values
+ */
+#define AUX_CLEAR		0x00000000
+#define AUX_DIAG_LED_ON		0x00000002
+#define AUX_RTS_UART1		0x00000004
+#define AUX_DTR_UART1		0x00000008
+#define AUX_KBD_COLUMN_12_HIGH	0x00000010
+#define AUX_KBD_COLUMN_12_OFF	0x00000020
+#define AUX_KBD_COLUMN_13_HIGH	0x00000040
+#define AUX_KBD_COLUMN_13_OFF	0x00000080
+
+#endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/dma.h linux/include/asm-arm/arch-l7200/dma.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/dma.h	Tue Jul 18 22:43:25 2000
+++ linux/include/asm-arm/arch-l7200/dma.h	Mon Dec  3 22:34:51 2001
@@ -1,26 +1,24 @@
 /*
  * linux/include/asm-arm/arch-l7200/dma.h
+ *
+ * Copyright (C) 2000 Steve Hill (sjhill@cotw.com)
+ *
+ * Changelog:
+ *  08-29-2000	SJH	Created
  */
 #ifndef __ASM_ARCH_DMA_H
 #define __ASM_ARCH_DMA_H
 
+/* DMA is not yet implemented! It should be the same as acorn, copy over.. */
+
 /*
  * This is the maximum DMA address that can be DMAd to.
  * There should not be more than (0xd0000000 - 0xc0000000)
  * bytes of RAM.
  */
-#define MAX_DMA_ADDRESS		0xd0000000
-#define MAX_DMA_CHANNELS	8
-
-#define DMA_0			0
-#define DMA_1			1
-#define DMA_2			2
-#define DMA_3			3
-#define DMA_S0			4
-#define DMA_S1			5
-#define DMA_VIRTUAL_FLOPPY	6
-#define DMA_VIRTUAL_SOUND	7
+#define MAX_DMA_ADDRESS         0xd0000000
+#define MAX_DMA_CHANNELS        0
 
-#define DMA_FLOPPY		DMA_VIRTUAL_FLOPPY
+#define DMA_S0                  0
 
-#endif
+#endif /* _ASM_ARCH_DMA_H */
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/gp_timers.h linux/include/asm-arm/arch-l7200/gp_timers.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/gp_timers.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/gp_timers.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,42 @@
+/*
+ * linux/include/asm-arm/arch-l7200/gp_timers.h
+ *
+ * Copyright (C) 2000 Steve Hill (sjhill@cotw.com)
+ *
+ * Changelog:
+ *   07-28-2000	SJH	Created file
+ *   08-02-2000	SJH	Used structure for registers
+ */
+#ifndef _ASM_ARCH_GPTIMERS_H
+#define _ASM_ARCH_GPTIMERS_H
+
+#include <asm/arch/hardware.h>
+
+/*
+ * Layout of L7200 general purpose timer registers
+ */
+struct GPT_Regs {
+	unsigned int TIMERLOAD;
+	unsigned int TIMERVALUE;
+	unsigned int TIMERCONTROL;
+	unsigned int TIMERCLEAR;
+};
+
+#define GPT_BASE		(IO_BASE_2 + 0x3000)
+#define l7200_timer1_regs	((volatile struct GPT_Regs *) (GPT_BASE))
+#define l7200_timer2_regs	((volatile struct GPT_Regs *) (GPT_BASE + 0x20))
+
+/*
+ * General register values
+ */
+#define	GPT_PRESCALE_1		0x00000000
+#define	GPT_PRESCALE_16		0x00000004
+#define	GPT_PRESCALE_256	0x00000008
+#define GPT_MODE_FREERUN	0x00000000
+#define GPT_MODE_PERIODIC	0x00000040
+#define GPT_ENABLE		0x00000080
+#define GPT_BZTOG		0x00000100
+#define GPT_BZMOD		0x00000200
+#define GPT_LOAD_MASK 		0x0000ffff
+
+#endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/gpio.h linux/include/asm-arm/arch-l7200/gpio.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/gpio.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/gpio.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,105 @@
+/****************************************************************************/
+/*
+ *      linux/include/asm-arm/arch-l7200/gpio.h
+ *
+ *      Registers and  helper functions for the L7200 Link-Up Systems
+ *      GPIO.
+ *
+ *      (C) Copyright 2000, S A McConnell  (samcconn@cotw.com)
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/****************************************************************************/
+
+#define GPIO_OFF   0x00005000  /* Offset from IO_START to the GPIO reg's. */
+
+/* IO_START and IO_BASE are defined in hardware.h */
+
+#define GPIO_START (IO_START_2 + GPIO_OFF) /* Physical addr of the GPIO reg. */
+#define GPIO_BASE  (IO_BASE_2  + GPIO_OFF) /* Virtual addr of the GPIO reg. */
+
+/* Offsets from the start of the GPIO for all the registers. */
+#define PADR_OFF     0x000
+#define PADDR_OFF    0x004
+#define PASBSR_OFF   0x008
+#define PAEENR_OFF   0x00c
+#define PAESNR_OFF   0x010
+#define PAESTR_OFF   0x014
+#define PAIMR_OFF    0x018
+#define PAINT_OFF    0x01c
+
+#define PBDR_OFF     0x020
+#define PBDDR_OFF    0x024
+#define PBSBSR_OFF   0x028
+#define PBIMR_OFF    0x038
+#define PBINT_OFF    0x03c
+
+#define PCDR_OFF     0x040
+#define PCDDR_OFF    0x044
+#define PCSBSR_OFF   0x048
+#define PCIMR_OFF    0x058
+#define PCINT_OFF    0x05c
+
+#define PDDR_OFF     0x060
+#define PDDDR_OFF    0x064
+#define PDSBSR_OFF   0x068
+#define PDEENR_OFF   0x06c
+#define PDESNR_OFF   0x070
+#define PDESTR_OFF   0x074
+#define PDIMR_OFF    0x078
+#define PDINT_OFF    0x07c
+
+#define PEDR_OFF     0x080
+#define PEDDR_OFF    0x084
+#define PESBSR_OFF   0x088
+#define PEEENR_OFF   0x08c
+#define PEESNR_OFF   0x090
+#define PEESTR_OFF   0x094
+#define PEIMR_OFF    0x098
+#define PEINT_OFF    0x09c
+
+/* Define the GPIO registers for use by device drivers and the kernel. */
+#define PADR   (*(volatile unsigned long *)(GPIO_BASE+PADR_OFF))
+#define PADDR  (*(volatile unsigned long *)(GPIO_BASE+PADDR_OFF))
+#define PASBSR (*(volatile unsigned long *)(GPIO_BASE+PASBSR_OFF))
+#define PAEENR (*(volatile unsigned long *)(GPIO_BASE+PAEENR_OFF))
+#define PAESNR (*(volatile unsigned long *)(GPIO_BASE+PAESNR_OFF))
+#define PAESTR (*(volatile unsigned long *)(GPIO_BASE+PAESTR_OFF))
+#define PAIMR  (*(volatile unsigned long *)(GPIO_BASE+PAIMR_OFF))
+#define PAINT  (*(volatile unsigned long *)(GPIO_BASE+PAINT_OFF))
+
+#define PBDR   (*(volatile unsigned long *)(GPIO_BASE+PBDR_OFF))
+#define PBDDR  (*(volatile unsigned long *)(GPIO_BASE+PBDDR_OFF))
+#define PBSBSR (*(volatile unsigned long *)(GPIO_BASE+PBSBSR_OFF))
+#define PBIMR  (*(volatile unsigned long *)(GPIO_BASE+PBIMR_OFF))
+#define PBINT  (*(volatile unsigned long *)(GPIO_BASE+PBINT_OFF))
+
+#define PCDR   (*(volatile unsigned long *)(GPIO_BASE+PCDR_OFF))
+#define PCDDR  (*(volatile unsigned long *)(GPIO_BASE+PCDDR_OFF))
+#define PCSBSR (*(volatile unsigned long *)(GPIO_BASE+PCSBSR_OFF))
+#define PCIMR  (*(volatile unsigned long *)(GPIO_BASE+PCIMR_OFF))
+#define PCINT  (*(volatile unsigned long *)(GPIO_BASE+PCINT_OFF))
+
+#define PDDR   (*(volatile unsigned long *)(GPIO_BASE+PDDR_OFF))
+#define PDDDR  (*(volatile unsigned long *)(GPIO_BASE+PDDDR_OFF))
+#define PDSBSR (*(volatile unsigned long *)(GPIO_BASE+PDSBSR_OFF))
+#define PDEENR (*(volatile unsigned long *)(GPIO_BASE+PDEENR_OFF))
+#define PDESNR (*(volatile unsigned long *)(GPIO_BASE+PDESNR_OFF))
+#define PDESTR (*(volatile unsigned long *)(GPIO_BASE+PDESTR_OFF))
+#define PDIMR  (*(volatile unsigned long *)(GPIO_BASE+PDIMR_OFF))
+#define PDINT  (*(volatile unsigned long *)(GPIO_BASE+PDINT_OFF))
+
+#define PEDR   (*(volatile unsigned long *)(GPIO_BASE+PEDR_OFF))
+#define PEDDR  (*(volatile unsigned long *)(GPIO_BASE+PEDDR_OFF))
+#define PESBSR (*(volatile unsigned long *)(GPIO_BASE+PESBSR_OFF))
+#define PEEENR (*(volatile unsigned long *)(GPIO_BASE+PEEENR_OFF))
+#define PEESNR (*(volatile unsigned long *)(GPIO_BASE+PEESNR_OFF))
+#define PEESTR (*(volatile unsigned long *)(GPIO_BASE+PEESTR_OFF))
+#define PEIMR  (*(volatile unsigned long *)(GPIO_BASE+PEIMR_OFF))
+#define PEINT  (*(volatile unsigned long *)(GPIO_BASE+PEINT_OFF))
+
+#define VEE_EN         0x02
+#define BACKLIGHT_EN   0x04
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/hardware.h linux/include/asm-arm/arch-l7200/hardware.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/hardware.h	Mon Nov 27 17:07:59 2000
+++ linux/include/asm-arm/arch-l7200/hardware.h	Mon Dec  3 22:34:51 2001
@@ -13,6 +13,8 @@
  *   04-21-2000	 RS 	Changed mapping of I/O in virtual space
  *   04-25-2000	SJH	Removed unused symbols and such
  *   05-05-2000	SJH	Complete rewrite
+ *   07-31-2000	SJH	Added undocumented debug auxillary port to
+ *			get at last two columns for keyboard driver
  */
 #ifndef __ASM_ARCH_HARDWARE_H
 #define __ASM_ARCH_HARDWARE_H
@@ -33,6 +35,18 @@
 #define IO_START_2		0x90000000      /* I/O */
 #define IO_SIZE_2		0x01000000
 #define IO_BASE_2		0xd1000000
+
+#define AUX_START		0x1a000000      /* AUX PORT */
+#define AUX_SIZE 		0x01000000
+#define AUX_BASE		0xd2000000
+
+#define FLASH1_START		0x00000000      /* FLASH BANK 1 */
+#define FLASH1_SIZE 		0x01000000
+#define FLASH1_BASE		0xd3000000
+
+#define FLASH2_START		0x10000000      /* FLASH BANK 2 */
+#define FLASH2_SIZE 		0x01000000
+#define FLASH2_BASE		0xd4000000
 
 #define ISA_START		0x20000000	/* ISA */
 #define ISA_SIZE		0x20000000
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/io.h linux/include/asm-arm/arch-l7200/io.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/io.h	Sun Aug 13 09:54:15 2000
+++ linux/include/asm-arm/arch-l7200/io.h	Mon Dec  3 22:34:51 2001
@@ -3,8 +3,9 @@
  *
  * Copyright (C) 2000 Steve Hill (sjhill@cotw.com)
  *
- * Modifications:
+ * Changelog:
  *  03-21-2000	SJH	Created from linux/include/asm-arm/arch-nexuspci/io.h
+ *  08-31-2000	SJH	Added in IO functions necessary for new drivers
  */
 #ifndef __ASM_ARM_ARCH_IO_H
 #define __ASM_ARM_ARCH_IO_H
@@ -14,16 +15,49 @@
 #define IO_SPACE_LIMIT 0xffffffff
 
 /*
+ * There are not real ISA nor PCI buses, so we fake it.
+ */
+#define __io_pci(a)             (PCIO_BASE + (a))
+#define __mem_pci(a)            ((unsigned long)(a))
+#define __mem_isa(a)            ((unsigned long)(a))
+
+#define __ioaddr(p)             __io_pci(p)
+
+/*
+ * Generic virtual read/write
+ */
+#define __arch_getb(a)          (*(volatile unsigned char *)(a))
+#define __arch_getl(a)          (*(volatile unsigned int  *)(a))
+
+extern __inline__ unsigned int __arch_getw(unsigned long a)
+{
+	unsigned int value;
+	__asm__ __volatile__("ldr%?h    %0, [%1, #0]    @ getw"
+		: "=&r" (value)
+		: "r" (a));
+	return value;
+}
+
+#define __arch_putb(v,a)        (*(volatile unsigned char *)(a) = (v))
+#define __arch_putl(v,a)        (*(volatile unsigned int  *)(a) = (v))
+
+extern __inline__ void __arch_putw(unsigned int value, unsigned long a)
+{
+        __asm__ __volatile__("str%?h    %0, [%1, #0]    @ putw"
+                : : "r" (value), "r" (a));
+}
+
+/*
  * Translated address IO functions
  *
  * IO address has already been translated to a virtual address
  */
-#define outb_t(v,p)	(*(volatile unsigned char *)(p) = (v))
-#define inb_t(p)	(*(volatile unsigned char *)(p))
-#define outw_t(v,p)	(*(volatile unsigned int *)(p) = (v))
-#define inw_t(p)	(*(volatile unsigned int *)(p))
-#define outl_t(v,p)	(*(volatile unsigned long *)(p) = (v))
-#define inl_t(p)	(*(volatile unsigned long *)(p))
+#define outb_t(v,p)		(*(volatile unsigned char *)(p) = (v))
+#define inb_t(p)		(*(volatile unsigned char *)(p))
+#define outw_t(v,p)		(*(volatile unsigned int *)(p) = (v))
+#define inw_t(p)		(*(volatile unsigned int *)(p))
+#define outl_t(v,p)		(*(volatile unsigned long *)(p) = (v))
+#define inl_t(p)		(*(volatile unsigned long *)(p))
 
 /*
  * FIXME - These are to allow for linking. On all the other
@@ -32,11 +66,13 @@
  *         macros will eventually become more involved. Use
  *         with caution and don't be surprised by kernel oopses!!!
  */
-#define inb(p)	 	inb_t(p)
-#define inw(p)	 	inw_t(p)
-#define inl(p)	 	inl_t(p)
-#define outb(v,p)	outb_t(v,p)
-#define outw(v,p)	outw_t(v,p)
-#define outl(v,p)	outl_t(v,p)
+#define inb(p)		 	inb_t(p)
+#define inw(p)	 		inw_t(p)
+#define inl(p)	 		inl_t(p)
+#define outb(v,p)		outb_t(v,p)
+#define outw(v,p)		outw_t(v,p)
+#define outl(v,p)		outl_t(v,p)
+
+#define __arch_ioremap		__ioremap
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/irq.h linux/include/asm-arm/arch-l7200/irq.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/irq.h	Mon May 15 12:00:33 2000
+++ linux/include/asm-arm/arch-l7200/irq.h	Mon Dec  3 22:34:51 2001
@@ -10,6 +10,8 @@
  *   05-05-2000 SJH     Complete rewrite
  */
 
+#include <asm/arch/hardware.h>
+
 /*
  * IRQ base register
  */
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/irqs.h linux/include/asm-arm/arch-l7200/irqs.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/irqs.h	Mon May 15 12:00:33 2000
+++ linux/include/asm-arm/arch-l7200/irqs.h	Mon Dec  3 22:34:51 2001
@@ -7,6 +7,12 @@
  * Changelog:
  *   01-02-2000 RS	Create l7200 version
  *   03-28-2000 SJH	Removed unused interrupt
+ *   07-28-2000 SJH	Added pseudo-keyboard interrupt
+ */
+
+/*
+ * NOTE: The second timer (Timer 2) is used as the keyboard
+ *       interrupt when the keyboard driver is enabled.
  */
 
 #define NR_IRQS          32
@@ -16,7 +22,7 @@
 #define IRQ_DEBUG_RX      2   /* Comm Rx debug */
 #define IRQ_DEBUG_TX      3   /* Comm Tx debug */
 #define IRQ_GCTC1         4   /* Timer 1 */
-#define IRQ_GCTC2         5   /* Timer 2 */
+#define IRQ_GCTC2         5   /* Timer 2 / Keyboard */
 #define IRQ_DMA           6   /* DMA controller */
 #define IRQ_CLCD          7   /* Color LCD controller */
 #define IRQ_SM_RX         8   /* Smart card */
@@ -40,6 +46,11 @@
 #define IRQ_INT0         26   /* External active low interrupt */
 #define IRQ_INT1         27   /* External active low interrupt */
 #define IRQ_INT2         28   /* External active low interrupt */
-#define IRQ_INT3         29   /* External active low interrupt */
+#define IRQ_UCB1200      29   /* Interrupt generated by UCB1200*/
 #define IRQ_BAT_LO       30   /* Low batery or external power */
 #define IRQ_MEDIA_CHG    31   /* Media change interrupt */
+
+/*
+ * This is the offset of the FIQ "IRQ" numbers
+ */
+#define FIQ_START	64
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/keyboard.h linux/include/asm-arm/arch-l7200/keyboard.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/keyboard.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/keyboard.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,49 @@
+/*
+ *  linux/include/asm-arm/arch-l7200/keyboard.h
+ *
+ *  Keyboard driver definitions for LinkUp Systems L7200 architecture
+ *
+ *  Copyright (C) 2000 Scott A McConnell (samcconn@cotw.com)
+ *                     Steve Hill (sjhill@cotw.com)
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ *
+ * Changelog:
+ *   07-18-2000	SAM	Created file
+ *   07-28-2000	SJH	Complete rewrite
+ */
+
+#include <asm/irq.h>
+
+/*
+ * Layout of L7200 keyboard registers
+ */
+struct KBD_Port {       
+	unsigned int KBDR;
+	unsigned int KBDMR;
+	unsigned int KBSBSR;
+	unsigned int Reserved;
+	unsigned int KBKSR;
+};
+
+#define KBD_BASE        IO_BASE_2 + 0x4000
+#define l7200kbd_hwregs ((volatile struct KBD_Port *) (KBD_BASE))
+
+extern void l7200kbd_init_hw(void);
+extern int l7200kbd_translate(unsigned char scancode, unsigned char *keycode,
+			      char raw_mode);
+
+#define kbd_setkeycode(sc,kc)		(-EINVAL)
+#define kbd_getkeycode(sc)		(-EINVAL)
+
+#define kbd_translate(sc, kcp, rm)      ({ *(kcp) = (sc); 1; })
+#define kbd_unexpected_up(kc)           (0200)
+#define kbd_leds(leds)                  do {} while (0)
+#define kbd_init_hw()                   l7200kbd_init_hw()
+#define kbd_sysrq_xlate                 do {} while (0)
+#define kbd_disable_irq()               disable_irq(IRQ_GCTC2)
+#define kbd_enable_irq()                enable_irq(IRQ_GCTC2)
+
+#define SYSRQ_KEY	13
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/pmpcon.h linux/include/asm-arm/arch-l7200/pmpcon.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/pmpcon.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/pmpcon.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,46 @@
+/****************************************************************************/
+/*
+ *  linux/include/asm-arm/arch-l7200/pmpcon.h
+ *
+ *   Registers and  helper functions for the L7200 Link-Up Systems
+ *   DC/DC converter register.
+ *
+ *   (C) Copyright 2000, S A McConnell  (samcconn@cotw.com)
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/****************************************************************************/
+
+#define PMPCON_OFF 0x00006000  /* Offset from IO_START_2. */
+
+/* IO_START_2 and IO_BASE_2 are defined in hardware.h */
+
+#define PMPCON_START (IO_START_2 + PMPCON_OFF)  /* Physical address of reg. */
+#define PMPCON_BASE  (IO_BASE_2  + PMPCON_OFF)  /* Virtual address of reg. */
+
+
+#define PMPCON (*(volatile unsigned int *)(PMPCON_BASE))
+
+#define PWM2_50CYCLE 0x800
+#define CONTRAST     0x9
+
+#define PWM1H (CONTRAST)
+#define PWM1L (CONTRAST << 4)
+
+#define PMPCON_VALUE  (PWM2_50CYCLE | PWM1L | PWM1H) 
+	
+/* PMPCON = 0x811;   // too light and fuzzy
+ * PMPCON = 0x844;   
+ * PMPCON = 0x866;   // better color poor depth
+ * PMPCON = 0x888;   // Darker but better depth 
+ * PMPCON = 0x899;   // Darker even better depth
+ * PMPCON = 0x8aa;   // too dark even better depth
+ * PMPCON = 0X8cc;   // Way too dark
+ */
+
+/* As CONTRAST value increases the greater the depth perception and
+ * the darker the colors.
+ */
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/pmu.h linux/include/asm-arm/arch-l7200/pmu.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/pmu.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/pmu.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,125 @@
+/****************************************************************************/
+/*
+ *  linux/include/asm-arm/arch-l7200/pmu.h
+ *
+ *   Registers and  helper functions for the L7200 Link-Up Systems
+ *   Power Management Unit (PMU).
+ *
+ *   (C) Copyright 2000, S A McConnell  (samcconn@cotw.com)
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/****************************************************************************/
+
+#define PMU_OFF   0x00050000  /* Offset from IO_START to the PMU registers. */
+
+/* IO_START and IO_BASE are defined in hardware.h */
+
+#define PMU_START (IO_START + PMU_OFF)  /* Physical addr. of the PMU reg. */
+#define PMU_BASE  (IO_BASE  + PMU_OFF)  /* Virtual addr. of the PMU reg. */
+
+
+/* Define the PMU registers for use by device drivers and the kernel. */
+
+typedef struct {
+     unsigned int CURRENT;  /* Current configuration register */
+     unsigned int NEXT;     /* Next configuration register */
+     unsigned int reserved;
+     unsigned int RUN;      /* Run configuration register */
+     unsigned int COMM;     /* Configuration command register */
+     unsigned int SDRAM;    /* SDRAM configuration bypass register */
+} pmu_interface;
+
+#define PMU ((volatile pmu_interface *)(PMU_BASE))
+
+
+/* Macro's for reading the common register fields. */
+
+#define GET_TRANSOP(reg)  ((reg >> 25) & 0x03) /* Bits 26-25 */
+#define GET_OSCEN(reg)    ((reg >> 16) & 0x01)
+#define GET_OSCMUX(reg)   ((reg >> 15) & 0x01)
+#define GET_PLLMUL(reg)   ((reg >>  9) & 0x3f) /* Bits 14-9 */
+#define GET_PLLEN(reg)    ((reg >>  8) & 0x01)
+#define GET_PLLMUX(reg)   ((reg >>  7) & 0x01)
+#define GET_BCLK_DIV(reg) ((reg >>  3) & 0x03) /* Bits 4-3 */
+#define GET_SDRB_SEL(reg) ((reg >>  2) & 0x01)
+#define GET_SDRF_SEL(reg) ((reg >>  1) & 0x01)
+#define GET_FASTBUS(reg)  (reg & 0x1)
+
+/* CFG_NEXT register */
+
+#define CFG_NEXT_CLOCKRECOVERY ((PMU->NEXT >> 18) & 0x7f)   /* Bits 24-18 */
+#define CFG_NEXT_INTRET        ((PMU->NEXT >> 17) & 0x01)
+#define CFG_NEXT_SDR_STOP      ((PMU->NEXT >>  6) & 0x01)
+#define CFG_NEXT_SYSCLKEN      ((PMU->NEXT >>  5) & 0x01)
+
+/* Useful field values that can be used to construct the
+ * CFG_NEXT and CFG_RUN registers.
+ */
+
+#define TRANSOP_NOP      0<<25  /* NOCHANGE_NOSTALL */
+#define NOCHANGE_STALL   1<<25
+#define CHANGE_NOSTALL   2<<25
+#define CHANGE_STALL     3<<25
+
+#define INTRET           1<<17
+#define OSCEN            1<<16
+#define OSCMUX           1<<15
+
+/* PLL frequencies */
+
+#define PLLMUL_0         0<<9         /*  3.6864 MHz */
+#define PLLMUL_1         1<<9         /*  ?????? MHz */
+#define PLLMUL_5         5<<9         /*  18.432 MHz */
+#define PLLMUL_10       10<<9         /*  36.864 MHz */
+#define PLLMUL_18       18<<9         /*  ?????? MHz */
+#define PLLMUL_20       20<<9         /*  73.728 MHz */
+#define PLLMUL_32       32<<9         /*  ?????? MHz */
+#define PLLMUL_35       35<<9         /* 129.024 MHz */
+#define PLLMUL_36       36<<9         /*  ?????? MHz */
+#define PLLMUL_39       39<<9         /*  ?????? MHz */
+#define PLLMUL_40       40<<9         /* 147.456 MHz */
+
+/* Clock recovery times */
+
+#define CRCLOCK_1        1<<18
+#define CRCLOCK_2        2<<18
+#define CRCLOCK_4        4<<18
+#define CRCLOCK_8        8<<18
+#define CRCLOCK_16      16<<18
+#define CRCLOCK_32      32<<18
+#define CRCLOCK_63      63<<18
+#define CRCLOCK_127    127<<18
+
+#define PLLEN            1<<8
+#define PLLMUX           1<<7
+#define SDR_STOP         1<<6
+#define SYSCLKEN         1<<5
+
+#define BCLK_DIV_4       2<<3
+#define BCLK_DIV_2       1<<3
+#define BCLK_DIV_1       0<<3
+
+#define SDRB_SEL         1<<2
+#define SDRF_SEL         1<<1
+#define FASTBUS          1<<0
+
+
+/* CFG_SDRAM */
+
+#define SDRREFFQ         1<<0  /* Only if SDRSTOPRQ is not set. */
+#define SDRREFACK        1<<1  /* Read-only */
+#define SDRSTOPRQ        1<<2  /* Only if SDRREFFQ is not set. */
+#define SDRSTOPACK       1<<3  /* Read-only */
+#define PICEN            1<<4  /* Enable Co-procesor */
+#define PICTEST          1<<5
+
+#define GET_SDRREFFQ    ((PMU->SDRAM >> 0) & 0x01)
+#define GET_SDRREFACK   ((PMU->SDRAM >> 1) & 0x01) /* Read-only */
+#define GET_SDRSTOPRQ   ((PMU->SDRAM >> 2) & 0x01)
+#define GET_SDRSTOPACK  ((PMU->SDRAM >> 3) & 0x01) /* Read-only */
+#define GET_PICEN       ((PMU->SDRAM >> 4) & 0x01)
+#define GET_PICTEST     ((PMU->SDRAM >> 5) & 0x01)
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/serial.h linux/include/asm-arm/arch-l7200/serial.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/serial.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/serial.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,39 @@
+/*
+ * linux/include/asm-arm/arch-l7200/serial.h
+ *
+ * Copyright (c) 2000 Rob Scott (rscott@mtrob.fdns.net)
+ *                    Steve Hill (sjhill@cotw.com)
+ *
+ * Changelog:
+ *  03-20-2000  SJH     Created
+ *  03-26-2000  SJH     Added flags for serial ports
+ *  03-27-2000  SJH     Corrected BASE_BAUD value
+ *  04-14-2000  RS      Made register addr dependent on IO_BASE
+ *  05-03-2000  SJH     Complete rewrite
+ *  05-09-2000	SJH	Stripped out architecture specific serial stuff
+ *                      and placed it in a separate file
+ *  07-28-2000	SJH	Moved base baud rate variable
+ */
+#ifndef __ASM_ARCH_SERIAL_H
+#define __ASM_ARCH_SERIAL_H
+
+/*
+ * This assumes you have a 3.6864 MHz clock for your UART.
+ */
+#define BASE_BAUD	3686400
+
+/*
+ * Standard COM flags
+ */
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+#define RS_TABLE_SIZE 2
+
+#define STD_SERIAL_PORT_DEFNS		\
+	/* MAGIC UART CLK   PORT       IRQ     FLAGS */			\
+	{ 0, BASE_BAUD, UART1_BASE, IRQ_UART_1, STD_COM_FLAGS },  /* ttyLU0 */ \
+	{ 0, BASE_BAUD, UART2_BASE, IRQ_UART_2, STD_COM_FLAGS },  /* ttyLU1 */ \
+
+#define EXTRA_SERIAL_PORT_DEFNS
+
+#endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/sib.h linux/include/asm-arm/arch-l7200/sib.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/sib.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/sib.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,119 @@
+/****************************************************************************/
+/*
+ *  linux/include/asm-arm/arch-l7200/sib.h
+ *
+ *  Registers and helper functions for the Serial Interface Bus.
+ *
+ *  (C) Copyright 2000, S A McConnell  (samcconn@cotw.com)
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/****************************************************************************/
+
+#define SIB_OFF   0x00040000  /* Offset from IO_START to the SIB reg's. */
+
+/* IO_START and IO_BASE are defined in hardware.h */
+
+#define SIB_START (IO_START + SIB_OFF) /* Physical addr of the SIB reg. */
+#define SIB_BASE  (IO_BASE  + SIB_OFF) /* Virtual addr of the SIB reg.  */
+
+/* Offsets from the start of the SIB for all the registers. */
+
+/* Define the SIB registers for use by device drivers and the kernel. */
+
+typedef struct
+{
+     unsigned int MCCR;    /* SIB Control Register           Offset: 0x00 */
+     unsigned int RES1;    /* Reserved                       Offset: 0x04 */
+     unsigned int MCDR0;   /* SIB Data Register 0            Offset: 0x08 */
+     unsigned int MCDR1;   /* SIB Data Register 1            Offset: 0x0c */
+     unsigned int MCDR2;   /* SIB Data Register 2 (UCB1x00)  Offset: 0x10 */
+     unsigned int RES2;    /* Reserved                       Offset: 0x14 */
+     unsigned int MCSR;    /* SIB Status Register            Offset: 0x18 */
+} SIB_Interface;
+
+#define SIB ((volatile SIB_Interface *) (SIB_BASE))
+
+/* MCCR */
+
+#define INTERNAL_FREQ   9216000  /* Hertz */
+#define AUDIO_FREQ         5000  /* Hertz */
+#define TELECOM_FREQ       5000  /* Hertz */
+
+#define AUDIO_DIVIDE    (INTERNAL_FREQ / (32 * AUDIO_FREQ))
+#define TELECOM_DIVIDE  (INTERNAL_FREQ / (32 * TELECOM_FREQ))
+
+#define MCCR_ASD57      AUDIO_DIVIDE
+#define MCCR_TSD57      (TELECOM_DIVIDE << 8)
+#define MCCR_MCE        (1 << 16)             /* SIB enable */
+#define MCCR_ECS        (1 << 17)             /* External Clock Select */
+#define MCCR_ADM        (1 << 18)             /* A/D Data Sampling */
+#define MCCR_PMC        (1 << 26)             /* PIN Multiplexer Control */
+
+
+#define GET_ASD ((SIB->MCCR >>  0) & 0x3f) /* Audio Sample Rate Div. */
+#define GET_TSD ((SIB->MCCR >>  8) & 0x3f) /* Telcom Sample Rate Div. */
+#define GET_MCE ((SIB->MCCR >> 16) & 0x01) /* SIB Enable */
+#define GET_ECS ((SIB->MCCR >> 17) & 0x01) /* External Clock Select */
+#define GET_ADM ((SIB->MCCR >> 18) & 0x01) /* A/D Data Sampling Mode */
+#define GET_TTM ((SIB->MCCR >> 19) & 0x01) /* Telco Trans. FIFO I mask */ 
+#define GET_TRM ((SIB->MCCR >> 20) & 0x01) /* Telco Recv. FIFO I mask */
+#define GET_ATM ((SIB->MCCR >> 21) & 0x01) /* Audio Trans. FIFO I mask */ 
+#define GET_ARM ((SIB->MCCR >> 22) & 0x01) /* Audio Recv. FIFO I mask */
+#define GET_LBM ((SIB->MCCR >> 23) & 0x01) /* Loop Back Mode */
+#define GET_ECP ((SIB->MCCR >> 24) & 0x03) /* Extern. Clck Prescale sel */
+#define GET_PMC ((SIB->MCCR >> 26) & 0x01) /* PIN Multiplexer Control */
+#define GET_ERI ((SIB->MCCR >> 27) & 0x01) /* External Read Interrupt */
+#define GET_EWI ((SIB->MCCR >> 28) & 0x01) /* External Write Interrupt */
+
+/* MCDR0 */
+
+#define AUDIO_RECV     ((SIB->MCDR0 >> 4) & 0xfff)
+#define AUDIO_WRITE(v) ((SIB->MCDR0 = (v & 0xfff) << 4))
+
+/* MCDR1 */
+
+#define TELECOM_RECV     ((SIB->MCDR1 >> 2) & 032fff)
+#define TELECOM_WRITE(v) ((SIB->MCDR1 = (v & 0x3fff) << 2))
+
+
+/* MCSR */
+
+#define MCSR_ATU (1 << 4)  /* Audio Transmit FIFO Underrun */
+#define MCSR_ARO (1 << 5)  /* Audio Receive  FIFO Underrun */
+#define MCSR_TTU (1 << 6)  /* TELECOM Transmit FIFO Underrun */
+#define MCSR_TRO (1 << 7)  /* TELECOM Receive  FIFO Underrun */
+
+#define MCSR_CLEAR_UNDERUN_BITS (MCSR_ATU | MCSR_ARO | MCSR_TTU | MCSR_TRO)
+
+
+#define GET_ATS ((SIB->MCSR >>  0) & 0x01) /* Audio Transmit FIFO Service Req*/
+#define GET_ARS ((SIB->MCSR >>  1) & 0x01) /* Audio Recv FIFO Service Request*/
+#define GET_TTS ((SIB->MCSR >>  2) & 0x01) /* TELECOM Transmit FIFO  Flag */
+#define GET_TRS ((SIB->MCSR >>  3) & 0x01) /* TELECOM Recv FIFO Service Req. */
+#define GET_ATU ((SIB->MCSR >>  4) & 0x01) /* Audio Transmit FIFO Underrun */
+#define GET_ARO ((SIB->MCSR >>  5) & 0x01) /* Audio Receive  FIFO Underrun */
+#define GET_TTU ((SIB->MCSR >>  6) & 0x01) /* TELECOM Transmit FIFO Underrun */
+#define GET_TRO ((SIB->MCSR >>  7) & 0x01) /* TELECOM Receive  FIFO Underrun */
+#define GET_ANF ((SIB->MCSR >>  8) & 0x01) /* Audio Transmit FIFO not full */
+#define GET_ANE ((SIB->MCSR >>  9) & 0x01) /* Audio Receive FIFO not empty */
+#define GET_TNF ((SIB->MCSR >> 10) & 0x01) /* Telecom Transmit FIFO not full */
+#define GET_TNE ((SIB->MCSR >> 11) & 0x01) /* Telecom Receive FIFO not empty */
+#define GET_CWC ((SIB->MCSR >> 12) & 0x01) /* Codec Write Complete */
+#define GET_CRC ((SIB->MCSR >> 13) & 0x01) /* Codec Read Complete */
+#define GET_ACE ((SIB->MCSR >> 14) & 0x01) /* Audio Codec Enabled */
+#define GET_TCE ((SIB->MCSR >> 15) & 0x01) /* Telecom Codec Enabled */
+
+/* MCDR2 */
+
+#define MCDR2_rW               (1 << 16)
+
+#define WRITE_MCDR2(reg, data) (SIB->MCDR2 =((reg<<17)|MCDR2_rW|(data&0xffff)))
+#define MCDR2_WRITE_COMPLETE   GET_CWC
+
+#define INITIATE_MCDR2_READ(reg) (SIB->MCDR2 = (reg << 17))
+#define MCDR2_READ_COMPLETE      GET_CRC
+#define MCDR2_READ               (SIB->MCDR2 & 0xffff)
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/sys-clock.h linux/include/asm-arm/arch-l7200/sys-clock.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/sys-clock.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-l7200/sys-clock.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,67 @@
+/****************************************************************************/
+/*
+ *  linux/include/asm-arm/arch-l7200/sys-clock.h
+ *
+ *   Registers and  helper functions for the L7200 Link-Up Systems
+ *   System clocks.
+ *
+ *   (C) Copyright 2000, S A McConnell  (samcconn@cotw.com)
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/****************************************************************************/
+
+#define SYS_CLOCK_OFF   0x00050030  /* Offset from IO_START. */
+
+/* IO_START and IO_BASE are defined in hardware.h */
+
+#define SYS_CLOCK_START (IO_START + SYS_CLCOK_OFF)  /* Physical address */
+#define SYS_CLOCK_BASE  (IO_BASE  + SYS_CLOCK_OFF)  /* Virtual address  */
+
+/* Define the interface to the SYS_CLOCK */
+
+typedef struct
+{
+     unsigned int ENABLE;
+     unsigned int ESYNC;
+     unsigned int SELECT;
+} sys_clock_interface;
+
+#define SYS_CLOCK   ((volatile sys_clock_interface *)(SYS_CLOCK_BASE))
+
+//#define CLOCK_EN    (*(volatile unsigned long *)(PMU_BASE+CLOCK_EN_OFF))
+//#define CLOCK_ESYNC (*(volatile unsigned long *)(PMU_BASE+CLOCK_ESYNC_OFF))
+//#define CLOCK_SEL   (*(volatile unsigned long *)(PMU_BASE+CLOCK_SEL_OFF))
+
+/* SYS_CLOCK -> ENABLE */
+
+#define SYN_EN          1<<0
+#define B18M_EN         1<<1
+#define CLK3M6_EN       1<<2
+#define BUART_EN        1<<3
+#define CLK18MU_EN      1<<4
+#define FIR_EN          1<<5
+#define MIRN_EN         1<<6
+#define UARTM_EN        1<<7
+#define SIBADC_EN       1<<8
+#define ALTD_EN         1<<9
+#define CLCLK_EN        1<<10
+
+/* SYS_CLOCK -> SELECT */
+
+#define CLK18M_DIV      1<<0
+#define MIR_SEL         1<<1
+#define SSP_SEL         1<<4
+#define MM_DIV          1<<5
+#define MM_SEL          1<<6
+#define ADC_SEL_2       0<<7
+#define ADC_SEL_4       1<<7
+#define ADC_SEL_8       3<<7
+#define ADC_SEL_16      7<<7
+#define ADC_SEL_32      0x0f<<7
+#define ADC_SEL_64      0x1f<<7
+#define ADC_SEL_128     0x3f<<7
+#define ALTD_SEL        1<<13
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/system.h linux/include/asm-arm/arch-l7200/system.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/system.h	Sun Aug 13 09:54:15 2000
+++ linux/include/asm-arm/arch-l7200/system.h	Mon Dec  3 22:34:51 2001
@@ -14,9 +14,8 @@
 
 static void arch_idle(void)
 {
-        while (!current->need_resched && !hlt_counter) {
-		cpu_do_idle(IDLE_WAIT_SLOW);
-	}
+	/* fixme: this needs to be cleaned up (converted from ASM code) --rmk */
+	*(unsigned long *)(IO_BASE + 0x50004) = 1;	/* idle mode */
 }
 
 extern inline void arch_reset(char mode)
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-l7200/time.h linux/include/asm-arm/arch-l7200/time.h
--- linux-2.4.4-pre2/include/asm-arm/arch-l7200/time.h	Sun Aug 13 09:54:15 2000
+++ linux/include/asm-arm/arch-l7200/time.h	Mon Dec  3 22:34:51 2001
@@ -40,7 +40,7 @@
 #define RTC_EN_STWDOG	0x08      /* Enable watchdog */
 
 /*
- * Handler for timer interrupt
+ * Handler for RTC timer interrupt
  */
 static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -50,7 +50,7 @@
 }
 
 /*
- * Set up timer interrupt, and return the current time in seconds.
+ * Set up RTC timer interrupt, and return the current time in seconds.
  */
 extern __inline__ void setup_timer(void)
 {
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-rpc/hardware.h linux/include/asm-arm/arch-rpc/hardware.h
--- linux-2.4.4-pre2/include/asm-arm/arch-rpc/hardware.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-rpc/hardware.h	Mon Dec  3 22:34:51 2001
@@ -43,66 +43,27 @@
 #define FLUSH_BASE		0xdf000000
 #define UNCACHEABLE_ADDR	0xdf010000
 
-
-#ifndef __ASSEMBLY__
-
-/*
- * for use with inb/outb
- */
-#define IO_VIDC_AUDIO_BASE	0x80140000
-#define IO_VIDC_BASE		0x80100000
-#define IO_IOMD_BASE		0x80080000
-#define IOC_BASE		0x80080000
-
-#define IO_EC_EASI_BASE		0x81400000
-#define IO_EC_IOC4_BASE		0x8009c000
-#define IO_EC_IOC_BASE		0x80090000
-#define IO_EC_MEMC8_BASE	0x8000ac00
-#define IO_EC_MEMC_BASE		0x80000000
-
-/*
- * IO definitions
- */
-#define EXPMASK_BASE		((volatile unsigned char *)0xe0360000)
-#define IOEB_BASE		((volatile unsigned char *)0xe0350050)
-#define PCIO_FLOPPYDMABASE	((volatile unsigned char *)0xe002a000)
-#define PCIO_BASE		0xe0010000
-
 /*
- * Offsets from RAM base
+ * IO Addresses
  */
-#define PARAMS_OFFSET		0x0100
-
-/*
- * RAM definitions
- */
-#define GET_MEMORY_END(p)	(PAGE_OFFSET + p->u1.s.page_size * \
-						(p->u1.s.pages_in_bank[0] + \
-						 p->u1.s.pages_in_bank[1] + \
-						 p->u1.s.pages_in_bank[2] + \
-						 p->u1.s.pages_in_bank[3]))
-
-#define Z_PARAMS_BASE		(RAM_START + PARAMS_OFFSET)
-#define FLUSH_BASE_PHYS		0x00000000	/* ROM */
-
-#else
-
-#define VIDC_SND_BASE		0xe0500000
 #define VIDC_BASE		0xe0400000
+#define EXPMASK_BASE		0xe0360000
 #define IOMD_BASE		0xe0200000
 #define IOC_BASE		0xe0200000
-#define PCIO_FLOPPYDMABASE	0xe002a000
 #define PCIO_BASE		0xe0010000
+#define FLOPPYDMA_BASE		0xe002a000
 
-#endif
+#define FLUSH_BASE_PHYS		0x00000000	/* ROM */
+
+#define vidc_writel(val)	__raw_writel(val, VIDC_BASE)
 
-#ifndef __ASSEMBLY__
-#define __EXPMASK(offset)	(((volatile unsigned char *)EXPMASK_BASE)[offset])
-#else
-#define __EXPMASK(offset)	offset
-#endif
+#define IO_EC_EASI_BASE		0x81400000
+#define IO_EC_IOC4_BASE		0x8009c000
+#define IO_EC_IOC_BASE		0x80090000
+#define IO_EC_MEMC8_BASE	0x8000ac00
+#define IO_EC_MEMC_BASE		0x80000000
 
-#define	EXPMASK_STATUS	__EXPMASK(0x00)
-#define EXPMASK_ENABLE	__EXPMASK(0x04)
+#define	EXPMASK_STATUS		(EXPMASK_BASE + 0x00)
+#define EXPMASK_ENABLE		(EXPMASK_BASE + 0x04)
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-rpc/irq.h linux/include/asm-arm/arch-rpc/irq.h
--- linux-2.4.4-pre2/include/asm-arm/arch-rpc/irq.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-rpc/irq.h	Mon Dec  3 22:34:51 2001
@@ -12,129 +12,100 @@
  *   22-08-1998	RMK	Restructured IRQ routines
  */
 #include <asm/hardware/iomd.h>
+#include <asm/io.h>
 
 #define fixup_irq(x) (x)
 
 static void rpc_mask_irq_ack_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]\n"
-"	strb	%1, [%3]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKA)),
-	  "r" (ioaddr(IOMD_IRQCLRA)));
+	mask = 1 << irq;
+	val = iomd_readb(IOMD_IRQMASKA);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKA);
+	iomd_writeb(mask, IOMD_IRQCLRA);
 }
 
 static void rpc_mask_irq_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKA)));
+	mask = 1 << irq;
+	val = iomd_readb(IOMD_IRQMASKA);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKA);
 }
 
 static void rpc_unmask_irq_a(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKA)));
+	mask = 1 << irq;
+	val = iomd_readb(IOMD_IRQMASKA);
+	iomd_writeb(val | mask, IOMD_IRQMASKA);
 }
 
 static void rpc_mask_irq_b(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKB)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKB);
+	iomd_writeb(val & ~mask, IOMD_IRQMASKB);
 }
 
 static void rpc_unmask_irq_b(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_IRQMASKB)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_IRQMASKB);
+	iomd_writeb(val | mask, IOMD_IRQMASKB);
 }
 
 static void rpc_mask_irq_dma(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_DMAMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_DMAMASK);
+	iomd_writeb(val & ~mask, IOMD_DMAMASK);
 }
 
 static void rpc_unmask_irq_dma(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_DMAMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_DMAMASK);
+	iomd_writeb(val | mask, IOMD_DMAMASK);
 }
 
 static void rpc_mask_irq_fiq(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	bic	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_FIQMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_FIQMASK);
+	iomd_writeb(val & ~mask, IOMD_FIQMASK);
 }
 
 static void rpc_unmask_irq_fiq(unsigned int irq)
 {
-	unsigned int temp;
+	unsigned int val, mask;
 
-	__asm__ __volatile__(
-	"ldrb	%0, [%2]\n"
-"	orr	%0, %0, %1\n"
-"	strb	%0, [%2]"
-	: "=&r" (temp)
-	: "r" (1 << (irq & 7)), "r" (ioaddr(IOMD_FIQMASK)));
+	mask = 1 << (irq & 7);
+	val = iomd_readb(IOMD_FIQMASK);
+	iomd_writeb(val | mask, IOMD_FIQMASK);
 }
 
 static __inline__ void irq_init_irq(void)
 {
-	extern void ecard_disableirq(unsigned int irq);
-	extern void ecard_enableirq(unsigned int irq);
 	int irq;
 
-	outb(0, IOMD_IRQMASKA);
-	outb(0, IOMD_IRQMASKB);
-	outb(0, IOMD_FIQMASK);
-	outb(0, IOMD_DMAMASK);
+	iomd_writeb(0, IOMD_IRQMASKA);
+	iomd_writeb(0, IOMD_IRQMASKB);
+	iomd_writeb(0, IOMD_FIQMASK);
+	iomd_writeb(0, IOMD_DMAMASK);
 
 	for (irq = 0; irq < NR_IRQS; irq++) {
 		switch (irq) {
@@ -156,19 +127,14 @@
 			irq_desc[irq].unmask   = rpc_unmask_irq_b;
 			break;
 
-		case 16 ... 21:
-			irq_desc[irq].valid    = 1;
+		case 16 ... 19:
+		case 21:
 			irq_desc[irq].noautoenable = 1;
+		case 20:
+			irq_desc[irq].valid    = 1;
 			irq_desc[irq].mask_ack = rpc_mask_irq_dma;
 			irq_desc[irq].mask     = rpc_mask_irq_dma;
 			irq_desc[irq].unmask   = rpc_unmask_irq_dma;
-			break;
-
-		case 32 ... 39:
-			irq_desc[irq].valid    = 1;
-			irq_desc[irq].mask_ack = ecard_disableirq;
-			irq_desc[irq].mask     = ecard_disableirq;
-			irq_desc[irq].unmask   = ecard_enableirq;
 			break;
 
 		case 64 ... 71:
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-rpc/system.h linux/include/asm-arm/arch-rpc/system.h
--- linux-2.4.4-pre2/include/asm-arm/arch-rpc/system.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-rpc/system.h	Mon Dec  3 22:34:51 2001
@@ -35,11 +35,7 @@
 
 extern __inline__ void arch_reset(char mode)
 {
-	extern void ecard_reset(int card);
-
-	ecard_reset(-1);
-
-	outb(0, IOMD_ROMCR0);
+	iomd_writeb(0, IOMD_ROMCR0);
 
 	/*
 	 * Jump into the ROM
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/SA-1100.h linux/include/asm-arm/arch-sa1100/SA-1100.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/SA-1100.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-sa1100/SA-1100.h	Mon Dec  3 22:34:51 2001
@@ -271,6 +271,7 @@
 
 #define UDCCR_UDD	0x00000001	/* UDC Disable                     */
 #define UDCCR_UDA	0x00000002	/* UDC Active (read)               */
+#define UDCCR_RESIM	0x00000004	/* Resume Interrupt Mask, per errata */
 #define UDCCR_EIM	0x00000008	/* End-point 0 Interrupt Mask      */
                 	        	/* (disable)                       */
 #define UDCCR_RIM	0x00000010	/* Receive Interrupt Mask          */
@@ -279,6 +280,7 @@
                 	        	/* (disable)                       */
 #define UDCCR_SRM	0x00000040	/* Suspend/Resume interrupt Mask   */
                 	        	/* (disable)                       */
+#define UDCCR_SUSIM	UDCCR_SRM	/* Per errata, SRM just masks suspend */
 #define UDCCR_REM	0x00000080	/* REset interrupt Mask (disable)  */
 
 #define UDCAR_ADD	Fld (7, 0)	/* function ADDress                */
@@ -502,6 +504,32 @@
 #define Ser3UTSR1	        	/* Ser. port 3 UART Status Reg. 1  */ \
                 	(*((volatile Word *) io_p2v (_Ser3UTSR1)))
 
+#elif LANGUAGE == Assembly
+#define Ser1UTCR0	( io_p2v (_Ser1UTCR0))
+#define Ser1UTCR1	( io_p2v (_Ser1UTCR1))
+#define Ser1UTCR2	( io_p2v (_Ser1UTCR2))
+#define Ser1UTCR3	( io_p2v (_Ser1UTCR3))
+#define Ser1UTDR	( io_p2v (_Ser1UTDR))
+#define Ser1UTSR0	( io_p2v (_Ser1UTSR0))
+#define Ser1UTSR1	( io_p2v (_Ser1UTSR1))
+
+#define Ser2UTCR0	( io_p2v (_Ser2UTCR0))
+#define Ser2UTCR1	( io_p2v (_Ser2UTCR1))
+#define Ser2UTCR2	( io_p2v (_Ser2UTCR2))
+#define Ser2UTCR3	( io_p2v (_Ser2UTCR3))
+#define Ser2UTCR4	( io_p2v (_Ser2UTCR4))
+#define Ser2UTDR	( io_p2v (_Ser2UTDR))
+#define Ser2UTSR0	( io_p2v (_Ser2UTSR0))
+#define Ser2UTSR1	( io_p2v (_Ser2UTSR1))
+
+#define Ser3UTCR0	( io_p2v (_Ser3UTCR0))
+#define Ser3UTCR1	( io_p2v (_Ser3UTCR1))
+#define Ser3UTCR2	( io_p2v (_Ser3UTCR2))
+#define Ser3UTCR3	( io_p2v (_Ser3UTCR3))
+#define Ser3UTDR	( io_p2v (_Ser3UTDR))
+#define Ser3UTSR0	( io_p2v (_Ser3UTSR0))
+#define Ser3UTSR1	( io_p2v (_Ser3UTSR1))
+
 #endif /* LANGUAGE == C */
 
 #define UTCR0_PE	0x00000001	/* Parity Enable                   */
@@ -1229,6 +1257,17 @@
                 	(*((volatile Word *) io_p2v (_PGSR)))
 #define POSR    	        	/* PM Oscillator Status Reg.       */ \
                 	(*((volatile Word *) io_p2v (_POSR)))
+
+#elif LANGUAGE == Assembly
+#define PMCR          	(io_p2v (_PMCR))
+#define PSSR          	(io_p2v (_PSSR))
+#define PSPR          	(io_p2v (_PSPR))
+#define PWER          	(io_p2v (_PWER))
+#define PCFR          	(io_p2v (_PCFR))
+#define PPCR          	(io_p2v (_PPCR))
+#define PGSR          	(io_p2v (_PGSR))
+#define POSR          	(io_p2v (_POSR))
+
 #endif /* LANGUAGE == C */
 
 #define PMCR_SF 	0x00000001	/* Sleep Force (set only)          */
@@ -1481,6 +1520,17 @@
                 	(*((volatile Word *) io_p2v (_GEDR)))
 #define GAFR    	        	/* GPIO Alternate Function Reg.    */ \
                 	(*((volatile Word *) io_p2v (_GAFR)))
+#elif LANGUAGE == Assembly
+
+#define GPLR  (io_p2v (_GPLR))
+#define GPDR  (io_p2v (_GPDR))
+#define GPSR  (io_p2v (_GPSR))
+#define GPCR  (io_p2v (_GPCR))
+#define GRER  (io_p2v (_GRER))
+#define GFER  (io_p2v (_GFER))
+#define GEDR  (io_p2v (_GEDR))
+#define GAFR  (io_p2v (_GAFR))
+
 #endif /* LANGUAGE == C */
 
 #define GPIO_MIN	(0)
@@ -1784,6 +1834,11 @@
 #define MDCAS0  	(MDCAS [0])	/*  DRAM CAS shift reg. 0          */
 #define MDCAS1  	(MDCAS [1])	/*  DRAM CAS shift reg. 1          */
 #define MDCAS2  	(MDCAS [2])	/*  DRAM CAS shift reg. 2          */
+
+#elif LANGUAGE == Assembly
+
+#define MDCNFG		(io_p2v(_MDCNFG))
+
 #endif /* LANGUAGE == C */
 
 /* SA1100 MDCNFG values */
@@ -1870,6 +1925,13 @@
                 	((volatile Word *) io_p2v (_MSC (0)))
 #define MSC0    	(MSC [0])	/*  Static memory Control reg. 0   */
 #define MSC1    	(MSC [1])	/*  Static memory Control reg. 1   */
+
+#elif LANGUAGE == Assembly
+
+#define MSC0		io_p2v(0xa0000010)
+#define MSC1		io_p2v(0xa0000014)
+#define MSC2		io_p2v(0xa000002c)
+
 #endif /* LANGUAGE == C */
 
 #define MSC_Bnk(Nb)	        	/* static memory Bank [0..3]       */ \
@@ -1895,32 +1957,32 @@
                 	        	/* First access - 1(.5) [Tmem]     */
 #define MSC_1stRdAcc(Tcpu)      	/*  1st Read Access time (burst    */ \
                 	        	/*  static memory) [3..65 Tcpu]    */ \
-                	(((Tcpu) - 3)/2 << FShft (MSC_RDF))
+                	((((Tcpu) - 3)/2) << FShft (MSC_RDF))
 #define MSC_Ceil1stRdAcc(Tcpu)  	/*  Ceil. of 1stRdAcc [3..65 Tcpu] */ \
-                	(((Tcpu) - 2)/2 << FShft (MSC_RDF))
+                	((((Tcpu) - 2)/2) << FShft (MSC_RDF))
 #define MSC_RdAcc(Tcpu)	        	/*  Read Access time (non-burst    */ \
                 	        	/*  static memory) [2..64 Tcpu]    */ \
-                	(((Tcpu) - 2)/2 << FShft (MSC_RDF))
+                	((((Tcpu) - 2)/2) << FShft (MSC_RDF))
 #define MSC_CeilRdAcc(Tcpu)     	/*  Ceil. of RdAcc [2..64 Tcpu]    */ \
-                	(((Tcpu) - 1)/2 << FShft (MSC_RDF))
+                	((((Tcpu) - 1)/2) << FShft (MSC_RDF))
 #define MSC_RDN 	Fld (5, 8)	/* ROM/static memory read Delay    */
                 	        	/* Next access - 1 [Tmem]          */
 #define MSC_NxtRdAcc(Tcpu)      	/*  Next Read Access time (burst   */ \
                 	        	/*  static memory) [2..64 Tcpu]    */ \
-                	(((Tcpu) - 2)/2 << FShft (MSC_RDN))
+                	((((Tcpu) - 2)/2) << FShft (MSC_RDN))
 #define MSC_CeilNxtRdAcc(Tcpu)  	/*  Ceil. of NxtRdAcc [2..64 Tcpu] */ \
-                	(((Tcpu) - 1)/2 << FShft (MSC_RDN))
+                	((((Tcpu) - 1)/2) << FShft (MSC_RDN))
 #define MSC_WrAcc(Tcpu)	        	/*  Write Access time (non-burst   */ \
                 	        	/*  static memory) [2..64 Tcpu]    */ \
-                	(((Tcpu) - 2)/2 << FShft (MSC_RDN))
+                	((((Tcpu) - 2)/2) << FShft (MSC_RDN))
 #define MSC_CeilWrAcc(Tcpu)     	/*  Ceil. of WrAcc [2..64 Tcpu]    */ \
-                	(((Tcpu) - 1)/2 << FShft (MSC_RDN))
+                	((((Tcpu) - 1)/2) << FShft (MSC_RDN))
 #define MSC_RRR 	Fld (3, 13)	/* ROM/static memory RecoveRy      */
                 	        	/* time/2 [Tmem]                   */
 #define MSC_Rec(Tcpu)	        	/*  Recovery time [0..28 Tcpu]     */ \
-                	((Tcpu)/4 << FShft (MSC_RRR))
+                	(((Tcpu)/4) << FShft (MSC_RRR))
 #define MSC_CeilRec(Tcpu)       	/*  Ceil. of Rec [0..28 Tcpu]      */ \
-                	(((Tcpu) + 3)/4 << FShft (MSC_RRR))
+                	((((Tcpu) + 3)/4) << FShft (MSC_RRR))
 
 
 /*
@@ -1955,20 +2017,20 @@
 
 #define MECR_BSIO	Fld (5, 0)	/* BCLK Select I/O - 1 [Tmem]      */
 #define MECR_IOClk(Tcpu)        	/*  I/O Clock [2..64 Tcpu]         */ \
-                	(((Tcpu) - 2)/2 << FShft (MECR_BSIO))
+                	((((Tcpu) - 2)/2) << FShft (MECR_BSIO))
 #define MECR_CeilIOClk(Tcpu)    	/*  Ceil. of IOClk [2..64 Tcpu]    */ \
-                	(((Tcpu) - 1)/2 << FShft (MECR_BSIO))
+                	((((Tcpu) - 1)/2) << FShft (MECR_BSIO))
 #define MECR_BSA	Fld (5, 5)	/* BCLK Select Attribute - 1       */
                 	        	/* [Tmem]                          */
 #define MECR_AttrClk(Tcpu)      	/*  Attribute Clock [2..64 Tcpu]   */ \
-                	(((Tcpu) - 2)/2 << FShft (MECR_BSA))
+                	((((Tcpu) - 2)/2) << FShft (MECR_BSA))
 #define MECR_CeilAttrClk(Tcpu)  	/*  Ceil. of AttrClk [2..64 Tcpu]  */ \
-                	(((Tcpu) - 1)/2 << FShft (MECR_BSA))
+                	((((Tcpu) - 1)/2) << FShft (MECR_BSA))
 #define MECR_BSM	Fld (5, 10)	/* BCLK Select Memory - 1 [Tmem]   */
 #define MECR_MemClk(Tcpu)       	/*  Memory Clock [2..64 Tcpu]      */ \
-                	(((Tcpu) - 2)/2 << FShft (MECR_BSM))
+                	((((Tcpu) - 2)/2) << FShft (MECR_BSM))
 #define MECR_CeilMemClk(Tcpu)   	/*  Ceil. of MemClk [2..64 Tcpu]   */ \
-                	(((Tcpu) - 1)/2 << FShft (MECR_BSM))
+                	((((Tcpu) - 1)/2) << FShft (MECR_BSM))
 
 /*
  * On SA1110 only
@@ -1981,6 +2043,10 @@
 #define MDREFR \
                 	(*((volatile Word *) io_p2v (_MDREFR)))
 
+#elif LANGUAGE == Assembly
+
+#define MDREFR		(io_p2v(_MDREFR))
+
 #endif /* LANGUAGE == C */
 
 #define MDREFR_TRASR		Fld (4, 0)
@@ -2456,18 +2522,18 @@
                 	(DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev + \
                 	 DDAR_Ser3UARTRc + DDAR_DevAdd (_Ser3UTDR))
 #define DDAR_Ser4MCP0Wr	        	/* Ser. port 4 MCP 0 Write (audio) */ \
-                	(DDAR_DevWr + DDAR_Brst8 + DDAR_16BitDev + \
+                	(DDAR_DevWr + DDAR_Brst4 + DDAR_16BitDev + \
                 	 DDAR_Ser4MCP0Tr + DDAR_DevAdd (_Ser4MCDR0))
 #define DDAR_Ser4MCP0Rd	        	/* Ser. port 4 MCP 0 Read (audio)  */ \
-                	(DDAR_DevRd + DDAR_Brst8 + DDAR_16BitDev + \
+                	(DDAR_DevRd + DDAR_Brst4 + DDAR_16BitDev + \
                 	 DDAR_Ser4MCP0Rc + DDAR_DevAdd (_Ser4MCDR0))
 #define DDAR_Ser4MCP1Wr	        	/* Ser. port 4 MCP 1 Write         */ \
                 	        	/* (telecom)                       */ \
-                	(DDAR_DevWr + DDAR_Brst8 + DDAR_16BitDev + \
+                	(DDAR_DevWr + DDAR_Brst4 + DDAR_16BitDev + \
                 	 DDAR_Ser4MCP1Tr + DDAR_DevAdd (_Ser4MCDR1))
 #define DDAR_Ser4MCP1Rd	        	/* Ser. port 4 MCP 1 Read          */ \
                 	        	/* (telecom)                       */ \
-                	(DDAR_DevRd + DDAR_Brst8 + DDAR_16BitDev + \
+                	(DDAR_DevRd + DDAR_Brst4 + DDAR_16BitDev + \
                 	 DDAR_Ser4MCP1Rc + DDAR_DevAdd (_Ser4MCDR1))
 #define DDAR_Ser4SSPWr	        	/* Ser. port 4 SSP Write (16 bits) */ \
                 	(DDAR_DevWr + DDAR_Brst4 + DDAR_16BitDev + \
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/assabet.h linux/include/asm-arm/arch-sa1100/assabet.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/assabet.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-sa1100/assabet.h	Mon Dec  3 22:34:51 2001
@@ -32,7 +32,7 @@
 #define BCR (*(volatile unsigned int *)(BCR_BASE))
 
 #define BCR_DB1110	(0x00A07410)
-#define BCR_DB1111	(0x00A07462)
+#define BCR_DB1111	(0x00A074E2)
 
 #define BCR_CF_PWR	(1<<0)	/* Compact Flash Power (1 = 3.3v, 0 = off) */
 #define BCR_CF_RST	(1<<1)	/* Compact Flash Reset (1 = power up reset) */
@@ -89,6 +89,7 @@
 
 #define IRQ_GPIO_CF_IRQ		IRQ_GPIO21
 #define IRQ_GPIO_CF_CD		IRQ_GPIO22
+#define IRQ_GPIO_MBREQ		IRQ_GPIO22
 #define IRQ_GPIO_UCB1300_IRQ	IRQ_GPIO23
 #define IRQ_GPIO_CF_BVD2	IRQ_GPIO24
 #define IRQ_GPIO_CF_BVD1	IRQ_GPIO25
@@ -139,6 +140,9 @@
 #define IRR_ETHERNET		(1<<0)
 #define IRR_USAR		(1<<1)
 #define IRR_SA1111		(1<<2)
+
+#define AUD_SEL_1341            (1<<0)
+#define AUD_MUTE_1341           (1<<1)
 
 #define NCR_GP01_OFF		(1<<0)
 #define NCR_TP_PWR_EN		(1<<1)
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/bitsy.h linux/include/asm-arm/arch-sa1100/bitsy.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/bitsy.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-sa1100/bitsy.h	Mon Dec  3 22:34:51 2001
@@ -1,6 +1,9 @@
 #ifndef _INCLUDE_BITSY_H_
 #define _INCLUDE_BITSY_H_
 
+#define GPIO_BITSY_NPOWER_BUTTON    GPIO_GPIO (0)
+#define GPIO_BITSY_ACTION_BUTTON    GPIO_GPIO (18)
+
 #define GPIO_BITSY_PCMCIA_CD0  GPIO_GPIO (17)
 #define GPIO_BITSY_PCMCIA_CD1  GPIO_GPIO (10)
 #define GPIO_BITSY_PCMCIA_IRQ0 GPIO_GPIO (21)
@@ -15,6 +18,9 @@
 #define GPIO_BITSY_L3_MODE     GPIO_GPIO (15)
 
 
+
+#define IRQ_GPIO_BITSY_NPOWER_BUTTON    IRQ_GPIO0
+#define IRQ_GPIO_BITSY_ACTION_BUTTON    IRQ_GPIO18
 #define IRQ_GPIO_BITSY_PCMCIA_CD0	IRQ_GPIO17
 #define IRQ_GPIO_BITSY_PCMCIA_CD1	IRQ_GPIO10
 #define IRQ_GPIO_BITSY_PCMCIA_IRQ0	IRQ_GPIO21
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/dma.h linux/include/asm-arm/arch-sa1100/dma.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/dma.h	Tue Jul 18 22:43:25 2000
+++ linux/include/asm-arm/arch-sa1100/dma.h	Mon Dec  3 22:34:51 2001
@@ -1,12 +1,107 @@
 /*
  * linux/include/asm-arm/arch-sa1100/dma.h
+ *
+ * Generic SA1100 DMA support
+ *
+ * Copyright (C) 2000 Nicolas Pitre
+ *
  */
+
 #ifndef __ASM_ARCH_DMA_H
 #define __ASM_ARCH_DMA_H
 
+#include <linux/config.h>
+#include "hardware.h"
+
+
+/*
+ * This is the maximum DMA address that can be DMAd to.
+ */
 #define MAX_DMA_ADDRESS		0xffffffff
 
-/* No DMA (strictly, a lie :-) ) */
+
+/*
+ * The regular generic DMA interface is inappropriate for the
+ * SA1100 DMA model.  None of the SA1100 specific drivers using
+ * DMA are portable anyway so it's pointless to try to twist the
+ * regular DMA API to accommodate them.
+ */
 #define MAX_DMA_CHANNELS	0
+
+
+/*
+ * The SA1100 has six internal DMA channels.
+ */
+#define SA1100_DMA_CHANNELS     6
+
+
+/*
+ * The SA-1111 SAC has two DMA channels.
+ */
+#define SA1111_SAC_DMA_CHANNELS 2
+#define SA1111_SAC_XMT_CHANNEL  0
+#define SA1111_SAC_RCV_CHANNEL  1
+
+
+/*
+ * The SA-1111 SAC channels will reside in the same index space as
+ * the built-in SA-1100 channels, and will take on the next available
+ * identifiers after the 1100.
+ */
+#define SA1111_SAC_DMA_BASE     SA1100_DMA_CHANNELS
+
+#ifdef CONFIG_SA1111
+# define MAX_SA1100_DMA_CHANNELS (SA1100_DMA_CHANNELS + SA1111_SAC_DMA_CHANNELS)
+#else
+# define MAX_SA1100_DMA_CHANNELS SA1100_DMA_CHANNELS
+#endif
+
+
+/*
+ * All possible SA1100 devices a DMA channel can be attached to.
+ */
+typedef enum {
+	DMA_Ser0UDCWr  = DDAR_Ser0UDCWr,   /* Ser. port 0 UDC Write */
+	DMA_Ser0UDCRd  = DDAR_Ser0UDCRd,   /* Ser. port 0 UDC Read */
+	DMA_Ser1UARTWr = DDAR_Ser1UARTWr,  /* Ser. port 1 UART Write */
+	DMA_Ser1UARTRd = DDAR_Ser1UARTRd,  /* Ser. port 1 UART Read */
+	DMA_Ser1SDLCWr = DDAR_Ser1SDLCWr,  /* Ser. port 1 SDLC Write */
+	DMA_Ser1SDLCRd = DDAR_Ser1SDLCRd,  /* Ser. port 1 SDLC Read */
+	DMA_Ser2UARTWr = DDAR_Ser2UARTWr,  /* Ser. port 2 UART Write */
+	DMA_Ser2UARTRd = DDAR_Ser2UARTRd,  /* Ser. port 2 UART Read */
+	DMA_Ser2HSSPWr = DDAR_Ser2HSSPWr,  /* Ser. port 2 HSSP Write */
+	DMA_Ser2HSSPRd = DDAR_Ser2HSSPRd,  /* Ser. port 2 HSSP Read */
+	DMA_Ser3UARTWr = DDAR_Ser3UARTWr,  /* Ser. port 3 UART Write */
+	DMA_Ser3UARTRd = DDAR_Ser3UARTRd,  /* Ser. port 3 UART Read */
+	DMA_Ser4MCP0Wr = DDAR_Ser4MCP0Wr,  /* Ser. port 4 MCP 0 Write (audio) */
+	DMA_Ser4MCP0Rd = DDAR_Ser4MCP0Rd,  /* Ser. port 4 MCP 0 Read (audio) */
+	DMA_Ser4MCP1Wr = DDAR_Ser4MCP1Wr,  /* Ser. port 4 MCP 1 Write */
+	DMA_Ser4MCP1Rd = DDAR_Ser4MCP1Rd,  /* Ser. port 4 MCP 1 Read */
+	DMA_Ser4SSPWr  = DDAR_Ser4SSPWr,   /* Ser. port 4 SSP Write (16 bits) */
+	DMA_Ser4SSPRd  = DDAR_Ser4SSPRd    /* Ser. port 4 SSP Read (16 bits) */
+} dma_device_t;
+
+
+typedef void (*dma_callback_t)( void *buf_id, int size );
+
+
+/* SA1100 DMA API */
+extern int sa1100_request_dma( dmach_t *channel, const char *device_id );
+extern int sa1100_dma_set_callback( dmach_t channel, dma_callback_t cb );
+extern int sa1100_dma_set_device( dmach_t channel, dma_device_t device );
+extern int sa1100_dma_set_spin( dmach_t channel, dma_addr_t addr, int size );
+extern int sa1100_dma_queue_buffer( dmach_t channel, void *buf_id,
+				    dma_addr_t data, int size );
+extern int sa1100_dma_get_current( dmach_t channel, void **buf_id, dma_addr_t *addr );
+extern int sa1100_dma_stop( dmach_t channel );
+extern int sa1100_dma_resume( dmach_t channel );
+extern int sa1100_dma_flush_all( dmach_t channel );
+extern void sa1100_free_dma( dmach_t channel );
+
+/* Sa1111 DMA interface (all but registration uses the above) */
+extern int sa1111_sac_request_dma( dmach_t *channel, const char *device_id,
+				   unsigned int direction );
+extern int sa1111_check_dma_bug( dma_addr_t addr );
+
 
 #endif /* _ASM_ARCH_DMA_H */
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/keyboard.h linux/include/asm-arm/arch-sa1100/keyboard.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/keyboard.h	Tue Jul 18 22:43:25 2000
+++ linux/include/asm-arm/arch-sa1100/keyboard.h	Mon Dec  3 22:34:51 2001
@@ -10,7 +10,9 @@
 #include <linux/config.h>
 
 
-#ifdef CONFIG_SA1100_BRUTUS
+// #ifdef CONFIG_SA1100_BRUTUS
+/* need fixing... */
+#if 0
 
 extern int Brutus_kbd_translate(unsigned char scancode, unsigned char *keycode,
 			   char raw_mode);
@@ -32,6 +34,113 @@
 
 #define SYSRQ_KEY 0x54
 
+#elif CONFIG_SA1100_GRAPHICSCLIENT
+extern int gc_kbd_translate(unsigned char scancode, unsigned char *keycode, char raw_mode);
+extern void gc_kbd_leds(unsigned char leds);
+extern void gc_kbd_init_hw(void);
+extern void gc_kbd_enable_irq(void);
+extern void gc_kbd_disable_irq(void);
+extern unsigned char gc_kbd_sysrq_xlate[128];
+
+#define kbd_setkeycode(x...)    (-ENOSYS)
+#define kbd_getkeycode(x...)    (-ENOSYS)
+#define kbd_translate           gc_kbd_translate
+#define kbd_unexpected_up(x...) (1)
+#define kbd_leds                gc_kbd_leds
+#define kbd_init_hw             gc_kbd_init_hw
+#define kbd_enable_irq          gc_kbd_enable_irq
+#define kbd_disable_irq         gc_kbd_disable_irq
+#define kbd_sysrq_xlate         gc_kbd_sysrq_xlate
+
+#elif defined(CONFIG_SA1111)   /*@@@@@*/
+
+#define KEYBOARD_IRQ           TPRXINT
+#define DISABLE_KBD_DURING_INTERRUPTS  0
+
+/* redefine some macros */
+#ifdef KBD_DATA_REG
+#undef KBD_DATA_REG
+#endif
+#ifdef KBD_STATUS_REG
+#undef KBD_STATUS_REG
+#endif
+#ifdef KBD_CNTL_REG
+#undef KBD_CNTL_REG
+#endif
+#define KBD_DATA_REG           KBDDATA
+#define KBD_STATUS_REG         KBDSTAT
+#define KBD_CNTL_REG           KBDCR
+
+extern int sa1111_setkeycode(unsigned int scancode, unsigned int keycode);
+extern int sa1111_getkeycode(unsigned int scancode);
+extern int sa1111_translate(unsigned char scancode, unsigned char *keycode,
+				char raw_mode);
+extern char sa1111_unexpected_up(unsigned char keycode);
+extern void sa1111_leds(unsigned char leds);
+extern void sa1111_init_hw(void);
+extern unsigned char sa1111_sysrq_xlate[128];
+
+#define kbd_setkeycode		sa1111_setkeycode
+#define kbd_getkeycode		sa1111_getkeycode
+#define kbd_translate		sa1111_translate
+#define kbd_unexpected_up	sa1111_unexpected_up
+#define kbd_leds		sa1111_leds
+#define kbd_init_hw		sa1111_init_hw
+#define kbd_sysrq_xlate		sa1111_sysrq_xlate
+#define kbd_disable_irq(x...)	do{;}while(0)
+#define kbd_enable_irq(x...)	do{;}while(0)
+
+#define SYSRQ_KEY 0x54
+
+/* resource allocation */
+#define kbd_request_region()
+#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
+					"keyboard", NULL)
+
+/* How to access the keyboard macros on this platform.  */
+#define kbd_read_input()	(*KBDDATA & 0x00ff)
+#define kbd_read_status()	(*KBDSTAT & 0x01ff)
+#define kbd_write_output(val)	(*KBDDATA = (val))
+#define kbd_write_command(val)	(*KBDCR = (val))
+
+/* Some stoneage hardware needs delays after some operations.  */
+#define kbd_pause() do {;} while(0)
+
+/* bit definitions for some registers */
+#define KBD_CR_ENA	(1<<3)
+
+#define KBD_STAT_RXB	(1<<4)
+#define KBD_STAT_RXF	(1<<5)
+#define KBD_STAT_TXB	(1<<6)
+#define KBD_STAT_TXE	(1<<7)
+#define KBD_STAT_STP	(1<<8)
+
+/*
+ * Machine specific bits for the PS/2 driver
+ */
+
+#define AUX_IRQ		MSRXINT
+
+#define aux_request_irq(hand, dev_id)  \
+		request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
+#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id)
+
+/* How to access the mouse macros on this platform.  */
+#define mse_read_input()	(*MSEDATA & 0x00ff)
+#define mse_read_status()	(*MSESTAT & 0x01ff)
+#define mse_write_output(val)	(*MSEDATA = (val))
+#define mse_write_command(val)	(*MSECR = (val))
+
+/* bit definitions for some registers */
+#define MSE_CR_ENA	(1<<3)
+
+#define MSE_STAT_RXB	(1<<4)
+#define MSE_STAT_RXF	(1<<5)
+#define MSE_STAT_TXB	(1<<6)
+#define MSE_STAT_TXE	(1<<7)
+#define MSE_STAT_STP	(1<<8)
+
+
 #else
 
 /* dummy i.e. no real keyboard */
@@ -39,11 +148,18 @@
 #define kbd_getkeycode(x...)	(-ENOSYS)
 #define kbd_translate(x...)	(0)
 #define kbd_unexpected_up(x...)	(1)
-#define kbd_leds(x...)		do { } while (0)
-#define kbd_init_hw(x...)	do { } while (0)
-#define kbd_enable_irq(x...)	do { } while (0)
-#define kbd_disable_irq(x...)	do { } while (0)
+#define kbd_leds(x...)		do {;} while (0)
+#define kbd_init_hw(x...)	do {;} while (0)
+#define kbd_enable_irq(x...)	do {;} while (0)
+#define kbd_disable_irq(x...)	do {;} while (0)
+
+#endif
+
 
+/* needed if MAGIC_SYSRQ is enabled for serial console */
+#ifndef SYSRQ_KEY
+#define SYSRQ_KEY		-1
+#define kbd_sysrq_xlate         ((unsigned char *)NULL)
 #endif
 
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/mmzone.h linux/include/asm-arm/arch-sa1100/mmzone.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/mmzone.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/arch-sa1100/mmzone.h	Mon Dec  3 22:34:51 2001
@@ -21,21 +21,7 @@
  * 	node 3:  0xd8000000 - 0xdfffffff
  */
 
-
-/*
- * Currently defined in arch/arm/mm/mm-sa1100.c
- */
-extern pg_data_t sa1100_node_data[];
-
-/*
- * Return a pointer to the node data for node n.
- */
-#define NODE_DATA(nid)	(&sa1100_node_data[nid])
-
-/*
- * NODE_MEM_MAP gives the kaddr for the mem_map of the node.
- */
-#define NODE_MEM_MAP(nid)	(NODE_DATA(nid)->node_mem_map)
+#define NR_NODES	4
 
 /*
  * Given a kernel address, find the home node of the underlying memory.
@@ -71,7 +57,7 @@
  */
 #define VALID_PAGE(page) \
 ({ unsigned int node = KVADDR_TO_NID(page); \
-   ( (node < 4) && \
+   ( (node < NR_NODES) && \
      ((unsigned)((page) - NODE_MEM_MAP(node)) < NODE_DATA(node)->node_size) ); \
 })
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/pcmcia.h linux/include/asm-arm/arch-sa1100/pcmcia.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/pcmcia.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-sa1100/pcmcia.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,70 @@
+/*
+ * linux/include/asm/arch/pcmcia.h
+ *
+ * Copyright (C) 2000 John G Dorsey <john+@cs.cmu.edu>
+ *
+ * This file contains definitions for the low-level SA-1100 kernel PCMCIA
+ * interface. Please see linux/Documentation/arm/SA1100/PCMCIA for details.
+ */
+
+#ifndef _ASM_ARCH_PCMCIA
+#define _ASM_ARCH_PCMCIA
+
+
+/* Ideally, we'd support up to MAX_SOCK sockets, but the SA-1100 only
+ * has support for two. This shows up in lots of hardwired ways, such
+ * as the fact that MECR only has enough bits to configure two sockets.
+ * Since it's so entrenched in the hardware, limiting the software
+ * in this way doesn't seem too terrible.
+ */
+#define SA1100_PCMCIA_MAX_SOCK   (2)
+
+
+#ifndef __ASSEMBLY__
+
+struct pcmcia_init {
+  void (*handler)(int irq, void *dev, struct pt_regs *regs);
+};
+
+struct pcmcia_state {
+  unsigned detect: 1,
+            ready: 1,
+             bvd1: 1,
+             bvd2: 1,
+           wrprot: 1,
+            vs_3v: 1,
+            vs_Xv: 1;
+};
+
+struct pcmcia_state_array {
+  unsigned int size;
+  struct pcmcia_state *state;
+};
+
+struct pcmcia_configure {
+  unsigned sock: 8,
+            vcc: 8,
+            vpp: 8,
+         output: 1,
+        speaker: 1,
+          reset: 1;
+};
+
+struct pcmcia_irq_info {
+  unsigned int sock;
+  unsigned int irq;
+};
+
+struct pcmcia_low_level {
+  int (*init)(struct pcmcia_init *);
+  int (*shutdown)(void);
+  int (*socket_state)(struct pcmcia_state_array *);
+  int (*get_irq_info)(struct pcmcia_irq_info *);
+  int (*configure_socket)(const struct pcmcia_configure *);
+};
+
+extern struct pcmcia_low_level *pcmcia_low_level;
+
+#endif  /* __ASSEMBLY__ */
+
+#endif
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/serial.h linux/include/asm-arm/arch-sa1100/serial.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/serial.h	Thu Jan 13 13:30:31 2000
+++ linux/include/asm-arm/arch-sa1100/serial.h	Mon Dec  3 22:34:51 2001
@@ -22,11 +22,18 @@
 
 #define RS_TABLE_SIZE 4
 
-#define STD_SERIAL_PORT_DEFNS			\
-	/* UART CLK   PORT IRQ     FLAGS        */			\
-	{ 0, BASE_BAUD, 0x3F8, IRQ_GPIO3, STD_COM_FLAGS },	/* ttyS0 */	\
-	{ 0, BASE_BAUD, 0x2F8, IRQ_GPIO3, STD_COM_FLAGS },	/* ttyS1 */	\
-	{ 0, BASE_BAUD, 0x3E8, IRQ_GPIO3, STD_COM_FLAGS },	/* ttyS2 */	\
-	{ 0, BASE_BAUD, 0x2E8, IRQ_GPIO3, STD_COM4_FLAGS }	/* ttyS3 */
 
+/*
+ * Rather empty table...
+ * Hardwired serial ports should be defined here.
+ * PCMCIA will fill it dynamically.
+ */
+#define STD_SERIAL_PORT_DEFNS	\
+       /* UART	CLK     	PORT		IRQ	FLAGS		*/ \
+	{ 0,	BASE_BAUD,	0, 		0,	STD_COM_FLAGS },   \
+	{ 0,	BASE_BAUD,	0, 		0,	STD_COM_FLAGS },   \
+	{ 0,	BASE_BAUD,	0, 		0,	STD_COM_FLAGS },   \
+	{ 0,	BASE_BAUD,	0, 		0,	STD_COM_FLAGS }
+
+#define EXTRA_SERIAL_PORT_DEFNS
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-sa1100/system.h linux/include/asm-arm/arch-sa1100/system.h
--- linux-2.4.4-pre2/include/asm-arm/arch-sa1100/system.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/arch-sa1100/system.h	Mon Dec  3 22:34:51 2001
@@ -7,7 +7,7 @@
 
 static inline void arch_idle(void)
 {
-	while (!current->need_resched && !hlt_counter)
+	if (!hlt_counter)
 		cpu_do_idle(0);
 }
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/dma.h linux/include/asm-arm/arch-shark/dma.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/dma.h	Mon Mar 27 10:46:29 2000
+++ linux/include/asm-arm/arch-shark/dma.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/dma.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  */
 #ifndef __ASM_ARCH_DMA_H
 #define __ASM_ARCH_DMA_H
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/hardware.h linux/include/asm-arm/arch-shark/hardware.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/hardware.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-shark/hardware.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/hardware.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * derived from:
  * linux/include/asm-arm/arch-ebsa110/hardware.h
@@ -40,14 +40,16 @@
 /* defines for the Framebuffer */
 #define FB_START                0x06000000
 
-/* Registers for Framebuffer */
-/*#define FBREG_START             0x06800000*/
-
 #define UNCACHEABLE_ADDR        0xdf010000
 
 #define SEQUOIA_LED_GREEN       (1<<6)
 #define SEQUOIA_LED_AMBER       (1<<5)
 #define SEQUOIA_LED_BACK        (1<<7)
+
+#define pcibios_assign_all_busses()     1
+
+#define PCIBIOS_MIN_IO          0x6000
+#define PCIBIOS_MIN_MEM         0x50000000
 
 #endif
 
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/ide.h linux/include/asm-arm/arch-shark/ide.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/ide.h	Mon Jun 26 12:04:42 2000
+++ linux/include/asm-arm/arch-shark/ide.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/ide.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * derived from:
  * linux/include/asm-arm/arch-ebsa285/ide.h
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/io.h linux/include/asm-arm/arch-shark/io.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/io.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/arch-shark/io.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/io.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * derived from:
  * linux/include/asm-arm/arch-ebsa110/io.h
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/irq.h linux/include/asm-arm/arch-shark/irq.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/irq.h	Mon Mar 27 10:46:29 2000
+++ linux/include/asm-arm/arch-shark/irq.h	Mon Dec  3 22:34:51 2001
@@ -1,13 +1,14 @@
 /*
  * linux/include/asm-arm/arch-shark/irq.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * derived from linux/arch/ppc/kernel/i8259.c and:
  * include/asm-arm/arch-ebsa110/irq.h
  * Copyright (C) 1996-1998 Russell King
  */
 
+#include <asm/io.h>
 #define fixup_irq(x) (x)
 
 /*
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/irqs.h linux/include/asm-arm/arch-shark/irqs.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/irqs.h	Mon Mar 27 10:46:29 2000
+++ linux/include/asm-arm/arch-shark/irqs.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/irqs.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  */
 
 #define NR_IRQS			16
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/keyboard.h linux/include/asm-arm/arch-shark/keyboard.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/keyboard.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-shark/keyboard.h	Mon Dec  3 22:34:51 2001
@@ -1,6 +1,6 @@
 /*
  * linux/include/asm-arm/arch-shark/keyboard.h
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  * 
  * Derived from linux/include/asm-arm/arch-ebsa285/keyboard.h
  * (C) 1998 Russell King
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/memory.h linux/include/asm-arm/arch-shark/memory.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/memory.h	Mon Nov 27 17:07:59 2000
+++ linux/include/asm-arm/arch-shark/memory.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/memory.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * derived from:
  * linux/include/asm-arm/arch-ebsa110/memory.h
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/param.h linux/include/asm-arm/arch-shark/param.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/param.h	Mon Mar 27 10:46:29 2000
+++ linux/include/asm-arm/arch-shark/param.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/param.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  */
 
 /* This must be a power of 2 because the RTC
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/system.h linux/include/asm-arm/arch-shark/system.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/system.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-shark/system.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/system.h
  *
- * Copyright (c) 1996-1998 Russell King.
+ * by Alexander Schulz <aschulz@netwinder.org>
  */
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/time.h linux/include/asm-arm/arch-shark/time.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/time.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-shark/time.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-arm/arch-shark/time.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
  * Uses the real time clock because you can't run
  * the timer with level triggered interrupts and
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/timex.h linux/include/asm-arm/arch-shark/timex.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/timex.h	Mon Mar 27 10:46:29 2000
+++ linux/include/asm-arm/arch-shark/timex.h	Mon Dec  3 22:34:51 2001
@@ -1,5 +1,5 @@
 /*
  * linux/include/asm-arm/arch-shark/timex.h
  *
- * by Alexander.Schulz@stud.uni-karlsruhe.de
+ * by Alexander Schulz <aschulz@netwinder.org>
  */
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-shark/uncompress.h linux/include/asm-arm/arch-shark/uncompress.h
--- linux-2.4.4-pre2/include/asm-arm/arch-shark/uncompress.h	Mon Mar 27 10:46:29 2000
+++ linux/include/asm-arm/arch-shark/uncompress.h	Mon Dec  3 22:34:51 2001
@@ -1,31 +1,57 @@
 /*
- * linux/include/asm-arm/arch-ebsa110/uncompress.h
+ * linux/include/asm-arm/arch-shark/uncompress.h
+ * by Alexander Schulz <aschulz@netwinder.org>
  *
+ * derived from:
+ * linux/include/asm-arm/arch-ebsa285/uncompress.h
  * Copyright (C) 1996,1997,1998 Russell King
  */
 
+#define SERIAL_BASE ((volatile unsigned char *)0x400003f8)
+
+static __inline__ void putc(char c)
+{
+	int t;
+
+	SERIAL_BASE[0] = c;
+	t=0x10000;
+	while (t--);
+}
+
 /*
  * This does not append a newline
  */
 static void puts(const char *s)
 {
-	__asm__ __volatile__("
-	ldrb	%0, [%2], #1
-	teq	%0, #0
-	beq	3f
-1:	strb	%0, [%3]
-2:	ldrb	%1, [%3, #0x14]
-	and	%1, %1, #0x60
-	teq	%1, #0x60
-	bne	2b
-	teq	%0, #'\n'
-	moveq	%0, #'\r'
-	beq	1b
-	ldrb	%0, [%2], #1
-	teq	%0, #0
-	bne	1b
-3:	" : : "r" (0), "r" (0), "r" (s), "r" (0xf0000be0) : "cc");
+	while (*s) {
+		putc(*s);
+		if (*s == '\n')
+			putc('\r');
+		s++;
+	}
+}
+
+#ifdef DEBUG
+static void putn(unsigned long z)
+{
+	int i;
+	char x;
+
+	putc('0');
+	putc('x');
+	for (i=0;i<8;i++) {
+		x='0'+((z>>((7-i)*4))&0xf);
+		if (x>'9') x=x-'0'+'A'-10;
+		putc(x);
+	}
+}
+
+static void putr()
+{
+	putc('\n');
+	putc('\r');
 }
+#endif
 
 /*
  * nothing to do
diff -urN linux-2.4.4-pre2/include/asm-arm/arch-tbox/time.h linux/include/asm-arm/arch-tbox/time.h
--- linux-2.4.4-pre2/include/asm-arm/arch-tbox/time.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/arch-tbox/time.h	Mon Dec  3 22:34:51 2001
@@ -8,6 +8,13 @@
  * our soft copy.
  */
 
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
 #include <asm/io.h>
 #include <asm/hardware.h>
 
diff -urN linux-2.4.4-pre2/include/asm-arm/byteorder.h linux/include/asm-arm/byteorder.h
--- linux-2.4.4-pre2/include/asm-arm/byteorder.h	Fri Oct 27 10:55:01 2000
+++ linux/include/asm-arm/byteorder.h	Mon Dec  3 22:34:51 2001
@@ -1,6 +1,21 @@
+/*
+ *  linux/include/asm-arm/byteorder.h
+ *
+ * ARM Endian-ness.  In little endian mode, the data bus is connected such
+ * that byte accesses appear as:
+ *  0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31
+ * and word accesses (data or instruction) appear as:
+ *  d0...d31
+ *
+ * When in big endian mode, byte accesses appear as:
+ *  0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7
+ * and word accesses (data or instruction) appear as:
+ *  d0...d31
+ */
 #ifndef __ASM_ARM_BYTEORDER_H
 #define __ASM_ARM_BYTEORDER_H
 
+
 #include <asm/types.h>
 
 #if !defined(__STRICT_ANSI__) || defined(__KERNEL__)
@@ -8,7 +23,11 @@
 #  define __SWAB_64_THRU_32__
 #endif
 
+#ifdef __ARMEB__
+#include <linux/byteorder/big_endian.h>
+#else
 #include <linux/byteorder/little_endian.h>
+#endif
 
 #endif
 
diff -urN linux-2.4.4-pre2/include/asm-arm/elf.h linux/include/asm-arm/elf.h
--- linux-2.4.4-pre2/include/asm-arm/elf.h	Wed Oct 20 16:29:08 1999
+++ linux/include/asm-arm/elf.h	Mon Dec  3 22:34:51 2001
@@ -30,7 +30,7 @@
  */
 #define ELF_CLASS	ELFCLASS32
 #ifdef __ARMEB__
-#define ELF_DATA	ELFDATA2LSB;
+#define ELF_DATA	ELFDATA2MSB;
 #else
 #define ELF_DATA	ELFDATA2LSB;
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/hardware/clps7111.h linux/include/asm-arm/hardware/clps7111.h
--- linux-2.4.4-pre2/include/asm-arm/hardware/clps7111.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/hardware/clps7111.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,181 @@
+/*
+ *  linux/include/asm-arm/hardware/clps7111.h
+ *
+ *  This file contains the hardware definitions of the CLPS7111 internal
+ *  registers.
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_HARDWARE_CLPS7111_H
+#define __ASM_HARDWARE_CLPS7111_H
+
+#define CLPS7111_PHYS_BASE	(0x80000000)
+
+#ifndef __ASSEMBLY__
+#define clps_readb(off)		__raw_readb(CLPS7111_BASE + (off))
+#define clps_readl(off)		__raw_readl(CLPS7111_BASE + (off))
+#define clps_writeb(val,off)	__raw_writeb(val, CLPS7111_BASE + (off))
+#define clps_writel(val,off)	__raw_writel(val, CLPS7111_BASE + (off))
+#endif
+
+#define PADR		(0x0000)
+#define PBDR		(0x0001)
+#define PDDR		(0x0003)
+#define PADDR		(0x0040)
+#define PBDDR		(0x0041)
+#define PDDDR		(0x0043)
+#define PEDR		(0x0080)
+#define PEDDR		(0x00c0)
+#define SYSCON1		(0x0100)
+#define SYSFLG1		(0x0140)
+#define MEMCFG1		(0x0180)
+#define MEMCFG2		(0x01c0)
+#define DRFPR		(0x0200)
+#define INTSR1		(0x0240)
+#define INTMR1		(0x0280)
+#define LCDCON		(0x02c0)
+#define TC2D		(0x0340)
+#define RTCDR		(0x0380)
+#define RTCMR		(0x03c0)
+#define PMPCON		(0x0400)
+#define CODR		(0x0440)
+#define UARTDR1		(0x0480)
+#define UBRLCR1		(0x04c0)
+#define SYNCIO		(0x0500)
+#define PALLSW		(0x0540)
+#define PALMSW		(0x0580)
+#define STFCLR		(0x05c0)
+#define BLEOI		(0x0600)
+#define MCEOI		(0x0640)
+#define TEOI		(0x0680)
+#define TC1EOI		(0x06c0)
+#define TC2EOI		(0x0700)
+#define RTCEOI		(0x0740)
+#define UMSEOI		(0x0780)
+#define COEOI		(0x07c0)
+#define HALT		(0x0800)
+#define STDBY		(0x0840)
+
+#define FBADDR		(0x1000)
+#define SYSCON2		(0x1100)
+#define SYSFLG2		(0x1140)
+#define INTSR2		(0x1240)
+#define INTMR2		(0x1280)
+#define UARTDR2		(0x1480)
+#define UBRLCR2		(0x14c0)
+#define SS2DR		(0x1500)
+#define SRXEOF		(0x1600)
+#define SS2POP		(0x16c0)
+#define KBDEOI		(0x1700)
+
+/* common bits: SYSCON1 / SYSCON2 */
+#define SYSCON_UARTEN		(1 << 8)
+
+#define SYSCON1_KBDSCAN(x)	((x) & 15)
+#define SYSCON1_KBDSCANMASK	(15)
+#define SYSCON1_TC1M		(1 << 4)
+#define SYSCON1_TC1S		(1 << 5)
+#define SYSCON1_TC2M		(1 << 6)
+#define SYSCON1_TC2S		(1 << 7)
+#define SYSCON1_UART1EN		SYSCON_UARTEN
+#define SYSCON1_BZTOG		(1 << 9)
+#define SYSCON1_BZMOD		(1 << 10)
+#define SYSCON1_DBGEN		(1 << 11)
+#define SYSCON1_LCDEN		(1 << 12)
+#define SYSCON1_CDENTX		(1 << 13)
+#define SYSCON1_CDENRX		(1 << 14)
+#define SYSCON1_SIREN		(1 << 15)
+#define SYSCON1_ADCKSEL(x)	(((x) & 3) << 16)
+#define SYSCON1_ADCKSEL_MASK	(3 << 16)
+#define SYSCON1_EXCKEN		(1 << 18)
+#define SYSCON1_WAKEDIS		(1 << 19)
+#define SYSCON1_IRTXM		(1 << 20)
+
+/* common bits: SYSFLG1 / SYSFLG2 */
+#define SYSFLG_UBUSY		(1 << 11)
+#define SYSFLG_URXFE		(1 << 22)
+#define SYSFLG_UTXFF		(1 << 23)
+
+#define SYSFLG1_MCDR		(1 << 0)
+#define SYSFLG1_DCDET		(1 << 1)
+#define SYSFLG1_WUDR		(1 << 2)
+#define SYSFLG1_WUON		(1 << 3)
+#define SYSFLG1_CTS		(1 << 8)
+#define SYSFLG1_DSR		(1 << 9)
+#define SYSFLG1_DCD		(1 << 10)
+#define SYSFLG1_UBUSY		SYSFLG_UBUSY
+#define SYSFLG1_NBFLG		(1 << 12)
+#define SYSFLG1_RSTFLG		(1 << 13)
+#define SYSFLG1_PFFLG		(1 << 14)
+#define SYSFLG1_CLDFLG		(1 << 15)
+#define SYSFLG1_URXFE		SYSFLG_URXFE
+#define SYSFLG1_UTXFF		SYSFLG_UTXFF
+#define SYSFLG1_CRXFE		(1 << 24)
+#define SYSFLG1_CTXFF		(1 << 25)
+#define SYSFLG1_SSIBUSY		(1 << 26)
+#define SYSFLG1_ID		(1 << 29)
+
+#define SYSFLG2_SSRXOF		(1 << 0)
+#define SYSFLG2_RESVAL		(1 << 1)
+#define SYSFLG2_RESFRM		(1 << 2)
+#define SYSFLG2_SS2RXFE		(1 << 3)
+#define SYSFLG2_SS2TXFF		(1 << 4)
+#define SYSFLG2_SS2TXUF		(1 << 5)
+#define SYSFLG2_CKMODE		(1 << 6)
+#define SYSFLG2_UBUSY		SYSFLG_UBUSY
+#define SYSFLG2_URXFE		SYSFLG_URXFE
+#define SYSFLG2_UTXFF		SYSFLG_UTXFF
+
+#define LCDCON_GSEN		(1 << 30)
+#define LCDCON_GSMD		(1 << 31)
+
+#define SYSCON2_SERSEL		(1 << 0)
+#define SYSCON2_KBD6		(1 << 1)
+#define SYSCON2_DRAMZ		(1 << 2)
+#define SYSCON2_KBWEN		(1 << 3)
+#define SYSCON2_SS2TXEN		(1 << 4)
+#define SYSCON2_PCCARD1		(1 << 5)
+#define SYSCON2_PCCARD2		(1 << 6)
+#define SYSCON2_SS2RXEN		(1 << 7)
+#define SYSCON2_UART2EN		SYSCON_UARTEN
+#define SYSCON2_SS2MAEN		(1 << 9)
+#define SYSCON2_OSTB		(1 << 12)
+#define SYSCON2_CLKENSL		(1 << 13)
+#define SYSCON2_BUZFREQ		(1 << 14)
+
+/* common bits: UARTDR1 / UARTDR2 */
+#define UARTDR_FRMERR		(1 << 8)
+#define UARTDR_PARERR		(1 << 9)
+#define UARTDR_OVERR		(1 << 10)
+
+/* common bits: UBRLCR1 / UBRLCR2 */
+#define UBRLCR_BAUD_MASK	((1 << 12) - 1)
+#define UBRLCR_BREAK		(1 << 12)
+#define UBRLCR_PRTEN		(1 << 13)
+#define UBRLCR_EVENPRT		(1 << 14)
+#define UBRLCR_XSTOP		(1 << 15)
+#define UBRLCR_FIFOEN		(1 << 16)
+#define UBRLCR_WRDLEN5		(0 << 17)
+#define UBRLCR_WRDLEN6		(1 << 17)
+#define UBRLCR_WRDLEN7		(2 << 17)
+#define UBRLCR_WRDLEN8		(3 << 17)
+#define UBRLCR_WRDLEN_MASK	(3 << 17)
+
+#define SYNCIO_SMCKEN		(1 << 13)
+#define SYNCIO_TXFRMEN		(1 << 14)
+
+#endif /* __ASM_HARDWARE_CLPS7111_H */
diff -urN linux-2.4.4-pre2/include/asm-arm/hardware/ep7212.h linux/include/asm-arm/hardware/ep7212.h
--- linux-2.4.4-pre2/include/asm-arm/hardware/ep7212.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/hardware/ep7212.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,84 @@
+/*
+ *  linux/include/asm-arm/hardware/ep7212.h
+ *
+ *  This file contains the hardware definitions of the EP7212 internal
+ *  registers.
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_HARDWARE_EP7212_H
+#define __ASM_HARDWARE_EP7212_H
+
+/*
+ * define EP7212_BASE to be the base address of the region
+ * you want to access.
+ */
+
+#define EP7212_PHYS_BASE	(0x80000000)
+
+#ifndef __ASSEMBLY__
+#define ep_readl(off)		__raw_readl(EP7212_BASE + (off))
+#define ep_writel(val,off)	__raw_writel(val, EP7212_BASE + (off))
+#endif
+
+/*
+ * These registers are specific to the EP7212 only
+ */
+#define DAIR			0x2000
+#define DAIR0			0x2040
+#define DAIDR1			0x2080
+#define DAIDR2			0x20c0
+#define DAISR			0x2100
+#define SYSCON3			0x2200
+#define INTSR3			0x2240
+#define INTMR3			0x2280
+#define LEDFLSH			0x22c0
+
+#define DAIR_DAIEN		(1 << 16)
+#define DAIR_ECS		(1 << 17)
+#define DAIR_LCTM		(1 << 19)
+#define DAIR_LCRM		(1 << 20)
+#define DAIR_RCTM		(1 << 21)
+#define DAIR_RCRM		(1 << 22)
+#define DAIR_LBM		(1 << 23)
+
+#define DAIDR2_FIFOEN		(1 << 15)
+#define DAIDR2_FIFOLEFT		(0x0d << 16)
+#define DAIDR2_FIFORIGHT	(0x11 << 16)
+
+#define DAISR_RCTS		(1 << 0)
+#define DAISR_RCRS		(1 << 1)
+#define DAISR_LCTS		(1 << 2)
+#define DAISR_LCRS		(1 << 3)
+#define DAISR_RCTU		(1 << 4)
+#define DAISR_RCRO		(1 << 5)
+#define DAISR_LCTU		(1 << 6)
+#define DAISR_LCRO		(1 << 7)
+#define DAISR_RCNF		(1 << 8)
+#define DAISR_RCNE		(1 << 9)
+#define DAISR_LCNF		(1 << 10)
+#define DAISR_LCNE		(1 << 11)
+#define DAISR_FIFO		(1 << 12)
+
+#define SYSCON3_ADCCON		(1 << 0)
+#define SYSCON3_DAISEL		(1 << 3)
+#define SYSCON3_ADCCKNSEN	(1 << 4)
+#define SYSCON3_FASTWAKE	(1 << 8)
+#define SYSCON3_DAIEN		(1 << 9)
+
+
+#endif /* __ASM_HARDWARE_EP7212_H */
diff -urN linux-2.4.4-pre2/include/asm-arm/hardware/ioc.h linux/include/asm-arm/hardware/ioc.h
--- linux-2.4.4-pre2/include/asm-arm/hardware/ioc.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/hardware/ioc.h	Mon Dec  3 22:34:51 2001
@@ -10,58 +10,63 @@
  *  Use these macros to read/write the IOC.  All it does is perform the actual
  *  read/write.
  */
-
-#ifndef IOC_CONTROL
+#ifndef __ASMARM_HARDWARE_IOC_H
+#define __ASMARM_HARDWARE_IOC_H
 
 #ifndef __ASSEMBLY__
-#define __IOC(offset)	(IOC_BASE + (offset >> 2))
-#else
-#define __IOC(offset)	offset
+
+/*
+ * We use __raw_base variants here so that we give the compiler the
+ * chance to keep IOC_BASE in a register.
+ */
+#define ioc_readb(off)		__raw_base_readb(IOC_BASE, (off))
+#define ioc_writeb(val,off)	__raw_base_writeb(val, IOC_BASE, (off))
+
 #endif
 
-#define IOC_CONTROL	__IOC(0x00)
-#define IOC_KARTTX	__IOC(0x04)
-#define IOC_KARTRX	__IOC(0x04)
-
-#define IOC_IRQSTATA	__IOC(0x10)
-#define IOC_IRQREQA	__IOC(0x14)
-#define IOC_IRQCLRA	__IOC(0x14)
-#define IOC_IRQMASKA	__IOC(0x18)
-
-#define IOC_IRQSTATB	__IOC(0x20)
-#define IOC_IRQREQB	__IOC(0x24)
-#define IOC_IRQMASKB	__IOC(0x28)
-
-#define IOC_FIQSTAT	__IOC(0x30)
-#define IOC_FIQREQ	__IOC(0x34)
-#define IOC_FIQMASK	__IOC(0x38)
-
-#define IOC_T0CNTL	__IOC(0x40)
-#define IOC_T0LTCHL	__IOC(0x40)
-#define IOC_T0CNTH	__IOC(0x44)
-#define IOC_T0LTCHH	__IOC(0x44)
-#define IOC_T0GO	__IOC(0x48)
-#define IOC_T0LATCH	__IOC(0x4c)
-
-#define IOC_T1CNTL	__IOC(0x50)
-#define IOC_T1LTCHL	__IOC(0x50)
-#define IOC_T1CNTH	__IOC(0x54)
-#define IOC_T1LTCHH	__IOC(0x54)
-#define IOC_T1GO	__IOC(0x58)
-#define IOC_T1LATCH	__IOC(0x5c)
-
-#define IOC_T2CNTL	__IOC(0x60)
-#define IOC_T2LTCHL	__IOC(0x60)
-#define IOC_T2CNTH	__IOC(0x64)
-#define IOC_T2LTCHH	__IOC(0x64)
-#define IOC_T2GO	__IOC(0x68)
-#define IOC_T2LATCH	__IOC(0x6c)
-
-#define IOC_T3CNTL	__IOC(0x70)
-#define IOC_T3LTCHL	__IOC(0x70)
-#define IOC_T3CNTH	__IOC(0x74)
-#define IOC_T3LTCHH	__IOC(0x74)
-#define IOC_T3GO	__IOC(0x78)
-#define IOC_T3LATCH	__IOC(0x7c)
+#define IOC_CONTROL	(0x00)
+#define IOC_KARTTX	(0x04)
+#define IOC_KARTRX	(0x04)
+
+#define IOC_IRQSTATA	(0x10)
+#define IOC_IRQREQA	(0x14)
+#define IOC_IRQCLRA	(0x14)
+#define IOC_IRQMASKA	(0x18)
+
+#define IOC_IRQSTATB	(0x20)
+#define IOC_IRQREQB	(0x24)
+#define IOC_IRQMASKB	(0x28)
+
+#define IOC_FIQSTAT	(0x30)
+#define IOC_FIQREQ	(0x34)
+#define IOC_FIQMASK	(0x38)
+
+#define IOC_T0CNTL	(0x40)
+#define IOC_T0LTCHL	(0x40)
+#define IOC_T0CNTH	(0x44)
+#define IOC_T0LTCHH	(0x44)
+#define IOC_T0GO	(0x48)
+#define IOC_T0LATCH	(0x4c)
+
+#define IOC_T1CNTL	(0x50)
+#define IOC_T1LTCHL	(0x50)
+#define IOC_T1CNTH	(0x54)
+#define IOC_T1LTCHH	(0x54)
+#define IOC_T1GO	(0x58)
+#define IOC_T1LATCH	(0x5c)
+
+#define IOC_T2CNTL	(0x60)
+#define IOC_T2LTCHL	(0x60)
+#define IOC_T2CNTH	(0x64)
+#define IOC_T2LTCHH	(0x64)
+#define IOC_T2GO	(0x68)
+#define IOC_T2LATCH	(0x6c)
+
+#define IOC_T3CNTL	(0x70)
+#define IOC_T3LTCHL	(0x70)
+#define IOC_T3CNTH	(0x74)
+#define IOC_T3LTCHH	(0x74)
+#define IOC_T3GO	(0x78)
+#define IOC_T3LATCH	(0x7c)
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/hardware/iomd.h linux/include/asm-arm/hardware/iomd.h
--- linux-2.4.4-pre2/include/asm-arm/hardware/iomd.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/hardware/iomd.h	Mon Dec  3 22:34:51 2001
@@ -1,5 +1,5 @@
 /*
- *  linux/include/asm-arm/iomd.h
+ *  linux/include/asm-arm/hardware/iomd.h
  *
  *  Copyright (C) 1999 Russell King
  *
@@ -10,111 +10,121 @@
  *  This file contains information out the IOMD ASIC used in the
  *  Acorn RiscPC and subsequently integrated into the CLPS7500 chips.
  */
+#ifndef __ASMARM_HARDWARE_IOMD_H
+#define __ASMARM_HARDWARE_IOMD_H
+
 #include <linux/config.h>
 
 #ifndef __ASSEMBLY__
-#define __IOMD(offset)	(IO_IOMD_BASE + (offset >> 2))
-#else
-#define __IOMD(offset)	offset
+
+/*
+ * We use __raw_base variants here so that we give the compiler the
+ * chance to keep IOC_BASE in a register.
+ */
+#define iomd_readb(off)		__raw_base_readb(IOMD_BASE, (off))
+#define iomd_readl(off)		__raw_base_readl(IOMD_BASE, (off))
+#define iomd_writeb(val,off)	__raw_base_writeb(val, IOMD_BASE, (off))
+#define iomd_writel(val,off)	__raw_base_writel(val, IOMD_BASE, (off))
+
 #endif
 
-#define IOMD_CONTROL	__IOMD(0x000)
-#define IOMD_KARTTX	__IOMD(0x004)
-#define IOMD_KARTRX	__IOMD(0x004)
-#define IOMD_KCTRL	__IOMD(0x008)
+#define IOMD_CONTROL	(0x000)
+#define IOMD_KARTTX	(0x004)
+#define IOMD_KARTRX	(0x004)
+#define IOMD_KCTRL	(0x008)
 
 #ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_IOLINES	__IOMD(0x00C)
+#define IOMD_IOLINES	(0x00C)
 #endif
 
-#define IOMD_IRQSTATA	__IOMD(0x010)
-#define IOMD_IRQREQA	__IOMD(0x014)
-#define IOMD_IRQCLRA	__IOMD(0x014)
-#define IOMD_IRQMASKA	__IOMD(0x018)
+#define IOMD_IRQSTATA	(0x010)
+#define IOMD_IRQREQA	(0x014)
+#define IOMD_IRQCLRA	(0x014)
+#define IOMD_IRQMASKA	(0x018)
 
 #ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_SUSMODE	__IOMD(0x01C)
+#define IOMD_SUSMODE	(0x01C)
 #endif
 
-#define IOMD_IRQSTATB	__IOMD(0x020)
-#define IOMD_IRQREQB	__IOMD(0x024)
-#define IOMD_IRQMASKB	__IOMD(0x028)
+#define IOMD_IRQSTATB	(0x020)
+#define IOMD_IRQREQB	(0x024)
+#define IOMD_IRQMASKB	(0x028)
 
-#define IOMD_FIQSTAT	__IOMD(0x030)
-#define IOMD_FIQREQ	__IOMD(0x034)
-#define IOMD_FIQMASK	__IOMD(0x038)
+#define IOMD_FIQSTAT	(0x030)
+#define IOMD_FIQREQ	(0x034)
+#define IOMD_FIQMASK	(0x038)
 
 #ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_CLKCTL	__IOMD(0x03C)
+#define IOMD_CLKCTL	(0x03C)
 #endif
 
-#define IOMD_T0CNTL	__IOMD(0x040)
-#define IOMD_T0LTCHL	__IOMD(0x040)
-#define IOMD_T0CNTH	__IOMD(0x044)
-#define IOMD_T0LTCHH	__IOMD(0x044)
-#define IOMD_T0GO	__IOMD(0x048)
-#define IOMD_T0LATCH	__IOMD(0x04c)
+#define IOMD_T0CNTL	(0x040)
+#define IOMD_T0LTCHL	(0x040)
+#define IOMD_T0CNTH	(0x044)
+#define IOMD_T0LTCHH	(0x044)
+#define IOMD_T0GO	(0x048)
+#define IOMD_T0LATCH	(0x04c)
 
-#define IOMD_T1CNTL	__IOMD(0x050)
-#define IOMD_T1LTCHL	__IOMD(0x050)
-#define IOMD_T1CNTH	__IOMD(0x054)
-#define IOMD_T1LTCHH	__IOMD(0x054)
-#define IOMD_T1GO	__IOMD(0x058)
-#define IOMD_T1LATCH	__IOMD(0x05c)
+#define IOMD_T1CNTL	(0x050)
+#define IOMD_T1LTCHL	(0x050)
+#define IOMD_T1CNTH	(0x054)
+#define IOMD_T1LTCHH	(0x054)
+#define IOMD_T1GO	(0x058)
+#define IOMD_T1LATCH	(0x05c)
 
 #ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_IRQSTATC	__IOMD(0x060)
-#define IOMD_IRQREQC	__IOMD(0x064)
-#define IOMD_IRQMASKC	__IOMD(0x068)
+#define IOMD_IRQSTATC	(0x060)
+#define IOMD_IRQREQC	(0x064)
+#define IOMD_IRQMASKC	(0x068)
 
-#define IOMD_VIDMUX	__IOMD(0x06c)
+#define IOMD_VIDMUX	(0x06c)
 
-#define IOMD_IRQSTATD	__IOMD(0x070)
-#define IOMD_IRQREQD	__IOMD(0x074)
-#define IOMD_IRQMASKD	__IOMD(0x078)
+#define IOMD_IRQSTATD	(0x070)
+#define IOMD_IRQREQD	(0x074)
+#define IOMD_IRQMASKD	(0x078)
 #endif
 
-#define IOMD_ROMCR0	__IOMD(0x080)
-#define IOMD_ROMCR1	__IOMD(0x084)
+#define IOMD_ROMCR0	(0x080)
+#define IOMD_ROMCR1	(0x084)
 #ifdef CONFIG_ARCH_RPC
-#define IOMD_DRAMCR	__IOMD(0x088)
+#define IOMD_DRAMCR	(0x088)
 #endif
-#define IOMD_REFCR	__IOMD(0x08C)
+#define IOMD_REFCR	(0x08C)
 
-#define IOMD_FSIZE	__IOMD(0x090)
-#define IOMD_ID0	__IOMD(0x094)
-#define IOMD_ID1	__IOMD(0x098)
-#define IOMD_VERSION	__IOMD(0x09C)
+#define IOMD_FSIZE	(0x090)
+#define IOMD_ID0	(0x094)
+#define IOMD_ID1	(0x098)
+#define IOMD_VERSION	(0x09C)
 
 #ifdef CONFIG_ARCH_RPC
-#define IOMD_MOUSEX	__IOMD(0x0A0)
-#define IOMD_MOUSEY	__IOMD(0x0A4)
+#define IOMD_MOUSEX	(0x0A0)
+#define IOMD_MOUSEY	(0x0A4)
 #endif
 
 #ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_MSEDAT	__IOMD(0x0A8)
-#define IOMD_MSECTL	__IOMD(0x0Ac)
+#define IOMD_MSEDAT	(0x0A8)
+#define IOMD_MSECTL	(0x0Ac)
 #endif
 
 #ifdef CONFIG_ARCH_RPC
-#define IOMD_DMATCR	__IOMD(0x0C0)
+#define IOMD_DMATCR	(0x0C0)
 #endif
-#define IOMD_IOTCR	__IOMD(0x0C4)
-#define IOMD_ECTCR	__IOMD(0x0C8)
+#define IOMD_IOTCR	(0x0C4)
+#define IOMD_ECTCR	(0x0C8)
 #ifdef CONFIG_ARCH_RPC
-#define IOMD_DMAEXT	__IOMD(0x0CC)
+#define IOMD_DMAEXT	(0x0CC)
 #endif
 #ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_ASTCR	__IOMD(0x0CC)
-#define IOMD_DRAMCR	__IOMD(0x0D0)
-#define IOMD_SELFREF	__IOMD(0x0D4)
-#define IOMD_ATODICR	__IOMD(0x0E0)
-#define IOMD_ATODSR	__IOMD(0x0E4)
-#define IOMD_ATODCC	__IOMD(0x0E8)
-#define IOMD_ATODCNT1	__IOMD(0x0EC)
-#define IOMD_ATODCNT2	__IOMD(0x0F0)
-#define IOMD_ATODCNT3	__IOMD(0x0F4)
-#define IOMD_ATODCNT4	__IOMD(0x0F8)
+#define IOMD_ASTCR	(0x0CC)
+#define IOMD_DRAMCR	(0x0D0)
+#define IOMD_SELFREF	(0x0D4)
+#define IOMD_ATODICR	(0x0E0)
+#define IOMD_ATODSR	(0x0E4)
+#define IOMD_ATODCC	(0x0E8)
+#define IOMD_ATODCNT1	(0x0EC)
+#define IOMD_ATODCNT2	(0x0F0)
+#define IOMD_ATODCNT3	(0x0F4)
+#define IOMD_ATODCNT4	(0x0F8)
 #endif
 
 #ifdef CONFIG_ARCH_RPC
@@ -123,63 +133,63 @@
 #define DMA_EXT_IO2	4
 #define DMA_EXT_IO3	8
 
-#define IOMD_IO0CURA	__IOMD(0x100)
-#define IOMD_IO0ENDA	__IOMD(0x104)
-#define IOMD_IO0CURB	__IOMD(0x108)
-#define IOMD_IO0ENDB	__IOMD(0x10C)
-#define IOMD_IO0CR	__IOMD(0x110)
-#define IOMD_IO0ST	__IOMD(0x114)
-
-#define IOMD_IO1CURA	__IOMD(0x120)
-#define IOMD_IO1ENDA	__IOMD(0x124)
-#define IOMD_IO1CURB	__IOMD(0x128)
-#define IOMD_IO1ENDB	__IOMD(0x12C)
-#define IOMD_IO1CR	__IOMD(0x130)
-#define IOMD_IO1ST	__IOMD(0x134)
-
-#define IOMD_IO2CURA	__IOMD(0x140)
-#define IOMD_IO2ENDA	__IOMD(0x144)
-#define IOMD_IO2CURB	__IOMD(0x148)
-#define IOMD_IO2ENDB	__IOMD(0x14C)
-#define IOMD_IO2CR	__IOMD(0x150)
-#define IOMD_IO2ST	__IOMD(0x154)
-
-#define IOMD_IO3CURA	__IOMD(0x160)
-#define IOMD_IO3ENDA	__IOMD(0x164)
-#define IOMD_IO3CURB	__IOMD(0x168)
-#define IOMD_IO3ENDB	__IOMD(0x16C)
-#define IOMD_IO3CR	__IOMD(0x170)
-#define IOMD_IO3ST	__IOMD(0x174)
-#endif
-
-#define IOMD_SD0CURA	__IOMD(0x180)
-#define IOMD_SD0ENDA	__IOMD(0x184)
-#define IOMD_SD0CURB	__IOMD(0x188)
-#define IOMD_SD0ENDB	__IOMD(0x18C)
-#define IOMD_SD0CR	__IOMD(0x190)
-#define IOMD_SD0ST	__IOMD(0x194)
-
-#ifdef CONFIG_ARCH_RPC
-#define IOMD_SD1CURA	__IOMD(0x1A0)
-#define IOMD_SD1ENDA	__IOMD(0x1A4)
-#define IOMD_SD1CURB	__IOMD(0x1A8)
-#define IOMD_SD1ENDB	__IOMD(0x1AC)
-#define IOMD_SD1CR	__IOMD(0x1B0)
-#define IOMD_SD1ST	__IOMD(0x1B4)
-#endif
-
-#define IOMD_CURSCUR	__IOMD(0x1C0)
-#define IOMD_CURSINIT	__IOMD(0x1C4)
-
-#define IOMD_VIDCUR	__IOMD(0x1D0)
-#define IOMD_VIDEND	__IOMD(0x1D4)
-#define IOMD_VIDSTART	__IOMD(0x1D8)
-#define IOMD_VIDINIT	__IOMD(0x1DC)
-#define IOMD_VIDCR	__IOMD(0x1E0)
-
-#define IOMD_DMASTAT	__IOMD(0x1F0)
-#define IOMD_DMAREQ	__IOMD(0x1F4)
-#define IOMD_DMAMASK	__IOMD(0x1F8)
+#define IOMD_IO0CURA	(0x100)
+#define IOMD_IO0ENDA	(0x104)
+#define IOMD_IO0CURB	(0x108)
+#define IOMD_IO0ENDB	(0x10C)
+#define IOMD_IO0CR	(0x110)
+#define IOMD_IO0ST	(0x114)
+
+#define IOMD_IO1CURA	(0x120)
+#define IOMD_IO1ENDA	(0x124)
+#define IOMD_IO1CURB	(0x128)
+#define IOMD_IO1ENDB	(0x12C)
+#define IOMD_IO1CR	(0x130)
+#define IOMD_IO1ST	(0x134)
+
+#define IOMD_IO2CURA	(0x140)
+#define IOMD_IO2ENDA	(0x144)
+#define IOMD_IO2CURB	(0x148)
+#define IOMD_IO2ENDB	(0x14C)
+#define IOMD_IO2CR	(0x150)
+#define IOMD_IO2ST	(0x154)
+
+#define IOMD_IO3CURA	(0x160)
+#define IOMD_IO3ENDA	(0x164)
+#define IOMD_IO3CURB	(0x168)
+#define IOMD_IO3ENDB	(0x16C)
+#define IOMD_IO3CR	(0x170)
+#define IOMD_IO3ST	(0x174)
+#endif
+
+#define IOMD_SD0CURA	(0x180)
+#define IOMD_SD0ENDA	(0x184)
+#define IOMD_SD0CURB	(0x188)
+#define IOMD_SD0ENDB	(0x18C)
+#define IOMD_SD0CR	(0x190)
+#define IOMD_SD0ST	(0x194)
+
+#ifdef CONFIG_ARCH_RPC
+#define IOMD_SD1CURA	(0x1A0)
+#define IOMD_SD1ENDA	(0x1A4)
+#define IOMD_SD1CURB	(0x1A8)
+#define IOMD_SD1ENDB	(0x1AC)
+#define IOMD_SD1CR	(0x1B0)
+#define IOMD_SD1ST	(0x1B4)
+#endif
+
+#define IOMD_CURSCUR	(0x1C0)
+#define IOMD_CURSINIT	(0x1C4)
+
+#define IOMD_VIDCUR	(0x1D0)
+#define IOMD_VIDEND	(0x1D4)
+#define IOMD_VIDSTART	(0x1D8)
+#define IOMD_VIDINIT	(0x1DC)
+#define IOMD_VIDCR	(0x1E0)
+
+#define IOMD_DMASTAT	(0x1F0)
+#define IOMD_DMAREQ	(0x1F4)
+#define IOMD_DMAMASK	(0x1F8)
 
 #define DMA_END_S	(1 << 31)
 #define DMA_END_L	(1 << 30)
@@ -192,39 +202,6 @@
 #define DMA_ST_INT	2
 #define DMA_ST_AB	1
 
-#ifndef IOC_CONTROL
-/*
- * IOC compatability
- */
-#define IOC_CONTROL	IOMD_CONTROL
-#define IOC_IRQSTATA	IOMD_IRQSTATA
-#define IOC_IRQREQA	IOMD_IRQREQA
-#define IOC_IRQCLRA	IOMD_IRQCLRA
-#define IOC_IRQMASKA	IOMD_IRQMASKA
-
-#define IOC_IRQSTATB	IOMD_IRQSTATB
-#define IOC_IRQREQB	IOMD_IRQREQB
-#define IOC_IRQMASKB	IOMD_IRQMASKB
-
-#define IOC_FIQSTAT	IOMD_FIQSTAT
-#define IOC_FIQREQ	IOMD_FIQREQ
-#define IOC_FIQMASK	IOMD_FIQMASK
-
-#define IOC_T0CNTL	IOMD_T0CNTL
-#define IOC_T0LTCHL	IOMD_T0LTCHL
-#define IOC_T0CNTH	IOMD_T0CNTH
-#define IOC_T0LTCHH	IOMD_T0LTCHH
-#define IOC_T0GO	IOMD_T0GO
-#define IOC_T0LATCH	IOMD_T0LATCH
-
-#define IOC_T1CNTL	IOMD_T1CNTL
-#define IOC_T1LTCHL	IOMD_T1LTCHL
-#define IOC_T1CNTH	IOMD_T1CNTH
-#define IOC_T1LTCHH	IOMD_T1LTCHH
-#define IOC_T1GO	IOMD_T1GO
-#define IOC_T1LATCH	IOMD_T1LATCH
-#endif
-
 /*
  * DMA (MEMC) compatability
  */
@@ -247,3 +224,4 @@
 } while (0)
 #endif
 
+#endif
diff -urN linux-2.4.4-pre2/include/asm-arm/io.h linux/include/asm-arm/io.h
--- linux-2.4.4-pre2/include/asm-arm/io.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/io.h	Mon Dec  3 22:34:51 2001
@@ -104,11 +104,13 @@
 		_ret = __ioremap(iomem_to_phys(_off),_size,0);	\
 	_ret;							\
  })
+
+#define __arch_iounmap __iounmap
 #endif
 
 #define ioremap(off,sz)			__arch_ioremap((off),(sz),0)
 #define ioremap_nocache(off,sz)		__arch_ioremap((off),(sz),1)
-#define iounmap(_addr)			__iounmap(_addr)
+#define iounmap(_addr)			__arch_iounmap(_addr)
 
 /*
  * DMA-consistent mapping functions.  These allocate/free a region of
@@ -188,7 +190,7 @@
 	return retval;
 }
 
-#else	/* __mem_pci */
+#elif !defined(readb)
 
 #define readb(addr)			(__readwrite_bug("readb"),0)
 #define readw(addr)			(__readwrite_bug("readw"),0)
diff -urN linux-2.4.4-pre2/include/asm-arm/mmzone.h linux/include/asm-arm/mmzone.h
--- linux-2.4.4-pre2/include/asm-arm/mmzone.h	Mon Sep 18 15:15:24 2000
+++ linux/include/asm-arm/mmzone.h	Mon Dec  3 22:34:51 2001
@@ -10,6 +10,21 @@
 #ifndef __ASM_MMZONE_H
 #define __ASM_MMZONE_H
 
+/*
+ * Currently defined in arch/arm/mm/discontig.c
+ */
+extern pg_data_t discontig_node_data[];
+
+/*
+ * Return a pointer to the node data for node n.
+ */
+#define NODE_DATA(nid)		(&discontig_node_data[nid])
+
+/*
+ * NODE_MEM_MAP gives the kaddr for the mem_map of the node.
+ */
+#define NODE_MEM_MAP(nid)	(NODE_DATA(nid)->node_mem_map)
+
 #include <asm/arch/mmzone.h>
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/pgalloc.h linux/include/asm-arm/pgalloc.h
--- linux-2.4.4-pre2/include/asm-arm/pgalloc.h	Mon Sep 18 15:15:24 2000
+++ linux/include/asm-arm/pgalloc.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/include/asm-arm/pgalloc.h
  *
- *  Copyright (C) 2000 Russell King
+ *  Copyright (C) 2000-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -25,6 +25,11 @@
 #define flush_tlb_pgtables(mm,start,end)	do { } while (0)
 
 /*
+ * Processor specific parts...
+ */
+#include <asm/proc/pgalloc.h>
+
+/*
  * Page table cache stuff
  */
 #ifndef CONFIG_NO_PGT_CACHE
@@ -68,20 +73,13 @@
 	pgtable_cache_size++;
 }
 
-/* We don't use pmd cache, so this is a dummy routine */
-#define get_pmd_fast()		((pmd_t *)0)
-
-extern __inline__ void free_pmd_fast(pmd_t *pmd)
-{
-}
-
-extern __inline__ pte_t *get_pte_fast(void)
+static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address)
 {
 	unsigned long *ret;
 
 	if((ret = pte_quicklist) != NULL) {
 		pte_quicklist = (unsigned long *)__pte_next(ret);
-		ret[0] = ret[1];
+		ret[0] = 0;
 		clean_dcache_entry(ret);
 		pgtable_cache_size--;
 	}
@@ -97,119 +95,47 @@
 
 #else	/* CONFIG_NO_PGT_CACHE */
 
-#define pgd_quicklist		((unsigned long *)0)
-#define pmd_quicklist		((unsigned long *)0)
-#define pte_quicklist		((unsigned long *)0)
+#define pgd_quicklist			((unsigned long *)0)
+#define pmd_quicklist			((unsigned long *)0)
+#define pte_quicklist			((unsigned long *)0)
 
-#define get_pgd_fast()		((pgd_t *)0)
-#define get_pmd_fast()		((pmd_t *)0)
-#define get_pte_fast()		((pte_t *)0)
-
-#define free_pgd_fast(pgd)	free_pgd_slow(pgd)
-#define free_pmd_fast(pmd)	free_pmd_slow(pmd)
-#define free_pte_fast(pte)	free_pte_slow(pte)
+#define get_pgd_fast()			((pgd_t *)0)
+#define pte_alloc_one_fast(mm,addr)	((pte_t *)0)
 
-#endif	/* CONFIG_NO_PGT_CACHE */
+#define free_pgd_fast(pgd)		free_pgd_slow(pgd)
+#define free_pte_fast(pte)		pte_free_slow(pte)
 
-extern pgd_t *get_pgd_slow(void);
-extern void free_pgd_slow(pgd_t *pgd);
+#endif	/* CONFIG_NO_PGT_CACHE */
 
-#define free_pmd_slow(pmd)	do { } while (0)
+#define pte_free(pte)			free_pte_fast(pte)
 
-extern pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long addr_preadjusted);
-extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long addr_preadjusted);
-extern void free_pte_slow(pte_t *pte);
 
 /*
- * Allocate and free page tables. The xxx_kernel() versions are
- * used to allocate a kernel page table - this turns on ASN bits
- * if any.
+ * Since we have only two-level page tables, these are trivial
  */
-#define pte_free_kernel(pte)	free_pte_fast(pte)
-#define pte_free(pte)		free_pte_fast(pte)
+#define pmd_alloc_one_fast(mm,addr)	({ BUG(); ((pmd_t *)1); })
+#define pmd_alloc_one(mm,addr)		({ BUG(); ((pmd_t *)2); })
+#define pmd_free_slow(pmd)		do { } while (0)
+#define pmd_free_fast(pmd)		do { } while (0)
+#define pmd_free(pmd)			do { } while (0)
+#define pgd_populate(mm,pmd,pte)	BUG()
 
-#ifndef pte_alloc_kernel
-extern __inline__ pte_t * pte_alloc_kernel(pmd_t *pmd, unsigned long address)
-{
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-	if (pmd_none(*pmd)) {
-		pte_t *page = (pte_t *) get_pte_fast();
-
-		if (!page)
-			return get_pte_kernel_slow(pmd, address);
-		set_pmd(pmd, mk_kernel_pmd(page));
-		return page + address;
-	}
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd_kernel(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
-}
-#endif
-
-extern __inline__ pte_t *pte_alloc(pmd_t * pmd, unsigned long address)
-{
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-	if (pmd_none(*pmd)) {
-		pte_t *page = (pte_t *) get_pte_fast();
-
-		if (!page)
-			return get_pte_slow(pmd, address);
-		set_pmd(pmd, mk_user_pmd(page));
-		return page + address;
-	}
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
-}
-
-#define pmd_free_kernel		pmd_free
-#define pmd_free(pmd)		do { } while (0)
-
-#define pmd_alloc_kernel	pmd_alloc
-extern __inline__ pmd_t *pmd_alloc(pgd_t *pgd, unsigned long address)
-{
-	return (pmd_t *) pgd;
-}
-
-#define pgd_free(pgd)		free_pgd_fast(pgd)
+extern pgd_t *get_pgd_slow(struct mm_struct *mm);
+extern void free_pgd_slow(pgd_t *pgd);
 
-extern __inline__ pgd_t *pgd_alloc(void)
+extern __inline__ pgd_t *pgd_alloc(struct mm_struct *mm)
 {
 	pgd_t *pgd;
 
 	pgd = get_pgd_fast();
 	if (!pgd)
-		pgd = get_pgd_slow();
+		pgd = get_pgd_slow(mm);
 
 	return pgd;
 }
 
-extern int do_check_pgt_cache(int, int);
+#define pgd_free(pgd)			free_pgd_fast(pgd)
 
-extern __inline__ void set_pgdir(unsigned long address, pgd_t entry)
-{
-	struct task_struct * p;
-
-	read_lock(&tasklist_lock);
-	for_each_task(p) {
-		if (!p->mm)
-			continue;
-		*pgd_offset(p->mm,address) = entry;
-	}
-	read_unlock(&tasklist_lock);
-
-#ifndef CONFIG_NO_PGT_CACHE
-	{
-		pgd_t *pgd;
-		for (pgd = (pgd_t *)pgd_quicklist; pgd;
-		     pgd = (pgd_t *)__pgd_next(pgd))
-			pgd[address >> PGDIR_SHIFT] = entry;
-	}
-#endif
-}
+extern int do_check_pgt_cache(int, int);
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-arm/pgtable.h linux/include/asm-arm/pgtable.h
--- linux-2.4.4-pre2/include/asm-arm/pgtable.h	Tue Mar  6 19:44:35 2001
+++ linux/include/asm-arm/pgtable.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/include/asm-arm/pgtable.h
  *
- *  Copyright (C) 2000 Russell King
+ *  Copyright (C) 2000-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -75,12 +75,6 @@
 extern struct page *empty_zero_page;
 #define ZERO_PAGE(vaddr)	(empty_zero_page)
 
-/*
- * Handling allocation failures during page table setup.
- */
-extern void __handle_bad_pmd(pmd_t *pmd);
-extern void __handle_bad_pmd_kernel(pmd_t *pmd);
-
 #define pte_none(pte)		(!pte_val(pte))
 #define pte_clear(ptep)		set_pte((ptep), __pte(0))
 
@@ -98,6 +92,7 @@
 #endif
 
 #define pmd_none(pmd)		(!pmd_val(pmd))
+#define pmd_present(pmd)	(pmd_val(pmd))
 #define pmd_clear(pmdp)		set_pmd(pmdp, __pmd(0))
 
 /*
@@ -163,8 +158,6 @@
 }
 
 extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
-
-#define update_mmu_cache(vma,address,pte) do { } while (0)
 
 /* Encode and decode a swap entry.
  *
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armo/cache.h linux/include/asm-arm/proc-armo/cache.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armo/cache.h	Mon Sep 18 15:15:24 2000
+++ linux/include/asm-arm/proc-armo/cache.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/include/asm-arm/proc-armo/cache.h
  *
- *  Copyright (C) 1999-2000 Russell King
+ *  Copyright (C) 1999-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -37,8 +37,9 @@
  *  - flush_tlb_range(mm, start, end) flushes a range of pages
  */
 #define flush_tlb_all()				memc_update_all()
-#define flush_tlb_mm(mm)			do { } while (0)
-#define flush_tlb_range(mm, start, end)		do { (void)(start); (void)(end); } while (0)
+#define flush_tlb_mm(mm)			memc_update_mm(mm)
+#define flush_tlb_range(mm,start,end)		\
+		do { memc_update_mm(mm); (void)(start); (void)(end); } while (0)
 #define flush_tlb_page(vma, vmaddr)		do { } while (0)
 
 /*
@@ -57,7 +58,7 @@
 	processor._set_pgd(current->active_mm->pgd);
 }
 
-extern __inline__ void memc_update_mm(struct mm_struct *mm)
+static inline void memc_update_mm(struct mm_struct *mm)
 {
 	cpu_memc_update_all(mm->pgd);
 
@@ -66,19 +67,26 @@
 }
 
 extern __inline__ void
-memc_update_addr(struct mm_struct *mm, pte_t pte, unsigned long vaddr)
+memc_clear(struct mm_struct *mm, struct page *page)
 {
-	cpu_memc_update_entry(mm->pgd, pte_val(pte), vaddr);
+	cpu_memc_update_entry(mm->pgd, (unsigned long) page_address(page), 0);
 
 	if (mm == current->active_mm)
 		processor._set_pgd(mm->pgd);
 }
 
-extern __inline__ void
-memc_clear(struct mm_struct *mm, struct page *page)
+static inline void
+memc_update_addr(struct mm_struct *mm, pte_t pte, unsigned long vaddr)
 {
-	cpu_memc_update_entry(mm->pgd, (unsigned long) page_address(page), 0);
+	cpu_memc_update_entry(mm->pgd, pte_val(pte), vaddr);
 
 	if (mm == current->active_mm)
 		processor._set_pgd(mm->pgd);
+}
+
+static inline void
+update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	memc_update_addr(mm, pte, addr);
 }
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armo/pgalloc.h linux/include/asm-arm/proc-armo/pgalloc.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armo/pgalloc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/proc-armo/pgalloc.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,47 @@
+/*
+ *  linux/include/asm-arm/proc-armo/pgalloc.h
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * Page table allocation/freeing primitives for 26-bit ARM processors.
+ */
+
+/* unfortunately, this includes linux/mm.h and the rest of the universe. */
+#include <linux/slab.h>
+
+extern kmem_cache_t *pte_cache;
+
+/*
+ * Allocate one PTE table.
+ *
+ * Note that we keep the processor copy of the PTE entries separate
+ * from the Linux copy.  The processor copies are offset by -PTRS_PER_PTE
+ * words from the Linux copy.
+ */
+static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	return kmem_cache_alloc(pte_cache, GFP_KERNEL);
+}
+
+/*
+ * Free one PTE table.
+ */
+static inline void pte_free_slow(pte_t *pte)
+{
+	if (pte)
+		kmem_cache_free(pte_cache, pte);
+}
+
+/*
+ * Populate the pmdp entry with a pointer to the pte.  This pmd is part
+ * of the mm address space.
+ *
+ * If 'mm' is the init tasks mm, then we are doing a vmalloc, and we
+ * need to set stuff up correctly for it.
+ */
+#define pmd_populate(mm,pmdp,pte)				\
+	do {							\
+		set_pmd(pmdp, __mk_pmd(pte, _PAGE_TABLE));	\
+	} while (0)
+
+
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armo/pgtable.h linux/include/asm-arm/proc-armo/pgtable.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armo/pgtable.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/proc-armo/pgtable.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/include/asm-arm/proc-armo/pgtable.h
  *
- *  Copyright (C) 1995-1999 Russell King
+ *  Copyright (C) 1995-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -33,7 +33,7 @@
 #define pmd_bad(pmd)		((pmd_val(pmd) & 0xfc000002))
 #define set_pmd(pmdp,pmd)	((*(pmdp)) = (pmd))
 
-extern __inline__ pmd_t __mk_pmd(pte_t *ptep, unsigned long prot)
+static inline pmd_t __mk_pmd(pte_t *ptep, unsigned long prot)
 {
 	unsigned long pte_ptr = (unsigned long)ptep;
 	pmd_t pmd;
@@ -43,11 +43,7 @@
 	return pmd;
 }
 
-/* these are aliases for the above function */
-#define mk_user_pmd(ptep)	__mk_pmd(ptep, _PAGE_TABLE)
-#define mk_kernel_pmd(ptep)	__mk_pmd(ptep, _PAGE_TABLE)
-
-extern __inline__ unsigned long pmd_page(pmd_t pmd)
+static inline unsigned long pmd_page(pmd_t pmd)
 {
 	return __phys_to_virt(pmd_val(pmd) & ~_PAGE_TABLE);
 }
@@ -81,7 +77,6 @@
 #define pte_dirty(pte)			(!(pte_val(pte) & _PAGE_CLEAN))
 #define pte_young(pte)			(!(pte_val(pte) & _PAGE_OLD))
 
-extern inline pte_t pte_nocache(pte_t pte)	{ return pte; }
 extern inline pte_t pte_wrprotect(pte_t pte)    { pte_val(pte) |= _PAGE_READONLY;  return pte; }
 extern inline pte_t pte_rdprotect(pte_t pte)    { pte_val(pte) |= _PAGE_NOT_USER;  return pte; }
 extern inline pte_t pte_exprotect(pte_t pte)    { pte_val(pte) |= _PAGE_NOT_USER;  return pte; }
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armo/ptrace.h linux/include/asm-arm/proc-armo/ptrace.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armo/ptrace.h	Mon Sep 18 15:15:24 2000
+++ linux/include/asm-arm/proc-armo/ptrace.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/include/asm-arm/proc-armo/ptrace.h
  *
- *  Copyright (C) 1996-1999 Russell King
+ *  Copyright (C) 1996-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -14,6 +14,10 @@
 #define FIQ26_MODE	0x01
 #define IRQ26_MODE	0x02
 #define SVC26_MODE	0x03
+#define USR_MODE	USR26_MODE
+#define FIQ_MODE	FIQ26_MODE
+#define IRQ_MODE	IRQ26_MODE
+#define SVC_MODE	SVC26_MODE
 #define MODE_MASK	0x03
 #define F_BIT		(1 << 26)
 #define I_BIT		(1 << 27)
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armv/cache.h linux/include/asm-arm/proc-armv/cache.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armv/cache.h	Mon Sep 18 15:15:24 2000
+++ linux/include/asm-arm/proc-armv/cache.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/include/asm-arm/proc-armv/cache.h
  *
- *  Copyright (C) 1999-2000 Russell King
+ *  Copyright (C) 1999-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -154,3 +154,9 @@
 			cpu_tlb_invalidate_page((_page),		\
 				 ((_vma)->vm_flags & VM_EXEC));		\
 	} while (0)
+
+/*
+ * 32-bit ARM Processors don't have any MMU cache
+ */
+#define update_mmu_cache(vma,address,pte) do { } while (0)
+
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armv/pgalloc.h linux/include/asm-arm/proc-armv/pgalloc.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armv/pgalloc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/proc-armv/pgalloc.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,58 @@
+/*
+ *  linux/include/asm-arm/proc-armv/pgalloc.h
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * Page table allocation/freeing primitives for 32-bit ARM processors.
+ */
+
+/* unfortunately, this includes linux/mm.h and the rest of the universe. */
+#include <linux/slab.h>
+
+extern kmem_cache_t *pte_cache;
+
+/*
+ * Allocate one PTE table.
+ *
+ * Note that we keep the processor copy of the PTE entries separate
+ * from the Linux copy.  The processor copies are offset by -PTRS_PER_PTE
+ * words from the Linux copy.
+ */
+static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	pte_t *pte;
+
+	pte = kmem_cache_alloc(pte_cache, GFP_KERNEL);
+	if (pte)
+		pte += PTRS_PER_PTE;
+	return pte;
+}
+
+/*
+ * Free one PTE table.
+ */
+static inline void pte_free_slow(pte_t *pte)
+{
+	if (pte) {
+		pte -= PTRS_PER_PTE;
+		kmem_cache_free(pte_cache, pte);
+	}
+}
+
+/*
+ * Populate the pmdp entry with a pointer to the pte.  This pmd is part
+ * of the mm address space.
+ *
+ * If 'mm' is the init tasks mm, then we are doing a vmalloc, and we
+ * need to set stuff up correctly for it.
+ */
+#define pmd_populate(mm,pmdp,pte)			\
+	do {						\
+		unsigned long __prot;			\
+		if (mm == &init_mm)			\
+			__prot = _PAGE_KERNEL_TABLE;	\
+		else					\
+			__prot = _PAGE_USER_TABLE;	\
+		set_pmd(pmdp, __mk_pmd(pte, __prot));	\
+	} while (0)
+
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armv/pgtable.h linux/include/asm-arm/proc-armv/pgtable.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armv/pgtable.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/proc-armv/pgtable.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *  linux/include/asm-arm/proc-armv/pgtable.h
  *
- *  Copyright (C) 1995-1999 Russell King
+ *  Copyright (C) 1995-2001 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -48,7 +48,7 @@
 #define pmd_bad(pmd)		(pmd_val(pmd) & 2)
 #define set_pmd(pmdp,pmd)	cpu_set_pmd(pmdp,pmd)
 
-extern __inline__ pmd_t __mk_pmd(pte_t *ptep, unsigned long prot)
+static inline pmd_t __mk_pmd(pte_t *ptep, unsigned long prot)
 {
 	unsigned long pte_ptr = (unsigned long)ptep;
 	pmd_t pmd;
@@ -64,11 +64,7 @@
 	return pmd;
 }
 
-/* these are aliases for the above function */
-#define mk_user_pmd(ptep)	__mk_pmd(ptep, _PAGE_USER_TABLE)
-#define mk_kernel_pmd(ptep)	__mk_pmd(ptep, _PAGE_KERNEL_TABLE)
-
-extern __inline__ unsigned long pmd_page(pmd_t pmd)
+static inline unsigned long pmd_page(pmd_t pmd)
 {
 	unsigned long ptr;
 
@@ -161,7 +157,6 @@
 PTE_BIT_FUNC(mkdirty,   |= L_PTE_DIRTY);
 PTE_BIT_FUNC(mkold,     &= ~L_PTE_YOUNG);
 PTE_BIT_FUNC(mkyoung,   |= L_PTE_YOUNG);
-PTE_BIT_FUNC(nocache,   &= ~L_PTE_CACHEABLE);
 
 /*
  * Mark the prot value as uncacheable and unbufferable.
diff -urN linux-2.4.4-pre2/include/asm-arm/proc-armv/uaccess.h linux/include/asm-arm/proc-armv/uaccess.h
--- linux-2.4.4-pre2/include/asm-arm/proc-armv/uaccess.h	Tue Mar  6 19:44:35 2001
+++ linux/include/asm-arm/proc-armv/uaccess.h	Mon Dec  3 22:34:51 2001
@@ -91,7 +91,7 @@
 	"	.align	3\n"					\
 	"	.long	1b, 3b\n"				\
 	"	.previous"					\
-	: "=r" (err), "=r" (x)					\
+	: "=r" (err), "=&r" (x)					\
 	: "r" (addr), "i" (-EFAULT), "0" (err))
 
 #define __get_user_asm_half(x,addr,err)				\
@@ -117,7 +117,7 @@
 	"	.align	3\n"					\
 	"	.long	1b, 3b\n"				\
 	"	.previous"					\
-	: "=r" (err), "=r" (x)					\
+	: "=r" (err), "=&r" (x)					\
 	: "r" (addr), "i" (-EFAULT), "0" (err))
 
 extern unsigned long __arch_copy_from_user(void *to, const void *from, unsigned long n);
diff -urN linux-2.4.4-pre2/include/asm-arm/processor.h linux/include/asm-arm/processor.h
--- linux-2.4.4-pre2/include/asm-arm/processor.h	Sat Dec 30 09:35:40 2000
+++ linux/include/asm-arm/processor.h	Mon Dec  3 22:34:51 2001
@@ -38,6 +38,7 @@
 
 #define EISA_bus 0
 #define MCA_bus 0
+#define MCA_bus__is_a_macro
 
 #include <asm/atomic.h>
 #include <asm/ptrace.h>
diff -urN linux-2.4.4-pre2/include/asm-arm/procinfo.h linux/include/asm-arm/procinfo.h
--- linux-2.4.4-pre2/include/asm-arm/procinfo.h	Mon Sep 18 15:15:24 2000
+++ linux/include/asm-arm/procinfo.h	Mon Dec  3 22:34:51 2001
@@ -46,10 +46,13 @@
 
 #endif	/* __ASSEMBLY__ */
 
-#define HWCAP_SWP	1
-#define HWCAP_HALF	2
-#define HWCAP_THUMB	4
-#define HWCAP_26BIT	8	/* Play it safe */
+#define HWCAP_SWP	 1
+#define HWCAP_HALF	 2
+#define HWCAP_THUMB	 4
+#define HWCAP_26BIT	 8	/* Play it safe */
+#define HWCAP_FAST_MULT	 16
+#define HWCAP_FPA        32
+#define HWCAP_VFP        64
+#define HWCAP_EDSP	 128
 
 #endif
-
diff -urN linux-2.4.4-pre2/include/asm-arm/setup.h linux/include/asm-arm/setup.h
--- linux-2.4.4-pre2/include/asm-arm/setup.h	Thu Feb  8 16:32:44 2001
+++ linux/include/asm-arm/setup.h	Mon Dec  3 22:34:51 2001
@@ -207,7 +207,7 @@
 /*
  * Memory map description
  */
-#define NR_BANKS 4
+#define NR_BANKS 8
 
 struct meminfo {
 	int nr_banks;
diff -urN linux-2.4.4-pre2/include/asm-arm/unistd.h linux/include/asm-arm/unistd.h
--- linux-2.4.4-pre2/include/asm-arm/unistd.h	Tue Mar  6 19:44:35 2001
+++ linux/include/asm-arm/unistd.h	Mon Dec  3 22:34:51 2001
@@ -224,6 +224,10 @@
 #define __NR_setfsuid32			(__NR_SYSCALL_BASE+215)
 #define __NR_setfsgid32			(__NR_SYSCALL_BASE+216)
 #define __NR_getdents64			(__NR_SYSCALL_BASE+217)
+#define __NR_pivot_root			(__NR_SYSCALL_BASE+218)
+#define __NR_mincore			(__NR_SYSCALL_BASE+219)
+#define __NR_madvise			(__NR_SYSCALL_BASE+220)
+#define __NR_fcntl64			(__NR_SYSCALL_BASE+221)
 
 #define __sys2(x) #x
 #define __sys1(x) __sys2(x)
diff -urN linux-2.4.4-pre2/include/asm-arm/xor.h linux/include/asm-arm/xor.h
--- linux-2.4.4-pre2/include/asm-arm/xor.h	Tue Mar  6 19:44:35 2001
+++ linux/include/asm-arm/xor.h	Mon Dec  3 22:34:51 2001
@@ -8,7 +8,289 @@
  * published by the Free Software Foundation.
  */
 
+/*
+ *  linux/include/asm-arm/xor.h
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ *  linux/include/asm-arm/xor.h
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
 #include <asm-generic/xor.h>
+
+#define __XOR(a1, a2) a1 ^= a2
+
+#define GET_BLOCK_2(dst) \
+	__asm__("ldmia	%0, {%1, %2}" \
+		: "=r" (dst), "=r" (a1), "=r" (a2) \
+		: "0" (dst))
+
+#define GET_BLOCK_4(dst) \
+	__asm__("ldmia	%0, {%1, %2, %3, %4}" \
+		: "=r" (dst), "=r" (a1), "=r" (a2), "=r" (a3), "=r" (a4) \
+		: "0" (dst))
+
+#define XOR_BLOCK_2(src) \
+	__asm__("ldmia	%0!, {%1, %2}" \
+		: "=r" (src), "=r" (b1), "=r" (b2) \
+		: "0" (src)); \
+	__XOR(a1, b1); __XOR(a2, b2);
+
+#define XOR_BLOCK_4(src) \
+	__asm__("ldmia	%0!, {%1, %2, %3, %4}" \
+		: "=r" (src), "=r" (b1), "=r" (b2), "=r" (b3), "=r" (b4) \
+		: "0" (src)); \
+	__XOR(a1, b1); __XOR(a2, b2); __XOR(a3, b3); __XOR(a4, b4)
+
+#define PUT_BLOCK_2(dst) \
+	__asm__ __volatile__("stmia	%0!, {%2, %3}" \
+		: "=r" (dst) \
+		: "0" (dst), "r" (a1), "r" (a2))
+
+#define PUT_BLOCK_4(dst) \
+	__asm__ __volatile__("stmia	%0!, {%2, %3, %4, %5}" \
+		: "=r" (dst) \
+		: "0" (dst), "r" (a1), "r" (a2), "r" (a3), "r" (a4))
+
+static void
+xor_arm4regs_2(unsigned long bytes, unsigned long *p1, unsigned long *p2)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 4;
+	register unsigned int a1 __asm__("r4");
+	register unsigned int a2 __asm__("r5");
+	register unsigned int a3 __asm__("r6");
+	register unsigned int a4 __asm__("r7");
+	register unsigned int b1 __asm__("r8");
+	register unsigned int b2 __asm__("r9");
+	register unsigned int b3 __asm__("ip");
+	register unsigned int b4 __asm__("lr");
+
+	do {
+		GET_BLOCK_4(p1);
+		XOR_BLOCK_4(p2);
+		PUT_BLOCK_4(p1);
+	} while (--lines);
+}
+
+static void
+xor_arm4regs_3(unsigned long bytes, unsigned long *p1, unsigned long *p2,
+		unsigned long *p3)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 4;
+	register unsigned int a1 __asm__("r4");
+	register unsigned int a2 __asm__("r5");
+	register unsigned int a3 __asm__("r6");
+	register unsigned int a4 __asm__("r7");
+	register unsigned int b1 __asm__("r8");
+	register unsigned int b2 __asm__("r9");
+	register unsigned int b3 __asm__("ip");
+	register unsigned int b4 __asm__("lr");
+
+	do {
+		GET_BLOCK_4(p1);
+		XOR_BLOCK_4(p2);
+		XOR_BLOCK_4(p3);
+		PUT_BLOCK_4(p1);
+	} while (--lines);
+}
+
+static void
+xor_arm4regs_4(unsigned long bytes, unsigned long *p1, unsigned long *p2,
+		unsigned long *p3, unsigned long *p4)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 2;
+	register unsigned int a1 __asm__("r8");
+	register unsigned int a2 __asm__("r9");
+	register unsigned int b1 __asm__("ip");
+	register unsigned int b2 __asm__("lr");
+
+	do {
+		GET_BLOCK_2(p1);
+		XOR_BLOCK_2(p2);
+		XOR_BLOCK_2(p3);
+		XOR_BLOCK_2(p4);
+		PUT_BLOCK_2(p1);
+	} while (--lines);
+}
+
+static void
+xor_arm4regs_5(unsigned long bytes, unsigned long *p1, unsigned long *p2,
+		unsigned long *p3, unsigned long *p4, unsigned long *p5)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 2;
+	register unsigned int a1 __asm__("r8");
+	register unsigned int a2 __asm__("r9");
+	register unsigned int b1 __asm__("ip");
+	register unsigned int b2 __asm__("lr");
+
+	do {
+		GET_BLOCK_2(p1);
+		XOR_BLOCK_2(p2);
+		XOR_BLOCK_2(p3);
+		XOR_BLOCK_2(p4);
+		XOR_BLOCK_2(p5);
+		PUT_BLOCK_2(p1);
+	} while (--lines);
+}
+
+static struct xor_block_template xor_block_arm4regs = {
+	name:	"arm4regs",
+	do_2:	xor_arm4regs_2,
+	do_3:	xor_arm4regs_3,
+	do_4:	xor_arm4regs_4,
+	do_5:	xor_arm4regs_5,
+};
+
+#undef XOR_TRY_TEMPLATES
+#define XOR_TRY_TEMPLATES			\
+	do {					\
+		xor_speed(&xor_block_arm4regs);	\
+		xor_speed(&xor_block_8regs);	\
+		xor_speed(&xor_block_32regs);	\
+	} while (0)
+
+#define __XOR(a1, a2) a1 ^= a2
+
+#define GET_BLOCK_2(dst) \
+	__asm__("ldmia	%0, {%1, %2}" \
+		: "=r" (dst), "=r" (a1), "=r" (a2) \
+		: "0" (dst))
+
+#define GET_BLOCK_4(dst) \
+	__asm__("ldmia	%0, {%1, %2, %3, %4}" \
+		: "=r" (dst), "=r" (a1), "=r" (a2), "=r" (a3), "=r" (a4) \
+		: "0" (dst))
+
+#define XOR_BLOCK_2(src) \
+	__asm__("ldmia	%0!, {%1, %2}" \
+		: "=r" (src), "=r" (b1), "=r" (b2) \
+		: "0" (src)); \
+	__XOR(a1, b1); __XOR(a2, b2);
+
+#define XOR_BLOCK_4(src) \
+	__asm__("ldmia	%0!, {%1, %2, %3, %4}" \
+		: "=r" (src), "=r" (b1), "=r" (b2), "=r" (b3), "=r" (b4) \
+		: "0" (src)); \
+	__XOR(a1, b1); __XOR(a2, b2); __XOR(a3, b3); __XOR(a4, b4)
+
+#define PUT_BLOCK_2(dst) \
+	__asm__ __volatile__("stmia	%0!, {%2, %3}" \
+		: "=r" (dst) \
+		: "0" (dst), "r" (a1), "r" (a2))
+
+#define PUT_BLOCK_4(dst) \
+	__asm__ __volatile__("stmia	%0!, {%2, %3, %4, %5}" \
+		: "=r" (dst) \
+		: "0" (dst), "r" (a1), "r" (a2), "r" (a3), "r" (a4))
+
+static void
+xor_arm4regs_2(unsigned long bytes, unsigned long *p1, unsigned long *p2)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 4;
+	register unsigned int a1 __asm__("r4");
+	register unsigned int a2 __asm__("r5");
+	register unsigned int a3 __asm__("r6");
+	register unsigned int a4 __asm__("r7");
+	register unsigned int b1 __asm__("r8");
+	register unsigned int b2 __asm__("r9");
+	register unsigned int b3 __asm__("ip");
+	register unsigned int b4 __asm__("lr");
+
+	do {
+		GET_BLOCK_4(p1);
+		XOR_BLOCK_4(p2);
+		PUT_BLOCK_4(p1);
+	} while (--lines);
+}
+
+static void
+xor_arm4regs_3(unsigned long bytes, unsigned long *p1, unsigned long *p2,
+		unsigned long *p3)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 4;
+	register unsigned int a1 __asm__("r4");
+	register unsigned int a2 __asm__("r5");
+	register unsigned int a3 __asm__("r6");
+	register unsigned int a4 __asm__("r7");
+	register unsigned int b1 __asm__("r8");
+	register unsigned int b2 __asm__("r9");
+	register unsigned int b3 __asm__("ip");
+	register unsigned int b4 __asm__("lr");
+
+	do {
+		GET_BLOCK_4(p1);
+		XOR_BLOCK_4(p2);
+		XOR_BLOCK_4(p3);
+		PUT_BLOCK_4(p1);
+	} while (--lines);
+}
+
+static void
+xor_arm4regs_4(unsigned long bytes, unsigned long *p1, unsigned long *p2,
+		unsigned long *p3, unsigned long *p4)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 2;
+	register unsigned int a1 __asm__("r8");
+	register unsigned int a2 __asm__("r9");
+	register unsigned int b1 __asm__("ip");
+	register unsigned int b2 __asm__("lr");
+
+	do {
+		GET_BLOCK_2(p1);
+		XOR_BLOCK_2(p2);
+		XOR_BLOCK_2(p3);
+		XOR_BLOCK_2(p4);
+		PUT_BLOCK_2(p1);
+	} while (--lines);
+}
+
+static void
+xor_arm4regs_5(unsigned long bytes, unsigned long *p1, unsigned long *p2,
+		unsigned long *p3, unsigned long *p4, unsigned long *p5)
+{
+	unsigned int lines = bytes / sizeof(unsigned long) / 2;
+	register unsigned int a1 __asm__("r8");
+	register unsigned int a2 __asm__("r9");
+	register unsigned int b1 __asm__("ip");
+	register unsigned int b2 __asm__("lr");
+
+	do {
+		GET_BLOCK_2(p1);
+		XOR_BLOCK_2(p2);
+		XOR_BLOCK_2(p3);
+		XOR_BLOCK_2(p4);
+		XOR_BLOCK_2(p5);
+		PUT_BLOCK_2(p1);
+	} while (--lines);
+}
+
+static struct xor_block_template xor_block_arm4regs = {
+	name:	"arm4regs",
+	do_2:	xor_arm4regs_2,
+	do_3:	xor_arm4regs_3,
+	do_4:	xor_arm4regs_4,
+	do_5:	xor_arm4regs_5,
+};
+
+#undef XOR_TRY_TEMPLATES
+#define XOR_TRY_TEMPLATES			\
+	do {					\
+		xor_speed(&xor_block_arm4regs);	\
+		xor_speed(&xor_block_8regs);	\
+		xor_speed(&xor_block_32regs);	\
+	} while (0)
 
 #define __XOR(a1, a2) a1 ^= a2
 
diff -urN linux-2.4.4-pre2/include/asm-i386/kmap_types.h linux/include/asm-i386/kmap_types.h
--- linux-2.4.4-pre2/include/asm-i386/kmap_types.h	Thu Nov 11 10:33:42 1999
+++ linux/include/asm-i386/kmap_types.h	Mon Dec  3 22:34:51 2001
@@ -4,6 +4,8 @@
 enum km_type {
 	KM_BOUNCE_READ,
 	KM_BOUNCE_WRITE,
+	KM_SKB_DATA,
+	KM_SKB_DATA_SOFTIRQ,
 	KM_TYPE_NR
 };
 
diff -urN linux-2.4.4-pre2/include/asm-i386/rwsem-spin.h linux/include/asm-i386/rwsem-spin.h
--- linux-2.4.4-pre2/include/asm-i386/rwsem-spin.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-i386/rwsem-spin.h	Mon Dec  3 22:34:51 2001
@@ -8,6 +8,14 @@
 #ifndef _I386_RWSEM_SPIN_H
 #define _I386_RWSEM_SPIN_H
 
+#include <linux/config.h>
+
+#ifndef _LINUX_RWSEM_H
+#error please dont include asm/rwsem-spin.h directly, use linux/rwsem.h instead
+#endif
+
+#include <linux/spinlock.h>
+
 #ifdef __KERNEL__
 
 #define CONFIG_USING_SPINLOCK_BASED_RWSEM 1
@@ -16,7 +24,7 @@
  * the semaphore definition
  */
 struct rw_semaphore {
-	atomic_t		count;
+	signed long		count;
 #define RWSEM_UNLOCKED_VALUE		0x00000000
 #define RWSEM_ACTIVE_BIAS		0x00000001
 #define RWSEM_ACTIVE_MASK		0x0000ffff
@@ -53,7 +61,7 @@
 #endif
 
 #define __RWSEM_INITIALIZER(name,count) \
-{ ATOMIC_INIT(RWSEM_UNLOCKED_VALUE), SPIN_LOCK_UNLOCKED, \
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \
 	__WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
 	__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
 
@@ -66,7 +74,7 @@
 
 static inline void init_rwsem(struct rw_semaphore *sem)
 {
-	atomic_set(&sem->count, RWSEM_UNLOCKED_VALUE);
+	sem->count = RWSEM_UNLOCKED_VALUE;
 	spin_lock_init(&sem->lock);
 	init_waitqueue_head(&sem->wait);
 #if RWSEM_DEBUG
@@ -106,7 +114,7 @@
 		"  jmp       1b\n"
 #endif
 		"4:\n\t"
-		"  call      __down_read_failed\n\t"
+		"  call      __rwsem_down_read_failed\n\t"
 		"  jmp       2b\n"
 		".previous"
 		"# ending __down_read\n\t"
@@ -147,7 +155,7 @@
 		"  jmp       1b\n"
 #endif
 		"4:\n\t"
-		"  call     __down_write_failed\n\t"
+		"  call     __rwsem_down_write_failed\n\t"
 		"  jmp      2b\n"
 		".previous\n"
 		"# ending down_write"
@@ -233,6 +241,83 @@
 		: "=m"(sem->count), "=m"(sem->lock)
 		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count), "m"(sem->lock)
 		: "memory");
+}
+
+/*
+ * implement exchange and add functionality
+ */
+static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
+{
+	int tmp = delta;
+
+	__asm__ __volatile__(
+		"# beginning rwsem_atomic_update\n\t"
+#ifdef CONFIG_SMP
+LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* try to grab the spinlock */
+		"  js        3f\n" /* jump if failed */
+		"1:\n\t"
+#endif
+		"  xchgl     %0,(%1)\n\t" /* retrieve the old value */
+		"  addl      %0,(%1)\n\t" /* add 0xffff0001, result in memory */
+#ifdef CONFIG_SMP
+		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* release the spinlock */
+#endif
+		".section .text.lock,\"ax\"\n"
+#ifdef CONFIG_SMP
+		"3:\n\t" /* spin on the spinlock till we get it */
+		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t"
+		"  rep;nop   \n\t"
+		"  jle       3b\n\t"
+		"  jmp       1b\n"
+#endif
+		".previous\n"
+		"# ending rwsem_atomic_update\n\t"
+		: "+r"(tmp)
+		: "r"(sem)
+		: "memory");
+
+	return tmp+delta;
+}
+
+/*
+ * implement compare and exchange functionality on the rw-semaphore count LSW
+ */
+static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
+{
+	__u16 prev;
+
+	__asm__ __volatile__(
+		"# beginning rwsem_cmpxchgw\n\t"
+#ifdef CONFIG_SMP
+LOCK_PREFIX	"  decb      "RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* try to grab the spinlock */
+		"  js        3f\n" /* jump if failed */
+		"1:\n\t"
+#endif
+		"  cmpw      %w1,(%3)\n\t"
+		"  jne       4f\n\t" /* jump if old doesn't match sem->count LSW */
+		"  movw      %w2,(%3)\n\t" /* replace sem->count LSW with the new value */
+		"2:\n\t"
+#ifdef CONFIG_SMP
+		"  movb      $1,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* release the spinlock */
+#endif
+		".section .text.lock,\"ax\"\n"
+#ifdef CONFIG_SMP
+		"3:\n\t" /* spin on the spinlock till we get it */
+		"  cmpb      $0,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t"
+		"  rep;nop   \n\t"
+		"  jle       3b\n\t"
+		"  jmp       1b\n"
+#endif
+		"4:\n\t"
+		"  movw      (%3),%w0\n" /* we'll want to return the current value */
+		"  jmp       2b\n"
+		".previous\n"
+		"# ending rwsem_cmpxchgw\n\t"
+		: "=r"(prev)
+		: "r0"(old), "r"(new), "r"(sem)
+		: "memory");
+
+	return prev;
 }
 
 #endif /* __KERNEL__ */
diff -urN linux-2.4.4-pre2/include/asm-i386/rwsem-xadd.h linux/include/asm-i386/rwsem-xadd.h
--- linux-2.4.4-pre2/include/asm-i386/rwsem-xadd.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-i386/rwsem-xadd.h	Mon Dec  3 22:34:51 2001
@@ -2,42 +2,22 @@
  *
  * Written by David Howells (dhowells@redhat.com), 2001.
  * Derived from asm-i386/semaphore.h
- *
- *
- * The MSW of the count is the negated number of active writers and waiting
- * lockers, and the LSW is the total number of active locks
- *
- * The lock count is initialized to 0 (no active and no waiting lockers).
- *
- * When a writer subtracts WRITE_BIAS, it'll get 0xffff0001 for the case of an
- * uncontended lock. This can be determined because XADD returns the old value.
- * Readers increment by 1 and see a positive value when uncontended, negative
- * if there are writers (and maybe) readers waiting (in which case it goes to
- * sleep).
- *
- * The value of WAITING_BIAS supports up to 32766 waiting processes. This can
- * be extended to 65534 by manually checking the whole MSW rather than relying
- * on the S flag.
- *
- * The value of ACTIVE_BIAS supports up to 65535 active processes.
- *
- * This should be totally fair - if anything is waiting, a process that wants a
- * lock will go to the back of the queue. When the currently active lock is
- * released, if there's a writer at the front of the queue, then that and only
- * that will be woken up; if there's a bunch of consequtive readers at the
- * front, then they'll all be woken up, but no other readers will be.
  */
 
 #ifndef _I386_RWSEM_XADD_H
 #define _I386_RWSEM_XADD_H
 
+#ifndef _LINUX_RWSEM_H
+#error please dont include asm/rwsem-xadd.h directly, use linux/rwsem.h instead
+#endif
+
 #ifdef __KERNEL__
 
 /*
  * the semaphore definition
  */
 struct rw_semaphore {
-	atomic_t		count;
+	signed long		count;
 #define RWSEM_UNLOCKED_VALUE		0x00000000
 #define RWSEM_ACTIVE_BIAS		0x00000001
 #define RWSEM_ACTIVE_MASK		0x0000ffff
@@ -72,7 +52,7 @@
 #endif
 
 #define __RWSEM_INITIALIZER(name,count) \
-{ ATOMIC_INIT(RWSEM_UNLOCKED_VALUE), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+{ RWSEM_UNLOCKED_VALUE, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
 	__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
 
 #define __DECLARE_RWSEM_GENERIC(name,count) \
@@ -84,7 +64,7 @@
 
 static inline void init_rwsem(struct rw_semaphore *sem)
 {
-	atomic_set(&sem->count, RWSEM_UNLOCKED_VALUE);
+	sem->count = RWSEM_UNLOCKED_VALUE;
 	init_waitqueue_head(&sem->wait);
 #if RWSEM_DEBUG
 	sem->debug = 0;
@@ -108,7 +88,7 @@
 		"1:\n\t"
 		".section .text.lock,\"ax\"\n"
 		"2:\n\t"
-		"  call      __down_read_failed\n\t"
+		"  call      __rwsem_down_read_failed\n\t"
 		"  jmp       1b\n"
 		".previous"
 		"# ending down_read\n\t"
@@ -133,7 +113,7 @@
 		"1:\n\t"
 		".section .text.lock,\"ax\"\n"
 		"2:\n\t"
-		"  call      __down_write_failed\n\t"
+		"  call      __rwsem_down_write_failed\n\t"
 		"  jmp       1b\n"
 		".previous\n"
 		"# ending down_write"
@@ -188,6 +168,30 @@
 		: "=m"(sem->count)
 		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count)
 		: "memory");
+}
+
+/*
+ * implement exchange and add functionality
+ */
+static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
+{
+	int tmp = delta;
+
+	__asm__ __volatile__(
+		LOCK_PREFIX "xadd %0,(%1)"
+		: "+r"(tmp)
+		: "r"(sem)
+		: "memory");
+
+	return tmp+delta;
+}
+
+/*
+ * implement compare and exchange functionality on the rw-semaphore count LSW
+ */
+static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
+{
+	return cmpxchg((__u16*)&sem->count,0,RWSEM_ACTIVE_BIAS);
 }
 
 #endif /* __KERNEL__ */
diff -urN linux-2.4.4-pre2/include/asm-i386/rwsem.h linux/include/asm-i386/rwsem.h
--- linux-2.4.4-pre2/include/asm-i386/rwsem.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-i386/rwsem.h	Mon Dec  3 22:34:51 2001
@@ -8,126 +8,23 @@
 #ifndef _I386_RWSEM_H
 #define _I386_RWSEM_H
 
-#include <linux/linkage.h>
-
-#define RWSEM_DEBUG 0
-#define RWSEM_DEBUG_MAGIC 0
-
-#ifdef __KERNEL__
-
-#include <asm/system.h>
-#include <asm/atomic.h>
-#include <asm/spinlock.h>
-#include <linux/wait.h>
-
-#if RWSEM_DEBUG
-#define rwsemdebug(FMT,...) do { if (sem->debug) printk(FMT,__VA_ARGS__); } while(0)
-#else
-#define rwsemdebug(FMT,...)
+#ifndef _LINUX_RWSEM_H
+#error please dont include asm/rwsem.h directly, use linux/rwsem.h instead
 #endif
 
-/* old gcc */
-#if RWSEM_DEBUG
-//#define rwsemdebug(FMT, ARGS...) do { if (sem->debug) printk(FMT,##ARGS); } while(0)
-#else
-//#define rwsemdebug(FMT, ARGS...)
-#endif
+#ifdef __KERNEL__
 
+#define __HAVE_ARCH_SPECIFIC_RWSEM_IMPLEMENTATION 1
 #ifdef CONFIG_X86_XADD
 #include <asm/rwsem-xadd.h> /* use XADD based semaphores if possible */
 #else
-#include <asm/rwsem-spin.h> /* use spinlock based semaphores otherwise */
+#include <asm/rwsem-spin.h> /* use optimised spinlock based semaphores otherwise */
 #endif
 
 /* we use FASTCALL convention for the helpers */
-extern struct rw_semaphore *FASTCALL(__down_read_failed(struct rw_semaphore *sem));
-extern struct rw_semaphore *FASTCALL(__down_write_failed(struct rw_semaphore *sem));
+extern struct rw_semaphore *FASTCALL(__rwsem_down_read_failed(struct rw_semaphore *sem));
+extern struct rw_semaphore *FASTCALL(__rwsem_down_write_failed(struct rw_semaphore *sem));
 extern struct rw_semaphore *FASTCALL(__rwsem_wake(struct rw_semaphore *sem));
-
-/*
- * lock for reading
- */
-static inline void down_read(struct rw_semaphore *sem)
-{
-	rwsemdebug("Entering down_read(count=%08x)\n",atomic_read(&sem->count));
-
-#if RWSEM_DEBUG_MAGIC
-	if (sem->__magic != (long)&sem->__magic)
-		BUG();
-#endif
-
-	__down_read(sem);
-
-#if RWSEM_DEBUG_MAGIC
-	if (atomic_read(&sem->writers))
-		BUG();
-	atomic_inc(&sem->readers);
-#endif
-
-	rwsemdebug("Leaving down_read(count=%08x)\n",atomic_read(&sem->count));
-}
-
-/*
- * lock for writing
- */
-static inline void down_write(struct rw_semaphore *sem)
-{
-	rwsemdebug("Entering down_write(count=%08x)\n",atomic_read(&sem->count));
-
-#if RWSEM_DEBUG_MAGIC
-	if (sem->__magic != (long)&sem->__magic)
-		BUG();
-#endif
-
-	__down_write(sem);
-
-#if RWSEM_DEBUG_MAGIC
-	if (atomic_read(&sem->writers))
-		BUG();
-	if (atomic_read(&sem->readers))
-		BUG();
-	atomic_inc(&sem->writers);
-#endif
-
-	rwsemdebug("Leaving down_write(count=%08x)\n",atomic_read(&sem->count));
-}
-
-/*
- * release a read lock
- */
-static inline void up_read(struct rw_semaphore *sem)
-{
-	rwsemdebug("Entering up_read(count=%08x)\n",atomic_read(&sem->count));
-
-#if RWSEM_DEBUG_MAGIC
-	if (atomic_read(&sem->writers))
-		BUG();
-	atomic_dec(&sem->readers);
-#endif
-	__up_read(sem);
-
-	rwsemdebug("Leaving up_read(count=%08x)\n",atomic_read(&sem->count));
-}
-
-/*
- * release a write lock
- */
-static inline void up_write(struct rw_semaphore *sem)
-{
-	rwsemdebug("Entering up_write(count=%08x)\n",atomic_read(&sem->count));
-
-#if RWSEM_DEBUG_MAGIC
-	if (atomic_read(&sem->readers))
-		BUG();
-	if (atomic_read(&sem->writers) != 1)
-		BUG();
-	atomic_dec(&sem->writers);
-#endif
-	__up_write(sem);
-
-	rwsemdebug("Leaving up_write(count=%08x)\n",atomic_read(&sem->count));
-}
-
 
 #endif /* __KERNEL__ */
 #endif /* _I386_RWSEM_H */
diff -urN linux-2.4.4-pre2/include/asm-i386/semaphore.h linux/include/asm-i386/semaphore.h
--- linux-2.4.4-pre2/include/asm-i386/semaphore.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-i386/semaphore.h	Mon Dec  3 22:34:51 2001
@@ -39,7 +39,7 @@
 #include <asm/system.h>
 #include <asm/atomic.h>
 #include <linux/wait.h>
-#include <asm/rwsem.h>
+#include <linux/rwsem.h>
 
 struct semaphore {
 	atomic_t count;
diff -urN linux-2.4.4-pre2/include/asm-ia64/module.h linux/include/asm-ia64/module.h
--- linux-2.4.4-pre2/include/asm-ia64/module.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-ia64/module.h	Mon Dec  3 22:34:51 2001
@@ -7,7 +7,6 @@
  * Copyright (C) 2000 Mike Stephens <mike.stephens@intel.com>
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <asm/unwind.h>
diff -urN linux-2.4.4-pre2/include/asm-ia64/pgtable.h linux/include/asm-ia64/pgtable.h
--- linux-2.4.4-pre2/include/asm-ia64/pgtable.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-ia64/pgtable.h	Mon Dec  3 22:34:51 2001
@@ -125,7 +125,6 @@
 
 #include <asm/bitops.h>
 #include <asm/mmu_context.h>
-#include <asm/processor.h>
 #include <asm/system.h>
 
 /*
diff -urN linux-2.4.4-pre2/include/asm-ia64/processor.h linux/include/asm-ia64/processor.h
--- linux-2.4.4-pre2/include/asm-ia64/processor.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-ia64/processor.h	Mon Dec  3 22:34:51 2001
@@ -168,7 +168,6 @@
 #define IA64_THREAD_UAC_NOPRINT	(__IA64_UL(1) << 3)	/* don't log unaligned accesses */
 #define IA64_THREAD_UAC_SIGBUS	(__IA64_UL(1) << 4)	/* generate SIGBUS on unaligned acc. */
 #define IA64_THREAD_KRBS_SYNCED	(__IA64_UL(1) << 5)	/* krbs synced with process vm? */
-#define IA64_THREAD_MAP_SHARED	(__IA64_UL(1) << 6)	/* ugly: just a tmp flag for mmap() */
 #define IA64_KERNEL_DEATH	(__IA64_UL(1) << 63)	/* see die_if_kernel()... */
 
 #define IA64_THREAD_UAC_SHIFT	3
diff -urN linux-2.4.4-pre2/include/asm-ia64/sal.h linux/include/asm-ia64/sal.h
--- linux-2.4.4-pre2/include/asm-ia64/sal.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-ia64/sal.h	Mon Dec  3 22:34:51 2001
@@ -16,7 +16,6 @@
  *                      (plus examples of platform error info structures from smariset @ Intel)
  */
 
-#include <linux/config.h>
 #include <linux/spinlock.h>
 
 #include <asm/pal.h>
diff -urN linux-2.4.4-pre2/include/asm-ia64/sn/arc/types.h linux/include/asm-ia64/sn/arc/types.h
--- linux-2.4.4-pre2/include/asm-ia64/sn/arc/types.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-ia64/sn/arc/types.h	Thu Jan  4 15:25:55 2001
@@ -9,8 +9,6 @@
 #ifndef _ASM_SN_ARC_TYPES_H
 #define _ASM_SN_ARC_TYPES_H
 
-#include <linux/config.h>
-
 typedef char		CHAR;
 typedef short		SHORT;
 typedef long		LARGE_INTEGER __attribute__ ((__mode__ (__DI__)));
diff -urN linux-2.4.4-pre2/include/asm-ia64/sn/synergy.h linux/include/asm-ia64/sn/synergy.h
--- linux-2.4.4-pre2/include/asm-ia64/sn/synergy.h	Mon Dec  3 22:34:42 2001
+++ linux/include/asm-ia64/sn/synergy.h	Mon Dec  3 22:34:51 2001
@@ -1,6 +1,8 @@
 #ifndef ASM_IA64_SN_SYNERGY_H
 #define ASM_IA64_SN_SYNERGY_H
 
+#include <linux/config.h>
+
 #include "asm/io.h"
 #include "asm/sn/nodepda.h"
 #include "asm/sn/intr_public.h"
diff -urN linux-2.4.4-pre2/include/asm-ppc/kmap_types.h linux/include/asm-ppc/kmap_types.h
--- linux-2.4.4-pre2/include/asm-ppc/kmap_types.h	Sat Nov 11 18:23:10 2000
+++ linux/include/asm-ppc/kmap_types.h	Mon Dec  3 22:34:51 2001
@@ -5,6 +5,8 @@
 enum km_type {
 	KM_BOUNCE_READ,
 	KM_BOUNCE_WRITE,
+	KM_SKB_DATA,
+	KM_SKB_DATA_SOFTIRQ,
 	KM_TYPE_NR
 };
 
diff -urN linux-2.4.4-pre2/include/asm-ppc/tqm8xx.h linux/include/asm-ppc/tqm8xx.h
--- linux-2.4.4-pre2/include/asm-ppc/tqm8xx.h	Sat Mar  3 10:52:14 2001
+++ linux/include/asm-ppc/tqm8xx.h	Mon Dec  3 22:34:51 2001
@@ -12,6 +12,8 @@
 #ifndef __MACH_TQM8xx_DEFS
 #define __MACH_TQM8xx_DEFS
 
+#include <linux/config.h>
+
 #ifndef __ASSEMBLY__
 
 typedef	void (interrupt_handler_t)(void *);
diff -urN linux-2.4.4-pre2/include/asm-sh/bugs.h linux/include/asm-sh/bugs.h
--- linux-2.4.4-pre2/include/asm-sh/bugs.h	Thu Jan  4 13:19:13 2001
+++ linux/include/asm-sh/bugs.h	Mon Dec  3 22:34:51 2001
@@ -34,6 +34,10 @@
 		*p++ = '4';
 		printk("CPU: SH7750\n");
 		break;
+	case CPU_ST40STB1:
+		*p++ = '4';
+		printk("CPU: ST40STB1\n");
+		break;
 	default:
 		printk("CPU: ??????\n");
 		break;
diff -urN linux-2.4.4-pre2/include/asm-sh/ec3104.h linux/include/asm-sh/ec3104.h
--- linux-2.4.4-pre2/include/asm-sh/ec3104.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/ec3104.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,43 @@
+#ifndef __ASM_EC3104_H
+#define __ASM_EC3104_H
+
+
+/*
+ * Most of the register set is at 0xb0ec0000 - 0xb0ecffff.
+ *
+ * as far as I've figured it out the register map is:
+ * 0xb0ec0000 - id string
+ * 0xb0ec0XXX - power management
+ * 0xb0ec1XXX - interrupt control
+ * 0xb0ec3XXX - ps2 port (touch pad on aero 8000)
+ * 0xb0ec6XXX - i2c
+ * 0xb0ec7000 - first serial port (proprietary connector on aero 8000)
+ * 0xb0ec8000 - second serial port
+ * 0xb0ec9000 - third serial port
+ * 0xb0eca000 - fourth serial port (keyboard controller on aero 8000)
+ * 0xb0eccXXX - GPIO
+ * 0xb0ecdXXX - GPIO
+ */
+
+#define EC3104_BASE	0xb0ec0000
+
+#define EC3104_SER4_DATA	(EC3104_BASE+0xa000)
+#define EC3104_SER4_IIR		(EC3104_BASE+0xa008)
+#define EC3104_SER4_MCR		(EC3104_BASE+0xa010)
+#define EC3104_SER4_LSR		(EC3104_BASE+0xa014)
+#define EC3104_SER4_MSR		(EC3104_BASE+0xa018)
+
+/*
+ * our ISA bus.  this seems to be real ISA.
+ */
+#define EC3104_ISA_BASE	0xa5000000
+
+#define EC3104_IRQ	11
+#define EC3104_IRQBASE	64
+
+#define EC3104_IRQ_SER1	EC3104_IRQBASE + 7
+#define EC3104_IRQ_SER2	EC3104_IRQBASE + 8
+#define EC3104_IRQ_SER3	EC3104_IRQBASE + 9
+#define EC3104_IRQ_SER4	EC3104_IRQBASE + 10
+
+#endif /* __ASM_EC3104_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/hd64461.h linux/include/asm-sh/hd64461.h
--- linux-2.4.4-pre2/include/asm-sh/hd64461.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/hd64461.h	Mon Dec  3 22:34:51 2001
@@ -11,10 +11,22 @@
 #define HD64461_SYSCR	0x10002
 #define HD64461_SCPUCR	0x10004
 
-#define HD64461_CPTWAR	0x11030	
-#define HD64461_CPTWDR	0x11032
-#define HD64461_CPTRAR	0x11034	
-#define HD64461_CPTRDR	0x11036
+#define HD64461_LCDCBAR		0x11000
+#define HD64461_LCDCLOR		0x11002
+#define HD64461_LCDCCRR		0x11004
+#define	HD64461_LDR1		0x11010
+#define	HD64461_LDR2		0x11012
+#define	HD64461_LDHNCR		0x11014
+#define	HD64461_LDHNSR		0x11016
+#define HD64461_LDVNTR		0x11018
+#define HD64461_LDVNDR		0x1101a
+#define HD64461_LDVSPR		0x1101c
+#define HD64461_LDR3		0x1101e
+
+#define HD64461_CPTWAR		0x11030	
+#define HD64461_CPTWDR		0x11032
+#define HD64461_CPTRAR		0x11034	
+#define HD64461_CPTRDR		0x11036
 
 #define HD64461_PCC0ISR         0x12000
 #define HD64461_PCC0GCR         0x12002
@@ -30,6 +42,23 @@
 #define HD64461_P1OCR           0x1202c
 #define HD64461_PGCR            0x1202e
 
+#define HD64461_GPACR		0x14000
+#define HD64461_GPBCR		0x14002
+#define HD64461_GPCCR		0x14004
+#define HD64461_GPDCR		0x14006
+#define HD64461_GPADR		0x14010
+#define HD64461_GPBDR		0x14012
+#define HD64461_GPCDR		0x14014
+#define HD64461_GPDDR		0x14016
+#define HD64461_GPAICR		0x14020
+#define HD64461_GPBICR		0x14022
+#define HD64461_GPCICR		0x14024
+#define HD64461_GPDICR		0x14026
+#define HD64461_GPAISR		0x14040
+#define HD64461_GPBISR		0x14042
+#define HD64461_GPCISR		0x14044
+#define HD64461_GPDISR		0x14046
+
 #define HD64461_NIRR		0x15000
 #define HD64461_NIMR		0x15002
 
@@ -40,6 +69,6 @@
 #define CONFIG_HD64461_IRQ	36
 #endif
 
-#define HD64461_IRQBASE	64
+#define HD64461_IRQBASE		OFFCHIP_IRQ_BASE
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-sh/hd64465.h linux/include/asm-sh/hd64465.h
--- linux-2.4.4-pre2/include/asm-sh/hd64465.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/hd64465.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,257 @@
+#ifndef _ASM_SH_HD64465_
+#define _ASM_SH_HD64465_ 1
+/*
+ * $Id: hd64465.h,v 1.3 2001/02/07 18:31:20 stuart_menefy Exp $
+ *
+ * Hitachi HD64465 companion chip support
+ *
+ * by Greg Banks <gbanks@pocketpenguins.com>
+ * (c) 2000 PocketPenguins Inc.
+ *
+ * Derived from <asm/hd64461.h> which bore the message:
+ * Copyright (C) 2000 YAEGASHI Takeshi
+ */
+#include <linux/config.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+/*
+ * Note that registers are defined here as virtual port numbers,
+ * which have no meaning except to get translated by hd64465_isa_port2addr()
+ * to an address in the range 0xb0000000-0xb3ffffff.  Note that
+ * this translation happens to consist of adding the lower 16 bits
+ * of the virtual port number to 0xb0000000.  Note also that the manual
+ * shows addresses as absolute physical addresses starting at 0x10000000,
+ * so e.g. the NIRR register is listed as 0x15000 here, 0x10005000 in the
+ * manual, and accessed using address 0xb0005000 - Greg.
+ */
+
+/* System registers */
+#define HD64465_REG_SRR     0x1000c 	/* System Revision Register */
+#define HD64465_REG_SDID    0x10010 	/* System Device ID Reg */
+#define     HD64465_SDID            0x8122  /* 64465 device ID */
+
+/* Power Management registers */
+#define HD64465_REG_SMSCR   0x10000 	/* System Module Standby Control Reg */
+#define	    HD64465_SMSCR_PS2ST     0x4000  /* PS/2 Standby */
+#define	    HD64465_SMSCR_ADCST     0x1000  /* ADC Standby */
+#define	    HD64465_SMSCR_UARTST    0x0800  /* UART Standby */
+#define	    HD64465_SMSCR_SCDIST    0x0200  /* Serial Codec Standby */
+#define	    HD64465_SMSCR_PPST	    0x0100  /* Parallel Port Standby */
+#define	    HD64465_SMSCR_PC0ST     0x0040  /* PCMCIA0 Standby */
+#define	    HD64465_SMSCR_PC1ST     0x0020  /* PCMCIA1 Standby */
+#define	    HD64465_SMSCR_AFEST     0x0010  /* AFE Standby */
+#define	    HD64465_SMSCR_TM0ST     0x0008  /* Timer0 Standby */
+#define	    HD64465_SMSCR_TM1ST     0x0004  /* Timer1 Standby */
+#define	    HD64465_SMSCR_IRDAST    0x0002  /* IRDA Standby */
+#define	    HD64465_SMSCR_KBCST     0x0001  /* Keyboard Controller Standby */
+ 
+/* Interrupt Controller registers */
+#define HD64465_REG_NIRR    0x15000  	/* Interrupt Request Register */
+#define HD64465_REG_NIMR    0x15002  	/* Interrupt Mask Register */
+#define HD64465_REG_NITR    0x15004  	/* Interrupt Trigger Mode Register */
+
+/* Timer registers */
+#define HD64465_REG_TCVR1   0x16000  	/* Timer 1 constant value register  */
+#define HD64465_REG_TCVR0   0x16002	/* Timer 0 constant value register  */
+#define HD64465_REG_TRVR1   0x16004	/* Timer 1 read value register  */
+#define HD64465_REG_TRVR0   0x16006	/* Timer 0 read value register  */
+#define HD64465_REG_TCR1    0x16008	/* Timer 1 control register  */
+#define HD64465_REG_TCR0    0x1600A	/* Timer 0 control register  */
+#define	    HD64465_TCR_EADT 	0x10	    /* Enable ADTRIG# signal */
+#define	    HD64465_TCR_ETMO 	0x08	    /* Enable TMO signal */
+#define	    HD64465_TCR_PST_MASK 0x06	    /* Clock Prescale */
+#define	    HD64465_TCR_PST_1 	0x06	    /* 1:1 */
+#define	    HD64465_TCR_PST_4 	0x04	    /* 1:4 */
+#define	    HD64465_TCR_PST_8 	0x02	    /* 1:8 */
+#define	    HD64465_TCR_PST_16 	0x00	    /* 1:16 */
+#define	    HD64465_TCR_TSTP 	0x01	    /* Start/Stop timer */
+#define HD64465_REG_TIRR    0x1600C	/* Timer interrupt request register  */
+#define HD64465_REG_TIDR    0x1600E	/* Timer interrupt disable register  */
+#define HD64465_REG_PWM1CS  0x16010	/* PWM 1 clock scale register  */
+#define HD64465_REG_PWM1LPC 0x16012	/* PWM 1 low pulse width counter register  */
+#define HD64465_REG_PWM1HPC 0x16014	/* PWM 1 high pulse width counter register  */
+#define HD64465_REG_PWM0CS  0x16018	/* PWM 0 clock scale register  */
+#define HD64465_REG_PWM0LPC 0x1601A	/* PWM 0 low pulse width counter register  */
+#define HD64465_REG_PWM0HPC 0x1601C	/* PWM 0 high pulse width counter register  */
+
+/* Analog/Digital Converter registers */
+#define HD64465_REG_ADDRA   0x1E000	/* A/D data register A */
+#define HD64465_REG_ADDRB   0x1E002	/* A/D data register B */
+#define HD64465_REG_ADDRC   0x1E004	/* A/D data register C */
+#define HD64465_REG_ADDRD   0x1E006	/* A/D data register D */
+#define HD64465_REG_ADCSR   0x1E008	/* A/D control/status register */
+#define     HD64465_ADCSR_ADF	    0x80    /* A/D End Flag */
+#define     HD64465_ADCSR_ADST	    0x40    /* A/D Start Flag */
+#define     HD64465_ADCSR_ADIS	    0x20    /* A/D Interrupt Status */
+#define     HD64465_ADCSR_TRGE	    0x10    /* A/D Trigger Enable */
+#define     HD64465_ADCSR_ADIE	    0x08    /* A/D Interrupt Enable */
+#define     HD64465_ADCSR_SCAN	    0x04    /* A/D Scan Mode */
+#define     HD64465_ADCSR_CH_MASK   0x03    /* A/D Channel */
+#define HD64465_REG_ADCALCR 0x1E00A  	/* A/D calibration sample control */
+#define HD64465_REG_ADCAL   0x1E00C  	/* A/D calibration data register */
+
+
+/* General Purpose I/O ports registers */
+#define HD64465_REG_GPACR   0x14000  	/* Port A Control Register */
+#define HD64465_REG_GPBCR   0x14002  	/* Port B Control Register */
+#define HD64465_REG_GPCCR   0x14004  	/* Port C Control Register */
+#define HD64465_REG_GPDCR   0x14006  	/* Port D Control Register */
+#define HD64465_REG_GPECR   0x14008  	/* Port E Control Register */
+#define HD64465_REG_GPADR   0x14010  	/* Port A Data Register */
+#define HD64465_REG_GPBDR   0x14012  	/* Port B Data Register */
+#define HD64465_REG_GPCDR   0x14014  	/* Port C Data Register */
+#define HD64465_REG_GPDDR   0x14016  	/* Port D Data Register */
+#define HD64465_REG_GPEDR   0x14018  	/* Port E Data Register */
+#define HD64465_REG_GPAICR  0x14020  	/* Port A Interrupt Control Register */
+#define HD64465_REG_GPBICR  0x14022  	/* Port B Interrupt Control Register */
+#define HD64465_REG_GPCICR  0x14024  	/* Port C Interrupt Control Register */
+#define HD64465_REG_GPDICR  0x14026  	/* Port D Interrupt Control Register */
+#define HD64465_REG_GPEICR  0x14028  	/* Port E Interrupt Control Register */
+#define HD64465_REG_GPAISR  0x14040  	/* Port A Interrupt Status Register */
+#define HD64465_REG_GPBISR  0x14042  	/* Port B Interrupt Status Register */
+#define HD64465_REG_GPCISR  0x14044  	/* Port C Interrupt Status Register */
+#define HD64465_REG_GPDISR  0x14046  	/* Port D Interrupt Status Register */
+#define HD64465_REG_GPEISR  0x14048  	/* Port E Interrupt Status Register */
+
+/* PCMCIA bridge interface */
+#define HD64465_REG_PCC0ISR	0x12000	/* socket 0 interface status */ 
+#define     HD64465_PCCISR_PREADY   	 0x80    /* mem card ready / io card IREQ */
+#define     HD64465_PCCISR_PIREQ    	 0x80
+#define     HD64465_PCCISR_PMWP     	 0x40    /* mem card write-protected */
+#define     HD64465_PCCISR_PVS2 	 0x20    /* voltage select pin 2 */
+#define     HD64465_PCCISR_PVS1 	 0x10    /* voltage select pin 1 */
+#define     HD64465_PCCISR_PCD_MASK 	 0x0c    /* card detect */
+#define     HD64465_PCCISR_PBVD_MASK     0x03    /* battery voltage */
+#define     HD64465_PCCISR_PBVD_BATGOOD  0x03    /* battery good */
+#define     HD64465_PCCISR_PBVD_BATWARN  0x01    /* battery low warning */
+#define     HD64465_PCCISR_PBVD_BATDEAD1 0x02    /* battery dead */
+#define     HD64465_PCCISR_PBVD_BATDEAD2 0x00    /* battery dead */
+#define HD64465_REG_PCC0GCR	0x12002	/* socket 0 general control */ 
+#define     HD64465_PCCGCR_PDRV   	 0x80    /* output drive */
+#define     HD64465_PCCGCR_PCCR   	 0x40    /* PC card reset */
+#define     HD64465_PCCGCR_PCCT   	 0x20    /* PC card type, 1=IO&mem, 0=mem */
+#define     HD64465_PCCGCR_PVCC0   	 0x10    /* voltage control pin VCC0SEL0 */
+#define     HD64465_PCCGCR_PMMOD   	 0x08    /* memory mode */
+#define     HD64465_PCCGCR_PPA25   	 0x04    /* pin A25 */
+#define     HD64465_PCCGCR_PPA24   	 0x02    /* pin A24 */
+#define     HD64465_PCCGCR_PREG   	 0x01    /* ping PCC0REG# */
+#define HD64465_REG_PCC0CSCR	0x12004	/* socket 0 card status change */ 
+#define     HD64465_PCCCSCR_PSCDI   	 0x80    /* sw card detect intr */
+#define     HD64465_PCCCSCR_PSWSEL   	 0x40    /* power select */
+#define     HD64465_PCCCSCR_PIREQ   	 0x20    /* IREQ intr req */
+#define     HD64465_PCCCSCR_PSC   	 0x10    /* STSCHG (status change) pin */
+#define     HD64465_PCCCSCR_PCDC   	 0x08    /* CD (card detect) change */
+#define     HD64465_PCCCSCR_PRC   	 0x04    /* ready change */
+#define     HD64465_PCCCSCR_PBW   	 0x02    /* battery warning change */
+#define     HD64465_PCCCSCR_PBD   	 0x01    /* battery dead change */
+#define HD64465_REG_PCC0CSCIER	0x12006	/* socket 0 card status change interrupt enable */ 
+#define     HD64465_PCCCSCIER_PCRE   	 0x80    /* change reset enable */
+#define     HD64465_PCCCSCIER_PIREQE_MASK   	0x60   /* IREQ enable */
+#define     HD64465_PCCCSCIER_PIREQE_DISABLED	0x00   /* IREQ disabled */
+#define     HD64465_PCCCSCIER_PIREQE_LEVEL  	0x20   /* IREQ level-triggered */
+#define     HD64465_PCCCSCIER_PIREQE_FALLING	0x40   /* IREQ falling-edge-trig */
+#define     HD64465_PCCCSCIER_PIREQE_RISING 	0x60   /* IREQ rising-edge-trig */
+#define     HD64465_PCCCSCIER_PSCE   	 0x10    /* status change enable */
+#define     HD64465_PCCCSCIER_PCDE   	 0x08    /* card detect change enable */
+#define     HD64465_PCCCSCIER_PRE   	 0x04    /* ready change enable */
+#define     HD64465_PCCCSCIER_PBWE   	 0x02    /* battery warn change enable */
+#define     HD64465_PCCCSCIER_PBDE   	 0x01    /* battery dead change enable*/
+#define HD64465_REG_PCC0SCR	0x12008	/* socket 0 software control */ 
+#define     HD64465_PCCSCR_SHDN   	 0x10    /* TPS2206 SHutDowN pin */
+#define     HD64465_PCCSCR_SWP   	 0x01    /* write protect */
+#define HD64465_REG_PCCPSR	0x1200A	/* serial power switch control */ 
+#define HD64465_REG_PCC1ISR	0x12010	/* socket 1 interface status */ 
+#define HD64465_REG_PCC1GCR	0x12012	/* socket 1 general control */ 
+#define HD64465_REG_PCC1CSCR	0x12014	/* socket 1 card status change */ 
+#define HD64465_REG_PCC1CSCIER	0x12016	/* socket 1 card status change interrupt enable */ 
+#define HD64465_REG_PCC1SCR	0x12018	/* socket 1 software control */ 
+
+
+/* PS/2 Keyboard and mouse controller -- *not* register compatible */
+#define HD64465_REG_KBCSR   	0x1dc00 /* Keyboard Control/Status reg */
+#define     HD64465_KBCSR_KBCIE   	 0x8000    /* KBCK Input Enable */
+#define     HD64465_KBCSR_KBCOE   	 0x4000    /* KBCK Output Enable */
+#define     HD64465_KBCSR_KBDOE   	 0x2000    /* KB DATA Output Enable */
+#define     HD64465_KBCSR_KBCD   	 0x1000    /* KBCK Driven */
+#define     HD64465_KBCSR_KBDD   	 0x0800    /* KB DATA Driven */
+#define     HD64465_KBCSR_KBCS   	 0x0400    /* KBCK pin Status */
+#define     HD64465_KBCSR_KBDS   	 0x0200    /* KB DATA pin Status */
+#define     HD64465_KBCSR_KBDP   	 0x0100    /* KB DATA Parity bit */
+#define     HD64465_KBCSR_KBD_MASK   	 0x00ff    /* KD DATA shift reg */
+#define HD64465_REG_KBISR   	0x1dc04 /* Keyboard Interrupt Status reg */
+#define     HD64465_KBISR_KBRDF   	 0x0001    /* KB Received Data Full */
+#define HD64465_REG_MSCSR   	0x1dc10 /* Mouse Control/Status reg */
+#define HD64465_REG_MSISR   	0x1dc14 /* Mouse Interrupt Status reg */
+
+
+/*
+ * Logical address at which the HD64465 is mapped.  Note that this
+ * should always be in the P2 segment (uncached and untranslated).
+ */
+#ifndef CONFIG_HD64465_IOBASE
+#define CONFIG_HD64465_IOBASE	0xb0000000
+#endif
+/*
+ * The HD64465 multiplexes all its modules' interrupts onto
+ * this single interrupt.
+ */
+#ifndef CONFIG_HD64465_IRQ
+#define CONFIG_HD64465_IRQ	5
+#endif
+
+
+#define _HD64465_IO_MASK	0xf8000000
+#define is_hd64465_addr(addr) \
+	((addr & _HD64465_IO_MASK) == (CONFIG_HD64465_IOBASE & _HD64465_IO_MASK))
+
+/*
+ * A range of 16 virtual interrupts generated by
+ * demuxing the HD64465 muxed interrupt.
+ */
+#define HD64465_IRQ_BASE	OFFCHIP_IRQ_BASE
+#define HD64465_IRQ_NUM 	16
+#define HD64465_IRQ_ADC     	(HD64465_IRQ_BASE+0)
+#define HD64465_IRQ_USB     	(HD64465_IRQ_BASE+1)
+#define HD64465_IRQ_SCDI    	(HD64465_IRQ_BASE+2)
+#define HD64465_IRQ_PARALLEL	(HD64465_IRQ_BASE+3)
+/* bit 4 is reserved */
+#define HD64465_IRQ_UART    	(HD64465_IRQ_BASE+5)
+#define HD64465_IRQ_IRDA    	(HD64465_IRQ_BASE+6)
+#define HD64465_IRQ_PS2MOUSE	(HD64465_IRQ_BASE+7)
+#define HD64465_IRQ_KBC     	(HD64465_IRQ_BASE+8)
+#define HD64465_IRQ_TIMER1   	(HD64465_IRQ_BASE+9)
+#define HD64465_IRQ_TIMER0  	(HD64465_IRQ_BASE+10)
+#define HD64465_IRQ_GPIO    	(HD64465_IRQ_BASE+11)
+#define HD64465_IRQ_AFE     	(HD64465_IRQ_BASE+12)
+#define HD64465_IRQ_PCMCIA1 	(HD64465_IRQ_BASE+13)
+#define HD64465_IRQ_PCMCIA0 	(HD64465_IRQ_BASE+14)
+#define HD64465_IRQ_PS2KBD     	(HD64465_IRQ_BASE+15)
+
+/* Constants for PCMCIA mappings */
+#define HD64465_PCC_WINDOW	0x01000000
+
+#define HD64465_PCC0_BASE	0xb8000000	/* area 6 */
+#define HD64465_PCC0_ATTR	(HD64465_PCC0_BASE)
+#define HD64465_PCC0_COMM	(HD64465_PCC0_BASE+HD64465_PCC_WINDOW)
+#define HD64465_PCC0_IO		(HD64465_PCC0_BASE+2*HD64465_PCC_WINDOW)
+
+#define HD64465_PCC1_BASE	0xb4000000	/* area 5 */
+#define HD64465_PCC1_ATTR	(HD64465_PCC1_BASE)
+#define HD64465_PCC1_COMM	(HD64465_PCC1_BASE+HD64465_PCC_WINDOW)
+#define HD64465_PCC1_IO		(HD64465_PCC1_BASE+2*HD64465_PCC_WINDOW)
+
+/*
+ * Base of USB controller interface (as memory)
+ */
+#define HD64465_USB_BASE    	(CONFIG_HD64465_IOBASE+0xb000)
+#define HD64465_USB_LEN    	0x1000
+/*
+ * Base of embedded SRAM, used for USB controller.
+ */
+#define HD64465_SRAM_BASE    	(CONFIG_HD64465_IOBASE+0x9000)
+#define HD64465_SRAM_LEN    	0x1000
+
+
+
+#endif /* _ASM_SH_HD64465_  */
diff -urN linux-2.4.4-pre2/include/asm-sh/hd64465_gpio.h linux/include/asm-sh/hd64465_gpio.h
--- linux-2.4.4-pre2/include/asm-sh/hd64465_gpio.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/hd64465_gpio.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,47 @@
+#ifndef _ASM_SH_HD64465_GPIO_
+#define _ASM_SH_HD64465_GPIO_ 1
+/*
+ * $Id: hd64465_gpio.h,v 1.1 2001/01/02 15:35:22 mjd Exp $
+ *
+ * Hitachi HD64465 companion chip: General Purpose IO pins support.
+ * This layer enables other device drivers to configure GPIO
+ * pins, get and set their values, and register an interrupt
+ * routine for when input pins change in hardware.
+ *
+ * by Greg Banks <gbanks@pocketpenguins.com>
+ * (c) 2000 PocketPenguins Inc.
+ */
+#include <linux/config.h>
+#include <asm/hd64465.h>
+
+/* Macro to construct a portpin number (used in all
+ * subsequent functions) from a port letter and a pin
+ * number, e.g. HD64465_GPIO_PORTPIN('A', 5).
+ */
+#define HD64465_GPIO_PORTPIN(port,pin)	(((port)-'A')<<3|(pin))
+
+/* Pin configuration constants for _configure() */
+#define HD64465_GPIO_FUNCTION2	0	/* use the pin's *other* function */
+#define HD64465_GPIO_OUT	1	/* output */
+#define HD64465_GPIO_IN_PULLUP	2	/* input, pull-up MOS on */
+#define HD64465_GPIO_IN		3	/* input */
+
+/* Configure a pin's direction */
+extern void hd64465_gpio_configure(int portpin, int direction);
+
+/* Get, set value */
+extern void hd64465_gpio_set_pin(int portpin, unsigned int value);
+extern unsigned int hd64465_gpio_get_pin(int portpin);
+extern void hd64465_gpio_set_port(int port, unsigned int value);
+extern unsigned int hd64465_gpio_get_port(int port);
+
+/* mode constants for _register_irq() */
+#define HD64465_GPIO_FALLING	0
+#define HD64465_GPIO_RISING	1
+
+/* Interrupt on external value change */
+extern void hd64465_gpio_register_irq(int portpin, int mode,
+	void (*handler)(int portpin, void *dev), void *dev);
+extern void hd64465_gpio_unregister_irq(int portpin);
+
+#endif /* _ASM_SH_HD64465_GPIO_  */
diff -urN linux-2.4.4-pre2/include/asm-sh/ide.h linux/include/asm-sh/ide.h
--- linux-2.4.4-pre2/include/asm-sh/ide.h	Mon Oct  2 11:57:34 2000
+++ linux/include/asm-sh/ide.h	Mon Dec  3 22:34:51 2001
@@ -28,7 +28,7 @@
 {
 	switch (base) {
 		case 0x01f0: return 77;
-		case 0x0170: return 77;
+		case 0x0170: return 78;
 		default:
 			return 0;
 	}
diff -urN linux-2.4.4-pre2/include/asm-sh/io.h linux/include/asm-sh/io.h
--- linux-2.4.4-pre2/include/asm-sh/io.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/io.h	Mon Dec  3 22:34:51 2001
@@ -35,7 +35,7 @@
  */
 
 #ifdef __KERNEL__
-#ifdef CONFIG_SH_GENERIC
+#if defined(CONFIG_SH_GENERIC) || defined(CONFIG_SH_CQREEK) || defined(CONFIG_SH_UNKNOWN)
 
 /* In a generic kernel, we always go through the machine vector.  */
 
@@ -70,7 +70,6 @@
 # define __writel(v,a)	sh_mv.mv_writel((v),(a))
 
 # define __ioremap(a,s)	sh_mv.mv_ioremap((a), (s))
-# define __ioremap_nocache(a,s)	sh_mv.mv_ioremap_nocache((a), (s))
 # define __iounmap(a)	sh_mv.mv_iounmap((a))
 
 # define __isa_port2addr(a)	sh_mv.mv_isa_port2addr(a)
@@ -110,10 +109,20 @@
 
 # if defined(CONFIG_SH_HP600)
 #  include <asm/io_hd64461.h>
-# elif defined(CONFIG_SH_OVERDRIVE)
+# elif defined(CONFIG_SH_7750_OVERDRIVE)
 #  include <asm/io_od.h>
 # elif defined(CONFIG_SH_SOLUTION_ENGINE)
 #  include <asm/io_se.h>
+# elif defined(CONFIG_SH_DMIDA) || \
+       defined(CONFIG_SH_STB1_HARP) || \
+       defined(CONFIG_SH_STB1_OVERDRIVE)
+#  include <asm/io_hd64465.h>
+# elif defined(CONFIG_SH_EC3104)
+#  include <asm/io_ec3104.h>
+# elif defined(CONFIG_SH_DREAMCAST)
+#  include <asm/io_dc.h>
+# elif defined(CONFIG_SH_CAT68701)
+#  include <asm/io_cat68701.h>
 # elif defined(CONFIG_SH_UNKNOWN)
 #  include <asm/io_unknown.h>
 # else
@@ -126,40 +135,38 @@
 #endif /* __KERNEL__ */
 
 /* These are always function calls, in both kernel and user space */
-extern unsigned int	_inb (unsigned long port);
-extern unsigned int	_inw (unsigned long port);
+extern unsigned char	_inb (unsigned long port);
+extern unsigned short	_inw (unsigned long port);
 extern unsigned int	_inl (unsigned long port);
-extern void		_outb (unsigned char b,unsigned long port);
-extern void		_outw (unsigned short w,unsigned long port);
-extern void		_outl (unsigned int l,unsigned long port);
-extern unsigned int	_inb_p (unsigned long port);
-extern unsigned int	_inw_p (unsigned long port);
+extern void		_outb (unsigned char b, unsigned long port);
+extern void		_outw (unsigned short w, unsigned long port);
+extern void		_outl (unsigned int l, unsigned long port);
+extern unsigned char	_inb_p (unsigned long port);
+extern unsigned short	_inw_p (unsigned long port);
 extern unsigned int	_inl_p (unsigned long port);
-extern void		_outb_p (unsigned char b,unsigned long port);
-extern void		_outw_p (unsigned short w,unsigned long port);
-extern void		_outl_p (unsigned int l,unsigned long port);
+extern void		_outb_p (unsigned char b, unsigned long port);
+extern void		_outw_p (unsigned short w, unsigned long port);
+extern void		_outl_p (unsigned int l, unsigned long port);
 extern void		_insb (unsigned long port, void *dst, unsigned long count);
 extern void		_insw (unsigned long port, void *dst, unsigned long count);
 extern void		_insl (unsigned long port, void *dst, unsigned long count);
 extern void		_outsb (unsigned long port, const void *src, unsigned long count);
 extern void		_outsw (unsigned long port, const void *src, unsigned long count);
 extern void		_outsl (unsigned long port, const void *src, unsigned long count);
-extern unsigned long	_readb(unsigned long addr);
-extern unsigned long	_readw(unsigned long addr);
-extern unsigned long	_readl(unsigned long addr);
+extern unsigned char	_readb(unsigned long addr);
+extern unsigned short	_readw(unsigned long addr);
+extern unsigned int	_readl(unsigned long addr);
 extern void		_writeb(unsigned char b, unsigned long addr);
 extern void		_writew(unsigned short b, unsigned long addr);
 extern void		_writel(unsigned int b, unsigned long addr);
 
 #ifdef __KERNEL__
-extern unsigned long	___raw_readb(unsigned long addr);
-extern unsigned long	___raw_readw(unsigned long addr);
-extern unsigned long	___raw_readl(unsigned long addr);
-extern unsigned long	___raw_readq(unsigned long addr);
+extern unsigned char	___raw_readb(unsigned long addr);
+extern unsigned short	___raw_readw(unsigned long addr);
+extern unsigned int	___raw_readl(unsigned long addr);
 extern void		___raw_writeb(unsigned char b, unsigned long addr);
 extern void		___raw_writew(unsigned short b, unsigned long addr);
 extern void		___raw_writel(unsigned int b, unsigned long addr);
-extern void		___raw_writeq(unsigned long b, unsigned long addr);
 #endif
 
 #ifdef __KERNEL__
@@ -291,20 +298,20 @@
 
 /* Userspace declarations.  */
 
-extern unsigned int	inb(unsigned long port);
-extern unsigned int	inw(unsigned long port);
+extern unsigned char	inb(unsigned long port);
+extern unsigned short	inw(unsigned long port);
 extern unsigned int	inl(unsigned long port);
-extern void		outb(unsigned char b,unsigned long port);
-extern void		outw(unsigned short w,unsigned long port);
-extern void		outl(unsigned int l,unsigned long port);
-extern void		insb (unsigned long port, void *dst, unsigned long count);
-extern void		insw (unsigned long port, void *dst, unsigned long count);
-extern void		insl (unsigned long port, void *dst, unsigned long count);
-extern void		outsb (unsigned long port, const void *src, unsigned long count);
-extern void		outsw (unsigned long port, const void *src, unsigned long count);
-extern void		outsl (unsigned long port, const void *src, unsigned long count);
-extern unsigned long	readb(unsigned long addr);
-extern unsigned long	readw(unsigned long addr);
+extern void		outb(unsigned char b, unsigned long port);
+extern void		outw(unsigned short w, unsigned long port);
+extern void		outl(unsigned int l, unsigned long port);
+extern void		insb(unsigned long port, void *dst, unsigned long count);
+extern void		insw(unsigned long port, void *dst, unsigned long count);
+extern void		insl(unsigned long port, void *dst, unsigned long count);
+extern void		outsb(unsigned long port, const void *src, unsigned long count);
+extern void		outsw(unsigned long port, const void *src, unsigned long count);
+extern void		outsl(unsigned long port, const void *src, unsigned long count);
+extern unsigned char	readb(unsigned long addr);
+extern unsigned short	readw(unsigned long addr);
 extern unsigned long	readl(unsigned long addr);
 extern void		writeb(unsigned char b, unsigned long addr);
 extern void		writew(unsigned short b, unsigned long addr);
@@ -342,17 +349,17 @@
 extern void memset_io(unsigned long, int, unsigned long);
 
 /* SuperH on-chip I/O functions */
-extern __inline__ unsigned long ctrl_inb(unsigned long addr)
+extern __inline__ unsigned char ctrl_inb(unsigned long addr)
 {
 	return *(volatile unsigned char*)addr;
 }
 
-extern __inline__ unsigned long ctrl_inw(unsigned long addr)
+extern __inline__ unsigned short ctrl_inw(unsigned long addr)
 {
 	return *(volatile unsigned short*)addr;
 }
 
-extern __inline__ unsigned long ctrl_inl(unsigned long addr)
+extern __inline__ unsigned int ctrl_inl(unsigned long addr)
 {
 	return *(volatile unsigned long*)addr;
 }
@@ -412,20 +419,12 @@
 	return __ioremap(offset, size);
 }
 
-/*
- * This one maps high address device memory and turns off caching for that area.
- * it's useful if some control registers are in such an area and write combining
- * or read caching is not desirable:
- */
-static __inline__ void * ioremap_nocache (unsigned long offset, unsigned long size)
-{
-	return __ioremap_nocache(offset, size);
-}
-
 static __inline__ void iounmap(void *addr)
 {
 	return __iounmap(addr);
 }
+
+#define ioremap_nocache(off,size) ioremap(off,size)
 
 static __inline__ int check_signature(unsigned long io_addr,
 			const unsigned char *signature, int length)
diff -urN linux-2.4.4-pre2/include/asm-sh/io_cat68701.h linux/include/asm-sh/io_cat68701.h
--- linux-2.4.4-pre2/include/asm-sh/io_cat68701.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/io_cat68701.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,89 @@
+/*
+ * include/asm-sh/io_cat68701.h
+ *
+ * Copyright 2000 Stuart Menefy (stuart.menefy@st.com)
+ *           2001 Yutarou Ebihar (ebihara@si-linux.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * IO functions for an AONE Corp. CAT-68701 SH7708 Borad
+ */
+
+#ifndef _ASM_SH_IO_CAT68701_H
+#define _ASM_SH_IO_CAT68701_H
+
+#include <asm/io_generic.h>
+
+extern unsigned char cat68701_inb(unsigned long port);
+extern unsigned short cat68701_inw(unsigned long port);
+extern unsigned int cat68701_inl(unsigned long port);
+
+extern void cat68701_outb(unsigned char value, unsigned long port);
+extern void cat68701_outw(unsigned short value, unsigned long port);
+extern void cat68701_outl(unsigned int value, unsigned long port);
+
+extern unsigned char cat68701_inb_p(unsigned long port);
+extern void cat68701_outb_p(unsigned char value, unsigned long port);
+
+extern void cat68701_insb(unsigned long port, void *addr, unsigned long count);
+extern void cat68701_insw(unsigned long port, void *addr, unsigned long count);
+extern void cat68701_insl(unsigned long port, void *addr, unsigned long count);
+extern void cat68701_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void cat68701_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void cat68701_outsl(unsigned long port, const void *addr, unsigned long count);
+
+extern unsigned char cat68701_readb(unsigned long addr);
+extern unsigned short cat68701_readw(unsigned long addr);
+extern unsigned int cat68701_readl(unsigned long addr);
+extern void cat68701_writeb(unsigned char b, unsigned long addr);
+extern void cat68701_writew(unsigned short b, unsigned long addr);
+extern void cat68701_writel(unsigned int b, unsigned long addr);
+
+extern void * cat68701_ioremap(unsigned long offset, unsigned long size);
+extern void cat68701_iounmap(void *addr);
+
+extern unsigned long cat68701_isa_port2addr(unsigned long offset);
+extern int cat68701_irq_demux(int irq);
+
+extern void setup_cat68701(void);
+extern void init_cat68701_IRQ(void);
+extern void heartbeat_cat68701(void);
+
+#ifdef __WANT_IO_DEF
+
+# define __inb			cat68701_inb
+# define __inw			cat68701_inw
+# define __inl			cat68701_inl
+# define __outb			cat68701_outb
+# define __outw			cat68701_outw
+# define __outl			cat68701_outl
+
+# define __inb_p		cat68701_inb_p
+# define __inw_p		cat68701_inw
+# define __inl_p		cat68701_inl
+# define __outb_p		cat68701_outb_p
+# define __outw_p		cat68701_outw
+# define __outl_p		cat68701_outl
+
+# define __insb			cat68701_insb
+# define __insw			cat68701_insw
+# define __insl			cat68701_insl
+# define __outsb		cat68701_outsb
+# define __outsw		cat68701_outsw
+# define __outsl		cat68701_outsl
+
+# define __readb		cat68701_readb
+# define __readw		cat68701_readw
+# define __readl		cat68701_readl
+# define __writeb		cat68701_writeb
+# define __writew		cat68701_writew
+# define __writel		cat68701_writel
+
+# define __isa_port2addr	cat68701_isa_port2addr
+# define __ioremap		generic_ioremap
+# define __iounmap		generic_iounmap
+
+#endif
+
+#endif /* _ASM_SH_IO_CAT68701_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/io_dc.h linux/include/asm-sh/io_dc.h
--- linux-2.4.4-pre2/include/asm-sh/io_dc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/io_dc.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,49 @@
+/*
+ *	$Id: io_dc.h,v 1.1 2001/04/01 15:02:56 yaegashi Exp $
+ *	IO functions for SEGA Dreamcast
+ */
+
+#ifndef _ASM_SH_IO_DREAMCAST_H
+#define _ASM_SH_IO_DREAMCAST_H
+
+#include <asm/io_generic.h>
+
+unsigned long dreamcast_isa_port2addr(unsigned long offset);
+
+#ifdef __WANT_IO_DEF
+
+# define __inb			generic_inb
+# define __inw			generic_inw
+# define __inl			generic_inl
+# define __outb			generic_outb
+# define __outw			generic_outw
+# define __outl			generic_outl
+
+# define __inb_p		generic_inb_p
+# define __inw_p		generic_inw
+# define __inl_p		generic_inl
+# define __outb_p		generic_outb_p
+# define __outw_p		generic_outw
+# define __outl_p		generic_outl
+
+# define __insb			generic_insb
+# define __insw			generic_insw
+# define __insl			generic_insl
+# define __outsb		generic_outsb
+# define __outsw		generic_outsw
+# define __outsl		generic_outsl
+
+# define __readb		generic_readb
+# define __readw		generic_readw
+# define __readl		generic_readl
+# define __writeb		generic_writeb
+# define __writew		generic_writew
+# define __writel		generic_writel
+
+# define __isa_port2addr	dreamcast_isa_port2addr
+# define __ioremap		generic_ioremap
+# define __iounmap		generic_iounmap
+
+#endif
+
+#endif /* _ASM_SH_IO_DREAMCAST_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/io_ec3104.h linux/include/asm-sh/io_ec3104.h
--- linux-2.4.4-pre2/include/asm-sh/io_ec3104.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/io_ec3104.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,54 @@
+#ifndef _ASM_SH_IO_EC3104_H
+#define _ASM_SH_IO_EC3104_H
+
+#include <asm/io_generic.h>
+#include <linux/types.h>
+
+extern unsigned char ec3104_inb(unsigned long port);
+extern unsigned short ec3104_inw(unsigned long port);
+extern unsigned long ec3104_inl(unsigned long port);
+
+extern void ec3104_outb(unsigned char value, unsigned long port);
+extern void ec3104_outw(unsigned short value, unsigned long port);
+extern void ec3104_outl(unsigned long value, unsigned long port);
+
+extern int ec3104_irq_demux(int irq);
+
+#ifdef __WANT_IO_DEF
+
+# define __inb			ec3104_inb
+# define __inw			ec3104_inw
+# define __inl			ec3104_inl
+# define __outb			ec3104_outb
+# define __outw			ec3104_outw
+# define __outl			ec3104_outl
+
+# define __inb_p		ec3104_inb
+# define __inw_p		ec3104_inw
+# define __inl_p		ec3104_inl
+# define __outb_p		ec3104_outb
+# define __outw_p		ec3104_outw
+# define __outl_p		ec3104_outl
+
+# define __insb			generic_insb
+# define __insw			generic_insw
+# define __insl			generic_insl
+# define __outsb		generic_outsb
+# define __outsw		generic_outsw
+# define __outsl		generic_outsl
+
+# define __readb		generic_readb
+# define __readw		generic_readw
+# define __readl		generic_readl
+# define __writeb		generic_writeb
+# define __writew		generic_writew
+# define __writel		generic_writel
+
+# define __isa_port2addr	generic_isa_port2addr
+# define __ioremap		generic_ioremap
+# define __ioremap_nocache	generic_ioremap_nocache
+# define __iounmap		generic_iounmap
+
+#endif
+
+#endif /* _ASM_SH_IO_EC3104_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/io_generic.h linux/include/asm-sh/io_generic.h
--- linux-2.4.4-pre2/include/asm-sh/io_generic.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/io_generic.h	Mon Dec  3 22:34:51 2001
@@ -14,37 +14,36 @@
 
 extern unsigned long generic_io_base;
 
-extern unsigned long generic_inb(unsigned int port);
-extern unsigned long generic_inw(unsigned int port);
-extern unsigned long generic_inl(unsigned int port);
+extern unsigned char generic_inb(unsigned long port);
+extern unsigned short generic_inw(unsigned long port);
+extern unsigned int generic_inl(unsigned long port);
 
-extern void generic_outb(unsigned long value, unsigned int port);
-extern void generic_outw(unsigned long value, unsigned int port);
-extern void generic_outl(unsigned long value, unsigned int port);
+extern void generic_outb(unsigned char value, unsigned long port);
+extern void generic_outw(unsigned short value, unsigned long port);
+extern void generic_outl(unsigned int value, unsigned long port);
 
-extern unsigned long generic_inb_p(unsigned int port);
-extern unsigned long generic_inw_p(unsigned int port);
-extern unsigned long generic_inl_p(unsigned int port);
-extern void generic_outb_p(unsigned long value, unsigned int port);
-extern void generic_outw_p(unsigned long value, unsigned int port);
-extern void generic_outl_p(unsigned long value, unsigned int port);
+extern unsigned char generic_inb_p(unsigned long port);
+extern unsigned short generic_inw_p(unsigned long port);
+extern unsigned int generic_inl_p(unsigned long port);
+extern void generic_outb_p(unsigned char value, unsigned long port);
+extern void generic_outw_p(unsigned short value, unsigned long port);
+extern void generic_outl_p(unsigned int value, unsigned long port);
 
-extern void generic_insb(unsigned int port, void *addr, unsigned long count);
-extern void generic_insw(unsigned int port, void *addr, unsigned long count);
-extern void generic_insl(unsigned int port, void *addr, unsigned long count);
-extern void generic_outsb(unsigned int port, const void *addr, unsigned long count);
-extern void generic_outsw(unsigned int port, const void *addr, unsigned long count);
-extern void generic_outsl(unsigned int port, const void *addr, unsigned long count);
+extern void generic_insb(unsigned long port, void *addr, unsigned long count);
+extern void generic_insw(unsigned long port, void *addr, unsigned long count);
+extern void generic_insl(unsigned long port, void *addr, unsigned long count);
+extern void generic_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void generic_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void generic_outsl(unsigned long port, const void *addr, unsigned long count);
 
-extern unsigned long generic_readb(unsigned long addr);
-extern unsigned long generic_readw(unsigned long addr);
-extern unsigned long generic_readl(unsigned long addr);
+extern unsigned char generic_readb(unsigned long addr);
+extern unsigned short generic_readw(unsigned long addr);
+extern unsigned int generic_readl(unsigned long addr);
 extern void generic_writeb(unsigned char b, unsigned long addr);
 extern void generic_writew(unsigned short b, unsigned long addr);
 extern void generic_writel(unsigned int b, unsigned long addr);
 
 extern void *generic_ioremap(unsigned long offset, unsigned long size);
-extern void *generic_ioremap_nocache (unsigned long offset, unsigned long size);
 extern void generic_iounmap(void *addr);
 
 extern unsigned long generic_isa_port2addr(unsigned long offset);
diff -urN linux-2.4.4-pre2/include/asm-sh/io_hd64461.h linux/include/asm-sh/io_hd64461.h
--- linux-2.4.4-pre2/include/asm-sh/io_hd64461.h	Mon Oct  2 11:57:34 2000
+++ linux/include/asm-sh/io_hd64461.h	Mon Dec  3 22:34:51 2001
@@ -14,23 +14,24 @@
 
 #include <asm/io_generic.h>
 
-extern unsigned long hd64461_inb(unsigned int port);
-extern unsigned long hd64461_inw(unsigned int port);
-extern unsigned long hd64461_inl(unsigned int port);
-
-extern void hd64461_outb(unsigned long value, unsigned int port);
-extern void hd64461_outw(unsigned long value, unsigned int port);
-extern void hd64461_outl(unsigned long value, unsigned int port);
-
-extern unsigned long hd64461_inb_p(unsigned int port);
-extern void hd64461_outb_p(unsigned long value, unsigned int port);
-
-extern void hd64461_insb(unsigned int port, void *addr, unsigned long count);
-extern void hd64461_insw(unsigned int port, void *addr, unsigned long count);
-extern void hd64461_insl(unsigned int port, void *addr, unsigned long count);
-extern void hd64461_outsb(unsigned int port, const void *addr, unsigned long count);
-extern void hd64461_outsw(unsigned int port, const void *addr, unsigned long count);
-extern void hd64461_outsl(unsigned int port, const void *addr, unsigned long count);
+extern unsigned char hd64461_inb(unsigned long port);
+extern unsigned short hd64461_inw(unsigned long port);
+extern unsigned int hd64461_inl(unsigned long port);
+
+extern void hd64461_outb(unsigned char value, unsigned long port);
+extern void hd64461_outw(unsigned short value, unsigned long port);
+extern void hd64461_outl(unsigned int value, unsigned long port);
+
+extern unsigned char hd64461_inb_p(unsigned long port);
+extern void hd64461_outb_p(unsigned char value, unsigned long port);
+
+extern void hd64461_insb(unsigned long port, void *addr, unsigned long count);
+extern void hd64461_insw(unsigned long port, void *addr, unsigned long count);
+extern void hd64461_insl(unsigned long port, void *addr, unsigned long count);
+extern void hd64461_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void hd64461_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void hd64461_outsl(unsigned long port, const void *addr, unsigned long count);
+extern int hd64461_irq_demux(int irq);
 
 #ifdef __WANT_IO_DEF
 
@@ -64,6 +65,7 @@
 
 # define __isa_port2addr	generic_isa_port2addr
 # define __ioremap		generic_ioremap
+# define __ioremap_nocache	generic_ioremap_nocache
 # define __iounmap		generic_iounmap
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-sh/io_hd64465.h linux/include/asm-sh/io_hd64465.h
--- linux-2.4.4-pre2/include/asm-sh/io_hd64465.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/io_hd64465.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,90 @@
+/*
+ * include/asm-sh/io_hd64465.h
+ *
+ * By Greg Banks <gbanks@pocketpenguins.com>
+ * (c) 2000 PocketPenguins Inc.
+ *
+ * Derived from io_hd64461.h, which bore the message:
+ * Copyright 2000 Stuart Menefy (stuart.menefy@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * IO functions for an HD64465 "Windows CE Intelligent Peripheral Controller".
+ */
+
+#ifndef _ASM_SH_IO_HD64465_H
+#define _ASM_SH_IO_HD64465_H
+
+#include <asm/io_generic.h>
+
+extern unsigned char hd64465_inb(unsigned long port);
+extern unsigned short hd64465_inw(unsigned long port);
+extern unsigned int hd64465_inl(unsigned long port);
+
+extern void hd64465_outb(unsigned char value, unsigned long port);
+extern void hd64465_outw(unsigned short value, unsigned long port);
+extern void hd64465_outl(unsigned int value, unsigned long port);
+
+extern unsigned char hd64465_inb_p(unsigned long port);
+extern void hd64465_outb_p(unsigned char value, unsigned long port);
+
+extern void hd64465_insb(unsigned long port, void *addr, unsigned long count);
+extern void hd64465_insw(unsigned long port, void *addr, unsigned long count);
+extern void hd64465_insl(unsigned long port, void *addr, unsigned long count);
+extern void hd64465_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void hd64465_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void hd64465_outsl(unsigned long port, const void *addr, unsigned long count);
+extern unsigned long hd64465_isa_port2addr(unsigned long offset);
+extern int hd64465_irq_demux(int irq);
+/* Provision for generic secondary demux step -- used by PCMCIA code */
+extern void hd64465_register_irq_demux(int irq,
+		int (*demux)(int irq, void *dev), void *dev);
+extern void hd64465_unregister_irq_demux(int irq);
+/* Set this variable to 1 to see port traffic */
+extern int hd64465_io_debug;
+/* Map a range of ports to a range of kernel virtual memory.
+ */
+extern void hd64465_port_map(unsigned short baseport, unsigned int nports,
+			     unsigned long addr, unsigned char shift);
+extern void hd64465_port_unmap(unsigned short baseport, unsigned int nports);
+
+
+#ifdef __WANT_IO_DEF
+
+# define __inb			hd64465_inb
+# define __inw			hd64465_inw
+# define __inl			hd64465_inl
+# define __outb			hd64465_outb
+# define __outw			hd64465_outw
+# define __outl			hd64465_outl
+
+# define __inb_p		hd64465_inb_p
+# define __inw_p		hd64465_inw
+# define __inl_p		hd64465_inl
+# define __outb_p		hd64465_outb_p
+# define __outw_p		hd64465_outw
+# define __outl_p		hd64465_outl
+
+# define __insb			hd64465_insb
+# define __insw			hd64465_insw
+# define __insl			hd64465_insl
+# define __outsb		hd64465_outsb
+# define __outsw		hd64465_outsw
+# define __outsl		hd64465_outsl
+
+# define __readb		generic_readb
+# define __readw		generic_readw
+# define __readl		generic_readl
+# define __writeb		generic_writeb
+# define __writew		generic_writew
+# define __writel		generic_writel
+
+# define __isa_port2addr	hd64465_isa_port2addr
+# define __ioremap		generic_ioremap
+# define __iounmap		generic_iounmap
+
+
+#endif
+
+#endif /* _ASM_SH_IO_HD64465_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/io_od.h linux/include/asm-sh/io_od.h
--- linux-2.4.4-pre2/include/asm-sh/io_od.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/io_od.h	Mon Dec  3 22:34:51 2001
@@ -14,27 +14,27 @@
 
 #include <asm/io_generic.h>
 
-extern unsigned long od_inb(unsigned int port);
-extern unsigned long od_inw(unsigned int port);
-extern unsigned long od_inl(unsigned int port);
-
-extern void od_outb(unsigned long value, unsigned int port);
-extern void od_outw(unsigned long value, unsigned int port);
-extern void od_outl(unsigned long value, unsigned int port);
-
-extern unsigned long od_inb_p(unsigned int port);
-extern unsigned long od_inw_p(unsigned int port);
-extern unsigned long od_inl_p(unsigned int port);
-extern void od_outb_p(unsigned long value, unsigned int port);
-extern void od_outw_p(unsigned long value, unsigned int port);
-extern void od_outl_p(unsigned long value, unsigned int port);
-
-extern void od_insb(unsigned int port, void *addr, unsigned long count);
-extern void od_insw(unsigned int port, void *addr, unsigned long count);
-extern void od_insl(unsigned int port, void *addr, unsigned long count);
-extern void od_outsb(unsigned int port, const void *addr, unsigned long count);
-extern void od_outsw(unsigned int port, const void *addr, unsigned long count);
-extern void od_outsl(unsigned int port, const void *addr, unsigned long count);
+extern unsigned char od_inb(unsigned long port);
+extern unsigned short od_inw(unsigned long port);
+extern unsigned int od_inl(unsigned long port);
+
+extern void od_outb(unsigned char value, unsigned long port);
+extern void od_outw(unsigned short value, unsigned long port);
+extern void od_outl(unsigned int value, unsigned long port);
+
+extern unsigned char od_inb_p(unsigned long port);
+extern unsigned short od_inw_p(unsigned long port);
+extern unsigned int od_inl_p(unsigned long port);
+extern void od_outb_p(unsigned char value, unsigned long port);
+extern void od_outw_p(unsigned short value, unsigned long port);
+extern void od_outl_p(unsigned int value, unsigned long port);
+
+extern void od_insb(unsigned long port, void *addr, unsigned long count);
+extern void od_insw(unsigned long port, void *addr, unsigned long count);
+extern void od_insl(unsigned long port, void *addr, unsigned long count);
+extern void od_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void od_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void od_outsl(unsigned long port, const void *addr, unsigned long count);
 
 extern unsigned long od_isa_port2addr(unsigned long offset);
 
@@ -70,7 +70,6 @@
 
 # define __isa_port2addr	od_isa_port2addr
 # define __ioremap		generic_ioremap
-# define __ioremap_nocache	generic_ioremap_nocache
 # define __iounmap		generic_iounmap
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-sh/io_se.h linux/include/asm-sh/io_se.h
--- linux-2.4.4-pre2/include/asm-sh/io_se.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/io_se.h	Mon Dec  3 22:34:51 2001
@@ -14,27 +14,27 @@
 
 #include <asm/io_generic.h>
 
-extern unsigned long se_inb(unsigned int port);
-extern unsigned long se_inw(unsigned int port);
-extern unsigned long se_inl(unsigned int port);
-
-extern void se_outb(unsigned long value, unsigned int port);
-extern void se_outw(unsigned long value, unsigned int port);
-extern void se_outl(unsigned long value, unsigned int port);
-
-extern unsigned long se_inb_p(unsigned int port);
-extern void se_outb_p(unsigned long value, unsigned int port);
-
-extern void se_insb(unsigned int port, void *addr, unsigned long count);
-extern void se_insw(unsigned int port, void *addr, unsigned long count);
-extern void se_insl(unsigned int port, void *addr, unsigned long count);
-extern void se_outsb(unsigned int port, const void *addr, unsigned long count);
-extern void se_outsw(unsigned int port, const void *addr, unsigned long count);
-extern void se_outsl(unsigned int port, const void *addr, unsigned long count);
-
-extern unsigned long se_readb(unsigned long addr);
-extern unsigned long se_readw(unsigned long addr);
-extern unsigned long se_readl(unsigned long addr);
+extern unsigned char se_inb(unsigned long port);
+extern unsigned short se_inw(unsigned long port);
+extern unsigned int se_inl(unsigned long port);
+
+extern void se_outb(unsigned char value, unsigned long port);
+extern void se_outw(unsigned short value, unsigned long port);
+extern void se_outl(unsigned int value, unsigned long port);
+
+extern unsigned char se_inb_p(unsigned long port);
+extern void se_outb_p(unsigned char value, unsigned long port);
+
+extern void se_insb(unsigned long port, void *addr, unsigned long count);
+extern void se_insw(unsigned long port, void *addr, unsigned long count);
+extern void se_insl(unsigned long port, void *addr, unsigned long count);
+extern void se_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void se_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void se_outsl(unsigned long port, const void *addr, unsigned long count);
+
+extern unsigned char se_readb(unsigned long addr);
+extern unsigned short se_readw(unsigned long addr);
+extern unsigned int se_readl(unsigned long addr);
 extern void se_writeb(unsigned char b, unsigned long addr);
 extern void se_writew(unsigned short b, unsigned long addr);
 extern void se_writel(unsigned int b, unsigned long addr);
@@ -73,7 +73,6 @@
 
 # define __isa_port2addr	se_isa_port2addr
 # define __ioremap		generic_ioremap
-# define __ioremap_nocache	generic_ioremap_nocache
 # define __iounmap		generic_iounmap
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-sh/io_unknown.h linux/include/asm-sh/io_unknown.h
--- linux-2.4.4-pre2/include/asm-sh/io_unknown.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/io_unknown.h	Mon Dec  3 22:34:51 2001
@@ -12,38 +12,37 @@
 #ifndef _ASM_SH_IO_UNKNOWN_H
 #define _ASM_SH_IO_UNKNOWN_H
 
-extern unsigned long unknown_inb(unsigned int port);
-extern unsigned long unknown_inw(unsigned int port);
-extern unsigned long unknown_inl(unsigned int port);
-
-extern void unknown_outb(unsigned long value, unsigned int port);
-extern void unknown_outw(unsigned long value, unsigned int port);
-extern void unknown_outl(unsigned long value, unsigned int port);
-
-extern unsigned long unknown_inb_p(unsigned int port);
-extern unsigned long unknown_inw_p(unsigned int port);
-extern unsigned long unknown_inl_p(unsigned int port);
-extern void unknown_outb_p(unsigned long value, unsigned int port);
-extern void unknown_outw_p(unsigned long value, unsigned int port);
-extern void unknown_outl_p(unsigned long value, unsigned int port);
-
-extern void unknown_insb(unsigned int port, void *addr, unsigned long count);
-extern void unknown_insw(unsigned int port, void *addr, unsigned long count);
-extern void unknown_insl(unsigned int port, void *addr, unsigned long count);
-extern void unknown_outsb(unsigned int port, const void *addr, unsigned long count);
-extern void unknown_outsw(unsigned int port, const void *addr, unsigned long count);
-extern void unknown_outsl(unsigned int port, const void *addr, unsigned long count);
-
-extern unsigned long unknown_readb(unsigned long addr);
-extern unsigned long unknown_readw(unsigned long addr);
-extern unsigned long unknown_readl(unsigned long addr);
+extern unsigned char unknown_inb(unsigned long port);
+extern unsigned short unknown_inw(unsigned long port);
+extern unsigned int unknown_inl(unsigned long port);
+
+extern void unknown_outb(unsigned char value, unsigned long port);
+extern void unknown_outw(unsigned short value, unsigned long port);
+extern void unknown_outl(unsigned int value, unsigned long port);
+
+extern unsigned char unknown_inb_p(unsigned long port);
+extern unsigned short unknown_inw_p(unsigned long port);
+extern unsigned int unknown_inl_p(unsigned long port);
+extern void unknown_outb_p(unsigned char value, unsigned long port);
+extern void unknown_outw_p(unsigned short value, unsigned long port);
+extern void unknown_outl_p(unsigned int value, unsigned long port);
+
+extern void unknown_insb(unsigned long port, void *addr, unsigned long count);
+extern void unknown_insw(unsigned long port, void *addr, unsigned long count);
+extern void unknown_insl(unsigned long port, void *addr, unsigned long count);
+extern void unknown_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void unknown_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void unknown_outsl(unsigned long port, const void *addr, unsigned long count);
+
+extern unsigned char unknown_readb(unsigned long addr);
+extern unsigned short unknown_readw(unsigned long addr);
+extern unsigned int unknown_readl(unsigned long addr);
 extern void unknown_writeb(unsigned char b, unsigned long addr);
 extern void unknown_writew(unsigned short b, unsigned long addr);
 extern void unknown_writel(unsigned int b, unsigned long addr);
 
 extern unsigned long unknown_isa_port2addr(unsigned long offset);
-extern void * unknown_ioremap(unsigned long offset, unsigned long size);
-extern void * unknown_ioremap_nocache (unsigned long offset, unsigned long size);
+extern void *unknown_ioremap(unsigned long offset, unsigned long size);
 extern void unknown_iounmap(void *addr);
 
 #ifdef __WANT_IO_DEF
@@ -78,7 +77,6 @@
 
 # define __isa_port2addr	unknown_isa_port2addr
 # define __ioremap		unknown_ioremap
-# define __ioremap_nocache	unknown_ioremap_nocache
 # define __iounmap		unknown_iounmap
 
 #endif
diff -urN linux-2.4.4-pre2/include/asm-sh/irq.h linux/include/asm-sh/irq.h
--- linux-2.4.4-pre2/include/asm-sh/irq.h	Mon Oct  2 11:57:34 2000
+++ linux/include/asm-sh/irq.h	Mon Dec  3 22:34:51 2001
@@ -12,6 +12,7 @@
 
 #include <linux/config.h>
 #include <asm/machvec.h>
+#include <asm/ptrace.h>		/* for pt_regs */
 
 #if defined(__sh3__)
 #define INTC_IPRA  	0xfffffee2UL
@@ -32,12 +33,24 @@
 #define RTC_IPR_POS	 0
 #define RTC_PRIORITY	TIMER_PRIORITY
 
+#define DMTE0_IRQ	34
+#define DMTE1_IRQ	35
+#define DMTE2_IRQ	36
+#define DMTE3_IRQ	37
+#define DMAE_IRQ	38
+#define DMA_IPR_ADDR	INTC_IPRC
+#define DMA_IPR_POS	2
+#define DMA_PRIORITY	7
+
+#if defined (CONFIG_CPU_SUBTYPE_SH7707) || defined (CONFIG_CPU_SUBTYPE_SH7708) || \
+    defined (CONFIG_CPU_SUBTYPE_SH7709) || defined (CONFIG_CPU_SUBTYPE_SH7750)
 #define SCI_ERI_IRQ	23
 #define SCI_RXI_IRQ	24
 #define SCI_TXI_IRQ	25
 #define SCI_IPR_ADDR	INTC_IPRB
 #define SCI_IPR_POS	1
 #define SCI_PRIORITY	3
+#endif
 
 #if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
 #define SCIF_ERI_IRQ	56
@@ -55,7 +68,7 @@
 #define IRDA_IPR_ADDR	INTC_IPRE
 #define IRDA_IPR_POS	2
 #define IRDA_PRIORITY	3
-#elif defined(CONFIG_CPU_SUBTYPE_SH7750)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_ST40STB1)
 #define SCIF_ERI_IRQ	40
 #define SCIF_RXI_IRQ	41
 #define SCIF_BRI_IRQ	42
@@ -63,38 +76,87 @@
 #define SCIF_IPR_ADDR	INTC_IPRC
 #define SCIF_IPR_POS	1
 #define SCIF_PRIORITY	3
+#if defined(CONFIG_CPU_SUBTYPE_ST40STB1)
+#define SCIF1_ERI_IRQ	23
+#define SCIF1_RXI_IRQ	24
+#define SCIF1_BRI_IRQ	25
+#define SCIF1_TXI_IRQ	26
+#define SCIF1_IPR_ADDR	INTC_IPRB
+#define SCIF1_IPR_POS	1
+#define SCIF1_PRIORITY	3
+#endif
 #endif
 
-#ifdef CONFIG_SH_GENERIC
-/* In a generic kernel, NR_IRQS is an upper bound, and we should use
- * ACTUAL_NR_IRQS (which uses the machine vector) to get the correct value.
+/* NR_IRQS is made from three components:
+ *   1. ONCHIP_NR_IRQS - number of IRLS + on-chip peripherial modules
+ *   2. PINT_NR_IRQS   - number of PINT interrupts
+ *   3. OFFCHIP_NR_IRQS - numbe of IRQs from off-chip peripherial modules
  */
-#define NR_IRQS 80
-#define ACTUAL_NR_IRQS (sh_mv.mv_nr_irqs)
+
+/* 1. ONCHIP_NR_IRQS */
+#ifdef CONFIG_SH_GENERIC
+# define ONCHIP_NR_IRQS 89
 #else
-#if defined(__SH4__)
-/*
- * 48 = 32+16
- *
- * 32 for on chip support modules.
- * 16 for external interrupts.
- *
- */
-#define NR_IRQS	48
-#elif defined(CONFIG_CPU_SUBTYPE_SH7707)
-#define NR_IRQS 64
-#elif defined(CONFIG_CPU_SUBTYPE_SH7708)
-#define NR_IRQS 32
-#elif defined(CONFIG_CPU_SUBTYPE_SH7709)
-#ifdef CONFIG_HD64461
-#define NR_IRQS 80		/* HD64461_IRQBASE+16, see hd64461.h */
+# if defined(CONFIG_CPU_SUBTYPE_SH7707)
+#  define ONCHIP_NR_IRQS 64
+#  define PINT_NR_IRQS   16
+# elif defined(CONFIG_CPU_SUBTYPE_SH7708)
+#  define ONCHIP_NR_IRQS 32
+# elif defined(CONFIG_CPU_SUBTYPE_SH7709)
+#  define ONCHIP_NR_IRQS 64	// Actually 61
+#  define PINT_NR_IRQS   16
+# elif defined(CONFIG_CPU_SUBTYPE_SH7750)
+#  define ONCHIP_NR_IRQS 48	// Actually 44
+# elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
+#  define ONCHIP_NR_IRQS 89
+# endif
+#endif
+
+/* 2. PINT_NR_IRQS */
+#ifdef CONFIG_SH_GENERIC
+# define PINT_NR_IRQS 16
 #else
-#define NR_IRQS 61
+# ifndef PINT_NR_IRQS
+#  define PINT_NR_IRQS 0
+# endif
 #endif
+
+#if PINT_NR_IRQS > 0
+# define PINT_IRQ_BASE  ONCHIP_NR_IRQS
 #endif
-#define ACTUAL_NR_IRQS NR_IRQS
+
+/* 3. OFFCHIP_NR_IRQS */
+#ifdef CONFIG_SH_GENERIC
+# define OFFCHIP_NR_IRQS 16
+#else
+# if defined(CONFIG_HD64461)
+#  define OFFCHIP_NR_IRQS 16
+# elif defined(CONFIG_HD64465)
+#  define OFFCHIP_NR_IRQS 16
+# elif defined (CONFIG_SH_EC3104)
+#  define OFFCHIP_NR_IRQS 16
+# else
+#  define OFFCHIP_NR_IRQS 0
+# endif
 #endif
 
+#if OFFCHIP_NR_IRQS > 0
+# define OFFCHIP_IRQ_BASE (ONCHIP_NR_IRQS + PINT_NR_IRQS)
+#endif
+
+/* NR_IRQS. 1+2+3 */
+#define NR_IRQS (ONCHIP_NR_IRQS + PINT_NR_IRQS + OFFCHIP_NR_IRQS)
+
+/* In a generic kernel, NR_IRQS is an upper bound, and we should use
+ * ACTUAL_NR_IRQS (which uses the machine vector) to get the correct value.
+ */
+#ifdef CONFIG_SH_GENERIC
+# define ACTUAL_NR_IRQS (sh_mv.mv_nr_irqs)
+#else
+# define ACTUAL_NR_IRQS NR_IRQS
+#endif
+
+
 extern void disable_irq(unsigned int);
 extern void disable_irq_nosync(unsigned int);
 extern void enable_irq(unsigned int);
@@ -123,6 +185,15 @@
 #define INTC_IPRF	0xa400001cUL
 #endif
 
+#define PORT_PACR	0xa4000100UL
+#define PORT_PBCR	0xa4000102UL
+#define PORT_PCCR	0xa4000104UL
+#define PORT_PFCR	0xa400010aUL
+#define PORT_PADR  	0xa4000120UL
+#define PORT_PBDR  	0xa4000122UL
+#define PORT_PCDR  	0xa4000124UL
+#define PORT_PFDR  	0xa400012aUL
+
 #define IRQ0_IRQ	32
 #define IRQ1_IRQ	33
 #define IRQ2_IRQ	34
@@ -130,19 +201,19 @@
 #define IRQ4_IRQ	36
 #define IRQ5_IRQ	37
 
-#define IRQ0_IRP_ADDR	INTC_IPRC
-#define IRQ1_IRP_ADDR	INTC_IPRC
-#define IRQ2_IRP_ADDR	INTC_IPRC
-#define IRQ3_IRP_ADDR	INTC_IPRC
-#define IRQ4_IRP_ADDR	INTC_IPRD
-#define IRQ5_IRP_ADDR	INTC_IPRD
-
-#define IRQ0_IRP_POS	0
-#define IRQ1_IRP_POS	1
-#define IRQ2_IRP_POS	2
-#define IRQ3_IRP_POS	3
-#define IRQ4_IRP_POS	0
-#define IRQ5_IRP_POS	1
+#define IRQ0_IPR_ADDR	INTC_IPRC
+#define IRQ1_IPR_ADDR	INTC_IPRC
+#define IRQ2_IPR_ADDR	INTC_IPRC
+#define IRQ3_IPR_ADDR	INTC_IPRC
+#define IRQ4_IPR_ADDR	INTC_IPRD
+#define IRQ5_IPR_ADDR	INTC_IPRD
+
+#define IRQ0_IPR_POS	0
+#define IRQ1_IPR_POS	1
+#define IRQ2_IPR_POS	2
+#define IRQ3_IPR_POS	3
+#define IRQ4_IPR_POS	0
+#define IRQ5_IPR_POS	1
 
 #define IRQ0_PRIORITY	1
 #define IRQ1_PRIORITY	1
@@ -150,21 +221,77 @@
 #define IRQ3_PRIORITY	1
 #define IRQ4_PRIORITY	1
 #define IRQ5_PRIORITY	1
-#endif
 
-extern int hd64461_irq_demux(int irq);
+#define PINT0_IRQ	40
+#define PINT8_IRQ	41
+
+#define PINT0_IPR_ADDR	INTC_IPRD
+#define PINT8_IPR_ADDR	INTC_IPRD
+
+#define PINT0_IPR_POS	3
+#define PINT8_IPR_POS	2
+#define PINT0_PRIORITY	2
+#define PINT8_PRIORITY	2
 
+extern int ipr_irq_demux(int irq);
+#define __irq_demux(irq) ipr_irq_demux(irq)
+
+#else
+#define __irq_demux(irq) irq
+#endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 */
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+#define INTC2_FIRST_IRQ 64
+#define NR_INTC2_IRQS 25
+ 
+#define INTC2_BASE0 0xfe080000
+#define INTC2_INTC2MODE  (INTC2_BASE0+0x80)
+ 
+#define INTC2_INTPRI_OFFSET	0x00
+#define INTC2_INTREQ_OFFSET	0x20
+#define INTC2_INTMSK_OFFSET	0x40
+#define INTC2_INTMSKCLR_OFFSET	0x60
+ 
+extern void make_intc2_irq(unsigned int irq,unsigned int addr,
+                           unsigned int group,int pos,int priority);
+ 
+#endif                                                                        
+       
 #ifdef CONFIG_SH_GENERIC
+
 extern __inline__ int irq_demux(int irq) {
 	if (sh_mv.mv_irq_demux) {
 		irq = sh_mv.mv_irq_demux(irq);
 	}
-	return irq;
+	return __irq_demux(irq);
 }
+
 #elif defined(CONFIG_HD64461)
+
+extern int hd64461_irq_demux(int irq);
 #define irq_demux(irq) hd64461_irq_demux(irq)
+
+#elif defined(CONFIG_HD64465)
+
+extern int hd64465_irq_demux(int irq);
+#define irq_demux(irq) hd64465_irq_demux(irq)
+
+#elif defined(CONFIG_SH_EC3104)
+
+extern int ec3104_irq_demux(int irq);
+#define irq_demux ec3104_irq_demux
+
+#elif defined(CONFIG_SH_CAT68701)
+
+extern int cat68701_irq_demux(int irq);
+#define irq_demux cat68701_irq_demux
+
 #else
-#define irq_demux(irq) irq
+
+#define irq_demux(irq) __irq_demux(irq)
+
 #endif
+
+
 
 #endif /* __ASM_SH_IRQ_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/keyboard-ec3104.h linux/include/asm-sh/keyboard-ec3104.h
--- linux-2.4.4-pre2/include/asm-sh/keyboard-ec3104.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/keyboard-ec3104.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,17 @@
+extern unsigned char ec3104_kbd_sysrq_xlate[];
+extern int ec3104_kbd_setkeycode(unsigned int scancode, unsigned int keycode);
+extern int ec3104_kbd_getkeycode(unsigned int scancode);
+extern int ec3104_kbd_translate(unsigned char, unsigned char *, char);
+extern char ec3104_kbd_unexpected_up(unsigned char);
+extern void ec3104_kbd_leds(unsigned char);
+extern void ec3104_kbd_init_hw(void);
+
+#define SYSRQ_KEY 0x54
+
+#define kbd_sysrq_xlate ec3104_kbd_sysrq_xlate
+#define kbd_setkeycode ec3104_kbd_setkeycode
+#define kbd_getkeycode ec3104_kbd_getkeycode
+#define kbd_translate ec3104_kbd_translate
+#define kbd_unexpected_up ec3104_kbd_unexpected_up
+#define kbd_leds ec3104_kbd_leds
+#define kbd_init_hw ec3104_kbd_init_hw
diff -urN linux-2.4.4-pre2/include/asm-sh/keyboard.h linux/include/asm-sh/keyboard.h
--- linux-2.4.4-pre2/include/asm-sh/keyboard.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/keyboard.h	Mon Dec  3 22:34:51 2001
@@ -4,8 +4,12 @@
  *	$Id: keyboard.h,v 1.1 2000/06/10 21:45:48 yaegashi Exp $
  */
 
+#include <linux/config.h>
 #include <asm/machvec.h>
 
+#ifdef CONFIG_SH_EC3104
+#include <asm/keyboard-ec3104.h>
+#else
 static __inline__ int kbd_setkeycode(unsigned int scancode,
 				     unsigned int keycode)
 {
@@ -34,6 +38,7 @@
 }
 
 extern void hp600_kbd_init_hw(void);
+extern void dreamcast_kbd_init_hw(void);
 
 static __inline__ void kbd_init_hw(void)
 {
@@ -42,4 +47,5 @@
 	}
 }
 
+#endif
 #endif
diff -urN linux-2.4.4-pre2/include/asm-sh/machvec.h linux/include/asm-sh/machvec.h
--- linux-2.4.4-pre2/include/asm-sh/machvec.h	Wed Aug  9 13:59:04 2000
+++ linux/include/asm-sh/machvec.h	Mon Dec  3 22:34:51 2001
@@ -13,45 +13,45 @@
 #include <linux/config.h>
 #include <linux/types.h>
 
+struct timeval;
+
 struct sh_machine_vector
 {
 	const char *mv_name;
 
 	int mv_nr_irqs;
 
-	unsigned long (*mv_inb)(unsigned int);
-	unsigned long (*mv_inw)(unsigned int);
-	unsigned long (*mv_inl)(unsigned int);
-	void (*mv_outb)(unsigned long, unsigned int);
-	void (*mv_outw)(unsigned long, unsigned int);
-	void (*mv_outl)(unsigned long, unsigned int);
-
-	unsigned long (*mv_inb_p)(unsigned int);
-	unsigned long (*mv_inw_p)(unsigned int);
-	unsigned long (*mv_inl_p)(unsigned int);
-	void (*mv_outb_p)(unsigned long, unsigned int);
-	void (*mv_outw_p)(unsigned long, unsigned int);
-	void (*mv_outl_p)(unsigned long, unsigned int);
-
-	void (*mv_insb)(unsigned int port, void *addr, unsigned long count);
-	void (*mv_insw)(unsigned int port, void *addr, unsigned long count);
-	void (*mv_insl)(unsigned int port, void *addr, unsigned long count);
-	void (*mv_outsb)(unsigned int port, const void *addr, unsigned long count);
-	void (*mv_outsw)(unsigned int port, const void *addr, unsigned long count);
-	void (*mv_outsl)(unsigned int port, const void *addr, unsigned long count);
-	
-	unsigned long (*mv_readb)(unsigned long);
-	unsigned long (*mv_readw)(unsigned long);
-	unsigned long (*mv_readl)(unsigned long);
+	unsigned char (*mv_inb)(unsigned long);
+	unsigned short (*mv_inw)(unsigned long);
+	unsigned int (*mv_inl)(unsigned long);
+	void (*mv_outb)(unsigned char, unsigned long);
+	void (*mv_outw)(unsigned short, unsigned long);
+	void (*mv_outl)(unsigned int, unsigned long);
+
+	unsigned char (*mv_inb_p)(unsigned long);
+	unsigned short (*mv_inw_p)(unsigned long);
+	unsigned int (*mv_inl_p)(unsigned long);
+	void (*mv_outb_p)(unsigned char, unsigned long);
+	void (*mv_outw_p)(unsigned short, unsigned long);
+	void (*mv_outl_p)(unsigned int, unsigned long);
+
+	void (*mv_insb)(unsigned long port, void *addr, unsigned long count);
+	void (*mv_insw)(unsigned long port, void *addr, unsigned long count);
+	void (*mv_insl)(unsigned long port, void *addr, unsigned long count);
+	void (*mv_outsb)(unsigned long port, const void *addr, unsigned long count);
+	void (*mv_outsw)(unsigned long port, const void *addr, unsigned long count);
+	void (*mv_outsl)(unsigned long port, const void *addr, unsigned long count);
+
+	unsigned char (*mv_readb)(unsigned long);
+	unsigned short (*mv_readw)(unsigned long);
+	unsigned int (*mv_readl)(unsigned long);
 	void (*mv_writeb)(unsigned char, unsigned long);
 	void (*mv_writew)(unsigned short, unsigned long);
 	void (*mv_writel)(unsigned int, unsigned long);
 
 	void* (*mv_ioremap)(unsigned long offset, unsigned long size);
-	void* (*mv_ioremap_nocache)(unsigned long offset, unsigned long size);
 	void (*mv_iounmap)(void *addr);
 
-	unsigned long (*mv_port2addr)(unsigned long offset);
 	unsigned long (*mv_isa_port2addr)(unsigned long offset);
 
 	int (*mv_irq_demux)(int irq);
@@ -63,9 +63,17 @@
 
 	void (*mv_heartbeat)(void);
 
+	void (*mv_rtc_gettimeofday)(struct timeval *tv);
+	int (*mv_rtc_settimeofday)(const struct timeval *tv);
+
 	unsigned int mv_hw_se : 1;
 	unsigned int mv_hw_hp600 : 1;
+	unsigned int mv_hw_hp620 : 1;
+	unsigned int mv_hw_hp680 : 1;
+	unsigned int mv_hw_hp690 : 1;
 	unsigned int mv_hw_hd64461 : 1;
+	unsigned int mv_hw_hd64465 : 1;
+	unsigned int mv_hw_dreamcast : 1;
 };
 
 extern struct sh_machine_vector sh_mv;
@@ -74,7 +82,12 @@
 #ifdef CONFIG_SH_GENERIC
 #define MACH_SE		(sh_mv.mv_hw_se)
 #define MACH_HP600	(sh_mv.mv_hw_hp600)
+#define MACH_HP620	(sh_mv.mv_hw_hp620)
+#define MACH_HP680	(sh_mv.mv_hw_hp680)
+#define MACH_HP690	(sh_mv.mv_hw_hp690)
 #define MACH_HD64461	(sh_mv.mv_hw_hd64461)
+#define MACH_HD64465	(sh_mv.mv_hw_hd64465)
+#define MACH_DREAMCAST	(sh_mv.mv_hw_dreamcast)
 #else
 # ifdef CONFIG_SH_SOLUTION_ENGINE
 #  define MACH_SE		1
@@ -86,10 +99,40 @@
 # else
 #  define MACH_HP600		0
 # endif
+# ifdef CONFIG_SH_HP620
+#  define MACH_HP620		1
+# else
+#  define MACH_HP620		0
+# endif
+# ifdef CONFIG_SH_HP680
+#  define MACH_HP680		1
+# else
+#  define MACH_HP680		0
+# endif
+# ifdef CONFIG_SH_HP690
+#  define MACH_HP690		1
+# else
+#  define MACH_HP690		0
+# endif
 # ifdef CONFIG_HD64461
 #  define MACH_HD64461		1
 # else
 #  define MACH_HD64461		0
+# endif
+# ifdef CONFIG_HD64465
+#  define MACH_HD64465		1
+# else
+#  define MACH_HD64465		0
+# endif
+# ifdef CONFIG_SH_EC3104
+#  define MACH_EC3104		1
+# else
+#  define MACH_EC3104		0
+# endif
+# ifdef CONFIG_SH_DREAMCAST
+#  define MACH_DREAMCAST	1
+# else
+#  define MACH_DREAMCAST	0
 # endif
 #endif
 
diff -urN linux-2.4.4-pre2/include/asm-sh/pci.h linux/include/asm-sh/pci.h
--- linux-2.4.4-pre2/include/asm-sh/pci.h	Mon Oct  2 11:57:34 2000
+++ linux/include/asm-sh/pci.h	Mon Dec  3 22:34:51 2001
@@ -7,13 +7,13 @@
    already-configured bus numbers - to be used for buggy BIOSes
    or architectures with incomplete PCI setup by the loader */
 
-#define pcibios_assign_all_busses()	0
+#define pcibios_assign_all_busses()	1
 
 /* These are currently the correct values for the STM overdrive board. 
  * We need some way of setting this on a board specific way, it will 
  * not be the same on other boards I think
  */
-#if 1 /* def CONFIG_SH_OVERDRIVE */
+#if 1 /* def CONFIG_SH_7750_OVERDRIVE || def CONFIG_CPU_SUBTYPE_ST40STB1 */
 #define PCIBIOS_MIN_IO		0x2000
 #define PCIBIOS_MIN_MEM		0x10000000
 #endif
@@ -70,6 +70,8 @@
 static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr,
 					size_t size,int directoin)
 {
+  
+        flush_cache_all();
 	return virt_to_bus(ptr);
 }
 
@@ -104,6 +106,7 @@
 static inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg,
 			     int nents,int direction)
 {
+        flush_cache_all();
 	return nents;
 }
 
@@ -145,6 +148,19 @@
 {
 	/* Nothing to do */
 }
+
+
+/* Return whether the given PCI device DMA address mask can
+ * be supported properly.  For example, if your device can
+ * only drive the low 24-bits during PCI bus mastering, then
+ * you would pass 0x00ffffff as the mask to this function.
+ */
+extern inline int pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask)
+{
+	return 1;
+}
+
+
 
 /* These macros should be used after a pci_map_sg call has been done
  * to get bus addresses of each of the SG entries and their lengths.
diff -urN linux-2.4.4-pre2/include/asm-sh/pgalloc-2level.h linux/include/asm-sh/pgalloc-2level.h
--- linux-2.4.4-pre2/include/asm-sh/pgalloc-2level.h	Fri Oct 13 12:06:52 2000
+++ linux/include/asm-sh/pgalloc-2level.h	Wed Dec 31 16:00:00 1969
@@ -1,23 +0,0 @@
-#ifndef __ASM_SH_PGALLOC_2LEVEL_H
-#define __ASM_SH_PGALLOC_2LEVEL_H
-
-/*
- * traditional two-level paging, page table allocation routines:
- */
-
-static __inline__ pmd_t *get_pmd_fast(void)
-{
-	return (pmd_t *)0;
-}
-
-static __inline__ void free_pmd_fast(pmd_t *pmd) { }
-static __inline__ void free_pmd_slow(pmd_t *pmd) { }
-
-static __inline__ pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address)
-{
-	if (!pgd)
-		BUG();
-	return (pmd_t *) pgd;
-}
-
-#endif /* __ASM_SH_PGALLOC_2LEVEL_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/pgalloc.h linux/include/asm-sh/pgalloc.h
--- linux-2.4.4-pre2/include/asm-sh/pgalloc.h	Fri Oct 13 12:06:52 2000
+++ linux/include/asm-sh/pgalloc.h	Mon Dec  3 22:34:51 2001
@@ -10,23 +10,22 @@
 #define pte_quicklist (current_cpu_data.pte_quick)
 #define pgtable_cache_size (current_cpu_data.pgtable_cache_sz)
 
-#include <asm/pgalloc-2level.h>
+#define pmd_populate(mm, pmd, pte) \
+		set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)))
 
 /*
- * Allocate and free page tables. The xxx_kernel() versions are
- * used to allocate a kernel page table - this turns on ASN bits
- * if any.
+ * Allocate and free page tables.
  */
 
 static __inline__ pgd_t *get_pgd_slow(void)
 {
 	unsigned int pgd_size = (USER_PTRS_PER_PGD * sizeof(pgd_t));
-	pgd_t *ret = (pgd_t *)kmalloc(pgd_size, GFP_KERNEL);
+	pgd_t *pgd = (pgd_t *)kmalloc(pgd_size, GFP_KERNEL);
 
-	if (ret)
-		memset(ret, 0, pgd_size);
+	if (pgd)
+		memset(pgd, 0, pgd_size);
 
-	return ret;
+	return pgd;
 }
 
 static __inline__ pgd_t *get_pgd_fast(void)
@@ -54,14 +53,21 @@
 	kfree(pgd);
 }
 
-extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted);
-extern pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long address_preadjusted);
+static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	pte_t *pte;
+
+	pte = (pte_t *) __get_free_page(GFP_KERNEL);
+	if (pte)
+		clear_page(pte);
+	return pte;
+}
 
-static __inline__ pte_t *get_pte_fast(void)
+static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address)
 {
 	unsigned long *ret;
 
-	if((ret = (unsigned long *)pte_quicklist) != NULL) {
+	if ((ret = (unsigned long *)pte_quicklist) != NULL) {
 		pte_quicklist = (unsigned long *)(*ret);
 		ret[0] = ret[1];
 		pgtable_cache_size--;
@@ -69,65 +75,21 @@
 	return (pte_t *)ret;
 }
 
-static __inline__ void free_pte_fast(pte_t *pte)
+static __inline__ void pte_free_fast(pte_t *pte)
 {
 	*(unsigned long *)pte = (unsigned long) pte_quicklist;
 	pte_quicklist = (unsigned long *) pte;
 	pgtable_cache_size++;
 }
 
-static __inline__ void free_pte_slow(pte_t *pte)
+static __inline__ void pte_free_slow(pte_t *pte)
 {
 	free_page((unsigned long)pte);
 }
 
-#define pte_free_kernel(pte)    free_pte_slow(pte)
-#define pte_free(pte)	   free_pte_slow(pte)
-#define pgd_free(pgd)	   free_pgd_slow(pgd)
-#define pgd_alloc()	     get_pgd_fast()
-
-static __inline__ pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
-{
-	if (!pmd)
-		BUG();
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-	if (pmd_none(*pmd)) {
-		pte_t * page = (pte_t *) get_pte_fast();
-		
-		if (!page)
-			return get_pte_kernel_slow(pmd, address);
-		set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(page)));
-		return page + address;
-	}
-	if (pmd_bad(*pmd)) {
-		__handle_bad_pmd_kernel(pmd);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
-}
-
-static __inline__ pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
-{
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-
-	if (pmd_none(*pmd))
-		goto getnew;
-	if (pmd_bad(*pmd))
-		goto fix;
-	return (pte_t *)pmd_page(*pmd) + address;
-getnew:
-{
-	unsigned long page = (unsigned long) get_pte_fast();
-	
-	if (!page)
-		return get_pte_slow(pmd, address);
-	set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(page)));
-	return (pte_t *)page + address;
-}
-fix:
-	__handle_bad_pmd(pmd);
-	return NULL;
-}
+#define pte_free(pte)		pte_free_slow(pte)
+#define pgd_free(pgd)		free_pgd_slow(pgd)
+#define pgd_alloc()		get_pgd_fast()
 
 /*
  * allocating and freeing a pmd is trivial: the 1-entry pmd is
@@ -137,8 +99,12 @@
 {
 }
 
-#define pmd_free_kernel		pmd_free
-#define pmd_alloc_kernel	pmd_alloc
+#define pmd_alloc_one_fast(mm, addr)	({ BUG(); ((pmd_t *)1); })
+#define pmd_alloc_one(mm, addr)		({ BUG(); ((pmd_t *)2); })
+#define pmd_free_slow(x)		do { } while (0)
+#define pmd_free_fast(x)		do { } while (0)
+#define pmd_free(x)			do { } while (0)
+#define pgd_populate(mm, pmd, pte)	BUG()
 
 extern int do_check_pgt_cache(int, int);
 
diff -urN linux-2.4.4-pre2/include/asm-sh/pgtable-2level.h linux/include/asm-sh/pgtable-2level.h
--- linux-2.4.4-pre2/include/asm-sh/pgtable-2level.h	Thu Jan  4 13:19:13 2001
+++ linux/include/asm-sh/pgtable-2level.h	Mon Dec  3 22:34:51 2001
@@ -32,7 +32,7 @@
 static inline int pgd_none(pgd_t pgd)		{ return 0; }
 static inline int pgd_bad(pgd_t pgd)		{ return 0; }
 static inline int pgd_present(pgd_t pgd)	{ return 1; }
-#define pgd_clear(xp)	do { } while (0)
+static inline void pgd_clear (pgd_t * pgd) 	{ }
 
 /*
  * Certain architectures need to do special things when PTEs
diff -urN linux-2.4.4-pre2/include/asm-sh/pgtable.h linux/include/asm-sh/pgtable.h
--- linux-2.4.4-pre2/include/asm-sh/pgtable.h	Sun Jan 28 18:56:00 2001
+++ linux/include/asm-sh/pgtable.h	Mon Dec  3 22:34:51 2001
@@ -166,12 +166,6 @@
 #define __S110	PAGE_SHARED
 #define __S111	PAGE_SHARED
 
-/*
- * Handling allocation failures during page table setup.
- */
-extern void __handle_bad_pmd(pmd_t * pmd);
-extern void __handle_bad_pmd_kernel(pmd_t * pmd);
-
 #define pte_none(x)	(!pte_val(x))
 #define pte_present(x)	(pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE))
 #define pte_clear(xp)	do { set_pte(xp, __pte(0)); } while (0)
diff -urN linux-2.4.4-pre2/include/asm-sh/processor.h linux/include/asm-sh/processor.h
--- linux-2.4.4-pre2/include/asm-sh/processor.h	Thu Jan  4 13:19:13 2001
+++ linux/include/asm-sh/processor.h	Mon Dec  3 22:34:51 2001
@@ -24,6 +24,7 @@
 	CPU_SH7708,		/* Represents 7707, 7708, 7708S, 7708R, 7709 */
 	CPU_SH7729,		/* Represents 7709A, 7729 */
 	CPU_SH7750,
+	CPU_ST40STB1,
 	CPU_SH_NONE
 };
 
diff -urN linux-2.4.4-pre2/include/asm-sh/serial-ec3104.h linux/include/asm-sh/serial-ec3104.h
--- linux-2.4.4-pre2/include/asm-sh/serial-ec3104.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sh/serial-ec3104.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,24 @@
+#include <asm/ec3104.h>
+/* Naturally we don't know the exact value but 115200 baud has a divisor
+ * of 9 and 19200 baud has a divisor of 52, so this seems like a good
+ * guess.  */
+#define BASE_BAUD (16800000 / 16)
+
+#define RS_TABLE_SIZE  3
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+/* there is a fourth serial port with the expected values as well, but
+ * it's got the keyboard controller behind it so we can't really use it
+ * (without moving the keyboard driver to userspace, which doesn't sound
+ * like a very good idea) */
+#define STD_SERIAL_PORT_DEFNS			\
+	/* UART CLK   PORT IRQ     FLAGS        */			\
+	{ 0, BASE_BAUD, 0x11C00, EC3104_IRQBASE+7, STD_COM_FLAGS }, /* ttyS0 */	\
+	{ 0, BASE_BAUD, 0x12000, EC3104_IRQBASE+8, STD_COM_FLAGS }, /* ttyS1 */	\
+	{ 0, BASE_BAUD, 0x12400, EC3104_IRQBASE+9, STD_COM_FLAGS }, /* ttyS2 */
+
+#define SERIAL_PORT_DFNS STD_SERIAL_PORT_DEFNS
+
+/* XXX: This should be moved ino irq.h */
+#define irq_cannonicalize(x) (x)
diff -urN linux-2.4.4-pre2/include/asm-sh/serial.h linux/include/asm-sh/serial.h
--- linux-2.4.4-pre2/include/asm-sh/serial.h	Sat Jul 22 07:42:06 2000
+++ linux/include/asm-sh/serial.h	Mon Dec  3 22:34:51 2001
@@ -7,6 +7,13 @@
 #ifndef _ASM_SERIAL_H
 #define _ASM_SERIAL_H
 
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#ifdef CONFIG_SH_EC3104
+#include <asm/serial-ec3104.h>
+#else
+
 /*
  * This assumes you have a 1.8432 MHz clock for your UART.
  *
@@ -16,18 +23,32 @@
  */
 #define BASE_BAUD ( 1843200 / 16 )
 
-#define RS_TABLE_SIZE  2
-
 #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
 
+#ifdef CONFIG_HD64465
+#include <asm/hd64465.h>
+
+#define RS_TABLE_SIZE  1
+
+#define STD_SERIAL_PORT_DEFNS                   \
+        /* UART CLK   PORT IRQ     FLAGS        */                      \
+        { 0, BASE_BAUD, 0x3F8, HD64465_IRQ_UART, STD_COM_FLAGS }  /* ttyS0 */
+
+#else
+
+#define RS_TABLE_SIZE  2
+
 #define STD_SERIAL_PORT_DEFNS			\
 	/* UART CLK   PORT IRQ     FLAGS        */			\
 	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */	\
 	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }	/* ttyS1 */
 
+#endif
+
 #define SERIAL_PORT_DFNS STD_SERIAL_PORT_DEFNS
 
 /* XXX: This should be moved ino irq.h */
 #define irq_cannonicalize(x) (x)
 
+#endif
 #endif /* _ASM_SERIAL_H */
diff -urN linux-2.4.4-pre2/include/asm-sh/sigcontext.h linux/include/asm-sh/sigcontext.h
--- linux-2.4.4-pre2/include/asm-sh/sigcontext.h	Mon Apr 24 13:54:17 2000
+++ linux/include/asm-sh/sigcontext.h	Mon Dec  3 22:34:51 2001
@@ -16,7 +16,7 @@
 #if defined(__SH4__)
 	/* FPU registers */
 	unsigned long sc_fpregs[16];
-	unsigned long long sc_xdregs[8];
+	unsigned long sc_xfpregs[16];
 	unsigned int sc_fpscr;
 	unsigned int sc_fpul;
 	unsigned int sc_ownedfp;
diff -urN linux-2.4.4-pre2/include/asm-sparc/kmap_types.h linux/include/asm-sparc/kmap_types.h
--- linux-2.4.4-pre2/include/asm-sparc/kmap_types.h	Fri Aug  4 18:16:11 2000
+++ linux/include/asm-sparc/kmap_types.h	Mon Dec  3 22:34:51 2001
@@ -4,6 +4,8 @@
 enum km_type {
 	KM_BOUNCE_READ,
 	KM_BOUNCE_WRITE,
+	KM_SKB_DATA,
+	KM_SKB_DATA_SOFTIRQ,
 	KM_TYPE_NR
 };
 
diff -urN linux-2.4.4-pre2/include/asm-sparc/perfctr.h linux/include/asm-sparc/perfctr.h
--- linux-2.4.4-pre2/include/asm-sparc/perfctr.h	Tue Oct 27 09:52:21 1998
+++ linux/include/asm-sparc/perfctr.h	Mon Dec  3 22:34:51 2001
@@ -60,10 +60,10 @@
 #ifndef __KERNEL__
 
 #define  PRIV 0x00000001
-#define  USR  0x00000002
-#define  SYS  0x00000004
+#define  SYS  0x00000002
+#define  USR  0x00000004
 
-/* Pic.S0 Selection Bit Field Encoding  */
+/* Pic.S0 Selection Bit Field Encoding, Ultra-I/II  */
 #define  CYCLE_CNT            0x00000000
 #define  INSTR_CNT            0x00000010
 #define  DISPATCH0_IC_MISS    0x00000020
@@ -77,7 +77,38 @@
 #define  EC_SNOOP_INV         0x000000E0
 #define  EC_RD_HIT            0x000000F0
 
-/* Pic.S1 Selection Bit Field Encoding  */
+/* Pic.S0 Selection Bit Field Encoding, Ultra-III  */
+#define  US3_CYCLE_CNT	      	0x00000000
+#define  US3_INSTR_CNT	      	0x00000010
+#define  US3_DISPATCH0_IC_MISS	0x00000020
+#define  US3_DISPATCH0_BR_TGT	0x00000030
+#define  US3_DISPATCH0_2ND_BR	0x00000040
+#define  US3_RSTALL_STOREQ	0x00000050
+#define  US3_RSTALL_IU_USE	0x00000060
+#define  US3_IC_REF		0x00000080
+#define  US3_DC_RD		0x00000090
+#define  US3_DC_WR		0x000000a0
+#define  US3_EC_REF		0x000000c0
+#define  US3_EC_WR_HIT_RTO	0x000000d0
+#define  US3_EC_SNOOP_INV	0x000000e0
+#define  US3_EC_RD_MISS		0x000000f0
+#define  US3_PC_PORT0_RD	0x00000100
+#define  US3_SI_SNOOP		0x00000110
+#define  US3_SI_CIQ_FLOW	0x00000120
+#define  US3_SI_OWNED		0x00000130
+#define  US3_SW_COUNT_0		0x00000140
+#define  US3_IU_BR_MISS_TAKEN	0x00000150
+#define  US3_IU_BR_COUNT_TAKEN	0x00000160
+#define  US3_DISP_RS_MISPRED	0x00000170
+#define  US3_FA_PIPE_COMPL	0x00000180
+#define  US3_MC_READS_0		0x00000200
+#define  US3_MC_READS_1		0x00000210
+#define  US3_MC_READS_2		0x00000220
+#define  US3_MC_READS_3		0x00000230
+#define  US3_MC_STALLS_0	0x00000240
+#define  US3_MC_STALLS_2	0x00000250
+
+/* Pic.S1 Selection Bit Field Encoding, Ultra-I/II  */
 #define  CYCLE_CNT_D1         0x00000000
 #define  INSTR_CNT_D1         0x00000800
 #define  DISPATCH0_IC_MISPRED 0x00001000
@@ -90,6 +121,47 @@
 #define  EC_WB                0x00006800
 #define  EC_SNOOP_CB          0x00007000
 #define  EC_IT_HIT            0x00007800
+
+/* Pic.S1 Selection Bit Field Encoding, Ultra-III  */
+#define  US3_CYCLE_CNT_D1	0x00000000
+#define  US3_INSTR_CNT_D1	0x00000800
+#define  US3_DISPATCH0_MISPRED	0x00001000
+#define  US3_IC_MISS_CANCELLED	0x00001800
+#define  US3_RE_ENDIAN_MISS	0x00002000
+#define  US3_RE_FPU_BYPASS	0x00002800
+#define  US3_RE_DC_MISS		0x00003000
+#define  US3_RE_EC_MISS		0x00003800
+#define  US3_IC_MISS		0x00004000
+#define  US3_DC_RD_MISS		0x00004800
+#define  US3_DC_WR_MISS		0x00005000
+#define  US3_RSTALL_FP_USE	0x00005800
+#define  US3_EC_MISSES		0x00006000
+#define  US3_EC_WB		0x00006800
+#define  US3_EC_SNOOP_CB	0x00007000
+#define  US3_EC_IC_MISS		0x00007800
+#define  US3_RE_PC_MISS		0x00008000
+#define  US3_ITLB_MISS		0x00008800
+#define  US3_DTLB_MISS		0x00009000
+#define  US3_WC_MISS		0x00009800
+#define  US3_WC_SNOOP_CB	0x0000a000
+#define  US3_WC_SCRUBBED	0x0000a800
+#define  US3_WC_WB_WO_READ	0x0000b000
+#define  US3_PC_SOFT_HIT	0x0000c000
+#define  US3_PC_SNOOP_INV	0x0000c800
+#define  US3_PC_HARD_HIT	0x0000d000
+#define  US3_PC_PORT1_RD	0x0000d800
+#define  US3_SW_COUNT_1		0x0000e000
+#define  US3_IU_STAT_BR_MIS_UNTAKEN	0x0000e800
+#define  US3_IU_STAT_BR_COUNT_UNTAKEN	0x0000f000
+#define  US3_PC_MS_MISSES	0x0000f800
+#define  US3_MC_WRITES_0	0x00010800
+#define  US3_MC_WRITES_1	0x00011000
+#define  US3_MC_WRITES_2	0x00011800
+#define  US3_MC_WRITES_3	0x00012000
+#define  US3_MC_STALLS_1	0x00012800
+#define  US3_MC_STALLS_3	0x00013000
+#define  US3_RE_RAW_MISS	0x00013800
+#define  US3_FM_PIPE_COMPLETION	0x00014000
 
 struct vcounter_struct {
   unsigned long long vcnt0;
diff -urN linux-2.4.4-pre2/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- linux-2.4.4-pre2/include/asm-sparc/processor.h	Mon Jan  1 10:37:41 2001
+++ linux/include/asm-sparc/processor.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.80 2000/12/31 10:05:43 davem Exp $
+/* $Id: processor.h,v 1.81 2001/03/27 02:36:37 davem Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -90,7 +90,6 @@
 
 #define SPARC_FLAG_KTHREAD      0x1    /* task is a kernel thread */
 #define SPARC_FLAG_UNALIGNED    0x2    /* is allowed to do unaligned accesses */
-#define SPARC_FLAG_MMAPSHARED	0x4    /* task wants a shared mmap */
 
 #define INIT_MMAP { &init_mm, (0), (0), \
 		    NULL, __pgprot(0x0) , VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
diff -urN linux-2.4.4-pre2/include/asm-sparc64/asi.h linux/include/asm-sparc64/asi.h
--- linux-2.4.4-pre2/include/asm-sparc64/asi.h	Sun Mar 25 18:14:21 2001
+++ linux/include/asm-sparc64/asi.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: asi.h,v 1.4 2001/03/15 02:08:46 davem Exp $ */
+/* $Id: asi.h,v 1.5 2001/03/29 11:47:47 davem Exp $ */
 #ifndef _SPARC64_ASI_H
 #define _SPARC64_ASI_H
 
@@ -80,6 +80,7 @@
 #define ASI_BRPRED_ARRAY	0x6f /* (III) Branch Prediction RAM diag	*/
 #define ASI_BLK_AIUP		0x70 /* Primary, user, block load/store		*/
 #define ASI_BLK_AIUS		0x71 /* Secondary, user, block load/store	*/
+#define ASI_MCU_CTRL_REG	0x72 /* (III) Memory controller registers	*/
 #define ASI_EC_DATA		0x74 /* (III) E-cache data staging register	*/
 #define ASI_EC_CTRL		0x75 /* (III) E-cache control register		*/
 #define ASI_EC_W		0x76 /* E-cache diag write access		*/
diff -urN linux-2.4.4-pre2/include/asm-sparc64/bbc.h linux/include/asm-sparc64/bbc.h
--- linux-2.4.4-pre2/include/asm-sparc64/bbc.h	Sun Mar 25 18:14:21 2001
+++ linux/include/asm-sparc64/bbc.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: bbc.h,v 1.1 2001/03/24 06:03:03 davem Exp $
+/* $Id: bbc.h,v 1.2 2001/03/26 23:47:18 davem Exp $
  * bbc.h: Defines for BootBus Controller found on UltraSPARC-III
  *        systems.
  *
@@ -89,7 +89,7 @@
  * is asserted can be controlled by this regiser.
  */
 #define BBC_WDACTION_RST  0x01	/* When set, watchdog causes system reset.
-				 * When clear, all cpus receive XIR reset.
+				 * When clear, BBC ignores watchdog signal.
 				 */
 #define BBC_WDACTION_RESV 0xfe	/* Reserved */
 
diff -urN linux-2.4.4-pre2/include/asm-sparc64/chafsr.h linux/include/asm-sparc64/chafsr.h
--- linux-2.4.4-pre2/include/asm-sparc64/chafsr.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/chafsr.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,150 @@
+/* $Id: chafsr.h,v 1.1 2001/03/28 10:56:34 davem Exp $ */
+#ifndef _SPARC64_CHAFSR_H
+#define _SPARC64_CHAFSR_H
+
+/* Cheetah Asynchronous Fault Status register, ASI=0x4C VA<63:0>=0x0 */
+
+/* All bits of this register except M_SYNDROME and E_SYNDROME are
+ * read, write 1 to clear.  M_SYNDROME and E_SYNDROME are read-only.
+ */
+
+/* Multiple errors of the same type have occurred.  This bit is set when
+ * an uncorrectable error or a SW correctable error occurs and the status
+ * bit to report that error is already set.  When multiple errors of
+ * different types are indicated by setting multiple status bits.
+ *
+ * This bit is not set if multiple HW corrected errors with the same
+ * status bit occur, only uncorrectable and SW correctable ones have
+ * this behavior.
+ *
+ * This bit is not set when multiple ECC errors happen within a single
+ * 64-byte system bus transaction.  Only the first ECC error in a 16-byte
+ * subunit will be logged.  All errors in subsequent 16-byte subunits
+ * from the same 64-byte transaction are ignored.
+ */
+#define CHAFSR_ME		0x0020000000000000
+
+/* Privileged state error has occurred.  This is a capture of PSTATE.PRIV
+ * at the time the error is detected.
+ */
+#define CHAFSR_PRIV		0x0010000000000000
+
+/* The following bits 51 (CHAFSR_PERR) to 33 (CHAFSR_CE) are sticky error
+ * bits and record the most recently detected errors.  Bits accumulate
+ * errors that have been detected since the last write to clear the bit.
+ */
+
+/* System interface protocol error.  The processor asserts its' ERROR
+ * pin when this event occurs and it also logs a specific cause code
+ * into a JTAG scannable flop.
+ */
+#define CHAFSR_PERR		0x0008000000000000
+
+/* Internal processor error.  The processor asserts its' ERROR
+ * pin when this event occurs and it also logs a specific cause code
+ * into a JTAG scannable flop.
+ */
+#define CHAFSR_IERR		0x0004000000000000
+
+/* System request parity error on incoming address */
+#define CHAFSR_ISAP		0x0002000000000000
+
+/* HW Corrected system bus MTAG ECC error */
+#define CHAFSR_EMC		0x0001000000000000
+
+/* Uncorrectable system bus MTAG ECC error */
+#define CHAFSR_EMU		0x0000800000000000
+
+/* HW Corrected system bus data ECC error for read of interrupt vector */
+#define CHAFSR_IVC		0x0000400000000000
+
+/* Uncorrectable system bus data ECC error for read of interrupt vector */
+#define CHAFSR_IVU		0x0000200000000000
+
+/* Unmappeed error from system bus */
+#define CHAFSR_TO		0x0000100000000000
+
+/* Bus error response from system bus */
+#define CHAFSR_BERR		0x0000080000000000
+
+/* SW Correctable E-cache ECC error for instruction fetch or data access
+ * other than block load.
+ */
+#define CHAFSR_UCC		0x0000040000000000
+
+/* Uncorrectable E-cache ECC error for instruction fetch or data access
+ * other than block load.
+ */
+#define CHAFSR_UCU		0x0000020000000000
+
+/* Copyout HW Corrected ECC error */
+#define CHAFSR_CPC		0x0000010000000000
+
+/* Copyout Uncorrectable ECC error */
+#define CHAFSR_CPU		0x0000008000000000
+
+/* HW Corrected ECC error from E-cache for writeback */
+#define CHAFSR_WDC		0x0000004000000000
+
+/* Uncorrectable ECC error from E-cache for writeback */
+#define CHAFSR_WDU		0x0000002000000000
+
+/* HW Corrected ECC error from E-cache for store merge or block load */
+#define CHAFSR_EDC		0x0000001000000000
+
+/* Uncorrectable ECC error from E-cache for store merge or block load */
+#define CHAFSR_EDU		0x0000000800000000
+
+/* Uncorrectable system bus data ECC error for read of memory or I/O */
+#define CHAFSR_UE		0x0000000400000000
+
+/* HW Corrected system bus data ECC error for read of memory or I/O */
+#define CHAFSR_CE		0x0000000200000000
+
+#define CHAFSR_ERRORS		(CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP | CHAFSR_EMC | \
+				 CHAFSR_EMU | CHAFSR_IVC | CHAFSR_IVU | CHAFSR_TO | \
+				 CHAFSR_BERR | CHAFSR_UCC | CHAFSR_UCU | CHAFSR_CPC | \
+				 CHAFSR_CPU | CHAFSR_WDC | CHAFSR_WDU | CHAFSR_EDC | \
+				 CHAFSR_EDU | CHAFSR_UE | CHAFSR_CE)
+
+/* System bus MTAG ECC syndrome.  This field captures the status of the
+ * first occurrence of the highest-priority error according to the M_SYND
+ * overwrite policy.  After the AFSR sticky bit, corresponding to the error
+ * for which the M_SYND is reported, is cleared, the contents of the M_SYND
+ * field will be unchanged by will be unfrozen for further error capture.
+ */
+#define CHAFSR_M_SYNDROME	0x00000000000f0000
+#define CHAFSR_M_SYNDROME_SHIFT	16
+
+/* System bus or E-cache data ECC syndrome.  This field captures the status
+ * of the first occurrence of the highest-priority error according to the
+ * E_SYND overwrite policy.  After the AFSR sticky bit, corresponding to the
+ * error for which the E_SYND is reported, is cleare, the contents of the E_SYND
+ * field will be unchanged but will be unfrozen for further error capture.
+ */
+#define CHAFSR_E_SYNDROME	0x00000000000001ff
+#define CHAFSR_E_SYNDROME_SHIFT	0
+
+/* The AFSR must be explicitly cleared by software, it is not cleared automatically
+ * by a read.  Writes to bits <51:33> with bits set will clear the corresponding
+ * bits in the AFSR.  Bits assosciated with disrupting traps must be cleared before
+ * interrupts are re-enabled to prevent multiple traps for the same error.  I.e.
+ * PSTATE.IE and AFSR bits control delivery of disrupting traps.
+ *
+ * Since there is only one AFAR, when multiple events have been logged by the
+ * bits in the AFSR, at most one of these events will have its status captured
+ * in the AFAR.  The highest priority of those event bits will get AFAR logging.
+ * The AFAR will be unlocked and available to capture the address of another event
+ * as soon as the one bit in AFSR that corresponds to the event logged in AFAR is
+ * cleared.  For example, if AFSR.CE is detected, then AFSR.UE (which overwrites
+ * the AFAR), and AFSR.UE is cleared by not AFSR.CE, then the AFAR will be unlocked
+ * and ready for another event, even though AFSR.CE is still set.  The same rules
+ * also apply to the M_SYNDROME and E_SYNDROME fields of the AFSR.
+ */
+
+/* Software bit set by linux trap handlers to indicate that the trap was
+ * signalled at %tl >= 1.
+ */
+#define CHAFSR_TL1		0x8000000000000000
+
+#endif /* _SPARC64_CHAFSR_H */
diff -urN linux-2.4.4-pre2/include/asm-sparc64/chmctrl.h linux/include/asm-sparc64/chmctrl.h
--- linux-2.4.4-pre2/include/asm-sparc64/chmctrl.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/chmctrl.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,184 @@
+/* $Id: chmctrl.h,v 1.1 2001/03/29 11:43:28 davem Exp $ */
+#ifndef _SPARC64_CHMCTRL_H
+#define _SPARC64_CHMCTRL_H
+
+/* Cheetah memory controller programmable registers. */
+#define CHMCTRL_TCTRL1		0x00 /* Memory Timing Control I		*/
+#define CHMCTRL_TCTRL2		0x08 /* Memory Timing Control II	*/
+#define CHMCTRL_TCTRL3		0x38 /* Memory Timing Control III	*/
+#define CHMCTRL_TCTRL4		0x40 /* Memory Timing Control IV	*/
+#define CHMCTRL_DECODE1		0x10 /* Memory Address Decode I		*/
+#define CHMCTRL_DECODE2		0x18 /* Memory Address Decode II	*/
+#define CHMCTRL_DECODE3		0x20 /* Memory Address Decode III	*/
+#define CHMCTRL_DECODE4		0x28 /* Memory Address Decode IV	*/
+#define CHMCTRL_MACTRL		0x30 /* Memory Address Control		*/
+
+/* Memory Timing Control I */
+#define TCTRL1_SDRAMCTL_DLY	0xf000000000000000
+#define TCTRL1_SDRAMCTL_DLY_SHIFT     60
+#define TCTRL1_SDRAMCLK_DLY	0x0e00000000000000
+#define TCTRL1_SDRAMCLK_DLY_SHIFT     57
+#define TCTRL1_R		0x0100000000000000
+#define TCTRL1_R_SHIFT 		      56
+#define TCTRL1_AUTORFR_CYCLE	0x00fe000000000000
+#define TCTRL1_AUTORFR_CYCLE_SHIFT    49
+#define TCTRL1_RD_WAIT		0x0001f00000000000
+#define TCTRL1_RD_WAIT_SHIFT	      44
+#define TCTRL1_PC_CYCLE		0x00000fc000000000
+#define TCTRL1_PC_CYCLE_SHIFT	      38
+#define TCTRL1_WR_MORE_RAS_PW	0x0000003f00000000
+#define TCTRL1_WR_MORE_RAS_PW_SHIFT   32
+#define TCTRL1_RD_MORE_RAW_PW	0x00000000fc000000
+#define TCTRL1_RD_MORE_RAS_PW_SHIFT   26
+#define TCTRL1_ACT_WR_DLY	0x0000000003f00000
+#define TCTRL1_ACT_WR_DLY_SHIFT	      20
+#define TCTRL1_ACT_RD_DLY	0x00000000000fc000
+#define TCTRL1_ACT_RD_DLY_SHIFT	      14
+#define TCTRL1_BANK_PRESENT	0x0000000000003000
+#define TCTRL1_BANK_PRESENT_SHIFT     12
+#define TCTRL1_RFR_INT		0x0000000000000ff8
+#define TCTRL1_RFR_INT_SHIFT	      3
+#define TCTRL1_SET_MODE_REG	0x0000000000000004
+#define TCTRL1_SET_MODE_REG_SHIFT     2
+#define TCTRL1_RFR_ENABLE	0x0000000000000002
+#define TCTRL1_RFR_ENABLE_SHIFT	      1
+#define TCTRL1_PRECHG_ALL	0x0000000000000001
+#define TCTRL1_PRECHG_ALL_SHIFT	      0
+
+/* Memory Timing Control II */
+#define TCTRL2_WR_MSEL_DLY	0xfc00000000000000
+#define TCTRL2_WR_MSEL_DLY_SHIFT      58
+#define TCTRL2_RD_MSEL_DLY	0x03f0000000000000
+#define TCTRL2_RD_MSEL_DLY_SHIFT      52
+#define TCTRL2_WRDATA_THLD	0x000c000000000000
+#define TCTRL2_WRDATA_THLD_SHIFT      50
+#define TCTRL2_RDWR_RD_TI_DLY	0x0003f00000000000
+#define TCTRL2_RDWR_RD_TI_DLY_SHIFT   44
+#define TCTRL2_AUTOPRECHG_ENBL	0x0000080000000000
+#define TCTRL2_AUTOPRECHG_ENBL_SHIFT  43
+#define TCTRL2_RDWR_PI_MORE_DLY	0x000007c000000000
+#define TCTRL2_RDWR_PI_MORE_DLY_SHIFT 38
+#define TCTRL2_RDWR_1_DLY	0x0000003f00000000
+#define TCTRL2_RDWR_1_DLY_SHIFT       32
+#define TCTRL2_WRWR_PI_MORE_DLY	0x00000000f8000000
+#define TCTRL2_WRWR_PI_MORE_DLY_SHIFT 27
+#define TCTRL2_WRWR_1_DLY	0x0000000007e00000
+#define TCTRL2_WRWR_1_DLY_SHIFT       21
+#define TCTRL2_RDWR_RD_PI_MORE_DLY 0x00000000001f0000
+#define TCTRL2_RDWR_RD_PI_MORE_DLY_SHIFT 16
+#define TCTRL2_R		0x0000000000008000
+#define TCTRL2_R_SHIFT		      15
+#define TCTRL2_SDRAM_MODE_REG_DATA 0x0000000000007fff
+#define TCTRL2_SDRAM_MODE_REG_DATA_SHIFT 0
+
+/* Memory Timing Control III */
+#define TCTRL3_SDRAM_CTL_DLY	0xf000000000000000
+#define TCTRL3_SDRAM_CTL_DLY_SHIFT    60
+#define TCTRL3_SDRAM_CLK_DLY	0x0e00000000000000
+#define TCTRL3_SDRAM_CLK_DLY_SHIFT    57
+#define TCTRL3_R		0x0100000000000000
+#define TCTRL3_R_SHIFT		      56
+#define TCTRL3_AUTO_RFR_CYCLE	0x00fe000000000000
+#define TCTRL3_AUTO_RFR_CYCLE_SHIFT   49
+#define TCTRL3_RD_WAIT		0x0001f00000000000
+#define TCTRL3_RD_WAIT_SHIFT	      44
+#define TCTRL3_PC_CYCLE		0x00000fc000000000
+#define TCTRL3_PC_CYCLE_SHIFT	      38
+#define TCTRL3_WR_MORE_RAW_PW	0x0000003f00000000
+#define TCTRL3_WR_MORE_RAW_PW_SHIFT   32
+#define TCTRL3_RD_MORE_RAW_PW	0x00000000fc000000
+#define TCTRL3_RD_MORE_RAW_PW_SHIFT   26
+#define TCTRL3_ACT_WR_DLY	0x0000000003f00000
+#define TCTRL3_ACT_WR_DLY_SHIFT       20
+#define TCTRL3_ACT_RD_DLY	0x00000000000fc000
+#define TCTRL3_ACT_RD_DLY_SHIFT       14
+#define TCTRL3_BANK_PRESENT	0x0000000000003000
+#define TCTRL3_BANK_PRESENT_SHIFT     12
+#define TCTRL3_RFR_INT		0x0000000000000ff8
+#define TCTRL3_RFR_INT_SHIFT	      3
+#define TCTRL3_SET_MODE_REG	0x0000000000000004
+#define TCTRL3_SET_MODE_REG_SHIFT     2
+#define TCTRL3_RFR_ENABLE	0x0000000000000002
+#define TCTRL3_RFR_ENABLE_SHIFT       1
+#define TCTRL3_PRECHG_ALL	0x0000000000000001
+#define TCTRL3_PRECHG_ALL_SHIFT	      0
+
+/* Memory Timing Control IV */
+#define TCTRL4_WR_MSEL_DLY	0xfc00000000000000
+#define TCTRL4_WR_MSEL_DLY_SHIFT      58
+#define TCTRL4_RD_MSEL_DLY	0x03f0000000000000
+#define TCTRL4_RD_MSEL_DLY_SHIFT      52
+#define TCTRL4_WRDATA_THLD	0x000c000000000000
+#define TCTRL4_WRDATA_THLD_SHIFT      50
+#define TCTRL4_RDWR_RD_RI_DLY	0x0003f00000000000
+#define TCTRL4_RDWR_RD_RI_DLY_SHIFT   44
+#define TCTRL4_AUTO_PRECHG_ENBL	0x0000080000000000
+#define TCTRL4_AUTO_PRECHG_ENBL_SHIFT 43
+#define TCTRL4_RD_WR_PI_MORE_DLY 0x000007c000000000
+#define TCTRL4_RD_WR_PI_MORE_DLY_SHIFT 38
+#define TCTRL4_RD_WR_TI_DLY	0x0000003f00000000
+#define TCTRL4_RD_WR_TI_DLY_SHIFT     32
+#define TCTRL4_WR_WR_PI_MORE_DLY 0x00000000f8000000
+#define TCTRL4_WR_WR_PI_MORE_DLY_SHIFT 27
+#define TCTRL4_WR_WR_TI_DLY	0x0000000007e00000
+#define TCTRL4_WR_WR_TI_DLY_SHIFT     21
+#define TCTRL4_RDWR_RD_PI_MORE_DLY 0x00000000001f0000
+#define TCTRL4_RDWR_RD_PI_MORE_DLY_SHIFT 16
+#define TCTRL4_R		0x0000000000008000
+#define TCTRL4_R_SHIFT		      15
+#define TCTRL4_SDRAM_MODE_REG_DATA 0x0000000000007fff
+#define TCTRL4_SDRAM_MODE_REG_DATA_SHIFT 0
+
+/* All 4 memory address decoding registers have the
+ * same layout.
+ */
+#define MEM_DECODE_VALID	0x8000000000000000 /* Valid */
+#define MEM_DECODE_VALID_SHIFT	      63
+#define MEM_DECODE_UK		0x001ffe0000000000 /* Upper mask */
+#define MEM_DECODE_UK_SHIFT	      41
+#define MEM_DECODE_UM		0x0000001ffff00000 /* Upper match */
+#define MEM_DECODE_UM_SHIFT	      20
+#define MEM_DECODE_LK		0x000000000003c000 /* Lower mask */
+#define MEM_DECODE_LK_SHIFT	      14
+#define MEM_DECODE_LM		0x0000000000000f00 /* Lower match */
+#define MEM_DECODE_LM_SHIFT           8
+
+#define PA_UPPER_BITS		0x000007fffc000000
+#define PA_UPPER_BITS_SHIFT	26
+#define PA_LOWER_BITS		0x00000000000003c0
+#define PA_LOWER_BITS_SHIFT	6
+
+#define MACTRL_R0		         0x8000000000000000
+#define MACTRL_R0_SHIFT		         63
+#define MACTRL_ADDR_LE_PW                0x7000000000000000
+#define MACTRL_ADDR_LE_PW_SHIFT		 60
+#define MACTRL_CMD_PW                    0x0f00000000000000
+#define MACTRL_CMD_PW_SHIFT		 56
+#define MACTRL_HALF_MODE_WR_MSEL_DLY     0x00fc000000000000
+#define MACTRL_HALF_MODE_WR_MSEL_DLY_SHIFT 50
+#define MACTRL_HALF_MODE_RD_MSEL_DLY     0x0003f00000000000
+#define MACTRL_HALF_MODE_RD_MSEL_DLY_SHIFT 44
+#define MACTRL_HALF_MODE_SDRAM_CTL_DLY   0x00000f0000000000
+#define MACTRL_HALF_MODE_SDRAM_CTL_DLY_SHIFT 40
+#define MACTRL_HALF_MODE_SDRAM_CLK_DLY   0x000000e000000000
+#define MACTRL_HALF_MODE_SDRAM_CLK_DLY_SHIFT 37
+#define MACTRL_R1                        0x0000001000000000
+#define MACTRL_R1_SHIFT                      36
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B3 0x0000000f00000000
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B3_SHIFT 32
+#define MACTRL_ENC_INTLV_B3              0x00000000f8000000
+#define MACTRL_ENC_INTLV_B3_SHIFT              27
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B2 0x0000000007800000
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B2_SHIFT 23
+#define MACTRL_ENC_INTLV_B2              0x00000000007c0000
+#define MACTRL_ENC_INTLV_B2_SHIFT              18
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B1 0x000000000003c000
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B1_SHIFT 14
+#define MACTRL_ENC_INTLV_B1              0x0000000000003e00
+#define MACTRL_ENC_INTLV_B1_SHIFT               9
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B0 0x00000000000001e0
+#define MACTRL_BANKSEL_N_ROWADDR_SIZE_B0_SHIFT  5
+#define MACTRL_ENC_INTLV_B0              0x000000000000001f
+#define MACTRL_ENC_INTLV_B0_SHIFT               0
+
+#endif /* _SPARC64_CHMCTRL_H */
diff -urN linux-2.4.4-pre2/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h
--- linux-2.4.4-pre2/include/asm-sparc64/elf.h	Mon Mar 26 11:00:29 2001
+++ linux/include/asm-sparc64/elf.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.28 2001/03/24 09:36:02 davem Exp $ */
+/* $Id: elf.h,v 1.29 2001/03/30 07:10:48 davem Exp $ */
 #ifndef __ASM_SPARC64_ELF_H
 #define __ASM_SPARC64_ELF_H
 
diff -urN linux-2.4.4-pre2/include/asm-sparc64/estate.h linux/include/asm-sparc64/estate.h
--- linux-2.4.4-pre2/include/asm-sparc64/estate.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/estate.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,50 @@
+/* $Id: estate.h,v 1.1 2001/03/28 10:56:34 davem Exp $ */
+#ifndef _SPARC64_ESTATE_H
+#define _SPARC64_ESTATE_H
+
+/* UltraSPARC-III E-cache Error Enable */
+#define ESTATE_ERROR_FMT	0x0000000000040000 /* Force MTAG ECC		*/
+#define ESTATE_ERROR_FMESS	0x000000000003c000 /* Forced MTAG ECC val	*/
+#define ESTATE_ERROR_FMD	0x0000000000002000 /* Force DATA ECC		*/
+#define ESTATE_ERROR_FDECC	0x0000000000001ff0 /* Forced DATA ECC val	*/
+#define ESTATE_ERROR_UCEEN	0x0000000000000008 /* See below			*/
+#define ESTATE_ERROR_NCEEN	0x0000000000000002 /* See below			*/
+#define ESTATE_ERROR_CEEN	0x0000000000000001 /* See below			*/
+
+/* UCEEN enables the fast_ECC_error trap for: 1) software correctable E-cache
+ * errors 2) uncorrectable E-cache errors.  Such events only occur on reads
+ * of the E-cache by the local processor for: 1) data loads 2) instruction
+ * fetches 3) atomic operations.  Such events _cannot_ occur for: 1) merge
+ * 2) writeback 2) copyout.  The AFSR bits assosciated with these traps are
+ * UCC and UCU.
+ */
+
+/* NCEEN enables instruction_access_error, data_access_error, and ECC_error traps
+ * for uncorrectable ECC errors and system errors.
+ *
+ * Uncorrectable system bus data error or MTAG ECC error, system bus TimeOUT,
+ * or system bus BusERR:
+ * 1) As the result of an instruction fetch, will generate instruction_access_error
+ * 2) As the result of a load etc. will generate data_access_error.
+ * 3) As the result of store merge completion, writeback, or copyout will
+ *    generate a disrupting ECC_error trap.
+ * 4) As the result of such errors on instruction vector fetch can generate any
+ *    of the 3 trap types.
+ *
+ * The AFSR bits assosciated with these traps are EMU, EDU, WDU, CPU, IVU, UE,
+ * BERR, and TO.
+ */
+
+/* CEEN enables the ECC_error trap for hardware corrected ECC errors.  System bus
+ * reads resulting in a hardware corrected data or MTAG ECC error will generate an
+ * ECC_error disrupting trap with this bit enabled.
+ *
+ * This same trap will also be generated when a hardware corrected ECC error results
+ * during store merge, writeback, and copyout operations.
+ */
+
+/* In general, if the trap enable bits above are disabled the AFSR bits will still
+ * log the events even though the trap will not be generated by the processor.
+ */
+
+#endif /* _SPARC64_ESTATE_H */
diff -urN linux-2.4.4-pre2/include/asm-sparc64/mc146818rtc.h linux/include/asm-sparc64/mc146818rtc.h
--- linux-2.4.4-pre2/include/asm-sparc64/mc146818rtc.h	Sun Mar 25 18:14:21 2001
+++ linux/include/asm-sparc64/mc146818rtc.h	Mon Dec  3 22:34:51 2001
@@ -4,6 +4,7 @@
 #ifndef __ASM_SPARC64_MC146818RTC_H
 #define __ASM_SPARC64_MC146818RTC_H
 
+#include <linux/config.h>
 #include <asm/io.h>
 
 #ifndef RTC_PORT
diff -urN linux-2.4.4-pre2/include/asm-sparc64/perfctr.h linux/include/asm-sparc64/perfctr.h
--- linux-2.4.4-pre2/include/asm-sparc64/perfctr.h	Tue Oct 27 09:52:21 1998
+++ linux/include/asm-sparc64/perfctr.h	Mon Dec  3 22:34:51 2001
@@ -60,10 +60,10 @@
 #ifndef __KERNEL__
 
 #define  PRIV 0x00000001
-#define  USR  0x00000002
-#define  SYS  0x00000004
+#define  SYS  0x00000002
+#define  USR  0x00000004
 
-/* Pic.S0 Selection Bit Field Encoding  */
+/* Pic.S0 Selection Bit Field Encoding, Ultra-I/II  */
 #define  CYCLE_CNT            0x00000000
 #define  INSTR_CNT            0x00000010
 #define  DISPATCH0_IC_MISS    0x00000020
@@ -77,7 +77,38 @@
 #define  EC_SNOOP_INV         0x000000E0
 #define  EC_RD_HIT            0x000000F0
 
-/* Pic.S1 Selection Bit Field Encoding  */
+/* Pic.S0 Selection Bit Field Encoding, Ultra-III  */
+#define  US3_CYCLE_CNT	      	0x00000000
+#define  US3_INSTR_CNT	      	0x00000010
+#define  US3_DISPATCH0_IC_MISS	0x00000020
+#define  US3_DISPATCH0_BR_TGT	0x00000030
+#define  US3_DISPATCH0_2ND_BR	0x00000040
+#define  US3_RSTALL_STOREQ	0x00000050
+#define  US3_RSTALL_IU_USE	0x00000060
+#define  US3_IC_REF		0x00000080
+#define  US3_DC_RD		0x00000090
+#define  US3_DC_WR		0x000000a0
+#define  US3_EC_REF		0x000000c0
+#define  US3_EC_WR_HIT_RTO	0x000000d0
+#define  US3_EC_SNOOP_INV	0x000000e0
+#define  US3_EC_RD_MISS		0x000000f0
+#define  US3_PC_PORT0_RD	0x00000100
+#define  US3_SI_SNOOP		0x00000110
+#define  US3_SI_CIQ_FLOW	0x00000120
+#define  US3_SI_OWNED		0x00000130
+#define  US3_SW_COUNT_0		0x00000140
+#define  US3_IU_BR_MISS_TAKEN	0x00000150
+#define  US3_IU_BR_COUNT_TAKEN	0x00000160
+#define  US3_DISP_RS_MISPRED	0x00000170
+#define  US3_FA_PIPE_COMPL	0x00000180
+#define  US3_MC_READS_0		0x00000200
+#define  US3_MC_READS_1		0x00000210
+#define  US3_MC_READS_2		0x00000220
+#define  US3_MC_READS_3		0x00000230
+#define  US3_MC_STALLS_0	0x00000240
+#define  US3_MC_STALLS_2	0x00000250
+
+/* Pic.S1 Selection Bit Field Encoding, Ultra-I/II  */
 #define  CYCLE_CNT_D1         0x00000000
 #define  INSTR_CNT_D1         0x00000800
 #define  DISPATCH0_IC_MISPRED 0x00001000
@@ -90,6 +121,47 @@
 #define  EC_WB                0x00006800
 #define  EC_SNOOP_CB          0x00007000
 #define  EC_IT_HIT            0x00007800
+
+/* Pic.S1 Selection Bit Field Encoding, Ultra-III  */
+#define  US3_CYCLE_CNT_D1	0x00000000
+#define  US3_INSTR_CNT_D1	0x00000800
+#define  US3_DISPATCH0_MISPRED	0x00001000
+#define  US3_IC_MISS_CANCELLED	0x00001800
+#define  US3_RE_ENDIAN_MISS	0x00002000
+#define  US3_RE_FPU_BYPASS	0x00002800
+#define  US3_RE_DC_MISS		0x00003000
+#define  US3_RE_EC_MISS		0x00003800
+#define  US3_IC_MISS		0x00004000
+#define  US3_DC_RD_MISS		0x00004800
+#define  US3_DC_WR_MISS		0x00005000
+#define  US3_RSTALL_FP_USE	0x00005800
+#define  US3_EC_MISSES		0x00006000
+#define  US3_EC_WB		0x00006800
+#define  US3_EC_SNOOP_CB	0x00007000
+#define  US3_EC_IC_MISS		0x00007800
+#define  US3_RE_PC_MISS		0x00008000
+#define  US3_ITLB_MISS		0x00008800
+#define  US3_DTLB_MISS		0x00009000
+#define  US3_WC_MISS		0x00009800
+#define  US3_WC_SNOOP_CB	0x0000a000
+#define  US3_WC_SCRUBBED	0x0000a800
+#define  US3_WC_WB_WO_READ	0x0000b000
+#define  US3_PC_SOFT_HIT	0x0000c000
+#define  US3_PC_SNOOP_INV	0x0000c800
+#define  US3_PC_HARD_HIT	0x0000d000
+#define  US3_PC_PORT1_RD	0x0000d800
+#define  US3_SW_COUNT_1		0x0000e000
+#define  US3_IU_STAT_BR_MIS_UNTAKEN	0x0000e800
+#define  US3_IU_STAT_BR_COUNT_UNTAKEN	0x0000f000
+#define  US3_PC_MS_MISSES	0x0000f800
+#define  US3_MC_WRITES_0	0x00010800
+#define  US3_MC_WRITES_1	0x00011000
+#define  US3_MC_WRITES_2	0x00011800
+#define  US3_MC_WRITES_3	0x00012000
+#define  US3_MC_STALLS_1	0x00012800
+#define  US3_MC_STALLS_3	0x00013000
+#define  US3_RE_RAW_MISS	0x00013800
+#define  US3_FM_PIPE_COMPLETION	0x00014000
 
 struct vcounter_struct {
   unsigned long long vcnt0;
diff -urN linux-2.4.4-pre2/include/asm-sparc64/pgalloc.h linux/include/asm-sparc64/pgalloc.h
--- linux-2.4.4-pre2/include/asm-sparc64/pgalloc.h	Mon Mar 26 11:03:48 2001
+++ linux/include/asm-sparc64/pgalloc.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: pgalloc.h,v 1.18 2001/03/24 09:36:01 davem Exp $ */
+/* $Id: pgalloc.h,v 1.19 2001/03/30 07:10:48 davem Exp $ */
 #ifndef _SPARC64_PGALLOC_H
 #define _SPARC64_PGALLOC_H
 
diff -urN linux-2.4.4-pre2/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- linux-2.4.4-pre2/include/asm-sparc64/pgtable.h	Sun Mar 25 18:14:21 2001
+++ linux/include/asm-sparc64/pgtable.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.138 2001/03/08 09:55:56 davem Exp $
+/* $Id: pgtable.h,v 1.139 2001/03/27 02:36:37 davem Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -302,9 +302,15 @@
 
 #include <asm-generic/pgtable.h>
 
-#endif /* !(__ASSEMBLY__) */
-
 /* We provide our own get_unmapped_area to cope with VA holes for userland */
 #define HAVE_ARCH_UNMAPPED_AREA
+
+/* We provide a special get_unmapped_area for framebuffer mmaps to try and use
+ * the largest alignment possible such that larget PTEs can be used.
+ */
+extern unsigned long get_fb_unmapped_area(struct file *filp, unsigned long, unsigned long, unsigned long, unsigned long);
+#define HAVE_ARCH_FB_UNMAPPED_AREA
+
+#endif /* !(__ASSEMBLY__) */
 
 #endif /* !(_SPARC64_PGTABLE_H) */
diff -urN linux-2.4.4-pre2/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- linux-2.4.4-pre2/include/asm-sparc64/processor.h	Sun Mar 25 18:14:21 2001
+++ linux/include/asm-sparc64/processor.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.69 2001/03/08 22:08:51 davem Exp $
+/* $Id: processor.h,v 1.70 2001/03/27 02:36:38 davem Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -79,7 +79,6 @@
 #define SPARC_FLAG_32BIT        0x04    /* task is older 32-bit binary		*/
 #define SPARC_FLAG_NEWCHILD     0x08    /* task is just-spawned child process	*/
 #define SPARC_FLAG_PERFCTR	0x10    /* task has performance counters active	*/
-#define SPARC_FLAG_MMAPSHARED	0x20    /* task wants a shared mmap             */
 
 #define FAULT_CODE_WRITE	0x01	/* Write access, implies D-TLB		*/
 #define FAULT_CODE_DTLB		0x02	/* Miss happened in D-TLB		*/
diff -urN linux-2.4.4-pre2/include/asm-sparc64/spitfire.h linux/include/asm-sparc64/spitfire.h
--- linux-2.4.4-pre2/include/asm-sparc64/spitfire.h	Sun Mar 25 18:14:21 2001
+++ linux/include/asm-sparc64/spitfire.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: spitfire.h,v 1.14 2001/03/22 07:26:04 davem Exp $
+/* $Id: spitfire.h,v 1.15 2001/03/27 00:10:15 davem Exp $
  * spitfire.h: SpitFire/BlackBird/Cheetah inline MMU operations.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -13,10 +13,15 @@
  * and ASI_IMMU, that is there is a distinct and unique copy of
  * each these registers for each TLB.
  */
-#define TSB_TAG_TARGET		0x0000000000000000
-#define TLB_SFSR		0x0000000000000018
-#define TSB_REG			0x0000000000000028
-#define TLB_TAG_ACCESS		0x0000000000000030
+#define TSB_TAG_TARGET		0x0000000000000000 /* All chips				*/
+#define TLB_SFSR		0x0000000000000018 /* All chips				*/
+#define TSB_REG			0x0000000000000028 /* All chips				*/
+#define TLB_TAG_ACCESS		0x0000000000000030 /* All chips				*/
+#define VIRT_WATCHPOINT		0x0000000000000038 /* All chips				*/
+#define PHYS_WATCHPOINT		0x0000000000000040 /* All chips				*/
+#define TSB_EXTENSION_P		0x0000000000000048 /* Ultra-III and later		*/
+#define TSB_EXTENSION_S		0x0000000000000050 /* Ultra-III and later, D-TLB only	*/
+#define TSB_EXTENSION_N		0x0000000000000058 /* Ultra-III and later		*/
 
 /* These registers only exist as one entity, and are accessed
  * via ASI_DMMU only.
diff -urN linux-2.4.4-pre2/include/asm-sparc64/ttable.h linux/include/asm-sparc64/ttable.h
--- linux-2.4.4-pre2/include/asm-sparc64/ttable.h	Wed Apr 12 09:12:35 2000
+++ linux/include/asm-sparc64/ttable.h	Mon Dec  3 22:34:51 2001
@@ -1,4 +1,4 @@
-/* $Id: ttable.h,v 1.15 2000/04/03 10:36:42 davem Exp $ */
+/* $Id: ttable.h,v 1.16 2001/03/28 10:56:34 davem Exp $ */
 #ifndef _SPARC64_TTABLE_H
 #define _SPARC64_TTABLE_H
 
@@ -54,13 +54,6 @@
 	 clr	%l6;					\
 	nop;
 	
-#define TRAPTL1_CEE			\
-	ldxa	[%g0] ASI_AFSR, %g1;	\
-	membar	#Sync;			\
-	stxa	%g1, [%g0] ASI_AFSR;	\
-	membar	#Sync;			\
-	retry; nop; nop; nop;
-
 #define TRAP_ARG(routine, arg)				\
 	sethi	%hi(109f), %g7;				\
 	ba,pt	%xcc, etrap;				\
diff -urN linux-2.4.4-pre2/include/linux/blk.h linux/include/linux/blk.h
--- linux-2.4.4-pre2/include/linux/blk.h	Mon Mar 26 15:48:46 2001
+++ linux/include/linux/blk.h	Mon Dec  3 22:34:51 2001
@@ -42,7 +42,6 @@
 extern int swimiop_init(void);
 extern int amiga_floppy_init(void);
 extern int atari_floppy_init(void);
-extern int nbd_init(void);
 extern int ez_init(void);
 extern int bpcd_init(void);
 extern int ps2esdi_init(void);
@@ -51,6 +50,7 @@
 #if defined(CONFIG_ARCH_S390)
 extern int mdisk_init(void);
 extern int dasd_init(void);
+extern int xpram_init(void);
 #endif /* CONFIG_ARCH_S390 */
 
 extern void set_device_ro(kdev_t dev,int flag);
diff -urN linux-2.4.4-pre2/include/linux/blkdev.h linux/include/linux/blkdev.h
--- linux-2.4.4-pre2/include/linux/blkdev.h	Mon Mar 26 15:48:13 2001
+++ linux/include/linux/blkdev.h	Mon Dec  3 22:34:51 2001
@@ -161,7 +161,6 @@
 extern void blk_init_queue(request_queue_t *, request_fn_proc *);
 extern void blk_cleanup_queue(request_queue_t *);
 extern void blk_queue_headactive(request_queue_t *, int);
-extern void blk_queue_pluggable(request_queue_t *, plug_device_fn *);
 extern void blk_queue_make_request(request_queue_t *, make_request_fn *);
 extern void generic_unplug_device(void *);
 
diff -urN linux-2.4.4-pre2/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.4.4-pre2/include/linux/fs.h	Mon Mar 26 15:48:11 2001
+++ linux/include/linux/fs.h	Mon Dec  3 22:34:51 2001
@@ -774,6 +774,8 @@
 	int (*lock) (struct file *, int, struct file_lock *);
 	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
 	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
+	ssize_t (*writepage) (struct file *, struct page *, int, size_t, loff_t *, int);
+	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
 };
 
 struct inode_operations {
@@ -1225,6 +1227,7 @@
 	}
 	return inode;
 }
+extern void remove_suid(struct inode *inode);
 
 extern void insert_inode_hash(struct inode *);
 extern void remove_inode_hash(struct inode *);
@@ -1272,6 +1275,7 @@
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
 
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
+extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
 extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t);
diff -urN linux-2.4.4-pre2/include/linux/icmp.h linux/include/linux/icmp.h
--- linux-2.4.4-pre2/include/linux/icmp.h	Mon Mar 26 15:48:11 2001
+++ linux/include/linux/icmp.h	Mon Dec  3 22:34:51 2001
@@ -80,30 +80,6 @@
   } un;
 };
 
-#ifdef __KERNEL__
-
-#include <linux/ip.h>
-
-/*
- *	Build xmit assembly blocks
- */
-
-struct icmp_bxm
-{
-	void *data_ptr;
-	int data_len;
-	struct icmphdr icmph;
-	unsigned long csum;
-	struct ip_options replyopts;
-	unsigned char  optbuf[40];
-};
-
-struct sk_buff;
-
-extern void icmp_reply(struct icmp_bxm *, struct sk_buff *);
-
-#endif
-
 /*
  *	constants for (set|get)sockopt
  */
diff -urN linux-2.4.4-pre2/include/linux/icmpv6.h linux/include/linux/icmpv6.h
--- linux-2.4.4-pre2/include/linux/icmpv6.h	Mon Mar 26 15:48:19 2001
+++ linux/include/linux/icmpv6.h	Mon Dec  3 22:34:51 2001
@@ -143,7 +143,7 @@
 							   int *err);
 extern void				icmpv6_cleanup(void);
 extern void				icmpv6_param_prob(struct sk_buff *skb,
-							  int code, void *pos);
+							  int code, int pos);
 #endif
 
 #endif
diff -urN linux-2.4.4-pre2/include/linux/if_packet.h linux/include/linux/if_packet.h
--- linux-2.4.4-pre2/include/linux/if_packet.h	Thu Aug 10 13:01:26 2000
+++ linux/include/linux/if_packet.h	Mon Dec  3 22:34:51 2001
@@ -53,6 +53,7 @@
 #define TP_STATUS_USER		1
 #define TP_STATUS_COPY		2
 #define TP_STATUS_LOSING	4
+#define TP_STATUS_CSUMNOTREADY	8
 	unsigned int	tp_len;
 	unsigned int	tp_snaplen;
 	unsigned short	tp_mac;
diff -urN linux-2.4.4-pre2/include/linux/if_wanpipe.h linux/include/linux/if_wanpipe.h
--- linux-2.4.4-pre2/include/linux/if_wanpipe.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/if_wanpipe.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,128 @@
+/*****************************************************************************
+* if_wanpipe.h	Header file for the Sangoma AF_WANPIPE Socket 	
+*
+* Author: 	Nenad Corbic 	
+*
+* Copyright:	(c) 2000 Sangoma Technologies Inc.
+*
+*		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.
+* ============================================================================
+*
+* Jan 28, 2000	Nenad Corbic 	Initial Version
+*
+*****************************************************************************/
+
+#ifndef __LINUX_IF_WAN_PACKET_H
+#define __LINUX_IF_WAN_PACKET_H
+
+struct wan_sockaddr_ll
+{
+	unsigned short	sll_family;
+	unsigned short	sll_protocol;
+	int		sll_ifindex;
+	unsigned short	sll_hatype;
+	unsigned char	sll_pkttype;
+	unsigned char	sll_halen;
+	unsigned char	sll_addr[8];
+	unsigned char   sll_device[14];
+	unsigned char 	sll_card[14];
+};
+
+typedef struct 
+{
+	unsigned char free;
+	unsigned char sk_state;
+	int rcvbuf;
+	int sndbuf;
+	int rmem;
+	int wmem;
+	int sk_count;
+	unsigned char bound;
+	char name[14];
+	unsigned char d_state;
+	unsigned char svc;
+	unsigned short lcn;
+	unsigned char mbox;
+	unsigned char cmd_busy;
+	unsigned char command;
+	unsigned poll;
+	unsigned poll_cnt;
+	int rblock;	
+} wan_debug_hdr_t;
+
+#define MAX_NUM_DEBUG  10
+#define X25_PROT       0x16
+#define PVC_PROT       0x17	
+
+typedef struct
+{
+	wan_debug_hdr_t debug[MAX_NUM_DEBUG];
+}wan_debug_t;
+
+#define	SIOC_WANPIPE_GET_CALL_DATA	(SIOCPROTOPRIVATE + 0)
+#define	SIOC_WANPIPE_SET_CALL_DATA	(SIOCPROTOPRIVATE + 1)
+#define SIOC_WANPIPE_ACCEPT_CALL	(SIOCPROTOPRIVATE + 2)
+#define SIOC_WANPIPE_CLEAR_CALL	        (SIOCPROTOPRIVATE + 3)
+#define SIOC_WANPIPE_RESET_CALL	        (SIOCPROTOPRIVATE + 4)
+#define SIOC_WANPIPE_DEBUG	        (SIOCPROTOPRIVATE + 5)
+#define SIOC_WANPIPE_SET_NONBLOCK	(SIOCPROTOPRIVATE + 6)
+#define SIOC_WANPIPE_CHECK_TX		(SIOCPROTOPRIVATE + 7)
+#define SIOC_WANPIPE_SOCK_STATE		(SIOCPROTOPRIVATE + 8)
+
+/* Packet types */
+
+#define WAN_PACKET_HOST		0		/* To us		*/
+#define WAN_PACKET_BROADCAST	1		/* To all		*/
+#define WAN_PACKET_MULTICAST	2		/* To group		*/
+#define WAN_PACKET_OTHERHOST	3		/* To someone else 	*/
+#define WAN_PACKET_OUTGOING		4		/* Outgoing of any type */
+/* These ones are invisible by user level */
+#define WAN_PACKET_LOOPBACK		5		/* MC/BRD frame looped back */
+#define WAN_PACKET_FASTROUTE	6		/* Fastrouted frame	*/
+
+
+/* X25 specific */
+#define WAN_PACKET_DATA 	7
+#define WAN_PACKET_CMD 		8
+#define WAN_PACKET_ASYNC	9
+#define WAN_PACKET_ERR	       10
+
+/* Packet socket options */
+
+#define WAN_PACKET_ADD_MEMBERSHIP		1
+#define WAN_PACKET_DROP_MEMBERSHIP		2
+
+#define WAN_PACKET_MR_MULTICAST	0
+#define WAN_PACKET_MR_PROMISC	1
+#define WAN_PACKET_MR_ALLMULTI	2
+
+#ifdef __KERNEL__
+#ifndef netdevice_t
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ #define netdevice_t struct net_device
+#else
+ #define netdevice_t struct device
+#endif
+#endif
+
+/* Private wanpipe socket structures. */
+struct wanpipe_opt
+{
+	void   *mbox;		/* Mail box  */
+	void   *card; 		/* Card bouded to */
+	netdevice_t *dev;	/* Bounded device */
+	unsigned short lcn;	/* Binded LCN */
+	unsigned char  svc;	/* 0=pvc, 1=svc */
+	unsigned char  timer;   /* flag for delayed transmit*/	
+	struct timer_list tx_timer;
+	unsigned poll_cnt;
+	unsigned char force;	/* Used to force sock release */
+	atomic_t packet_sent;   
+};
+#endif
+
+#endif
diff -urN linux-2.4.4-pre2/include/linux/if_wanpipe_common.h linux/include/linux/if_wanpipe_common.h
--- linux-2.4.4-pre2/include/linux/if_wanpipe_common.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/if_wanpipe_common.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,67 @@
+/*****************************************************************************
+* if_wanipe_common.h   Sangoma Driver/Socket common area definitions.
+*
+* Author:       Nenad Corbic <ncorbic@sangoma.com>
+*
+* Copyright:    (c) 2000 Sangoma Technologies Inc.
+*
+*               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.
+* ============================================================================
+* Jan 13, 2000  Nenad Corbic      Initial version
+*****************************************************************************/
+
+
+#ifndef _WANPIPE_SOCK_DRIVER_COMMON_H
+#define _WANPIPE_SOCK_DRIVER_COMMON_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ #define netdevice_t struct net_device
+#else
+ #define netdevice_t struct device
+#endif
+
+
+typedef struct {
+	netdevice_t *slave;
+	atomic_t packet_sent;
+	atomic_t receive_block;
+	atomic_t command;
+	atomic_t disconnect;
+	atomic_t driver_busy;
+	unsigned char common_critical;
+	struct timer_list *tx_timer;
+	struct sock *sk;		/* Wanpipe Sock bind's here */ 
+	int   (*func) (struct sk_buff *, netdevice_t *, 
+                       struct sock *);
+
+	struct tq_struct wanpipe_task;    /* Immediate BH handler task */
+	unsigned char rw_bind;			  /* Sock bind state */
+	unsigned char usedby;
+	unsigned char state;
+	unsigned char svc;
+	unsigned short lcn;
+	void *mbox;
+} wanpipe_common_t;
+
+
+enum {
+	WANSOCK_UNCONFIGURED,	/* link/channel is not configured */
+	WANSOCK_DISCONNECTED,	/* link/channel is disconnected */
+	WANSOCK_CONNECTING,		/* connection is in progress */
+	WANSOCK_CONNECTED,		/* link/channel is operational */
+	WANSOCK_LIMIT,		/* for verification only */
+	WANSOCK_DUALPORT,		/* for Dual Port cards */
+	WANSOCK_DISCONNECTING,
+	WANSOCK_BINDED,
+	WANSOCK_BIND_LISTEN,
+	WANSOCK_LISTEN
+};
+
+#endif
+
+
diff -urN linux-2.4.4-pre2/include/linux/igmp.h linux/include/linux/igmp.h
--- linux-2.4.4-pre2/include/linux/igmp.h	Mon Aug 23 10:01:02 1999
+++ linux/include/linux/igmp.h	Mon Dec  3 22:34:51 2001
@@ -104,7 +104,7 @@
 };
 
 extern int ip_check_mc(struct in_device *dev, u32 mc_addr);
-extern int igmp_rcv(struct sk_buff *, unsigned short);
+extern int igmp_rcv(struct sk_buff *);
 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
 extern void ip_mc_drop_socket(struct sock *sk);
diff -urN linux-2.4.4-pre2/include/linux/isdn.h linux/include/linux/isdn.h
--- linux-2.4.4-pre2/include/linux/isdn.h	Fri Feb  9 11:29:44 2001
+++ linux/include/linux/isdn.h	Mon Dec  3 22:34:51 2001
@@ -37,14 +37,8 @@
  * the correspondent code in isdn.c
  */
 
-#ifdef CONFIG_COBALT_MICRO_SERVER
-/* Save memory */
-#define ISDN_MAX_DRIVERS    2
-#define ISDN_MAX_CHANNELS   8
-#else
 #define ISDN_MAX_DRIVERS    32
 #define ISDN_MAX_CHANNELS   64
-#endif
 #define ISDN_MINOR_B        0
 #define ISDN_MINOR_BMAX     (ISDN_MAX_CHANNELS-1)
 #define ISDN_MINOR_CTRL     64
@@ -420,7 +414,7 @@
   struct net_device dev;               /* interface to upper levels        */
 #ifdef CONFIG_ISDN_PPP
   ippp_bundle * pb;		/* pointer to the common bundle structure
-   			         * with the the per-bundle data */
+   			         * with the per-bundle data */
 #endif
 #ifdef CONFIG_ISDN_X25
   struct concap_proto  *cprot; /* connection oriented encapsulation protocol */
diff -urN linux-2.4.4-pre2/include/linux/kernel.h linux/include/linux/kernel.h
--- linux-2.4.4-pre2/include/linux/kernel.h	Mon Dec 11 12:49:54 2000
+++ linux/include/linux/kernel.h	Mon Dec  3 22:34:51 2001
@@ -62,7 +62,7 @@
 extern int vsprintf(char *buf, const char *, va_list);
 extern int get_option(char **str, int *pint);
 extern char *get_options(char *str, int nints, int *ints);
-extern unsigned long memparse(char *ptr, char **retptr);
+extern unsigned long long memparse(char *ptr, char **retptr);
 extern void dev_probe_lock(void);
 extern void dev_probe_unlock(void);
 
diff -urN linux-2.4.4-pre2/include/linux/major.h linux/include/linux/major.h
--- linux-2.4.4-pre2/include/linux/major.h	Sat Dec 30 11:23:14 2000
+++ linux/include/linux/major.h	Mon Dec  3 22:34:51 2001
@@ -128,6 +128,8 @@
 #define IDE8_MAJOR	90
 #define IDE9_MAJOR	91
 
+#define UBD_MAJOR	98
+
 #define AURORA_MAJOR 79
 
 #define JSFD_MAJOR	99
@@ -155,6 +157,9 @@
 #define CPUID_MAJOR		203
 
 #define OSST_MAJOR	206	/* OnStream-SCx0 SCSI tape */
+
+#define IBM_TTY3270_MAJOR       227	/* Official allocations now */
+#define IBM_FS3270_MAJOR        228
 
 /*
  * Tests for SCSI devices.
diff -urN linux-2.4.4-pre2/include/linux/miscdevice.h linux/include/linux/miscdevice.h
--- linux-2.4.4-pre2/include/linux/miscdevice.h	Mon Mar 26 15:48:22 2001
+++ linux/include/linux/miscdevice.h	Mon Dec  3 22:34:51 2001
@@ -21,6 +21,7 @@
 #define NVRAM_MINOR 144
 #define I2O_MINOR 166
 #define MICROCODE_MINOR		184
+#define MPT_MINOR	220
 #define MISC_DYNAMIC_MINOR 255
 
 #define SGI_GRAPHICS_MINOR   146
diff -urN linux-2.4.4-pre2/include/linux/mm.h linux/include/linux/mm.h
--- linux-2.4.4-pre2/include/linux/mm.h	Mon Mar 26 15:48:13 2001
+++ linux/include/linux/mm.h	Mon Dec  3 22:34:51 2001
@@ -434,7 +434,8 @@
 extern void __insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
 extern void build_mmap_avl(struct mm_struct *);
 extern void exit_mmap(struct mm_struct *);
-extern unsigned long get_unmapped_area(unsigned long, unsigned long);
+
+extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
 
 extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 	unsigned long len, unsigned long prot,
diff -urN linux-2.4.4-pre2/include/linux/mroute.h linux/include/linux/mroute.h
--- linux-2.4.4-pre2/include/linux/mroute.h	Mon Mar 26 15:48:59 2001
+++ linux/include/linux/mroute.h	Mon Dec  3 22:34:51 2001
@@ -215,8 +215,8 @@
 	__u32	flags;
 };
 
-extern int pim_rcv(struct sk_buff * , unsigned short);
-extern int pim_rcv_v1(struct sk_buff * , unsigned short len);
+extern int pim_rcv(struct sk_buff *);
+extern int pim_rcv_v1(struct sk_buff *);
 
 struct rtmsg;
 extern int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait);
diff -urN linux-2.4.4-pre2/include/linux/net.h linux/include/linux/net.h
--- linux-2.4.4-pre2/include/linux/net.h	Mon Mar 26 15:48:11 2001
+++ linux/include/linux/net.h	Mon Dec  3 22:34:51 2001
@@ -82,6 +82,7 @@
 
 struct scm_cookie;
 struct vm_area_struct;
+struct page;
 
 struct proto_ops {
   int	family;
@@ -108,6 +109,7 @@
   int   (*sendmsg)	(struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm);
   int   (*recvmsg)	(struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm);
   int	(*mmap)		(struct file *file, struct socket *sock, struct vm_area_struct * vma);
+  ssize_t (*sendpage)	(struct socket *sock, struct page *page, int offset, size_t size, int flags);
 };
 
 struct net_proto_family 
diff -urN linux-2.4.4-pre2/include/linux/netdevice.h linux/include/linux/netdevice.h
--- linux-2.4.4-pre2/include/linux/netdevice.h	Mon Mar 26 15:48:19 2001
+++ linux/include/linux/netdevice.h	Mon Dec  3 22:34:51 2001
@@ -653,6 +653,7 @@
 extern unsigned long	netdev_fc_xoff;
 extern atomic_t netdev_dropping;
 extern int		netdev_set_master(struct net_device *dev, struct net_device *master);
+extern struct sk_buff * skb_checksum_help(struct sk_buff *skb);
 #ifdef CONFIG_NET_FASTROUTE
 extern int		netdev_fastroute;
 extern int		netdev_fastroute_obstacles;
diff -urN linux-2.4.4-pre2/include/linux/rwsem-spinlock.h linux/include/linux/rwsem-spinlock.h
--- linux-2.4.4-pre2/include/linux/rwsem-spinlock.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/rwsem-spinlock.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,174 @@
+/* rwsem-spinlock.h: fallback C implementation
+ *
+ * Copyright (c) 2001   David Howells (dhowells@redhat.com).
+ */
+
+#ifndef _LINUX_RWSEM_SPINLOCK_H
+#define _LINUX_RWSEM_SPINLOCK_H
+
+#ifndef _LINUX_RWSEM_H
+#error please dont include asm/rwsem-spinlock.h directly, use linux/rwsem.h instead
+#endif
+
+#include <linux/spinlock.h>
+
+#ifdef __KERNEL__
+
+#define CONFIG_USING_SPINLOCK_BASED_RWSEM 1
+
+/*
+ * the semaphore definition
+ */
+struct rw_semaphore {
+	signed long			count;
+#define RWSEM_UNLOCKED_VALUE		0x00000000
+#define RWSEM_ACTIVE_BIAS		0x00000001
+#define RWSEM_ACTIVE_MASK		0x0000ffff
+#define RWSEM_WAITING_BIAS		(-0x00010000)
+#define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
+#define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
+	spinlock_t		lock;
+#define RWSEM_SPINLOCK_OFFSET_STR	"4" /* byte offset of spinlock */
+	wait_queue_head_t	wait;
+#define RWSEM_WAITING_FOR_READ	WQ_FLAG_CONTEXT_0	/* bits to use in wait_queue_t.flags */
+#define RWSEM_WAITING_FOR_WRITE	WQ_FLAG_CONTEXT_1
+#if RWSEM_DEBUG
+	int			debug;
+#endif
+#if RWSEM_DEBUG_MAGIC
+	long			__magic;
+	atomic_t		readers;
+	atomic_t		writers;
+#endif
+};
+
+/*
+ * initialisation
+ */
+#if RWSEM_DEBUG
+#define __RWSEM_DEBUG_INIT      , 0
+#else
+#define __RWSEM_DEBUG_INIT	/* */
+#endif
+#if RWSEM_DEBUG_MAGIC
+#define __RWSEM_DEBUG_MINIT(name)	, (int)&(name).__magic, ATOMIC_INIT(0), ATOMIC_INIT(0)
+#else
+#define __RWSEM_DEBUG_MINIT(name)	/* */
+#endif
+
+#define __RWSEM_INITIALIZER(name,count) \
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \
+	__WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+	__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
+
+#define __DECLARE_RWSEM_GENERIC(name,count) \
+	struct rw_semaphore name = __RWSEM_INITIALIZER(name,count)
+
+#define DECLARE_RWSEM(name) __DECLARE_RWSEM_GENERIC(name,RW_LOCK_BIAS)
+#define DECLARE_RWSEM_READ_LOCKED(name) __DECLARE_RWSEM_GENERIC(name,RW_LOCK_BIAS-1)
+#define DECLARE_RWSEM_WRITE_LOCKED(name) __DECLARE_RWSEM_GENERIC(name,0)
+
+static inline void init_rwsem(struct rw_semaphore *sem)
+{
+	sem->count = RWSEM_UNLOCKED_VALUE;
+	spin_lock_init(&sem->lock);
+	init_waitqueue_head(&sem->wait);
+#if RWSEM_DEBUG
+	sem->debug = 0;
+#endif
+#if RWSEM_DEBUG_MAGIC
+	sem->__magic = (long)&sem->__magic;
+	atomic_set(&sem->readers, 0);
+	atomic_set(&sem->writers, 0);
+#endif
+}
+
+/*
+ * lock for reading
+ */
+static inline void __down_read(struct rw_semaphore *sem)
+{
+	int count;
+	spin_lock(&sem->lock);
+	sem->count += RWSEM_ACTIVE_READ_BIAS;
+	count = sem->count;
+	spin_unlock(&sem->lock);
+	if (count<0)
+		rwsem_down_read_failed(sem);
+}
+
+/*
+ * lock for writing
+ */
+static inline void __down_write(struct rw_semaphore *sem)
+{
+	int count;
+	spin_lock(&sem->lock);
+	count = sem->count;
+	sem->count += RWSEM_ACTIVE_WRITE_BIAS;
+	spin_unlock(&sem->lock);
+	if (count)
+		rwsem_down_write_failed(sem);
+}
+
+/*
+ * unlock after reading
+ */
+static inline void __up_read(struct rw_semaphore *sem)
+{
+	int count;
+	spin_lock(&sem->lock);
+	count = sem->count;
+	sem->count -= RWSEM_ACTIVE_READ_BIAS;
+	spin_unlock(&sem->lock);
+	if (count<0 && !((count-RWSEM_ACTIVE_READ_BIAS)&RWSEM_ACTIVE_MASK))
+		rwsem_wake(sem);
+}
+
+/*
+ * unlock after writing
+ */
+static inline void __up_write(struct rw_semaphore *sem)
+{
+	int count;
+	spin_lock(&sem->lock);
+	sem->count -= RWSEM_ACTIVE_WRITE_BIAS;
+	count = sem->count;
+	spin_unlock(&sem->lock);
+	if (count<0)
+		rwsem_wake(sem);
+}
+
+/*
+ * implement exchange and add functionality
+ */
+static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
+{
+	int count;
+
+	spin_lock(&sem->lock);
+	sem->count += delta;
+	count = sem->count;
+	spin_unlock(&sem->lock);
+
+	return count;
+}
+
+/*
+ * implement compare and exchange functionality on the rw-semaphore count LSW
+ */
+static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
+{
+	__u16 prev;
+
+	spin_lock(&sem->lock);
+	prev = sem->count & RWSEM_ACTIVE_MASK;
+	if (prev==old)
+		sem->count = (sem->count & ~RWSEM_ACTIVE_MASK) | new;
+	spin_unlock(&sem->lock);
+
+	return prev;
+}
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_RWSEM_SPINLOCK_H */
diff -urN linux-2.4.4-pre2/include/linux/rwsem.h linux/include/linux/rwsem.h
--- linux-2.4.4-pre2/include/linux/rwsem.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/rwsem.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,148 @@
+/* rwsem.h: R/W semaphores, public interface
+ *
+ * Written by David Howells (dhowells@redhat.com).
+ * Derived from asm-i386/semaphore.h
+ *
+ *
+ * The MSW of the count is the negated number of active writers and waiting
+ * lockers, and the LSW is the total number of active locks
+ *
+ * The lock count is initialized to 0 (no active and no waiting lockers).
+ *
+ * When a writer subtracts WRITE_BIAS, it'll get 0xffff0001 for the case of an
+ * uncontended lock. This can be determined because XADD returns the old value.
+ * Readers increment by 1 and see a positive value when uncontended, negative
+ * if there are writers (and maybe) readers waiting (in which case it goes to
+ * sleep).
+ *
+ * The value of WAITING_BIAS supports up to 32766 waiting processes. This can
+ * be extended to 65534 by manually checking the whole MSW rather than relying
+ * on the S flag.
+ *
+ * The value of ACTIVE_BIAS supports up to 65535 active processes.
+ *
+ * This should be totally fair - if anything is waiting, a process that wants a
+ * lock will go to the back of the queue. When the currently active lock is
+ * released, if there's a writer at the front of the queue, then that and only
+ * that will be woken up; if there's a bunch of consequtive readers at the
+ * front, then they'll all be woken up, but no other readers will be.
+ */
+
+#ifndef _LINUX_RWSEM_H
+#define _LINUX_RWSEM_H
+
+#include <linux/linkage.h>
+
+#define RWSEM_DEBUG 0
+#define RWSEM_DEBUG_MAGIC 0
+
+#ifdef __KERNEL__
+
+#include <asm/system.h>
+#include <asm/atomic.h>
+#include <linux/wait.h>
+
+#if RWSEM_DEBUG
+#define rwsemdebug(FMT, ARGS...) do { if (sem->debug) printk(FMT,##ARGS); } while(0)
+#else
+#define rwsemdebug(FMT, ARGS...)
+#endif
+
+/* we use FASTCALL convention for the helpers */
+extern struct rw_semaphore *FASTCALL(rwsem_down_read_failed(struct rw_semaphore *sem));
+extern struct rw_semaphore *FASTCALL(rwsem_down_write_failed(struct rw_semaphore *sem));
+extern struct rw_semaphore *FASTCALL(rwsem_wake(struct rw_semaphore *sem));
+
+#include <asm/rwsem.h> /* find the arch specific bits */
+
+#ifndef __HAVE_ARCH_SPECIFIC_RWSEM_IMPLEMENTATION
+#include <linux/rwsem-spinlock.h>
+#endif
+
+/*
+ * lock for reading
+ */
+static inline void down_read(struct rw_semaphore *sem)
+{
+	rwsemdebug("Entering down_read(count=%08lx)\n",sem->count);
+
+#if RWSEM_DEBUG_MAGIC
+	if (sem->__magic != (long)&sem->__magic)
+		BUG();
+#endif
+
+	__down_read(sem);
+
+#if RWSEM_DEBUG_MAGIC
+	if (atomic_read(&sem->writers))
+		BUG();
+	atomic_inc(&sem->readers);
+#endif
+
+	rwsemdebug("Leaving down_read(count=%08lx)\n",sem->count);
+}
+
+/*
+ * lock for writing
+ */
+static inline void down_write(struct rw_semaphore *sem)
+{
+	rwsemdebug("Entering down_write(count=%08lx)\n",sem->count);
+
+#if RWSEM_DEBUG_MAGIC
+	if (sem->__magic != (long)&sem->__magic)
+		BUG();
+#endif
+
+	__down_write(sem);
+
+#if RWSEM_DEBUG_MAGIC
+	if (atomic_read(&sem->writers))
+		BUG();
+	if (atomic_read(&sem->readers))
+		BUG();
+	atomic_inc(&sem->writers);
+#endif
+
+	rwsemdebug("Leaving down_write(count=%08lx)\n",sem->count);
+}
+
+/*
+ * release a read lock
+ */
+static inline void up_read(struct rw_semaphore *sem)
+{
+	rwsemdebug("Entering up_read(count=%08lx)\n",sem->count);
+
+#if RWSEM_DEBUG_MAGIC
+	if (atomic_read(&sem->writers))
+		BUG();
+	atomic_dec(&sem->readers);
+#endif
+	__up_read(sem);
+
+	rwsemdebug("Leaving up_read(count=%08lx)\n",sem->count);
+}
+
+/*
+ * release a write lock
+ */
+static inline void up_write(struct rw_semaphore *sem)
+{
+	rwsemdebug("Entering up_write(count=%08lx)\n",sem->count);
+
+#if RWSEM_DEBUG_MAGIC
+	if (atomic_read(&sem->readers))
+		BUG();
+	if (atomic_read(&sem->writers) != 1)
+		BUG();
+	atomic_dec(&sem->writers);
+#endif
+	__up_write(sem);
+
+	rwsemdebug("Leaving up_write(count=%08lx)\n",sem->count);
+}
+
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_RWSEM_H */
diff -urN linux-2.4.4-pre2/include/linux/scc.h linux/include/linux/scc.h
--- linux-2.4.4-pre2/include/linux/scc.h	Thu Jan  4 12:50:12 2001
+++ linux/include/linux/scc.h	Mon Dec  3 22:34:51 2001
@@ -213,8 +213,6 @@
 /* SCC channel structure */
 
 struct scc_channel {
-	int magic;			/* magic word */
-
 	int init;			/* channel exists? */
 
 	struct net_device *dev;		/* link to device control structure */
diff -urN linux-2.4.4-pre2/include/linux/sdla_asy.h linux/include/linux/sdla_asy.h
--- linux-2.4.4-pre2/include/linux/sdla_asy.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sdla_asy.h	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,226 @@
+/*****************************************************************************
+* sdla_asy.h	Header file for the Sangoma S508/S514 asynchronous code API	
+*
+* Author: 	Gideon Hack 	
+*
+* Copyright:	(c) 2000 Sangoma Technologies Inc.
+*
+*		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.
+* ============================================================================
+*
+* Jan 28, 2000	Gideon Hack 	Initial Version
+*
+*****************************************************************************/
+
+
+#ifndef _WANPIPE_ASYNC_H
+#define _WANPIPE_ASYNC_H
+
+/* ----------------------------------------------------------------------------
+ *                        Interface commands
+ * --------------------------------------------------------------------------*/
+
+#define SET_ASY_CONFIGURATION		0xE2	/* set the asychronous operational configuration */
+#define READ_ASY_CONFIGURATION		0xE3	/* read the current asychronous operational configuration */
+#define ENABLE_ASY_COMMUNICATIONS	0xE4	/* enable asychronous communications */
+#define DISABLE_ASY_COMMUNICATIONS	0xE5	/* disable asychronous communications */
+#define READ_ASY_OPERATIONAL_STATS	0xE7	/* retrieve the asychronous operational statistics */
+#define FLUSH_ASY_OPERATIONAL_STATS	0xE8	/* flush the asychronous operational statistics */
+#define TRANSMIT_ASY_BREAK_SIGNAL	0xEC	/* transmit an asychronous break signal */
+
+
+
+/* ----------------------------------------------------------------------------
+ *                     Return codes from interface commands
+ * --------------------------------------------------------------------------*/
+
+#define COMMAND_INVALID_FOR_PORT	0x50	/* the command is invalid for the selected port */
+#define DISABLE_ASY_COMMS_BEFORE_CFG 	0xE1	/* communications must be disabled before setting the configuration */
+#define ASY_COMMS_ENABLED		0xE1	/* communications are currently enabled */
+#define ASY_COMMS_DISABLED		0xE1	/* communications are currently disabled */
+#define ASY_CFG_BEFORE_COMMS_ENABLED	0xE2	/* perform a SET_ASY_CONFIGURATION before enabling comms */
+#define LGTH_ASY_CFG_DATA_INVALID  	0xE2	/* the length of the passed configuration data is invalid */
+#define INVALID_ASY_CFG_DATA		0xE3	/* the passed configuration data is invalid */
+#define ASY_BREAK_SIGNAL_BUSY		0xEC	/* a break signal is being transmitted */
+
+
+
+/* ----------------------------------------------------------------------------
+ *   Constants for the SET_ASY_CONFIGURATION/READ_ASY_CONFIGURATION command
+ * --------------------------------------------------------------------------*/
+
+/* the asynchronous configuration structure */
+typedef struct {
+	unsigned long baud_rate			PACKED;							/* the baud rate */	
+	unsigned short line_config_options	PACKED;	/* line configuration options */
+	unsigned short modem_config_options	PACKED;	/* modem configuration options */
+	unsigned short asy_API_options		PACKED;	/* asynchronous API options */
+	unsigned short asy_protocol_options	PACKED; /* asynchronous protocol options */
+	unsigned short Tx_bits_per_char		PACKED;	/* number of bits per tx character */
+	unsigned short Rx_bits_per_char		PACKED;	/* number of bits per received character */
+	unsigned short stop_bits		PACKED;	/* number of stop bits per character */
+	unsigned short parity			PACKED;	/* parity definition */
+	unsigned short break_timer		PACKED;	/* the break signal timer */
+	unsigned short asy_Rx_inter_char_timer	PACKED;	/* the receive inter-character timer */
+	unsigned short asy_Rx_complete_length	PACKED;	/* the receive 'buffer complete' length */
+	unsigned short XON_char			PACKED;	/* the XON character */
+	unsigned short XOFF_char		PACKED;	/* the XOFF character */
+	unsigned short asy_statistics_options	PACKED;	/* async operational stat options */
+	unsigned long ptr_shared_mem_info_struct    PACKED;/* ptr to the shared memory area information structure */
+	unsigned long ptr_asy_Tx_stat_el_cfg_struct PACKED;/* ptr to the transmit status element configuration structure */
+	unsigned long ptr_asy_Rx_stat_el_cfg_struct PACKED;/* ptr to the receive status element configuration structure */
+} ASY_CONFIGURATION_STRUCT;
+
+/* permitted minimum and maximum values for setting the asynchronous configuration */
+#define MIN_ASY_BAUD_RATE		50	/* maximum baud rate */
+#define MAX_ASY_BAUD_RATE		250000	/* minimum baud rate */
+#define MIN_ASY_BITS_PER_CHAR		5	/* minimum number of bits per character */
+#define MAX_ASY_BITS_PER_CHAR		8	/* maximum number of bits per character */
+#define MIN_BREAK_TMR_VAL		0	/* minimum break signal timer */
+#define MAX_BREAK_TMR_VAL		5000	/* maximum break signal timer */
+#define MIN_ASY_RX_INTER_CHAR_TMR	0	/* minimum receive inter-character timer */
+#define MAX_ASY_RX_INTER_CHAR_TMR	30000	/* maximum receive inter-character timer */
+#define MIN_ASY_RX_CPLT_LENGTH		0	/* minimum receive 'length complete' value */
+#define MAX_ASY_RX_CPLT_LENGTH		2000	/* maximum receive 'length complete' value */
+
+/* bit settings for the 'asy_API_options' */
+#define ASY_RX_DATA_TRANSPARENT		0x0001	/* do not strip parity and unused bits from received characters */
+
+/* bit settings for the 'asy_protocol_options' */
+#define ASY_RTS_HS_FOR_RX		0x0001	/* RTS handshaking is used for reception control */
+#define ASY_XON_XOFF_HS_FOR_RX		0x0002	/* XON/XOFF handshaking is used for reception control */
+#define ASY_XON_XOFF_HS_FOR_TX		0x0004	/* XON/XOFF handshaking is used for transmission control */
+#define ASY_DCD_HS_FOR_TX		0x0008	/* DCD handshaking is used for transmission control */
+#define ASY_CTS_HS_FOR_TX		0x0020	/* CTS handshaking is used for transmission control */
+
+/* bit settings for the 'stop_bits' definition */
+#define ONE_STOP_BIT			1			/* representation for 1 stop bit */
+#define TWO_STOP_BITS			2			/* representation for 2 stop bits */
+#define ONE_AND_A_HALF_STOP_BITS	3			/* representation for 1.5 stop bits */
+
+/* bit settings for the 'parity' definition */
+#define NO_PARITY			0			/* representation for no parity */
+#define ODD_PARITY			1			/* representation for odd parity */
+#define EVEN_PARITY			2			/* representation for even parity */
+
+
+
+/* ----------------------------------------------------------------------------
+ *    Constants for the READ_COMMS_ERROR_STATS command (asynchronous mode)
+ * --------------------------------------------------------------------------*/
+
+/* the communications error statistics structure */
+typedef struct {
+	unsigned short Rx_overrun_err_count   	PACKED;	/* receiver overrun error count */
+	unsigned short Rx_parity_err_count	PACKED;	/* parity errors received count */
+	unsigned short Rx_framing_err_count	PACKED;	/* framing errors received count */
+	unsigned short comms_err_stat_reserved_1 PACKED;/* reserved for later use */
+	unsigned short comms_err_stat_reserved_2 PACKED;/* reserved for later use */
+	unsigned short comms_err_stat_reserved_3 PACKED;/* reserved for later use */
+	unsigned short comms_err_stat_reserved_4 PACKED;/* reserved for later use */
+	unsigned short comms_err_stat_reserved_5 PACKED;/* reserved for later use */
+	unsigned short DCD_state_change_count 	PACKED;	/* DCD state change count */
+	unsigned short CTS_state_change_count	PACKED;	/* CTS state change count */
+} ASY_COMMS_ERROR_STATS_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ *         Constants for the READ_ASY_OPERATIONAL_STATS command
+ * --------------------------------------------------------------------------*/
+
+/* the asynchronous operational statistics structure */
+typedef struct {
+
+	/* Data transmission statistics */
+	unsigned long Data_blocks_Tx_count PACKED;/* number of blocks transmitted */
+	unsigned long Data_bytes_Tx_count  PACKED;/* number of bytes transmitted */
+	unsigned long Data_Tx_throughput   PACKED;/* transmit throughput */
+	unsigned long no_ms_for_Data_Tx_thruput_comp PACKED;/* millisecond time used for the Tx throughput computation */
+	unsigned long Tx_Data_discard_lgth_err_count PACKED;/* number of Data blocks discarded (length error) */
+	unsigned long reserved_Data_frm_Tx_stat1 PACKED;/* reserved for later use */
+	unsigned long reserved_Data_frm_Tx_stat2 PACKED;/* reserved for later use */
+	unsigned long reserved_Data_frm_Tx_stat3 PACKED;/* reserved for later use */
+
+	/* Data reception statistics */
+	unsigned long Data_blocks_Rx_count PACKED;/* number of blocks received */
+	unsigned long Data_bytes_Rx_count  PACKED;/* number of bytes received */
+	unsigned long Data_Rx_throughput   PACKED;/* receive throughput */
+	unsigned long no_ms_for_Data_Rx_thruput_comp PACKED;/* millisecond time used for the Rx throughput computation */
+	unsigned long Rx_Data_bytes_discard_count    PACKED;/* received Data bytes discarded */
+	unsigned long reserved_Data_frm_Rx_stat1     PACKED;/* reserved for later use */
+
+	/* handshaking protocol statistics */
+	unsigned short XON_chars_Tx_count	PACKED;	/* number of XON characters transmitted */
+	unsigned short XOFF_chars_Tx_count	PACKED;	/* number of XOFF characters transmitted */
+	unsigned short XON_chars_Rx_count	PACKED;	/* number of XON characters received */
+	unsigned short XOFF_chars_Rx_count	PACKED;	/* number of XOFF characters received */
+	unsigned short Tx_halt_modem_low_count	PACKED; /* number of times Tx halted (modem line low) */
+	unsigned short Rx_halt_RTS_low_count	PACKED;	/* number of times Rx halted by setting RTS low */
+	unsigned long reserved_handshaking_stat1 PACKED;/* reserved for later use */
+
+	/* break statistics */
+	unsigned short break_Tx_count	PACKED;	/* number of break sequences transmitted */
+	unsigned short break_Rx_count	PACKED;	/* number of break sequences received */
+	unsigned long reserved_break_stat1 PACKED;/* reserved for later use */
+
+	/* miscellaneous statistics */
+	unsigned long reserved_misc_stat1	PACKED;	/* reserved for later use */
+	unsigned long reserved_misc_stat2	PACKED;	/* reserved for later use */
+
+} ASY_OPERATIONAL_STATS_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ *                      Constants for Data transmission
+ * --------------------------------------------------------------------------*/
+
+/* the Data block transmit status element configuration structure */
+typedef struct {
+	unsigned short number_Tx_status_elements PACKED;		/* number of transmit status elements */
+	unsigned long base_addr_Tx_status_elements PACKED;	/* base address of the transmit element list */
+	unsigned long next_Tx_status_element_to_use PACKED;	/* pointer to the next transmit element to be used */
+} ASY_TX_STATUS_EL_CFG_STRUCT;
+
+
+/* the Data block transmit status element structure */
+typedef struct {
+	unsigned char opp_flag PACKED;								/* opp flag */
+	unsigned short data_length PACKED;						/* length of the block to be transmitted */
+	unsigned char reserved_1 PACKED;							/* reserved for internal use */
+	unsigned long reserved_2 PACKED;							/* reserved for internal use */
+	unsigned long reserved_3 PACKED;							/* reserved for internal use */
+	unsigned long ptr_data_bfr PACKED;						/* pointer to the data area */
+} ASY_DATA_TX_STATUS_EL_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ *                      Constants for Data reception
+ * --------------------------------------------------------------------------*/
+
+/* the Data block receive status element configuration structure */
+typedef struct {
+	unsigned short number_Rx_status_elements    PACKED;/* number of receive status elements */
+	unsigned long base_addr_Rx_status_elements  PACKED;/* base address of the receive element list */
+	unsigned long next_Rx_status_element_to_use PACKED;/* pointer to the next receive element to be used */
+	unsigned long base_addr_Rx_buffer	PACKED;/* base address of the receive data buffer */
+	unsigned long end_addr_Rx_buffer 	PACKED;/* end address of the receive data buffer */
+} ASY_RX_STATUS_EL_CFG_STRUCT;
+
+/* the Data block receive status element structure */
+typedef struct {
+	unsigned char opp_flag 		PACKED;	/* opp flag */
+	unsigned short data_length 	PACKED;	/* length of the received data block */
+	unsigned char reserved_1 	PACKED;	/* reserved for internal use */
+	unsigned short time_stamp 	PACKED; /* receive time stamp (HDLC_STREAMING_MODE) */
+	unsigned short data_buffered 	PACKED;	/* the number of data bytes still buffered */
+	unsigned long reserved_2 	PACKED;	/* reserved for internal use */
+	unsigned long ptr_data_bfr 	PACKED;	/* pointer to the data area */
+} ASY_DATA_RX_STATUS_EL_STRUCT;
+
+#endif
diff -urN linux-2.4.4-pre2/include/linux/sdla_chdlc.h linux/include/linux/sdla_chdlc.h
--- linux-2.4.4-pre2/include/linux/sdla_chdlc.h	Wed Jan 26 13:25:58 2000
+++ linux/include/linux/sdla_chdlc.h	Mon Dec  3 22:34:51 2001
@@ -4,7 +4,7 @@
  Author:      	Gideon Hack
 		Nenad Corbic <ncorbic@sangoma.com>	
 
- Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+ Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 
 		This program is free software; you can redistribute it and/or
 		modify it under the term of the GNU General Public License
@@ -137,6 +137,8 @@
 #define TRACE_PROT			0x01
 #define TRACE_DATA			0x02
 
+#define DISCARD_RX_ERROR_FRAMES	0x0001
+
 /* ----------------------------------------------------------------------------
  *                     Return codes from interface commands
  * --------------------------------------------------------------------------*/
@@ -358,6 +360,9 @@
 #define IGNORE_KPALV_FOR_LINK_STAT		0x0004
 /* ignore keepalive frames in determining the CHDLC link status */ 
 
+#define SINGLE_TX_BUFFER			0x4000 
+/* configure a single transmit buffer */
+
 #define HDLC_STREAMING_MODE			0x8000
 
 /*   settings for the 'CHDLC_statistics_options' */
@@ -469,7 +474,7 @@
 	unsigned long reserved_CDP_stat5 PACKED;		/* reserved for later */
 	unsigned long reserved_CDP_stat6 PACKED;		/* reserved for later */
 
-	/* Incomming frames with a format error statistics */
+	/* Incoming frames with a format error statistics */
 	unsigned short Rx_frm_incomp_CHDLC_hdr_count PACKED;	/* frames received of with incomplete Cisco HDLC header */
 	unsigned short Rx_frms_too_long_count PACKED;		/* frames received of excessive length count */
 	unsigned short Rx_invalid_CHDLC_addr_count PACKED;	/* frames received with an invalid CHDLC address count */
@@ -484,7 +489,7 @@
 	unsigned long reserved_frm_format_err4 PACKED;		/* reserved for later */
 
 	/* CHDLC timeout/retry statistics */
-	unsigned short SLARP_Rx_keepalive_TO_count PACKED;	/* timeout count for incomming SLARP frames */
+	unsigned short SLARP_Rx_keepalive_TO_count PACKED;	/* timeout count for incoming SLARP frames */
 	unsigned short SLARP_Request_TO_count PACKED;		/* timeout count for SLARP Request frames */
 	unsigned long To_retry_reserved_stat1 PACKED;		/* reserved for later */
 	unsigned long To_retry_reserved_stat2 PACKED;		/* reserved for later */
diff -urN linux-2.4.4-pre2/include/linux/sdla_fr.h linux/include/linux/sdla_fr.h
--- linux-2.4.4-pre2/include/linux/sdla_fr.h	Wed Jan 26 13:25:58 2000
+++ linux/include/linux/sdla_fr.h	Mon Dec  3 22:34:51 2001
@@ -4,7 +4,7 @@
 * Author:       Gideon Hack  	
 *		Nenad Corbic <ncorbic@sangoma.com> 	
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
@@ -541,7 +541,7 @@
 } fr_encap_hdr_t;
 
 typedef struct {
-	fr_encap_hdr_t 		fr_encap_hdr	PACKED;
+//	fr_encap_hdr_t 		fr_encap_hdr	PACKED;
 	ip_pkt_t 		ip_pkt		PACKED;
 	udp_pkt_t		udp_pkt		PACKED;
 	wp_mgmt_t 		wp_mgmt       	PACKED;
diff -urN linux-2.4.4-pre2/include/linux/sdla_ppp.h linux/include/linux/sdla_ppp.h
--- linux-2.4.4-pre2/include/linux/sdla_ppp.h	Wed Jan 26 13:25:58 2000
+++ linux/include/linux/sdla_ppp.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*****************************************************************************
 * sdla_ppp.h	Sangoma PPP firmware API definitions.
 *
-* Author:	Gene Kozin	<74604.152@compuserve.com>
+* Author:	Nenad Corbic	<ncorbic@sangoma.com>
 *
 * Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
 *
@@ -10,6 +10,7 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Feb 24, 2000  Nenad Corbic    v2.1.2
 * Jan 06, 1997	Gene Kozin	v2.0
 * Apr 11, 1996	Gene Kozin	Initial version.
 *****************************************************************************/
@@ -214,7 +215,8 @@
 {
 	unsigned short txb_num	PACKED;	/* 00: number of transmit buffers */
 	unsigned long  txb_ptr	PACKED;	/* 02: pointer to the buffer ctl. */
-	unsigned char  rsrv1[26] PACKED;
+	unsigned long  txb_nxt  PACKED;
+	unsigned char  rsrv1[22] PACKED;
 	unsigned short rxb_num	PACKED;	/* 20: number of receive buffers */
 	unsigned long  rxb_ptr	PACKED;	/* 22: pointer to the buffer ctl. */
 	unsigned long  rxb1_ptr	PACKED;	/* 26: pointer to the first buf.ctl. */
diff -urN linux-2.4.4-pre2/include/linux/sdla_x25.h linux/include/linux/sdla_x25.h
--- linux-2.4.4-pre2/include/linux/sdla_x25.h	Wed Jun 24 14:34:44 1998
+++ linux/include/linux/sdla_x25.h	Mon Dec  3 22:34:51 2001
@@ -1,15 +1,16 @@
 /*****************************************************************************
 * sdla_x25.h	Sangoma X.25 firmware API definitions.
 *
-* Author:	Gene Kozin	<74604.152@compuserve.com>
+* Author:	Nenad Corbic	<ncorbic@sangoma.com>
 *
-* Copyright:	(c) 1995-1996 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 *
 *		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.
+		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Feb 28, 2000  Nenad Corbic    Updated for socket based x25api
 * Dec 13, 1996	Gene Kozin	Initial version
 *****************************************************************************/
 #ifndef	_SDLA_X25_H
@@ -18,39 +19,35 @@
 /*----------------------------------------------------------------------------
  * Notes:
  * ------
- * 1. All structures defined in this file are byte-aligned.  To ensure
- *    portability of this code between different platforms and compilers, one
- *    of the following defines must be defined before including this file:
- *
- *	Compiler	Platform	Define		Use option
- *	--------	--------	------		----------
- *	GNU C		Linux		_GNUC_		-
- *	Microsoft C	DOS/Windows	_MSC_		-
+ * 1. All structures defined in this file are byte-alined.  
+ *	Compiler	Platform	
+ *	--------	--------
+ *	GNU C		Linux
  *
  */
 
-#ifdef		_GNUC_
-#  ifndef	PACKED
-#    define	PACKED	__attribute__((packed))
-#  endif	/* PACKED */
-#else
-#  define	PACKED
-#endif
-#ifdef		_MSC_
-#  pragma	pack(1)
-#endif
+#ifndef	PACKED
+#	define	PACKED	__attribute__((packed))
+#endif	/* PACKED */
 
 /******	CONSTANTS DEFINITIONS ***********************************************/
 
 #define	X25_MAX_CHAN	255	/* max number of open X.25 circuits */
 #define	X25_MAX_DATA	1024	/* max length of X.25 data buffer */
-
 /*
  * X.25 shared memory layout.
  */
 #define	X25_MBOX_OFFS	0x16B0	/* general mailbox block */
 #define	X25_RXMBOX_OFFS	0x1AD0	/* receive mailbox */
 #define	X25_STATUS_OFFS	0x1EF0	/* X.25 status structure */
+#define X25_MB_VECTOR	0xE000	/* S514 mailbox window vecotr */
+#define X25_MISC_HDLC_BITS 0x1F00 /*X.25 miscallaneous HDLC bits */
+
+/* code levels */
+#define HDLC_LEVEL 0x01
+#define X25_LEVEL  0x02
+#define X25_AND_HDLC_LEVEL 0x03
+#define DO_HDLC_LEVEL_ERROR_CHECKING 0x04
 
 /****** DATA STRUCTURES *****************************************************/
 
@@ -100,11 +97,12 @@
 #define X25_HDLC_READ		0x21	/* read HDLC information frame */
 #define X25_HDLC_READ_CONFIG	0x12	/* read HDLC configuration */
 #define X25_HDLC_SET_CONFIG	0x13	/* set HDLC configuration */
+#define SET_PROTOCOL_LEVEL	0x1F	/* set protocol level */
 /*----- X.25-level commands -----------*/
 #define X25_READ		0x22	/* read X.25 packet */
 #define X25_WRITE		0x23	/* send X.25 packet */
 #define X25_PLACE_CALL		0x30	/* place a call on SVC */
-#define X25_ACCEPT_CALL		0x31	/* accept incoming call */
+#define X25_ACCEPT_CALL		0x31	/* accept incomming call */
 #define X25_CLEAR_CALL		0x32	/* clear call */
 #define X25_CLEAR_CONFRM	0x33	/* send clear confirmation packet */
 #define X25_RESET		0x34	/* send reset request packet */
@@ -116,14 +114,14 @@
 #define X25_REGISTRATION_RQST	0x3A	/* send registration request packet */
 #define X25_REGISTRATION_CONFRM	0x3B	/* send registration confirmation */
 #define X25_IS_DATA_AVAILABLE	0x40	/* querry receive queue */
-#define X25_INCOMING_CALL_CTL	0x41	/* select incoming call options */
+#define X25_INCOMMING_CALL_CTL	0x41	/* select incomming call options */
 #define X25_CONFIGURE_PVC	0x42	/* configure PVC */
 #define X25_GET_ACTIVE_CHANNELS	0x43	/* get a list of active circuits */
 #define X25_READ_CHANNEL_CONFIG	0x44	/* read virt. circuit configuration */
 #define X25_FLUSH_DATA_BUFFERS	0x45	/* flush X.25-level data buffers */
 #define X25_READ_HISTORY_TABLE	0x46	/* read asynchronous event log */
 #define X25_HISTORY_TABLE_CTL	0x47	/* control asynchronous event log */
-#define	X25_GET_TX_D_BIT_STATUS	0x48	/* is packet with D-bit acknowledged */
+#define	X25_GET_TX_D_BIT_STATUS	0x48	/* is packet with D-bit acknowleged */
 #define	X25_READ_STATISTICS	0x49	/* read X.25-level statistics */
 #define	X25_FLUSH_STATISTICS	0x4A	/* flush X.25-level statistics */
 #define	X25_READ_CONFIGURATION	0x50	/* read HDLC & X.25 configuration */
@@ -139,7 +137,7 @@
 #define X25RES_LINK_CLOSED	0x03
 #define X25RES_INVAL_LENGTH	0x04
 #define X25RES_INVAL_CMD	0x05
-#define X25RES_UNNUMBERED_FRAME	0x06	/* unnumbered frame received */
+#define X25RES_UNNUMBERED_FRAME	0x06	/* unnunbered frame received */
 #define X25RES_FRM_REJECT_MODE	0x07	/* link is in Frame Reject mode */
 #define X25RES_MODEM_FAILURE	0x08	/* DCD and/or CTS dropped */
 #define X25RES_N2_RETRY_LIMIT	0x09	/* N2 retry limit has been exceeded */
@@ -153,13 +151,13 @@
 #define X25RES_INVAL_FORMAT	0x37	/* invalid packet format */
 #define X25RES_D_BIT_NOT_SUPPRT	0x38	/* D-bit pragmatics not supported */
 #define X25RES_FACIL_NOT_SUPPRT	0x39	/* Call facility not supported */
-#define X25RES_INVAL_CALL_ARG	0x3A	/* erroneous call arguments */
-#define X25RES_INVAL_CALL_DATA	0x3B	/* erroneous call user data */
+#define X25RES_INVAL_CALL_ARG	0x3A	/* errorneous call arguments */
+#define X25RES_INVAL_CALL_DATA	0x3B	/* errorneous call user data */
 #define X25RES_ASYNC_PACKET	0x40	/* asynchronous packet received */
-#define X25RES_PROTO_VIOLATION	0x41	/* protocol violation occurred */
+#define X25RES_PROTO_VIOLATION	0x41	/* protocol violation occured */
 #define X25RES_PKT_TIMEOUT	0x42	/* X.25 packet time out */
 #define X25RES_PKT_RETRY_LIMIT	0x43	/* X.25 packet retry limit exceeded */
-/*----- Command-dependent results -----*/
+/*----- Command-dependant results -----*/
 #define X25RES_LINK_DISC	0x00	/* HDLC_LINK_STATUS */
 #define X25RES_LINK_IN_ABM	0x01	/* HDLC_LINK_STATUS */
 #define X25RES_NO_DATA		0x01	/* HDLC_READ/READ_TRACE_DATA*/
@@ -167,7 +165,7 @@
 #define X25RES_LINK_IS_OPEN	0x01	/* HDLC_LINK_OPEN */
 #define X25RES_LINK_IS_DISC	0x02	/* HDLC_LINK_DISC */
 #define X25RES_LINK_IS_CLOSED	0x03	/* HDLC_LINK_CLOSE */
-#define X25RES_INVAL_PARAM	0x31	/* INCOMING_CALL_CTL */
+#define X25RES_INVAL_PARAM	0x31	/* INCOMMING_CALL_CTL */
 #define X25RES_INVAL_CONFIG	0x35	/* REGISTR_RQST/CONFRM */
 
 /*
@@ -207,6 +205,22 @@
 #define PVE_RESTART_RQST	0x34
 #define PVE_DIAGNOSTIC		0x37
 
+#define INTR_ON_RX_FRAME            0x01
+#define INTR_ON_TX_FRAME            0x02
+#define INTR_ON_MODEM_STATUS_CHANGE 0x04
+#define INTR_ON_COMMAND_COMPLETE    0x08
+#define INTR_ON_X25_ASY_TRANSACTION 0x10
+#define INTR_ON_TIMER		    0x40
+#define DIRECT_RX_INTR_USAGE        0x80
+
+#define NO_INTR_PENDING  	        0x00
+#define RX_INTR_PENDING			0x01	
+#define TX_INTR_PENDING			0x02
+#define MODEM_INTR_PENDING		0x04
+#define COMMAND_COMPLETE_INTR_PENDING 	0x08
+#define X25_ASY_TRANS_INTR_PENDING	0x10
+#define TIMER_INTR_PENDING		0x40
+
 /*----------------------------------------------------------------------------
  * X.25 Mailbox.
  *	This structure is located at offsets X25_MBOX_OFFS and X25_RXMBOX_OFFS
@@ -239,7 +253,7 @@
 typedef struct X25Status
 {
 	unsigned short pvc_map	PACKED;	/* 00h: PVC map */
-	unsigned short icc_map	PACKED;	/* 02h: Incoming Chan. map */
+	unsigned short icc_map	PACKED;	/* 02h: Incomming Chan. map */
 	unsigned short twc_map	PACKED;	/* 04h: Two-way Cnan. map */
 	unsigned short ogc_map	PACKED;	/* 06h: Outgoing Chan. map */
 	TX25TimeStamp tstamp	PACKED;	/* 08h: timestamp (BCD) */
@@ -256,7 +270,7 @@
 #define X25_RX_INTR	0x01	/* receive interrupt */
 #define X25_TX_INTR	0x02	/* transmit interrupt */
 #define X25_MODEM_INTR	0x04	/* modem status interrupt (CTS/DCD) */
-#define X25_EVENT_INTR	0x10	/* asynchronous event encountered */
+#define X25_EVENT_INTR	0x10	/* asyncronous event encountered */
 #define X25_CMD_INTR	0x08	/* interface command complete */
 
 /*
@@ -374,7 +388,7 @@
  */
 typedef struct X25Config
 {
-	unsigned char baudRate		PACKED;	/* 00h:  */
+unsigned char baudRate		PACKED;	/* 00h:  */
 	unsigned char t1		PACKED;	/* 01h:  */
 	unsigned char t2		PACKED;	/* 02h:  */
 	unsigned char n2		PACKED;	/* 03h:  */
@@ -390,8 +404,8 @@
 	unsigned short pktMTU		PACKED;	/* 0Fh:  */
 	unsigned short loPVC		PACKED;	/* 11h:  */
 	unsigned short hiPVC		PACKED;	/* 13h:  */
-	unsigned short loIncomingSVC	PACKED;	/* 15h:  */
-	unsigned short hiIncomingSVC	PACKED;	/* 17h:  */
+	unsigned short loIncommingSVC	PACKED;	/* 15h:  */
+	unsigned short hiIncommingSVC	PACKED;	/* 17h:  */
 	unsigned short loTwoWaySVC	PACKED;	/* 19h:  */
 	unsigned short hiTwoWaySVC	PACKED;	/* 1Bh:  */
 	unsigned short loOutgoingSVC	PACKED;	/* 1Dh:  */
@@ -421,8 +435,8 @@
 {
 	unsigned short loPVC		PACKED;	/* 00h: lowest PVC number */
 	unsigned short hiPVC		PACKED;	/* 02h: highest PVC number */
-	unsigned short loIncomingSVC	PACKED;	/* 04h: lowest incoming SVC */
-	unsigned short hiIncomingSVC	PACKED;	/* 06h: highest incoming SVC */
+	unsigned short loIncommingSVC	PACKED;	/* 04h: lowest incoming SVC */
+	unsigned short hiIncommingSVC	PACKED;	/* 06h: highest incoming SVC */
 	unsigned short loTwoWaySVC	PACKED;	/* 08h: lowest two-way SVC */
 	unsigned short hiTwoWaySVC	PACKED;	/* 0Ah: highest two-way SVC */
 	unsigned short loOutgoingSVC	PACKED;	/* 0Ch: lowest outgoing SVC */
@@ -499,7 +513,7 @@
 /*
  * Defines for the 'type' field.
  */
-#define X25LOG_INCOMING		0x00
+#define X25LOG_INCOMMING	0x00
 #define X25LOG_APPLICATION 	0x01
 #define X25LOG_AUTOMATIC	0x02
 #define X25LOG_ERROR		0x04
@@ -568,7 +582,7 @@
 #define X25_TRCERR_RX_BADCRC	0x20	/* receive CRC error */
 #define X25_TRCERR_RX_OVERRUN	0x30	/* receiver overrun error */
 #define X25_TRCERR_RX_TOO_LONG	0x40	/* excessive frame length error */
-#define X25_TRCERR_TX_ABORT	0x70	/* aborted frame transmission error */
+#define X25_TRCERR_TX_ABORT	0x70	/* aborted frame transmittion error */
 #define X25_TRCERR_TX_UNDERRUN	0x80	/* transmit underrun error */
 
 /*****************************************************************************
@@ -582,7 +596,7 @@
 	unsigned char data[0]	PACKED;
 } THDLCFrame;
 
-typedef struct X25Pkt			/*----- X.25 Packet Format ----------*/
+typedef struct X25Pkt			/*----- X.25 Paket Format ----------*/
 {
 	unsigned char lcn_hi	PACKED;	/* 4 MSB of Logical Channel Number */
 	unsigned char lcn_lo	PACKED;	/* 8 LSB of Logical Channel Number */
@@ -619,7 +633,140 @@
 #define	X25PKT_RR_MASKED	0x01	/* Receive Ready packet after masking */
 #define	X25PKT_RNR_MASKED	0x05	/* Receive Not Ready after masking  */
 
-#ifdef		_MSC_
-#  pragma	pack()
-#endif
+
+typedef struct {
+	TX25Cmd cmd		PACKED;
+	char data[X25_MAX_DATA]	PACKED;
+} mbox_cmd_t;
+
+
+typedef struct {
+	unsigned char  qdm	PACKED;	/* Q/D/M bits */
+	unsigned char  cause	PACKED;	/* cause field */
+	unsigned char  diagn	PACKED;	/* diagnostics */
+	unsigned char  pktType  PACKED;
+	unsigned short length   PACKED;
+	unsigned char  result	PACKED;
+	unsigned short lcn	PACKED;
+	char reserved[7]	PACKED;
+}x25api_hdr_t;
+
+
+typedef struct {
+	x25api_hdr_t hdr	PACKED;
+	char data[X25_MAX_DATA]	PACKED;
+}x25api_t;
+
+
+/* 
+ * XPIPEMON Definitions
+ */
+
+/* valid ip_protocol for UDP management */
+#define UDPMGMT_UDP_PROTOCOL 0x11
+#define UDPMGMT_XPIPE_SIGNATURE         "XLINK8ND"
+#define UDPMGMT_DRVRSTATS_SIGNATURE     "DRVSTATS"
+
+/* values for request/reply byte */
+#define UDPMGMT_REQUEST	0x01
+#define UDPMGMT_REPLY	0x02
+#define UDP_OFFSET	12
+
+
+typedef struct {
+	unsigned char opp_flag  PACKED; /* the opp flag */
+	unsigned char command	PACKED;	/* command code */
+	unsigned short length	PACKED;	/* transfer data length */
+	unsigned char result	PACKED;	/* return code */
+	unsigned char pf	PACKED;	/* P/F bit */
+	unsigned short lcn	PACKED;	/* logical channel */
+	unsigned char qdm	PACKED;	/* Q/D/M bits */
+	unsigned char cause	PACKED;	/* cause field */
+	unsigned char diagn	PACKED;	/* diagnostics */
+	unsigned char pktType	PACKED;	/* packet type */
+	unsigned char resrv[4]	PACKED;	/* reserved */
+} cblock_t;
+
+typedef struct {
+	ip_pkt_t 		ip_pkt		PACKED;
+	udp_pkt_t		udp_pkt		PACKED;
+	wp_mgmt_t 		wp_mgmt       	PACKED;
+        cblock_t                cblock          PACKED;
+        unsigned char           data[4080]      PACKED;
+} x25_udp_pkt_t;
+
+
+typedef struct read_hdlc_stat {
+	unsigned short inf_frames_rx_ok PACKED;
+        unsigned short inf_frames_rx_out_of_seq PACKED;
+	unsigned short inf_frames_rx_no_data PACKED;
+	unsigned short inf_frames_rx_dropped PACKED;
+	unsigned short inf_frames_rx_data_too_long PACKED;
+	unsigned short inf_frames_rx_invalid_addr PACKED;
+	unsigned short inf_frames_tx_ok PACKED;
+        unsigned short inf_frames_tx_retransmit PACKED;
+       	unsigned short T1_timeouts PACKED;
+	unsigned short SABM_frames_rx PACKED;
+	unsigned short DISC_frames_rx PACKED;
+	unsigned short DM_frames_rx PACKED;
+	unsigned short FRMR_frames_rx PACKED;
+	unsigned short SABM_frames_tx PACKED;
+	unsigned short DISC_frames_tx PACKED;
+	unsigned short DM_frames_tx PACKED;
+	unsigned short FRMR_frames_tx PACKED;
+} read_hdlc_stat_t;
+
+typedef struct read_comms_err_stats{
+	unsigned char overrun_err_rx PACKED;
+	unsigned char CRC_err PACKED;
+	unsigned char abort_frames_rx PACKED;
+	unsigned char frames_dropped_buf_full PACKED;
+	unsigned char abort_frames_tx PACKED;
+	unsigned char transmit_underruns PACKED;
+	unsigned char missed_tx_underruns_intr PACKED;
+	unsigned char reserved PACKED;
+	unsigned char DCD_drop PACKED;
+	unsigned char CTS_drop PACKED;
+} read_comms_err_stats_t;
+
+typedef struct trace_data {
+	unsigned short length PACKED;
+	unsigned char  type PACKED;
+	unsigned char  trace_dropped PACKED;
+	unsigned char  reserved[5] PACKED;
+	unsigned short timestamp PACKED;
+        unsigned char  data PACKED;
+} trace_data_t;
+
+enum {UDP_XPIPE_TYPE};
+
+#define XPIPE_ENABLE_TRACING                    0x14
+#define XPIPE_DISABLE_TRACING                   0x14
+#define XPIPE_GET_TRACE_INFO                    0x16
+#define XPIPE_FT1_READ_STATUS                   0x74
+#define XPIPE_DRIVER_STAT_IFSEND                0x75
+#define XPIPE_DRIVER_STAT_INTR                  0x76
+#define XPIPE_DRIVER_STAT_GEN                   0x77
+#define XPIPE_FLUSH_DRIVER_STATS                0x78
+#define XPIPE_ROUTER_UP_TIME                    0x79        
+#define XPIPE_SET_FT1_MODE			0x81
+#define XPIPE_FT1_STATUS_CTRL			0x80
+
+
+/* error messages */
+#define NO_BUFFS_OR_CLOSED_WIN  0x33
+#define DATA_LENGTH_TOO_BIG     0x32
+#define NO_DATA_AVAILABLE       0x33
+#define Z80_TIMEOUT_ERROR       0x0a   
+#define	NO_BUFFS		0x08
+
+
+/* Trace options */
+#define TRACE_DEFAULT		0x03
+#define TRACE_SUPERVISOR_FRMS	0x10
+#define TRACE_ASYNC_FRMS	0x20
+#define TRACE_ALL_HDLC_FRMS	0x40
+#define TRACE_DATA_FRMS		0x08
+
+
 #endif	/* _SDLA_X25_H */
diff -urN linux-2.4.4-pre2/include/linux/sdladrv.h linux/include/linux/sdladrv.h
--- linux-2.4.4-pre2/include/linux/sdladrv.h	Mon Dec 11 13:00:00 2000
+++ linux/include/linux/sdladrv.h	Mon Dec  3 22:34:51 2001
@@ -3,7 +3,7 @@
 *
 * Author: 	Gideon Hack	
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
@@ -20,13 +20,21 @@
 #define	_SDLADRV_H
 
 #include <linux/version.h>
-#if LINUX_VERSION_CODE >= 0x020100
+
+#ifndef KERNEL_VERSION
+  #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#define LINUX_2_4	
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 #define LINUX_2_1
+#else
+#define LINUX_2_0
 #endif
 
 #define	SDLA_MAXIORANGE	4	/* maximum I/O port range */
 #define	SDLA_WINDOWSIZE	0x2000	/* default dual-port memory window size */
-
 /****** Data Structures *****************************************************/
 
 /*----------------------------------------------------------------------------
@@ -41,7 +49,8 @@
 	int irq;			/* interrupt request level */
 	char S514_cpu_no[1];		/* PCI CPU Number */
 	unsigned char S514_slot_no;	/* PCI Slot Number */
-#ifdef LINUX_2_1
+	char auto_pci_cfg;		/* Autodetect PCI Slot */
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct pci_dev *pci_dev;	/* PCI device */
 #else
 	unsigned char pci_bus;		/* PCI bus number */
@@ -73,5 +82,7 @@
 extern int sdla_poke	(sdlahw_t* hw, unsigned long addr, void* buf,
 			 unsigned len);
 extern int sdla_exec	(void* opflag);
+
+extern unsigned wanpipe_hw_probe(void);
 
 #endif	/* _SDLADRV_H */
diff -urN linux-2.4.4-pre2/include/linux/sdlapci.h linux/include/linux/sdlapci.h
--- linux-2.4.4-pre2/include/linux/sdlapci.h	Wed Jan 26 13:25:58 2000
+++ linux/include/linux/sdlapci.h	Mon Dec  3 22:34:51 2001
@@ -4,7 +4,7 @@
 *
 * Author:	Gideon Hack	<ghack@sangoma.com>
 *
-* Copyright:	(c) 1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1999-2000 Sangoma Technologies Inc.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
@@ -62,7 +62,11 @@
 #define S514_CPU_START		0x01
 
 /* The maximum number of S514 adapters supported */
-#define MAX_S514_CARDS		8
+#define MAX_S514_CARDS		20	
+
+#define PCI_CARD_TYPE		0x2E
+#define S514_DUAL_CPU		0x12
+#define S514_SINGLE_CPU		0x11
 
 #endif	/* _SDLAPCI_H */
 
diff -urN linux-2.4.4-pre2/include/linux/serialP.h linux/include/linux/serialP.h
--- linux-2.4.4-pre2/include/linux/serialP.h	Mon Mar 26 15:50:00 2001
+++ linux/include/linux/serialP.h	Mon Dec  3 22:34:51 2001
@@ -181,7 +181,7 @@
 #define SPCI_FL_IRQBASE4       (0x0004 << 4)
 #define SPCI_FL_GET_IRQBASE(x)        ((x & SPCI_FL_IRQ_MASK) >> 4)
 
-/* Use sucessive BARs (PCI base address registers), 
+/* Use successive BARs (PCI base address registers), 
    else use offset into some specified BAR */
 #define SPCI_FL_BASE_TABLE	0x0100
 
diff -urN linux-2.4.4-pre2/include/linux/serio.h linux/include/linux/serio.h
--- linux-2.4.4-pre2/include/linux/serio.h	Wed Jun 21 08:22:21 2000
+++ linux/include/linux/serio.h	Mon Dec  3 22:34:51 2001
@@ -2,7 +2,7 @@
 #define _SERIO_H
 
 /*
- * $Id: serio.h,v 1.7 2000/06/01 11:39:46 vojtech Exp $
+ * $Id: serio.h,v 1.8 2000/07/17 10:42:14 vojtech Exp $
  *
  * Copyright (C) 1999 Vojtech Pavlik
  *
@@ -103,6 +103,7 @@
 #define SERIO_SPACEBALL	0x1b
 #define SERIO_GUNZE	0x1c
 #define SERIO_IFORCE	0x1d
+#define SERIO_STINGER	0x1e
 
 #define SERIO_ID	0xff00UL
 #define SERIO_EXTRA	0xff0000UL
diff -urN linux-2.4.4-pre2/include/linux/shmem_fs.h linux/include/linux/shmem_fs.h
--- linux-2.4.4-pre2/include/linux/shmem_fs.h	Fri Dec 29 14:07:24 2000
+++ linux/include/linux/shmem_fs.h	Mon Dec  3 22:34:51 2001
@@ -19,6 +19,7 @@
 
 struct shmem_inode_info {
 	spinlock_t	lock;
+	unsigned long	max_index;
 	swp_entry_t	i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */
 	swp_entry_t   **i_indirect; /* doubly indirect blocks */
 	unsigned long	swapped;
diff -urN linux-2.4.4-pre2/include/linux/skbuff.h linux/include/linux/skbuff.h
--- linux-2.4.4-pre2/include/linux/skbuff.h	Mon Mar 26 15:48:19 2001
+++ linux/include/linux/skbuff.h	Mon Dec  3 22:34:51 2001
@@ -18,10 +18,13 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/time.h>
+#include <linux/cache.h>
 
 #include <asm/atomic.h>
 #include <asm/types.h>
 #include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
 
 #define HAVE_ALLOC_SKB		/* For the drivers to know */
 #define HAVE_ALIGNABLE_SKB	/* Ditto 8)		   */
@@ -31,6 +34,47 @@
 #define CHECKSUM_HW 1
 #define CHECKSUM_UNNECESSARY 2
 
+#define SKB_DATA_ALIGN(X)	(((X) + (SMP_CACHE_BYTES-1)) & ~(SMP_CACHE_BYTES-1))
+#define SKB_MAX_HEAD(X)		((PAGE_SIZE - (X) - sizeof(struct skb_shared_info))&~(SMP_CACHE_BYTES-1))
+
+/* A. Checksumming of received packets by device.
+ *
+ *	NONE: device failed to checksum this packet.
+ *		skb->csum is undefined.
+ *
+ *	UNNECESSARY: device parsed packet and wouldbe verified checksum.
+ *		skb->csum is undefined.
+ *	      It is bad option, but, unfortunately, many of vendors do this.
+ *	      Apparently with secret goal to sell you new device, when you
+ *	      will add new protocol to your host. F.e. IPv6. 8)
+ *
+ *	HW: the most generic way. Device supplied checksum of _all_
+ *	    the packet as seen by netif_rx in skb->csum.
+ *	    NOTE: Even if device supports only some protocols, but
+ *	    is able to produce some skb->csum, it MUST use HW,
+ *	    not UNNECESSARY.
+ *
+ * B. Checksumming on output.
+ *
+ *	NONE: skb is checksummed by protocol or csum is not required.
+ *
+ *	HW: device is required to csum packet as seen by hard_start_xmit
+ *	from skb->h.raw to the end and to record the checksum
+ *	at skb->h.raw+skb->csum.
+ *
+ *	Device must show its capabilities in dev->features, set
+ *	at device setup time.
+ *	NETIF_F_HW_CSUM	- it is clever device, it is able to checksum
+ *			  everything.
+ *	NETIF_F_NO_CSUM - loopback or reliable single hop media.
+ *	NETIF_F_IP_CSUM - device is dumb. It is able to csum only
+ *			  TCP/UDP over IPv4. Sigh. Vendors like this
+ *			  way by an unknown reason. Though, see comment above
+ *			  about CHECKSUM_UNNECESSARY. 8)
+ *
+ *	Any questions? No questions, good. 		--ANK
+ */
+
 #ifdef __i386__
 #define NET_CALLER(arg) (*(((void**)&arg)-1))
 #else
@@ -57,6 +101,29 @@
 	spinlock_t	lock;
 };
 
+struct sk_buff;
+
+#define MAX_SKB_FRAGS 6
+
+typedef struct skb_frag_struct skb_frag_t;
+
+struct skb_frag_struct
+{
+	struct page *page;
+	__u16 page_offset;
+	__u16 size;
+};
+
+/* This data is invariant across clones and lives at
+ * the end of the header data, ie. at skb->end.
+ */
+struct skb_shared_info {
+	atomic_t	dataref;
+	unsigned int	nr_frags;
+	struct sk_buff	*frag_list;
+	skb_frag_t	frags[MAX_SKB_FRAGS];
+};
+
 struct sk_buff {
 	/* These two members must be first. */
 	struct sk_buff	* next;			/* Next buffer in list 				*/
@@ -107,9 +174,10 @@
 	char		cb[48];	 
 
 	unsigned int 	len;			/* Length of actual data			*/
+ 	unsigned int 	data_len;
 	unsigned int	csum;			/* Checksum 					*/
-	volatile char 	used;			/* Data moved to user and not MSG_PEEK		*/
-	unsigned char	cloned, 		/* head may be cloned (check refcnt to be sure). */
+	unsigned char 	__unused,		/* Dead field, may be reused			*/
+			cloned, 		/* head may be cloned (check refcnt to be sure). */
   			pkt_type,		/* Packet class					*/
   			ip_summed;		/* Driver fed us an IP checksum			*/
 	__u32		priority;		/* Packet queueing priority			*/
@@ -122,6 +190,7 @@
 	unsigned char	*data;			/* Data head pointer				*/
 	unsigned char	*tail;			/* Tail pointer					*/
 	unsigned char 	*end;			/* End pointer					*/
+
 	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
 #ifdef CONFIG_NETFILTER
 	/* Can be used for communication between hooks. */
@@ -158,11 +227,13 @@
 #include <asm/system.h>
 
 extern void			__kfree_skb(struct sk_buff *skb);
-extern struct sk_buff *		skb_peek_copy(struct sk_buff_head *list);
 extern struct sk_buff *		alloc_skb(unsigned int size, int priority);
 extern void			kfree_skbmem(struct sk_buff *skb);
 extern struct sk_buff *		skb_clone(struct sk_buff *skb, int priority);
 extern struct sk_buff *		skb_copy(const struct sk_buff *skb, int priority);
+extern struct sk_buff *		pskb_copy(struct sk_buff *skb, int gfp_mask);
+extern int			pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask);
+extern struct sk_buff *		skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom);
 extern struct sk_buff *		skb_copy_expand(const struct sk_buff *skb, 
 						int newheadroom,
 						int newtailroom,
@@ -171,14 +242,8 @@
 extern void	skb_over_panic(struct sk_buff *skb, int len, void *here);
 extern void	skb_under_panic(struct sk_buff *skb, int len, void *here);
 
-/* Backwards compatibility */
-#define skb_realloc_headroom(skb, nhr) skb_copy_expand(skb, nhr, skb_tailroom(skb), GFP_ATOMIC)
-
 /* Internal */
-static inline atomic_t *skb_datarefp(struct sk_buff *skb)
-{
-	return (atomic_t *)(skb->end);
-}
+#define skb_shinfo(SKB)		((struct skb_shared_info *)((SKB)->end))
 
 /**
  *	skb_queue_empty - check if a queue is empty
@@ -243,7 +308,7 @@
 
 static inline int skb_cloned(struct sk_buff *skb)
 {
-	return skb->cloned && atomic_read(skb_datarefp(skb)) != 1;
+	return skb->cloned && atomic_read(&skb_shinfo(skb)->dataref) != 1;
 }
 
 /**
@@ -679,6 +744,20 @@
 	return result;
 }
 
+static inline int skb_is_nonlinear(const struct sk_buff *skb)
+{
+	return skb->data_len;
+}
+
+static inline int skb_headlen(const struct sk_buff *skb)
+{
+	return skb->len - skb->data_len;
+}
+
+#define SKB_PAGE_ASSERT(skb) do { if (skb_shinfo(skb)->nr_frags) BUG(); } while (0)
+#define SKB_FRAG_ASSERT(skb) do { if (skb_shinfo(skb)->frag_list) BUG(); } while (0)
+#define SKB_LINEAR_ASSERT(skb) do { if (skb_is_nonlinear(skb)) BUG(); } while (0)
+
 /*
  *	Add data to an sk_buff
  */
@@ -686,6 +765,7 @@
 static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
 {
 	unsigned char *tmp=skb->tail;
+	SKB_LINEAR_ASSERT(skb);
 	skb->tail+=len;
 	skb->len+=len;
 	return tmp;
@@ -704,6 +784,7 @@
 static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
 {
 	unsigned char *tmp=skb->tail;
+	SKB_LINEAR_ASSERT(skb);
 	skb->tail+=len;
 	skb->len+=len;
 	if(skb->tail>skb->end) {
@@ -742,6 +823,8 @@
 static inline char *__skb_pull(struct sk_buff *skb, unsigned int len)
 {
 	skb->len-=len;
+	if (skb->len < skb->data_len)
+		BUG();
 	return 	skb->data+=len;
 }
 
@@ -763,6 +846,33 @@
 	return __skb_pull(skb,len);
 }
 
+extern unsigned char * __pskb_pull_tail(struct sk_buff *skb, int delta);
+
+static inline char *__pskb_pull(struct sk_buff *skb, unsigned int len)
+{
+	if (len > skb_headlen(skb) &&
+	    __pskb_pull_tail(skb, len-skb_headlen(skb)) == NULL)
+		return NULL;
+	skb->len -= len;
+	return 	skb->data += len;
+}
+
+static inline unsigned char * pskb_pull(struct sk_buff *skb, unsigned int len)
+{	
+	if (len > skb->len)
+		return NULL;
+	return __pskb_pull(skb,len);
+}
+
+static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len)
+{
+	if (len <= skb_headlen(skb))
+		return 1;
+	if (len > skb->len)
+		return 0;
+	return (__pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL);
+}
+
 /**
  *	skb_headroom - bytes at buffer head
  *	@skb: buffer to check
@@ -784,7 +894,7 @@
 
 static inline int skb_tailroom(const struct sk_buff *skb)
 {
-	return skb->end-skb->tail;
+	return skb_is_nonlinear(skb) ? 0 : skb->end-skb->tail;
 }
 
 /**
@@ -802,11 +912,16 @@
 	skb->tail+=len;
 }
 
+extern int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc);
 
 static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
 {
-	skb->len = len;
-	skb->tail = skb->data+len;
+	if (!skb->data_len) {
+		skb->len = len;
+		skb->tail = skb->data+len;
+	} else {
+		___pskb_trim(skb, len, 0);
+	}
 }
 
 /**
@@ -825,6 +940,25 @@
 	}
 }
 
+
+static inline int __pskb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (!skb->data_len) {
+		skb->len = len;
+		skb->tail = skb->data+len;
+		return 0;
+	} else {
+		return ___pskb_trim(skb, len, 1);
+	}
+}
+
+static inline int pskb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (len < skb->len)
+		return __pskb_trim(skb, len);
+	return 0;
+}
+
 /**
  *	skb_orphan - orphan a buffer
  *	@skb: buffer to orphan
@@ -920,32 +1054,57 @@
 }
 
 /**
- *	skb_cow - copy a buffer if need be
- *	@skb: buffer to copy
+ *	skb_cow - copy header of skb when it is required
+ *	@skb: buffer to cow
  *	@headroom: needed headroom
  *
- *	If the buffer passed lacks sufficient headroom or is a clone then
- *	it is copied and the additional headroom made available. If there
- *	is no free memory %NULL is returned. The new buffer is returned if
- *	a copy was made (and the old one dropped a reference). The existing
- *	buffer is returned otherwise.
+ *	If the skb passed lacks sufficient headroom or its data part
+ *	is shared, data is reallocated. If reallocation fails, an error
+ *	is returned and original skb is not changed.
  *
- *	This function primarily exists to avoid making two copies when making
- *	a writable copy of a buffer and then growing the headroom.
+ *	The result is skb with writable area skb->head...skb->tail
+ *	and at least @headroom of space at head.
  */
- 
 
-static inline struct sk_buff *
+static inline int
 skb_cow(struct sk_buff *skb, unsigned int headroom)
 {
-	headroom = (headroom+15)&~15;
+	int delta = headroom - skb_headroom(skb);
 
-	if ((unsigned)skb_headroom(skb) < headroom || skb_cloned(skb)) {
-		struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
-		kfree_skb(skb);
-		skb = skb2;
-	}
-	return skb;
+	if (delta < 0)
+		delta = 0;
+
+	if (delta || skb_cloned(skb))
+		return pskb_expand_head(skb, (delta+15)&~15, 0, GFP_ATOMIC);
+	return 0;
+}
+
+/**
+ *	skb_linearize - convert paged skb to linear one
+ *	@skb: buffer to linarize
+ *	@gfp_mask: allocation mode
+ *
+ *	If there is no free memory -ENOMEM is returned, otherwise zero
+ *	is returned and the old skb data released.  */
+int skb_linearize(struct sk_buff *skb, int gfp);
+
+static inline void *kmap_skb_frag(const skb_frag_t *frag)
+{
+#ifdef CONFIG_HIGHMEM
+	if (in_irq())
+		BUG();
+
+	local_bh_disable();
+#endif
+	return kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ);
+}
+
+static inline void kunmap_skb_frag(void *vaddr)
+{
+	kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ);
+#ifdef CONFIG_HIGHMEM
+	local_bh_enable();
+#endif
 }
 
 #define skb_queue_walk(queue, skb) \
@@ -956,9 +1115,15 @@
 
 extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
 extern unsigned int		datagram_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait);
-extern int			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
-extern int			skb_copy_datagram_iovec(struct sk_buff *from, int offset, struct iovec *to,int size);
+extern int			skb_copy_datagram(const struct sk_buff *from, int offset, char *to,int size);
+extern int			skb_copy_datagram_iovec(const struct sk_buff *from, int offset, struct iovec *to,int size);
+extern int			skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int *csump);
+extern int			skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct iovec *iov);
 extern void			skb_free_datagram(struct sock * sk, struct sk_buff *skb);
+
+extern unsigned int		skb_checksum(const struct sk_buff *skb, int offset, int len, unsigned int csum);
+extern int			skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len);
+extern unsigned int		skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum);
 
 extern void skb_init(void);
 extern void skb_add_mtu(int mtu);
diff -urN linux-2.4.4-pre2/include/linux/socket.h linux/include/linux/socket.h
--- linux-2.4.4-pre2/include/linux/socket.h	Mon Mar 26 15:48:10 2001
+++ linux/include/linux/socket.h	Mon Dec  3 22:34:51 2001
@@ -155,6 +155,7 @@
 #define AF_SNA		22	/* Linux SNA Project (nutters!) */
 #define AF_IRDA		23	/* IRDA sockets			*/
 #define AF_PPPOX	24	/* PPPoX sockets		*/
+#define AF_WANPIPE	25	/* Wanpipe API Sockets */
 #define AF_MAX		32	/* For now.. */
 
 /* Protocol families, same as address families. */
@@ -184,6 +185,7 @@
 #define PF_SNA		AF_SNA
 #define PF_IRDA		AF_IRDA
 #define PF_PPPOX	AF_PPPOX
+#define PF_WANPIPE	AF_WANPIPE
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
@@ -209,6 +211,7 @@
 #define MSG_RST		0x1000
 #define MSG_ERRQUEUE	0x2000	/* Fetch message from error queue */
 #define MSG_NOSIGNAL	0x4000	/* Do not generate SIGPIPE */
+#define MSG_MORE	0x8000	/* Sender will send more */
 
 #define MSG_EOF         MSG_FIN
 
diff -urN linux-2.4.4-pre2/include/linux/sysctl.h linux/include/linux/sysctl.h
--- linux-2.4.4-pre2/include/linux/sysctl.h	Mon Mar 26 15:48:10 2001
+++ linux/include/linux/sysctl.h	Mon Dec  3 22:34:51 2001
@@ -117,6 +117,8 @@
 	KERN_OVERFLOWGID=47,	/* int: overflow GID */
 	KERN_SHMPATH=48,	/* string: path to shm fs */
 	KERN_HOTPLUG=49,	/* string: path to hotplug policy agent */
+	KERN_IEEE_EMULATION_WARNINGS=50, /* int: unimplemented ieee instructions */
+	KERN_S390_USER_DEBUG_LOGGING=51  /* int: dumps of user faults */
 };
 
 
diff -urN linux-2.4.4-pre2/include/linux/tcp.h linux/include/linux/tcp.h
--- linux-2.4.4-pre2/include/linux/tcp.h	Mon Mar 26 15:48:11 2001
+++ linux/include/linux/tcp.h	Mon Dec  3 22:34:51 2001
@@ -126,6 +126,7 @@
 #define TCP_DEFER_ACCEPT	9	/* Wake up listener only when data arrive */
 #define TCP_WINDOW_CLAMP	10	/* Bound advertised window */
 #define TCP_INFO		11	/* Information about this connection. */
+#define TCP_QUICKACK		12	/* Block/reenable quick acks */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
diff -urN linux-2.4.4-pre2/include/linux/tpqic02.h linux/include/linux/tpqic02.h
--- linux-2.4.4-pre2/include/linux/tpqic02.h	Mon Mar 26 15:50:01 2001
+++ linux/include/linux/tpqic02.h	Mon Dec  3 22:34:51 2001
@@ -588,7 +588,7 @@
  */
 
 #define	TP_REWCLOSE(d)	((MINOR(d)&0x01) == 1)	   		/* rewind bit */
-			   /* rewind is only done if data has been transfered */
+			   /* rewind is only done if data has been transferred */
 #define	TP_DENS(dev)	((MINOR(dev) >> 1) & 0x07) 	      /* tape density */
 #define TP_UNIT(dev)	((MINOR(dev) >> 4) & 0x07)	       /* unit number */
 
@@ -633,8 +633,8 @@
 #define EXC_WP		3	/* Write protected */
 #define EXC_EOM		4	/* EOM */
 #define EXC_RWA		5	/* read/write abort */
-#define EXC_XBAD	6	/* read error, bad block transfered */
-#define EXC_XFILLER	7	/* read error, filler block transfered */
+#define EXC_XBAD	6	/* read error, bad block transferred */
+#define EXC_XFILLER	7	/* read error, filler block transferred */
 #define EXC_NDT		8	/* read error, no data */
 #define EXC_NDTEOM	9	/* read error, no data & EOM */
 #define EXC_NDTBOM	10	/* read error, no data & BOM */
diff -urN linux-2.4.4-pre2/include/linux/videodev.h linux/include/linux/videodev.h
--- linux-2.4.4-pre2/include/linux/videodev.h	Fri Mar  2 11:12:10 2001
+++ linux/include/linux/videodev.h	Mon Dec  3 22:34:51 2001
@@ -373,6 +373,8 @@
 #define VID_HARDWARE_ZR36067	26	/* Zoran ZR36067/36060 */
 #define VID_HARDWARE_OV511	27	
 #define VID_HARDWARE_ZR356700	28	/* Zoran 36700 series */
+#define VID_HARDWARE_W9966	29
+
 
 /*
  *	Initialiser list
diff -urN linux-2.4.4-pre2/include/linux/wanpipe.h linux/include/linux/wanpipe.h
--- linux-2.4.4-pre2/include/linux/wanpipe.h	Mon Dec 11 13:00:10 2000
+++ linux/include/linux/wanpipe.h	Mon Dec  3 22:34:51 2001
@@ -5,13 +5,17 @@
 * Author: 	Nenad Corbic <ncorbic@sangoma.com>
 *		Gideon Hack  	
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
+* Nov 3,  2000  Nenad Corbic    Added config_id to sdla_t structure.
+*                               Used to determine the protocol running.
+* Jul 13, 2000  Nenad Corbic	Added SyncPPP Support
+* Feb 24, 2000  Nenad Corbic    Added support for x25api driver
 * Oct 04, 1999  Nenad Corbic    New CHDLC and FRAME RELAY code, SMP support
 * Jun 02, 1999  Gideon Hack	Added 'update_call_count' for Cisco HDLC 
 *				support
@@ -35,10 +39,58 @@
 #ifndef	_WANPIPE_H
 #define	_WANPIPE_H
 
-#include <linux/config.h>
+#include <linux/version.h>
 
-#ifdef CONFIG_SMP
-#include <asm/spinlock.h>       /* Support for SMP Locking */
+#ifndef KERNEL_VERSION
+  #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ 
+ #define LINUX_2_4
+ #define netdevice_t struct net_device
+
+ #define FREE_READ 1
+ #define FREE_WRITE 0
+
+ #define stop_net_queue(a) 	netif_stop_queue(a) 
+ #define start_net_queue(a) 	netif_start_queue(a)
+ #define is_queue_stopped(a)	netif_queue_stopped(a)
+ #define wake_net_dev(a)	netif_wake_queue(a)
+ #define is_dev_running(a)	netif_running(a)
+ #define wan_dev_kfree_skb(a,b)	dev_kfree_skb_any(a)
+
+
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+
+ #define LINUX_2_1
+ #define netdevice_t struct device
+ #define FREE_READ 1
+ #define FREE_WRITE 0
+
+ #define stop_net_queue(a) 	(set_bit(0, &##a->tbusy)) 
+ #define start_net_queue(a) 	(clear_bit(0,&##a->tbusy))
+ #define is_queue_stopped(a)	(##a->tbusy)
+ #define wake_net_dev(a)	{clear_bit(0,&##a->tbusy);mark_bh(NET_BH);}
+ #define is_dev_running(a)	(test_bit(0,&##a->start))
+ #define wan_dev_kfree_skb(a,b)	dev_kfree_skb(a)
+
+#else
+ #define LINUX_2_0
+ #define netdevice_t struct device
+
+ #define test_and_set_bit set_bit
+ #define net_ratelimit() 1 
+
+ #define stop_net_queue(a) 	(set_bit(0, &##a->tbusy)) 
+ #define start_net_queue(a) 	(clear_bit(0,&##a->tbusy))
+ #define is_queue_stopped(a)	(##a->tbusy)
+ #define wake_net_dev(a)	{clear_bit(0,&##a->tbusy);mark_bh(NET_BH);}
+ #define is_dev_running(a)	(test_bit(0,(void*)&##a->start))
+ #define wan_dev_kfree_skb(a,b) dev_kfree_skb(a,b)  		 
+ #define spin_lock_init(a)
+ #define spin_lock(a)
+ #define spin_unlock(a)
 #endif
 
 #include <linux/wanrouter.h>
@@ -49,7 +101,7 @@
 #define	PACKED	__attribute__((packed))
 #endif
 
-#define	WANPIPE_MAGIC	0x414C4453L	/* signatire: 'SDLA' reversed */
+#define	WANPIPE_MAGIC	0x414C4453L	/* signature: 'SDLA' reversed */
 
 /* IOCTL numbers (up to 16) */
 #define	WANPIPE_DUMP	(ROUTER_USER+0)	/* dump adapter's memory */
@@ -64,6 +116,51 @@
 #define UDPMGMT_REPLY	0x02
 #define UDP_OFFSET	12
 
+#define MAX_CMD_BUFF 	10
+#define MAX_X25_LCN 	255	/* Maximum number of x25 channels */
+#define MAX_LCN_NUM	4095	/* Maximum lcn number */
+#define MAX_FT1_RETRY 	100
+
+#ifdef LINUX_2_4
+  #ifndef AF_WANPIPE
+	#define AF_WANPIPE 25
+	#ifndef PF_WANPIPE
+		#define PF_WANPIPE AF_WANPIPE
+	#endif
+  #endif
+
+#else
+  #ifndef AF_WANPIPE
+	#define AF_WANPIPE 24
+	#ifndef PF_WANPIPE
+		#define PF_WANPIPE AF_WANPIPE
+	#endif
+  #endif
+#endif
+
+
+#define TX_TIMEOUT 5*HZ
+
+/* General Critical Flags */
+#define SEND_CRIT	0x00
+#define PERI_CRIT	0x01
+
+/* Chdlc and PPP polling critical flag */
+#define POLL_CRIT	0x03
+
+/* Frame Relay Tx IRQ send critical flag */
+#define SEND_TXIRQ_CRIT 0x02
+
+/* Frame Relay ARP critical flag */
+#define ARP_CRIT	0x03
+
+/* Bit maps for dynamic interface configuration
+ * DYN_OPT_ON : turns this option on/off 
+ * DEV_DOWN   : device was shutdown by the driver not
+ *              by user 
+ */
+#define DYN_OPT_ON	0x00
+#define DEV_DOWN	0x01
 
 /*
  * Data structures for IOCTL calls.
@@ -197,6 +294,9 @@
 } pipe_mgmt_stat_t;
 
 
+typedef struct {
+	struct sk_buff *skb;
+} bh_data_t, cmd_data_t;
 
 #define MAX_LGTH_UDP_MGNT_PKT 2000
  
@@ -210,11 +310,27 @@
 #define	WUM_KILL	0x50
 #define	WUM_EXEC	0x51
 
+#define WANPIPE		0x00
+#define API		0x01
+#define BRIDGE		0x02
+#define BRIDGE_NODE	0x03
+
 #ifdef	__KERNEL__
 /****** Kernel Interface ****************************************************/
 
 #include <linux/sdladrv.h>	/* SDLA support module API definitions */
 #include <linux/sdlasfm.h>	/* SDLA firmware module definitions */
+#include <linux/tqueue.h>
+#ifdef LINUX_2_4
+  #include <linux/serial.h>
+  #include <linux/serialP.h>
+  #include <linux/serial_reg.h>
+  #include <asm/serial.h>
+#endif
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
 
 #ifndef	min
 #define min(a,b) (((a)<(b))?(a):(b))
@@ -230,6 +346,7 @@
 	 	  ((ch)>=(unsigned)'a'&&(ch)<=(unsigned)'f')||\
 	 	  ((ch)>=(unsigned)'A'&&(ch)<=(unsigned)'F'))?1:0)
 
+
 /****** Data Structures *****************************************************/
 
 /* Adapter Data Space.
@@ -241,6 +358,7 @@
 	char devname[WAN_DRVNAME_SZ+1];	/* card name */
 	sdlahw_t hw;			/* hardware configuration */
 	wan_device_t wandev;		/* WAN device data space */
+	
 	unsigned open_cnt;		/* number of open interfaces */
 	unsigned long state_tick;	/* link state timestamp */
 	unsigned intr_mode;		/* Type of Interrupt Mode */
@@ -248,30 +366,71 @@
 	char buff_int_mode_unbusy;	/* flag for carrying out dev_tint */  
 	char dlci_int_mode_unbusy;	/* flag for carrying out dev_tint */
 	char configured;		/* flag for previous configurations */
+	
 	unsigned short irq_dis_if_send_count; /* Disabling irqs in if_send*/
 	unsigned short irq_dis_poll_count;   /* Disabling irqs in poll routine*/
 	unsigned short force_enable_irq;
 	char TracingEnabled;		/* flag for enabling trace */
 	global_stats_t statistics;	/* global statistics */
-#ifdef CONFIG_SMP
-	spinlock_t lock;                /* Support for SMP Locking */
-#endif
 	void* mbox;			/* -> mailbox */
 	void* rxmb;			/* -> receive mailbox */
 	void* flags;			/* -> adapter status flags */
 	void (*isr)(struct sdla* card);	/* interrupt service routine */
 	void (*poll)(struct sdla* card); /* polling routine */
 	int (*exec)(struct sdla* card, void* u_cmd, void* u_data);
-
-	struct sdla *next;		/* Secondary Port Device: Piggibacking */
+					/* Used by the listen() system call */		
+	/* Wanpipe Socket Interface */
+	int   (*func) (struct sk_buff *, struct sock *);
+	struct sock *sk;
+
+	/* Shutdown function */
+	void (*disable_comm) (struct sdla *card);
+
+	/* Secondary Port Device: Piggibacking */
+	struct sdla *next;
+
+	/* TTY driver variables */
+	unsigned char tty_opt;
+	struct tty_struct *tty;
+	unsigned int tty_minor;
+	unsigned int tty_open;
+	unsigned char *tty_buf;
+	unsigned char *tty_rx;
+	struct tq_struct tty_task_queue;
+	
 	union
 	{
 		struct
 		{			/****** X.25 specific data **********/
-			unsigned lo_pvc;
-			unsigned hi_pvc;
-			unsigned lo_svc;
-			unsigned hi_svc;
+			u32 lo_pvc;
+			u32 hi_pvc;
+			u32 lo_svc;
+			u32 hi_svc;
+			netdevice_t *svc_to_dev_map[MAX_X25_LCN];
+			netdevice_t *pvc_to_dev_map[MAX_X25_LCN];
+			netdevice_t *tx_dev;
+			netdevice_t *cmd_dev;
+			u32 no_dev;
+			volatile u8 *hdlc_buf_status;
+			u32 tx_interrupts_pending;
+                        u16 timer_int_enabled;
+			netdevice_t *poll_device;
+			atomic_t command_busy;
+
+			u16 udp_pkt_lgth;
+                        u32 udp_type;
+                        u8  udp_pkt_src;
+			u32 udp_lcn;
+                        netdevice_t * udp_dev;
+                        s8 udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT];
+
+		 	u8 LAPB_hdlc;		/* Option to turn off X25 and run only LAPB */
+			u8 logging;		/* Option to log call messages */
+			u8 oob_on_modem;	/* Option to send modem status to the api */
+			u16 num_of_ch;		/* Number of channels configured by the user */
+
+			struct tq_struct x25_poll_task;
+			struct timer_list x25_timer;
 		} x;
 		struct
 		{			/****** frame relay specific data ***/
@@ -281,7 +440,7 @@
 			unsigned rx_top;	/* S508 receive buffer end */
 			unsigned short node_dlci[100];
 			unsigned short dlci_num;
-                        struct net_device *dlci_to_dev_map[991 + 1];
+                        netdevice_t *dlci_to_dev_map[991 + 1];
                         unsigned tx_interrupts_pending;
                         unsigned short timer_int_enabled;
                         unsigned short udp_pkt_lgth;
@@ -289,11 +448,13 @@
                         char udp_pkt_src;
                         unsigned udp_dlci;
                         char udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT];
-                        void* trc_el_base;      /* first trace element */
-                        void* trc_el_last;      /* last trace element */
-                        void *curr_trc_el;      /* current trace element */
-                        unsigned short trc_bfr_space; /* trace buffer space */
+                        void* trc_el_base;      		/* first trace element */
+                        void* trc_el_last;      		/* last trace element */
+                        void *curr_trc_el;      		/* current trace element */
+                        unsigned short trc_bfr_space; 		/* trace buffer space */
 			unsigned char  update_comms_stats;
+			netdevice_t *arp_dev;
+			spinlock_t if_send_lock;
 		} f;
 		struct			/****** PPP-specific data ***********/
 		{
@@ -307,6 +468,10 @@
 			unsigned rx_top;	/* S508 receive buffer end */
 			char ip_mode;		/* STATIC/HOST/PEER IP Mode */
 			char authenticator;	/* Authenticator for PAP/CHAP */
+			unsigned char comm_enabled; /* Is comm enabled or not */
+			unsigned char peer_route;   /* Process Peer Route */	
+			unsigned long *txbuf_next;  /* Next Tx buffer to use */ 
+			unsigned long *rxbuf_next;  /* Next Rx buffer to use */
 		} p;
 		struct			/* Cisco HDLC-specific data */
 		{
@@ -324,6 +489,7 @@
 			void* rxbuf_last;	/* -> last Rx buffer */
 			unsigned rx_base;	/* S508 receive buffer base */
 			unsigned rx_top;	/* S508 receive buffer end */
+			unsigned char receive_only; /* high speed receivers */
 			unsigned short protocol_options;
 			unsigned short kpalv_tx;	/* Tx kpalv timer */
 			unsigned short kpalv_rx;	/* Rx kpalv timer */
@@ -332,6 +498,19 @@
 			unsigned state;			/* state of the link */
 			unsigned char api_status;
 			unsigned char update_call_count;
+			unsigned short api_options;	/* for async config */
+			unsigned char  async_mode;
+		        unsigned short tx_bits_per_char;
+		        unsigned short rx_bits_per_char;
+		        unsigned short stop_bits;
+		        unsigned short parity;
+			unsigned short break_timer;
+		        unsigned short inter_char_timer;
+		        unsigned short rx_complete_length;
+		        unsigned short xon_char;
+		        unsigned short xoff_char;
+			unsigned char comm_enabled; /* Is comm enabled or not */
+			unsigned char backup;
 		} c;
 		struct
 		{
@@ -368,6 +547,18 @@
 int bsc_init (sdla_t* card, wandev_conf_t* conf);	/* BSC streaming */
 int hdlc_init(sdla_t* card, wandev_conf_t* conf);	/* HDLC support */
 int wpft1_init (sdla_t* card, wandev_conf_t* conf);     /* FT1 Config support */
+int wsppp_init (sdla_t* card, wandev_conf_t* conf);	/* Sync PPP on top of RAW CHDLC */
+
+extern sdla_t * wanpipe_find_card(char *);
+extern sdla_t * wanpipe_find_card_num (int);
+
+extern void wanpipe_queue_tq (struct tq_struct *);
+extern void wanpipe_mark_bh (void);
+extern void wakeup_sk_bh (netdevice_t *);
+extern int change_dev_flags (netdevice_t *, unsigned); 
+extern unsigned long get_ip_address (netdevice_t *dev, int option);
+extern void add_gateway(sdla_t *, netdevice_t *);
+
 
 #endif	/* __KERNEL__ */
 #endif	/* _WANPIPE_H */
diff -urN linux-2.4.4-pre2/include/linux/wanrouter.h linux/include/linux/wanrouter.h
--- linux-2.4.4-pre2/include/linux/wanrouter.h	Mon Mar 26 15:50:18 2001
+++ linux/include/linux/wanrouter.h	Mon Dec  3 22:34:51 2001
@@ -5,19 +5,19 @@
 *
 * Author: 	Nenad Corbic <ncorbic@sangoma.com>
 *		Gideon Hack 	
-* Additions:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *
-* Copyright:	(c) 1995-1999 Sangoma Technologies Inc.
+* Copyright:	(c) 1995-2000 Sangoma Technologies Inc.
 *
 *		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.
 * ============================================================================
+* Jul 21, 2000  Nenad Corbic	Added WAN_FT1_READY State
+* Feb 24, 2000  Nenad Corbic    Added support for socket based x25api
+* Jan 28, 2000  Nenad Corbic    Added support for the ASYNC protocol.
 * Oct 04, 1999  Nenad Corbic 	Updated for 2.1.0 release
 * Jun 02, 1999  Gideon Hack	Added support for the S514 adapter.
-* May 23, 1999	Arnaldo Melo	Added local_addr to wanif_conf_t
-*				WAN_DISCONNECTING state added
 * Jul 20, 1998	David Fong	Added Inverse ARP options to 'wanif_conf_t'
 * Jun 12, 1998	David Fong	Added Cisco HDLC support.
 * Dec 16, 1997	Jaspreet Singh	Moved 'enable_IPX' and 'network_number' to
@@ -42,8 +42,24 @@
 *****************************************************************************/
 #include <linux/version.h>
 
-#if LINUX_VERSION_CODE >= 0x020100
-#define LINUX_2_1
+#ifndef KERNEL_VERSION
+  #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ #define LINUX_2_4
+ #define netdevice_t struct net_device
+ #include <linux/spinlock.h>       /* Support for SMP Locking */
+
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ #define LINUX_2_1
+ #define netdevice_t struct device
+ #include <asm/spinlock.h>       /* Support for SMP Locking */
+
+#else
+ #define LINUX_2_0
+ #define netdevice_t struct device
+ #define spinlock_t int
 #endif
 
 #ifndef	_ROUTER_H
@@ -135,6 +151,10 @@
 	unsigned r12_r22;	/* RESET retransmission limit (0..250) */
 	unsigned r13_r23;	/* CLEAR retransmission limit (0..250) */
 	unsigned ccitt_compat;	/* compatibility mode: 1988/1984/1980 */
+	unsigned x25_conf_opt;   /* User defined x25 config optoins */
+	unsigned char LAPB_hdlc_only; /* Run in HDLC only mode */
+	unsigned char logging;   /* Control connection logging */  
+	unsigned char oob_on_modem; /* Whether to send modem status to the user app */
 } wan_x25_conf_t;
 
 /*----------------------------------------------------------------------------
@@ -182,6 +202,7 @@
 	unsigned char ignore_cts;	/*  Ignore these to determine	*/
 	unsigned char ignore_keepalive;	/*  link status (Yes or No)	*/
 	unsigned char hdlc_streaming;	/*  hdlc_streaming mode (Y/N) */
+	unsigned char receive_only;	/*  no transmit buffering (Y/N) */
 	unsigned keepalive_tx_tmr;	/* transmit keepalive timer */
 	unsigned keepalive_rx_tmr;	/* receive  keepalive timer */
 	unsigned keepalive_err_margin;	/* keepalive_error_tolerance */
@@ -204,6 +225,7 @@
 	int dma;		/* DMA request level */
         char S514_CPU_no[1];	/* S514 PCI adapter CPU number ('A' or 'B') */
         unsigned PCI_slot_no;	/* S514 PCI adapter slot number */
+	char auto_pci_cfg;	/* S515 PCI automatic slot detection */
 	char comm_port;		/* Communication Port (PRI=0, SEC=1) */ 
 	unsigned bps;		/* data transfer rate */
 	unsigned mtu;		/* maximum transmit unit size */
@@ -216,6 +238,12 @@
 	char station;		/* DTE/DCE, primary/secondary, etc. */
 	char connection;	/* permanent/switched/on-demand */
 	char read_mode;		/* read mode: Polling or interrupt */
+	char receive_only;	/* disable tx buffers */
+	char tty;		/* Create a fake tty device */
+	unsigned tty_major;	/* Major number for wanpipe tty device */
+	unsigned tty_minor; 	/* Minor number for wanpipe tty device */
+	unsigned tty_mode;	/* TTY operation mode SYNC or ASYNC */
+	char backup;		/* Backup Mode */
 	unsigned hw_opt[4];	/* other hardware options */
 	unsigned reserved[4];
 				/****** arbitrary data ***************/
@@ -237,6 +265,7 @@
 #define WANCONFIG_CHDLC	104	/* Cisco HDLC Link */
 #define WANCONFIG_BSC	105	/* BiSync Streaming */
 #define WANCONFIG_HDLC	106	/* HDLC Support */
+#define WANCONFIG_MPPP  107	/* Multi Port PPP over RAW CHDLC */
 
 /*
  * Configuration options defines.
@@ -288,6 +317,15 @@
 #define	WANOPT_PPP_HOST		1
 #define	WANOPT_PPP_PEER		2
 
+/* ASY Mode Options */
+#define WANOPT_ONE 		1
+#define WANOPT_TWO		2
+#define WANOPT_ONE_AND_HALF	3
+
+#define WANOPT_NONE	0
+#define WANOPT_ODD      1
+#define WANOPT_EVEN	2
+
 /* CHDLC Protocol Options */
 /* DF Commmented out for now.
 
@@ -303,6 +341,9 @@
 #define	WANOPT_INTR	0
 #define WANOPT_POLL	1
 
+
+#define WANOPT_TTY_SYNC  0
+#define WANOPT_TTY_ASYNC 1
 /*----------------------------------------------------------------------------
  * WAN Link Status Info (for ROUTER_STAT IOCTL).
  */
@@ -345,7 +386,15 @@
 	WAN_CONNECTED,		/* link/channel is operational */
 	WAN_LIMIT,		/* for verification only */
 	WAN_DUALPORT,		/* for Dual Port cards */
-	WAN_DISCONNECTING	/* link/channel is disconnecting */
+	WAN_DISCONNECTING,
+	WAN_FT1_READY		/* FT1 Configurator Ready */
+};
+
+enum {
+	WAN_LOCAL_IP,
+	WAN_POINTOPOINT_IP,
+	WAN_NETMASK_IP,
+	WAN_BROADCAST_IP
 };
 
 /* 'modem_status' masks */
@@ -374,9 +423,6 @@
 	unsigned inarp_interval;	/* sec, between InARP requests */
 	unsigned long network_number;	/* Network Number for IPX */
 	char mc;			/* Multicast on or off */
-	char local_addr[WAN_ADDRESS_SZ+1];/* local media address, ASCIIZ */
-	unsigned char port;		/* board port */
-	unsigned char protocol;		/* prococol used in this channel (TCPOX25 or X25) */
 	char pap;			/* PAP enabled or disabled */
 	char chap;			/* CHAP enabled or disabled */
 	unsigned char userid[511];	/* List of User Id */
@@ -386,6 +432,7 @@
 	unsigned char ignore_cts;	/*  Ignore these to determine */
 	unsigned char ignore_keepalive;	/*  link status (Yes or No) */
 	unsigned char hdlc_streaming;	/*  Hdlc streaming mode (Y/N) */
+	unsigned char receive_only;	/*  no transmit buffering (Y/N) */
 	unsigned keepalive_tx_tmr;	/* transmit keepalive timer */
 	unsigned keepalive_rx_tmr;	/* receive  keepalive timer */
 	unsigned keepalive_err_margin;	/* keepalive_error_tolerance */
@@ -395,6 +442,27 @@
 	char clocking;			/* external/internal */
 	unsigned bps;			/* data transfer rate */
 	unsigned mtu;			/* maximum transmit unit size */
+	unsigned char if_down;		/* brind down interface when disconnected */
+	unsigned char gateway;		/* Is this interface a gateway */
+	unsigned char true_if_encoding;	/* Set the dev->type to true board protocol */
+
+	unsigned char asy_data_trans;     /* async API options */
+        unsigned char rts_hs_for_receive; /* async Protocol options */
+        unsigned char xon_xoff_hs_for_receive;
+	unsigned char xon_xoff_hs_for_transmit;
+	unsigned char dcd_hs_for_transmit;
+	unsigned char cts_hs_for_transmit;
+	unsigned char async_mode;
+	unsigned tx_bits_per_char;
+	unsigned rx_bits_per_char;
+	unsigned stop_bits;  
+	unsigned char parity;
+ 	unsigned break_timer;
+        unsigned inter_char_timer;
+	unsigned rx_complete_length;
+	unsigned xon_char;
+	unsigned xoff_char;
+
 } wanif_conf_t;
 
 #ifdef	__KERNEL__
@@ -440,30 +508,40 @@
 					/****** status and statistics *******/
 	char state;			/* device state */
 	char api_status;		/* device api status */
+      #if defined(LINUX_2_1) || defined(LINUX_2_4)
 	struct net_device_stats stats; 	/* interface statistics */
+      #else
+	struct enet_statistics stats;	/* interface statistics */
+      #endif
 	unsigned reserved[16];		/* reserved for future use */
 	unsigned long critical;		/* critical section flag */
+	spinlock_t lock;                /* Support for SMP Locking */
+
 					/****** device management methods ***/
 	int (*setup) (struct wan_device *wandev, wandev_conf_t *conf);
 	int (*shutdown) (struct wan_device *wandev);
 	int (*update) (struct wan_device *wandev);
 	int (*ioctl) (struct wan_device *wandev, unsigned cmd,
 		unsigned long arg);
-	int (*new_if) (struct wan_device *wandev, struct net_device *dev,
+	int (*new_if) (struct wan_device *wandev, netdevice_t *dev,
 		wanif_conf_t *conf);
-	int (*del_if) (struct wan_device *wandev, struct net_device *dev);
+	int (*del_if) (struct wan_device *wandev, netdevice_t *dev);
 					/****** maintained by the router ****/
 	struct wan_device* next;	/* -> next device */
-	struct net_device* dev;		/* list of network interfaces */
+	netdevice_t* dev;		/* list of network interfaces */
 	unsigned ndev;			/* number of interfaces */
+      #ifdef LINUX_2_4
 	struct proc_dir_entry *dent;	/* proc filesystem entry */
+      #else
+	struct proc_dir_entry dent;	/* proc filesystem entry */
+      #endif
 } wan_device_t;
 
 /* Public functions available for device drivers */
 extern int register_wan_device(wan_device_t *wandev);
 extern int unregister_wan_device(char *name);
-unsigned short wanrouter_type_trans(struct sk_buff *skb, struct net_device *dev);
-int wanrouter_encapsulate(struct sk_buff *skb, struct net_device *dev);
+unsigned short wanrouter_type_trans(struct sk_buff *skb, netdevice_t *dev);
+int wanrouter_encapsulate(struct sk_buff *skb, netdevice_t *dev,unsigned short type);
 
 /* Proc interface functions. These must not be called by the drivers! */
 extern int wanrouter_proc_init(void);
@@ -471,6 +549,11 @@
 extern int wanrouter_proc_add(wan_device_t *wandev);
 extern int wanrouter_proc_delete(wan_device_t *wandev);
 extern int wanrouter_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+
+extern void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags);
+extern void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags);
+
+
 
 /* Public Data */
 extern wan_device_t *router_devlist;	/* list of registered devices */
diff -urN linux-2.4.4-pre2/include/linux/wavefront.h linux/include/linux/wavefront.h
--- linux-2.4.4-pre2/include/linux/wavefront.h	Mon Jan  4 11:42:43 1999
+++ linux/include/linux/wavefront.h	Mon Dec  3 22:34:51 2001
@@ -31,7 +31,7 @@
 	member, which has the same semantics anyway. 
      */
 
-#endif __GNUC__
+#endif /* __GNUC__ */
 
 /***************************** WARNING ********************************
   PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO 
@@ -40,11 +40,11 @@
 
 #ifndef NUM_MIDIKEYS 
 #define NUM_MIDIKEYS 128
-#endif  NUM_MIDIKEYS
+#endif  /* NUM_MIDIKEYS */
 
 #ifndef NUM_MIDICHANNELS
 #define NUM_MIDICHANNELS 16
-#endif  NUM_MIDICHANNELS
+#endif  /* NUM_MIDICHANNELS */
 
 /* These are very useful/important. the original wavefront interface
    was developed on a 16 bit system, where sizeof(int) = 2
@@ -667,9 +667,9 @@
 
 /* Allow direct user-space control over FX memory/coefficient data.
    In theory this could be used to download the FX microprogram,
-   but it would be a little slower, and involve some wierd code.
+   but it would be a little slower, and involve some weird code.
  */
 
 #define WFFX_MEMSET              69
 
-#endif __wavefront_h__
+#endif /* __wavefront_h__ */
diff -urN linux-2.4.4-pre2/include/linux/wireless.h linux/include/linux/wireless.h
--- linux-2.4.4-pre2/include/linux/wireless.h	Mon Mar 26 15:48:17 2001
+++ linux/include/linux/wireless.h	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :	9	16.10.99
+ * Version :	11	28.3.01
  *
  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
  */
@@ -63,7 +63,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	10
+#define WIRELESS_EXT	11
 
 /*
  * Changes :
@@ -111,6 +111,11 @@
  *	- Add PM modifier : MAX/MIN/RELATIVE
  *	- Add encoding option : IW_ENCODE_NOKEY
  *	- Add TxPower ioctls (work like TxRate)
+ *
+ * V10 to V11
+ * ----------
+ *	- Add WE version in range (help backward/forward compatibility)
+ *	- Add retry ioctls (work like PM)
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
@@ -153,7 +158,7 @@
  * The "flags" member indicate if the ESSID is active or not (promiscuous).
  */
 
-/* Other parameters usefull in 802.11 and some other devices */
+/* Other parameters useful in 802.11 and some other devices */
 #define SIOCSIWRATE	0x8B20		/* set default bit rate (bps) */
 #define SIOCGIWRATE	0x8B21		/* get default bit rate (bps) */
 #define SIOCSIWRTS	0x8B22		/* set RTS/CTS threshold (bytes) */
@@ -162,6 +167,8 @@
 #define SIOCGIWFRAG	0x8B25		/* get fragmentation thr (bytes) */
 #define SIOCSIWTXPOW	0x8B26		/* set transmit power (dBm) */
 #define SIOCGIWTXPOW	0x8B27		/* get transmit power (dBm) */
+#define SIOCSIWRETRY	0x8B28		/* set retry limits and lifetime */
+#define SIOCGIWRETRY	0x8B29		/* get retry limits and lifetime */
 
 /* Encoding stuff (scrambling, hardware security, WEP...) */
 #define SIOCSIWENCODE	0x8B2A		/* set encoding token & mode */
@@ -272,6 +279,16 @@
 #define IW_TXPOW_DBM		0x0000	/* Value is in dBm */
 #define IW_TXPOW_MWATT		0x0001	/* Value is in mW */
 
+/* Retry limits and lifetime flags available */
+#define IW_RETRY_ON		0x0000	/* No details... */
+#define IW_RETRY_TYPE		0xF000	/* Type of parameter */
+#define IW_RETRY_LIMIT		0x1000	/* Maximum number of retries*/
+#define IW_RETRY_LIFETIME	0x2000	/* Maximum duration of retries in us */
+#define IW_RETRY_MODIFIER	0x000F	/* Modify a parameter */
+#define IW_RETRY_MIN		0x0001	/* Value is a minimum  */
+#define IW_RETRY_MAX		0x0002	/* Value is a maximum */
+#define IW_RETRY_RELATIVE	0x0004	/* Value is not in seconds/ms/us */
+
 /****************************** TYPES ******************************/
 
 /* --------------------------- SUBTYPES --------------------------- */
@@ -288,7 +305,7 @@
 
 /*
  *	For all data larger than 16 octets, we need to use a
- *	pointer to memory alocated in user space.
+ *	pointer to memory allocated in user space.
  */
 struct	iw_point
 {
@@ -385,6 +402,7 @@
 		struct iw_param	rts;		/* RTS threshold threshold */
 		struct iw_param	frag;		/* Fragmentation threshold */
 		__u32		mode;		/* Operation mode */
+		struct iw_param	retry;		/* Retry limits & lifetime */
 
 		struct iw_point	encoding;	/* Encoding stuff : tokens */
 		struct iw_param	power;		/* PM duration/timeout */
@@ -462,6 +480,19 @@
 	__u16		txpower_capa;	/* What options are supported */
 	__u8		num_txpower;	/* Number of entries in the list */
 	__s32		txpower[IW_MAX_TXPOWER];	/* list, in bps */
+
+	/* Wireless Extension version info */
+	__u8		we_version_compiled;	/* Must be WIRELESS_EXT */
+	__u8		we_version_source;	/* Last update of source */
+
+	/* Retry limits and lifetime */
+	__u16		retry_capa;	/* What retry options are supported */
+	__u16		retry_flags;	/* How to decode max/min retry limit */
+	__u16		r_time_flags;	/* How to decode max/min retry life */
+	__s32		min_retry;	/* Minimal number of retries */
+	__s32		max_retry;	/* Maximal number of retries */
+	__s32		min_r_time;	/* Minimal retry lifetime */
+	__s32		max_r_time;	/* Maximal retry lifetime */
 };
 
 /*
diff -urN linux-2.4.4-pre2/include/net/icmp.h linux/include/net/icmp.h
--- linux-2.4.4-pre2/include/net/icmp.h	Mon Mar 26 15:48:59 2001
+++ linux/include/net/icmp.h	Mon Dec  3 22:34:51 2001
@@ -35,9 +35,8 @@
 #define ICMP_INC_STATS_BH(field)	SNMP_INC_STATS_BH(icmp_statistics, field)
 #define ICMP_INC_STATS_USER(field) 	SNMP_INC_STATS_USER(icmp_statistics, field)
 
-extern void	icmp_send(struct sk_buff *skb_in,  int type, int code,
-			  unsigned long info);
-extern int	icmp_rcv(struct sk_buff *skb, unsigned short len);
+extern void	icmp_send(struct sk_buff *skb_in,  int type, int code, u32 info);
+extern int	icmp_rcv(struct sk_buff *skb);
 extern int	icmp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern void	icmp_init(struct net_proto_family *ops);
 
diff -urN linux-2.4.4-pre2/include/net/ip.h linux/include/net/ip.h
--- linux-2.4.4-pre2/include/net/ip.h	Mon Mar 26 15:48:31 2001
+++ linux/include/net/ip.h	Mon Dec  3 22:34:51 2001
@@ -188,11 +188,16 @@
 
 extern void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst);
 
-static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst)
+static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst, struct sock *sk)
 {
-	if (iph->frag_off&__constant_htons(IP_DF))
-		iph->id = 0;
-	else
+	if (iph->frag_off&__constant_htons(IP_DF)) {
+		/* This is only to work around buggy Windows95/2000
+		 * VJ compression implementations.  If the ID field
+		 * does not change, they drop every other packet in
+		 * a TCP stream using header compression.
+		 */
+		iph->id = ((sk && sk->daddr) ? htons(sk->protinfo.af_inet.id++) : 0);
+	} else
 		__ip_select_ident(iph, dst);
 }
 
diff -urN linux-2.4.4-pre2/include/net/ipip.h linux/include/net/ipip.h
--- linux-2.4.4-pre2/include/net/ipip.h	Fri Aug  4 18:18:49 2000
+++ linux/include/net/ipip.h	Mon Dec  3 22:34:51 2001
@@ -29,8 +29,9 @@
 	int err;							\
 	int pkt_len = skb->len;						\
 									\
+	skb->ip_summed = CHECKSUM_NONE;					\
 	iph->tot_len = htons(skb->len);					\
-	ip_select_ident(iph, &rt->u.dst);				\
+	ip_select_ident(iph, &rt->u.dst, NULL);				\
 	ip_send_check(iph);						\
 									\
 	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, do_ip_send); \
diff -urN linux-2.4.4-pre2/include/net/ipv6.h linux/include/net/ipv6.h
--- linux-2.4.4-pre2/include/net/ipv6.h	Mon Mar 26 15:48:54 2001
+++ linux/include/net/ipv6.h	Mon Dec  3 22:34:51 2001
@@ -4,7 +4,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: ipv6.h,v 1.22 2000/09/18 05:54:13 davem Exp $
+ *	$Id: ipv6.h,v 1.23 2000/12/13 18:31:48 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -181,11 +181,11 @@
 
 extern int			ip6_call_ra_chain(struct sk_buff *skb, int sel);
 
-extern u8 *			ipv6_reassembly(struct sk_buff **skb, u8 *nhptr);
+extern int			ipv6_reassembly(struct sk_buff **skb, int);
 
-extern u8 *			ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr);
+extern int			ipv6_parse_hopopts(struct sk_buff *skb, int);
 
-extern u8 *			ipv6_parse_exthdrs(struct sk_buff **skb, u8 *nhptr);
+extern int			ipv6_parse_exthdrs(struct sk_buff **skb, int);
 
 extern struct ipv6_txoptions *  ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
 
@@ -303,8 +303,10 @@
 						    struct ipv6_txoptions *opt,
 						    u8 *proto);
 
-extern u8 *			ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, 
+extern int			ipv6_skip_exthdr(struct sk_buff *, int start,
 					         u8 *nexthdrp, int len);
+
+extern int 			ipv6_ext_hdr(u8 nexthdr);
 
 extern struct ipv6_txoptions *	ipv6_invert_rthdr(struct sock *sk,
 						  struct ipv6_rt_hdr *hdr);
diff -urN linux-2.4.4-pre2/include/net/ndisc.h linux/include/net/ndisc.h
--- linux-2.4.4-pre2/include/net/ndisc.h	Mon Mar 26 15:48:52 2001
+++ linux/include/net/ndisc.h	Mon Dec  3 22:34:51 2001
@@ -60,7 +60,7 @@
 
 extern void			ndisc_cleanup(void);
 
-extern int			ndisc_rcv(struct sk_buff *skb, unsigned long len);
+extern int			ndisc_rcv(struct sk_buff *skb);
 
 extern void			ndisc_send_ns(struct net_device *dev,
 					      struct neighbour *neigh,
@@ -91,13 +91,9 @@
 
 extern void			igmp6_cleanup(void);
 
-extern int			igmp6_event_query(struct sk_buff *skb,
-						  struct icmp6hdr *hdr,
-						  int len);
-
-extern int			igmp6_event_report(struct sk_buff *skb,
-						   struct icmp6hdr *hdr,
-						   int len);
+extern int			igmp6_event_query(struct sk_buff *skb);
+
+extern int			igmp6_event_report(struct sk_buff *skb);
 
 extern void			igmp6_cleanup(void);
 
diff -urN linux-2.4.4-pre2/include/net/protocol.h linux/include/net/protocol.h
--- linux-2.4.4-pre2/include/net/protocol.h	Mon Mar 26 15:48:11 2001
+++ linux/include/net/protocol.h	Mon Dec  3 22:34:51 2001
@@ -36,8 +36,8 @@
 /* This is used to register protocols. */
 struct inet_protocol 
 {
-	int			(*handler)(struct sk_buff *skb, unsigned short len);
-	void			(*err_handler)(struct sk_buff *skb, unsigned char *dp, int len);
+	int			(*handler)(struct sk_buff *skb);
+	void			(*err_handler)(struct sk_buff *skb, u32 info);
 	struct inet_protocol	*next;
 	unsigned char		protocol;
 	unsigned char		copy:1;
@@ -48,12 +48,11 @@
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 struct inet6_protocol 
 {
-	int	(*handler)(struct sk_buff *skb,
-			unsigned long len);
+	int	(*handler)(struct sk_buff *skb);
 
-	void	(*err_handler)(struct sk_buff *skb, struct ipv6hdr *hdr,
+	void	(*err_handler)(struct sk_buff *skb,
 			       struct inet6_skb_parm *opt,
-			       int type, int code, unsigned char *buff,
+			       int type, int code, int offset,
 			       __u32 info);
 	struct inet6_protocol *next;
 	unsigned char	protocol;
diff -urN linux-2.4.4-pre2/include/net/raw.h linux/include/net/raw.h
--- linux-2.4.4-pre2/include/net/raw.h	Mon Aug 23 10:01:02 1999
+++ linux/include/net/raw.h	Mon Dec  3 22:34:51 2001
@@ -21,7 +21,7 @@
 extern struct proto raw_prot;
 
 
-extern void 	raw_err(struct sock *, struct sk_buff *);
+extern void 	raw_err(struct sock *, struct sk_buff *, u32 info);
 extern int 	raw_rcv(struct sock *, struct sk_buff *);
 
 /* Note: v4 ICMP wants to get at this stuff, if you change the
diff -urN linux-2.4.4-pre2/include/net/rawv6.h linux/include/net/rawv6.h
--- linux-2.4.4-pre2/include/net/rawv6.h	Mon Aug 23 10:01:02 1999
+++ linux/include/net/rawv6.h	Mon Dec  3 22:34:51 2001
@@ -8,23 +8,21 @@
 extern rwlock_t raw_v6_lock;
 
 extern struct sock * ipv6_raw_deliver(struct sk_buff *skb,
-				      int nexthdr, unsigned long len);
+				      int nexthdr);
 
 
 extern struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
 				    struct in6_addr *loc_addr, struct in6_addr *rmt_addr);
 
 extern int			rawv6_rcv(struct sock *sk,
-					  struct sk_buff *skb, 
-					  unsigned long len);
+					  struct sk_buff *skb);
 
 
 extern void			rawv6_err(struct sock *sk,
 					  struct sk_buff *skb,
-					  struct ipv6hdr *hdr,
 					  struct inet6_skb_parm *opt,
 					  int type, int code, 
-					  unsigned char *buff, u32 info);
+					  int offset, u32 info);
 
 #endif
 
diff -urN linux-2.4.4-pre2/include/net/sock.h linux/include/net/sock.h
--- linux-2.4.4-pre2/include/net/sock.h	Mon Mar 26 15:48:31 2001
+++ linux/include/net/sock.h	Mon Dec  3 22:34:51 2001
@@ -56,6 +56,10 @@
 #if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
 #include <net/x25.h>
 #endif
+#if defined(CONFIG_WAN_ROUTER) || defined(CONFIG_WAN_ROUTER_MODULE)
+#include <linux/if_wanpipe.h>
+#endif
+
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #include <net/ax25.h>
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
@@ -204,6 +208,7 @@
 	__u8			mc_loop;		/* Loopback */
 	unsigned		recverr : 1,
 				freebind : 1;
+	__u16			id;			/* ID counter for DF pkts */
 	__u8			pmtudisc;
 	int			mc_index;		/* Multicast device index */
 	__u32			mc_addr;
@@ -331,6 +336,8 @@
 
 	struct tcp_func		*af_specific;	/* Operations which are AF_INET{4,6} specific	*/
 	struct sk_buff		*send_head;	/* Front of stuff to transmit			*/
+	struct page		*sndmsg_page;	/* Cached page for sendmsg			*/
+	u32			sndmsg_off;	/* Cached offset for sendmsg			*/
 
  	__u32	rcv_wnd;	/* Current receiver window		*/
 	__u32	rcv_wup;	/* rcv_nxt on last window update sent	*/
@@ -381,8 +388,6 @@
 				 * the first SYN. */
 	__u32	undo_marker;	/* tracking retrans started here. */
 	int	undo_retrans;	/* number of undoable retransmissions. */
-	__u32	syn_seq;	/* Seq of received SYN. */
-	__u32	fin_seq;	/* Seq of received FIN. */
 	__u32	urg_seq;	/* Seq of received urgent pointer */
 	__u16	urg_data;	/* Saved octet of OOB data and control flags */
 	__u8	pending;	/* Scheduled timer event	*/
@@ -535,6 +540,7 @@
 	unsigned char		debug;
 	unsigned char		rcvtstamp;
 	unsigned char		userlocks;
+	int			route_caps;
 	int			proc;
 	unsigned long	        lingertime;
 
@@ -645,6 +651,9 @@
 #if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
 		struct irda_sock        *irda;
 #endif
+#if defined(CONFIG_WAN_ROUTER) || defined(CONFIG_WAN_ROUTER_MODULE)
+               struct wanpipe_opt      *af_wanpipe;
+#endif
 	} protinfo;  		
 
 
@@ -833,14 +842,11 @@
 						int *optlen);
 extern struct sk_buff 		*sock_alloc_send_skb(struct sock *sk,
 						     unsigned long size,
-						     unsigned long fallback,
 						     int noblock,
 						     int *errcode);
 extern void *sock_kmalloc(struct sock *sk, int size, int priority);
 extern void sock_kfree_s(struct sock *sk, void *mem, int size);
 
-extern int copy_and_csum_toiovec(struct iovec *iov, struct sk_buff *skb, int hlen);
-
 /*
  * Functions to fill in entries in struct proto_ops when a protocol
  * does not implement a particular function.
@@ -877,6 +883,10 @@
 extern int			sock_no_mmap(struct file *file,
 					     struct socket *sock,
 					     struct vm_area_struct *vma);
+extern ssize_t			sock_no_sendpage(struct socket *sock,
+						struct page *page,
+						int offset, size_t size, 
+						int flags);
 
 /*
  *	Default socket callbacks and setup code
diff -urN linux-2.4.4-pre2/include/net/tcp.h linux/include/net/tcp.h
--- linux-2.4.4-pre2/include/net/tcp.h	Mon Mar 26 15:48:33 2001
+++ linux/include/net/tcp.h	Mon Dec  3 22:34:51 2001
@@ -19,10 +19,7 @@
 #define _TCP_H
 
 #define TCP_DEBUG 1
-#define FASTRETRANS_DEBUG 1
-
-/* Be paranoid about data immediately beyond right edge of window. */
-#undef  TCP_FORMAL_WINDOW
+#define FASTRETRANS_DEBUG 2
 
 /* Cancel timers, when they are not required. */
 #undef TCP_CLEAR_TIMERS
@@ -182,7 +179,6 @@
 	__u32			rcv_nxt;
 	__u32			snd_nxt;
 	__u32			rcv_wnd;
-	__u32			syn_seq;
         __u32			ts_recent;
         long			ts_recent_stamp;
 	unsigned long		ttd;
@@ -357,7 +353,7 @@
 #define TCP_TWKILL_PERIOD	(TCP_TIMEWAIT_LEN/TCP_TWKILL_SLOTS)
 
 #define TCP_SYNQ_INTERVAL	(HZ/5)	/* Period of SYNACK timer */
-#define TCP_SYNQ_HSIZE		64	/* Size of SYNACK hash table */
+#define TCP_SYNQ_HSIZE		512	/* Size of SYNACK hash table */
 
 #define TCP_PAWS_24DAYS	(60 * 60 * 24 * 24)
 #define TCP_PAWS_MSL	60		/* Per-host timestamps are invalidated
@@ -632,19 +628,18 @@
 extern void			__tcp_put_port(struct sock *sk);
 extern void			tcp_inherit_port(struct sock *sk, struct sock *child);
 
-extern void			tcp_v4_err(struct sk_buff *skb,
-					   unsigned char *, int);
+extern void			tcp_v4_err(struct sk_buff *skb, u32);
 
 extern void			tcp_shutdown (struct sock *sk, int how);
 
-extern int			tcp_v4_rcv(struct sk_buff *skb,
-					   unsigned short len);
+extern int			tcp_v4_rcv(struct sk_buff *skb);
 
 extern int			tcp_v4_remember_stamp(struct sock *sk);
 
 extern int		    	tcp_v4_tw_remember_stamp(struct tcp_tw_bucket *tw);
 
 extern int			tcp_sendmsg(struct sock *sk, struct msghdr *msg, int size);
+extern ssize_t			tcp_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags);
 
 extern int			tcp_ioctl(struct sock *sk, 
 					  int cmd, 
@@ -799,7 +794,7 @@
 
 /* tcp_output.c */
 
-extern int tcp_write_xmit(struct sock *);
+extern int tcp_write_xmit(struct sock *, int nonagle);
 extern int tcp_retransmit_skb(struct sock *, struct sk_buff *);
 extern void tcp_xmit_retransmit_queue(struct sock *);
 extern void tcp_simple_retransmit(struct sock *);
@@ -812,6 +807,7 @@
 extern int  tcp_send_synack(struct sock *);
 extern int  tcp_transmit_skb(struct sock *, struct sk_buff *);
 extern void tcp_send_skb(struct sock *, struct sk_buff *, int force_queue, unsigned mss_now);
+extern void tcp_push_one(struct sock *, unsigned mss_now);
 extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_delayed_ack(struct sock *sk);
 
@@ -942,6 +938,15 @@
 	__tcp_fast_path_on(tp, tp->snd_wnd>>tp->snd_wscale);
 }
 
+static inline void tcp_fast_path_check(struct sock *sk, struct tcp_opt *tp)
+{
+	if (skb_queue_len(&tp->out_of_order_queue) == 0 &&
+	    tp->rcv_wnd &&
+	    atomic_read(&sk->rmem_alloc) < sk->rcvbuf &&
+	    !tp->urg_data)
+		tcp_fast_path_on(tp);
+}
+
 /* Compute the actual receive window we are currently advertising.
  * Rcv_nxt can be after the window if our peer push more data
  * than the offered window.
@@ -1084,6 +1089,13 @@
 		return max(tp->snd_ssthresh, (tp->snd_cwnd>>1)+(tp->snd_cwnd>>2));
 }
 
+static inline void tcp_sync_left_out(struct tcp_opt *tp)
+{
+	if (tp->sack_ok && tp->sacked_out >= tp->packets_out - tp->lost_out)
+		tp->sacked_out = tp->packets_out - tp->lost_out;
+	tp->left_out = tp->sacked_out + tp->lost_out;
+}
+
 extern void tcp_cwnd_application_limited(struct sock *sk);
 
 /* Congestion window validation. (RFC2861) */
@@ -1229,7 +1241,7 @@
 		if (!tcp_skb_is_last(sk, skb))
 			nonagle = 1;
 		if (!tcp_snd_test(tp, skb, cur_mss, nonagle) ||
-		    tcp_write_xmit(sk))
+		    tcp_write_xmit(sk, nonagle))
 			tcp_check_probe_timer(sk, tp);
 	}
 	tcp_cwnd_validate(sk, tp);
@@ -1275,7 +1287,7 @@
 
 static __inline__ int __tcp_checksum_complete(struct sk_buff *skb)
 {
-	return (unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum));
+	return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
 }
 
 static __inline__ int tcp_checksum_complete(struct sk_buff *skb)
@@ -1299,23 +1311,31 @@
  * idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93)
  * failed somewhere. Latency? Burstiness? Well, at least now we will
  * see, why it failed. 8)8)				  --ANK
+ *
+ * NOTE: is this not too big to inline?
  */
 static __inline__ int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
 	if (tp->ucopy.task) {
-		if ((tp->ucopy.memory += skb->truesize) <= (sk->rcvbuf<<1)) {
-			__skb_queue_tail(&tp->ucopy.prequeue, skb);
-			if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
-				wake_up_interruptible(sk->sleep);
-				if (!tcp_ack_scheduled(tp))
-					tcp_reset_xmit_timer(sk, TCP_TIME_DACK, (3*TCP_RTO_MIN)/4);
+		__skb_queue_tail(&tp->ucopy.prequeue, skb);
+		tp->ucopy.memory += skb->truesize;
+		if (tp->ucopy.memory > sk->rcvbuf) {
+			struct sk_buff *skb1;
+
+			if (sk->lock.users) BUG();
+
+			while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
+				sk->backlog_rcv(sk, skb1);
+				NET_INC_STATS_BH(TCPPrequeueDropped);
 			}
-		} else {
-			NET_INC_STATS_BH(TCPPrequeueDropped);
-			tp->ucopy.memory -= skb->truesize;
-			__kfree_skb(skb);
+
+			tp->ucopy.memory = 0;
+		} else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
+			wake_up_interruptible(sk->sleep);
+			if (!tcp_ack_scheduled(tp))
+				tcp_reset_xmit_timer(sk, TCP_TIME_DACK, (3*TCP_RTO_MIN)/4);
 		}
 		return 1;
 	}
@@ -1677,19 +1697,40 @@
 	}
 }
 
-static inline struct sk_buff *tcp_alloc_skb(struct sock *sk, int size, int gfp)
+static inline struct sk_buff *tcp_alloc_pskb(struct sock *sk, int size, int mem, int gfp)
 {
-	struct sk_buff *skb = alloc_skb(size, gfp);
+	struct sk_buff *skb = alloc_skb(size+MAX_TCP_HEADER, gfp);
 
 	if (skb) {
+		skb->truesize += mem;
 		if (sk->forward_alloc >= (int)skb->truesize ||
-		    tcp_mem_schedule(sk, skb->truesize, 0))
+		    tcp_mem_schedule(sk, skb->truesize, 0)) {
+			skb_reserve(skb, MAX_TCP_HEADER);
 			return skb;
+		}
 		__kfree_skb(skb);
 	} else {
 		tcp_enter_memory_pressure();
 		tcp_moderate_sndbuf(sk);
 	}
+	return NULL;
+}
+
+static inline struct sk_buff *tcp_alloc_skb(struct sock *sk, int size, int gfp)
+{
+	return tcp_alloc_pskb(sk, size, 0, gfp);
+}
+
+static inline struct page * tcp_alloc_page(struct sock *sk)
+{
+	if (sk->forward_alloc >= (int)PAGE_SIZE ||
+	    tcp_mem_schedule(sk, PAGE_SIZE, 0)) {
+		struct page *page = alloc_pages(sk->allocation, 0);
+		if (page)
+			return page;
+	}
+	tcp_enter_memory_pressure();
+	tcp_moderate_sndbuf(sk);
 	return NULL;
 }
 
diff -urN linux-2.4.4-pre2/include/net/udp.h linux/include/net/udp.h
--- linux-2.4.4-pre2/include/net/udp.h	Mon Mar 26 15:49:02 2001
+++ linux/include/net/udp.h	Mon Dec  3 22:34:51 2001
@@ -59,13 +59,13 @@
 extern struct proto udp_prot;
 
 
-extern void	udp_err(struct sk_buff *, unsigned char *, int);
+extern void	udp_err(struct sk_buff *, u32);
 extern int	udp_connect(struct sock *sk,
 			    struct sockaddr *usin, int addr_len);
 
 extern int	udp_sendmsg(struct sock *sk, struct msghdr *msg, int len);
 
-extern int	udp_rcv(struct sk_buff *skb, unsigned short len);
+extern int	udp_rcv(struct sk_buff *skb);
 extern int	udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern int	udp_disconnect(struct sock *sk, int flags);
 
diff -urN linux-2.4.4-pre2/include/video/newport.h linux/include/video/newport.h
--- linux-2.4.4-pre2/include/video/newport.h	Fri Feb 25 10:30:57 2000
+++ linux/include/video/newport.h	Mon Dec  3 22:34:51 2001
@@ -486,7 +486,7 @@
  * DCBMODE register defines:
  */
 
-/* Widht of the data being transfered for each DCBDATA[01] word */
+/* Width of the data being transferred for each DCBDATA[01] word */
 #define DCB_DATAWIDTH_4 0x0
 #define DCB_DATAWIDTH_1 0x1
 #define DCB_DATAWIDTH_2 0x2
diff -urN linux-2.4.4-pre2/kernel/fork.c linux/kernel/fork.c
--- linux-2.4.4-pre2/kernel/fork.c	Mon Dec  3 22:34:42 2001
+++ linux/kernel/fork.c	Mon Dec  3 22:34:51 2001
@@ -666,15 +666,17 @@
 	p->pdeath_signal = 0;
 
 	/*
-	 * "share" dynamic priority between parent and child, thus the
-	 * total amount of dynamic priorities in the system doesnt change,
-	 * more scheduling fairness. This is only important in the first
-	 * timeslice, on the long run the scheduling behaviour is unchanged.
+	 * Give the parent's dynamic priority entirely to the child.  The
+	 * total amount of dynamic priorities in the system doesn't change
+	 * (more scheduling fairness), but the child will run first, which
+	 * is especially useful in avoiding a lot of copy-on-write faults
+	 * if the child for a fork() just wants to do a few simple things
+	 * and then exec(). This is only important in the first timeslice.
+	 * In the long run, the scheduling behavior is unchanged.
 	 */
-	p->counter = (current->counter + 1) >> 1;
-	current->counter >>= 1;
-	if (!current->counter)
-		current->need_resched = 1;
+	p->counter = current->counter;
+	current->counter = 0;
+	current->need_resched = 1;
 
 	/*
 	 * Ok, add it to the run-queues and make it
diff -urN linux-2.4.4-pre2/kernel/sched.c linux/kernel/sched.c
--- linux-2.4.4-pre2/kernel/sched.c	Mon Dec  3 22:34:42 2001
+++ linux/kernel/sched.c	Mon Dec  3 22:34:51 2001
@@ -1093,9 +1093,11 @@
 	int i;
 
 	// Substract non-idle processes running on other CPUs.
-	for (i = 0; i < smp_num_cpus; i++)
-		if (aligned_data[i].schedule_data.curr != idle_task(i))
+	for (i = 0; i < smp_num_cpus; i++) {
+		int cpu = cpu_logical_map(i);
+		if (aligned_data[cpu].schedule_data.curr != idle_task(cpu))
 			nr_pending--;
+	}
 #else
 	// on UP this process is on the runqueue as well
 	nr_pending--;
diff -urN linux-2.4.4-pre2/kernel/sys.c linux/kernel/sys.c
--- linux-2.4.4-pre2/kernel/sys.c	Tue Mar  6 19:44:37 2001
+++ linux/kernel/sys.c	Mon Dec  3 22:34:51 2001
@@ -1089,7 +1089,7 @@
 			? -EFAULT : 0;
 }
 
-#if !defined(__ia64__) && !defined(__s390__)
+#if !defined(__ia64__) 
 
 /*
  *	Back compatibility for getrlimit. Needed for some apps.
diff -urN linux-2.4.4-pre2/kernel/sysctl.c linux/kernel/sysctl.c
--- linux-2.4.4-pre2/kernel/sysctl.c	Fri Feb 16 16:02:37 2001
+++ linux/kernel/sysctl.c	Mon Dec  3 22:34:51 2001
@@ -77,7 +77,7 @@
 #endif
 
 #ifdef CONFIG_ARCH_S390
-#ifdef CONFIG_IEEEFPU_EMULATION
+#ifdef CONFIG_MATHEMU
 extern int sysctl_ieee_emulation_warnings;
 #endif
 extern int sysctl_userprocess_debug;
@@ -242,7 +242,7 @@
 	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
 	 &minolduid, &maxolduid},
 #ifdef CONFIG_ARCH_S390
-#ifdef CONFIG_IEEEFPU_EMULATION
+#ifdef CONFIG_MATHEMU
 	{KERN_IEEE_EMULATION_WARNINGS,"ieee_emulation_warnings",
 	 &sysctl_ieee_emulation_warnings,sizeof(int),0644,NULL,&proc_dointvec},
 #endif
diff -urN linux-2.4.4-pre2/lib/Makefile linux/lib/Makefile
--- linux-2.4.4-pre2/lib/Makefile	Fri Dec 29 14:07:24 2000
+++ linux/lib/Makefile	Mon Dec  3 22:34:51 2001
@@ -8,9 +8,9 @@
 
 L_TARGET := lib.a
 
-export-objs := cmdline.o
+export-objs := cmdline.o rwsem.o
 
-obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o
+obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o rwsem.o
 
 ifneq ($(CONFIG_HAVE_DEC_LOCK),y) 
   obj-y += dec_and_lock.o
diff -urN linux-2.4.4-pre2/lib/cmdline.c linux/lib/cmdline.c
--- linux-2.4.4-pre2/lib/cmdline.c	Fri Aug 11 19:14:46 2000
+++ linux/lib/cmdline.c	Mon Dec  3 22:34:51 2001
@@ -93,9 +93,9 @@
  *	megabyte, or one gigabyte, respectively.
  */
 
-unsigned long memparse (char *ptr, char **retptr)
+unsigned long long memparse (char *ptr, char **retptr)
 {
-	unsigned long ret = simple_strtoul (ptr, retptr, 0);
+	unsigned long long ret = simple_strtoull (ptr, retptr, 0);
 
 	switch (**retptr) {
 	case 'G':
diff -urN linux-2.4.4-pre2/lib/rwsem.c linux/lib/rwsem.c
--- linux-2.4.4-pre2/lib/rwsem.c	Wed Dec 31 16:00:00 1969
+++ linux/lib/rwsem.c	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,156 @@
+/* rwsem.c: R/W semaphores: contention handling functions
+ *
+ * Written by David Howells (dhowells@redhat.com).
+ * Derived from arch/i386/kernel/semaphore.c
+ */
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+
+/*
+ * wait for the read lock to be granted
+ * - need to repeal the increment made inline by the caller
+ * - need to throw a write-lock style spanner into the works (sub 0x00010000 from count)
+ */
+struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem)
+{
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait,tsk);
+	signed long count;
+
+	rwsemdebug("[%d] Entering rwsem_down_read_failed(%08lx)\n",current->pid,sem->count);
+
+	/* this waitqueue context flag will be cleared when we are granted the lock */
+	__set_bit(RWSEM_WAITING_FOR_READ,&wait.flags);
+	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+
+	add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
+
+	/* note that we're now waiting on the lock, but no longer actively read-locking */
+	count = rwsem_atomic_update(RWSEM_WAITING_BIAS-RWSEM_ACTIVE_BIAS,sem);
+	rwsemdebug("X(%08lx)\n",count);
+
+	/* if there are no longer active locks, wake the front queued process(es) up
+	 * - it might even be this process, since the waker takes a more active part
+	 */
+	if (!(count & RWSEM_ACTIVE_MASK))
+		rwsem_wake(sem);
+
+	/* wait to be given the lock */
+	for (;;) {
+		if (!test_bit(RWSEM_WAITING_FOR_READ,&wait.flags))
+			break;
+		schedule();
+		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+	}
+
+	remove_wait_queue(&sem->wait,&wait);
+	tsk->state = TASK_RUNNING;
+
+	rwsemdebug("[%d] Leaving rwsem_down_read_failed(%08lx)\n",current->pid,sem->count);
+
+	return sem;
+}
+
+/*
+ * wait for the write lock to be granted
+ */
+struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem)
+{
+	struct task_struct *tsk = current;
+	DECLARE_WAITQUEUE(wait,tsk);
+	signed long count;
+
+	rwsemdebug("[%d] Entering rwsem_down_write_failed(%08lx)\n",current->pid,sem->count);
+
+	/* this waitqueue context flag will be cleared when we are granted the lock */
+	__set_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags);
+	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+
+	add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
+
+	/* note that we're waiting on the lock, but no longer actively locking */
+	count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
+	rwsemdebug("[%d] updated(%08lx)\n",current->pid,count);
+
+	/* if there are no longer active locks, wake the front queued process(es) up
+	 * - it might even be this process, since the waker takes a more active part
+	 */
+	if (!(count & RWSEM_ACTIVE_MASK))
+		rwsem_wake(sem);
+
+	/* wait to be given the lock */
+	for (;;) {
+		if (!test_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags))
+			break;
+		schedule();
+		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+	}
+
+	remove_wait_queue(&sem->wait,&wait);
+	tsk->state = TASK_RUNNING;
+
+	rwsemdebug("[%d] Leaving rwsem_down_write_failed(%08lx)\n",current->pid,sem->count);
+
+	return sem;
+}
+
+/*
+ * handle the lock being released whilst there are processes blocked on it that can now run
+ * - if we come here, then:
+ *   - the 'active part' of the count (&0x0000ffff) reached zero (but may no longer be zero)
+ *   - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so)
+ */
+struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
+{
+	signed long count;
+	int woken;
+
+	rwsemdebug("[%d] Entering rwsem_wake(%08lx)\n",current->pid,sem->count);
+
+ try_again:
+	/* try to grab an 'activity' marker
+	 * - need to make sure two copies of rwsem_wake() don't do this for two separate processes
+	 *   simultaneously
+	 * - be horribly naughty, and only deal with the LSW of the atomic counter
+	 */
+	if (rwsem_cmpxchgw(sem,0,RWSEM_ACTIVE_BIAS)!=0) {
+		rwsemdebug("[%d] rwsem_wake: abort wakeup due to renewed activity\n",current->pid);
+		goto out;
+	}
+
+	/* try to grant a single write lock if there's a writer at the front of the queue
+	 * - note we leave the 'active part' of the count incremented by 1 and the waiting part
+	 *   incremented by 0x00010000
+	 */
+	if (wake_up_ctx(&sem->wait,1,-RWSEM_WAITING_FOR_WRITE)==1)
+		goto out;
+
+	/* grant an infinite number of read locks to the readers at the front of the queue
+	 * - note we increment the 'active part' of the count by the number of readers just woken,
+	 *   less one for the activity decrement we've already done
+	 */
+	woken = wake_up_ctx(&sem->wait,65535,-RWSEM_WAITING_FOR_READ);
+	if (woken<=0)
+		goto counter_correction;
+
+	woken *= RWSEM_ACTIVE_BIAS-RWSEM_WAITING_BIAS;
+	woken -= RWSEM_ACTIVE_BIAS;
+	rwsem_atomic_update(woken,sem);
+
+ out:
+	rwsemdebug("[%d] Leaving rwsem_wake(%08lx)\n",current->pid,sem->count);
+	return sem;
+
+	/* come here if we need to correct the counter for odd SMP-isms */
+ counter_correction:
+	count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
+	rwsemdebug("[%d] corrected(%08lx)\n",current->pid,count);
+	if (!(count & RWSEM_ACTIVE_MASK))
+		goto try_again;
+	goto out;
+}
+
+EXPORT_SYMBOL(rwsem_down_read_failed);
+EXPORT_SYMBOL(rwsem_down_write_failed);
+EXPORT_SYMBOL(rwsem_wake);
diff -urN linux-2.4.4-pre2/mm/filemap.c linux/mm/filemap.c
--- linux-2.4.4-pre2/mm/filemap.c	Mon Dec  3 22:34:42 2001
+++ linux/mm/filemap.c	Mon Dec  3 22:34:51 2001
@@ -1061,7 +1061,7 @@
 
 	for (;;) {
 		struct page *page, **hash;
-		unsigned long end_index, nr;
+		unsigned long end_index, nr, ret;
 
 		end_index = inode->i_size >> PAGE_CACHE_SHIFT;
 		if (index > end_index)
@@ -1109,13 +1109,13 @@
 		 * "pos" here (the actor routine has to update the user buffer
 		 * pointers and the remaining count).
 		 */
-		nr = actor(desc, page, offset, nr);
-		offset += nr;
+		ret = actor(desc, page, offset, nr);
+		offset += ret;
 		index += offset >> PAGE_CACHE_SHIFT;
 		offset &= ~PAGE_CACHE_MASK;
 	
 		page_cache_release(page);
-		if (nr && desc->count)
+		if (ret == nr && desc->count)
 			continue;
 		break;
 
@@ -1208,7 +1208,7 @@
 	UPDATE_ATIME(inode);
 }
 
-static int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
+int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
 {
 	char *kaddr;
 	unsigned long left, count = desc->count;
@@ -1261,21 +1261,29 @@
 
 static int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset , unsigned long size)
 {
-	char *kaddr;
 	ssize_t written;
 	unsigned long count = desc->count;
 	struct file *file = (struct file *) desc->buf;
-	mm_segment_t old_fs;
 
 	if (size > count)
 		size = count;
-	old_fs = get_fs();
-	set_fs(KERNEL_DS);
 
-	kaddr = kmap(page);
-	written = file->f_op->write(file, kaddr + offset, size, &file->f_pos);
-	kunmap(page);
-	set_fs(old_fs);
+ 	if (file->f_op->writepage) {
+ 		written = file->f_op->writepage(file, page, offset,
+						size, &file->f_pos, size<count);
+	} else {
+		char *kaddr;
+		mm_segment_t old_fs;
+
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		kaddr = kmap(page);
+		written = file->f_op->write(file, kaddr + offset, size, &file->f_pos);
+		kunmap(page);
+
+		set_fs(old_fs);
+	}
 	if (written < 0) {
 		desc->error = written;
 		written = 0;
@@ -2407,7 +2415,7 @@
 	return page;
 }
 
-static inline void remove_suid(struct inode *inode)
+inline void remove_suid(struct inode *inode)
 {
 	unsigned int mode;
 
diff -urN linux-2.4.4-pre2/mm/mmap.c linux/mm/mmap.c
--- linux-2.4.4-pre2/mm/mmap.c	Mon Dec  3 22:34:42 2001
+++ linux/mm/mmap.c	Mon Dec  3 22:34:51 2001
@@ -233,14 +233,9 @@
 	/* Obtain the address to map to. we verify (or select) it and ensure
 	 * that it represents a valid section of the address space.
 	 */
-	if (flags & MAP_FIXED) {
-		if (addr & ~PAGE_MASK)
-			return -EINVAL;
-	} else {
-		addr = get_unmapped_area(addr, len);
-		if (!addr)
-			return -ENOMEM;
-	}
+	addr = get_unmapped_area(file, addr, len, pgoff, flags);
+	if (addr & ~PAGE_MASK)
+		return addr;
 
 	/* Do simple checking here so the lower-level routines won't have
 	 * to. we assume access permissions have been handled by the open
@@ -393,30 +388,51 @@
 }
 
 /* Get an address range which is currently unmapped.
- * For mmap() without MAP_FIXED and shmat() with addr=0.
- * Return value 0 means ENOMEM.
+ * For shmat() with addr=0.
+ *
+ * Ugly calling convention alert:
+ * Return value with the low bits set means error value,
+ * ie
+ *	if (ret & ~PAGE_MASK)
+ *		error = ret;
+ *
+ * This function "knows" that -ENOMEM has the bits set.
  */
 #ifndef HAVE_ARCH_UNMAPPED_AREA
-unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
+static inline unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
-	struct vm_area_struct * vmm;
+	struct vm_area_struct *vma;
 
 	if (len > TASK_SIZE)
-		return 0;
+		return -ENOMEM;
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 	addr = PAGE_ALIGN(addr);
 
-	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
-		/* At this point:  (!vmm || addr < vmm->vm_end). */
+	for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
+		/* At this point:  (!vma || addr < vma->vm_end). */
 		if (TASK_SIZE - len < addr)
-			return 0;
-		if (!vmm || addr + len <= vmm->vm_start)
+			return -ENOMEM;
+		if (!vma || addr + len <= vma->vm_start)
 			return addr;
-		addr = vmm->vm_end;
+		addr = vma->vm_end;
 	}
 }
-#endif
+#endif	
+
+unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	if (flags & MAP_FIXED) {
+		if (addr & ~PAGE_MASK)
+			return -EINVAL;
+		return addr;
+	}
+
+	if (file && file->f_op && file->f_op->get_unmapped_area)
+		return file->f_op->get_unmapped_area(file, addr, len, pgoff, flags);
+
+	return arch_get_unmapped_area(file, addr, len, pgoff, flags);
+}
 
 #define vm_avl_empty	(struct vm_area_struct *) NULL
 
diff -urN linux-2.4.4-pre2/mm/mremap.c linux/mm/mremap.c
--- linux-2.4.4-pre2/mm/mremap.c	Mon Mar 19 17:17:43 2001
+++ linux/mm/mremap.c	Mon Dec  3 22:34:51 2001
@@ -276,8 +276,13 @@
 	ret = -ENOMEM;
 	if (flags & MREMAP_MAYMOVE) {
 		if (!(flags & MREMAP_FIXED)) {
-			new_addr = get_unmapped_area(0, new_len);
-			if (!new_addr)
+			unsigned long map_flags = 0;
+			if (vma->vm_flags & VM_SHARED)
+				map_flags |= MAP_SHARED;
+
+			new_addr = get_unmapped_area(vma->vm_file, 0, new_len, vma->vm_pgoff, map_flags);
+			ret = new_addr;
+			if (new_addr & ~PAGE_MASK)
 				goto out;
 		}
 		ret = move_vma(vma, addr, old_len, new_len, new_addr);
diff -urN linux-2.4.4-pre2/mm/shmem.c linux/mm/shmem.c
--- linux-2.4.4-pre2/mm/shmem.c	Fri Mar  2 15:16:59 2001
+++ linux/mm/shmem.c	Mon Dec  3 22:34:51 2001
@@ -1,5 +1,5 @@
 /*
- * Resizable simple shmem filesystem for Linux.
+ * Resizable virtual memory filesystem for Linux.
  *
  * Copyright (C) 2000 Linus Torvalds.
  *		 2000 Transmeta Corp.
@@ -9,14 +9,12 @@
  */
 
 /*
- * This shared memory handling is heavily based on the ramfs. It
- * extends the ramfs by the ability to use swap which would makes it a
- * completely usable filesystem.
- *
- * But read and write are not supported (yet)
- *
+ * This virtual memory filesystem is heavily based on the ramfs. It
+ * extends ramfs by the ability to use swap and honor resource limits
+ * which makes it a completely usable filesystem.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
@@ -31,7 +29,8 @@
 
 #include <asm/uaccess.h>
 
-#define SHMEM_MAGIC	0x01021994
+/* This magic number is used in glibc for posix shared memory */
+#define TMPFS_MAGIC	0x01021994
 
 #define ENTRIES_PER_PAGE (PAGE_SIZE/sizeof(unsigned long))
 #define NR_SINGLE (ENTRIES_PER_PAGE + SHMEM_NR_DIRECT)
@@ -42,33 +41,74 @@
 static struct inode_operations shmem_inode_operations;
 static struct file_operations shmem_dir_operations;
 static struct inode_operations shmem_dir_inode_operations;
-static struct vm_operations_struct shmem_shared_vm_ops;
-static struct vm_operations_struct shmem_private_vm_ops;
+static struct inode_operations shmem_symlink_inode_operations;
+static struct vm_operations_struct shmem_vm_ops;
 
 LIST_HEAD (shmem_inodes);
 static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED;
 
+#define BLOCKS_PER_PAGE (PAGE_SIZE/512)
+
+/*
+ * shmem_recalc_inode - recalculate the size of an inode
+ *
+ * @inode: inode to recalc
+ *
+ * We have to calculate the free blocks since the mm can drop pages
+ * behind our back
+ *
+ * But we know that normally
+ * inodes->i_blocks/BLOCKS_PER_PAGE == 
+ * 			inode->i_mapping->nrpages + info->swapped
+ *
+ * So the mm freed 
+ * inodes->i_blocks/BLOCKS_PER_PAGE - 
+ *			(inode->i_mapping->nrpages + info->swapped)
+ *
+ * It has to be called with the spinlock held.
+ */
+
+static void shmem_recalc_inode(struct inode * inode)
+{
+	unsigned long freed;
+
+	freed = (inode->i_blocks/BLOCKS_PER_PAGE) -
+		(inode->i_mapping->nrpages + inode->u.shmem_i.swapped);
+	if (freed){
+		struct shmem_sb_info * info = &inode->i_sb->u.shmem_sb;
+		inode->i_blocks -= freed*BLOCKS_PER_PAGE;
+		spin_lock (&info->stat_lock);
+		info->free_blocks += freed;
+		spin_unlock (&info->stat_lock);
+	}
+}
+
 static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long index) 
 {
+	unsigned long offset;
+
 	if (index < SHMEM_NR_DIRECT)
 		return info->i_direct+index;
 
 	index -= SHMEM_NR_DIRECT;
-	if (index >= ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
-		return NULL;
+	offset = index % ENTRIES_PER_PAGE;
+	index /= ENTRIES_PER_PAGE;
+
+	if (index >= ENTRIES_PER_PAGE)
+		return ERR_PTR(-EFBIG);
 
 	if (!info->i_indirect) {
 		info->i_indirect = (swp_entry_t **) get_zeroed_page(GFP_USER);
 		if (!info->i_indirect)
-			return NULL;
+			return ERR_PTR(-ENOMEM);
 	}
-	if(!(info->i_indirect[index/ENTRIES_PER_PAGE])) {
-		info->i_indirect[index/ENTRIES_PER_PAGE] = (swp_entry_t *) get_zeroed_page(GFP_USER);
-		if (!info->i_indirect[index/ENTRIES_PER_PAGE])
-			return NULL;
+	if(!(info->i_indirect[index])) {
+		info->i_indirect[index] = (swp_entry_t *) get_zeroed_page(GFP_USER);
+		if (!info->i_indirect[index])
+			return ERR_PTR(-ENOMEM);
 	}
 	
-	return info->i_indirect[index/ENTRIES_PER_PAGE]+index%ENTRIES_PER_PAGE;
+	return info->i_indirect[index]+offset;
 }
 
 static int shmem_free_swp(swp_entry_t *dir, unsigned int count)
@@ -81,13 +121,13 @@
 		if (!ptr->val)
 			continue;
 		entry = *ptr;
-		swap_free (entry);
 		*ptr = (swp_entry_t){0};
 		freed++;
-		if (!(page = lookup_swap_cache(entry)))
-			continue;
-		delete_from_swap_cache(page);
-		page_cache_release(page);
+		if ((page = lookup_swap_cache(entry)) != NULL) {
+			delete_from_swap_cache(page);
+			page_cache_release(page);	
+		}
+		swap_free (entry);
 	}
 	return freed;
 }
@@ -98,7 +138,6 @@
  * @dir:	pointer to swp_entries 
  * @size:	number of entries in dir
  * @start:	offset to start from
- * @inode:	inode for statistics
  * @freed:	counter for freed pages
  *
  * It frees the swap entries from dir+start til dir+size
@@ -108,7 +147,7 @@
 
 static unsigned long 
 shmem_truncate_part (swp_entry_t * dir, unsigned long size, 
-		     unsigned long start, struct inode * inode, unsigned long *freed) {
+		     unsigned long start, unsigned long *freed) {
 	if (start > size)
 		return start - size;
 	if (dir)
@@ -117,56 +156,28 @@
 	return 0;
 }
 
-/*
- * shmem_recalc_inode - recalculate the size of an inode
- *
- * @inode: inode to recalc
- *
- * We have to calculate the free blocks since the mm can drop pages
- * behind our back
- *
- * But we know that normally
- * inodes->i_blocks == inode->i_mapping->nrpages + info->swapped
- *
- * So the mm freed 
- * inodes->i_blocks - (inode->i_mapping->nrpages + info->swapped)
- *
- * It has to be called with the spinlock held.
- */
-
-static void shmem_recalc_inode(struct inode * inode)
-{
-	unsigned long freed;
-
-	freed = inode->i_blocks -
-		(inode->i_mapping->nrpages + inode->u.shmem_i.swapped);
-	if (freed){
-		struct shmem_sb_info * info = &inode->i_sb->u.shmem_sb;
-		inode->i_blocks -= freed;
-		spin_lock (&info->stat_lock);
-		info->free_blocks += freed;
-		spin_unlock (&info->stat_lock);
-	}
-}
-
 static void shmem_truncate (struct inode * inode)
 {
 	int clear_base;
-	unsigned long start;
+	unsigned long index, start;
 	unsigned long freed = 0;
-	swp_entry_t **base, **ptr;
+	swp_entry_t **base, **ptr, **last;
 	struct shmem_inode_info * info = &inode->u.shmem_i;
 
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	spin_lock (&info->lock);
-	start = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (index > info->max_index)
+		goto out;
 
-	start = shmem_truncate_part (info->i_direct, SHMEM_NR_DIRECT, start, inode, &freed);
+	start = shmem_truncate_part (info->i_direct, SHMEM_NR_DIRECT, index, &freed);
 
 	if (!(base = info->i_indirect))
-		goto out;;
+		goto out;
 
 	clear_base = 1;
-	for (ptr = base; ptr < base + ENTRIES_PER_PAGE; ptr++) {
+	last = base + ((info->max_index - SHMEM_NR_DIRECT + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE);
+	for (ptr = base; ptr < last; ptr++) {
 		if (!start) {
 			if (!*ptr)
 				continue;
@@ -176,16 +187,16 @@
 			continue;
 		}
 		clear_base = 0;
-		start = shmem_truncate_part (*ptr, ENTRIES_PER_PAGE, start, inode, &freed);
+		start = shmem_truncate_part (*ptr, ENTRIES_PER_PAGE, start, &freed);
 	}
 
-	if (!clear_base) 
-		goto out;
-
-	free_page ((unsigned long)base);
-	info->i_indirect = 0;
+	if (clear_base) {
+		free_page ((unsigned long)base);
+		info->i_indirect = 0;
+	}
 
 out:
+	info->max_index = index;
 	info->swapped -= freed;
 	shmem_recalc_inode(inode);
 	spin_unlock (&info->lock);
@@ -207,15 +218,29 @@
 }
 
 /*
- * Move the page from the page cache to the swap cache
+ * Move the page from the page cache to the swap cache.
+ *
+ * The page lock prevents multiple occurences of shmem_writepage at
+ * once.  We still need to guard against racing with
+ * shmem_getpage_locked().  
  */
 static int shmem_writepage(struct page * page)
 {
-	int error;
+	int error = 0;
 	struct shmem_inode_info *info;
 	swp_entry_t *entry, swap;
+	struct inode *inode;
 
-	info = &page->mapping->host->u.shmem_i;
+	if (!PageLocked(page))
+		BUG();
+	
+	/* Only move to the swap cache if there are no other users of
+	 * the page. */
+	if (atomic_read(&page->count) > 2)
+		goto out;
+	
+	inode = page->mapping->host;
+	info = &inode->u.shmem_i;
 	swap = __get_swap_page(2);
 	if (!swap.val) {
 		set_page_dirty(page);
@@ -224,17 +249,15 @@
 	}
 
 	spin_lock(&info->lock);
-	shmem_recalc_inode(page->mapping->host);
-	entry = shmem_swp_entry (info, page->index);
-	if (!entry)	/* this had been allocted on page allocation */
+	entry = shmem_swp_entry(info, page->index);
+	if (IS_ERR(entry))	/* this had been allocted on page allocation */
 		BUG();
+	shmem_recalc_inode(page->mapping->host);
 	error = -EAGAIN;
-	if (entry->val) {
-                __swap_free(swap, 2);
-		goto out;
-        }
+	if (entry->val)
+		BUG();
 
-        *entry = swap;
+	*entry = swap;
 	error = 0;
 	/* Remove the from the page cache */
 	lru_cache_del(page);
@@ -245,97 +268,185 @@
 	page_cache_release(page);
 	set_page_dirty(page);
 	info->swapped++;
-out:
+
 	spin_unlock(&info->lock);
+out:
 	UnlockPage(page);
 	return error;
 }
 
 /*
- * shmem_nopage - either get the page from swap or allocate a new one
+ * shmem_getpage_locked - either get the page from swap or allocate a new one
  *
  * If we allocate a new one we do not mark it dirty. That's up to the
  * vm. If we swap it in we mark it dirty since we also free the swap
  * entry since a page cannot live in both the swap and page cache
+ *
+ * Called with the inode locked, so it cannot race with itself, but we
+ * still need to guard against racing with shm_writepage(), which might
+ * be trying to move the page to the swap cache as we run.
  */
-struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share)
+static struct page * shmem_getpage_locked(struct inode * inode, unsigned long idx)
 {
-	unsigned long size;
-	struct page * page;
-	unsigned int idx;
-	swp_entry_t *entry;
-	struct inode * inode = vma->vm_file->f_dentry->d_inode;
 	struct address_space * mapping = inode->i_mapping;
 	struct shmem_inode_info *info;
+	struct page * page;
+	swp_entry_t *entry;
 
-	idx = (address - vma->vm_start) >> PAGE_SHIFT;
-	idx += vma->vm_pgoff;
-
-	down (&inode->i_sem);
-	size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-	page = NOPAGE_SIGBUS;
-	if ((idx >= size) && (vma->vm_mm == current->mm))
-		goto out;
+	info = &inode->u.shmem_i;
 
-	/* retry, we may have slept */
-	page = __find_lock_page(mapping, idx, page_hash (mapping, idx));
+repeat:
+	page = find_lock_page(mapping, idx);
 	if (page)
-		goto cached_page;
+		return page;
 
-	info = &inode->u.shmem_i;
 	entry = shmem_swp_entry (info, idx);
-	if (!entry)
-		goto oom;
+	if (IS_ERR(entry))
+		return (void *)entry;
+
 	spin_lock (&info->lock);
-	shmem_recalc_inode(inode);
-	spin_unlock (&info->lock);
+	
+	/* The shmem_swp_entry() call may have blocked, and
+	 * shmem_writepage may have been moving a page between the page
+	 * cache and swap cache.  We need to recheck the page cache
+	 * under the protection of the info->lock spinlock. */
+
+	page = __find_get_page(mapping, idx, page_hash(mapping, idx));
+	if (page) {
+		if (TryLockPage(page))
+			goto wait_retry;
+		spin_unlock (&info->lock);
+		return page;
+	}
+	
 	if (entry->val) {
 		unsigned long flags;
 
 		/* Look it up and read it in.. */
-		page = lookup_swap_cache(*entry);
+		page = __find_get_page(&swapper_space, entry->val,
+				       page_hash(&swapper_space, entry->val));
 		if (!page) {
+			spin_unlock (&info->lock);
 			lock_kernel();
 			swapin_readahead(*entry);
 			page = read_swap_cache(*entry);
 			unlock_kernel();
 			if (!page) 
-				goto oom;
+				return ERR_PTR(-ENOMEM);
+			if (!Page_Uptodate(page)) {
+				page_cache_release(page);
+				return ERR_PTR(-EIO);
+			}
+			
+			/* Too bad we can't trust this page, because we
+			 * dropped the info->lock spinlock */
+			page_cache_release(page);
+			goto repeat;
 		}
 
 		/* We have to this with page locked to prevent races */
-		spin_lock (&info->lock);
+		if (TryLockPage(page)) 
+			goto wait_retry;
+
+		if (swap_count(page) > 2)
+			BUG();
+		
 		swap_free(*entry);
-		lock_page(page);
-		delete_from_swap_cache_nolock(page);
 		*entry = (swp_entry_t) {0};
+		delete_from_swap_cache_nolock(page);
 		flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced) | (1 << PG_arch_1));
 		page->flags = flags | (1 << PG_dirty);
 		add_to_page_cache_locked(page, mapping, idx);
 		info->swapped--;
 		spin_unlock (&info->lock);
 	} else {
+		spin_unlock (&info->lock);
 		spin_lock (&inode->i_sb->u.shmem_sb.stat_lock);
 		if (inode->i_sb->u.shmem_sb.free_blocks == 0)
 			goto no_space;
 		inode->i_sb->u.shmem_sb.free_blocks--;
 		spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
-		/* Ok, get a new page */
+
+		/* Ok, get a new page.  We don't have to worry about the
+		 * info->lock spinlock here: we cannot race against
+		 * shm_writepage because we have already verified that
+		 * there is no page present either in memory or in the
+		 * swap cache, so we are guaranteed to be populating a
+		 * new shm entry.  The inode semaphore we already hold
+		 * is enough to make this atomic. */
 		page = page_cache_alloc(mapping);
 		if (!page)
-			goto oom;
-		clear_user_highpage(page, address);
-		inode->i_blocks++;
+			return ERR_PTR(-ENOMEM);
+		clear_highpage(page);
+		inode->i_blocks += BLOCKS_PER_PAGE;
 		add_to_page_cache (page, mapping, idx);
 	}
+
 	/* We have the page */
-	SetPageUptodate (page);
+	SetPageUptodate(page);
 	if (info->locked)
 		page_cache_get(page);
+	return page;
+no_space:
+	spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
+	return ERR_PTR(-ENOSPC);
 
-cached_page:
-	UnlockPage (page);
-	up(&inode->i_sem);
+wait_retry:
+	spin_unlock (&info->lock);
+	wait_on_page(page);
+	page_cache_release(page);
+	goto repeat;
+}
+
+static int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr)
+{
+	struct address_space * mapping = inode->i_mapping;
+	int error;
+
+	*ptr = NOPAGE_SIGBUS;
+	if (inode->i_size <= (loff_t) idx * PAGE_CACHE_SIZE)
+		return -EFAULT;
+
+	*ptr = __find_get_page(mapping, idx, page_hash(mapping, idx));
+	if (*ptr) {
+		if (Page_Uptodate(*ptr))
+			return 0;
+		page_cache_release(*ptr);
+	}
+
+	down (&inode->i_sem);
+	/* retest we may have slept */
+	if (inode->i_size < (loff_t) idx * PAGE_CACHE_SIZE)
+		goto sigbus;
+	*ptr = shmem_getpage_locked(inode, idx);
+	if (IS_ERR (*ptr))
+		goto failed;
+	UnlockPage(*ptr);
+	up (&inode->i_sem);
+	return 0;
+failed:
+	up (&inode->i_sem);
+	error = PTR_ERR(*ptr);
+	*ptr = NOPAGE_OOM;
+	if (error != -EFBIG)
+		*ptr = NOPAGE_SIGBUS;
+	return error;
+sigbus:
+	*ptr = NOPAGE_SIGBUS;
+	return -EFAULT;
+}
+
+struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share)
+{
+	struct page * page;
+	unsigned int idx;
+	struct inode * inode = vma->vm_file->f_dentry->d_inode;
+
+	idx = (address - vma->vm_start) >> PAGE_SHIFT;
+	idx += vma->vm_pgoff;
+
+	if (shmem_getpage(inode, idx, &page))
+		return page;
 
 	if (no_share) {
 		struct page *new_page = page_cache_alloc(inode->i_mapping);
@@ -351,13 +462,45 @@
 
 	flush_page_to_ram (page);
 	return(page);
-no_space:
-	spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
-oom:
-	page = NOPAGE_OOM;
-out:
+}
+
+void shmem_lock(struct file * file, int lock)
+{
+	struct inode * inode = file->f_dentry->d_inode;
+	struct shmem_inode_info * info = &inode->u.shmem_i;
+	struct page * page;
+	unsigned long idx, size;
+
+	if (info->locked == lock)
+		return;
+	down(&inode->i_sem);
+	info->locked = lock;
+	size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	for (idx = 0; idx < size; idx++) {
+		page = find_lock_page(inode->i_mapping, idx);
+		if (!page)
+			continue;
+		if (!lock) {
+			/* release the extra count and our reference */
+			page_cache_release(page);
+			page_cache_release(page);
+		}
+		UnlockPage(page);
+	}
 	up(&inode->i_sem);
-	return page;
+}
+
+static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	struct vm_operations_struct * ops;
+	struct inode *inode = file->f_dentry->d_inode;
+
+	ops = &shmem_vm_ops;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	UPDATE_ATIME(inode);
+	vma->vm_ops = ops;
+	return 0;
 }
 
 struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev)
@@ -392,11 +535,13 @@
 			inode->i_fop = &shmem_file_operations;
 			break;
 		case S_IFDIR:
+			inode->i_nlink++;
 			inode->i_op = &shmem_dir_inode_operations;
 			inode->i_fop = &shmem_dir_operations;
 			break;
 		case S_IFLNK:
-			BUG();
+			inode->i_op = &shmem_symlink_inode_operations;
+			break;
 		}
 		spin_lock (&shmem_ilock);
 		list_add (&inode->u.shmem_i.list, &shmem_inodes);
@@ -405,49 +550,242 @@
 	return inode;
 }
 
+#ifdef CONFIG_TMPFS
+static ssize_t
+shmem_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos)
+{
+	struct inode	*inode = file->f_dentry->d_inode; 
+	unsigned long	limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+	loff_t		pos;
+	struct page	*page;
+	unsigned long	written;
+	long		status;
+	int		err;
+
+
+	down(&inode->i_sem);
+
+	pos = *ppos;
+	err = -EINVAL;
+	if (pos < 0)
+		goto out;
+
+	err = file->f_error;
+	if (err) {
+		file->f_error = 0;
+		goto out;
+	}
+
+	written = 0;
+
+	if (file->f_flags & O_APPEND)
+		pos = inode->i_size;
+
+	/*
+	 * Check whether we've reached the file size limit.
+	 */
+	err = -EFBIG;
+	if (limit != RLIM_INFINITY) {
+		if (pos >= limit) {
+			send_sig(SIGXFSZ, current, 0);
+			goto out;
+		}
+		if (count > limit - pos) {
+			send_sig(SIGXFSZ, current, 0);
+			count = limit - pos;
+		}
+	}
+
+	status	= 0;
+	if (count) {
+		remove_suid(inode);
+		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	}
+
+	while (count) {
+		unsigned long bytes, index, offset;
+		char *kaddr;
+		int deactivate = 1;
+
+		/*
+		 * Try to find the page in the cache. If it isn't there,
+		 * allocate a free page.
+		 */
+		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+		index = pos >> PAGE_CACHE_SHIFT;
+		bytes = PAGE_CACHE_SIZE - offset;
+		if (bytes > count) {
+			bytes = count;
+			deactivate = 0;
+		}
+
+		/*
+		 * Bring in the user page that we will copy from _first_.
+		 * Otherwise there's a nasty deadlock on copying from the
+		 * same page as we're writing to, without it being marked
+		 * up-to-date.
+		 */
+		{ volatile unsigned char dummy;
+			__get_user(dummy, buf);
+			__get_user(dummy, buf+bytes-1);
+		}
+
+		page = shmem_getpage_locked(inode, index);
+		status = PTR_ERR(page);
+		if (IS_ERR(page))
+			break;
+
+		/* We have exclusive IO access to the page.. */
+		if (!PageLocked(page)) {
+			PAGE_BUG(page);
+		}
+
+		kaddr = kmap(page);
+// can this do a truncated write? cr
+		status = copy_from_user(kaddr+offset, buf, bytes);
+		kunmap(page);
+		if (status)
+			goto fail_write;
+
+		flush_dcache_page(page);
+		if (bytes > 0) {
+			SetPageDirty(page);
+			written += bytes;
+			count -= bytes;
+			pos += bytes;
+			buf += bytes;
+			if (pos > inode->i_size) 
+				inode->i_size = pos;
+			if (inode->u.shmem_i.max_index <= index)
+				inode->u.shmem_i.max_index = index+1;
+
+		}
+unlock:
+		/* Mark it unlocked again and drop the page.. */
+		UnlockPage(page);
+		if (deactivate)
+			deactivate_page(page);
+		page_cache_release(page);
+
+		if (status < 0)
+			break;
+	}
+	*ppos = pos;
+
+	err = written ? written : status;
+out:
+	up(&inode->i_sem);
+	return err;
+fail_write:
+	status = -EFAULT;
+	ClearPageUptodate(page);
+	kunmap(page);
+	goto unlock;
+}
+
+static void do_shmem_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct address_space *mapping = inode->i_mapping;
+	unsigned long index, offset;
+	int nr = 1;
+
+	index = *ppos >> PAGE_CACHE_SHIFT;
+	offset = *ppos & ~PAGE_CACHE_MASK;
+
+	while (nr && desc->count) {
+		struct page *page;
+		unsigned long end_index, nr;
+
+		end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+		if (index > end_index)
+			break;
+		nr = PAGE_CACHE_SIZE;
+		if (index == end_index) {
+			nr = inode->i_size & ~PAGE_CACHE_MASK;
+			if (nr <= offset)
+				break;
+		}
+
+		nr = nr - offset;
+
+		if ((desc->error = shmem_getpage(inode, index, &page)))
+			break;
+
+		if (mapping->i_mmap_shared != NULL)
+			flush_dcache_page(page);
+
+		/*
+		 * Ok, we have the page, and it's up-to-date, so
+		 * now we can copy it to user space...
+		 *
+		 * The actor routine returns how many bytes were actually used..
+		 * NOTE! This may not be the same as how much of a user buffer
+		 * we filled up (we may be padding etc), so we can only update
+		 * "pos" here (the actor routine has to update the user buffer
+		 * pointers and the remaining count).
+		 */
+		nr = file_read_actor(desc, page, offset, nr);
+		offset += nr;
+		index += offset >> PAGE_CACHE_SHIFT;
+		offset &= ~PAGE_CACHE_MASK;
+	
+		page_cache_release(page);
+	}
+
+	*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
+	UPDATE_ATIME(inode);
+}
+
+static ssize_t shmem_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+{
+	ssize_t retval;
+
+	retval = -EFAULT;
+	if (access_ok(VERIFY_WRITE, buf, count)) {
+		retval = 0;
+
+		if (count) {
+			read_descriptor_t desc;
+
+			desc.written = 0;
+			desc.count = count;
+			desc.buf = buf;
+			desc.error = 0;
+			do_shmem_file_read(filp, ppos, &desc);
+
+			retval = desc.written;
+			if (!retval)
+				retval = desc.error;
+		}
+	}
+	return retval;
+}
+
 static int shmem_statfs(struct super_block *sb, struct statfs *buf)
 {
-	buf->f_type = SHMEM_MAGIC;
+	buf->f_type = TMPFS_MAGIC;
 	buf->f_bsize = PAGE_CACHE_SIZE;
 	spin_lock (&sb->u.shmem_sb.stat_lock);
-	if (sb->u.shmem_sb.max_blocks != ULONG_MAX || 
-	    sb->u.shmem_sb.max_inodes != ULONG_MAX) {
+	if (sb->u.shmem_sb.max_blocks == ULONG_MAX) {
+		/*
+		 * This is only a guestimate and not honoured.
+		 * We need it to make some programs happy which like to
+		 * test the free space of a file system.
+		 */
+		buf->f_bavail = buf->f_bfree = nr_free_pages() + nr_swap_pages + atomic_read(&buffermem_pages);
+		buf->f_blocks = buf->f_bfree + ULONG_MAX - sb->u.shmem_sb.free_blocks;
+	} else {
 		buf->f_blocks = sb->u.shmem_sb.max_blocks;
 		buf->f_bavail = buf->f_bfree = sb->u.shmem_sb.free_blocks;
-		buf->f_files = sb->u.shmem_sb.max_inodes;
-		buf->f_ffree = sb->u.shmem_sb.free_inodes;
 	}
+	buf->f_files = sb->u.shmem_sb.max_inodes;
+	buf->f_ffree = sb->u.shmem_sb.free_inodes;
 	spin_unlock (&sb->u.shmem_sb.stat_lock);
 	buf->f_namelen = 255;
 	return 0;
 }
 
-void shmem_lock(struct file * file, int lock)
-{
-	struct inode * inode = file->f_dentry->d_inode;
-	struct shmem_inode_info * info = &inode->u.shmem_i;
-	struct page * page;
-	unsigned long idx, size;
-
-	if (info->locked == lock)
-		return;
-	down(&inode->i_sem);
-	info->locked = lock;
-	size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-	for (idx = 0; idx < size; idx++) {
-		page = find_lock_page(inode->i_mapping, idx);
-		if (!page)
-			continue;
-		if (!lock) {
-			/* release the extra count and our reference */
-			page_cache_release(page);
-			page_cache_release(page);
-		}
-		UnlockPage(page);
-	}
-	up(&inode->i_sem);
-}
-
 /*
  * Lookup the data. This is trivial - if the dentry didn't already
  * exist, we know it is negative.
@@ -466,6 +804,7 @@
 	struct inode * inode = shmem_get_inode(dir->i_sb, mode, dev);
 	int error = -ENOSPC;
 
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	if (inode) {
 		d_instantiate(dentry, inode);
 		dget(dentry); /* Extra count - pin the dentry in core */
@@ -476,7 +815,12 @@
 
 static int shmem_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 {
-	return shmem_mknod(dir, dentry, mode | S_IFDIR, 0);
+	int error;
+
+	if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0)))
+		return error;
+	dir->i_nlink++;
+	return 0;
 }
 
 static int shmem_create(struct inode *dir, struct dentry *dentry, int mode)
@@ -494,6 +838,7 @@
 	if (S_ISDIR(inode->i_mode))
 		return -EPERM;
 
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	inode->i_nlink++;
 	atomic_inc(&inode->i_count);	/* New dentry reference */
 	dget(dentry);		/* Extra pinning count for the created dentry */
@@ -534,26 +879,24 @@
 	return 1;
 }
 
-/*
- * This works for both directories and regular files.
- * (non-directories will always have empty subdirs)
- */
 static int shmem_unlink(struct inode * dir, struct dentry *dentry)
 {
-	int retval = -ENOTEMPTY;
+	struct inode *inode = dentry->d_inode;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	inode->i_nlink--;
+	dput(dentry);	/* Undo the count from "create" - this does all the work */
+	return 0;
+}
 
-	if (shmem_empty(dentry)) {
-		struct inode *inode = dentry->d_inode;
+static int shmem_rmdir(struct inode * dir, struct dentry *dentry)
+{
+	if (!shmem_empty(dentry))
+		return -ENOTEMPTY;
 
-		inode->i_nlink--;
-		dput(dentry);	/* Undo the count from "create" - this does all the work */
-		retval = 0;
-	}
-	return retval;
+	dir->i_nlink--;
+	return shmem_unlink(dir, dentry);
 }
 
-#define shmem_rmdir shmem_unlink
-
 /*
  * The VFS layer already does all the dentry stuff for rename,
  * we just have to decrement the usage count for the target if
@@ -567,27 +910,77 @@
 	if (shmem_empty(new_dentry)) {
 		struct inode *inode = new_dentry->d_inode;
 		if (inode) {
+			inode->i_ctime = CURRENT_TIME;
 			inode->i_nlink--;
 			dput(new_dentry);
 		}
 		error = 0;
+		old_dentry->d_inode->i_ctime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
 	}
 	return error;
 }
 
-static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
+static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
 {
-	struct vm_operations_struct * ops;
-	struct inode *inode = file->f_dentry->d_inode;
+	int error;
+	int len;
+	struct inode *inode;
+	struct page *page;
+	char *kaddr;
 
-	ops = &shmem_private_vm_ops;
-	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
-		ops = &shmem_shared_vm_ops;
-	if (!inode->i_sb || !S_ISREG(inode->i_mode))
-		return -EACCES;
-	UPDATE_ATIME(inode);
-	vma->vm_ops = ops;
+	error = shmem_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
+	if (error)
+		return error;
+
+	len = strlen(symname);
+	if (len > PAGE_SIZE)
+		return -ENAMETOOLONG;
+		
+	inode = dentry->d_inode;
+	down(&inode->i_sem);
+	page = shmem_getpage_locked(inode, 0);
+	if (IS_ERR(page))
+		goto fail;
+	kaddr = kmap(page);
+	memcpy(kaddr, symname, len);
+	kunmap(page);
+	inode->i_size = len;
+	SetPageDirty(page);
+	UnlockPage(page);
+	page_cache_release(page);
+	up(&inode->i_sem);
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	return 0;
+fail:
+	up(&inode->i_sem);
+	return PTR_ERR(page);
+}
+
+static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	struct page * page;
+	int res = shmem_getpage(dentry->d_inode, 0, &page);
+
+	if (res)
+		return res;
+
+	res = vfs_readlink(dentry,buffer,buflen, kmap(page));
+	kunmap(page);
+	page_cache_release(page);
+	return res;
+}
+
+static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct page * page;
+	int res = shmem_getpage(dentry->d_inode, 0, &page);
+	if (res)
+		return res;
+
+	res = vfs_follow_link(nd, kmap(page));
+	kunmap(page);
+	page_cache_release(page);
+	return res;
 }
 
 static int shmem_parse_options(char *options, int *mode, unsigned long * blocks, unsigned long *inodes)
@@ -600,16 +993,24 @@
 	for ( ; this_char; this_char = strtok(NULL,",")) {
 		if ((value = strchr(this_char,'=')) != NULL)
 			*value++ = 0;
-		if (!strcmp(this_char,"nr_blocks")) {
+		if (!strcmp(this_char,"size")) {
+			unsigned long long size;
 			if (!value || !*value || !blocks)
 				return 1;
-			*blocks = simple_strtoul(value,&value,0);
+			size = memparse(value,&value);
+			if (*value)
+				return 1;
+			*blocks = size >> PAGE_CACHE_SHIFT;
+		} else if (!strcmp(this_char,"nr_blocks")) {
+			if (!value || !*value || !blocks)
+				return 1;
+			*blocks = memparse(value,&value);
 			if (*value)
 				return 1;
 		} else if (!strcmp(this_char,"nr_inodes")) {
 			if (!value || !*value || !inodes)
 				return 1;
-			*inodes = simple_strtoul(value,&value,0);
+			*inodes = memparse(value,&value);
 			if (*value)
 				return 1;
 		} else if (!strcmp(this_char,"mode")) {
@@ -622,9 +1023,42 @@
 		else
 			return 1;
 	}
+	return 0;
+}
+
+static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
+{
+	int error;
+	unsigned long max_blocks, blocks;
+	unsigned long max_inodes, inodes;
+	struct shmem_sb_info *info = &sb->u.shmem_sb;
+
+	if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes))
+		return -EINVAL;
+
+	spin_lock(&info->stat_lock);
+	blocks = info->max_blocks - info->free_blocks;
+	inodes = info->max_inodes - info->free_inodes;
+	error = -EINVAL;
+	if (max_blocks < blocks)
+		goto out;
+	if (max_inodes < inodes)
+		goto out;
+	error = 0;
+	info->max_blocks  = max_blocks;
+	info->free_blocks = max_blocks - blocks;
+	info->max_inodes  = max_inodes;
+	info->free_inodes = max_inodes - inodes;
+out:
+	spin_unlock(&info->stat_lock);
+	return error;
+}
 
+int shmem_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
 	return 0;
 }
+#endif
 
 static struct super_block *shmem_read_super(struct super_block * sb, void * data, int silent)
 {
@@ -634,19 +1068,22 @@
 	unsigned long inodes = ULONG_MAX;	/* unlimited */
 	int mode   = S_IRWXUGO | S_ISVTX;
 
+#ifdef CONFIG_TMPFS
 	if (shmem_parse_options (data, &mode, &blocks, &inodes)) {
-		printk(KERN_ERR "shmem fs invalid option\n");
+		printk(KERN_ERR "tmpfs invalid option\n");
 		return NULL;
 	}
+#endif
 
 	spin_lock_init (&sb->u.shmem_sb.stat_lock);
 	sb->u.shmem_sb.max_blocks = blocks;
 	sb->u.shmem_sb.free_blocks = blocks;
 	sb->u.shmem_sb.max_inodes = inodes;
 	sb->u.shmem_sb.free_inodes = inodes;
+	sb->s_maxbytes = (unsigned long long)(SHMEM_NR_DIRECT + (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)) << PAGE_CACHE_SHIFT;
 	sb->s_blocksize = PAGE_CACHE_SIZE;
 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
-	sb->s_magic = SHMEM_MAGIC;
+	sb->s_magic = TMPFS_MAGIC;
 	sb->s_op = &shmem_ops;
 	inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
 	if (!inode)
@@ -661,103 +1098,108 @@
 	return sb;
 }
 
-static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
-{
-	int error;
-	unsigned long max_blocks, blocks;
-	unsigned long max_inodes, inodes;
-	struct shmem_sb_info *info = &sb->u.shmem_sb;
 
-	if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes))
-		return -EINVAL;
-
-	spin_lock(&info->stat_lock);
-	blocks = info->max_blocks - info->free_blocks;
-	inodes = info->max_inodes - info->free_inodes;
-	error = -EINVAL;
-	if (max_blocks < blocks)
-		goto out;
-	if (max_inodes < inodes)
-		goto out;
-	error = 0;
-	info->max_blocks  = max_blocks;
-	info->free_blocks = max_blocks - blocks;
-	info->max_inodes  = max_inodes;
-	info->free_inodes = max_inodes - inodes;
-out:
-	spin_unlock(&info->stat_lock);
-	return error;
-}
 
 static struct address_space_operations shmem_aops = {
 	writepage: shmem_writepage
 };
 
 static struct file_operations shmem_file_operations = {
-	mmap:		shmem_mmap
+	mmap:	shmem_mmap,
+#ifdef CONFIG_TMPFS
+	read:	shmem_file_read,
+	write:	shmem_file_write,
+	fsync:	shmem_sync_file,
+#endif
 };
 
 static struct inode_operations shmem_inode_operations = {
 	truncate:	shmem_truncate,
 };
 
+static struct inode_operations shmem_symlink_inode_operations = {
+	truncate:	shmem_truncate,
+#ifdef CONFIG_TMPFS
+	readlink:	shmem_readlink,
+	follow_link:	shmem_follow_link,
+#endif
+};
+
 static struct file_operations shmem_dir_operations = {
 	read:		generic_read_dir,
 	readdir:	dcache_readdir,
+#ifdef CONFIG_TMPFS
+	fsync:		shmem_sync_file,
+#endif
 };
 
 static struct inode_operations shmem_dir_inode_operations = {
+#ifdef CONFIG_TMPFS
 	create:		shmem_create,
 	lookup:		shmem_lookup,
 	link:		shmem_link,
 	unlink:		shmem_unlink,
+	symlink:	shmem_symlink,
 	mkdir:		shmem_mkdir,
 	rmdir:		shmem_rmdir,
 	mknod:		shmem_mknod,
 	rename:		shmem_rename,
+#endif
 };
 
 static struct super_operations shmem_ops = {
+#ifdef CONFIG_TMPFS
 	statfs:		shmem_statfs,
 	remount_fs:	shmem_remount_fs,
+#endif
 	delete_inode:	shmem_delete_inode,
 	put_inode:	force_delete,	
 };
 
-static struct vm_operations_struct shmem_private_vm_ops = {
-	nopage:	shmem_nopage,
-};
-
-static struct vm_operations_struct shmem_shared_vm_ops = {
+static struct vm_operations_struct shmem_vm_ops = {
 	nopage:	shmem_nopage,
 };
 
+#ifdef CONFIG_TMPFS
+/* type "shm" will be tagged obsolete in 2.5 */
 static DECLARE_FSTYPE(shmem_fs_type, "shm", shmem_read_super, FS_LITTER);
+static DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER);
+#else
+static DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER|FS_NOMOUNT);
+#endif
 
 static int __init init_shmem_fs(void)
 {
 	int error;
 	struct vfsmount * res;
 
+	if ((error = register_filesystem(&tmpfs_fs_type))) {
+		printk (KERN_ERR "Could not register tmpfs\n");
+		return error;
+	}
+#ifdef CONFIG_TMPFS
 	if ((error = register_filesystem(&shmem_fs_type))) {
-		printk (KERN_ERR "Could not register shmem fs\n");
+		printk (KERN_ERR "Could not register shm fs\n");
 		return error;
 	}
-
-	res = kern_mount(&shmem_fs_type);
+	devfs_mk_dir (NULL, "shm", NULL);
+#endif
+	res = kern_mount(&tmpfs_fs_type);
 	if (IS_ERR (res)) {
-		printk (KERN_ERR "could not kern_mount shmem fs\n");
-		unregister_filesystem(&shmem_fs_type);
+		printk (KERN_ERR "could not kern_mount tmpfs\n");
+		unregister_filesystem(&tmpfs_fs_type);
 		return PTR_ERR(res);
 	}
 
-	devfs_mk_dir (NULL, "shm", NULL);
 	return 0;
 }
 
 static void __exit exit_shmem_fs(void)
 {
+#ifdef CONFIG_TMPFS
 	unregister_filesystem(&shmem_fs_type);
+#endif
+	unregister_filesystem(&tmpfs_fs_type);
 }
 
 module_init(init_shmem_fs)
@@ -853,7 +1295,7 @@
 	this.name = name;
 	this.len = strlen(name);
 	this.hash = 0; /* will go */
-	root = shmem_fs_type.kern_mnt->mnt_root;
+	root = tmpfs_fs_type.kern_mnt->mnt_root;
 	dentry = d_alloc(root, &this);
 	if (!dentry)
 		goto out;
@@ -870,7 +1312,8 @@
 
 	d_instantiate(dentry, inode);
 	dentry->d_inode->i_size = size;
-	file->f_vfsmnt = mntget(shmem_fs_type.kern_mnt);
+	shmem_truncate(inode);
+	file->f_vfsmnt = mntget(tmpfs_fs_type.kern_mnt);
 	file->f_dentry = dentry;
 	file->f_op = &shmem_file_operations;
 	file->f_mode = FMODE_WRITE | FMODE_READ;
@@ -901,6 +1344,8 @@
 	if (vma->vm_file)
 		fput (vma->vm_file);
 	vma->vm_file = file;
-	vma->vm_ops = &shmem_shared_vm_ops;
+	vma->vm_ops = &shmem_vm_ops;
 	return 0;
 }
+
+EXPORT_SYMBOL(shmem_file_setup);
diff -urN linux-2.4.4-pre2/mm/vmscan.c linux/mm/vmscan.c
--- linux-2.4.4-pre2/mm/vmscan.c	Mon Dec  3 22:34:42 2001
+++ linux/mm/vmscan.c	Mon Dec  3 22:34:51 2001
@@ -563,7 +563,7 @@
 
 			/*
 			 * Unlock the page and drop the extra reference.
-			 * We can only do it here because we ar accessing
+			 * We can only do it here because we are accessing
 			 * the page struct above.
 			 */
 			UnlockPage(page);
diff -urN linux-2.4.4-pre2/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- linux-2.4.4-pre2/net/appletalk/ddp.c	Mon Jan 22 13:32:10 2001
+++ linux/net/appletalk/ddp.c	Mon Dec  3 22:34:51 2001
@@ -1672,7 +1672,7 @@
 			sk, size, dev->name);
 
 	size += dev->hard_header_len;
-	skb = sock_alloc_send_skb(sk, size, 0, (flags & MSG_DONTWAIT), &err);
+	skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err);
 	if (!skb)
 		return err;
 	
@@ -1913,6 +1913,7 @@
 	sendmsg:	atalk_sendmsg,
 	recvmsg:	atalk_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
@@ -1988,7 +1989,7 @@
  * Use counts are incremented/decremented when
  * sockets are created/deleted.
  *
- * AppleTalk interfaces are not incremented untill atalkd is run
+ * AppleTalk interfaces are not incremented until atalkd is run
  * and are only decremented when they are downed.
  *
  * Ergo, before the AppleTalk module can be removed, all AppleTalk
diff -urN linux-2.4.4-pre2/net/atm/pvc.c linux/net/atm/pvc.c
--- linux-2.4.4-pre2/net/atm/pvc.c	Sat Nov 11 18:53:33 2000
+++ linux/net/atm/pvc.c	Mon Dec  3 22:34:51 2001
@@ -95,6 +95,7 @@
 	sendmsg:	atm_sendmsg,
 	recvmsg:	atm_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 
diff -urN linux-2.4.4-pre2/net/atm/svc.c linux/net/atm/svc.c
--- linux-2.4.4-pre2/net/atm/svc.c	Wed Nov 15 00:41:03 2000
+++ linux/net/atm/svc.c	Mon Dec  3 22:34:51 2001
@@ -408,6 +408,7 @@
 	sendmsg:	atm_sendmsg,
 	recvmsg:	atm_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 
diff -urN linux-2.4.4-pre2/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- linux-2.4.4-pre2/net/ax25/af_ax25.c	Fri Feb  9 11:34:13 2001
+++ linux/net/ax25/af_ax25.c	Mon Dec  3 22:34:51 2001
@@ -1451,7 +1451,7 @@
 	/* Assume the worst case */
 	size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN;
 
-	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
+	if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
 	skb_reserve(skb, size - len);
@@ -1805,6 +1805,7 @@
 	sendmsg:	ax25_sendmsg,
 	recvmsg:	ax25_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
diff -urN linux-2.4.4-pre2/net/core/datagram.c linux/net/core/datagram.c
--- linux-2.4.4-pre2/net/core/datagram.c	Sat Feb  3 10:39:12 2001
+++ linux/net/core/datagram.c	Mon Dec  3 22:34:51 2001
@@ -36,6 +36,7 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/poll.h>
+#include <linux/highmem.h>
 
 #include <net/ip.h>
 #include <net/protocol.h>
@@ -192,26 +193,216 @@
  *	Copy a datagram to a linear buffer.
  */
 
-int skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
+int skb_copy_datagram(const struct sk_buff *skb, int offset, char *to, int size)
 {
-	int err = -EFAULT;
+	struct iovec iov = { to, size };
 
-	if (!copy_to_user(to, skb->h.raw + offset, size))
-		err = 0;
-	return err;
+	return skb_copy_datagram_iovec(skb, offset, &iov, size);
 }
 
-
 /*
  *	Copy a datagram to an iovec.
  *	Note: the iovec is modified during the copy.
  */
- 
-int skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to,
-			    int size)
+int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec *to,
+			    int len)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+
+	/* Copy header. */
+	if ((copy = start-offset) > 0) {
+		if (copy > len)
+			copy = len;
+		if (memcpy_toiovec(to, skb->data + offset, copy))
+			goto fault;
+		if ((len -= copy) == 0)
+			return 0;
+		offset += copy;
+	}
+
+	/* Copy paged appendix. Hmm... why does this look so complicated? */
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			int err;
+			u8  *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+			struct page *page = frag->page;
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap(page);
+			err = memcpy_toiovec(to, vaddr + frag->page_offset +
+					     offset-start, copy);
+			kunmap(page);
+			if (err)
+				goto fault;
+			if (!(len -= copy))
+				return 0;
+			offset += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				if (copy > len)
+					copy = len;
+				if (skb_copy_datagram_iovec(list, offset-start, to, copy))
+					goto fault;
+				if ((len -= copy) == 0)
+					return 0;
+				offset += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return 0;
+
+fault:
+	return -EFAULT;
+}
+
+int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int *csump)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+	int pos = 0;
+
+	/* Copy header. */
+	if ((copy = start-offset) > 0) {
+		int err = 0;
+		if (copy > len)
+			copy = len;
+		*csump = csum_and_copy_to_user(skb->data+offset, to, copy, *csump, &err);
+		if (err)
+			goto fault;
+		if ((len -= copy) == 0)
+			return 0;
+		offset += copy;
+		to += copy;
+		pos = copy;
+	}
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			unsigned int csum2;
+			int err = 0;
+			u8  *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+			struct page *page = frag->page;
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap(page);
+			csum2 = csum_and_copy_to_user(vaddr + frag->page_offset +
+						      offset-start, to, copy, 0, &err);
+			kunmap(page);
+			if (err)
+				goto fault;
+			*csump = csum_block_add(*csump, csum2, pos);
+			if (!(len -= copy))
+				return 0;
+			offset += copy;
+			to += copy;
+			pos += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				unsigned int csum2 = 0;
+				if (copy > len)
+					copy = len;
+				if (skb_copy_and_csum_datagram(list, offset-start, to, copy, &csum2))
+					goto fault;
+				*csump = csum_block_add(*csump, csum2, pos);
+				if ((len -= copy) == 0)
+					return 0;
+				offset += copy;
+				to += copy;
+				pos += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return 0;
+
+fault:
+	return -EFAULT;
+}
+
+/* Copy and checkum skb to user iovec. Caller _must_ check that
+   skb will fit to this iovec.
+
+   Returns: 0       - success.
+            -EINVAL - checksum failure.
+	    -EFAULT - fault during copy. Beware, in this case iovec can be
+	              modified!
+ */
+
+int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct iovec *iov)
 {
-	return memcpy_toiovec(to, skb->h.raw + offset, size);
+	unsigned int csum;
+	int chunk = skb->len - hlen;
+
+	/* Skip filled elements. Pretty silly, look at memcpy_toiovec, though 8) */
+	while (iov->iov_len == 0)
+		iov++;
+
+	if (iov->iov_len < chunk) {
+		if ((unsigned short)csum_fold(skb_checksum(skb, 0, chunk+hlen, skb->csum)))
+			goto csum_error;
+		if (skb_copy_datagram_iovec(skb, hlen, iov, chunk))
+			goto fault;
+	} else {
+		csum = csum_partial(skb->data, hlen, skb->csum);
+		if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base, chunk, &csum))
+			goto fault;
+		if ((unsigned short)csum_fold(csum))
+			goto csum_error;
+		iov->iov_len -= chunk;
+		iov->iov_base += chunk;
+	}
+	return 0;
+
+csum_error:
+	return -EINVAL;
+
+fault:
+	return -EFAULT;
 }
+
+
 
 /*
  *	Datagram poll: Again totally generic. This also handles
diff -urN linux-2.4.4-pre2/net/core/dev.c linux/net/core/dev.c
--- linux-2.4.4-pre2/net/core/dev.c	Fri Mar  2 11:02:15 2001
+++ linux/net/core/dev.c	Mon Dec  3 22:34:51 2001
@@ -91,6 +91,8 @@
 #include <net/dst.h>
 #include <net/pkt_sched.h>
 #include <net/profile.h>
+#include <net/checksum.h>
+#include <linux/highmem.h>
 #include <linux/init.h>
 #include <linux/kmod.h>
 #include <linux/module.h>
@@ -503,7 +505,7 @@
 }
 
 /**
- *	dev_getbyhwaddr - find a device by its hardware addres
+ *	dev_getbyhwaddr - find a device by its hardware address
  *	@type: media type of device
  *	@ha: hardware address
  *
@@ -871,12 +873,10 @@
 			 */
 			skb2->mac.raw = skb2->data;
 
-			if (skb2->nh.raw < skb2->data || skb2->nh.raw >= skb2->tail) {
+			if (skb2->nh.raw < skb2->data || skb2->nh.raw > skb2->tail) {
 				if (net_ratelimit())
 					printk(KERN_DEBUG "protocol %04x is buggy, dev %s\n", skb2->protocol, dev->name);
 				skb2->nh.raw = skb2->data;
-				if (dev->hard_header)
-					skb2->nh.raw += dev->hard_header_len;
 			}
 
 			skb2->h.raw = skb2->nh.raw;
@@ -887,6 +887,55 @@
 	br_read_unlock(BR_NETPROTO_LOCK);
 }
 
+/* Calculate csum in the case, when packet is misrouted.
+ * If it failed by some reason, ignore and send skb with wrong
+ * checksum.
+ */
+struct sk_buff * skb_checksum_help(struct sk_buff *skb)
+{
+	int offset;
+	unsigned int csum;
+
+	offset = skb->h.raw - skb->data;
+	if (offset > (int)skb->len)
+		BUG();
+	csum = skb_checksum(skb, offset, skb->len-offset, 0);
+
+	offset = skb->tail - skb->h.raw;
+	if (offset <= 0)
+		BUG();
+	if (skb->csum+2 > offset)
+		BUG();
+
+	*(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
+	skb->ip_summed = CHECKSUM_NONE;
+	return skb;
+}
+
+#ifdef CONFIG_HIGHMEM
+/* Actually, we should eliminate this check as soon as we know, that:
+ * 1. IOMMU is present and allows to map all the memory.
+ * 2. No high memory really exists on this machine.
+ */
+
+static inline int
+illegal_highdma(struct net_device *dev, struct sk_buff *skb)
+{
+	int i;
+
+	if (dev->features&NETIF_F_HIGHDMA)
+		return 0;
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++)
+		if (skb_shinfo(skb)->frags[i].page >= highmem_start_page)
+			return 1;
+
+	return 0;
+}
+#else
+#define illegal_highdma(dev, skb)	(0)
+#endif
+
 /**
  *	dev_queue_xmit - transmit a buffer
  *	@skb: buffer to transmit
@@ -899,12 +948,41 @@
  *	guarantee the frame will be transmitted as it may be dropped due
  *	to congestion or traffic shaping.
  */
- 
+
 int dev_queue_xmit(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
 	struct Qdisc  *q;
 
+	if (skb_shinfo(skb)->frag_list &&
+	    !(dev->features&NETIF_F_FRAGLIST) &&
+	    skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+
+	/* Fragmented skb is linearized if device does not support SG,
+	 * or if at least one of fragments is in highmem and device
+	 * does not support DMA from it.
+	 */
+	if (skb_shinfo(skb)->nr_frags &&
+	    (!(dev->features&NETIF_F_SG) || illegal_highdma(dev, skb)) &&
+	    skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+
+	/* If packet is not checksummed and device does not support
+	 * checksumming for this protocol, complete checksumming here.
+	 */
+	if (skb->ip_summed == CHECKSUM_HW &&
+	    (!(dev->features&(NETIF_F_HW_CSUM|NETIF_F_NO_CSUM)) &&
+	     (!(dev->features&NETIF_F_IP_CSUM) ||
+	      skb->protocol != htons(ETH_P_IP)))) {
+		if ((skb = skb_checksum_help(skb)) == NULL)
+			return -ENOMEM;
+	}
+
 	/* Grab device queue */
 	spin_lock_bh(&dev->queue_lock);
 	q = dev->qdisc;
@@ -1182,6 +1260,10 @@
 		skb = skb_clone(skb, GFP_ATOMIC);
 		if (skb == NULL)
 			return ret;
+	}
+	if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return ret;
 	}
 
 	/* The assumption (correct one) is that old protocols
diff -urN linux-2.4.4-pre2/net/core/filter.c linux/net/core/filter.c
--- linux-2.4.4-pre2/net/core/filter.c	Sun Apr  2 15:38:54 2000
+++ linux/net/core/filter.c	Mon Dec  3 22:34:51 2001
@@ -72,7 +72,7 @@
 	/* len is UNSIGNED. Byte wide insns relies only on implicit
 	   type casts to prevent reading arbitrary memory locations.
 	 */
-	unsigned int len = skb->len;
+	unsigned int len = skb->len-skb->data_len;
 	struct sock_filter *fentry;	/* We walk down these */
 	u32 A = 0;	   		/* Accumulator */
 	u32 X = 0;   			/* Index Register */
@@ -201,7 +201,7 @@
 			case BPF_LD|BPF_W|BPF_ABS:
 				k = fentry->k;
 load_w:
-				if(k+sizeof(u32) <= len) {
+				if(k >= 0 && (unsigned int)(k+sizeof(u32)) <= len) {
 					A = ntohl(*(u32*)&data[k]);
 					continue;
 				}
@@ -214,13 +214,19 @@
 						A = ntohl(*(u32*)ptr);
 						continue;
 					}
+				} else {
+					u32 tmp;
+					if (!skb_copy_bits(skb, k, &tmp, 4)) {
+						A = ntohl(tmp);
+						continue;
+					}
 				}
 				return 0;
 
 			case BPF_LD|BPF_H|BPF_ABS:
 				k = fentry->k;
 load_h:
-				if(k + sizeof(u16) <= len) {
+				if(k >= 0 && (unsigned int) (k + sizeof(u16)) <= len) {
 					A = ntohs(*(u16*)&data[k]);
 					continue;
 				}
@@ -233,13 +239,19 @@
 						A = ntohs(*(u16*)ptr);
 						continue;
 					}
+				} else {
+					u16 tmp;
+					if (!skb_copy_bits(skb, k, &tmp, 2)) {
+						A = ntohs(tmp);
+						continue;
+					}
 				}
 				return 0;
 
 			case BPF_LD|BPF_B|BPF_ABS:
 				k = fentry->k;
 load_b:
-				if(k < len) {
+				if(k >= 0 && (unsigned int)k < len) {
 					A = data[k];
 					continue;
 				}
@@ -252,6 +264,12 @@
 						A = *ptr;
 						continue;
 					}
+				} else {
+					u8 tmp;
+					if (!skb_copy_bits(skb, k, &tmp, 1)) {
+						A = tmp;
+						continue;
+					}
 				}
 				return 0;
 
@@ -277,7 +295,7 @@
 
 			case BPF_LDX|BPF_B|BPF_MSH:
 				k = fentry->k;
-				if(k >= len)
+				if(k >= 0 && (unsigned int)k >= len)
 					return (0);
 				X = (data[k] & 0xf) << 2;
 				continue;
diff -urN linux-2.4.4-pre2/net/core/iovec.c linux/net/core/iovec.c
--- linux-2.4.4-pre2/net/core/iovec.c	Fri Feb  9 11:29:44 2001
+++ linux/net/core/iovec.c	Mon Dec  3 22:34:51 2001
@@ -102,48 +102,6 @@
 	return err;
 }
 
-/* Copy and checkum skb to user iovec. Caller _must_ check that
-   skb will fit to this iovec.
-
-   Returns: 0       - success.
-            -EINVAL - checksum failure.
-	    -EFAULT - fault during copy. Beware, in this case iovec can be
-	              modified!
- */
-
-int copy_and_csum_toiovec(struct iovec *iov, struct sk_buff *skb, int hlen)
-{
-	unsigned int csum;
-	int chunk = skb->len - hlen;
-
-	/* Skip filled elements. Pretty silly, look at memcpy_toiovec, though 8) */
-	while (iov->iov_len == 0)
-		iov++;
-
-	if (iov->iov_len < chunk) {
-		if ((unsigned short)csum_fold(csum_partial(skb->h.raw, chunk+hlen, skb->csum)))
-			goto csum_error;
-		if (memcpy_toiovec(iov, skb->h.raw + hlen, chunk))
-			goto fault;
-	} else {
-		int err = 0;
-		csum = csum_partial(skb->h.raw, hlen, skb->csum);
-		csum = csum_and_copy_to_user(skb->h.raw+hlen, iov->iov_base,
-					     chunk, csum, &err);
-		if (err || ((unsigned short)csum_fold(csum)))
-			goto csum_error;
-		iov->iov_len -= chunk;
-		iov->iov_base += chunk;
-	}
-	return 0;
-
-csum_error:
-	return -EINVAL;
-
-fault:
-	return -EFAULT;
-}
-
 /*
  *	In kernel copy to iovec. Returns -EFAULT on error.
  *
diff -urN linux-2.4.4-pre2/net/core/netfilter.c linux/net/core/netfilter.c
--- linux-2.4.4-pre2/net/core/netfilter.c	Mon Jan 22 13:30:21 2001
+++ linux/net/core/netfilter.c	Mon Dec  3 22:34:51 2001
@@ -451,6 +451,19 @@
 	unsigned int verdict;
 	int ret = 0;
 
+	/* This stopgap cannot be removed until all the hooks are audited. */
+	if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+	if (skb->ip_summed == CHECKSUM_HW) {
+		if (outdev == NULL) {
+			skb->ip_summed = CHECKSUM_NONE;
+		} else {
+			skb_checksum_help(skb);
+		}
+	}
+
 	/* We may already have this, but read-locks nest anyway */
 	br_read_lock_bh(BR_NETPROTO_LOCK);
 
diff -urN linux-2.4.4-pre2/net/core/skbuff.c linux/net/core/skbuff.c
--- linux-2.4.4-pre2/net/core/skbuff.c	Fri Feb 16 16:06:17 2001
+++ linux/net/core/skbuff.c	Mon Dec  3 22:34:51 2001
@@ -4,7 +4,7 @@
  *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
  *			Florian La Roche <rzsfl@rz.uni-sb.de>
  *
- *	Version:	$Id: skbuff.c,v 1.75 2000/12/08 17:15:53 davem Exp $
+ *	Version:	$Id: skbuff.c,v 1.87 2001/03/06 22:09:50 davem Exp $
  *
  *	Fixes:	
  *		Alan Cox	:	Fixed the worst of the load balancer bugs.
@@ -50,6 +50,7 @@
 #include <linux/skbuff.h>
 #include <linux/cache.h>
 #include <linux/init.h>
+#include <linux/highmem.h>
 
 #include <net/ip.h>
 #include <net/protocol.h>
@@ -185,8 +186,8 @@
 	}
 
 	/* Get the DATA. Size must match skb_add_mtu(). */
-	size = ((size + 15) & ~15); 
-	data = kmalloc(size + sizeof(atomic_t), gfp_mask);
+	size = SKB_DATA_ALIGN(size);
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
 	if (data == NULL)
 		goto nodata;
 
@@ -202,9 +203,12 @@
 	/* Set up other state */
 	skb->len = 0;
 	skb->cloned = 0;
+	skb->data_len = 0;
 
 	atomic_set(&skb->users, 1); 
-	atomic_set(skb_datarefp(skb), 1);
+	atomic_set(&(skb_shinfo(skb)->dataref), 1);
+	skb_shinfo(skb)->nr_frags = 0;
+	skb_shinfo(skb)->frag_list = NULL;
 	return skb;
 
 nodata:
@@ -248,14 +252,50 @@
 #endif
 }
 
+static void skb_drop_fraglist(struct sk_buff *skb)
+{
+	struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+	skb_shinfo(skb)->frag_list = NULL;
+
+	do {
+		struct sk_buff *this = list;
+		list = list->next;
+		kfree_skb(this);
+	} while (list);
+}
+
+static void skb_clone_fraglist(struct sk_buff *skb)
+{
+	struct sk_buff *list;
+
+	for (list = skb_shinfo(skb)->frag_list; list; list=list->next)
+		skb_get(list);
+}
+
+static void skb_release_data(struct sk_buff *skb)
+{
+	if (!skb->cloned ||
+	    atomic_dec_and_test(&(skb_shinfo(skb)->dataref))) {
+		if (skb_shinfo(skb)->nr_frags) {
+			int i;
+			for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+				put_page(skb_shinfo(skb)->frags[i].page);
+		}
+
+		if (skb_shinfo(skb)->frag_list)
+			skb_drop_fraglist(skb);
+
+		kfree(skb->head);
+	}
+}
+
 /*
  *	Free an skbuff by memory without cleaning the state. 
  */
 void kfree_skbmem(struct sk_buff *skb)
 {
-	if (!skb->cloned || atomic_dec_and_test(skb_datarefp(skb)))  
-		kfree(skb->head);
-
+	skb_release_data(skb);
 	skb_head_to_pool(skb);
 }
 
@@ -316,18 +356,53 @@
 			return NULL;
 	}
 
-	memcpy(n, skb, sizeof(*n));
-	atomic_inc(skb_datarefp(skb));
-	skb->cloned = 1;
-       
-	dst_clone(n->dst);
-	n->cloned = 1;
+#define C(x) n->x = skb->x
+
 	n->next = n->prev = NULL;
 	n->list = NULL;
 	n->sk = NULL;
+	C(stamp);
+	C(dev);
+	C(h);
+	C(nh);
+	C(mac);
+	C(dst);
+	dst_clone(n->dst);
+	memcpy(n->cb, skb->cb, sizeof(skb->cb));
+	C(len);
+	C(data_len);
+	C(csum);
+	n->cloned = 1;
+	C(pkt_type);
+	C(ip_summed);
+	C(priority);
 	atomic_set(&n->users, 1);
+	C(protocol);
+	C(security);
+	C(truesize);
+	C(head);
+	C(data);
+	C(tail);
+	C(end);
 	n->destructor = NULL;
 #ifdef CONFIG_NETFILTER
+	C(nfmark);
+	C(nfcache);
+	C(nfct);
+#ifdef CONFIG_NETFILTER_DEBUG
+	C(nf_debug);
+#endif
+#endif /*CONFIG_NETFILTER*/
+#if defined(CONFIG_HIPPI)
+	C(private);
+#endif
+#ifdef CONFIG_NET_SCHED
+	C(tc_index);
+#endif
+
+	atomic_inc(&(skb_shinfo(skb)->dataref));
+	skb->cloned = 1;
+#ifdef CONFIG_NETFILTER
 	nf_conntrack_get(skb->nfct);
 #endif
 	return n;
@@ -350,7 +425,6 @@
 	new->nh.raw=old->nh.raw+offset;
 	new->mac.raw=old->mac.raw+offset;
 	memcpy(new->cb, old->cb, sizeof(old->cb));
-	new->used=old->used;
 	atomic_set(&new->users, 1);
 	new->pkt_type=old->pkt_type;
 	new->stamp=old->stamp;
@@ -371,7 +445,7 @@
 }
 
 /**
- *	skb_copy	-	copy an sk_buff
+ *	skb_copy	-	create private copy of an sk_buff
  *	@skb: buffer to copy
  *	@gfp_mask: allocation priority
  *
@@ -380,18 +454,115 @@
  *	data to alter. Returns %NULL on failure or the pointer to the buffer
  *	on success. The returned buffer has a reference count of 1.
  *
- *	You must pass %GFP_ATOMIC as the allocation priority if this function
- *	is called from an interrupt.
+ *	As by-product this function converts non-linear &sk_buff to linear
+ *	one, so that &sk_buff becomes completely private and caller is allowed
+ *	to modify all the data of returned buffer. This means that this
+ *	function is not recommended for use in circumstances when only
+ *	header is going to be modified. Use pskb_copy() instead.
  */
  
 struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)
 {
 	struct sk_buff *n;
+	int headerlen = skb->data-skb->head;
+
+	/*
+	 *	Allocate the copy buffer
+	 */
+	n=alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);
+	if(n==NULL)
+		return NULL;
+
+	/* Set the data pointer */
+	skb_reserve(n,headerlen);
+	/* Set the tail pointer and length */
+	skb_put(n,skb->len);
+	n->csum = skb->csum;
+	n->ip_summed = skb->ip_summed;
+
+	if (skb_copy_bits(skb, -headerlen, n->head, headerlen+skb->len))
+		BUG();
+
+	copy_skb_header(n, skb);
+
+	return n;
+}
+
+/* Keep head the same: replace data */
+int skb_linearize(struct sk_buff *skb, int gfp_mask)
+{
+	unsigned int size;
+	u8 *data;
+	long offset;
+	int headerlen = skb->data - skb->head;
+	int expand = (skb->tail+skb->data_len) - skb->end;
+
+	if (skb_shared(skb))
+		BUG();
+
+	if (expand <= 0)
+		expand = 0;
+
+	size = (skb->end - skb->head + expand);
+	size = SKB_DATA_ALIGN(size);
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (data == NULL)
+		return -ENOMEM;
+
+	/* Copy entire thing */
+	if (skb_copy_bits(skb, -headerlen, data, headerlen+skb->len))
+		BUG();
+
+	/* Offset between the two in bytes */
+	offset = data - skb->head;
+
+	/* Free old data. */
+	skb_release_data(skb);
+
+	skb->head = data;
+	skb->end  = data + size;
+
+	/* Set up new pointers */
+	skb->h.raw += offset;
+	skb->nh.raw += offset;
+	skb->mac.raw += offset;
+	skb->tail += offset;
+	skb->data += offset;
+
+	/* Set up shinfo */
+	atomic_set(&(skb_shinfo(skb)->dataref), 1);
+	skb_shinfo(skb)->nr_frags = 0;
+	skb_shinfo(skb)->frag_list = NULL;
+
+	/* We are no longer a clone, even if we were. */
+	skb->cloned = 0;
+
+	skb->tail += skb->data_len;
+	skb->data_len = 0;
+	return 0;
+}
+
+
+/**
+ *	pskb_copy	-	create copy of an sk_buff with private head.
+ *	@skb: buffer to copy
+ *	@gfp_mask: allocation priority
+ *
+ *	Make a copy of both an &sk_buff and part of its data, located
+ *	in header. Fragmented data remain shared. This is used when
+ *	the caller wishes to modify only header of &sk_buff and needs
+ *	private copy of the header to alter. Returns %NULL on failure
+ *	or the pointer to the buffer on success.
+ *	The returned buffer has a reference count of 1.
+ */
+
+struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)
+{
+	struct sk_buff *n;
 
 	/*
 	 *	Allocate the copy buffer
 	 */
-	 
 	n=alloc_skb(skb->end - skb->head, gfp_mask);
 	if(n==NULL)
 		return NULL;
@@ -399,16 +570,120 @@
 	/* Set the data pointer */
 	skb_reserve(n,skb->data-skb->head);
 	/* Set the tail pointer and length */
-	skb_put(n,skb->len);
+	skb_put(n,skb_headlen(skb));
 	/* Copy the bytes */
-	memcpy(n->head,skb->head,skb->end-skb->head);
+	memcpy(n->data, skb->data, n->len);
 	n->csum = skb->csum;
+	n->ip_summed = skb->ip_summed;
+
+	n->data_len = skb->data_len;
+	n->len = skb->len;
+
+	if (skb_shinfo(skb)->nr_frags) {
+		int i;
+
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
+			get_page(skb_shinfo(n)->frags[i].page);
+		}
+		skb_shinfo(n)->nr_frags = i;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
+		skb_clone_fraglist(n);
+	}
+
 	copy_skb_header(n, skb);
 
 	return n;
 }
 
 /**
+ *	pskb_expand_head - reallocate header of &sk_buff
+ *	@skb: buffer to reallocate
+ *	@nhead: room to add at head
+ *	@ntail: room to add at tail
+ *	@gfp_mask: allocation priority
+ *
+ *	Expands (or creates identical copy, if &nhead and &ntail are zero)
+ *	header of skb. &sk_buff itself is not changed. &sk_buff MUST have
+ *	reference count of 1. Returns zero in the case of success or error,
+ *	if expansion failed. In the last case, &sk_buff is not changed.
+ *
+ *	All the pointers pointing into skb header may change and must be
+ *	reloaded after call to this function.
+ */
+
+int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask)
+{
+	int i;
+	u8 *data;
+	int size = nhead + (skb->end - skb->head) + ntail;
+	long off;
+
+	if (skb_shared(skb))
+		BUG();
+
+	size = SKB_DATA_ALIGN(size);
+
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (data == NULL)
+		goto nodata;
+
+	/* Copy only real data... and, alas, header. This should be
+	 * optimized for the cases when header is void. */
+	memcpy(data+nhead, skb->head, skb->tail-skb->head);
+	memcpy(data+size, skb->end, sizeof(struct skb_shared_info));
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++)
+		get_page(skb_shinfo(skb)->frags[i].page);
+
+	if (skb_shinfo(skb)->frag_list)
+		skb_clone_fraglist(skb);
+
+	skb_release_data(skb);
+
+	off = (data+nhead) - skb->head;
+
+	skb->head = data;
+	skb->end  = data+size;
+
+	skb->data += off;
+	skb->tail += off;
+	skb->mac.raw += off;
+	skb->h.raw += off;
+	skb->nh.raw += off;
+	skb->cloned = 0;
+	atomic_set(&skb_shinfo(skb)->dataref, 1);
+	return 0;
+
+nodata:
+	return -ENOMEM;
+}
+
+/* Make private copy of skb with writable head and some headroom */
+
+struct sk_buff *
+skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
+{
+	struct sk_buff *skb2;
+	int delta = headroom - skb_headroom(skb);
+
+	if (delta <= 0)
+		return pskb_copy(skb, GFP_ATOMIC);
+
+	skb2 = skb_clone(skb, GFP_ATOMIC);
+	if (skb2 == NULL ||
+	    !pskb_expand_head(skb2, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC))
+		return skb2;
+
+	kfree_skb(skb2);
+	return NULL;
+}
+
+
+/**
  *	skb_copy_expand	-	copy and expand sk_buff
  *	@skb: buffer to copy
  *	@newheadroom: new free bytes at head
@@ -439,7 +714,7 @@
 	 *	Allocate the copy buffer
 	 */
  	 
-	n=alloc_skb(newheadroom + (skb->tail - skb->data) + newtailroom,
+	n=alloc_skb(newheadroom + skb->len + newtailroom,
 		    gfp_mask);
 	if(n==NULL)
 		return NULL;
@@ -450,12 +725,431 @@
 	skb_put(n,skb->len);
 
 	/* Copy the data only. */
-	memcpy(n->data, skb->data, skb->len);
+	if (skb_copy_bits(skb, 0, n->data, skb->len))
+		BUG();
 
 	copy_skb_header(n, skb);
 	return n;
 }
 
+/* Trims skb to length len. It can change skb pointers, if "realloc" is 1.
+ * If realloc==0 and trimming is impossible without change of data,
+ * it is BUG().
+ */
+
+int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc)
+{
+	int offset = skb_headlen(skb);
+	int nfrags = skb_shinfo(skb)->nr_frags;
+	int i;
+
+	for (i=0; i<nfrags; i++) {
+		int end = offset + skb_shinfo(skb)->frags[i].size;
+		if (end > len) {
+			if (skb_cloned(skb)) {
+				if (!realloc)
+					BUG();
+				if (!pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+					return -ENOMEM;
+			}
+			if (len <= offset) {
+				put_page(skb_shinfo(skb)->frags[i].page);
+				skb_shinfo(skb)->nr_frags--;
+			} else {
+				skb_shinfo(skb)->frags[i].size = len-offset;
+			}
+		}
+		offset = end;
+	}
+
+	if (offset < len) {
+		skb->data_len -= skb->len - len;
+		skb->len = len;
+	} else {
+		if (len <= skb_headlen(skb)) {
+			skb->len = len;
+			skb->data_len = 0;
+			skb->tail = skb->data + len;
+			if (skb_shinfo(skb)->frag_list && !skb_cloned(skb))
+				skb_drop_fraglist(skb);
+		} else {
+			skb->data_len -= skb->len - len;
+			skb->len = len;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ *	__pskb_pull_tail - advance tail of skb header 
+ *	@skb: buffer to reallocate
+ *	@delta: number of bytes to advance tail
+ *
+ *	The function makes a sense only on a fragmented &sk_buff,
+ *	it expands header moving its tail forward and copying necessary
+ *	data from fragmented part.
+ *
+ *	&sk_buff MUST have reference count of 1.
+ *
+ *	Returns %NULL (and &sk_buff does not change) if pull failed
+ *	or value of new tail of skb in the case of success.
+ *
+ *	All the pointers pointing into skb header may change and must be
+ *	reloaded after call to this function.
+ */
+
+/* Moves tail of skb head forward, copying data from fragmented part,
+ * when it is necessary.
+ * 1. It may fail due to malloc failure.
+ * 2. It may change skb pointers.
+ *
+ * It is pretty complicated. Luckily, it is called only in exceptional cases.
+ */
+unsigned char * __pskb_pull_tail(struct sk_buff *skb, int delta)
+{
+	int i, k, eat;
+
+	/* If skb has not enough free space at tail, get new one
+	 * plus 128 bytes for future expansions. If we have enough
+	 * room at tail, reallocate without expansion only if skb is cloned.
+	 */
+	eat = (skb->tail+delta) - skb->end;
+
+	if (eat > 0 || skb_cloned(skb)) {
+		if (pskb_expand_head(skb, 0, eat>0 ? eat+128 : 0, GFP_ATOMIC))
+			return NULL;
+	}
+
+	if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))
+		BUG();
+
+	/* Optimization: no fragments, no reasons to preestimate
+	 * size of pulled pages. Superb.
+	 */
+	if (skb_shinfo(skb)->frag_list == NULL)
+		goto pull_pages;
+
+	/* Estimate size of pulled pages. */
+	eat = delta;
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		if (skb_shinfo(skb)->frags[i].size >= eat)
+			goto pull_pages;
+		eat -= skb_shinfo(skb)->frags[i].size;
+	}
+
+	/* If we need update frag list, we are in troubles.
+	 * Certainly, it possible to add an offset to skb data,
+	 * but taking into account that pulling is expected to
+	 * be very rare operation, it is worth to fight against
+	 * further bloating skb head and crucify ourselves here instead.
+	 * Pure masohism, indeed. 8)8)
+	 */
+	if (eat) {
+		struct sk_buff *list = skb_shinfo(skb)->frag_list;
+		struct sk_buff *clone = NULL;
+		struct sk_buff *insp = NULL;
+
+		do {
+			if (list == NULL)
+				BUG();
+
+			if (list->len <= eat) {
+				/* Eaten as whole. */
+				eat -= list->len;
+				list = list->next;
+				insp = list;
+			} else {
+				/* Eaten partially. */
+
+				if (skb_shared(list)) {
+					/* Sucks! We need to fork list. :-( */
+					clone = skb_clone(list, GFP_ATOMIC);
+					if (clone == NULL)
+						return NULL;
+					insp = list->next;
+					list = clone;
+				} else {
+					/* This may be pulled without
+					 * problems. */
+					insp = list;
+				}
+				if (pskb_pull(list, eat) == NULL) {
+					if (clone)
+						kfree_skb(clone);
+					return NULL;
+				}
+				break;
+			}
+		} while (eat);
+
+		/* Free pulled out fragments. */
+		while ((list = skb_shinfo(skb)->frag_list) != insp) {
+			skb_shinfo(skb)->frag_list = list->next;
+			kfree_skb(list);
+		}
+		/* And insert new clone at head. */
+		if (clone) {
+			clone->next = list;
+			skb_shinfo(skb)->frag_list = clone;
+		}
+	}
+	/* Success! Now we may commit changes to skb data. */
+
+pull_pages:
+	eat = delta;
+	k = 0;
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		if (skb_shinfo(skb)->frags[i].size <= eat) {
+			put_page(skb_shinfo(skb)->frags[i].page);
+			eat -= skb_shinfo(skb)->frags[i].size;
+		} else {
+			skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
+			if (eat) {
+				skb_shinfo(skb)->frags[k].page_offset += eat;
+				skb_shinfo(skb)->frags[k].size -= eat;
+				eat = 0;
+			}
+			k++;
+		}
+	}
+	skb_shinfo(skb)->nr_frags = k;
+
+	skb->tail += delta;
+	skb->data_len -= delta;
+
+	return skb->tail;
+}
+
+/* Copy some data bits from skb to kernel buffer. */
+
+int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+
+	if (offset > (int)skb->len-len)
+		goto fault;
+
+	/* Copy header. */
+	if ((copy = start-offset) > 0) {
+		if (copy > len)
+			copy = len;
+		memcpy(to, skb->data + offset, copy);
+		if ((len -= copy) == 0)
+			return 0;
+		offset += copy;
+		to += copy;
+	}
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			u8 *vaddr;
+
+			if (copy > len)
+				copy = len;
+
+			vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
+			memcpy(to, vaddr+skb_shinfo(skb)->frags[i].page_offset+
+			       offset-start, copy);
+			kunmap_skb_frag(vaddr);
+
+			if ((len -= copy) == 0)
+				return 0;
+			offset += copy;
+			to += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				if (copy > len)
+					copy = len;
+				if (skb_copy_bits(list, offset-start, to, copy))
+					goto fault;
+				if ((len -= copy) == 0)
+					return 0;
+				offset += copy;
+				to += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return 0;
+
+fault:
+	return -EFAULT;
+}
+
+/* Checksum skb data. */
+
+unsigned int skb_checksum(const struct sk_buff *skb, int offset, int len, unsigned int csum)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+	int pos = 0;
+
+	/* Checksum header. */
+	if ((copy = start-offset) > 0) {
+		if (copy > len)
+			copy = len;
+		csum = csum_partial(skb->data+offset, copy, csum);
+		if ((len -= copy) == 0)
+			return csum;
+		offset += copy;
+		pos = copy;
+	}
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			unsigned int csum2;
+			u8 *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap_skb_frag(frag);
+			csum2 = csum_partial(vaddr + frag->page_offset +
+					     offset-start, copy, 0);
+			kunmap_skb_frag(vaddr);
+			csum = csum_block_add(csum, csum2, pos);
+			if (!(len -= copy))
+				return csum;
+			offset += copy;
+			pos += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				unsigned int csum2;
+				if (copy > len)
+					copy = len;
+				csum2 = skb_checksum(list, offset-start, copy, 0);
+				csum = csum_block_add(csum, csum2, pos);
+				if ((len -= copy) == 0)
+					return csum;
+				offset += copy;
+				pos += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return csum;
+
+	BUG();
+	return csum;
+}
+
+/* Both of above in one bottle. */
+
+unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+	int pos = 0;
+
+	/* Copy header. */
+	if ((copy = start-offset) > 0) {
+		if (copy > len)
+			copy = len;
+		csum = csum_partial_copy_nocheck(skb->data+offset, to, copy, csum);
+		if ((len -= copy) == 0)
+			return csum;
+		offset += copy;
+		to += copy;
+		pos = copy;
+	}
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			unsigned int csum2;
+			u8 *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap_skb_frag(frag);
+			csum2 = csum_partial_copy_nocheck(vaddr + frag->page_offset +
+						      offset-start, to, copy, 0);
+			kunmap_skb_frag(vaddr);
+			csum = csum_block_add(csum, csum2, pos);
+			if (!(len -= copy))
+				return csum;
+			offset += copy;
+			to += copy;
+			pos += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			unsigned int csum2;
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				if (copy > len)
+					copy = len;
+				csum2 = skb_copy_and_csum_bits(list, offset-start, to, copy, 0);
+				csum = csum_block_add(csum, csum2, pos);
+				if ((len -= copy) == 0)
+					return csum;
+				offset += copy;
+				to += copy;
+				pos += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return csum;
+
+	BUG();
+	return csum;
+}
+
+
 #if 0
 /* 
  * 	Tune the memory allocator for a new MTU size.
@@ -463,7 +1157,7 @@
 void skb_add_mtu(int mtu)
 {
 	/* Must match allocation in alloc_skb */
-	mtu = ((mtu + 15) & ~15) + sizeof(atomic_t);
+	mtu = SKB_DATA_ALIGN(mtu) + sizeof(struct skb_shared_info);
 
 	kmem_add_cache_size(mtu);
 }
diff -urN linux-2.4.4-pre2/net/core/sock.c linux/net/core/sock.c
--- linux-2.4.4-pre2/net/core/sock.c	Sat Feb  3 11:26:44 2001
+++ linux/net/core/sock.c	Mon Dec  3 22:34:51 2001
@@ -7,7 +7,7 @@
  *		handler for protocols to use and generic option handler.
  *
  *
- * Version:	$Id: sock.c,v 1.104 2001/01/30 07:48:30 davem Exp $
+ * Version:	$Id: sock.c,v 1.109 2001/03/03 01:20:10 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -79,6 +79,7 @@
  *		Jay Schulist	:	Added SO_ATTACH_FILTER and SO_DETACH_FILTER.
  *		Andi Kleen	:	Add sock_kmalloc()/sock_kfree_s()
  *		Andi Kleen	:	Fix write_space callback
+ *		Chris Evans	:	Security fixes - signedness again
  *
  * To Fix:
  *
@@ -425,11 +426,13 @@
 		struct timeval tm;
 	} v;
 	
-	int lv=sizeof(int),len;
+	unsigned int lv=sizeof(int),len;
   	
   	if(get_user(len,optlen))
   		return -EFAULT;
-
+	if(len < 0)
+		return -EINVAL;
+		
   	switch(optname) 
   	{
 		case SO_DEBUG:		
@@ -727,6 +730,8 @@
 	clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
 	add_wait_queue(sk->sleep, &wait);
 	for (;;) {
+		if (!timeo)
+			break;
 		if (signal_pending(current))
 			break;
 		set_bit(SOCK_NOSPACE, &sk->socket->flags);
@@ -750,7 +755,7 @@
  */
 
 struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, 
-			unsigned long fallback, int noblock, int *errcode)
+			int noblock, int *errcode)
 {
 	int err;
 	struct sk_buff *skb;
@@ -780,15 +785,6 @@
 			goto failure;
 
 		if (atomic_read(&sk->wmem_alloc) < sk->sndbuf) {
-			if (fallback) {
-				/* The buffer get won't block, or use the atomic queue.
-			 	* It does produce annoying no free page messages still.
-			 	*/
-				skb = alloc_skb(size, GFP_BUFFER);
-				if (skb)
-					break;
-				try_size = fallback;
-			}
 			skb = alloc_skb(try_size, sk->allocation);
 			if (skb)
 				break;
@@ -1064,6 +1060,36 @@
 {
 	/* Mirror missing mmap method error code */
 	return -ENODEV;
+}
+
+ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags)
+{
+	ssize_t res;
+	struct msghdr msg;
+	struct iovec iov;
+	mm_segment_t old_fs;
+	char *kaddr;
+
+	kaddr = kmap(page);
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = flags;
+
+	iov.iov_base = kaddr + offset;
+	iov.iov_len = size;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	res = sock_sendmsg(sock, &msg, size);
+	set_fs(old_fs);
+
+	kunmap(page);
+	return res;
 }
 
 /*
diff -urN linux-2.4.4-pre2/net/decnet/af_decnet.c linux/net/decnet/af_decnet.c
--- linux-2.4.4-pre2/net/decnet/af_decnet.c	Sun Mar 25 18:14:25 2001
+++ linux/net/decnet/af_decnet.c	Mon Dec  3 22:34:51 2001
@@ -2128,6 +2128,7 @@
 	sendmsg:	dn_sendmsg,
 	recvmsg:	dn_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #ifdef CONFIG_SYSCTL
diff -urN linux-2.4.4-pre2/net/econet/af_econet.c linux/net/econet/af_econet.c
--- linux-2.4.4-pre2/net/econet/af_econet.c	Mon Oct 16 12:42:54 2000
+++ linux/net/econet/af_econet.c	Mon Dec  3 22:34:51 2001
@@ -297,7 +297,7 @@
 #ifdef CONFIG_ECONET_NATIVE
 		atomic_inc(&dev->refcnt);
 		
-		skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, 
+		skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 
 					  msg->msg_flags & MSG_DONTWAIT, &err);
 		if (skb==NULL)
 			goto out_unlock;
@@ -410,7 +410,7 @@
 	}
 
 	/* Get a skbuff (no data, just holds our cb information) */
-	if ((skb = sock_alloc_send_skb(sk, 0, 0, 
+	if ((skb = sock_alloc_send_skb(sk, 0, 
 			     msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
@@ -717,6 +717,7 @@
 	sendmsg:	econet_sendmsg,
 	recvmsg:	econet_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
diff -urN linux-2.4.4-pre2/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- linux-2.4.4-pre2/net/ipv4/af_inet.c	Fri Dec 29 14:07:24 2000
+++ linux/net/ipv4/af_inet.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.127 2000/12/22 19:51:50 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.129 2001/03/02 03:13:05 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -355,6 +355,8 @@
 	else
 		sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_WANT;
 
+	sk->protinfo.af_inet.id = 0;
+
 	sock_init_data(sock,sk);
 
 	sk->destruct = inet_sock_destruct;
@@ -934,7 +936,8 @@
 	getsockopt:	inet_getsockopt,
 	sendmsg:	inet_sendmsg,
 	recvmsg:	inet_recvmsg,
-	mmap:		sock_no_mmap
+	mmap:		sock_no_mmap,
+	sendpage:	tcp_sendpage
 };
 
 struct proto_ops inet_dgram_ops = {
@@ -955,6 +958,7 @@
 	sendmsg:	inet_sendmsg,
 	recvmsg:	inet_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 struct net_proto_family inet_family_ops = {
diff -urN linux-2.4.4-pre2/net/ipv4/arp.c linux/net/ipv4/arp.c
--- linux-2.4.4-pre2/net/ipv4/arp.c	Fri Feb  9 11:34:13 2001
+++ linux/net/ipv4/arp.c	Mon Dec  3 22:34:51 2001
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.90 2000/10/04 09:20:56 anton Exp $
+ * Version:	$Id: arp.c,v 1.96 2001/02/02 08:42:59 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
@@ -590,6 +590,13 @@
 	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
 		goto out_of_mem;
 
+	if (skb_is_nonlinear(skb)) {
+		if (skb_linearize(skb, GFP_ATOMIC) != 0)
+			goto freeskb;
+		arp = skb->nh.arph;
+		arp_ptr= (unsigned char *)(arp+1);
+	}
+
 	switch (dev_type) {
 	default:	
 		if (arp->ar_pro != __constant_htons(ETH_P_IP))
@@ -796,9 +803,10 @@
 	}
 
 out:
-	kfree_skb(skb);
 	if (in_dev)
 		in_dev_put(in_dev);
+freeskb:
+	kfree_skb(skb);
 out_of_mem:
 	return 0;
 }
diff -urN linux-2.4.4-pre2/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- linux-2.4.4-pre2/net/ipv4/icmp.c	Fri Aug  4 18:18:49 2000
+++ linux/net/ipv4/icmp.c	Mon Dec  3 22:34:51 2001
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@redhat.com>
  *
- *	Version: $Id: icmp.c,v 1.71 2000/08/02 06:01:48 davem Exp $
+ *	Version: $Id: icmp.c,v 1.73 2000/12/13 18:31:48 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -58,197 +58,6 @@
  *	- Should use skb_pull() instead of all the manual checking.
  *	  This would also greatly simply some upper layer error handlers. --AK
  *
- * RFC1122 (Host Requirements -- Comm. Layer) Status:
- * (boy, are there a lot of rules for ICMP)
- *  3.2.2 (Generic ICMP stuff)
- *   MUST discard messages of unknown type. (OK)
- *   MUST copy at least the first 8 bytes from the offending packet
- *     when sending ICMP errors. (OBSOLETE -- see RFC1812)
- *   MUST pass received ICMP errors up to protocol level. (OK)
- *   SHOULD send ICMP errors with TOS == 0. (OBSOLETE -- see RFC1812)
- *   MUST NOT send ICMP errors in reply to:
- *     ICMP errors (OK)
- *     Broadcast/multicast datagrams (OK)
- *     MAC broadcasts (OK)
- *     Non-initial fragments (OK)
- *     Datagram with a source address that isn't a single host. (OK)
- *  3.2.2.1 (Destination Unreachable)
- *   All the rules govern the IP layer, and are dealt with in ip.c, not here.
- *  3.2.2.2 (Redirect)
- *   Host SHOULD NOT send ICMP_REDIRECTs.  (OK)
- *   MUST update routing table in response to host or network redirects.
- *     (host OK, network OBSOLETE)
- *   SHOULD drop redirects if they're not from directly connected gateway
- *     (OK -- we drop it if it's not from our old gateway, which is close
- *      enough)
- * 3.2.2.3 (Source Quench)
- *   MUST pass incoming SOURCE_QUENCHs to transport layer (OK)
- *   Other requirements are dealt with at the transport layer.
- * 3.2.2.4 (Time Exceeded)
- *   MUST pass TIME_EXCEEDED to transport layer (OK)
- *   Other requirements dealt with at IP (generating TIME_EXCEEDED).
- * 3.2.2.5 (Parameter Problem)
- *   SHOULD generate these (OK)
- *   MUST pass received PARAMPROBLEM to transport layer (NOT YET)
- *   	[Solaris 2.X seems to assert EPROTO when this occurs] -- AC
- * 3.2.2.6 (Echo Request/Reply)
- *   MUST reply to ECHO_REQUEST, and give app to do ECHO stuff (OK, OK)
- *   MAY discard broadcast ECHO_REQUESTs. (Configurable with a sysctl.)
- *   MUST reply using same source address as the request was sent to.
- *     We're OK for unicast ECHOs, and it doesn't say anything about
- *     how to handle broadcast ones, since it's optional.
- *   MUST copy data from REQUEST to REPLY (OK)
- *     unless it would require illegal fragmentation (OK)
- *   MUST pass REPLYs to transport/user layer (OK)
- *   MUST use any provided source route (reversed) for REPLY. (NOT YET)
- * 3.2.2.7 (Information Request/Reply)
- *   MUST NOT implement this. (I guess that means silently discard...?) (OK)
- * 3.2.2.8 (Timestamp Request/Reply)
- *   MAY implement (OK)
- *   SHOULD be in-kernel for "minimum variability" (OK)
- *   MAY discard broadcast REQUESTs.  (OK, but see source for inconsistency)
- *   MUST reply using same source address as the request was sent to. (OK)
- *   MUST reverse source route, as per ECHO (NOT YET)
- *   MUST pass REPLYs to transport/user layer (requires RAW, just like 
- *	ECHO) (OK)
- *   MUST update clock for timestamp at least 15 times/sec (OK)
- *   MUST be "correct within a few minutes" (OK)
- * 3.2.2.9 (Address Mask Request/Reply)
- *   MAY implement (OK)
- *   MUST send a broadcast REQUEST if using this system to set netmask
- *     (OK... we don't use it)
- *   MUST discard received REPLYs if not using this system (OK)
- *   MUST NOT send replies unless specifically made agent for this sort
- *     of thing. (OK)
- *
- *
- * RFC 1812 (IPv4 Router Requirements) Status (even longer):
- *  4.3.2.1 (Unknown Message Types)
- *   MUST pass messages of unknown type to ICMP user iface or silently discard
- *     them (OK)
- *  4.3.2.2 (ICMP Message TTL)
- *   MUST initialize TTL when originating an ICMP message (OK)
- *  4.3.2.3 (Original Message Header)
- *   SHOULD copy as much data from the offending packet as possible without
- *     the length of the ICMP datagram exceeding 576 bytes (OK)
- *   MUST leave original IP header of the offending packet, but we're not
- *     required to undo modifications made (OK)
- *  4.3.2.4 (Original Message Source Address)
- *   MUST use one of addresses for the interface the orig. packet arrived as
- *     source address (OK)
- *  4.3.2.5 (TOS and Precedence)
- *   SHOULD leave TOS set to the same value unless the packet would be 
- *     discarded for that reason (OK)
- *   MUST use TOS=0 if not possible to leave original value (OK)
- *   MUST leave IP Precedence for Source Quench messages (OK -- not sent 
- *	at all)
- *   SHOULD use IP Precedence = 6 (Internetwork Control) or 7 (Network Control)
- *     for all other error messages (OK, we use 6)
- *   MAY allow configuration of IP Precedence (OK -- not done)
- *   MUST leave IP Precedence and TOS for reply messages (OK)
- *  4.3.2.6 (Source Route)
- *   SHOULD use reverse source route UNLESS sending Parameter Problem on source
- *     routing and UNLESS the packet would be immediately discarded (NOT YET)
- *  4.3.2.7 (When Not to Send ICMP Errors)
- *   MUST NOT send ICMP errors in reply to:
- *     ICMP errors (OK)
- *     Packets failing IP header validation tests unless otherwise noted (OK)
- *     Broadcast/multicast datagrams (OK)
- *     MAC broadcasts (OK)
- *     Non-initial fragments (OK)
- *     Datagram with a source address that isn't a single host. (OK)
- *  4.3.2.8 (Rate Limiting)
- *   SHOULD be able to limit error message rate (OK)
- *   SHOULD allow setting of rate limits (OK, in the source)
- *  4.3.3.1 (Destination Unreachable)
- *   All the rules govern the IP layer, and are dealt with in ip.c, not here.
- *  4.3.3.2 (Redirect)
- *   MAY ignore ICMP Redirects if running a routing protocol or if forwarding
- *     is enabled on the interface (OK -- ignores)
- *  4.3.3.3 (Source Quench)
- *   SHOULD NOT originate SQ messages (OK)
- *   MUST be able to limit SQ rate if originates them (OK as we don't 
- *	send them)
- *   MAY ignore SQ messages it receives (OK -- we don't)
- *  4.3.3.4 (Time Exceeded)
- *   Requirements dealt with at IP (generating TIME_EXCEEDED).
- *  4.3.3.5 (Parameter Problem)
- *   MUST generate these for all errors not covered by other messages (OK)
- *   MUST include original value of the value pointed by (OK)
- *  4.3.3.6 (Echo Request)
- *   MUST implement echo server function (OK)
- *   MUST process at ER of at least max(576, MTU) (OK)
- *   MAY reject broadcast/multicast ER's (We don't, but that's OK)
- *   SHOULD have a config option for silently ignoring ER's (OK)
- *   MUST have a default value for the above switch = NO (OK)
- *   MUST have application layer interface for Echo Request/Reply (OK)
- *   MUST reply using same source address as the request was sent to.
- *     We're OK for unicast ECHOs, and it doesn't say anything about
- *     how to handle broadcast ones, since it's optional.
- *   MUST copy data from Request to Reply (OK)
- *   SHOULD update Record Route / Timestamp options (??)
- *   MUST use reversed Source Route for Reply if possible (NOT YET)
- *  4.3.3.7 (Information Request/Reply)
- *   SHOULD NOT originate or respond to these (OK)
- *  4.3.3.8 (Timestamp / Timestamp Reply)
- *   MAY implement (OK)
- *   MUST reply to every Timestamp message received (OK)
- *   MAY discard broadcast REQUESTs.  (OK, but see source for inconsistency)
- *   MUST reply using same source address as the request was sent to. (OK)
- *   MUST use reversed Source Route if possible (NOT YET)
- *   SHOULD update Record Route / Timestamp options (??)
- *   MUST pass REPLYs to transport/user layer (requires RAW, just like 
- *	ECHO) (OK)
- *   MUST update clock for timestamp at least 16 times/sec (OK)
- *   MUST be "correct within a few minutes" (OK)
- * 4.3.3.9 (Address Mask Request/Reply)
- *   MUST have support for receiving AMRq and responding with AMRe (OK, 
- *	but only as a compile-time option)
- *   SHOULD have option for each interface for AMRe's, MUST default to 
- *	NO (NOT YET)
- *   MUST NOT reply to AMRq before knows the correct AM (OK)
- *   MUST NOT respond to AMRq with source address 0.0.0.0 on physical
- *    	interfaces having multiple logical i-faces with different masks
- *	(NOT YET)
- *   SHOULD examine all AMRe's it receives and check them (NOT YET)
- *   SHOULD log invalid AMRe's (AM+sender) (NOT YET)
- *   MUST NOT use contents of AMRe to determine correct AM (OK)
- *   MAY broadcast AMRe's after having configured address masks (OK -- doesn't)
- *   MUST NOT do broadcast AMRe's if not set by extra option (OK, no option)
- *   MUST use the { <NetPrefix>, -1 } form of broadcast addresses (OK)
- * 4.3.3.10 (Router Advertisement and Solicitations)
- *   MUST support router part of Router Discovery Protocol on all networks we
- *     support broadcast or multicast addressing. (OK -- done by gated)
- *   MUST have all config parameters with the respective defaults (OK)
- * 5.2.7.1 (Destination Unreachable)
- *   MUST generate DU's (OK)
- *   SHOULD choose a best-match response code (OK)
- *   SHOULD NOT generate Host Isolated codes (OK)
- *   SHOULD use Communication Administratively Prohibited when administratively
- *     filtering packets (NOT YET -- bug-to-bug compatibility)
- *   MAY include config option for not generating the above and silently
- *	discard the packets instead (OK)
- *   MAY include config option for not generating Precedence Violation and
- *     Precedence Cutoff messages (OK as we don't generate them at all)
- *   MUST use Host Unreachable or Dest. Host Unknown codes whenever other hosts
- *     on the same network might be reachable (OK -- no net unreach's at all)
- *   MUST use new form of Fragmentation Needed and DF Set messages (OK)
- * 5.2.7.2 (Redirect)
- *   MUST NOT generate network redirects (OK)
- *   MUST be able to generate host redirects (OK)
- *   SHOULD be able to generate Host+TOS redirects (NO as we don't use TOS)
- *   MUST have an option to use Host redirects instead of Host+TOS ones (OK as
- *     no Host+TOS Redirects are used)
- *   MUST NOT generate redirects unless forwarding to the same i-face and the
- *     dest. address is on the same subnet as the src. address and no source
- *     routing is in use. (OK)
- *   MUST NOT follow redirects when using a routing protocol (OK)
- *   MAY use redirects if not using a routing protocol (OK, compile-time option)
- *   MUST comply to Host Requirements when not acting as a router (OK)
- *  5.2.7.3 (Time Exceeded)
- *   MUST generate Time Exceeded Code 0 when discarding packet due to TTL=0 (OK)
- *   MAY have a per-interface option to disable origination of TE messages, but
- *     it MUST default to "originate" (OK -- we don't support it)
  */
 
 #include <linux/config.h>
@@ -282,6 +91,26 @@
 #define min(a,b)	((a)<(b)?(a):(b))
 
 /*
+ *	Build xmit assembly blocks
+ */
+
+struct icmp_bxm
+{
+	struct sk_buff *skb;
+	int offset;
+	int data_len;
+
+	unsigned int csum;
+	struct {
+		struct icmphdr icmph;
+		__u32	       times[3];
+	} data;
+	int head_len;
+	struct ip_options replyopts;
+	unsigned char  optbuf[40];
+};
+
+/*
  *	Statistics
  */
  
@@ -324,7 +153,7 @@
 {
 	unsigned long *output;		/* Address to increment on output */
 	unsigned long *input;		/* Address to increment on input */
-	void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len);
+	void (*handler)(struct sk_buff *skb);
 	short	error;		/* This ICMP is classed as an error message */
 	int *timeout; /* Rate limit */
 };
@@ -466,11 +295,12 @@
 {
 	struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;
 	struct icmphdr *icmph;
-	unsigned long csum;
+	unsigned int csum;
 
 	if (offset) {
-		icmp_param->csum=csum_partial_copy_nocheck(icmp_param->data_ptr+offset-sizeof(struct icmphdr), 
-				to, fraglen,icmp_param->csum);
+		icmp_param->csum=skb_copy_and_csum_bits(icmp_param->skb,
+							icmp_param->offset+(offset-icmp_param->head_len), 
+							to, fraglen,icmp_param->csum);
 		return 0;
 	}
 
@@ -479,22 +309,24 @@
 	 *	the other fragments first, so that we get the checksum
 	 *	for the whole packet here.
 	 */
-	csum = csum_partial_copy_nocheck((void *)&icmp_param->icmph,
-		to, sizeof(struct icmphdr), 
+	csum = csum_partial_copy_nocheck((void *)&icmp_param->data,
+		to, icmp_param->head_len,
 		icmp_param->csum);
-	csum = csum_partial_copy_nocheck(icmp_param->data_ptr,
-		to+sizeof(struct icmphdr),
-		fraglen-sizeof(struct icmphdr), csum);
+	csum=skb_copy_and_csum_bits(icmp_param->skb,
+				    icmp_param->offset, 
+				    to+icmp_param->head_len,
+				    fraglen-icmp_param->head_len,
+				    csum);
 	icmph=(struct icmphdr *)to;
 	icmph->checksum = csum_fold(csum);
 	return 0;
 }
- 
+
 /*
  *	Driving logic for building and sending ICMP messages.
  */
 
-void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
+static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
 {
 	struct sock *sk=icmp_socket->sk;
 	struct ipcm_cookie ipc;
@@ -507,9 +339,9 @@
 	if (icmp_xmit_lock_bh())
 		return;
 
-	icmp_param->icmph.checksum=0;
+	icmp_param->data.icmph.checksum=0;
 	icmp_param->csum=0;
-	icmp_out_count(icmp_param->icmph.type);
+	icmp_out_count(icmp_param->data.icmph.type);
 
 	sk->protinfo.af_inet.tos = skb->nh.iph->tos;
 	daddr = ipc.addr = rt->rt_src;
@@ -521,10 +353,10 @@
 	}
 	if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))
 		goto out;
-	if (icmpv4_xrlim_allow(rt, icmp_param->icmph.type, 
-			       icmp_param->icmph.code)) { 
+	if (icmpv4_xrlim_allow(rt, icmp_param->data.icmph.type, 
+			       icmp_param->data.icmph.code)) { 
 		ip_build_xmit(sk, icmp_glue_bits, icmp_param, 
-			      icmp_param->data_len+sizeof(struct icmphdr),
+			      icmp_param->data_len+icmp_param->head_len,
 			      &ipc, rt, MSG_DONTWAIT);
 	}
 	ip_rt_put(rt);
@@ -543,10 +375,9 @@
  *			MUST reply to only the first fragment.
  */
 
-void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info)
+void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info)
 {
 	struct iphdr *iph;
-	struct icmphdr *icmph;
 	int room;
 	struct icmp_bxm icmp_param;
 	struct rtable *rt = (struct rtable*)skb_in->dst;
@@ -558,10 +389,15 @@
 		return;
 
 	/*
-	 *	Find the original header
+	 *	Find the original header. It is expected to be valid, of course.
+	 *	Check this, icmp_send is called from the most obscure devices
+	 *	sometimes.
 	 */
 	iph = skb_in->nh.iph;
 
+	if ((u8*)iph < skb_in->head || (u8*)(iph+1) > skb_in->tail)
+		return;
+
 	/*
 	 *	No replies to physical multicast/broadcast
 	 */
@@ -589,17 +425,24 @@
 		 *	We are an error, check if we are replying to an ICMP error
 		 */
 		if (iph->protocol==IPPROTO_ICMP) {
-			icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
+			u8 inner_type;
+
+			if (skb_copy_bits(skb_in,
+					  skb_in->nh.raw + (iph->ihl<<2)
+					  + offsetof(struct icmphdr, type)
+					  - skb_in->data,
+					  &inner_type, 1))
+				return;
+
 			/*
 			 *	Assume any unknown ICMP type is an error. This isn't
 			 *	specified by the RFC, but think about it..
 			 */
-			if (icmph->type>NR_ICMP_TYPES || icmp_pointers[icmph->type].error)
+			if (inner_type>NR_ICMP_TYPES || icmp_pointers[inner_type].error)
 				return;
 		}
 	}
 
-
 	if (icmp_xmit_lock())
 		return;
 
@@ -625,12 +468,6 @@
 		((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) :
 			iph->tos;
 
-	/* XXX: use a more aggressive expire for routes created by 
-	 * this call (not longer than the rate limit timeout). 
-	 * It could be also worthwhile to not put them into ipv4
-	 * fast routing cache at first. Otherwise an attacker can
-	 * grow the routing table.
-	 */
 	if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0))
 		goto out;
 
@@ -642,13 +479,14 @@
 	 *	Prepare data for ICMP header.
 	 */
 
-	icmp_param.icmph.type=type;
-	icmp_param.icmph.code=code;
-	icmp_param.icmph.un.gateway = info;
-	icmp_param.icmph.checksum=0;
+	icmp_param.data.icmph.type=type;
+	icmp_param.data.icmph.code=code;
+	icmp_param.data.icmph.un.gateway = info;
+	icmp_param.data.icmph.checksum=0;
 	icmp_param.csum=0;
-	icmp_param.data_ptr=iph;
-	icmp_out_count(icmp_param.icmph.type);
+	icmp_param.skb=skb_in;
+	icmp_param.offset=skb_in->nh.raw - skb_in->data;
+	icmp_out_count(icmp_param.data.icmph.type);
 	icmp_socket->sk->protinfo.af_inet.tos = tos;
 	ipc.addr = iph->saddr;
 	ipc.opt = &icmp_param.replyopts;
@@ -669,10 +507,11 @@
 	room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen;
 	room -= sizeof(struct icmphdr);
 
-	icmp_param.data_len=(skb_in->tail-(u8*)iph);
+	icmp_param.data_len=skb_in->len-icmp_param.offset;
 	if (icmp_param.data_len > room)
 		icmp_param.data_len = room;
-	
+	icmp_param.head_len = sizeof(struct icmphdr);
+
 	ip_build_xmit(icmp_socket->sk, icmp_glue_bits, &icmp_param, 
 		icmp_param.data_len+sizeof(struct icmphdr),
 		&ipc, rt, MSG_DONTWAIT);
@@ -688,28 +527,35 @@
  *	Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH. 
  */
 
-static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_unreach(struct sk_buff *skb)
 {
 	struct iphdr *iph;
-	int hash;
+	struct icmphdr *icmph;
+	int hash, protocol;
 	struct inet_protocol *ipprot;
-	unsigned char *dp;
 	struct sock *raw_sk;
-	
+	u32 info = 0;
+
 	/*
 	 *	Incomplete header ?
 	 * 	Only checks for the IP header, there should be an
 	 *	additional check for longer headers in upper levels.
 	 */
 
-	if(len<sizeof(struct iphdr)) {
+	if (!pskb_may_pull(skb, sizeof(struct iphdr))) {
 		ICMP_INC_STATS_BH(IcmpInErrors);
 		return;
 	}
-		
-	iph = (struct iphdr *) (icmph + 1);
-	dp = (unsigned char*)iph;
-	
+
+	icmph = skb->h.icmph;
+	iph = (struct iphdr *) skb->data;
+
+	if (iph->ihl<5) {
+		/* Mangled header, drop. */
+		ICMP_INC_STATS_BH(IcmpInErrors);
+		return;
+	}
+
 	if(icmph->type==ICMP_DEST_UNREACH) {
 		switch(icmph->code & 15) {
 			case ICMP_NET_UNREACH:
@@ -726,11 +572,9 @@
 						printk(KERN_INFO "ICMP: %u.%u.%u.%u: fragmentation needed and DF set.\n",
 						       NIPQUAD(iph->daddr));
 				} else {
-					unsigned short new_mtu;
-					new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
-					if (!new_mtu) 
-						return;
-					icmph->un.frag.mtu = htons(new_mtu);
+					info = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
+					if (!info) 
+						goto out;
 				}
 				break;
 			case ICMP_SR_FAILED:
@@ -740,10 +584,12 @@
 			default:
 				break;
 		}
-		if (icmph->code>NR_ICMP_UNREACH) 
-			return;
+		if (icmph->code>NR_ICMP_UNREACH)
+			goto out;
+	} else if (icmph->type == ICMP_PARAMETERPROB) {
+		info = ntohl(icmph->un.gateway)>>24;
 	}
-	
+
 	/*
 	 *	Throw it at our lower layers
 	 *
@@ -751,7 +597,7 @@
 	 *	RFC 1122: 3.2.2.1 MUST pass ICMP unreach messages to the transport layer.
 	 *	RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer.
 	 */
-	 
+
 	/*
 	 *	Check the other end isnt violating RFC 1122. Some routers send
 	 *	bogus responses to broadcast frames. If you see this message
@@ -767,23 +613,33 @@
 			if (net_ratelimit())
 				printk(KERN_WARNING "%u.%u.%u.%u sent an invalid ICMP error to a broadcast.\n",
 			       	NIPQUAD(skb->nh.iph->saddr));
-			return; 
+			goto out;
 		}
 	}
 
+	/* Checkin full IP header plus 8 bytes of protocol to
+	 * avoid additional coding at protocol handlers.
+	 */
+	if (!pskb_may_pull(skb, iph->ihl*4+8))
+		goto out;
+
+	iph = (struct iphdr *) skb->data;
+	protocol = iph->protocol;
+
 	/*
 	 *	Deliver ICMP message to raw sockets. Pretty useless feature?
 	 */
 
 	/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
-	hash = iph->protocol & (MAX_INET_PROTOS - 1);
+	hash = protocol & (MAX_INET_PROTOS - 1);
 	read_lock(&raw_v4_lock);
 	if ((raw_sk = raw_v4_htable[hash]) != NULL) 
 	{
-		while ((raw_sk = __raw_v4_lookup(raw_sk, iph->protocol, iph->saddr,
+		while ((raw_sk = __raw_v4_lookup(raw_sk, protocol, iph->saddr,
 						 iph->daddr, skb->dev->ifindex)) != NULL) {
-			raw_err(raw_sk, skb);
+			raw_err(raw_sk, skb, info);
 			raw_sk = raw_sk->next;
+			iph = (struct iphdr *)skb->data;
 		}
 	}
 	read_unlock(&raw_v4_lock);
@@ -807,11 +663,12 @@
 		/* RFC1122: OK. Passes appropriate ICMP errors to the */
 		/* appropriate protocol layer (MUST), as per 3.2.2. */
 
-		if (iph->protocol == ipprot->protocol && ipprot->err_handler)
- 			ipprot->err_handler(skb, dp, len);
+		if (protocol == ipprot->protocol && ipprot->err_handler)
+ 			ipprot->err_handler(skb, info);
 
 		ipprot = nextip;
   	}
+out:
 }
 
 
@@ -819,24 +676,26 @@
  *	Handle ICMP_REDIRECT. 
  */
 
-static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_redirect(struct sk_buff *skb)
 {
 	struct iphdr *iph;
 	unsigned long ip;
 
-	if (len < sizeof(struct iphdr)) {
+	if (skb->len < sizeof(struct iphdr)) {
 		ICMP_INC_STATS_BH(IcmpInErrors);
 		return; 
 	}
-		
+
 	/*
 	 *	Get the copied header of the packet that caused the redirect
 	 */
-	 
-	iph = (struct iphdr *) (icmph + 1);
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		return;
+
+	iph = (struct iphdr *) skb->data;
 	ip = iph->daddr;
 
-	switch(icmph->code & 7) {
+	switch(skb->h.icmph->code & 7) {
 		case ICMP_REDIR_NET:
 		case ICMP_REDIR_NETTOS:
 			/*
@@ -846,7 +705,7 @@
 			 
 		case ICMP_REDIR_HOST:
 		case ICMP_REDIR_HOSTTOS:
-			ip_rt_redirect(skb->nh.iph->saddr, ip, icmph->un.gateway, iph->saddr, iph->tos, skb->dev);
+			ip_rt_redirect(skb->nh.iph->saddr, ip, skb->h.icmph->un.gateway, iph->saddr, iph->tos, skb->dev);
 			break;
 		default:
 			break;
@@ -862,15 +721,17 @@
  *	See also WRT handling of options once they are done and working.
  */
 
-static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_echo(struct sk_buff *skb)
 {
 	if (!sysctl_icmp_echo_ignore_all) {
 		struct icmp_bxm icmp_param;
 
-		icmp_param.icmph=*icmph;
-		icmp_param.icmph.type=ICMP_ECHOREPLY;
-		icmp_param.data_ptr=(icmph+1);
-		icmp_param.data_len=len;
+		icmp_param.data.icmph=*skb->h.icmph;
+		icmp_param.data.icmph.type=ICMP_ECHOREPLY;
+		icmp_param.skb=skb;
+		icmp_param.offset=0;
+		icmp_param.data_len=skb->len;
+		icmp_param.head_len=sizeof(struct icmphdr);
 		icmp_reply(&icmp_param, skb);
 	}
 }
@@ -883,34 +744,35 @@
  *		  MUST be updated at least at 15Hz.
  */
  
-static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_timestamp(struct sk_buff *skb)
 {
 	struct timeval tv;
-	__u32 times[3];		/* So the new timestamp works on ALPHA's.. */
 	struct icmp_bxm icmp_param;
 	
 	/*
 	 *	Too short.
 	 */
 	 
-	if(len<12) {
+	if(skb->len<4) {
 		ICMP_INC_STATS_BH(IcmpInErrors);
 		return;
 	}
-	
+
 	/*
 	 *	Fill in the current time as ms since midnight UT: 
 	 */
-	 
 	do_gettimeofday(&tv);
-	times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
-	times[2] = times[1];
-	memcpy((void *)&times[0], icmph+1, 4);		/* Incoming stamp */
-	icmp_param.icmph=*icmph;
-	icmp_param.icmph.type=ICMP_TIMESTAMPREPLY;
-	icmp_param.icmph.code=0;
-	icmp_param.data_ptr=&times;
-	icmp_param.data_len=12;
+	icmp_param.data.times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+	icmp_param.data.times[2] = icmp_param.data.times[1];
+	if (skb_copy_bits(skb, 0, &icmp_param.data.times[0], 4))
+		BUG();
+	icmp_param.data.icmph=*skb->h.icmph;
+	icmp_param.data.icmph.type=ICMP_TIMESTAMPREPLY;
+	icmp_param.data.icmph.code=0;
+	icmp_param.skb=skb;
+	icmp_param.offset=0;
+	icmp_param.data_len=0;
+	icmp_param.head_len=sizeof(struct icmphdr)+12;
 	icmp_reply(&icmp_param, skb);
 }
 
@@ -948,7 +810,7 @@
  * anyway...
  */
 
-static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_address(struct sk_buff *skb)
 {
 #if 0
 	if (net_ratelimit())
@@ -961,7 +823,7 @@
  *			loudly if an inconsistency is found.
  */
 
-static void icmp_address_reply(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_address_reply(struct sk_buff *skb)
 {
 	struct rtable *rt = (struct rtable*)skb->dst;
 	struct net_device *dev = skb->dev;
@@ -969,7 +831,7 @@
 	struct in_ifaddr *ifa;
 	u32 mask;
 
-	if (len < 4 || !(rt->rt_flags&RTCF_DIRECTSRC))
+	if (skb->len < 4 || !(rt->rt_flags&RTCF_DIRECTSRC))
 		return;
 
 	in_dev = in_dev_get(dev);
@@ -979,8 +841,8 @@
 	if (in_dev->ifa_list &&
 	    IN_DEV_LOG_MARTIANS(in_dev) &&
 	    IN_DEV_FORWARD(in_dev)) {
-
-		mask = *(u32*)&icmph[1];
+		if (skb_copy_bits(skb, 0, &mask, 4))
+			BUG();
 		for (ifa=in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
 			if (mask == ifa->ifa_mask && inet_ifa_match(rt->rt_src, ifa))
 				break;
@@ -994,7 +856,7 @@
 	in_dev_put(in_dev);
 }
 
-static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, int len)
+static void icmp_discard(struct sk_buff *skb)
 {
 }
 
@@ -1002,23 +864,36 @@
  *	Deal with incoming ICMP packets.
  */
  
-int icmp_rcv(struct sk_buff *skb, unsigned short len)
+int icmp_rcv(struct sk_buff *skb)
 {
 	struct icmphdr *icmph = skb->h.icmph;
 	struct rtable *rt = (struct rtable*)skb->dst;
 
 	ICMP_INC_STATS_BH(IcmpInMsgs);
 
+	switch (skb->ip_summed) {
+	case CHECKSUM_HW:
+		if ((u16)csum_fold(skb->csum) == 0)
+			break;
+		NETDEBUG(printk(KERN_DEBUG "icmp v4 hw csum failure\n"));
+	case CHECKSUM_NONE:
+		if ((u16)csum_fold(skb_checksum(skb, 0, skb->len, 0)))
+			goto error;
+	default:
+	}
+
+	if (!pskb_pull(skb, sizeof(struct icmphdr)))
+		goto error;
+
 	/*
 	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
 	 *
 	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently discarded.
 	 */
-	if(len < sizeof(struct icmphdr) ||
-	   ip_compute_csum((unsigned char *) icmph, len) ||
-	   icmph->type > NR_ICMP_TYPES)
+	if (icmph->type > NR_ICMP_TYPES)
 		goto error;
-	 
+
+
 	/*
 	 *	Parse the ICMP message 
 	 */
@@ -1042,9 +917,8 @@
   		}
 	}
 
-	len -= sizeof(struct icmphdr);
 	icmp_pointers[icmph->type].input[smp_processor_id()*2*sizeof(struct icmp_mib)/sizeof(unsigned long)]++;
-	(icmp_pointers[icmph->type].handler)(icmph, skb, len);
+	(icmp_pointers[icmph->type].handler)(skb);
 
 drop:
 	kfree_skb(skb);
diff -urN linux-2.4.4-pre2/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- linux-2.4.4-pre2/net/ipv4/igmp.c	Tue Jan  9 10:54:57 2001
+++ linux/net/ipv4/igmp.c	Mon Dec  3 22:34:51 2001
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.41 2000/08/31 23:39:12 davem Exp $
+ *	Version: $Id: igmp.c,v 1.45 2001/02/23 06:32:11 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -235,7 +235,7 @@
 	iph->saddr    = rt->rt_src;
 	iph->protocol = IPPROTO_IGMP;
 	iph->tot_len  = htons(IGMP_SIZE);
-	ip_select_ident(iph, &rt->u.dst);
+	ip_select_ident(iph, &rt->u.dst, NULL);
 	((u8*)&iph[1])[0] = IPOPT_RA;
 	((u8*)&iph[1])[1] = 4;
 	((u8*)&iph[1])[2] = 0;
@@ -341,23 +341,32 @@
 	read_unlock(&in_dev->lock);
 }
 
-int igmp_rcv(struct sk_buff *skb, unsigned short len)
+int igmp_rcv(struct sk_buff *skb)
 {
 	/* This basically follows the spec line by line -- see RFC1112 */
 	struct igmphdr *ih = skb->h.igmph;
 	struct in_device *in_dev = in_dev_get(skb->dev);
+	int len = skb->len;
 
 	if (in_dev==NULL) {
 		kfree_skb(skb);
 		return 0;
 	}
 
+	if (skb_is_nonlinear(skb)) {
+		if (skb_linearize(skb, GFP_ATOMIC) != 0) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+		ih = skb->h.igmph;
+	}
+
 	if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)) {
 		in_dev_put(in_dev);
 		kfree_skb(skb);
 		return 0;
 	}
-	
+
 	switch (ih->type) {
 	case IGMP_HOST_MEMBERSHIP_QUERY:
 		igmp_heard_query(in_dev, ih->code, ih->group);
@@ -372,7 +381,7 @@
 	case IGMP_PIM:
 #ifdef CONFIG_IP_PIMSM_V1
 		in_dev_put(in_dev);
-		return pim_rcv_v1(skb, len);
+		return pim_rcv_v1(skb);
 #endif
 	case IGMP_DVMRP:
 	case IGMP_TRACE:
diff -urN linux-2.4.4-pre2/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- linux-2.4.4-pre2/net/ipv4/ip_forward.c	Fri Oct 27 11:03:14 2000
+++ linux/net/ipv4/ip_forward.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		The IP forwarding functionality.
  *		
- * Version:	$Id: ip_forward.c,v 1.47 2000/10/24 22:54:26 davem Exp $
+ * Version:	$Id: ip_forward.c,v 1.48 2000/12/13 18:31:48 davem Exp $
  *
  * Authors:	see ip.c
  *
@@ -83,6 +83,8 @@
 
 	if (skb->pkt_type != PACKET_HOST)
 		goto drop;
+
+	skb->ip_summed = CHECKSUM_NONE;
 	
 	/*
 	 *	According to the RFC, we must first decrease the TTL field. If
@@ -116,10 +118,9 @@
 		ip_rt_send_redirect(skb);
 
 	/* We are about to mangle packet. Copy it! */
-	if ((skb = skb_cow(skb, dev2->hard_header_len)) == NULL)
-		return NET_RX_DROP;
+	if (skb_cow(skb, dev2->hard_header_len))
+		goto drop;
 	iph = skb->nh.iph;
-	opt = &(IPCB(skb)->opt);
 
 	/* Decrease ttl after skb cow done */
 	ip_decrease_ttl(iph);
diff -urN linux-2.4.4-pre2/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- linux-2.4.4-pre2/net/ipv4/ip_fragment.c	Sun Mar 25 18:14:25 2001
+++ linux/net/ipv4/ip_fragment.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.53 2000/12/08 17:15:53 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.57 2001/03/07 22:00:57 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -83,7 +83,8 @@
 	atomic_t	refcnt;
 	struct timer_list timer;	/* when will this queue expire?		*/
 	struct ipq	**pprev;
-	int		iif;		/* Device index - for icmp replies	*/
+	int		iif;
+	struct timeval	stamp;
 };
 
 /* Hash table. */
@@ -255,7 +256,6 @@
 
 	if ((qp->last_in&FIRST_IN) && qp->fragments != NULL) {
 		struct sk_buff *head = qp->fragments;
-
 		/* Send an ICMP "Fragment Reassembly Timeout" message. */
 		if ((head->dev = dev_get_by_index(qp->iif)) != NULL) {
 			icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
@@ -370,7 +370,6 @@
 /* Add new segment to existing queue. */
 static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
 	struct sk_buff *prev, *next;
 	int flags, offset;
 	int ihl, end;
@@ -378,14 +377,14 @@
 	if (qp->last_in & COMPLETE)
 		goto err;
 
-	offset = ntohs(iph->frag_off);
+ 	offset = ntohs(skb->nh.iph->frag_off);
 	flags = offset & ~IP_OFFSET;
 	offset &= IP_OFFSET;
 	offset <<= 3;		/* offset is in 8-byte chunks */
-	ihl = iph->ihl * 4;
+ 	ihl = skb->nh.iph->ihl * 4;
 
 	/* Determine the position of this fragment. */
-	end = offset + (ntohs(iph->tot_len) - ihl);
+ 	end = offset + skb->len - ihl;
 
 	/* Is this the final fragment? */
 	if ((flags & IP_MF) == 0) {
@@ -413,9 +412,10 @@
 	if (end == offset)
 		goto err;
 
-	/* Point into the IP datagram 'data' part. */
-	skb_pull(skb, (skb->nh.raw+ihl) - skb->data);
-	skb_trim(skb, end - offset);
+	if (pskb_pull(skb, ihl) == NULL)
+		goto err;
+	if (pskb_trim(skb, end-offset))
+		goto err;
 
 	/* Find out which fragments are in front and at the back of us
 	 * in the chain of fragments so far.  We must know where to put
@@ -439,7 +439,8 @@
 			offset += i;
 			if (end <= offset)
 				goto err;
-			skb_pull(skb, i);
+			if (!pskb_pull(skb, i))
+				goto err;
 			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
 				skb->ip_summed = CHECKSUM_NONE;
 		}
@@ -452,8 +453,9 @@
 			/* Eat head of the next overlapped fragment
 			 * and leave the loop. The next ones cannot overlap.
 			 */
+			if (!pskb_pull(next, i))
+				goto err;
 			FRAG_CB(next)->offset += i;
-			skb_pull(next, i);
 			qp->meat -= i;
 			if (next->ip_summed != CHECKSUM_UNNECESSARY)
 				next->ip_summed = CHECKSUM_NONE;
@@ -485,9 +487,10 @@
 	else
 		qp->fragments = skb;
 
-	if (skb->dev)
-		qp->iif = skb->dev->ifindex;
+ 	if (skb->dev)
+ 		qp->iif = skb->dev->ifindex;
 	skb->dev = NULL;
+	qp->stamp = skb->stamp;
 	qp->meat += skb->len;
 	atomic_add(skb->truesize, &ip_frag_mem);
 	if (offset == 0)
@@ -500,15 +503,10 @@
 }
 
 
-/* Build a new IP datagram from all its fragments.
- *
- * FIXME: We copy here because we lack an effective way of handling lists
- * of bits on input. Until the new skb data handling is in I'm not going
- * to touch this with a bargepole. 
- */
+/* Build a new IP datagram from all its fragments. */
+
 static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
 {
-	struct sk_buff *skb;
 	struct iphdr *iph;
 	struct sk_buff *fp, *head = qp->fragments;
 	int len;
@@ -526,61 +524,58 @@
 	if(len > 65535)
 		goto out_oversize;
 
-	skb = dev_alloc_skb(len);
-	if (!skb)
+	/* Head of list must not be cloned. */
+	if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
 		goto out_nomem;
 
-	/* Fill in the basic details. */
-	skb->mac.raw = skb->data;
-	skb->nh.raw = skb->data;
-	FRAG_CB(skb)->h = FRAG_CB(head)->h;
-	skb->ip_summed = head->ip_summed;
-	skb->csum = 0;
-
-	/* Copy the original IP headers into the new buffer. */
-	memcpy(skb_put(skb, ihlen), head->nh.iph, ihlen);
-
-	/* Copy the data portions of all fragments into the new buffer. */
-	for (fp=head; fp; fp = fp->next) {
-		memcpy(skb_put(skb, fp->len), fp->data, fp->len);
-
-		if (skb->ip_summed != fp->ip_summed)
-			skb->ip_summed = CHECKSUM_NONE;
-		else if (skb->ip_summed == CHECKSUM_HW)
-			skb->csum = csum_add(skb->csum, fp->csum);
-	}
-
-	skb->dst = dst_clone(head->dst);
-	skb->pkt_type = head->pkt_type;
-	skb->protocol = head->protocol;
-	skb->dev = dev;
-
-	/*
-	*  Clearly bogus, because security markings of the individual
-	*  fragments should have been checked for consistency before
-	*  gluing, and intermediate coalescing of fragments may have
-	*  taken place in ip_defrag() before ip_glue() ever got called.
-	*  If we're not going to do the consistency checking, we might
-	*  as well take the value associated with the first fragment.
-	*	--rct
-	*/
-	skb->security = head->security;
-
-#ifdef CONFIG_NETFILTER
-	/* Connection association is same as fragment (if any). */
-	skb->nfct = head->nfct;
-	nf_conntrack_get(skb->nfct);
-#ifdef CONFIG_NETFILTER_DEBUG
-	skb->nf_debug = head->nf_debug;
-#endif
-#endif
+	/* If the first fragment is fragmented itself, we split
+	 * it to two chunks: the first with data and paged part
+	 * and the second, holding only fragments. */
+	if (skb_shinfo(head)->frag_list) {
+		struct sk_buff *clone;
+		int i, plen = 0;
+
+		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
+			goto out_nomem;
+		clone->next = head->next;
+		head->next = clone;
+		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
+		skb_shinfo(head)->frag_list = NULL;
+		for (i=0; i<skb_shinfo(head)->nr_frags; i++)
+			plen += skb_shinfo(head)->frags[i].size;
+		clone->len = clone->data_len = head->data_len - plen;
+		head->data_len -= clone->len;
+		head->len -= clone->len;
+		clone->csum = 0;
+		clone->ip_summed = head->ip_summed;
+		atomic_add(clone->truesize, &ip_frag_mem);
+	}
 
-	/* Done with all fragments. Fixup the new IP header. */
-	iph = skb->nh.iph;
+	skb_shinfo(head)->frag_list = head->next;
+	skb_push(head, head->data - head->nh.raw);
+	atomic_sub(head->truesize, &ip_frag_mem);
+
+	for (fp=head->next; fp; fp = fp->next) {
+		head->data_len += fp->len;
+		head->len += fp->len;
+		if (head->ip_summed != fp->ip_summed)
+			head->ip_summed = CHECKSUM_NONE;
+		else if (head->ip_summed == CHECKSUM_HW)
+			head->csum = csum_add(head->csum, fp->csum);
+		head->truesize += fp->truesize;
+		atomic_sub(fp->truesize, &ip_frag_mem);
+	}
+
+	head->next = NULL;
+	head->dev = dev;
+	head->stamp = qp->stamp;
+
+	iph = head->nh.iph;
 	iph->frag_off = 0;
 	iph->tot_len = htons(len);
 	IP_INC_STATS_BH(IpReasmOKs);
-	return skb;
+	qp->fragments = NULL;
+	return head;
 
 out_nomem:
  	NETDEBUG(printk(KERN_ERR 
diff -urN linux-2.4.4-pre2/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
--- linux-2.4.4-pre2/net/ipv4/ip_gre.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv4/ip_gre.c	Mon Dec  3 22:34:51 2001
@@ -313,7 +313,7 @@
 }
 
 
-void ipgre_err(struct sk_buff *skb, unsigned char *dp, int len)
+void ipgre_err(struct sk_buff *skb, u32 info)
 {
 #ifndef I_WISH_WORLD_WERE_PERFECT
 
@@ -330,8 +330,8 @@
    by themself???
  */
 
-	struct iphdr *iph = (struct iphdr*)dp;
-	u16	     *p = (u16*)(dp+(iph->ihl<<2));
+	struct iphdr *iph = (struct iphdr*)skb->data;
+	u16	     *p = (u16*)(skb->data+(iph->ihl<<2));
 	int grehlen = (iph->ihl<<2) + 4;
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
@@ -350,7 +350,7 @@
 	}
 
 	/* If only 8 bytes returned, keyed message will be dropped here */
-	if (len < grehlen)
+	if (skb_headlen(skb) < grehlen)
 		return;
 
 	switch (type) {
@@ -559,17 +559,24 @@
 #endif
 }
 
-int ipgre_rcv(struct sk_buff *skb, unsigned short len)
+int ipgre_rcv(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
-	u8     *h = skb->h.raw;
-	u16    flags = *(u16*)h;
+	struct iphdr *iph;
+	u8     *h;
+	u16    flags;
 	u16    csum = 0;
 	u32    key = 0;
 	u32    seqno = 0;
 	struct ip_tunnel *tunnel;
 	int    offset = 4;
 
+	if (!pskb_may_pull(skb, 16))
+		goto drop_nolock;
+
+	iph = skb->nh.iph;
+	h = skb->data;
+	flags = *(u16*)h;
+
 	if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
 		/* - Version must be 0.
 		   - We do not support routing headers.
@@ -578,7 +585,16 @@
 			goto drop_nolock;
 
 		if (flags&GRE_CSUM) {
-			csum = ip_compute_csum(h, len);
+			if (skb->ip_summed == CHECKSUM_HW) {
+				csum = (u16)csum_fold(skb->csum);
+				if (csum)
+					skb->ip_summed = CHECKSUM_NONE;
+			}
+			if (skb->ip_summed == CHECKSUM_NONE) {
+				skb->csum = skb_checksum(skb, 0, skb->len, 0);
+				skb->ip_summed = CHECKSUM_HW;
+				csum = (u16)csum_fold(skb->csum);
+			}
 			offset += 4;
 		}
 		if (flags&GRE_KEY) {
@@ -594,9 +610,11 @@
 	read_lock(&ipgre_lock);
 	if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {
 		skb->mac.raw = skb->nh.raw;
-		skb->nh.raw = skb_pull(skb, h + offset - skb->data);
+		skb->nh.raw = __pskb_pull(skb, offset);
 		memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
-		skb->ip_summed = 0;
+		if (skb->ip_summed == CHECKSUM_HW)
+			skb->csum = csum_sub(skb->csum,
+					     csum_partial(skb->mac.raw, skb->nh.raw-skb->mac.raw, 0));
 		skb->protocol = *(u16*)(h + 2);
 		skb->pkt_type = PACKET_HOST;
 #ifdef CONFIG_NET_IPGRE_BROADCAST
@@ -1263,14 +1281,7 @@
 	printk(KERN_INFO "GRE over IPv4 tunneling driver\n");
 
 	ipgre_fb_tunnel_dev.priv = (void*)&ipgre_fb_tunnel;
-#ifdef MODULE
 	register_netdev(&ipgre_fb_tunnel_dev);
-#else
-	rtnl_lock();
-	register_netdevice(&ipgre_fb_tunnel_dev);
-	rtnl_unlock();
-#endif
-
 	inet_add_protocol(&ipgre_protocol);
 	return 0;
 }
diff -urN linux-2.4.4-pre2/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- linux-2.4.4-pre2/net/ipv4/ip_input.c	Mon Dec 11 12:37:04 2000
+++ linux/net/ipv4/ip_input.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.51 2000/12/08 17:15:53 davem Exp $
+ * Version:	$Id: ip_input.c,v 1.53 2000/12/18 19:01:50 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -207,8 +207,7 @@
 				skb2 = skb_clone(skb, GFP_ATOMIC);
 			if(skb2 != NULL) {
 				ret = 1;
-				ipprot->handler(skb2,
-						ntohs(iph->tot_len) - (iph->ihl * 4));
+				ipprot->handler(skb2);
 			}
 		}
 		ipprot = (struct inet_protocol *) ipprot->next;
@@ -219,18 +218,31 @@
 
 static inline int ip_local_deliver_finish(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
+	int ihl = skb->nh.iph->ihl*4;
 
 #ifdef CONFIG_NETFILTER_DEBUG
 	nf_debug_ip_local_deliver(skb);
 #endif /*CONFIG_NETFILTER_DEBUG*/
 
+	/* Pull out additionl 8 bytes to save some space in protocols. */
+	if (!pskb_may_pull(skb, ihl+8))
+		goto out;
+	__skb_pull(skb, ihl);
+
+#ifdef CONFIG_NETFILTER
+	/* Free reference early: we don't need it any more, and it may
+           hold ip_conntrack module loaded indefinitely. */
+	nf_conntrack_put(skb->nfct);
+	skb->nfct = NULL;
+#endif /*CONFIG_NETFILTER*/
+
         /* Point into the IP datagram, just past the header. */
-        skb->h.raw = skb->nh.raw + iph->ihl*4;
+        skb->h.raw = skb->data;
 
 	{
 		/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
-		int hash = iph->protocol & (MAX_INET_PROTOS - 1);
+		int protocol = skb->nh.iph->protocol;
+		int hash = protocol & (MAX_INET_PROTOS - 1);
 		struct sock *raw_sk = raw_v4_htable[hash];
 		struct inet_protocol *ipprot;
 		int flag;
@@ -239,23 +251,22 @@
 		 * don't care less
 		 */
 		if(raw_sk != NULL)
-			raw_sk = raw_v4_input(skb, iph, hash);
+			raw_sk = raw_v4_input(skb, skb->nh.iph, hash);
 
 		ipprot = (struct inet_protocol *) inet_protos[hash];
 		flag = 0;
 		if(ipprot != NULL) {
 			if(raw_sk == NULL &&
 			   ipprot->next == NULL &&
-			   ipprot->protocol == iph->protocol) {
+			   ipprot->protocol == protocol) {
 				int ret;
-				
+
 				/* Fast path... */
-				ret = ipprot->handler(skb, (ntohs(iph->tot_len) -
-							    (iph->ihl * 4)));
+				ret = ipprot->handler(skb);
 
 				return ret;
 			} else {
-				flag = ip_run_ipprot(skb, iph, ipprot, (raw_sk != NULL));
+				flag = ip_run_ipprot(skb, skb->nh.iph, ipprot, (raw_sk != NULL));
 			}
 		}
 
@@ -269,6 +280,7 @@
 			sock_put(raw_sk);
 		} else if (!flag) {		/* Free and report errors */
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);	
+out:
 			kfree_skb(skb);
 		}
 	}
@@ -281,13 +293,11 @@
  */ 
 int ip_local_deliver(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->nh.iph;
-
 	/*
 	 *	Reassemble IP fragments.
 	 */
 
-	if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+	if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
 		skb = ip_defrag(skb);
 		if (!skb)
 			return 0;
@@ -333,9 +343,8 @@
 		                                      --ANK (980813)
 		*/
 
-		skb = skb_cow(skb, skb_headroom(skb));
-		if (skb == NULL)
-			return NET_RX_DROP;
+		if (skb_cow(skb, skb_headroom(skb)))
+			goto drop;
 		iph = skb->nh.iph;
 
 		skb->ip_summed = 0;
@@ -387,6 +396,11 @@
 	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
 		goto out;
 
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		goto inhdr_error;
+
+	iph = skb->nh.iph;
+
 	/*
 	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
 	 *
@@ -398,9 +412,13 @@
 	 *	4.	Doesn't have a bogus length
 	 */
 
-	if (skb->len < sizeof(struct iphdr) || skb->len < (iph->ihl<<2))
+	if (iph->ihl < 5 || iph->version != 4)
 		goto inhdr_error; 
-	if (iph->ihl < 5 || iph->version != 4 || ip_fast_csum((u8 *)iph, iph->ihl) != 0)
+
+	if (!pskb_may_pull(skb, iph->ihl*4))
+		goto inhdr_error;
+
+	if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
 		goto inhdr_error; 
 
 	{
@@ -412,7 +430,11 @@
 		 * is IP we can trim to the true length of the frame.
 		 * Note this now means skb->len holds ntohs(iph->tot_len).
 		 */
-		__skb_trim(skb, len);
+		if (skb->len > len) {
+			__pskb_trim(skb, len);
+			if (skb->ip_summed == CHECKSUM_HW)
+				skb->ip_summed = CHECKSUM_NONE;
+		}
 	}
 
 	return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
diff -urN linux-2.4.4-pre2/net/ipv4/ip_nat_dumb.c linux/net/ipv4/ip_nat_dumb.c
--- linux-2.4.4-pre2/net/ipv4/ip_nat_dumb.c	Fri Oct 27 11:03:14 2000
+++ linux/net/ipv4/ip_nat_dumb.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		Dumb Network Address Translation.
  *
- * Version:	$Id: ip_nat_dumb.c,v 1.10 2000/10/24 22:54:26 davem Exp $
+ * Version:	$Id: ip_nat_dumb.c,v 1.11 2000/12/13 18:31:48 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -70,8 +70,14 @@
 			cksum  = (u16*)&((struct tcphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
 			if ((u8*)(cksum+1) > skb->tail)
 				goto truncated;
-			check  = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~(*cksum));
-			*cksum = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+			check = *cksum;
+			if (skb->ip_summed != CHECKSUM_HW)
+				check = ~check;
+			check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, check);
+			check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+			if (skb->ip_summed == CHECKSUM_HW)
+				check = ~check;
+			*cksum = check;
 			break;
 		case IPPROTO_UDP:
 			cksum  = (u16*)&((struct udphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
diff -urN linux-2.4.4-pre2/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- linux-2.4.4-pre2/net/ipv4/ip_output.c	Fri Oct 27 11:03:14 2000
+++ linux/net/ipv4/ip_output.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.87 2000/10/25 20:07:22 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.91 2001/03/29 06:25:55 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -95,7 +95,7 @@
 static int ip_dev_loopback_xmit(struct sk_buff *newskb)
 {
 	newskb->mac.raw = newskb->data;
-	skb_pull(newskb, newskb->nh.raw - newskb->data);
+	__skb_pull(newskb, newskb->nh.raw - newskb->data);
 	newskb->pkt_type = PACKET_LOOPBACK;
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
 	BUG_TRAP(newskb->dst);
@@ -141,7 +141,7 @@
 	iph->saddr    = rt->rt_src;
 	iph->protocol = sk->protocol;
 	iph->tot_len  = htons(skb->len);
-	ip_select_ident(iph, &rt->u.dst);
+	ip_select_ident(iph, &rt->u.dst, sk);
 	skb->nh.iph   = iph;
 
 	if (opt && opt->optlen) {
@@ -307,7 +307,7 @@
 	if (ip_dont_fragment(sk, &rt->u.dst))
 		iph->frag_off |= __constant_htons(IP_DF);
 
-	ip_select_ident(iph, &rt->u.dst);
+	ip_select_ident(iph, &rt->u.dst, sk);
 
 	/* Add an IP checksum. */
 	ip_send_check(iph);
@@ -328,7 +328,10 @@
 		kfree_skb(skb);
 		return -EMSGSIZE;
 	}
-	ip_select_ident(iph, &rt->u.dst);
+	ip_select_ident(iph, &rt->u.dst, sk);
+	if (skb->ip_summed == CHECKSUM_HW &&
+	    (skb = skb_checksum_help(skb)) == NULL)
+		return -ENOMEM;
 	return ip_fragment(skb, skb->dst->output);
 }
 
@@ -358,6 +361,7 @@
 				    sk->bound_dev_if))
 			goto no_route;
 		__sk_dst_set(sk, &rt->u.dst);
+		sk->route_caps = rt->u.dst.dev->features;
 	}
 	skb->dst = dst_clone(&rt->u.dst);
 
@@ -425,7 +429,7 @@
 	int err;
 	int offset, mf;
 	int mtu;
-	u16 id = 0;
+	u16 id;
 
 	int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
 	int nfrags=0;
@@ -495,6 +499,8 @@
 	 *	Begin outputting the bytes.
 	 */
 
+	id = (sk ? sk->protinfo.af_inet.id++ : 0);
+
 	do {
 		char *data;
 		struct sk_buff * skb;
@@ -503,7 +509,7 @@
 		 *	Get the memory we require with some space left for alignment.
 		 */
 
-		skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &err);
+		skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, flags&MSG_DONTWAIT, &err);
 		if (skb == NULL)
 			goto error;
 
@@ -659,7 +665,7 @@
 	int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
 
 	skb = sock_alloc_send_skb(sk, length+hh_len+15,
-				  0, flags&MSG_DONTWAIT, &err);
+				  flags&MSG_DONTWAIT, &err);
 	if(skb==NULL)
 		goto error; 
 	skb_reserve(skb, hh_len);
@@ -677,7 +683,7 @@
 		iph->tot_len = htons(length);
 		iph->frag_off = df;
 		iph->ttl=sk->protinfo.af_inet.mc_ttl;
-		ip_select_ident(iph, &rt->u.dst);
+		ip_select_ident(iph, &rt->u.dst, sk);
 		if (rt->rt_type != RTN_MULTICAST)
 			iph->ttl=sk->protinfo.af_inet.ttl;
 		iph->protocol=sk->protocol;
@@ -722,8 +728,8 @@
 int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
 {
 	struct iphdr *iph;
-	unsigned char *raw;
-	unsigned char *ptr;
+	int raw = 0;
+	int ptr;
 	struct net_device *dev;
 	struct sk_buff *skb2;
 	unsigned int mtu, hlen, left, len; 
@@ -738,17 +744,16 @@
 	 *	Point into the IP datagram header.
 	 */
 
-	raw = skb->nh.raw;
-	iph = (struct iphdr*)raw;
+	iph = skb->nh.iph;
 
 	/*
 	 *	Setup starting values.
 	 */
 
 	hlen = iph->ihl * 4;
-	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
+	left = skb->len - hlen;		/* Space per frame */
 	mtu = rt->u.dst.pmtu - hlen;	/* Size of data space */
-	ptr = raw + hlen;			/* Where to start from */
+	ptr = raw + hlen;		/* Where to start from */
 
 	/*
 	 *	Fragment the datagram.
@@ -806,12 +811,13 @@
 		 *	Copy the packet header into the new buffer.
 		 */
 
-		memcpy(skb2->nh.raw, raw, hlen);
+		memcpy(skb2->nh.raw, skb->data, hlen);
 
 		/*
 		 *	Copy a block of the IP datagram.
 		 */
-		memcpy(skb2->h.raw, ptr, len);
+		if (skb_copy_bits(skb, ptr, skb2->h.raw, len))
+			BUG();
 		left -= len;
 
 		/*
@@ -828,6 +834,9 @@
 		 */
 		if (offset == 0)
 			ip_options_fragment(skb);
+
+		/* Copy the flags to each fragment. */
+		IPCB(skb2)->flags = IPCB(skb)->flags;
 
 		/*
 		 *	Added AC : If we are fragmenting a fragment that's not the
diff -urN linux-2.4.4-pre2/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- linux-2.4.4-pre2/net/ipv4/ip_sockglue.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv4/ip_sockglue.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		The IP to API glue.
  *		
- * Version:	$Id: ip_sockglue.c,v 1.54 2000/11/28 13:34:56 davem Exp $
+ * Version:	$Id: ip_sockglue.c,v 1.56 2001/02/18 09:07:58 davem Exp $
  *
  * Authors:	see ip.c
  *
@@ -257,9 +257,8 @@
 	serr->port = port;
 
 	skb->h.raw = payload;
-	skb_pull(skb, payload - skb->data);
-
-	if (sock_queue_err_skb(sk, skb))
+	if (!skb_pull(skb, payload - skb->data) ||
+	    sock_queue_err_skb(sk, skb))
 		kfree_skb(skb);
 }
 
@@ -292,7 +291,7 @@
 	serr->port = port;
 
 	skb->h.raw = skb->tail;
-	skb_pull(skb, skb->tail - skb->data);
+	__skb_pull(skb, skb->tail - skb->data);
 
 	if (sock_queue_err_skb(sk, skb))
 		kfree_skb(skb);
@@ -323,7 +322,7 @@
 		msg->msg_flags |= MSG_TRUNC;
 		copied = len;
 	}
-	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	if (err)
 		goto out_free_skb;
 
@@ -333,9 +332,10 @@
 
 	sin = (struct sockaddr_in *)msg->msg_name;
 	if (sin) {
-		sin->sin_family = AF_INET; 
+		sin->sin_family = AF_INET;
 		sin->sin_addr.s_addr = *(u32*)(skb->nh.raw + serr->addr_offset);
-		sin->sin_port = serr->port; 
+		sin->sin_port = serr->port;
+		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
 	}
 
 	memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
@@ -344,6 +344,8 @@
 	if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
 		sin->sin_family = AF_INET;
 		sin->sin_addr.s_addr = skb->nh.iph->saddr;
+		sin->sin_port = 0;
+		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
 		if (sk->protinfo.af_inet.cmsg_flags)
 			ip_cmsg_recv(msg, skb);
 	}
@@ -661,7 +663,9 @@
 
 	if(get_user(len,optlen))
 		return -EFAULT;
-
+	if(len < 0)
+		return -EINVAL;
+		
 	lock_sock(sk);
 
 	switch(optname)	{
diff -urN linux-2.4.4-pre2/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- linux-2.4.4-pre2/net/ipv4/ipip.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv4/ipip.c	Mon Dec  3 22:34:51 2001
@@ -1,7 +1,7 @@
 /*
  *	Linux NET3:	IP/IP protocol decoder. 
  *
- *	Version: $Id: ipip.c,v 1.41 2000/11/28 13:13:27 davem Exp $
+ *	Version: $Id: ipip.c,v 1.44 2001/03/29 06:29:09 davem Exp $
  *
  *	Authors:
  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
@@ -123,11 +123,13 @@
 static int ipip_tunnel_init(struct net_device *dev);
 
 static struct net_device ipip_fb_tunnel_dev = {
-	"tunl0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, ipip_fb_tunnel_init,
+	name:	"tunl0",
+	init:	ipip_fb_tunnel_init,
 };
 
 static struct ip_tunnel ipip_fb_tunnel = {
-	NULL, &ipip_fb_tunnel_dev, {0, }, 0, 0, 0, 0, 0, 0, 0, {"tunl0", }
+	dev:	&ipip_fb_tunnel_dev,
+	parms:	{ name:	"tunl0", }
 };
 
 static struct ip_tunnel *tunnels_r_l[HASH_SIZE];
@@ -281,14 +283,12 @@
 		write_lock_bh(&ipip_lock);
 		tunnels_wc[0] = NULL;
 		write_unlock_bh(&ipip_lock);
-		dev_put(dev);
-	} else {
+	} else
 		ipip_tunnel_unlink((struct ip_tunnel*)dev->priv);
-		dev_put(dev);
-	}
+	dev_put(dev);
 }
 
-void ipip_err(struct sk_buff *skb, unsigned char *dp, int len)
+void ipip_err(struct sk_buff *skb, u32 info)
 {
 #ifndef I_WISH_WORLD_WERE_PERFECT
 
@@ -296,14 +296,11 @@
    8 bytes of packet payload. It means, that precise relaying of
    ICMP in the real Internet is absolutely infeasible.
  */
-	struct iphdr *iph = (struct iphdr*)dp;
+	struct iphdr *iph = (struct iphdr*)skb->data;
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
 	struct ip_tunnel *t;
 
-	if (len < sizeof(struct iphdr))
-		return;
-
 	switch (type) {
 	default:
 	case ICMP_PARAMETERPROB:
@@ -473,17 +470,19 @@
 		IP_ECN_set_ce(iph);
 }
 
-int ipip_rcv(struct sk_buff *skb, unsigned short len)
+int ipip_rcv(struct sk_buff *skb)
 {
 	struct iphdr *iph;
 	struct ip_tunnel *tunnel;
 
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		goto out;
+
 	iph = skb->nh.iph;
 	skb->mac.raw = skb->nh.raw;
-	skb->nh.raw = skb_pull(skb, skb->h.raw - skb->data);
+	skb->nh.raw = skb->data;
 	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
 	skb->protocol = __constant_htons(ETH_P_IP);
-	skb->ip_summed = 0;
 	skb->pkt_type = PACKET_HOST;
 
 	read_lock(&ipip_lock);
@@ -508,6 +507,7 @@
 	read_unlock(&ipip_lock);
 
 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+out:
 	kfree_skb(skb);
 	return 0;
 }
@@ -873,44 +873,34 @@
 }
 
 static struct inet_protocol ipip_protocol = {
-  ipip_rcv,             /* IPIP handler          */
-  ipip_err,             /* TUNNEL error control */
-  0,                    /* next                 */
-  IPPROTO_IPIP,         /* protocol ID          */
-  0,                    /* copy                 */
-  NULL,                 /* data                 */
-  "IPIP"                /* name                 */
+	handler:	ipip_rcv,
+	err_handler:	ipip_err,
+	protocol:	IPPROTO_IPIP,
+	name:		"IPIP"
 };
 
-#ifdef MODULE
-int init_module(void) 
-#else
+static const char banner[] __initdata =
+	KERN_INFO "IPv4 over IPv4 tunneling driver\n";
+
 int __init ipip_init(void)
-#endif
 {
-	printk(KERN_INFO "IPv4 over IPv4 tunneling driver\n");
+	printk(banner);
 
 	ipip_fb_tunnel_dev.priv = (void*)&ipip_fb_tunnel;
-#ifdef MODULE
 	register_netdev(&ipip_fb_tunnel_dev);
-#else
-	rtnl_lock();
-	register_netdevice(&ipip_fb_tunnel_dev);
-	rtnl_unlock();
-#endif
-
 	inet_add_protocol(&ipip_protocol);
 	return 0;
 }
 
-#ifdef MODULE
-
-void cleanup_module(void)
+static void __exit ipip_fini(void)
 {
 	if ( inet_del_protocol(&ipip_protocol) < 0 )
 		printk(KERN_INFO "ipip close: can't remove protocol\n");
 
-	unregister_netdevice(&ipip_fb_tunnel_dev);
+	unregister_netdev(&ipip_fb_tunnel_dev);
 }
 
+#ifdef MODULE
+module_init(ipip_init);
 #endif
+module_exit(ipip_fini);
diff -urN linux-2.4.4-pre2/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- linux-2.4.4-pre2/net/ipv4/ipmr.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv4/ipmr.c	Mon Dec  3 22:34:51 2001
@@ -9,7 +9,7 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Version: $Id: ipmr.c,v 1.55 2000/11/28 13:13:27 davem Exp $
+ *	Version: $Id: ipmr.c,v 1.59 2001/02/23 06:32:11 davem Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -980,6 +980,9 @@
 		return -EFAULT;
 
 	olr=min(olr,sizeof(int));
+	if(olr<0)
+		return -EINVAL;
+		
 	if(put_user(olr,optlen))
 		return -EFAULT;
 	if(optname==MRT_VERSION)
@@ -1092,7 +1095,7 @@
 	iph->protocol	=	IPPROTO_IPIP;
 	iph->ihl	=	5;
 	iph->tot_len	=	htons(skb->len);
-	ip_select_ident(iph, skb->dst);
+	ip_select_ident(iph, skb->dst, NULL);
 	ip_send_check(iph);
 
 	skb->h.ipiph = skb->nh.iph;
@@ -1383,14 +1386,22 @@
  * Handle IGMP messages of PIMv1
  */
 
-int pim_rcv_v1(struct sk_buff * skb, unsigned short len)
+int pim_rcv_v1(struct sk_buff * skb)
 {
 	struct igmphdr *pim = (struct igmphdr*)skb->h.raw;
 	struct iphdr   *encap;
 	struct net_device  *reg_dev = NULL;
 
+	if (skb_is_nonlinear(skb)) {
+		if (skb_linearize(skb, GFP_ATOMIC) != 0) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+		pim = (struct igmphdr*)skb->h.raw;
+	}
+
         if (!mroute_do_pim ||
-	    len < sizeof(*pim) + sizeof(*encap) ||
+	    skb->len < sizeof(*pim) + sizeof(*encap) ||
 	    pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) {
 		kfree_skb(skb);
                 return -EINVAL;
@@ -1405,7 +1416,7 @@
 	 */
 	if (!MULTICAST(encap->daddr) ||
 	    ntohs(encap->tot_len) == 0 ||
-	    ntohs(encap->tot_len) + sizeof(*pim) > len) {
+	    ntohs(encap->tot_len) + sizeof(*pim) > skb->len) {
 		kfree_skb(skb);
 		return -EINVAL;
 	}
@@ -1445,17 +1456,25 @@
 #endif
 
 #ifdef CONFIG_IP_PIMSM_V2
-int pim_rcv(struct sk_buff * skb, unsigned short len)
+int pim_rcv(struct sk_buff * skb)
 {
 	struct pimreghdr *pim = (struct pimreghdr*)skb->h.raw;
 	struct iphdr   *encap;
 	struct net_device  *reg_dev = NULL;
 
-        if (len < sizeof(*pim) + sizeof(*encap) ||
+	if (skb_is_nonlinear(skb)) {
+		if (skb_linearize(skb, GFP_ATOMIC) != 0) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+		pim = (struct pimreghdr*)skb->h.raw;
+	}
+
+        if (skb->len < sizeof(*pim) + sizeof(*encap) ||
 	    pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
 	    (pim->flags&PIM_NULL_REGISTER) ||
 	    (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
-	     ip_compute_csum((void *)pim, len))) {
+	     ip_compute_csum((void *)pim, skb->len))) {
 		kfree_skb(skb);
                 return -EINVAL;
         }
@@ -1464,7 +1483,7 @@
 	encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr));
 	if (!MULTICAST(encap->daddr) ||
 	    ntohs(encap->tot_len) == 0 ||
-	    ntohs(encap->tot_len) + sizeof(*pim) > len) {
+	    ntohs(encap->tot_len) + sizeof(*pim) > skb->len) {
 		kfree_skb(skb);
 		return -EINVAL;
 	}
diff -urN linux-2.4.4-pre2/net/ipv4/netfilter/ip_conntrack_core.c linux/net/ipv4/netfilter/ip_conntrack_core.c
--- linux-2.4.4-pre2/net/ipv4/netfilter/ip_conntrack_core.c	Mon Jan 22 13:30:21 2001
+++ linux/net/ipv4/netfilter/ip_conntrack_core.c	Mon Dec  3 22:34:51 2001
@@ -894,7 +894,12 @@
 	if (!skb) {
 		if (sk) sock_put(sk);
 		return skb;
+	} else if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		if (sk) sock_put(sk);
+		return NULL;
 	}
+
 	if (sk) {
 		skb_set_owner_w(skb, sk);
 		sock_put(sk);
@@ -986,7 +991,7 @@
 		return -ENOPROTOOPT;
 	}
 
-	if (*len != sizeof(struct sockaddr_in)) {
+	if ((unsigned int) *len < sizeof(struct sockaddr_in)) {
 		DEBUGP("SO_ORIGINAL_DST: len %u not %u\n",
 		       *len, sizeof(struct sockaddr_in));
 		return -EINVAL;
diff -urN linux-2.4.4-pre2/net/ipv4/netfilter/ip_fw_compat.c linux/net/ipv4/netfilter/ip_fw_compat.c
--- linux-2.4.4-pre2/net/ipv4/netfilter/ip_fw_compat.c	Fri Feb  9 11:34:13 2001
+++ linux/net/ipv4/netfilter/ip_fw_compat.c	Mon Dec  3 22:34:51 2001
@@ -96,7 +96,8 @@
 
 	/* Assume worse case: any hook could change packet */
 	(*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
-	(*pskb)->ip_summed = CHECKSUM_NONE;
+	if ((*pskb)->ip_summed == CHECKSUM_HW)
+		(*pskb)->ip_summed = CHECKSUM_NONE;
 
 	switch (hooknum) {
 	case NF_IP_PRE_ROUTING:
diff -urN linux-2.4.4-pre2/net/ipv4/netfilter/ip_nat_standalone.c linux/net/ipv4/netfilter/ip_nat_standalone.c
--- linux-2.4.4-pre2/net/ipv4/netfilter/ip_nat_standalone.c	Sat Feb  3 11:45:55 2001
+++ linux/net/ipv4/netfilter/ip_nat_standalone.c	Mon Dec  3 22:34:51 2001
@@ -64,7 +64,7 @@
 	(*pskb)->nfcache |= NFC_UNKNOWN;
 
 	/* If we had a hardware checksum before, it's now invalid */
-	if ((*pskb)->pkt_type != PACKET_LOOPBACK)
+	if ((*pskb)->ip_summed == CHECKSUM_HW)
 		(*pskb)->ip_summed = CHECKSUM_NONE;
 
 	ct = ip_conntrack_get(*pskb, &ctinfo);
diff -urN linux-2.4.4-pre2/net/ipv4/netfilter/ipchains_core.c linux/net/ipv4/netfilter/ipchains_core.c
--- linux-2.4.4-pre2/net/ipv4/netfilter/ipchains_core.c	Thu Apr 13 17:19:57 2000
+++ linux/net/ipv4/netfilter/ipchains_core.c	Mon Dec  3 22:34:51 2001
@@ -871,7 +871,7 @@
 	if (chainptr->chain == NULL) {
 
 		/* If pointer writes are atomic then turning off
-		 * interupts is not necessary. */
+		 * interrupts is not necessary. */
 		chainptr->chain = rule;
 		if (rule->branch) rule->branch->refcount++;
 		return 0;
diff -urN linux-2.4.4-pre2/net/ipv4/raw.c linux/net/ipv4/raw.c
--- linux-2.4.4-pre2/net/ipv4/raw.c	Fri Feb  9 11:29:44 2001
+++ linux/net/ipv4/raw.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		RAW - implementation of IP "raw" sockets.
  *
- * Version:	$Id: raw.c,v 1.56 2000/11/28 13:38:38 davem Exp $
+ * Version:	$Id: raw.c,v 1.60 2001/02/23 06:32:11 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -170,11 +170,10 @@
 	return sk;
 }
 
-void raw_err (struct sock *sk, struct sk_buff *skb)
+void raw_err (struct sock *sk, struct sk_buff *skb, u32 info)
 {
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
-	u32 info = 0;
 	int err = 0;
 	int harderr = 0;
 
@@ -195,7 +194,6 @@
 		return;
 	case ICMP_PARAMETERPROB:
 		err = EPROTO;
-		info = ntohl(skb->h.icmph->un.gateway)>>24;
 		harderr = 1;
 		break;
 	case ICMP_DEST_UNREACH:
@@ -207,12 +205,17 @@
 		if (code == ICMP_FRAG_NEEDED) {
 			harderr = (sk->protinfo.af_inet.pmtudisc != IP_PMTUDISC_DONT);
 			err = EMSGSIZE;
-			info = ntohs(skb->h.icmph->un.frag.mtu);
 		}
 	}
 
-	if (sk->protinfo.af_inet.recverr)
-		ip_icmp_error(sk, skb, err, 0, info, (u8 *)(skb->h.icmph + 1));
+	if (sk->protinfo.af_inet.recverr) {
+		struct iphdr *iph = (struct iphdr*)skb->data;
+		u8 *payload = skb->data+(iph->ihl<<2);
+
+		if (sk->protinfo.af_inet.hdrincl)
+			payload = skb->data;
+		ip_icmp_error(sk, skb, err, 0, info, payload);
+	}
 
 	if (sk->protinfo.af_inet.recverr || harderr) {
 		sk->err = err;
@@ -235,18 +238,9 @@
 	return NET_RX_SUCCESS;
 }
 
-/*
- *	This should be the easiest of all, all we do is
- *	copy it into a buffer. All demultiplexing is done
- *	in ip.c
- */
-
 int raw_rcv(struct sock *sk, struct sk_buff *skb)
 {
-	/* Now we need to copy this into memory. */
-	skb_trim(skb, ntohs(skb->nh.iph->tot_len));
-	
-	skb->h.raw = skb->nh.raw;
+	skb_push(skb, skb->data-skb->nh.raw);
 
 	raw_rcv_skb(sk, skb);
 	return 0;
@@ -296,7 +290,7 @@
 	 	 *	ip_build_xmit clean (well less messy).
 		 */
 		if (!iph->id)
-			ip_select_ident(iph, rfh->dst);
+			ip_select_ident(iph, rfh->dst, NULL);
 		iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl);
 	}
 	return 0;
@@ -498,7 +492,7 @@
 		msg->msg_flags |= MSG_TRUNC;
 		copied = len;
 	}
-	
+
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	if (err)
 		goto done;
@@ -540,6 +534,8 @@
 
 	if (get_user(len,optlen))
 		return -EFAULT;
+	if (len < 0)
+		return -EINVAL;
 	if (len > sizeof(struct icmp_filter))
 		len = sizeof(struct icmp_filter);
 	if (put_user(len, optlen))
diff -urN linux-2.4.4-pre2/net/ipv4/route.c linux/net/ipv4/route.c
--- linux-2.4.4-pre2/net/ipv4/route.c	Tue Oct 10 10:33:52 2000
+++ linux/net/ipv4/route.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.91 2000/10/03 07:29:00 anton Exp $
+ * Version:	$Id: route.c,v 1.93 2001/02/22 01:03:05 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -115,7 +115,7 @@
 int ip_rt_gc_elasticity = 8;
 int ip_rt_mtu_expires = 10*60*HZ;
 int ip_rt_min_pmtu = 512+20+20;
-int ip_rt_min_advmss = 536;
+int ip_rt_min_advmss = 256;
 
 static unsigned long rt_deadline;
 
@@ -667,7 +667,7 @@
 
 	spin_lock_bh(&ip_fb_id_lock);
 	salt = secure_ip_id(ip_fallback_id ^ iph->daddr);
-	iph->id = salt & 0xFFFF;
+	iph->id = htons(salt & 0xFFFF);
 	ip_fallback_id = salt;
 	spin_unlock_bh(&ip_fb_id_lock);
 }
@@ -684,7 +684,7 @@
 		   so that we need not to grab a lock to dereference it.
 		 */
 		if (rt->peer) {
-			iph->id = inet_getid(rt->peer);
+			iph->id = htons(inet_getid(rt->peer));
 			return;
 		}
 	} else {
diff -urN linux-2.4.4-pre2/net/ipv4/syncookies.c linux/net/ipv4/syncookies.c
--- linux-2.4.4-pre2/net/ipv4/syncookies.c	Fri Feb  9 11:29:44 2001
+++ linux/net/ipv4/syncookies.c	Mon Dec  3 22:34:51 2001
@@ -9,7 +9,7 @@
  *      as published by the Free Software Foundation; either version
  *      2 of the License, or (at your option) any later version.
  * 
- *  $Id: syncookies.c,v 1.12 2000/07/26 01:04:19 davem Exp $
+ *  $Id: syncookies.c,v 1.13 2001/02/13 01:17:26 davem Exp $
  *
  *  Missing: IPv6 support. 
  */
diff -urN linux-2.4.4-pre2/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- linux-2.4.4-pre2/net/ipv4/tcp.c	Fri Feb  9 11:34:13 2001
+++ linux/net/ipv4/tcp.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.180 2000/11/28 17:04:09 davem Exp $
+ * Version:	$Id: tcp.c,v 1.201 2001/03/06 22:42:56 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -520,6 +520,7 @@
 	sk->ack_backlog = 0;
 	tp->accept_queue = tp->accept_queue_tail = NULL;
 	tp->syn_wait_lock = RW_LOCK_UNLOCKED;
+	tcp_delack_init(tp);
 
 	lopt = kmalloc(sizeof(struct tcp_listen_opt), GFP_KERNEL);
 	if (!lopt)
@@ -642,11 +643,8 @@
 		if(sk->err)
 			return sock_error(sk);
 		if((1 << sk->state) &
-		   ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
-			if(sk->keepopen && !(flags&MSG_NOSIGNAL))
-				send_sig(SIGPIPE, tsk, 0);
+		   ~(TCPF_SYN_SENT | TCPF_SYN_RECV))
 			return -EPIPE;
-		}
 		if(!*timeo_p)
 			return -EAGAIN;
 		if(signal_pending(tsk))
@@ -675,56 +673,326 @@
 /*
  *	Wait for more memory for a socket
  */
-static long wait_for_tcp_memory(struct sock * sk, long timeo)
+static int wait_for_tcp_memory(struct sock * sk, long *timeo)
 {
+	int err = 0;
 	long vm_wait = 0;
-	long current_timeo = timeo;
+	long current_timeo = *timeo;
 	DECLARE_WAITQUEUE(wait, current);
 
 	if (tcp_memory_free(sk))
 		current_timeo = vm_wait = (net_random()%(HZ/5))+2;
 
-	clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
-
 	add_wait_queue(sk->sleep, &wait);
 	for (;;) {
-		set_bit(SOCK_NOSPACE, &sk->socket->flags);
+		set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
 
 		set_current_state(TASK_INTERRUPTIBLE);
 
+		if (sk->err || (sk->shutdown & SEND_SHUTDOWN))
+			goto do_error;
+		if (!*timeo)
+			goto do_nonblock;
 		if (signal_pending(current))
-			break;
+			goto do_interrupted;
+		clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
 		if (tcp_memory_free(sk) && !vm_wait)
 			break;
-		if (sk->shutdown & SEND_SHUTDOWN)
-			break;
-		if (sk->err)
-			break;
+
+		set_bit(SOCK_NOSPACE, &sk->socket->flags);
+		sk->tp_pinfo.af_tcp.write_pending++;
 		release_sock(sk);
 		if (!tcp_memory_free(sk) || vm_wait)
 			current_timeo = schedule_timeout(current_timeo);
 		lock_sock(sk);
+		sk->tp_pinfo.af_tcp.write_pending--;
+
 		if (vm_wait) {
-			if (timeo != MAX_SCHEDULE_TIMEOUT &&
-			    (timeo -= vm_wait-current_timeo) < 0)
-				timeo = 0;
-			break;
-		} else {
-			timeo = current_timeo;
+			vm_wait -= current_timeo;
+			current_timeo = *timeo;
+			if (current_timeo != MAX_SCHEDULE_TIMEOUT &&
+			    (current_timeo -= vm_wait) < 0)
+				current_timeo = 0;
+			vm_wait = 0;
 		}
+		*timeo = current_timeo;
 	}
+out:
 	current->state = TASK_RUNNING;
 	remove_wait_queue(sk->sleep, &wait);
-	return timeo;
+	return err;
+
+do_error:
+	err = -EPIPE;
+	goto out;
+do_nonblock:
+	err = -EAGAIN;
+	goto out;
+do_interrupted:
+	err = sock_intr_errno(*timeo);
+	goto out;
 }
 
-/* When all user supplied data has been queued set the PSH bit */
-#define PSH_NEEDED (seglen == 0 && iovlen == 0)
+ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags);
 
-/*
- *	This routine copies from a user buffer into a socket,
- *	and starts the transmit system.
- */
+static inline int
+can_coalesce(struct sk_buff *skb, int i, struct page *page, int off)
+{
+	if (i) {
+		skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1];
+		return page == frag->page &&
+			off == frag->page_offset+frag->size;
+	}
+	return 0;
+}
+
+static inline void
+fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size)
+{
+	skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+	frag->page = page;
+	frag->page_offset = off;
+	frag->size = size;
+	skb_shinfo(skb)->nr_frags = i+1;
+}
+
+static inline void tcp_mark_push(struct tcp_opt *tp, struct sk_buff *skb)
+{
+	TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
+	tp->pushed_seq = tp->write_seq;
+}
+
+static inline int forced_push(struct tcp_opt *tp)
+{
+	return after(tp->write_seq, tp->pushed_seq + (tp->max_window>>1));
+}
+
+static inline void
+skb_entail(struct sock *sk, struct tcp_opt *tp, struct sk_buff *skb)
+{
+	skb->csum = 0;
+	TCP_SKB_CB(skb)->seq = tp->write_seq;
+	TCP_SKB_CB(skb)->end_seq = tp->write_seq;
+	TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK;
+	TCP_SKB_CB(skb)->sacked = 0;
+	__skb_queue_tail(&sk->write_queue, skb);
+	tcp_charge_skb(sk, skb);
+	if (tp->send_head == NULL)
+		tp->send_head = skb;
+}
+
+static inline void
+tcp_mark_urg(struct tcp_opt *tp, int flags, struct sk_buff *skb)
+{
+	if (flags & MSG_OOB) {
+		tp->urg_mode = 1;
+		tp->snd_up = tp->write_seq;
+		TCP_SKB_CB(skb)->sacked |= TCPCB_URG;
+	}
+}
+
+static inline void
+tcp_push(struct sock *sk, struct tcp_opt *tp, int flags, int mss_now, int nonagle)
+{
+	if (tp->send_head) {
+		struct sk_buff *skb = sk->write_queue.prev;
+		if (!(flags&MSG_MORE) || forced_push(tp))
+			tcp_mark_push(tp, skb);
+		tcp_mark_urg(tp, flags, skb);
+		__tcp_push_pending_frames(sk, tp, mss_now, (flags&MSG_MORE) ? 2 : nonagle);
+	}
+}
+
+static int tcp_error(struct sock *sk, int flags, int err)
+{
+	if (err == -EPIPE)
+		err = sock_error(sk) ? : -EPIPE;
+	if (err == -EPIPE && !(flags&MSG_NOSIGNAL))
+		send_sig(SIGPIPE, current, 0);
+	return err;
+}
+
+ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	int mss_now;
+	int err;
+	ssize_t copied;
+	long timeo = sock_sndtimeo(sk, flags&MSG_DONTWAIT);
+
+	/* Wait for a connection to finish. */
+	if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
+		if((err = wait_for_tcp_connect(sk, 0, &timeo)) != 0)
+			goto out_err;
+
+	clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+
+	mss_now = tcp_current_mss(sk);
+	copied = 0;
+
+	err = -EPIPE;
+	if (sk->err || (sk->shutdown & SEND_SHUTDOWN))
+		goto do_error;
+
+	while (psize > 0) {
+		struct sk_buff *skb = sk->write_queue.prev;
+		int offset, size, copy, i;
+		struct page *page;
+
+		page = pages[poffset/PAGE_SIZE];
+		offset = poffset % PAGE_SIZE;
+		size = min(psize, PAGE_SIZE-offset);
+
+		if (tp->send_head==NULL || (copy = mss_now - skb->len) <= 0) {
+new_segment:
+			if (!tcp_memory_free(sk))
+				goto wait_for_sndbuf;
+
+			skb = tcp_alloc_pskb(sk, 0, tp->mss_cache, sk->allocation);
+			if (skb == NULL)
+				goto wait_for_memory;
+
+			skb_entail(sk, tp, skb);
+			copy = mss_now;
+		}
+
+		if (copy > size)
+			copy = size;
+
+		i = skb_shinfo(skb)->nr_frags;
+		if (can_coalesce(skb, i, page, offset)) {
+			skb_shinfo(skb)->frags[i-1].size += copy;
+		} else if (i < MAX_SKB_FRAGS) {
+			get_page(page);
+			fill_page_desc(skb, i, page, offset, copy);
+		} else {
+			tcp_mark_push(tp, skb);
+			goto new_segment;
+		}
+
+		skb->len += copy;
+		skb->data_len += copy;
+		skb->ip_summed = CHECKSUM_HW;
+		tp->write_seq += copy;
+		TCP_SKB_CB(skb)->end_seq += copy;
+
+		if (!copied)
+			TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH;
+
+		copied += copy;
+		poffset += copy;
+		if (!(psize -= copy))
+			goto out;
+
+		if (skb->len != mss_now || (flags&MSG_OOB))
+			continue;
+
+		if (forced_push(tp)) {
+			tcp_mark_push(tp, skb);
+			__tcp_push_pending_frames(sk, tp, mss_now, 1);
+		} else if (skb == tp->send_head)
+			tcp_push_one(sk, mss_now);
+		continue;
+
+wait_for_sndbuf:
+		set_bit(SOCK_NOSPACE, &sk->socket->flags);
+wait_for_memory:
+		if (copied)
+			tcp_push(sk, tp, flags&~MSG_MORE, mss_now, 1);
+
+		if ((err = wait_for_tcp_memory(sk, &timeo)) != 0)
+			goto do_error;
+
+		mss_now = tcp_current_mss(sk);
+	}
+
+out:
+	if (copied)
+		tcp_push(sk, tp, flags, mss_now, tp->nonagle);
+	return copied;
+
+do_error:
+	if (copied)
+		goto out;
+out_err:
+	return tcp_error(sk, flags, err);
+}
+
+ssize_t tcp_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags)
+{
+	ssize_t res;
+	struct sock *sk = sock->sk;
+
+#define TCP_ZC_CSUM_FLAGS (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM)
+
+	if (!(sk->route_caps & NETIF_F_SG) || 
+	    !(sk->route_caps & TCP_ZC_CSUM_FLAGS))
+		return sock_no_sendpage(sock, page, offset, size, flags);
+
+#undef TCP_ZC_CSUM_FLAGS
+
+	lock_sock(sk);
+	TCP_CHECK_TIMER(sk);
+	res = do_tcp_sendpages(sk, &page, offset, size, flags);
+	TCP_CHECK_TIMER(sk);
+	release_sock(sk);
+	return res;
+}
+
+#define TCP_PAGE(sk)	(sk->tp_pinfo.af_tcp.sndmsg_page)
+#define TCP_OFF(sk)	(sk->tp_pinfo.af_tcp.sndmsg_off)
+
+static inline int
+tcp_copy_to_page(struct sock *sk, char *from, struct sk_buff *skb,
+		 struct page *page, int off, int copy)
+{
+	int err = 0;
+	unsigned int csum;
+
+	csum = csum_and_copy_from_user(from, page_address(page)+off,
+				       copy, 0, &err);
+	if (!err) {
+		if (skb->ip_summed == CHECKSUM_NONE)
+			skb->csum = csum_block_add(skb->csum, csum, skb->len);
+		skb->len += copy;
+		skb->data_len += copy;
+		skb->truesize += copy;
+		sk->wmem_queued += copy;
+		sk->forward_alloc -= copy;
+	}
+	return err;
+}
+
+static inline int
+skb_add_data(struct sk_buff *skb, char *from, int copy)
+{
+	int err = 0;
+	unsigned int csum;
+	int off = skb->len;
+
+	csum = csum_and_copy_from_user(from, skb_put(skb, copy),
+				       copy, 0, &err);
+	if (!err) {
+		skb->csum = csum_block_add(skb->csum, csum, off);
+		return 0;
+	}
+
+	__skb_trim(skb, off);
+	return -EFAULT;
+}
+
+static inline int select_size(struct sock *sk, struct tcp_opt *tp)
+{
+	int tmp = tp->mss_cache;
+
+	if (sk->route_caps&NETIF_F_SG) {
+		int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER);
+
+		if (tmp >= pgbreak && tmp <= pgbreak + (MAX_SKB_FRAGS-1)*PAGE_SIZE)
+			tmp = pgbreak;
+	}
+	return tmp;
+}
 
 int tcp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
 {
@@ -736,20 +1004,18 @@
 	int err, copied;
 	long timeo;
 
-	err = 0;
 	tp = &(sk->tp_pinfo.af_tcp);
 
 	lock_sock(sk);
 	TCP_CHECK_TIMER(sk);
 
 	flags = msg->msg_flags;
-
 	timeo = sock_sndtimeo(sk, flags&MSG_DONTWAIT);
 
 	/* Wait for a connection to finish. */
 	if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
 		if((err = wait_for_tcp_connect(sk, flags, &timeo)) != 0)
-			goto out_unlock;
+			goto out_err;
 
 	/* This should be in poll */
 	clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
@@ -761,6 +1027,10 @@
 	iov = msg->msg_iov;
 	copied = 0;
 
+	err = -EPIPE;
+	if (sk->err || (sk->shutdown&SEND_SHUTDOWN))
+		goto do_error;
+
 	while (--iovlen >= 0) {
 		int seglen=iov->iov_len;
 		unsigned char * from=iov->iov_base;
@@ -768,200 +1038,159 @@
 		iov++;
 
 		while (seglen > 0) {
-			int copy, tmp, queue_it;
-
-			if (err)
-				goto do_fault2;
-
-			/* Stop on errors. */
-			if (sk->err)
-				goto do_sock_err;
-
-			/* Make sure that we are established. */
-			if (sk->shutdown & SEND_SHUTDOWN)
-				goto do_shutdown;
-	
-			/* Now we need to check if we have a half
-			 * built packet we can tack some data onto.
-			 */
+			int copy;
+			
 			skb = sk->write_queue.prev;
-			if (tp->send_head &&
-			    (mss_now > skb->len)) {
-				copy = skb->len;
-				if (skb_tailroom(skb) > 0) {
-					int last_byte_was_odd = (copy % 4);
-
-					copy = mss_now - copy;
-					if(copy > skb_tailroom(skb))
-						copy = skb_tailroom(skb);
-					if(copy > seglen)
-						copy = seglen;
-					if(last_byte_was_odd) {
-						if(copy_from_user(skb_put(skb, copy),
-								  from, copy))
-							err = -EFAULT;
-						skb->csum = csum_partial(skb->data,
-									 skb->len, 0);
-					} else {
-						skb->csum =
-							csum_and_copy_from_user(
-							from, skb_put(skb, copy),
-							copy, skb->csum, &err);
-					}
-					/*
-					 * FIXME: the *_user functions should
-					 *	  return how much data was
-					 *	  copied before the fault
-					 *	  occurred and then a partial
-					 *	  packet with this data should
-					 *	  be sent.  Unfortunately
-					 *	  csum_and_copy_from_user doesn't
-					 *	  return this information.
-					 *	  ATM it might send partly zeroed
-					 *	  data in this case.
-					 */
-					tp->write_seq += copy;
-					TCP_SKB_CB(skb)->end_seq += copy;
-					from += copy;
-					copied += copy;
-					seglen -= copy;
-					if (PSH_NEEDED ||
-					    after(tp->write_seq, tp->pushed_seq+(tp->max_window>>1))) {
-						TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
-						tp->pushed_seq = tp->write_seq;
-					}
-					if (flags&MSG_OOB) {
-						tp->urg_mode = 1;
-						tp->snd_up = tp->write_seq;
-						TCP_SKB_CB(skb)->sacked |= TCPCB_URG;
-					}
-					continue;
-				} else {
-					TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
-					tp->pushed_seq = tp->write_seq;
-				}
-			}
 
-			copy = min(seglen, mss_now);
+			if (tp->send_head == NULL ||
+			    (copy = mss_now - skb->len) <= 0) {
 
-			/* Determine how large of a buffer to allocate.  */
-			tmp = MAX_TCP_HEADER + 15 + tp->mss_cache;
-			if (copy < mss_now && !(flags & MSG_OOB)) {
-				/* What is happening here is that we want to
-				 * tack on later members of the users iovec
-				 * if possible into a single frame.  When we
-				 * leave this loop our we check to see if
-				 * we can send queued frames onto the wire.
+new_segment:
+				/* Allocate new segment. If the interface is SG,
+				 * allocate skb fitting to single page.
 				 */
-				queue_it = 1;
-			} else {
-				queue_it = 0;
-			}
-
-			skb = NULL;
-			if (tcp_memory_free(sk))
-				skb = tcp_alloc_skb(sk, tmp, sk->allocation);
-			if (skb == NULL) {
-				/* If we didn't get any memory, we need to sleep. */
-				set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
-				set_bit(SOCK_NOSPACE, &sk->socket->flags);
+				if (!tcp_memory_free(sk))
+					goto wait_for_sndbuf;
 
-				__tcp_push_pending_frames(sk, tp, mss_now, 1);
-
-				if (!timeo) {
-					err = -EAGAIN;
-					goto do_interrupted;
+				skb = tcp_alloc_pskb(sk, select_size(sk, tp), 0, sk->allocation);
+				if (skb == NULL)
+					goto wait_for_memory;
+
+				skb_entail(sk, tp, skb);
+				copy = mss_now;
+			}
+
+			/* Try to append data to the end of skb. */
+			if (copy > seglen)
+				copy = seglen;
+
+			/* Where to copy to? */
+			if (skb_tailroom(skb) > 0) {
+				/* We have some space in skb head. Superb! */
+				if (copy > skb_tailroom(skb))
+					copy = skb_tailroom(skb);
+				if ((err = skb_add_data(skb, from, copy)) != 0)
+					goto do_fault;
+			} else {
+				int merge = 0;
+				int i = skb_shinfo(skb)->nr_frags;
+				struct page *page = TCP_PAGE(sk);
+				int off = TCP_OFF(sk);
+
+				if (can_coalesce(skb, i, page, off) && off != PAGE_SIZE) {
+					/* We can extend the last page fragment. */
+					merge = 1;
+				} else if (i == MAX_SKB_FRAGS ||
+					   (i == 0 && !(sk->route_caps&NETIF_F_SG))) {
+					/* Need to add new fragment and cannot
+					 * do this because interface is non-SG,
+					 * or because all the page slots are busy.
+					 */
+					tcp_mark_push(tp, skb);
+					goto new_segment;
+				} else if (page) {
+					/* If page is cached, align
+					 * offset to L1 cache boundary
+					 */
+					off = (off+L1_CACHE_BYTES-1)&~(L1_CACHE_BYTES-1);
+					if (off == PAGE_SIZE) {
+						put_page(page);
+						TCP_PAGE(sk) = page = NULL;
+					}
 				}
-				if (signal_pending(current)) {
-					err = sock_intr_errno(timeo);
-					goto do_interrupted;
+
+				if (!page) {
+					/* Allocate new cache page. */
+					if (!(page=tcp_alloc_page(sk)))
+						goto wait_for_memory;
+					off = 0;
 				}
-				timeo = wait_for_tcp_memory(sk, timeo);
 
-				/* If SACK's were formed or PMTU events happened,
-				 * we must find out about it.
-				 */
-				mss_now = tcp_current_mss(sk);
-				continue;
-			}
+				if (copy > PAGE_SIZE-off)
+					copy = PAGE_SIZE-off;
 
-			seglen -= copy;
+				/* Time to copy data. We are close to the end! */
+				err = tcp_copy_to_page(sk, from, skb, page, off, copy);
+				if (err)
+					goto do_error;
+
+				/* Update the skb. */
+				if (merge) {
+					skb_shinfo(skb)->frags[i-1].size += copy;
+				} else {
+					fill_page_desc(skb, i, page, off, copy);
+					if (TCP_PAGE(sk)) {
+						get_page(page);
+					} else if (off + copy < PAGE_SIZE) {
+						get_page(page);
+						TCP_PAGE(sk) = page;
+					}
+				}
 
-			/* Prepare control bits for TCP header creation engine. */
-			if (PSH_NEEDED ||
-			    after(tp->write_seq+copy, tp->pushed_seq+(tp->max_window>>1))) {
-				TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK|TCPCB_FLAG_PSH;
-				tp->pushed_seq = tp->write_seq + copy;
-			} else {
-				TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK;
-			}
-			TCP_SKB_CB(skb)->sacked = 0;
-			if (flags & MSG_OOB) {
-				TCP_SKB_CB(skb)->sacked |= TCPCB_URG;
-				tp->urg_mode = 1;
-				tp->snd_up = tp->write_seq + copy;
+				TCP_OFF(sk) = off+copy;
 			}
 
-			/* TCP data bytes are SKB_PUT() on top, later
-			 * TCP+IP+DEV headers are SKB_PUSH()'d beneath.
-			 * Reserve header space and checksum the data.
-			 */
-			skb_reserve(skb, MAX_TCP_HEADER);
-			skb->csum = csum_and_copy_from_user(from,
-					skb_put(skb, copy), copy, 0, &err);
+			if (!copied)
+				TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH;
 
-			if (err)
-				goto do_fault;
+			tp->write_seq += copy;
+			TCP_SKB_CB(skb)->end_seq += copy;
 
 			from += copy;
 			copied += copy;
+			seglen -= copy;
+
+			if (skb->len != mss_now || (flags&MSG_OOB))
+				continue;
 
-			TCP_SKB_CB(skb)->seq = tp->write_seq;
-			TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + copy;
+			if (forced_push(tp)) {
+				tcp_mark_push(tp, skb);
+				__tcp_push_pending_frames(sk, tp, mss_now, 1);
+			} else if (skb == tp->send_head)
+				tcp_push_one(sk, mss_now);
+			continue;
 
-			/* This advances tp->write_seq for us. */
-			tcp_send_skb(sk, skb, queue_it, mss_now);
+wait_for_sndbuf:
+			set_bit(SOCK_NOSPACE, &sk->socket->flags);
+wait_for_memory:
+			if (copied)
+				tcp_push(sk, tp, flags&~MSG_MORE, mss_now, 1);
+
+			if ((err = wait_for_tcp_memory(sk, &timeo)) != 0)
+				goto do_error;
+
+			mss_now = tcp_current_mss(sk);
 		}
 	}
-	err = copied;
+
 out:
-	__tcp_push_pending_frames(sk, tp, mss_now, tp->nonagle);
-out_unlock:
+	if (copied)
+		tcp_push(sk, tp, flags, mss_now, tp->nonagle);
 	TCP_CHECK_TIMER(sk);
 	release_sock(sk);
-	return err;
+	return copied;
 
-do_sock_err:
-	if (copied)
-		err = copied;
-	else
-		err = sock_error(sk);
-	goto out;
-do_shutdown:
-	if (copied)
-		err = copied;
-	else {
-		if (!(flags&MSG_NOSIGNAL))
-			send_sig(SIGPIPE, current, 0);
-		err = -EPIPE;
-	}
-	goto out;
-do_interrupted:
-	if (copied)
-		err = copied;
-	goto out_unlock;
 do_fault:
-	__kfree_skb(skb);
-do_fault2:
+	if (skb->len==0) {
+		if (tp->send_head == skb) {
+			tp->send_head = skb->prev;
+			if (tp->send_head == (struct sk_buff*)&sk->write_queue)
+				tp->send_head = NULL;
+		}
+		__skb_unlink(skb, skb->list);
+		tcp_free_skb(sk, skb);
+	}
+
+do_error:
 	if (copied)
-		err = copied;
-	else
-		err = -EFAULT;
-	goto out;
+		goto out;
+out_err:
+	err = tcp_error(sk, flags, err);
+	TCP_CHECK_TIMER(sk);
+	release_sock(sk);
+	return err;
 }
 
-#undef PSH_NEEDED
-
 /*
  *	Handle reading urgent data. BSD has very simple semantics for
  *	this, no blocking and very strange errors 8)
@@ -991,7 +1220,7 @@
 		msg->msg_flags|=MSG_OOB;
 
 		if(len>0) {
-			if (!(flags & MSG_PEEK))
+			if (!(flags & MSG_PEEK) && !(flags & MSG_TRUNC))
 				err = memcpy_toiovec(msg->msg_iov, &c, 1);
 			len = 1;
 		} else
@@ -1033,17 +1262,13 @@
 static void cleanup_rbuf(struct sock *sk, int copied)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	struct sk_buff *skb;
 	int time_to_ack = 0;
 
-	/* NOTE! The socket must be locked, so that we don't get
-	 * a messed-up receive queue.
-	 */
-	while ((skb=skb_peek(&sk->receive_queue)) != NULL) {
-		if (!skb->used)
-			break;
-		tcp_eat_skb(sk, skb);
-	}
+#if TCP_DEBUG
+	struct sk_buff *skb = skb_peek(&sk->receive_queue);
+
+	BUG_TRAP(skb==NULL || before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
+#endif
 
 	if (tcp_ack_scheduled(tp)) {
 		   /* Delayed ACKs frequently hit locked sockets during bulk receive. */
@@ -1219,8 +1444,7 @@
 				goto found_ok_skb;
 			if (skb->h.th->fin)
 				goto found_fin_ok;
-			if (!(flags & MSG_PEEK))
-				skb->used = 1;
+			BUG_TRAP(flags&MSG_PEEK);
 			skb = skb->next;
 		} while (skb != (struct sk_buff *)&sk->receive_queue);
 
@@ -1233,7 +1457,8 @@
 			if (sk->err ||
 			    sk->state == TCP_CLOSE ||
 			    (sk->shutdown & RCV_SHUTDOWN) ||
-			    !timeo)
+			    !timeo ||
+			    (flags & MSG_PEEK))
 				break;
 		} else {
 			if (sk->done)
@@ -1358,15 +1583,16 @@
 						++*seq;
 						offset++;
 						used--;
+						if (!used)
+							goto skip_copy;
 					}
 				} else
 					used = urg_offset;
 			}
 		}
 
-		err = 0;
 		if (!(flags&MSG_TRUNC)) {
-			err = memcpy_toiovec(msg->msg_iov, ((unsigned char *)skb->h.th) + skb->h.th->doff*4 + offset, used);
+			err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, used);
 			if (err) {
 				/* Exception. Bailout! */
 				if (!copied)
@@ -1379,37 +1605,25 @@
 		copied += used;
 		len -= used;
 
-		if (after(tp->copied_seq,tp->urg_seq)) {
+skip_copy:
+		if (tp->urg_data && after(tp->copied_seq,tp->urg_seq)) {
 			tp->urg_data = 0;
-			if (skb_queue_len(&tp->out_of_order_queue) == 0
-#ifdef TCP_FORMAL_WINDOW
-			    && tcp_receive_window(tp)
-#endif
-			    ) {
-				tcp_fast_path_on(tp);
-			}
+			tcp_fast_path_check(sk, tp);
 		}
 		if (used + offset < skb->len)
 			continue;
 
-		/*	Process the FIN. We may also need to handle PSH
-		 *	here and make it break out of MSG_WAITALL.
-		 */
 		if (skb->h.th->fin)
 			goto found_fin_ok;
-		if (flags & MSG_PEEK)
-			continue;
-		skb->used = 1;
-		tcp_eat_skb(sk, skb);
+		if (!(flags & MSG_PEEK))
+			tcp_eat_skb(sk, skb);
 		continue;
 
 	found_fin_ok:
+		/* Process the FIN. */
 		++*seq;
-		if (flags & MSG_PEEK)
-			break;
-
-		/* All is done. */
-		skb->used = 1;
+		if (!(flags & MSG_PEEK))
+			tcp_eat_skb(sk, skb);
 		break;
 	} while (len > 0);
 
@@ -1556,7 +1770,7 @@
 	/* It cannot be in hash table! */
 	BUG_TRAP(sk->pprev==NULL);
 
-	/* It it has not 0 sk->num, it must be bound */
+	/* If it has not 0 sk->num, it must be bound */
 	BUG_TRAP(!sk->num || sk->prev!=NULL);
 
 #ifdef TCP_DEBUG
@@ -2041,7 +2255,7 @@
 		tp->defer_accept = 0;
 		if (val > 0) {
 			/* Translate value in seconds to number of retransmits */
-			while (val > ((TCP_TIMEOUT_INIT/HZ)<<tp->defer_accept))
+			while (tp->defer_accept < 32 && val > ((TCP_TIMEOUT_INIT/HZ)<<tp->defer_accept))
 				tp->defer_accept++;
 			tp->defer_accept++;
 		}
@@ -2060,6 +2274,21 @@
 		}
 		break;
 
+	case TCP_QUICKACK:
+		if (!val) {
+			tp->ack.pingpong = 1;
+		} else {
+			tp->ack.pingpong = 0;
+			if ((1<<sk->state)&(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT) &&
+			    tcp_ack_scheduled(tp)) {
+				tp->ack.pending |= TCP_ACK_PUSHED;
+				cleanup_rbuf(sk, 1);
+				if (!(val & 1))
+					tp->ack.pingpong = 1;
+			}
+		}
+		break;
+
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2082,6 +2311,9 @@
 		return -EFAULT;
 
 	len = min(len, sizeof(int));
+	
+	if(len < 0)
+		return -EINVAL;
 
 	switch(optname) {
 	case TCP_MAXSEG:
@@ -2180,6 +2412,9 @@
 			return -EFAULT;
 		return 0;
 	}
+	case TCP_QUICKACK:
+		val = !tp->ack.pingpong;
+		break;
 	default:
 		return -ENOPROTOOPT;
 	};
@@ -2230,7 +2465,10 @@
 	 *
 	 * The methodology is similar to that of the buffer cache.
 	 */
-	goal = num_physpages >> (23 - PAGE_SHIFT);
+	if (num_physpages >= (128 * 1024))
+		goal = num_physpages >> (21 - PAGE_SHIFT);
+	else
+		goal = num_physpages >> (23 - PAGE_SHIFT);
 
 	for(order = 0; (1UL << order) < goal; order++)
 		;
@@ -2284,9 +2522,9 @@
 	}
 	tcp_port_rover = sysctl_local_port_range[0] - 1;
 
-	sysctl_tcp_mem[0] = 64<<order;
-	sysctl_tcp_mem[1] = 200<<order;
-	sysctl_tcp_mem[2] = 256<<order;
+	sysctl_tcp_mem[0] = 768<<order;
+	sysctl_tcp_mem[1] = 1024<<order;
+	sysctl_tcp_mem[2] = 1536<<order;
 	if (sysctl_tcp_mem[2] - sysctl_tcp_mem[1] > 512)
 		sysctl_tcp_mem[1] = sysctl_tcp_mem[2] - 512;
 	if (sysctl_tcp_mem[1] - sysctl_tcp_mem[0] > 512)
diff -urN linux-2.4.4-pre2/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- linux-2.4.4-pre2/net/ipv4/tcp_input.c	Fri Feb  9 11:34:13 2001
+++ linux/net/ipv4/tcp_input.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.205 2000/12/13 18:31:48 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.226 2001/03/07 22:00:57 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -137,7 +137,7 @@
 		 *
 		 * "len" is invariant segment length, including TCP header.
 		 */
-		len = skb->tail - skb->h.raw;
+		len += skb->data - skb->h.raw;
 		if (len >= TCP_MIN_RCVMSS + sizeof(struct tcphdr) ||
 		    /* If PSH is not set, packet should be
 		     * full sized, provided peer TCP is not badly broken.
@@ -378,7 +378,8 @@
 		/* The _first_ data packet received, initialize
 		 * delayed ACK engine.
 		 */
-		tcp_enter_quickack_mode(tp);
+		tcp_incr_quickack(tp);
+		tp->ack.ato = TCP_ATO_MIN;
 	} else {
 		int m = now - tp->ack.lrcvtime;
 
@@ -510,7 +511,7 @@
 }
 
 /* Save metrics learned by this TCP session.
-   This function is called only, when TCP finishes sucessfully
+   This function is called only, when TCP finishes successfully
    i.e. when it enters TIME-WAIT or goes from LAST-ACK to CLOSE.
  */
 void tcp_update_metrics(struct sock *sk)
@@ -1016,7 +1017,7 @@
 			tp->fackets_out = cnt;
 		}
 	}
-	tp->left_out = tp->sacked_out + tp->lost_out;
+	tcp_sync_left_out(tp);
 
 	tp->reordering = min(tp->reordering, sysctl_tcp_reordering);
 	tp->ca_state = TCP_CA_Loss;
@@ -1052,6 +1053,15 @@
 	return IsReno(tp) ? tp->sacked_out+1 : tp->fackets_out;
 }
 
+static inline int tcp_skb_timedout(struct tcp_opt *tp, struct sk_buff *skb)
+{
+	return (tcp_time_stamp - TCP_SKB_CB(skb)->when > tp->rto);
+}
+
+static inline int tcp_head_timedout(struct sock *sk, struct tcp_opt *tp)
+{
+	return tp->packets_out && tcp_skb_timedout(tp, skb_peek(&sk->write_queue));
+}
 
 /* Linux NewReno/SACK/FACK/ECN state machine.
  * --------------------------------------
@@ -1157,7 +1167,13 @@
 	if (tcp_fackets_out(tp) > tp->reordering)
 		return 1;
 
-	/* Trick#3: It is still not OK... But will it be useful to delay
+	/* Trick#3 : when we use RFC2988 timer restart, fast
+	 * retransmit can be triggered by timeout of queue head.
+	 */
+	if (tcp_head_timedout(sk, tp))
+		return 1;
+
+	/* Trick#4: It is still not OK... But will it be useful to delay
 	 * recovery more?
 	 */
 	if (tp->packets_out <= tp->reordering &&
@@ -1178,8 +1194,10 @@
  */
 static void tcp_check_reno_reordering(struct tcp_opt *tp, int addend)
 {
-	if (tp->sacked_out + 1 > tp->packets_out) {
-		tp->sacked_out = tp->packets_out ? tp->packets_out - 1 : 0;
+	int holes = min(max(tp->lost_out, 1), tp->packets_out);
+
+	if (tp->sacked_out + holes > tp->packets_out) {
+		tp->sacked_out = tp->packets_out - holes;
 		tcp_update_reordering(tp, tp->packets_out+addend, 0);
 	}
 }
@@ -1190,7 +1208,7 @@
 {
 	++tp->sacked_out;
 	tcp_check_reno_reordering(tp, 0);
-	tp->left_out = tp->sacked_out + tp->lost_out;
+	tcp_sync_left_out(tp);
 }
 
 /* Account for ACK, ACKing some data in Reno Recovery phase. */
@@ -1198,17 +1216,14 @@
 static void tcp_remove_reno_sacks(struct sock *sk, struct tcp_opt *tp, int acked)
 {
 	if (acked > 0) {
-		/* One ACK eated lost packet. Must eat! */
-		BUG_TRAP(tp->lost_out == 0);
-
-		/* The rest eat duplicate ACKs. */
+		/* One ACK acked hole. The rest eat duplicate ACKs. */
 		if (acked-1 >= tp->sacked_out)
 			tp->sacked_out = 0;
 		else
 			tp->sacked_out -= acked-1;
 	}
 	tcp_check_reno_reordering(tp, acked);
-	tp->left_out = tp->sacked_out + tp->lost_out;
+	tcp_sync_left_out(tp);
 }
 
 static inline void tcp_reset_reno_sack(struct tcp_opt *tp)
@@ -1234,7 +1249,7 @@
 			tp->lost_out++;
 		}
 	}
-	tp->left_out = tp->sacked_out + tp->lost_out;
+	tcp_sync_left_out(tp);
 }
 
 /* Account newly detected lost packet(s) */
@@ -1249,6 +1264,24 @@
 	} else {
 		tcp_mark_head_lost(sk, tp, 1, tp->high_seq);
 	}
+
+	/* New heuristics: it is possible only after we switched
+	 * to restart timer each time when something is ACKed.
+	 * Hence, we can detect timed out packets during fast
+	 * retransmit without falling to slow start.
+	 */
+	if (tcp_head_timedout(sk, tp)) {
+		struct sk_buff *skb;
+
+		for_retrans_queue(skb, sk, tp) {
+			if (tcp_skb_timedout(tp, skb) &&
+			    !(TCP_SKB_CB(skb)->sacked&TCPCB_TAGBITS)) {
+				TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
+				tp->lost_out++;
+			}
+		}
+		tcp_sync_left_out(tp);
+	}
 }
 
 /* CWND moderation, preventing bursts due to too big ACKs
@@ -1490,7 +1523,7 @@
 	}
 
 	/* D. Synchronize left_out to current state. */
-	tp->left_out = tp->sacked_out + tp->lost_out;
+	tcp_sync_left_out(tp);
 
 	/* E. Check state exit conditions. State can be terminated
 	 *    when high_seq is ACKed. */
@@ -1516,8 +1549,13 @@
 
 		case TCP_CA_Disorder:
 			tcp_try_undo_dsack(sk, tp);
-			tp->undo_marker = 0;
-			tp->ca_state = TCP_CA_Open;
+			if (!tp->undo_marker ||
+			    /* For SACK case do not Open to allow to undo
+			     * catching for all duplicate ACKs. */
+			    IsReno(tp) || tp->snd_una != tp->high_seq) {
+				tp->undo_marker = 0;
+				tp->ca_state = TCP_CA_Open;
+			}
 			break;
 
 		case TCP_CA_Recovery:
@@ -1544,8 +1582,8 @@
 		}
 		break;
 	case TCP_CA_Loss:
-		if (flag & FLAG_ACKED)
-			tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
+		if (flag&FLAG_DATA_ACKED)
+			tp->retransmits = 0;
 		if (!tcp_try_undo_loss(sk, tp)) {
 			tcp_moderate_cwnd(tp);
 			tcp_xmit_retransmit_queue(sk);
@@ -1593,7 +1631,7 @@
 		tp->ca_state = TCP_CA_Recovery;
 	}
 
-	if (is_dupack)
+	if (is_dupack || tcp_head_timedout(sk, tp))
 		tcp_update_scoreboard(sk, tp);
 	tcp_cwnd_down(tp);
 	tcp_xmit_retransmit_queue(sk);
@@ -1613,16 +1651,18 @@
 	 *
 	 * See draft-ietf-tcplw-high-performance-00, section 3.3.
 	 * 1998/04/10 Andrey V. Savochkin <saw@msu.ru>
+	 *
+	 * Changed: reset backoff as soon as we see the first valid sample.
+	 * If we do not, we get strongly overstimated rto. With timestamps
+	 * samples are accepted even from very old segments: f.e., when rtt=1
+	 * increases to 8, we retransmit 5 times and after 8 seconds delayed
+	 * answer arrives rto becomes 120 seconds! If at least one of segments
+	 * in window is lost... Voila.	 			--ANK (010210)
 	 */
 	seq_rtt = tcp_time_stamp - tp->rcv_tsecr;
 	tcp_rtt_estimator(tp, seq_rtt);
 	tcp_set_rto(tp);
-	if (tp->backoff) {
-		if (!tp->retransmits || !(flag & FLAG_RETRANS_DATA_ACKED))
-			tp->backoff = 0;
-		else
-			tp->rto <<= tp->backoff;
-	}
+	tp->backoff = 0;
 	tcp_bound_rto(tp);
 }
 
@@ -1642,15 +1682,7 @@
 
 	tcp_rtt_estimator(tp, seq_rtt);
 	tcp_set_rto(tp);
-	if (tp->backoff) {
-		/* To relax it? We have valid sample as soon as we are
-		 * here. Why not to clear backoff?
-		 */
-		if (!tp->retransmits)
-			tp->backoff = 0;
-		else
-			tp->rto <<= tp->backoff;
-	}
+	tp->backoff = 0;
 	tcp_bound_rto(tp);
 }
 
@@ -1684,15 +1716,11 @@
 		} else
 			tp->snd_cwnd_cnt++;
         }
+	tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
 /* Restart timer after forward progress on connection.
- * RFC2988 recommends (and BSD does) to restart timer to now+rto,
- * which is certainly wrong and effectively means that
- * rto includes one more _full_ rtt.
- *
- * For details see:
- * 	ftp://ftp.inr.ac.ru:/ip-routing/README.rto
+ * RFC2988 recommends to restart timer to now+rto.
  */
 
 static __inline__ void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp)
@@ -1700,12 +1728,7 @@
 	if (tp->packets_out==0) {
 		tcp_clear_xmit_timer(sk, TCP_TIME_RETRANS);
 	} else {
-		struct sk_buff *skb = skb_peek(&sk->write_queue);
-		__u32 when = tp->rto + tp->rttvar - (tcp_time_stamp - TCP_SKB_CB(skb)->when);
-
-		if ((__s32)when < (__s32)tp->rttvar)
-			when = tp->rttvar;
-		tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, min(when, TCP_RTO_MAX));
+		tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
 	}
 }
 
@@ -1857,12 +1880,7 @@
 			/* Note, it is the only place, where
 			 * fast path is recovered for sending TCP.
 			 */
-			if (skb_queue_len(&tp->out_of_order_queue) == 0 &&
-#ifdef TCP_FORMAL_WINDOW
-			    tcp_receive_window(tp) &&
-#endif
-			    !tp->urg_data)
-				tcp_fast_path_on(tp);
+			tcp_fast_path_check(sk, tp);
 
 			if (nwin > tp->max_window) {
 				tp->max_window = nwin;
@@ -1873,16 +1891,6 @@
 
 	tp->snd_una = ack;
 
-#ifdef TCP_DEBUG
-	if (before(tp->snd_una + tp->snd_wnd, tp->snd_nxt)) {
-		if (tp->snd_nxt-(tp->snd_una + tp->snd_wnd) >= (1<<tp->snd_wscale)
-		    && net_ratelimit())
-			printk(KERN_DEBUG "TCP: peer %u.%u.%u.%u:%u/%u shrinks window %u:%u:%u. Bad, what else can I say?\n",
-			       NIPQUAD(sk->daddr), htons(sk->dport), sk->num,
-			       tp->snd_una, tp->snd_wnd, tp->snd_nxt);
-	}
-#endif
-
 	return flag;
 }
 
@@ -2224,7 +2232,6 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	tp->fin_seq = TCP_SKB_CB(skb)->end_seq;
 	tcp_schedule_ack(tp);
 
 	sk->shutdown |= RCV_SHUTDOWN;
@@ -2506,10 +2513,27 @@
 	}
 }
 
+static inline int tcp_rmem_schedule(struct sock *sk, struct sk_buff *skb)
+{
+	return (int)skb->truesize <= sk->forward_alloc ||
+		tcp_mem_schedule(sk, skb->truesize, 1);
+}
+
+static int tcp_prune_queue(struct sock *sk);
+
 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
 {
+	struct tcphdr *th = skb->h.th;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	int eaten = 0;
+	int eaten = -1;
+
+	th = skb->h.th;
+	__skb_pull(skb, th->doff*4);
+
+        if (skb->len == 0 && !th->fin)
+		goto drop;
+
+	TCP_ECN_accept_cwr(tp, skb);
 
 	if (tp->dsack) {
 		tp->dsack = 0;
@@ -2535,26 +2559,32 @@
 			__set_current_state(TASK_RUNNING);
 
 			local_bh_enable();
-			if (memcpy_toiovec(tp->ucopy.iov, skb->data, chunk)) {
+			if (skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) {
 				sk->err = EFAULT;
 				sk->error_report(sk);
 			}
 			local_bh_disable();
 			tp->ucopy.len -= chunk;
 			tp->copied_seq += chunk;
-			eaten = (chunk == skb->len && !skb->h.th->fin);
+			eaten = (chunk == skb->len && !th->fin);
 		}
 
-		if (!eaten) {
+		if (eaten <= 0) {
 queue_and_out:
+			if (eaten < 0 &&
+			    (atomic_read(&sk->rmem_alloc) > sk->rcvbuf ||
+			     !tcp_rmem_schedule(sk, skb))) {
+				if (tcp_prune_queue(sk) < 0 || !tcp_rmem_schedule(sk, skb))
+					goto drop;
+			}
 			tcp_set_owner_r(skb, sk);
 			__skb_queue_tail(&sk->receive_queue, skb);
 		}
 		tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
 		if(skb->len)
 			tcp_event_data_recv(sk, tp, skb);
-		if(skb->h.th->fin)
-			tcp_fin(skb, sk, skb->h.th);
+		if(th->fin)
+			tcp_fin(skb, sk, th);
 
 		if (skb_queue_len(&tp->out_of_order_queue)) {
 			tcp_ofo_queue(sk);
@@ -2569,15 +2599,9 @@
 		if(tp->num_sacks)
 			tcp_sack_remove(tp);
 
-		/* Turn on fast path. */ 
-		if (skb_queue_len(&tp->out_of_order_queue) == 0 &&
-#ifdef TCP_FORMAL_WINDOW
-		    tcp_receive_window(tp) &&
-#endif
-		    !tp->urg_data)
-			tcp_fast_path_on(tp);
+		tcp_fast_path_check(sk, tp);
 
-		if (eaten) {
+		if (eaten > 0) {
 			__kfree_skb(skb);
 		} else if (!sk->dead)
 			sk->data_ready(sk, 0);
@@ -2592,17 +2616,12 @@
 
 out_of_window:
 		tcp_schedule_ack(tp);
+drop:
 		__kfree_skb(skb);
 		return;
 	}
 
-	/* Out of window. F.e. zero window probe.
-	 *
-	 * Note: it is highly possible that we may open window and enqueue
-	 * this segment now. However, this will be known only after we queue
-	 * it, which will result in queue full of successive 1 byte BSD
-	 * window probes, it is SWS in fact. So, always reject it and send ACK.
-	 */
+	/* Out of window. F.e. zero window probe. */
 	if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt+tcp_receive_window(tp)))
 		goto out_of_window;
 
@@ -2626,6 +2645,12 @@
 
 	TCP_ECN_check_ce(tp, skb);
 
+	if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf ||
+	    !tcp_rmem_schedule(sk, skb)) {
+		if (tcp_prune_queue(sk) < 0 || !tcp_rmem_schedule(sk, skb))
+			goto drop;
+	}
+
 	/* Disable header prediction. */
 	tp->pred_flags = 0;
 	tcp_schedule_ack(tp);
@@ -2704,52 +2729,142 @@
 	}
 }
 
-
-static void tcp_collapse_queue(struct sock *sk, struct sk_buff_head *q)
+/* Collapse contiguous sequence of skbs head..tail with
+ * sequence numbers start..end.
+ * Segments with FIN/SYN are not collapsed (only because this
+ * simplifies code)
+ */
+static void
+tcp_collapse(struct sock *sk, struct sk_buff *head,
+	     struct sk_buff *tail, u32 start, u32 end)
 {
-	struct sk_buff *skb = skb_peek(q);
-	struct sk_buff *skb_next;
+	struct sk_buff *skb;
 
-	while (skb &&
-	       skb != (struct sk_buff *)q &&
-	       (skb_next = skb->next) != (struct sk_buff *)q) {
-		struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
-		struct tcp_skb_cb *scb_next = TCP_SKB_CB(skb_next);
-
-		if (scb->end_seq == scb_next->seq &&
-		    skb_tailroom(skb) >= skb_next->len &&
-#define TCP_DONT_COLLAPSE (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN)
-		    !(tcp_flag_word(skb->h.th)&TCP_DONT_COLLAPSE) &&
-		    !(tcp_flag_word(skb_next->h.th)&TCP_DONT_COLLAPSE)) {
-			/* OK to collapse two skbs to one */
-			memcpy(skb_put(skb, skb_next->len), skb_next->data, skb_next->len);
-			__skb_unlink(skb_next, skb_next->list);
-			scb->end_seq = scb_next->end_seq;
-			__kfree_skb(skb_next);
+	/* First, check that queue is collapsable and find
+	 * the point where collapsing can be useful. */
+	for (skb = head; skb != tail; ) {
+		/* No new bits? It is possible on ofo queue. */
+		if (!before(start, TCP_SKB_CB(skb)->end_seq)) {
+			struct sk_buff *next = skb->next;
+			__skb_unlink(skb, skb->list);
+			__kfree_skb(skb);
 			NET_INC_STATS_BH(TCPRcvCollapsed);
-		} else {
-			/* Lots of spare tailroom, reallocate this skb to trim it. */
-			if (tcp_win_from_space(skb->truesize) > skb->len &&
-			    skb_tailroom(skb) > sizeof(struct sk_buff) + 16) {
-				struct sk_buff *nskb;
-
-				nskb = skb_copy_expand(skb, skb_headroom(skb), 0, GFP_ATOMIC);
-				if (nskb) {
-					tcp_set_owner_r(nskb, sk);
-					memcpy(nskb->data-skb_headroom(skb),
-					       skb->data-skb_headroom(skb),
-					       skb_headroom(skb));
-					__skb_append(skb, nskb);
-					__skb_unlink(skb, skb->list);
-					__kfree_skb(skb);
-				}
+			skb = next;
+			continue;
+		}
+
+		/* The first skb to collapse is:
+		 * - not SYN/FIN and
+		 * - bloated or contains data before "start" or
+		 *   overlaps to the next one.
+		 */
+		if (!skb->h.th->syn && !skb->h.th->fin &&
+		    (tcp_win_from_space(skb->truesize) > skb->len ||
+		     before(TCP_SKB_CB(skb)->seq, start) ||
+		     (skb->next != tail &&
+		      TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb->next)->seq)))
+			break;
+
+		/* Decided to skip this, advance start seq. */
+		start = TCP_SKB_CB(skb)->end_seq;
+		skb = skb->next;
+	}
+	if (skb == tail || skb->h.th->syn || skb->h.th->fin)
+		return;
+
+	while (before(start, end)) {
+		struct sk_buff *nskb;
+		int header = skb_headroom(skb);
+		int copy = (PAGE_SIZE - sizeof(struct sk_buff) -
+			    sizeof(struct skb_shared_info) - header - 31)&~15;
+
+		/* Too big header? This can happen with IPv6. */
+		if (copy < 0)
+			return;
+		if (end-start < copy)
+			copy = end-start;
+		nskb = alloc_skb(copy+header, GFP_ATOMIC);
+		if (!nskb)
+			return;
+		skb_reserve(nskb, header);
+		memcpy(nskb->head, skb->head, header);
+		nskb->nh.raw = nskb->head + (skb->nh.raw-skb->head);
+		nskb->h.raw = nskb->head + (skb->h.raw-skb->head);
+		nskb->mac.raw = nskb->head + (skb->mac.raw-skb->head);
+		memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
+		TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(nskb)->end_seq = start;
+		__skb_insert(nskb, skb->prev, skb, skb->list);
+		tcp_set_owner_r(nskb, sk);
+
+		/* Copy data, releasing collapsed skbs. */
+		while (copy > 0) {
+			int offset = start - TCP_SKB_CB(skb)->seq;
+			int size = TCP_SKB_CB(skb)->end_seq - start;
+
+			if (offset < 0) BUG();
+			if (size > 0) {
+				size = min(copy, size);
+				if (skb_copy_bits(skb, offset, skb_put(nskb, size), size))
+					BUG();
+				TCP_SKB_CB(nskb)->end_seq += size;
+				copy -= size;
+				start += size;
+			}
+			if (!before(start, TCP_SKB_CB(skb)->end_seq)) {
+				struct sk_buff *next = skb->next;
+				__skb_unlink(skb, skb->list);
+				__kfree_skb(skb);
+				NET_INC_STATS_BH(TCPRcvCollapsed);
+				skb = next;
+				if (skb == tail || skb->h.th->syn || skb->h.th->fin)
+					return;
 			}
-			skb = skb_next;
 		}
 	}
 }
 
-/* Clean the out_of_order queue if we can, trying to get
+/* Collapse ofo queue. Algorithm: select contiguous sequence of skbs
+ * and tcp_collapse() them until all the queue is collapsed.
+ */
+static void tcp_collapse_ofo_queue(struct sock *sk)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct sk_buff *skb = skb_peek(&tp->out_of_order_queue);
+	struct sk_buff *head;
+	u32 start, end;
+
+	if (skb == NULL)
+		return;
+
+	start = TCP_SKB_CB(skb)->seq;
+	end = TCP_SKB_CB(skb)->end_seq;
+	head = skb;
+
+	for (;;) {
+		skb = skb->next;
+
+		/* Segment is terminated when we see gap or when
+		 * we are at the end of all the queue. */
+		if (skb == (struct sk_buff *)&tp->out_of_order_queue ||
+		    after(TCP_SKB_CB(skb)->seq, end) ||
+		    before(TCP_SKB_CB(skb)->end_seq, start)) {
+			tcp_collapse(sk, head, skb, start, end);
+			head = skb;
+			if (skb == (struct sk_buff *)&tp->out_of_order_queue)
+				break;
+			/* Start new segment */
+			start = TCP_SKB_CB(skb)->seq;
+			end = TCP_SKB_CB(skb)->end_seq;
+		} else {
+			if (before(TCP_SKB_CB(skb)->seq, start))
+				start = TCP_SKB_CB(skb)->seq;
+			if (after(TCP_SKB_CB(skb)->end_seq, end))
+				end = TCP_SKB_CB(skb)->end_seq;
+		}
+	}
+}
+
+/* Reduce allocated memory if we can, trying to get
  * the socket within its memory limits again.
  *
  * Return less than zero if we should start dropping frames
@@ -2769,8 +2884,10 @@
 	else if (tcp_memory_pressure)
 		tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4*tp->advmss);
 
-	tcp_collapse_queue(sk, &sk->receive_queue);
-	tcp_collapse_queue(sk, &tp->out_of_order_queue);
+	tcp_collapse_ofo_queue(sk);
+	tcp_collapse(sk, sk->receive_queue.next,
+		     (struct sk_buff*)&sk->receive_queue,
+		     tp->copied_seq, tp->rcv_nxt);
 	tcp_mem_reclaim(sk);
 
 	if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf)
@@ -2804,59 +2921,10 @@
 	NET_INC_STATS_BH(RcvPruned);
 
 	/* Massive buffer overcommit. */
+	tp->pred_flags = 0;
 	return -1;
 }
 
-static inline int tcp_rmem_schedule(struct sock *sk, struct sk_buff *skb)
-{
-	return (int)skb->truesize <= sk->forward_alloc ||
-		tcp_mem_schedule(sk, skb->truesize, 1);
-}
-
-/*
- *	This routine handles the data.  If there is room in the buffer,
- *	it will be have already been moved into it.  If there is no
- *	room, then we will just have to discard the packet.
- */
-
-static void tcp_data(struct sk_buff *skb, struct sock *sk, unsigned int len)
-{
-	struct tcphdr *th;
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-
-	th = skb->h.th;
-	skb_pull(skb, th->doff*4);
-	skb_trim(skb, len - (th->doff*4));
-
-        if (skb->len == 0 && !th->fin)
-		goto drop;
-
-	TCP_ECN_accept_cwr(tp, skb);
-
-	/* 
-	 *	If our receive queue has grown past its limits shrink it.
-	 *	Make sure to do this before moving rcv_nxt, otherwise
-	 *	data might be acked for that we don't have enough room.
-	 */
-	if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf ||
-	    !tcp_rmem_schedule(sk, skb)) {
-		if (tcp_prune_queue(sk) < 0 || !tcp_rmem_schedule(sk, skb))
-			goto drop;
-	}
-
-	tcp_data_queue(sk, skb);
-
-#ifdef TCP_DEBUG
-	if (before(tp->rcv_nxt, tp->copied_seq)) {
-		printk(KERN_DEBUG "*** tcp.c:tcp_data bug acked < copied\n");
-		tp->rcv_nxt = tp->copied_seq;
-	}
-#endif
-	return;
-
-drop:
-	__kfree_skb(skb);
-}
 
 /* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
  * As additional protections, we do not touch cwnd in retransmission phases,
@@ -2937,7 +3005,7 @@
 
 	if (after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) ||
 	    tcp_packets_in_flight(tp) >= tp->snd_cwnd ||
-	    tcp_write_xmit(sk))
+	    tcp_write_xmit(sk, tp->nonagle))
 		tcp_check_probe_timer(sk, tp);
 }
 
@@ -3009,6 +3077,19 @@
 	if (after(tp->copied_seq, ptr))
 		return;
 
+	/* Do not replay urg ptr.
+	 *
+	 * NOTE: interesting situation not covered by specs.
+	 * Misbehaving sender may send urg ptr, pointing to segment,
+	 * which we already have in ofo queue. We are not able to fetch
+	 * such data and will stay in TCP_URG_NOTYET until will be eaten
+	 * by recvmsg(). Seems, we are not obliged to handle such wicked
+	 * situations. But it is worth to think about possibility of some
+	 * DoSes using some hypothetical application level deadlock.
+	 */
+	if (before(ptr, tp->rcv_nxt))
+		return;
+
 	/* Do we already have a newer (or duplicate) urgent pointer? */
 	if (tp->urg_data && !after(ptr, tp->urg_seq))
 		return;
@@ -3027,9 +3108,27 @@
 	 * tp->copied_seq since we would read the last urgent byte again
 	 * as data, nor can we alter copied_seq until this data arrives
 	 * or we break the sematics of SIOCATMARK (and thus sockatmark())
-	 */
-	if (tp->urg_seq == tp->copied_seq)
-		tp->copied_seq++;	/* Move the copied sequence on correctly */
+	 *
+	 * NOTE. Double Dutch. Rendering to plain English: author of comment
+	 * above did something sort of 	send("A", MSG_OOB); send("B", MSG_OOB);
+	 * and expect that both A and B disappear from stream. This is _wrong_.
+	 * Though this happens in BSD with high probability, this is occasional.
+	 * Any application relying on this is buggy. Note also, that fix "works"
+	 * only in this artificial test. Insert some normal data between A and B and we will
+	 * decline of BSD again. Verdict: it is better to remove to trap
+	 * buggy users.
+	 */
+	if (tp->urg_seq == tp->copied_seq && tp->urg_data &&
+	    !sk->urginline &&
+	    tp->copied_seq != tp->rcv_nxt) {
+		struct sk_buff *skb = skb_peek(&sk->receive_queue);
+		tp->copied_seq++;
+		if (skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq)) {
+			__skb_unlink(skb, skb->list);
+			__kfree_skb(skb);
+		}
+	}
+
 	tp->urg_data = TCP_URG_NOTYET;
 	tp->urg_seq = ptr;
 
@@ -3038,7 +3137,7 @@
 }
 
 /* This is the 'fast' part of urgent handling. */
-static inline void tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long len)
+static inline void tcp_urg(struct sock *sk, struct sk_buff *skb, struct tcphdr *th)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
@@ -3048,11 +3147,14 @@
 
 	/* Do we wait for any urgent data? - normally not... */
 	if (tp->urg_data == TCP_URG_NOTYET) {
-		u32 ptr = tp->urg_seq - ntohl(th->seq) + (th->doff*4);
+		u32 ptr = tp->urg_seq - ntohl(th->seq) + (th->doff*4) - th->syn;
 
 		/* Is the urgent pointer pointing into this packet? */	 
-		if (ptr < len) {
-			tp->urg_data = TCP_URG_VALID | *(ptr + (unsigned char *) th);
+		if (ptr < skb->len) {
+			u8 tmp;
+			if (skb_copy_bits(skb, ptr, &tmp, 1))
+				BUG();
+			tp->urg_data = TCP_URG_VALID | tmp;
 			if (!sk->dead)
 				sk->data_ready(sk,0);
 		}
@@ -3067,9 +3169,9 @@
 
 	local_bh_enable();
 	if (skb->ip_summed==CHECKSUM_UNNECESSARY)
-		err = memcpy_toiovec(tp->ucopy.iov, skb->h.raw + hlen, chunk);
+		err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk);
 	else
-		err = copy_and_csum_toiovec(tp->ucopy.iov, skb, hlen);
+		err = skb_copy_and_csum_datagram_iovec(skb, hlen, tp->ucopy.iov);
 
 	if (!err) {
 update:
@@ -3117,32 +3219,6 @@
  * 	disabled when:
  *	- A zero window was announced from us - zero window probing
  *        is only handled properly in the slow path. 
- *	  [ NOTE: actually, it was made incorrectly and nobody ever noticed
- *	    this! Reason is clear: 1. Correct senders do not send
- *	    to zero window. 2. Even if a sender sends to zero window,
- *	    nothing terrible occurs.
- *
- *	    For now I cleaned this and fast path is really always disabled,
- *	    when window is zero, but I would be more happy to remove these
- *	    checks. Code will be only cleaner and _faster_.    --ANK
- *	
- *	    Later note. I've just found that slow path also accepts
- *	    out of window segments, look at tcp_sequence(). So...
- *	    it is the last argument: I repair all and comment out
- *	    repaired code by TCP_FORMAL_WINDOW.
- *	    [ I remember one rhyme from a chidren's book. (I apologize,
- *	      the trasnlation is not rhymed 8)): people in one (jewish) village
- *	      decided to build sauna, but divided to two parties.
- *	      The first one insisted that battens should not be dubbed,
- *	      another objected that foots will suffer of splinters,
- *	      the first fended that dubbed wet battens are too slippy
- *	      and people will fall and it is much more serious!
- *	      Certaiinly, all they went to rabbi.
- *	      After some thinking, he judged: "Do not be lazy!
- *	      Certainly, dub the battens! But put them by dubbed surface down."
- *          ]
- *        ]
- *
  *	- Out of order segments arrived.
  *	- Urgent data is expected.
  *	- There is no buffer space left
@@ -3348,7 +3424,7 @@
 
 	tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
 
-	if(th->syn && TCP_SKB_CB(skb)->seq != tp->syn_seq) {
+	if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
 		TCP_INC_STATS_BH(TcpInErrs);
 		NET_INC_STATS_BH(TCPAbortOnSyn);
 		tcp_reset(sk);
@@ -3360,10 +3436,10 @@
 		tcp_ack(sk, skb, FLAG_SLOWPATH);
 
 	/* Process urgent data. */
-	tcp_urg(sk, th, len);
+	tcp_urg(sk, skb, th);
 
 	/* step 7: process the segment text */
-	tcp_data(skb, sk, len);
+	tcp_data_queue(sk, skb);
 
 	tcp_data_snd_check(sk);
 	tcp_ack_snd_check(sk);
@@ -3452,8 +3528,6 @@
 		 */
 		tp->snd_wnd = ntohs(th->window);
 		tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq);
-		tp->syn_seq = TCP_SKB_CB(skb)->seq;
-		tp->fin_seq = TCP_SKB_CB(skb)->seq;
 
 		if (tp->wscale_ok == 0) {
 			tp->snd_wscale = tp->rcv_wscale = 0;
@@ -3488,7 +3562,7 @@
 
 		/* Remember, tcp_poll() does not lock socket!
 		 * Change state from SYN-SENT only after copied_seq
-		 * is initilized. */
+		 * is initialized. */
 		tp->copied_seq = tp->rcv_nxt;
 		mb();
 		tcp_set_state(sk, TCP_ESTABLISHED);
@@ -3498,7 +3572,7 @@
 			sk_wake_async(sk, 0, POLL_OUT);
 		}
 
-		if (tp->write_pending || tp->defer_accept) {
+		if (tp->write_pending || tp->defer_accept || tp->ack.pingpong) {
 			/* Save one ACK. Data will be ready after
 			 * several ticks, if write_pending is set.
 			 *
@@ -3508,6 +3582,8 @@
 			 */
 			tcp_schedule_ack(tp);
 			tp->ack.lrcvtime = tcp_time_stamp;
+			tp->ack.ato = TCP_ATO_MIN;
+			tcp_incr_quickack(tp);
 			tcp_enter_quickack_mode(tp);
 			tcp_reset_xmit_timer(sk, TCP_TIME_DACK, TCP_DELACK_MAX);
 
@@ -3683,21 +3759,9 @@
 
 	/*	step 4:
 	 *
-	 *	Check for a SYN, and ensure it matches the SYN we were
-	 *	first sent. We have to handle the rather unusual (but valid)
-	 *	sequence that KA9Q derived products may generate of
-	 *
-	 *	SYN
-	 *				SYN|ACK Data
-	 *	ACK	(lost)
-	 *				SYN|ACK Data + More Data
-	 *	.. we must ACK not RST...
-	 *
-	 *	We keep syn_seq as the sequence space occupied by the 
-	 *	original syn. 
+	 *	Check for a SYN in window.
 	 */
-
-	if (th->syn && TCP_SKB_CB(skb)->seq != tp->syn_seq) {
+	if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
 		NET_INC_STATS_BH(TCPAbortOnSyn);
 		tcp_reset(sk);
 		return 1;
@@ -3806,13 +3870,13 @@
 
 step6:
 	/* step 6: check the URG bit */
-	tcp_urg(sk, th, len);
+	tcp_urg(sk, skb, th);
 
 	/* step 7: process the segment text */
 	switch (sk->state) {
 	case TCP_CLOSE_WAIT:
 	case TCP_CLOSING:
-		if (!before(TCP_SKB_CB(skb)->seq, tp->fin_seq))
+		if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
 			break;
 	case TCP_FIN_WAIT1:
 	case TCP_FIN_WAIT2:
@@ -3830,7 +3894,7 @@
 		}
 		/* Fall through */
 	case TCP_ESTABLISHED: 
-		tcp_data(skb, sk, len);
+		tcp_data_queue(sk, skb);
 		queued = 1;
 		break;
 	}
diff -urN linux-2.4.4-pre2/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- linux-2.4.4-pre2/net/ipv4/tcp_ipv4.c	Mon Jan  1 11:01:58 2001
+++ linux/net/ipv4/tcp_ipv4.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.222 2000/12/08 17:15:53 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.228 2001/04/06 18:41:36 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -670,6 +670,7 @@
 	}
 
 	__sk_dst_set(sk, &rt->u.dst);
+	sk->route_caps = rt->u.dst.dev->features;
 
 	if (!sk->protinfo.af_inet.opt || !sk->protinfo.af_inet.opt->srr)
 		daddr = rt->rt_dst;
@@ -717,6 +718,7 @@
 	tp->ext_header_len = 0;
 	if (sk->protinfo.af_inet.opt)
 		tp->ext_header_len = sk->protinfo.af_inet.opt->optlen;
+	sk->protinfo.af_inet.id = tp->write_seq^jiffies;
 
 	tp->mss_clamp = 536;
 
@@ -726,6 +728,7 @@
 
 failure:
 	__sk_dst_reset(sk);
+	sk->route_caps = 0;
 	sk->dport = 0;
 	return err;
 }
@@ -850,32 +853,21 @@
  *
  */
 
-void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len)
+void tcp_v4_err(struct sk_buff *skb, u32 info)
 {
-	struct iphdr *iph = (struct iphdr*)dp;
-	struct tcphdr *th; 
+	struct iphdr *iph = (struct iphdr*)skb->data;
+	struct tcphdr *th = (struct tcphdr*)(skb->data+(iph->ihl<<2));
 	struct tcp_opt *tp;
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
-#if ICMP_MIN_LENGTH < 14
-	int no_flags = 0;
-#else
-#define no_flags 0
-#endif
 	struct sock *sk;
 	__u32 seq;
 	int err;
 
-	if (len < (iph->ihl << 2) + ICMP_MIN_LENGTH) { 
+	if (skb->len < (iph->ihl << 2) + 8) {
 		ICMP_INC_STATS_BH(IcmpInErrors); 
 		return;
 	}
-#if ICMP_MIN_LENGTH < 14
-	if (len < (iph->ihl << 2) + 14)
-		no_flags = 1;
-#endif
-
-	th = (struct tcphdr*)(dp+(iph->ihl<<2));
 
 	sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, tcp_v4_iif(skb));
 	if (sk == NULL) {
@@ -921,7 +913,7 @@
 
 		if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
 			if (sk->lock.users == 0)
-				do_pmtu_discovery(sk, iph, ntohs(skb->h.icmph->un.frag.mtu));
+				do_pmtu_discovery(sk, iph, info);
 			goto out;
 		}
 
@@ -940,15 +932,6 @@
 		if (sk->lock.users != 0)
 			goto out;
 
-		/* The final ACK of the handshake should be already 
-		 * handled in the new socket context, not here.
-		 * Strictly speaking - an ICMP error for the final
-		 * ACK should set the opening flag, but that is too
-		 * complicated right now. 
-		 */ 
-		if (!no_flags && !th->syn && !th->ack)
-			goto out;
-
 		req = tcp_v4_search_req(tp, iph, th, &prev); 
 		if (!req)
 			goto out;
@@ -976,8 +959,6 @@
 	case TCP_SYN_RECV:  /* Cannot happen.
 			       It can f.e. if SYNs crossed.
 			     */ 
-		if (!no_flags && !th->syn)
-			goto out;
 		if (sk->lock.users == 0) {
 			TCP_INC_STATS_BH(TcpAttemptFails);
 			sk->err = err;
@@ -1023,8 +1004,13 @@
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
 		       struct sk_buff *skb)
 {
-	th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
-				 csum_partial((char *)th, th->doff<<2, skb->csum));
+	if (skb->ip_summed == CHECKSUM_HW) {
+		th->check = ~tcp_v4_check(th, len, sk->saddr, sk->daddr, 0);
+		skb->csum = offsetof(struct tcphdr, check);
+	} else {
+		th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
+					 csum_partial((char *)th, th->doff<<2, skb->csum));
+	}
 }
 
 /*
@@ -1445,6 +1431,7 @@
 		goto exit;
 
 	newsk->dst_cache = dst;
+	newsk->route_caps = dst->dev->features;
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 	newsk->daddr = req->af.v4_req.rmt_addr;
@@ -1457,6 +1444,7 @@
 	newtp->ext_header_len = 0;
 	if (newsk->protinfo.af_inet.opt)
 		newtp->ext_header_len = newsk->protinfo.af_inet.opt->optlen;
+	newsk->protinfo.af_inet.id = newtp->write_seq^jiffies;
 
 	tcp_sync_mss(newsk, dst->pmtu);
 	newtp->advmss = dst->advmss;
@@ -1512,23 +1500,23 @@
 static int tcp_v4_checksum_init(struct sk_buff *skb)
 {
 	if (skb->ip_summed == CHECKSUM_HW) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		if (!tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
+				  skb->nh.iph->daddr,skb->csum))
+			return 0;
+
+		NETDEBUG(printk(KERN_DEBUG "hw tcp v4 csum failed\n"));
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+	if (skb->len <= 76) {
 		if (tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
-				 skb->nh.iph->daddr,skb->csum)) {
-			NETDEBUG(printk(KERN_DEBUG "hw tcp v4 csum failed\n"));
+				 skb->nh.iph->daddr,
+				 skb_checksum(skb, 0, skb->len, 0)))
 			return -1;
-		}
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	} else {
-		if (skb->len <= 76) {
-			if (tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
-					 skb->nh.iph->daddr,
-					 csum_partial((char *)skb->h.th, skb->len, 0)))
-				return -1;
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-		} else {
-			skb->csum = ~tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
-						  skb->nh.iph->daddr,0);
-		}
+		skb->csum = ~tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
+					  skb->nh.iph->daddr,0);
 	}
 	return 0;
 }
@@ -1601,7 +1589,7 @@
  *	From tcp_input.c
  */
 
-int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
+int tcp_v4_rcv(struct sk_buff *skb)
 {
 	struct tcphdr *th;
 	struct sock *sk;
@@ -1610,31 +1598,35 @@
 	if (skb->pkt_type!=PACKET_HOST)
 		goto discard_it;
 
-	th = skb->h.th;
-
-	/* Pull up the IP header. */
-	__skb_pull(skb, skb->h.raw - skb->data);
-
 	/* Count it even if it's bad */
 	TCP_INC_STATS_BH(TcpInSegs);
 
+	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+		goto discard_it;
+
+	th = skb->h.th;
+
+	if (th->doff < sizeof(struct tcphdr)/4)
+		goto bad_packet;
+	if (!pskb_may_pull(skb, th->doff*4))
+		goto discard_it;
+
 	/* An explanation is required here, I think.
 	 * Packet length and doff are validated by header prediction,
 	 * provided case of th->doff==0 is elimineted.
 	 * So, we defer the checks. */
-	if (th->doff < sizeof(struct tcphdr)/4 ||
-	    (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+	if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
 	     tcp_v4_checksum_init(skb) < 0))
 		goto bad_packet;
 
+	th = skb->h.th;
 	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
 	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
-				    len - th->doff*4);
+				    skb->len - th->doff*4);
 	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 	TCP_SKB_CB(skb)->when = 0;
 	TCP_SKB_CB(skb)->flags = skb->nh.iph->tos;
 	TCP_SKB_CB(skb)->sacked = 0;
-	skb->used = 0;
 
 	sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
 			     skb->nh.iph->daddr, ntohs(th->dest), tcp_v4_iif(skb));
@@ -1665,7 +1657,7 @@
 	return ret;
 
 no_tcp_socket:
-	if (len < (th->doff<<2) || tcp_checksum_complete(skb)) {
+	if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 bad_packet:
 		TCP_INC_STATS_BH(TcpInErrs);
 	} else {
@@ -1682,7 +1674,7 @@
 	goto discard_it;
 
 do_time_wait:
-	if (len < (th->doff<<2) || tcp_checksum_complete(skb)) {
+	if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 		TCP_INC_STATS_BH(TcpInErrs);
 		goto discard_and_relse;
 	}
@@ -1740,7 +1732,7 @@
 		return err;
 
 	__sk_dst_set(sk, &rt->u.dst);
-	/* sk->route_caps = rt->u.dst.dev->features; */
+	sk->route_caps = rt->u.dst.dev->features;
 
 	new_saddr = rt->rt_src;
 
@@ -1788,20 +1780,19 @@
 			      sk->bound_dev_if);
 	if (!err) {
 		__sk_dst_set(sk, &rt->u.dst);
-		/* sk->route_caps = rt->u.dst.dev->features; */
+		sk->route_caps = rt->u.dst.dev->features;
 		return 0;
 	}
 
 	/* Routing failed... */
-	/* sk->route_caps = 0; */
+	sk->route_caps = 0;
 
 	if (!sysctl_ip_dynaddr ||
 	    sk->state != TCP_SYN_SENT ||
 	    (sk->userlocks & SOCK_BINDADDR_LOCK) ||
-	    (err = tcp_v4_reselect_saddr(sk)) != 0) {
+	    (err = tcp_v4_reselect_saddr(sk)) != 0)
 		sk->err_soft=-err;
-		/* sk->error_report(sk); */
-	}
+
 	return err;
 }
 
@@ -1940,7 +1931,7 @@
 	/* Cleanup up the write buffer. */
   	tcp_writequeue_purge(sk);
 
-	/* Cleans up our, hopefuly empty, out_of_order_queue. */
+	/* Cleans up our, hopefully empty, out_of_order_queue. */
   	__skb_queue_purge(&tp->out_of_order_queue);
 
 	/* Clean prequeue, it must be empty really */
@@ -1950,6 +1941,10 @@
 	if(sk->prev != NULL)
 		tcp_put_port(sk);
 
+	/* If sendmsg cached page exists, toss it. */
+	if (tp->sndmsg_page != NULL)
+		__free_page(tp->sndmsg_page);
+
 	atomic_dec(&tcp_sockets_allocated);
 
 	return 0;
@@ -2076,7 +2071,7 @@
 			if (pos >= offset) {
 				get_tcp_sock(sk, tmpbuf, num);
 				len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-				if (len >= length) {
+				if (pos >= offset + length) {
 					tcp_listen_unlock();
 					goto out_no_bh;
 				}
@@ -2097,7 +2092,7 @@
 							continue;
 						get_openreq(sk, req, tmpbuf, num, uid);
 						len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-						if(len >= length) {
+						if (pos >= offset + length) {
 							read_unlock_bh(&tp->syn_wait_lock);
 							tcp_listen_unlock();
 							goto out_no_bh;
@@ -2129,7 +2124,7 @@
 				continue;
 			get_tcp_sock(sk, tmpbuf, num);
 			len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2144,7 +2139,7 @@
 				continue;
 			get_timewait_sock(tw, tmpbuf, num);
 			len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2159,7 +2154,7 @@
 	begin = len - (pos - offset);
 	*start = buffer + begin;
 	len -= begin;
-	if(len > length)
+	if (len > length)
 		len = length;
 	if (len < 0)
 		len = 0; 
diff -urN linux-2.4.4-pre2/net/ipv4/tcp_minisocks.c linux/net/ipv4/tcp_minisocks.c
--- linux-2.4.4-pre2/net/ipv4/tcp_minisocks.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv4/tcp_minisocks.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_minisocks.c,v 1.5 2000/11/28 17:04:10 davem Exp $
+ * Version:	$Id: tcp_minisocks.c,v 1.9 2001/03/06 22:42:56 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -105,7 +105,7 @@
  *   lifetime in the internet, which results in wrong conclusion, that
  *   it is set to catch "old duplicate segments" wandering out of their path.
  *   It is not quite correct. This timeout is calculated so that it exceeds
- *   maximal retransmision timeout enough to allow to lose one (or more)
+ *   maximal retransmission timeout enough to allow to lose one (or more)
  *   segments sent by peer and our ACKs. This time may be calculated from RTO.
  * * When TIME-WAIT socket receives RST, it means that another end
  *   finally closed and we are allowed to kill TIME-WAIT too.
@@ -155,7 +155,7 @@
 		if (th->rst)
 			goto kill;
 
-		if (th->syn && TCP_SKB_CB(skb)->seq != tw->syn_seq)
+		if (th->syn && !before(TCP_SKB_CB(skb)->seq, tw->rcv_nxt))
 			goto kill_with_rst;
 
 		/* Dup ACK? */
@@ -377,7 +377,6 @@
 		tw->rcv_nxt	= tp->rcv_nxt;
 		tw->snd_nxt	= tp->snd_nxt;
 		tw->rcv_wnd	= tcp_receive_window(tp);
-		tw->syn_seq	= tp->syn_seq;
 		tw->ts_recent	= tp->ts_recent;
 		tw->ts_recent_stamp= tp->ts_recent_stamp;
 		tw->pprev_death = NULL;
@@ -691,8 +690,6 @@
 		newtp->snd_una = req->snt_isn + 1;
 		newtp->snd_sml = req->snt_isn + 1;
 
-		tcp_delack_init(newtp);
-
 		tcp_prequeue_init(newtp);
 
 		tcp_init_wl(newtp, req->snt_isn, req->rcv_isn);
@@ -734,8 +731,6 @@
 
 		newtp->probes_out = 0;
 		newtp->num_sacks = 0;
-		newtp->syn_seq = req->rcv_isn;
-		newtp->fin_seq = req->rcv_isn;
 		newtp->urg_data = 0;
 		newtp->listen_opt = NULL;
 		newtp->accept_queue = newtp->accept_queue_tail = NULL;
@@ -822,7 +817,7 @@
 		}
 	}
 
-	/* Check for pure retransmited SYN. */
+	/* Check for pure retransmitted SYN. */
 	if (TCP_SKB_CB(skb)->seq == req->rcv_isn &&
 	    flg == TCP_FLAG_SYN &&
 	    !paws_reject) {
diff -urN linux-2.4.4-pre2/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- linux-2.4.4-pre2/net/ipv4/tcp_output.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv4/tcp_output.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.129 2000/11/28 17:04:10 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.136 2001/03/06 22:42:56 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -166,20 +166,9 @@
 	/* RFC1323 scaling applied */
 	new_win >>= tp->rcv_wscale;
 
-#ifdef TCP_FORMAL_WINDOW
-	if (new_win == 0) {
-		/* If we advertise zero window, disable fast path. */
+	/* If we advertise zero window, disable fast path. */
+	if (new_win == 0)
 		tp->pred_flags = 0;
-	} else if (cur_win == 0 && tp->pred_flags == 0 &&
-		   skb_queue_len(&tp->out_of_order_queue) == 0 &&
-		   !tp->urg_data) {
-		/* If we open zero window, enable fast path.
-		   Without this it will be open by the first data packet,
-		   it is too late to merge checksumming to copy.
-		 */
-		tcp_fast_path_on(tp);
-	}
-#endif
 
 	return new_win;
 }
@@ -337,6 +326,91 @@
 		tp->send_head = skb;
 }
 
+/* Send _single_ skb sitting at the send head. This function requires
+ * true push pending frames to setup probe timer etc.
+ */
+void tcp_push_one(struct sock *sk, unsigned cur_mss)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct sk_buff *skb = tp->send_head;
+
+	if (tcp_snd_test(tp, skb, cur_mss, 1)) {
+		/* Send it out now. */
+		TCP_SKB_CB(skb)->when = tcp_time_stamp;
+		if (tcp_transmit_skb(sk, skb_clone(skb, sk->allocation)) == 0) {
+			tp->send_head = NULL;
+			tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+			if (tp->packets_out++ == 0)
+				tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
+			return;
+		}
+	}
+}
+
+/* Split fragmented skb to two parts at length len. */
+
+static void skb_split(struct sk_buff *skb, struct sk_buff *skb1, u32 len)
+{
+	int i;
+	int pos = skb->len - skb->data_len;
+
+	if (len < pos) {
+		/* Split line is inside header. */
+		memcpy(skb_put(skb1, pos-len), skb->data + len, pos-len);
+
+		/* And move data appendix as is. */
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+			skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i];
+
+		skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags;
+		skb_shinfo(skb)->nr_frags = 0;
+
+		skb1->data_len = skb->data_len;
+		skb1->len += skb1->data_len;
+		skb->data_len = 0;
+		skb->len = len;
+		skb->tail = skb->data+len;
+	} else {
+		int k = 0;
+		int nfrags = skb_shinfo(skb)->nr_frags;
+
+		/* Second chunk has no header, nothing to copy. */
+
+		skb_shinfo(skb)->nr_frags = 0;
+		skb1->len = skb1->data_len = skb->len - len;
+		skb->len = len;
+		skb->data_len = len - pos;
+
+		for (i=0; i<nfrags; i++) {
+			int size = skb_shinfo(skb)->frags[i].size;
+			if (pos + size > len) {
+				skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i];
+
+				if (pos < len) {
+					/* Split frag.
+					 * We have to variants in this case:
+					 * 1. Move all the frag to the second
+					 *    part, if it is possible. F.e.
+					 *    this approach is mandatory for TUX,
+					 *    where splitting is expensive.
+					 * 2. Split is accurately. We make this.
+					 */
+					get_page(skb_shinfo(skb)->frags[i].page);
+					skb_shinfo(skb1)->frags[0].page_offset += (len-pos);
+					skb_shinfo(skb1)->frags[0].size -= (len-pos);
+					skb_shinfo(skb)->frags[i].size = len-pos;
+					skb_shinfo(skb)->nr_frags++;
+				}
+				k++;
+			} else {
+				skb_shinfo(skb)->nr_frags++;
+			}
+			pos += size;
+		}
+		skb_shinfo(skb1)->nr_frags = k;
+	}
+}
+
 /* Function to create two new TCP segments.  Shrinks the given segment
  * to the specified size and appends a new segment with the rest of the
  * packet to the list.  This won't be called frequently, I hope. 
@@ -349,19 +423,22 @@
 	int nsize = skb->len - len;
 	u16 flags;
 
+	if (skb_cloned(skb) &&
+	    skb_is_nonlinear(skb) &&
+	    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+		return -ENOMEM;
+
 	/* Get a new skb... force flag on. */
-	buff = tcp_alloc_skb(sk, nsize + MAX_TCP_HEADER, GFP_ATOMIC);
+	buff = tcp_alloc_skb(sk, nsize, GFP_ATOMIC);
 	if (buff == NULL)
 		return -ENOMEM; /* We'll just try again later. */
 	tcp_charge_skb(sk, buff);
 
-	/* Reserve space for headers. */
-	skb_reserve(buff, MAX_TCP_HEADER);
-		
 	/* Correct the sequence numbers. */
 	TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len;
 	TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq;
-	
+	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq;
+
 	/* PSH and FIN should only be set in the second packet. */
 	flags = TCP_SKB_CB(skb)->flags;
 	TCP_SKB_CB(skb)->flags = flags & ~(TCPCB_FLAG_FIN|TCPCB_FLAG_PSH);
@@ -371,18 +448,22 @@
 		tp->lost_out++;
 		tp->left_out++;
 	}
-	TCP_SKB_CB(buff)->sacked &= ~TCPCB_AT_TAIL;
+	TCP_SKB_CB(skb)->sacked &= ~TCPCB_AT_TAIL;
 
-	/* Copy and checksum data tail into the new buffer. */
-	buff->csum = csum_partial_copy_nocheck(skb->data + len, skb_put(buff, nsize),
-					       nsize, 0);
+	if (!skb_shinfo(skb)->nr_frags && skb->ip_summed != CHECKSUM_HW) {
+		/* Copy and checksum data tail into the new buffer. */
+		buff->csum = csum_partial_copy_nocheck(skb->data + len, skb_put(buff, nsize),
+						       nsize, 0);
 
-	/* This takes care of the FIN sequence number too. */
-	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq;
-	skb_trim(skb, len);
+		skb_trim(skb, len);
 
-	/* Rechecksum original buffer. */
-	skb->csum = csum_partial(skb->data, skb->len, 0);
+		skb->csum = csum_block_sub(skb->csum, buff->csum, len);
+	} else {
+		skb->ip_summed = CHECKSUM_HW;
+		skb_split(skb, buff, len);
+	}
+
+	buff->ip_summed = skb->ip_summed;
 
 	/* Looks stupid, but our code really uses when of
 	 * skbs, which it never sent before. --ANK
@@ -461,7 +542,7 @@
  * Returns 1, if no segments are in flight and we have queued segments, but
  * cannot send anything now because of SWS or another problem.
  */
-int tcp_write_xmit(struct sock *sk)
+int tcp_write_xmit(struct sock *sk, int nonagle)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	unsigned int mss_now;
@@ -482,7 +563,7 @@
 		mss_now = tcp_current_mss(sk); 
 
 		while((skb = tp->send_head) &&
-		      tcp_snd_test(tp, skb, mss_now, tcp_skb_is_last(sk, skb) ? tp->nonagle : 1)) {
+		      tcp_snd_test(tp, skb, mss_now, tcp_skb_is_last(sk, skb) ? nonagle : 1)) {
 			if (skb->len > mss_now) {
 				if (tcp_fragment(sk, skb, mss_now))
 					break;
@@ -568,22 +649,21 @@
 	 * but may be worse for the performance because of rcv_mss
 	 * fluctuations.  --SAW  1998/11/1
 	 */
-	unsigned int mss = tp->ack.rcv_mss;
-	int free_space;
-	u32 window;
-
-	/* Sometimes free_space can be < 0. */
-	free_space = tcp_space(sk); 
-	if (tp->window_clamp < mss)
-		mss = tp->window_clamp; 
+	int mss = tp->ack.rcv_mss;
+	int free_space = tcp_space(sk);
+	int full_space = min(tp->window_clamp, tcp_full_space(sk));
+	int window;
+
+	if (mss > full_space)
+		mss = full_space; 
 
-	if (free_space < (int)min(tp->window_clamp, tcp_full_space(sk)) / 2) {
+	if (free_space < full_space/2) {
 		tp->ack.quick = 0;
 
 		if (tcp_memory_pressure)
 			tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4*tp->advmss);
 
-		if (free_space < ((int)mss))
+		if (free_space < mss)
 			return 0;
 	}
 
@@ -599,9 +679,8 @@
 	 * is too small.
 	 */
 	window = tp->rcv_wnd;
-	if ((((int) window) <= (free_space - ((int) mss))) ||
-	    (((int) window) > free_space))
-		window = (((unsigned int) free_space)/mss)*mss;
+	if (window <= free_space - mss || window > free_space)
+		window = (free_space/mss)*mss;
 
 	return window;
 }
@@ -638,19 +717,14 @@
 		/* Ok.  We will be able to collapse the packet. */
 		__skb_unlink(next_skb, next_skb->list);
 
-		if(skb->len % 4) {
-			/* Must copy and rechecksum all data. */
+		if (next_skb->ip_summed == CHECKSUM_HW)
+			skb->ip_summed = CHECKSUM_HW;
+
+		if (skb->ip_summed != CHECKSUM_HW) {
 			memcpy(skb_put(skb, next_skb_size), next_skb->data, next_skb_size);
-			skb->csum = csum_partial(skb->data, skb->len, 0);
-		} else {
-			/* Optimize, actually we could also combine next_skb->csum
-			 * to skb->csum using a single add w/carry operation too.
-			 */
-			skb->csum = csum_partial_copy_nocheck(next_skb->data,
-							      skb_put(skb, next_skb_size),
-							      next_skb_size, skb->csum);
+			skb->csum = csum_block_add(skb->csum, next_skb->csum, skb->len);
 		}
-	
+
 		/* Update sequence range on original skb. */
 		TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(next_skb)->end_seq;
 
@@ -668,11 +742,12 @@
 			tp->lost_out--;
 			tp->left_out--;
 		}
+		/* Reno case is special. Sigh... */
 		if (!tp->sack_ok && tp->sacked_out) {
-			/* Reno case is special. Sigh... */
 			tp->sacked_out--;
 			tp->left_out--;
 		}
+
 		/* Not quite right: it can be > snd.fack, but
 		 * it is better to underestimate fackets.
 		 */
@@ -712,7 +787,7 @@
 	if (!lost)
 		return;
 
-	tp->left_out = tp->sacked_out + tp->lost_out;
+	tcp_sync_left_out(tp);
 
  	/* Don't muck with the congestion window here.
 	 * Reason is that we do not increase amount of _data_
@@ -745,6 +820,15 @@
 	if (atomic_read(&sk->wmem_alloc) > min(sk->wmem_queued+(sk->wmem_queued>>2),sk->sndbuf))
 		return -EAGAIN;
 
+	/* If receiver has shrunk his window, and skb is out of
+	 * new window, do not retransmit it. The exception is the
+	 * case, when window is shrunk to zero. In this case
+	 * our retransmit serves as a zero window probe.
+	 */
+	if (!before(TCP_SKB_CB(skb)->seq, tp->snd_una+tp->snd_wnd)
+	    && TCP_SKB_CB(skb)->seq != tp->snd_una)
+		return -EAGAIN;
+
 	if(skb->len > cur_mss) {
 		if(tcp_fragment(sk, skb, cur_mss))
 			return -ENOMEM; /* We'll try again later. */
@@ -758,6 +842,7 @@
 	   (skb->len < (cur_mss >> 1)) &&
 	   (skb->next != tp->send_head) &&
 	   (skb->next != (struct sk_buff *)&sk->write_queue) &&
+	   (skb_shinfo(skb)->nr_frags == 0 && skb_shinfo(skb->next)->nr_frags == 0) &&
 	   (sysctl_tcp_retrans_collapse != 0))
 		tcp_retrans_try_collapse(sk, skb, cur_mss);
 
@@ -771,9 +856,11 @@
 	if(skb->len > 0 &&
 	   (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
 	   tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
-		TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1;
-		skb_trim(skb, 0);
-		skb->csum = 0;
+		if (!pskb_trim(skb, 0)) {
+			TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1;
+			skb->ip_summed = CHECKSUM_NONE;
+			skb->csum = 0;
+		}
 	}
 
 	/* Make a copy, if the first transmission SKB clone we made
@@ -782,7 +869,7 @@
 	TCP_SKB_CB(skb)->when = tcp_time_stamp;
 
 	err = tcp_transmit_skb(sk, (skb_cloned(skb) ?
-				    skb_copy(skb, GFP_ATOMIC):
+				    pskb_copy(skb, GFP_ATOMIC):
 				    skb_clone(skb, GFP_ATOMIC)));
 
 	if (err == 0) {
@@ -912,28 +999,10 @@
 	 */
 	mss_now = tcp_current_mss(sk); 
 
-	/* Please, find seven differences of 2.3.33 and loook
-	 * what I broke here. 8) --ANK
-	 */
-
 	if(tp->send_head != NULL) {
-		/* tcp_write_xmit() takes care of the rest. */
 		TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
 		TCP_SKB_CB(skb)->end_seq++;
 		tp->write_seq++;
-
-		/* Special case to avoid Nagle bogosity.  If this
-		 * segment is the last segment, and it was queued
-		 * due to Nagle/SWS-avoidance, send it out now.
-		 */
-		if(tp->send_head == skb &&
-		   !after(tp->write_seq, tp->snd_una + tp->snd_wnd)) {
-			TCP_SKB_CB(skb)->when = tcp_time_stamp;
-			if (!tcp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL)))
-				update_send_head(sk, tp, skb);
-			else
-				tcp_check_probe_timer(sk, tp);
-		}
 	} else {
 		/* Socket is locked, keep trying until memory is available. */
 		for (;;) {
@@ -953,9 +1022,9 @@
 		/* FIN eats a sequence byte, write_seq advanced by tcp_send_skb(). */
 		TCP_SKB_CB(skb)->seq = tp->write_seq;
 		TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
-		tcp_send_skb(sk, skb, 0, mss_now);
-		__tcp_push_pending_frames(sk, tp, mss_now, 1);
+		tcp_send_skb(sk, skb, 1, mss_now);
 	}
+	__tcp_push_pending_frames(sk, tp, mss_now, 1);
 }
 
 /* We get here when a process closes a file descriptor (either due to
@@ -1224,23 +1293,6 @@
 	tp->ack.timeout = timeout;
 	if (!mod_timer(&tp->delack_timer, timeout))
 		sock_hold(sk);
-
-#ifdef TCP_FORMAL_WINDOW
-	/* Explanation. Header prediction path does not handle
-	 * case of zero window. If we send ACK immediately, pred_flags
-	 * are reset when sending ACK. If rcv_nxt is advanced and
-	 * ack is not sent, than delayed ack is scheduled.
-	 * Hence, it is the best place to check for zero window.
-	 */
-	if (tp->pred_flags) {
-		if (tcp_receive_window(tp) == 0)
-			tp->pred_flags = 0;
-	} else {
-		if (skb_queue_len(&tp->out_of_order_queue) == 0 &&
-		    !tp->urg_data)
-			tcp_fast_path_on(tp);
-	}
-#endif
 }
 
 /* This routine sends an ack and also updates the window. */
diff -urN linux-2.4.4-pre2/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- linux-2.4.4-pre2/net/ipv4/tcp_timer.c	Fri Feb  9 11:34:13 2001
+++ linux/net/ipv4/tcp_timer.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.81 2001/01/01 02:38:30 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.83 2001/03/07 22:00:57 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -326,6 +326,29 @@
 
 	BUG_TRAP(!skb_queue_empty(&sk->write_queue));
 
+	if (tp->snd_wnd == 0 && !sk->dead &&
+	    !((1<<sk->state)&(TCPF_SYN_SENT|TCPF_SYN_RECV))) {
+		/* Receiver dastardly shrinks window. Our retransmits
+		 * become zero probes, but we should not timeout this
+		 * connection. If the socket is an orphan, time it out,
+		 * we cannot allow such beasts to hang infinitely.
+		 */
+#ifdef TCP_DEBUG
+		if (net_ratelimit())
+			printk(KERN_DEBUG "TCP: Treason uncloaked! Peer %u.%u.%u.%u:%u/%u shrinks window %u:%u. Repaired.\n",
+			       NIPQUAD(sk->daddr), htons(sk->dport), sk->num,
+			       tp->snd_una, tp->snd_nxt);
+#endif
+		if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
+			tcp_write_err(sk);
+			goto out;
+		}
+		tcp_enter_loss(sk, 0);
+		tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
+		__sk_dst_reset(sk);
+		goto out_reset_timer;
+	}
+
 	if (tcp_write_timeout(sk))
 		goto out;
 
@@ -379,6 +402,8 @@
 	 */
 	tp->backoff++;
 	tp->retransmits++;
+
+out_reset_timer:
 	tp->rto = min(tp->rto << 1, TCP_RTO_MAX);
 	tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
 	if (tp->retransmits > sysctl_tcp_retries1)
diff -urN linux-2.4.4-pre2/net/ipv4/udp.c linux/net/ipv4/udp.c
--- linux-2.4.4-pre2/net/ipv4/udp.c	Fri Feb  9 11:34:13 2001
+++ linux/net/ipv4/udp.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.91 2000/11/28 13:38:38 davem Exp $
+ * Version:	$Id: udp.c,v 1.98 2001/03/06 21:15:10 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -291,22 +291,16 @@
  * to find the appropriate port.
  */
 
-void udp_err(struct sk_buff *skb, unsigned char *dp, int len)
+void udp_err(struct sk_buff *skb, u32 info)
 {
-	struct iphdr *iph = (struct iphdr*)dp;
-	struct udphdr *uh = (struct udphdr*)(dp+(iph->ihl<<2));
+	struct iphdr *iph = (struct iphdr*)skb->data;
+	struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2));
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
 	struct sock *sk;
 	int harderr;
-	u32 info;
 	int err;
 
-	if (len < (iph->ihl<<2)+sizeof(struct udphdr)) {
-		ICMP_INC_STATS_BH(IcmpInErrors);
-		return;
-	}
-
 	sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex);
 	if (sk == NULL) {
 		ICMP_INC_STATS_BH(IcmpInErrors);
@@ -314,7 +308,6 @@
 	}
 
 	err = 0;
-	info = 0;
 	harderr = 0;
 
 	switch (type) {
@@ -326,14 +319,12 @@
 		goto out;
 	case ICMP_PARAMETERPROB:
 		err = EPROTO;
-		info = ntohl(skb->h.icmph->un.gateway)>>24;
 		harderr = 1;
 		break;
 	case ICMP_DEST_UNREACH:
 		if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
 			if (sk->protinfo.af_inet.pmtudisc != IP_PMTUDISC_DONT) {
 				err = EMSGSIZE;
-				info = ntohs(skb->h.icmph->un.frag.mtu);
 				harderr = 1;
 				break;
 			}
@@ -379,10 +370,7 @@
 };
 
 /*
- *	Copy and checksum a UDP packet from user space into a buffer. We still have
- *	to do the planning to get ip_build_xmit to spot direct transfer to network
- *	card and provide an additional callback mode for direct user->board I/O
- *	transfers. That one will be fun.
+ *	Copy and checksum a UDP packet from user space into a buffer.
  */
  
 static int udp_getfrag(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
@@ -409,10 +397,7 @@
 }
 
 /*
- *	Unchecksummed UDP is sufficiently critical to stuff like ATM video conferencing
- *	that we use two routines for this for speed. Probably we ought to have a
- *	CONFIG_FAST_NET set for >10Mb/second boards to activate this sort of coding.
- *	Timing needed to verify if this is a valid decision.
+ *	Copy a UDP packet from user space into a buffer without checksumming.
  */
  
 static int udp_getfrag_nosum(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
@@ -625,7 +610,7 @@
 
 static __inline__ int __udp_checksum_complete(struct sk_buff *skb)
 {
-	return (unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum));
+	return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
 }
 
 static __inline__ int udp_checksum_complete(struct sk_buff *skb)
@@ -655,11 +640,6 @@
 	if (flags & MSG_ERRQUEUE)
 		return ip_recv_error(sk, msg, len);
 
-	/*
-	 *	From here the generic datagram does a lot of the work. Come
-	 *	the finished NET3, it will do _ALL_ the work!
-	 */
-
 	skb = skb_recv_datagram(sk, flags, noblock, &err);
 	if (!skb)
 		goto out;
@@ -679,9 +659,9 @@
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					      copied);
 	} else {
-		err = copy_and_csum_toiovec(msg->msg_iov, skb, sizeof(struct udphdr));
+		err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);
 
-		if (err)
+		if (err == -EINVAL)
 			goto csum_copy_err;
 	}
 
@@ -758,6 +738,7 @@
 	sk->daddr = rt->rt_dst;
 	sk->dport = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
+	sk->protinfo.af_inet.id = jiffies;
 
 	sk_dst_set(sk, &rt->u.dst);
 	return(0);
@@ -872,10 +853,13 @@
 	if (uh->check == 0) {
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	} else if (skb->ip_summed == CHECKSUM_HW) {
-		if (udp_check(uh, ulen, saddr, daddr, skb->csum))
-			return -1;
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
-	} else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+		if (!udp_check(uh, ulen, saddr, daddr, skb->csum))
+			return 0;
+		NETDEBUG(printk(KERN_DEBUG "udp v4 hw csum failure.\n"));
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+	if (skb->ip_summed != CHECKSUM_UNNECESSARY)
 		skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
 	/* Probably, we should checksum udp header (it should be in cache
 	 * in any case) and data in tiny packets (< rx copybreak).
@@ -887,7 +871,7 @@
  *	All we need to do is get the socket, and then do a checksum. 
  */
  
-int udp_rcv(struct sk_buff *skb, unsigned short len)
+int udp_rcv(struct sk_buff *skb)
 {
   	struct sock *sk;
   	struct udphdr *uh;
@@ -895,29 +879,22 @@
 	struct rtable *rt = (struct rtable*)skb->dst;
 	u32 saddr = skb->nh.iph->saddr;
 	u32 daddr = skb->nh.iph->daddr;
-
-	/*
-	 *	Get the header.
-	 */
-	 
-  	uh = skb->h.uh;
-	__skb_pull(skb, skb->h.raw - skb->data);
+	int len = skb->len;
 
   	IP_INC_STATS_BH(IpInDelivers);
 
 	/*
 	 *	Validate the packet and the UDP length.
 	 */
-	 
-	ulen = ntohs(uh->len);
+	ulen = ntohs(skb->h.uh->len);
 
-	if (ulen > len || ulen < sizeof(*uh)) {
-		NETDEBUG(printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len));
-		UDP_INC_STATS_BH(UdpInErrors);
-		kfree_skb(skb);
-		return(0);
-	}
-	skb_trim(skb, ulen);
+	if (ulen > len || ulen < sizeof(*uh))
+		goto short_packet;
+
+	if (pskb_trim(skb, ulen))
+		goto short_packet;
+
+  	uh = skb->h.uh;
 
 	if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0)
 		goto csum_error;
@@ -944,6 +921,12 @@
 	 * Hmm.  We got an UDP packet to a port to which we
 	 * don't wanna listen.  Ignore it.
 	 */
+	kfree_skb(skb);
+	return(0);
+
+short_packet:
+	NETDEBUG(printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len));
+	UDP_INC_STATS_BH(UdpInErrors);
 	kfree_skb(skb);
 	return(0);
 
diff -urN linux-2.4.4-pre2/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- linux-2.4.4-pre2/net/ipv6/af_inet6.c	Mon Oct 23 15:48:28 2000
+++ linux/net/ipv6/af_inet6.c	Mon Dec  3 22:34:51 2001
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.60 2000/10/19 01:05:34 davem Exp $
+ *	$Id: af_inet6.c,v 1.63 2001/03/02 03:13:05 davem Exp $
  *
  * 	Fixes:
  * 	Hideaki YOSHIFUJI	:	sin6_scope_id support
@@ -461,6 +461,7 @@
 	sendmsg:	inet_sendmsg,			/* ok		*/
 	recvmsg:	inet_recvmsg,			/* ok		*/
 	mmap:		sock_no_mmap,
+	sendpage:	tcp_sendpage
 };
 
 struct proto_ops inet6_dgram_ops = {
@@ -481,6 +482,7 @@
 	sendmsg:	inet_sendmsg,			/* ok		*/
 	recvmsg:	inet_recvmsg,			/* ok		*/
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 struct net_proto_family inet6_family_ops = {
diff -urN linux-2.4.4-pre2/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- linux-2.4.4-pre2/net/ipv6/datagram.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv6/datagram.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: datagram.c,v 1.21 2000/11/28 13:42:08 davem Exp $
+ *	$Id: datagram.c,v 1.22 2000/12/13 18:31:50 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -57,7 +57,7 @@
 	serr->port = port;
 
 	skb->h.raw = payload;
-	skb_pull(skb, payload - skb->data);
+	__skb_pull(skb, payload - skb->data);
 
 	if (sock_queue_err_skb(sk, skb))
 		kfree_skb(skb);
@@ -92,7 +92,7 @@
 	serr->port = fl->uli_u.ports.dport;
 
 	skb->h.raw = skb->tail;
-	skb_pull(skb, skb->tail - skb->data);
+	__skb_pull(skb, skb->tail - skb->data);
 
 	if (sock_queue_err_skb(sk, skb))
 		kfree_skb(skb);
@@ -123,7 +123,7 @@
 		msg->msg_flags |= MSG_TRUNC;
 		copied = len;
 	}
-	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	if (err)
 		goto out_free_skb;
 
diff -urN linux-2.4.4-pre2/net/ipv6/exthdrs.c linux/net/ipv6/exthdrs.c
--- linux-2.4.4-pre2/net/ipv6/exthdrs.c	Sat Jan  8 21:36:21 2000
+++ linux/net/ipv6/exthdrs.c	Mon Dec  3 22:34:51 2001
@@ -7,7 +7,7 @@
  *	Andi Kleen		<ak@muc.de>
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: exthdrs.c,v 1.10 2000/01/09 02:19:55 davem Exp $
+ *	$Id: exthdrs.c,v 1.12 2001/01/22 02:36:37 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -15,6 +15,11 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+/* Changes:
+ *	yoshfuji		: ensure not to overrun while parsing 
+ *				  tlv options.
+ */
+
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -41,15 +46,15 @@
 /*
  *	Parsing inbound headers.
  *
- *	Parsing function "func" returns pointer to the place,
+ *	Parsing function "func" returns offset wrt skb->nh of the place,
  *	where next nexthdr value is stored or NULL, if parsing
- *	failed. It should also update skb->h.
+ *	failed. It should also update skb->h tp point at the next header.
  */
 
 struct hdrtype_proc
 {
 	int	type;
-	u8*	(*func) (struct sk_buff **, u8 *ptr);
+	int	(*func) (struct sk_buff **, int offset);
 };
 
 /*
@@ -63,7 +68,7 @@
 struct tlvtype_proc
 {
 	int	type;
-	int	(*func) (struct sk_buff *, __u8 *ptr);
+	int	(*func) (struct sk_buff *, int offset);
 };
 
 /*********************
@@ -72,12 +77,12 @@
 
 /* An unknown option is detected, decide what to do */
 
-int ip6_tlvopt_unknown(struct sk_buff *skb, u8 *opt)
+int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff)
 {
-	switch ((opt[0] & 0xC0) >> 6) {
+	switch ((skb->nh.raw[optoff] & 0xC0) >> 6) {
 	case 0: /* ignore */
 		return 1;
-		
+
 	case 1: /* drop packet */
 		break;
 
@@ -88,7 +93,7 @@
 		if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
 			break;
 	case 2: /* send ICMP PARM PROB regardless and drop packet */
-		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, opt);
+		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
 		return 0;
 	};
 
@@ -98,24 +103,21 @@
 
 /* Parse tlv encoded option header (hop-by-hop or destination) */
 
-static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
-			 __u8 *nhptr)
+static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb)
 {
 	struct tlvtype_proc *curr;
-	u8 *ptr = skb->h.raw;
-	int len = ((ptr[1]+1)<<3) - 2;
+	int off = skb->h.raw - skb->nh.raw;
+	int len = ((skb->h.raw[1]+1)<<3);
 
-	ptr += 2;
+	if ((skb->h.raw + len) - skb->data > skb_headlen(skb))
+		goto bad;
 
-	if (skb->tail - (ptr + len) < 0) {
-		kfree_skb(skb);
-		return 0;
-	}
+	len -= 2;
 
 	while (len > 0) {
-		int optlen = ptr[1]+2;
+		int optlen = skb->nh.raw[off+1]+2;
 
-		switch (ptr[0]) {
+		switch (skb->nh.raw[off]) {
 		case IPV6_TLV_PAD0:
 			optlen = 1;
 			break;
@@ -124,24 +126,30 @@
 			break;
 
 		default: /* Other TLV code so scan list */
+			if (optlen > len)
+				goto bad;
 			for (curr=procs; curr->type >= 0; curr++) {
-				if (curr->type == ptr[0]) {
-					if (curr->func(skb, ptr) == 0)
+				if (curr->type == skb->nh.raw[off]) {
+					/* type specific length/alignment 
+					   checks will be perfomed in the 
+					   func(). */
+					if (curr->func(skb, off) == 0)
 						return 0;
 					break;
 				}
 			}
 			if (curr->type < 0) {
-				if (ip6_tlvopt_unknown(skb, ptr) == 0)
+				if (ip6_tlvopt_unknown(skb, off) == 0)
 					return 0;
 			}
 			break;
 		}
-		ptr += optlen;
+		off += optlen;
 		len -= optlen;
 	}
 	if (len == 0)
 		return 1;
+bad:
 	kfree_skb(skb);
 	return 0;
 }
@@ -155,37 +163,42 @@
 	{-1,			NULL}
 };
 
-static u8 *ipv6_dest_opt(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, int nhoff)
 {
 	struct sk_buff *skb=*skb_ptr;
 	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
-	struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
 
-	opt->dst1 = (u8*)hdr - skb->nh.raw;
+	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
+	    !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
+		kfree_skb(skb);
+		return -1;
+	}
+
+	opt->dst1 = skb->h.raw - skb->nh.raw;
 
-	if (ip6_parse_tlv(tlvprocdestopt_lst, skb, nhptr)) {
-		skb->h.raw += ((hdr->hdrlen+1)<<3);
-		return &hdr->nexthdr;
+	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
+		skb->h.raw += ((skb->h.raw[1]+1)<<3);
+		return opt->dst1;
 	}
 
-	return NULL;
+	return -1;
 }
 
 /********************************
   NONE header. No data in packet.
  ********************************/
 
-static u8 *ipv6_nodata(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_nodata(struct sk_buff **skb_ptr, int nhoff)
 {
 	kfree_skb(*skb_ptr);
-	return NULL;
+	return -1;
 }
 
 /********************************
   Routing header.
  ********************************/
 
-static u8* ipv6_routing_header(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
 {
 	struct sk_buff *skb = *skb_ptr;
 	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
@@ -194,36 +207,38 @@
 	int addr_type;
 	int n, i;
 
-	struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
+	struct ipv6_rt_hdr *hdr;
 	struct rt0_hdr *rthdr;
 
-	if (((hdr->hdrlen+1)<<3) > skb->tail - skb->h.raw) {
+	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
+	    !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
 		IP6_INC_STATS_BH(Ip6InHdrErrors);
 		kfree_skb(skb);
-		return NULL;
+		return -1;
+	}
+
+	hdr = (struct ipv6_rt_hdr *) skb->h.raw;
+
+	if ((ipv6_addr_type(&skb->nh.ipv6h->daddr)&IPV6_ADDR_MULTICAST) ||
+	    skb->pkt_type != PACKET_HOST) {
+		kfree_skb(skb);
+		return -1;
 	}
 
 looped_back:
 	if (hdr->segments_left == 0) {
-		opt->srcrt = (u8*)hdr - skb->nh.raw;
+		opt->srcrt = skb->h.raw - skb->nh.raw;
 		skb->h.raw += (hdr->hdrlen + 1) << 3;
 		opt->dst0 = opt->dst1;
 		opt->dst1 = 0;
-		return &hdr->nexthdr;		
+		return (&hdr->nexthdr) - skb->nh.raw;
 	}
 
-	if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01) {
-		u8 *pos = (u8*) hdr;
-
-		if (hdr->type != IPV6_SRCRT_TYPE_0)
-			pos += 2;
-		else
-			pos += 1;
-
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, pos);
-		return NULL;	
+	if (hdr->type != IPV6_SRCRT_TYPE_0 || (hdr->hdrlen & 0x01)) {
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, hdr->type != IPV6_SRCRT_TYPE_0 ? 2 : 1);
+		return -1;
 	}
-	
+
 	/*
 	 *	This is the routing header forwarding algorithm from
 	 *	RFC 1883, page 17.
@@ -232,8 +247,8 @@
 	n = hdr->hdrlen >> 1;
 
 	if (hdr->segments_left > n) {
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, &hdr->segments_left);
-		return NULL;
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
+		return -1;
 	}
 
 	/* We are about to mangle packet header. Be careful!
@@ -243,12 +258,15 @@
 		struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
 		kfree_skb(skb);
 		if (skb2 == NULL)
-			return NULL;
+			return -1;
 		*skb_ptr = skb = skb2;
 		opt = (struct inet6_skb_parm *)skb2->cb;
 		hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
 	}
 
+	if (skb->ip_summed == CHECKSUM_HW)
+		skb->ip_summed = CHECKSUM_NONE;
+
 	i = n - --hdr->segments_left;
 
 	rthdr = (struct rt0_hdr *) hdr;
@@ -257,9 +275,9 @@
 
 	addr_type = ipv6_addr_type(addr);
 
-	if (addr_type == IPV6_ADDR_MULTICAST) {
+	if (addr_type&IPV6_ADDR_MULTICAST) {
 		kfree_skb(skb);
-		return NULL;
+		return -1;
 	}
 
 	ipv6_addr_copy(&daddr, addr);
@@ -270,21 +288,21 @@
 	ip6_route_input(skb);
 	if (skb->dst->error) {
 		skb->dst->input(skb);
-		return NULL;
+		return -1;
 	}
 	if (skb->dst->dev->flags&IFF_LOOPBACK) {
 		if (skb->nh.ipv6h->hop_limit <= 1) {
 			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
 				    0, skb->dev);
 			kfree_skb(skb);
-			return NULL;
+			return -1;
 		}
 		skb->nh.ipv6h->hop_limit--;
 		goto looped_back;
 	}
 
 	skb->dst->input(skb);
-	return NULL;
+	return -1;
 }
 
 /*
@@ -374,20 +392,30 @@
    and opt->hdrlen is even. Shit!		--ANK (980730)
  */
 
-static u8 *ipv6_auth_hdr(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_auth_hdr(struct sk_buff **skb_ptr, int nhoff)
 {
 	struct sk_buff *skb=*skb_ptr;
 	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
-	struct ipv6_opt_hdr *hdr = (struct ipv6_opt_hdr *)skb->h.raw;
-	int len = (hdr->hdrlen+2)<<2;
+	int len;
+
+	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8))
+		goto fail;
+
+	len = (skb->h.raw[1]+1)<<2;
 
 	if (len&7)
-		return NULL;
-	opt->auth = (u8*)hdr - skb->nh.raw;
-	if (skb->h.raw + len > skb->tail)
-		return NULL;
+		goto fail;
+
+	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+len))
+		goto fail;
+
+	opt->auth = skb->h.raw - skb->nh.raw;
 	skb->h.raw += len;
-	return &hdr->nexthdr;
+	return opt->auth;
+
+fail:
+	kfree_skb(skb);
+	return -1;
 }
 
 /* This list MUST NOT contain entry for NEXTHDR_HOP.
@@ -408,22 +436,22 @@
 	{-1,			NULL}
 };
 
-u8 *ipv6_parse_exthdrs(struct sk_buff **skb_in, u8 *nhptr)
+int ipv6_parse_exthdrs(struct sk_buff **skb_in, int nhoff)
 {
 	struct hdrtype_proc *hdrt;
-	u8 nexthdr = *nhptr;
+	u8 nexthdr = (*skb_in)->nh.raw[nhoff];
 
 restart:
 	for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
 		if (hdrt->type == nexthdr) {
-			if ((nhptr = hdrt->func(skb_in, nhptr)) != NULL) {
-				nexthdr = *nhptr;
+			if ((nhoff = hdrt->func(skb_in, nhoff)) >= 0) {
+				nexthdr = (*skb_in)->nh.raw[nhoff];
 				goto restart;
 			}
-			return NULL;
+			return -1;
 		}
 	}
-	return nhptr;
+	return nhoff;
 }
 
 
@@ -433,37 +461,37 @@
 
 /* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */
 
-static int ipv6_hop_ra(struct sk_buff *skb, u8 *ptr)
+static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
 {
-	if (ptr[1] == 2) {
-		((struct inet6_skb_parm*)skb->cb)->ra = ptr - skb->nh.raw;
+	if (skb->nh.raw[optoff+1] == 2) {
+		((struct inet6_skb_parm*)skb->cb)->ra = optoff;
 		return 1;
 	}
 	if (net_ratelimit())
-		printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", ptr[1]);
+		printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", skb->nh.raw[optoff+1]);
 	kfree_skb(skb);
 	return 0;
 }
 
 /* Jumbo payload */
 
-static int ipv6_hop_jumbo(struct sk_buff *skb, u8 *ptr)
+static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
 {
 	u32 pkt_len;
 
-	if (ptr[1] != 4 || ((ptr-skb->nh.raw)&3) != 2) {
+	if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
 		if (net_ratelimit())
-			printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", ptr[1]);
+			printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", skb->nh.raw[optoff+1]);
 		goto drop;
 	}
 
-	pkt_len = ntohl(*(u32*)(ptr+2));
+	pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2));
 	if (pkt_len < 0x10000) {
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr+2);
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
 		return 0;
 	}
 	if (skb->nh.ipv6h->payload_len) {
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr);
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
 		return 0;
 	}
 
@@ -471,7 +499,11 @@
 		IP6_INC_STATS_BH(Ip6InTruncatedPkts);
 		goto drop;
 	}
-	skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+	if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
+		__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+		if (skb->ip_summed == CHECKSUM_HW)
+			skb->ip_summed = CHECKSUM_NONE;
+	}
 	return 1;
 
 drop:
@@ -485,12 +517,12 @@
 	{-1,			NULL}
 };
 
-u8 * ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr)
+int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff)
 {
 	((struct inet6_skb_parm*)skb->cb)->hop = sizeof(struct ipv6hdr);
-	if (ip6_parse_tlv(tlvprochopopt_lst, skb, nhptr))
-		return nhptr+((nhptr[1]+1)<<3);
-	return NULL;
+	if (ip6_parse_tlv(tlvprochopopt_lst, skb))
+		return sizeof(struct ipv6hdr);
+	return -1;
 }
 
 /*
@@ -684,7 +716,7 @@
  * find out if nexthdr is a well-known extension header or a protocol
  */
 
-static __inline__ int ipv6_ext_hdr(u8 nexthdr)
+int ipv6_ext_hdr(u8 nexthdr)
 {
 	/* 
 	 * find out if nexthdr is an extension header or a protocol
@@ -739,33 +771,36 @@
  * --ANK (980726)
  */
 
-u8 *ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, u8 *nexthdrp, int len)
+int ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, int len)
 {
 	u8 nexthdr = *nexthdrp;
 
 	while (ipv6_ext_hdr(nexthdr)) {
-		int hdrlen; 
+		struct ipv6_opt_hdr hdr;
+		int hdrlen;
 
-		if (len < sizeof(struct ipv6_opt_hdr))
-			return NULL;
+		if (len < (int)sizeof(struct ipv6_opt_hdr))
+			return -1;
 		if (nexthdr == NEXTHDR_NONE)
-			return NULL;
+			return -1;
+		if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
+			BUG();
 		if (nexthdr == NEXTHDR_FRAGMENT) {
-			struct frag_hdr *fhdr = (struct frag_hdr *) hdr;
+			struct frag_hdr *fhdr = (struct frag_hdr *) &hdr;
 			if (ntohs(fhdr->frag_off) & ~0x7)
 				break;
 			hdrlen = 8;
 		} else if (nexthdr == NEXTHDR_AUTH)
-			hdrlen = (hdr->hdrlen+2)<<2; 
+			hdrlen = (hdr.hdrlen+2)<<2; 
 		else
-			hdrlen = ipv6_optlen(hdr); 
+			hdrlen = ipv6_optlen(&hdr); 
 
-		nexthdr = hdr->nexthdr;
-		hdr = (struct ipv6_opt_hdr *) ((u8*)hdr + hdrlen);
+		nexthdr = hdr.nexthdr;
 		len -= hdrlen;
+		start += hdrlen;
 	}
 
 	*nexthdrp = nexthdr;
-	return (u8*)hdr;
+	return start;
 }
 
diff -urN linux-2.4.4-pre2/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- linux-2.4.4-pre2/net/ipv6/icmp.c	Mon Jan  1 11:01:58 2001
+++ linux/net/ipv6/icmp.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: icmp.c,v 1.28 2000/03/25 01:55:20 davem Exp $
+ *	$Id: icmp.c,v 1.31 2001/01/22 02:36:37 davem Exp $
  *
  *	Based on net/ipv4/icmp.c
  *
@@ -23,6 +23,8 @@
  *	Andi Kleen		:	exception handling
  *	Andi Kleen			add rate limits. never reply to a icmp.
  *					add more length checks and other fixes.
+ *	yoshfuji		:	ensure to sent parameter problem for
+ *					fragments.
  */
 
 #define __NO_VERSION__
@@ -66,7 +68,7 @@
 
 struct socket *icmpv6_socket;
 
-int icmpv6_rcv(struct sk_buff *skb, unsigned long len);
+int icmpv6_rcv(struct sk_buff *skb);
 
 static struct inet6_protocol icmpv6_protocol = 
 {
@@ -81,7 +83,8 @@
 
 struct icmpv6_msg {
 	struct icmp6hdr		icmph;
-	__u8 			*data;
+	struct sk_buff		*skb;
+	int			offset;
 	struct in6_addr		*daddr;
 	int			len;
 	__u32			csum;
@@ -136,19 +139,10 @@
 	struct icmp6hdr *icmph;
 	__u32 csum;
 
-	/* 
-	 *	in theory offset must be 0 since we never send more 
-	 *	than IPV6_MIN_MTU bytes on an error or more than the path mtu
-	 *	on an echo reply. (those are the rules on RFC 1883)
-	 *
-	 * 	Luckily, this statement is obsolete after
-	 *	draft-ietf-ipngwg-icmp-v2-00           --ANK (980730)
-	 */
-
 	if (offset) {
-		csum = csum_partial_copy_nocheck((void *) msg->data +
-						 offset - sizeof(struct icmp6hdr), 
-						 buff, len, msg->csum);
+		csum = skb_copy_and_csum_bits(msg->skb, msg->offset +
+					      (offset - sizeof(struct icmp6hdr)),
+					      buff, len, msg->csum);
 		msg->csum = csum;
 		return 0;
 	}
@@ -156,9 +150,9 @@
 	csum = csum_partial_copy_nocheck((void *) &msg->icmph, buff,
 					 sizeof(struct icmp6hdr), msg->csum);
 
-	csum = csum_partial_copy_nocheck((void *) msg->data, 
-					 buff + sizeof(struct icmp6hdr),
-					 len - sizeof(struct icmp6hdr), csum);
+	csum = skb_copy_and_csum_bits(msg->skb, msg->offset,
+				      buff + sizeof(struct icmp6hdr),
+				      len - sizeof(struct icmp6hdr), csum);
 
 	icmph = (struct icmp6hdr *) buff;
 
@@ -171,11 +165,9 @@
 /* 
  * Slightly more convenient version of icmpv6_send.
  */
-void icmpv6_param_prob(struct sk_buff *skb, int code, void *pos)
+void icmpv6_param_prob(struct sk_buff *skb, int code, int pos)
 {
-	int offset = (u8*)pos - (u8*)skb->nh.ipv6h; 
-	
-	icmpv6_send(skb, ICMPV6_PARAMPROB, code, offset, skb->dev);
+	icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos, skb->dev);
 	kfree_skb(skb);
 }
 
@@ -186,29 +178,30 @@
  *	- it was icmp error message.
  *	- it is truncated, so that it is known, that protocol is ICMPV6
  *	  (i.e. in the middle of some exthdr)
- *	- it is not the first fragment. BTW IPv6 specs say nothing about
- *	  this case, but it is clear, that our reply would be useless
- *	  for sender.
  *
  *	--ANK (980726)
  */
 
-static int is_ineligible(struct ipv6hdr *hdr, int len)
+static int is_ineligible(struct sk_buff *skb)
 {
-	u8 *ptr;
-	__u8 nexthdr = hdr->nexthdr;
+	int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data;
+	int len = skb->len - ptr;
+	__u8 nexthdr = skb->nh.ipv6h->nexthdr;
 
-	if (len < (int)sizeof(*hdr))
+	if (len < 0)
 		return 1;
 
-	ptr = ipv6_skip_exthdr((struct ipv6_opt_hdr *)(hdr+1), &nexthdr, len - sizeof(*hdr));
-	if (!ptr)
+	ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, len);
+	if (ptr < 0)
 		return 0;
 	if (nexthdr == IPPROTO_ICMPV6) {
-		struct icmp6hdr *ihdr =	(struct icmp6hdr *)ptr;
-		return (ptr - (u8*)hdr) > len || !(ihdr->icmp6_type & 0x80); 
+		u8 type;
+		if (skb_copy_bits(skb, ptr+offsetof(struct icmp6hdr, icmp6_type),
+				  &type, 1)
+		    || !(type & 0x80))
+			return 1;
 	}
-	return nexthdr == NEXTHDR_FRAGMENT;
+	return 0;
 }
 
 int sysctl_icmpv6_time = 1*HZ; 
@@ -263,9 +256,12 @@
 
 static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
 {
-	u8 *buff = skb->nh.raw;
+	u8 optval;
 
-	return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
+	offset += skb->nh.raw - skb->data;
+	if (skb_copy_bits(skb, offset, &optval, 1))
+		return 1;
+	return (optval&0xC0) == 0x80;
 }
 
 /*
@@ -284,15 +280,8 @@
 	int addr_type = 0;
 	int len;
 
-	/*
-	 *	sanity check pointer in case of parameter problem
-	 */
-
-	if (type == ICMPV6_PARAMPROB && 
-	    (info > (skb->tail - ((unsigned char *) hdr)))) {
-		printk(KERN_DEBUG "icmpv6_send: bug! pointer > skb\n");
+	if ((u8*)hdr < skb->head || (u8*)(hdr+1) > skb->tail)
 		return;
-	}
 
 	/*
 	 *	Make sure we respect the rules 
@@ -300,7 +289,6 @@
 	 *	Rule (e.1) is enforced by not using icmpv6_send
 	 *	in any code that processes icmp errors.
 	 */
-	
 	addr_type = ipv6_addr_type(&hdr->daddr);
 
 	if (ipv6_chk_addr(&hdr->daddr, skb->dev))
@@ -341,9 +329,9 @@
 	/* 
 	 *	Never answer to a ICMP packet.
 	 */
-	if (is_ineligible(hdr, (u8*)skb->tail - (u8*)hdr)) {
+	if (is_ineligible(skb)) {
 		if (net_ratelimit())
-			printk(KERN_DEBUG "icmpv6_send: no reply to icmp error/fragment\n"); 
+			printk(KERN_DEBUG "icmpv6_send: no reply to icmp error\n"); 
 		return;
 	}
 
@@ -371,12 +359,13 @@
 	msg.icmph.icmp6_cksum = 0;
 	msg.icmph.icmp6_pointer = htonl(info);
 
-	msg.data = skb->nh.raw;
+	msg.skb = skb;
+	msg.offset = skb->nh.raw - skb->data;
 	msg.csum = 0;
 	msg.daddr = &hdr->saddr;
 
-	len = min((skb->tail - ((unsigned char *) hdr)) + sizeof(struct icmp6hdr), 
-		  IPV6_MIN_MTU - sizeof(struct ipv6hdr));
+	len = skb->len - msg.offset + sizeof(struct icmp6hdr);
+	len = min(len, IPV6_MIN_MTU - sizeof(struct ipv6hdr));
 
 	if (len < 0) {
 		printk(KERN_DEBUG "icmp: len problem\n");
@@ -397,37 +386,30 @@
 static void icmpv6_echo_reply(struct sk_buff *skb)
 {
 	struct sock *sk = icmpv6_socket->sk;
-	struct ipv6hdr *hdr = skb->nh.ipv6h;
 	struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
 	struct in6_addr *saddr;
 	struct icmpv6_msg msg;
 	struct flowi fl;
-	unsigned char *data;
-	int len;
-
-	data = (char *) (icmph + 1);
 
-	saddr = &hdr->daddr;
+	saddr = &skb->nh.ipv6h->daddr;
 
 	if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST)
 		saddr = NULL;
 
-	len = skb->tail - data;
-	len += sizeof(struct icmp6hdr);
-
 	msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
 	msg.icmph.icmp6_code = 0;
 	msg.icmph.icmp6_cksum = 0;
 	msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
 	msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
 
-	msg.data = data;
+	msg.skb = skb;
+	msg.offset = 0;
 	msg.csum = 0;
-	msg.len = len;
-	msg.daddr = &hdr->saddr;
+	msg.len = skb->len + sizeof(struct icmp6hdr);
+	msg.daddr =  &skb->nh.ipv6h->saddr;
 
 	fl.proto = IPPROTO_ICMPV6;
-	fl.nl_u.ip6_u.daddr = &hdr->saddr;
+	fl.nl_u.ip6_u.daddr = msg.daddr;
 	fl.nl_u.ip6_u.saddr = saddr;
 	fl.oif = skb->dev->ifindex;
 	fl.fl6_flowlabel = 0;
@@ -437,7 +419,7 @@
 	if (icmpv6_xmit_lock_bh())
 		return;
 
-	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
 		       MSG_DONTWAIT);
 	ICMP6_INC_STATS_BH(Icmp6OutEchoReplies);
 	ICMP6_INC_STATS_BH(Icmp6OutMsgs);
@@ -445,29 +427,35 @@
 	icmpv6_xmit_unlock_bh();
 }
 
-static void icmpv6_notify(struct sk_buff *skb,
-			  int type, int code, u32 info, unsigned char *buff, int len)
+static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
 {
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
-	struct ipv6hdr *hdr = (struct ipv6hdr *) buff;
+	struct in6_addr *saddr, *daddr;
 	struct inet6_protocol *ipprot;
 	struct sock *sk;
-	u8 *pb;
+	int inner_offset;
 	int hash;
 	u8 nexthdr;
 
-	nexthdr = hdr->nexthdr;
-
-	len -= sizeof(struct ipv6hdr);
-	if (len < 0)
+	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
 		return;
 
-	/* now skip over extension headers */
-	pb = ipv6_skip_exthdr((struct ipv6_opt_hdr *) (hdr + 1), &nexthdr, len);
-	if (!pb)
+	nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr;
+	if (ipv6_ext_hdr(nexthdr)) {
+		/* now skip over extension headers */
+		inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, skb->len - sizeof(struct ipv6hdr));
+		if (inner_offset<0)
+			return;
+	} else {
+		inner_offset = sizeof(struct ipv6hdr);
+	}
+
+	/* Checkin header including 8 bytes of inner protocol header. */
+	if (!pskb_may_pull(skb, inner_offset+8))
 		return;
 
+	saddr = &skb->nh.ipv6h->saddr;
+	daddr = &skb->nh.ipv6h->daddr;
+
 	/* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
 	   Without this we will not able f.e. to make source routed
 	   pmtu discovery.
@@ -484,13 +472,13 @@
 			continue;
 
 		if (ipprot->err_handler)
-			ipprot->err_handler(skb, hdr, NULL, type, code, pb, info);
+			ipprot->err_handler(skb, NULL, type, code, inner_offset, info);
 	}
 
 	read_lock(&raw_v6_lock);
 	if ((sk = raw_v6_htable[hash]) != NULL) {
 		while((sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
-			rawv6_err(sk, skb, hdr, NULL, type, code, pb, info);
+			rawv6_err(sk, skb, NULL, type, code, inner_offset, info);
 			sk = sk->next;
 		}
 	}
@@ -501,28 +489,31 @@
  *	Handle icmp messages
  */
 
-int icmpv6_rcv(struct sk_buff *skb, unsigned long len)
+int icmpv6_rcv(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	struct in6_addr *saddr, *daddr;
 	struct ipv6hdr *orig_hdr;
-	struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
-	int ulen;
+	struct icmp6hdr *hdr;
 	int type;
 
 	ICMP6_INC_STATS_BH(Icmp6InMsgs);
 
-	if (len < sizeof(struct icmp6hdr))
-		goto discard_it;
+	saddr = &skb->nh.ipv6h->saddr;
+	daddr = &skb->nh.ipv6h->daddr;
 
 	/* Perform checksum. */
-	switch (skb->ip_summed) {	
-	case CHECKSUM_NONE:
-		skb->csum = csum_partial((char *)hdr, len, 0);
-	case CHECKSUM_HW:
-		if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, 
+	if (skb->ip_summed == CHECKSUM_HW) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
 				    skb->csum)) {
+			printk(KERN_DEBUG "ICMPv6 hw checksum failed\n");
+			skb->ip_summed = CHECKSUM_NONE;
+		}
+	}
+	if (skb->ip_summed == CHECKSUM_NONE) {
+		if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
+				    skb_checksum(skb, 0, skb->len, 0))) {
 			printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
 				ntohs(saddr->in6_u.u6_addr16[0]),
 				ntohs(saddr->in6_u.u6_addr16[1]),
@@ -542,14 +533,12 @@
 				ntohs(daddr->in6_u.u6_addr16[7]));
 			goto discard_it;
 		}
-	default:;
-		/* CHECKSUM_UNNECESSARY */
-	};
+	}
 
-	/*
-	 *	length of original packet carried in skb
-	 */
-	ulen = skb->tail - (unsigned char *) (hdr + 1);
+	if (!pskb_pull(skb, sizeof(struct icmp6hdr)))
+		goto discard_it;
+
+	hdr = (struct icmp6hdr *) skb->h.raw;
 
 	type = hdr->icmp6_type;
 
@@ -559,7 +548,6 @@
 		(&icmpv6_statistics[smp_processor_id()*2].Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++;
 
 	switch (type) {
-
 	case ICMPV6_ECHO_REQUEST:
 		icmpv6_echo_reply(skb);
 		break;
@@ -574,10 +562,12 @@
 		   destination cache will allow to solve this problem
 		   --ANK (980726)
 		 */
+		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+			goto discard_it;
+		hdr = (struct icmp6hdr *) skb->data;
 		orig_hdr = (struct ipv6hdr *) (hdr + 1);
-		if (ulen >= sizeof(struct ipv6hdr))
-			rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
-					   ntohl(hdr->icmp6_mtu));
+		rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
+				   ntohl(hdr->icmp6_mtu));
 
 		/*
 		 *	Drop through to notify
@@ -586,8 +576,7 @@
 	case ICMPV6_DEST_UNREACH:
 	case ICMPV6_TIME_EXCEED:
 	case ICMPV6_PARAMPROB:
-		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu,
-			      (char *) (hdr + 1), ulen);
+		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
 		break;
 
 	case NDISC_ROUTER_SOLICITATION:
@@ -595,15 +584,21 @@
 	case NDISC_NEIGHBOUR_SOLICITATION:
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
 	case NDISC_REDIRECT:
-		ndisc_rcv(skb, len);
+		if (skb_is_nonlinear(skb) &&
+		    skb_linearize(skb, GFP_ATOMIC) != 0) {
+			kfree_skb(skb);
+			return 0;
+		}
+
+		ndisc_rcv(skb);
 		break;
 
 	case ICMPV6_MGM_QUERY:
-		igmp6_event_query(skb, hdr, len);
+		igmp6_event_query(skb);
 		break;
 
 	case ICMPV6_MGM_REPORT:
-		igmp6_event_report(skb, hdr, len);
+		igmp6_event_report(skb);
 		break;
 
 	case ICMPV6_MGM_REDUCTION:
@@ -612,7 +607,7 @@
 	default:
 		if (net_ratelimit())
 			printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
-		
+
 		/* informational */
 		if (type & 0x80)
 			break;
@@ -622,8 +617,7 @@
 		 * must pass to upper level 
 		 */
 
-		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu,
-			      (char *) (hdr + 1), ulen);
+		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
 	};
 	kfree_skb(skb);
 	return 0;
diff -urN linux-2.4.4-pre2/net/ipv6/ip6_input.c linux/net/ipv6/ip6_input.c
--- linux-2.4.4-pre2/net/ipv6/ip6_input.c	Mon Dec 11 12:37:04 2000
+++ linux/net/ipv6/ip6_input.c	Mon Dec  3 22:34:51 2001
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Ian P. Morris		<I.P.Morris@soton.ac.uk>
  *
- *	$Id: ip6_input.c,v 1.18 2000/12/08 17:15:54 davem Exp $
+ *	$Id: ip6_input.c,v 1.19 2000/12/13 18:31:50 davem Exp $
  *
  *	Based in linux/net/ipv4/ip_input.c
  *
@@ -44,7 +44,6 @@
 
 static inline int ip6_rcv_finish( struct sk_buff *skb) 
 {
-
 	if (skb->dst == NULL)
 		ip6_route_input(skb);
 
@@ -69,9 +68,15 @@
 	 */
 	((struct inet6_skb_parm *)skb->cb)->iif = dev->ifindex;
 
+	if (skb->len < sizeof(struct ipv6hdr))
+		goto err;
+
+	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+		goto drop;
+
 	hdr = skb->nh.ipv6h;
 
-	if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
+	if (hdr->version != 6)
 		goto err;
 
 	pkt_len = ntohs(hdr->payload_len);
@@ -80,16 +85,24 @@
 	if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
 		if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
 			goto truncated;
-		skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+		if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
+			if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr)))
+				goto drop;
+			hdr = skb->nh.ipv6h;
+			if (skb->ip_summed == CHECKSUM_HW)
+				skb->ip_summed = CHECKSUM_NONE;
+		}
 	}
 
 	if (hdr->nexthdr == NEXTHDR_HOP) {
 		skb->h.raw = (u8*)(hdr+1);
-		if (!ipv6_parse_hopopts(skb, &hdr->nexthdr)) {
+		if (ipv6_parse_hopopts(skb, offsetof(struct ipv6hdr, nexthdr)) < 0) {
 			IP6_INC_STATS_BH(Ip6InHdrErrors);
 			return 0;
 		}
+		hdr = skb->nh.ipv6h;
 	}
+
 	return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish);
 truncated:
 	IP6_INC_STATS_BH(Ip6InTruncatedPkts);
@@ -111,12 +124,11 @@
 	struct ipv6hdr *hdr = skb->nh.ipv6h;
 	struct inet6_protocol *ipprot;
 	struct sock *raw_sk;
-	__u8 *nhptr;
+	int nhoff;
 	int nexthdr;
 	int found = 0;
 	u8 hash;
-	int len;
-	
+
 	skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
 
 	/*
@@ -124,13 +136,13 @@
 	 */
 
 	nexthdr = hdr->nexthdr;
-	nhptr = &hdr->nexthdr;
+	nhoff = offsetof(struct ipv6hdr, nexthdr);
 
 	/* Skip  hop-by-hop options, they are already parsed. */
 	if (nexthdr == NEXTHDR_HOP) {
-		nhptr = (u8*)(hdr+1);
-		nexthdr = *nhptr;
-		skb->h.raw += (nhptr[1]+1)<<3;
+		nhoff = sizeof(struct ipv6hdr);
+		nexthdr = skb->h.raw[0];
+		skb->h.raw += (skb->h.raw[1]+1)<<3;
 	}
 
 	/* This check is sort of optimization.
@@ -138,17 +150,23 @@
 	   which are missing with probability of 200%
 	 */
 	if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP) {
-		nhptr = ipv6_parse_exthdrs(&skb, nhptr);
-		if (nhptr == NULL)
+		nhoff = ipv6_parse_exthdrs(&skb, nhoff);
+		if (nhoff < 0)
 			return 0;
-		nexthdr = *nhptr;
+		nexthdr = skb->nh.raw[nhoff];
 		hdr = skb->nh.ipv6h;
 	}
-	len = skb->tail - skb->h.raw;
+
+	if (!pskb_pull(skb, skb->h.raw - skb->data))
+		goto discard;
+
+	if (skb->ip_summed == CHECKSUM_HW)
+		skb->csum = csum_sub(skb->csum,
+				     csum_partial(skb->nh.raw, skb->h.raw-skb->nh.raw, 0));
 
 	raw_sk = raw_v6_htable[nexthdr&(MAX_INET_PROTOS-1)];
 	if (raw_sk)
-		raw_sk = ipv6_raw_deliver(skb, nexthdr, len);
+		raw_sk = ipv6_raw_deliver(skb, nexthdr);
 
 	hash = nexthdr & (MAX_INET_PROTOS - 1);
 	for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
@@ -163,12 +181,12 @@
 			buff = skb_clone(skb, GFP_ATOMIC);
 
 		if (buff)
-			ipprot->handler(buff, len);
+			ipprot->handler(buff);
 		found = 1;
 	}
 
 	if (raw_sk) {
-		rawv6_rcv(raw_sk, skb, len);
+		rawv6_rcv(raw_sk, skb);
 		sock_put(raw_sk);
 		found = 1;
 	}
@@ -178,9 +196,13 @@
 	 */
 	if (!found) {
 		IP6_INC_STATS_BH(Ip6InUnknownProtos);
-		icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhptr);
+		icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhoff);
 	}
 
+	return 0;
+
+discard:
+	kfree_skb(skb);
 	return 0;
 }
 
diff -urN linux-2.4.4-pre2/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- linux-2.4.4-pre2/net/ipv6/ip6_output.c	Thu Jun 22 07:23:26 2000
+++ linux/net/ipv6/ip6_output.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.27 2000/06/21 17:18:40 davem Exp $
+ *	$Id: ip6_output.c,v 1.30 2001/03/03 01:20:10 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -85,7 +85,7 @@
 static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
 {
 	newskb->mac.raw = newskb->data;
-	skb_pull(newskb, newskb->nh.raw - newskb->data);
+	__skb_pull(newskb, newskb->nh.raw - newskb->data);
 	newskb->pkt_type = PACKET_LOOPBACK;
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
 	BUG_TRAP(newskb->dst);
@@ -113,7 +113,7 @@
 			   is not supported in any case.
 			 */
 			if (newskb)
-				NF_HOOK(PF_INET, NF_IP6_POST_ROUTING, newskb, NULL,
+				NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, newskb, NULL,
 					newskb->dev,
 					ip6_dev_loopback_xmit);
 
@@ -405,7 +405,7 @@
 
 	last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
 				       dst->dev->hard_header_len + 15,
-				       0, flags & MSG_DONTWAIT, &err);
+				       flags & MSG_DONTWAIT, &err);
 
 	if (last_skb == NULL)
 		return err;
@@ -624,7 +624,7 @@
 			goto out;
 
 		skb = sock_alloc_send_skb(sk, pktlength + 15 +
-					  dev->hard_header_len, 0,
+					  dev->hard_header_len,
 					  flags & MSG_DONTWAIT, &err);
 
 		if (skb == NULL) {
@@ -697,14 +697,14 @@
 			if (last) {
 				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 				if (skb2)
-					rawv6_rcv(last, skb2, skb2->len);
+					rawv6_rcv(last, skb2);
 			}
 			last = sk;
 		}
 	}
 
 	if (last) {
-		rawv6_rcv(last, skb, skb->len);
+		rawv6_rcv(last, skb);
 		read_unlock(&ip6_ra_lock);
 		return 1;
 	}
@@ -724,7 +724,9 @@
 	struct inet6_skb_parm *opt =(struct inet6_skb_parm*)skb->cb;
 	
 	if (ipv6_devconf.forwarding == 0 && opt->srcrt == 0)
-		goto drop;
+		goto error;
+
+	skb->ip_summed = CHECKSUM_NONE;
 
 	/*
 	 *	We DO NOT make any processing on
@@ -785,7 +787,7 @@
 	} else if (ipv6_addr_type(&hdr->saddr)&(IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK
 						|IPV6_ADDR_LINKLOCAL)) {
 		/* This check is security critical. */
-		goto drop;
+		goto error;
 	}
 
 	if (skb->len > dst->pmtu) {
@@ -797,8 +799,8 @@
 		return -EMSGSIZE;
 	}
 
-	if ((skb = skb_cow(skb, dst->dev->hard_header_len)) == NULL)
-		return 0;
+	if (skb_cow(skb, dst->dev->hard_header_len))
+		goto drop;
 
 	hdr = skb->nh.ipv6h;
 
@@ -809,8 +811,9 @@
 	IP6_INC_STATS_BH(Ip6OutForwDatagrams);
 	return NF_HOOK(PF_INET6,NF_IP6_FORWARD, skb, skb->dev, dst->dev, ip6_forward_finish);
 
-drop:
+error:
 	IP6_INC_STATS_BH(Ip6InAddrErrors);
+drop:
 	kfree_skb(skb);
 	return -EINVAL;
 }
diff -urN linux-2.4.4-pre2/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- linux-2.4.4-pre2/net/ipv6/mcast.c	Mon Sep 18 15:04:13 2000
+++ linux/net/ipv6/mcast.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.33 2000/09/18 05:59:48 davem Exp $
+ *	$Id: mcast.c,v 1.36 2001/03/03 01:20:10 davem Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -15,6 +15,11 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+/* Changes:
+ *
+ *	yoshfuji	: fix format of router-alert option
+ */
+
 #define __NO_VERSION__
 #include <linux/config.h>
 #include <linux/module.h>
@@ -392,17 +397,19 @@
 	spin_unlock(&ma->mca_lock);
 }
 
-int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+int igmp6_event_query(struct sk_buff *skb)
 {
 	struct ifmcaddr6 *ma;
 	struct in6_addr *addrp;
 	unsigned long resptime;
 	struct inet6_dev *idev;
+	struct icmp6hdr *hdr;
 
-
-	if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr))
+	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
 		return -EINVAL;
 
+	hdr = (struct icmp6hdr*) skb->h.raw;
+
 	/* Drop queries with not link local source */
 	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
 		return -EINVAL;
@@ -437,19 +444,22 @@
 }
 
 
-int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+int igmp6_event_report(struct sk_buff *skb)
 {
 	struct ifmcaddr6 *ma;
 	struct in6_addr *addrp;
 	struct inet6_dev *idev;
+	struct icmp6hdr *hdr;
 
 	/* Our own report looped back. Ignore it. */
 	if (skb->pkt_type == PACKET_LOOPBACK)
 		return 0;
 
-	if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr))
+	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
 		return -EINVAL;
 
+	hdr = (struct icmp6hdr*) skb->h.raw;
+
 	/* Drop reports with not link local source */
 	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
 		return -EINVAL;
@@ -491,7 +501,7 @@
 	struct in6_addr all_routers;
 	int err, len, payload_len, full_len;
 	u8 ra[8] = { IPPROTO_ICMPV6, 0,
-		     IPV6_TLV_ROUTERALERT, 0, 0, 0,
+		     IPV6_TLV_ROUTERALERT, 2, 0, 0,
 		     IPV6_TLV_PADN, 0 };
 
 	snd_addr = addr;
@@ -504,7 +514,7 @@
 	payload_len = len + sizeof(ra);
 	full_len = sizeof(struct ipv6hdr) + payload_len;
 
-	skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, 0, &err);
+	skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, &err);
 
 	if (skb == NULL)
 		return;
diff -urN linux-2.4.4-pre2/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- linux-2.4.4-pre2/net/ipv6/ndisc.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv6/ndisc.c	Mon Dec  3 22:34:51 2001
@@ -338,7 +338,7 @@
 	}
 
 	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				  0, 0, &err);
+				  0, &err);
 
 	if (skb == NULL) {
 		ND_PRINTK1("send_na: alloc skb failed\n");
@@ -397,7 +397,7 @@
 		len += NDISC_OPT_SPACE(dev->addr_len);
 
 	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				  0, 0, &err);
+				  0, &err);
 	if (skb == NULL) {
 		ND_PRINTK1("send_ns: alloc skb failed\n");
 		return;
@@ -458,7 +458,7 @@
 		len += NDISC_OPT_SPACE(dev->addr_len);
 
         skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				  0, 0, &err);
+				  0, &err);
 	if (skb == NULL) {
 		ND_PRINTK1("send_ns: alloc skb failed\n");
 		return;
@@ -869,7 +869,7 @@
  	}
 
 	buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				   0, 0, &err);
+				   0, &err);
 	if (buff == NULL) {
 		ND_PRINTK1("ndisc_send_redirect: alloc_skb failed\n");
 		return;
@@ -957,11 +957,11 @@
 
 static void pndisc_redo(struct sk_buff *skb)
 {
-	ndisc_rcv(skb, skb->len);
+	ndisc_rcv(skb);
 	kfree_skb(skb);
 }
 
-int ndisc_rcv(struct sk_buff *skb, unsigned long len)
+int ndisc_rcv(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
 	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
@@ -969,6 +969,8 @@
 	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
 	struct neighbour *neigh;
 	struct inet6_ifaddr *ifp;
+
+	__skb_push(skb, skb->data-skb->h.raw);
 
 	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
diff -urN linux-2.4.4-pre2/net/ipv6/raw.c linux/net/ipv6/raw.c
--- linux-2.4.4-pre2/net/ipv6/raw.c	Fri Feb  9 11:29:44 2001
+++ linux/net/ipv6/raw.c	Mon Dec  3 22:34:51 2001
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.42 2000/11/28 13:38:38 davem Exp $
+ *	$Id: raw.c,v 1.45 2001/02/18 09:10:42 davem Exp $
  *
  *	Fixes:
  *	Hideaki YOSHIFUJI	:	sin6_scope_id support
@@ -116,8 +116,11 @@
 	struct raw6_opt *opt;
 
 	opt = &sk->tp_pinfo.tp_raw;
-	icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1);
-	return test_bit(icmph->icmp6_type, &opt->filter);
+	if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) {
+		icmph = (struct icmp6hdr *) skb->data;
+		return test_bit(icmph->icmp6_type, &opt->filter);
+	}
+	return 0;
 }
 
 /*
@@ -125,8 +128,7 @@
  *	(should consider queueing the skb in the sock receive_queue
  *	without calling rawv6.c)
  */
-struct sock * ipv6_raw_deliver(struct sk_buff *skb,
-			       int nexthdr, unsigned long len)
+struct sock * ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
 {
 	struct in6_addr *saddr;
 	struct in6_addr *daddr;
@@ -163,7 +165,7 @@
 
 			buff = skb_clone(skb, GFP_ATOMIC);
 			if (buff)
-				rawv6_rcv(sk2, buff, len);
+				rawv6_rcv(sk2, buff);
 		}
 	}
 
@@ -177,8 +179,6 @@
 	return sk;
 }
 
-
-
 /* This cleans up af_inet6 a bit. -DaveM */
 static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
@@ -239,16 +239,13 @@
 	return err;
 }
 
-void rawv6_err(struct sock *sk, struct sk_buff *skb, struct ipv6hdr *hdr,
+void rawv6_err(struct sock *sk, struct sk_buff *skb,
 	       struct inet6_skb_parm *opt,
-	       int type, int code, unsigned char *buff, u32 info)
+	       int type, int code, int offset, u32 info)
 {
 	int err;
 	int harderr;
 
-	if (buff > skb->tail)
-		return;
-
 	/* Report error on raw socket, if:
 	   1. User requested recverr.
 	   2. Socket is connected (otherwise the error indication
@@ -261,8 +258,12 @@
 	if (type == ICMPV6_PKT_TOOBIG)
 		harderr = (sk->net_pinfo.af_inet6.pmtudisc == IPV6_PMTUDISC_DO);
 
-	if (sk->net_pinfo.af_inet6.recverr)
-		ipv6_icmp_error(sk, skb, err, 0, ntohl(info), buff);
+	if (sk->net_pinfo.af_inet6.recverr) {
+		u8 *payload = skb->data;
+		if (!sk->protinfo.af_inet.hdrincl)
+			payload += offset;
+		ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload);
+	}
 
 	if (sk->net_pinfo.af_inet6.recverr || harderr) {
 		sk->err = err;
@@ -290,10 +291,12 @@
  *	maybe we could have the network decide uppon a hint if it 
  *	should call raw_rcv for demultiplexing
  */
-int rawv6_rcv(struct sock *sk, struct sk_buff *skb, unsigned long len)
+int rawv6_rcv(struct sock *sk, struct sk_buff *skb)
 {
-	if (sk->protinfo.af_inet.hdrincl)
+	if (sk->protinfo.af_inet.hdrincl) {
+		__skb_push(skb, skb->nh.raw - skb->data);
 		skb->h.raw = skb->nh.raw;
+	}
 
 	rawv6_rcv_skb(sk, skb);
 	return 0;
@@ -325,7 +328,7 @@
 	if (!skb)
 		goto out;
 
-	copied = skb->tail - skb->h.raw;
+	copied = skb->len;
   	if (copied > len) {
   		copied = len;
   		msg->msg_flags |= MSG_TRUNC;
@@ -594,6 +597,8 @@
 	case ICMPV6_FILTER:
 		if (get_user(len, optlen))
 			return -EFAULT;
+		if (len < 0)
+			return -EINVAL;
 		if (len > sizeof(struct icmp6_filter))
 			len = sizeof(struct icmp6_filter);
 		if (put_user(len, optlen))
diff -urN linux-2.4.4-pre2/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- linux-2.4.4-pre2/net/ipv6/reassembly.c	Mon Dec  3 22:34:42 2001
+++ linux/net/ipv6/reassembly.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: reassembly.c,v 1.22 2000/12/08 17:41:54 davem Exp $
+ *	$Id: reassembly.c,v 1.26 2001/03/07 22:00:57 davem Exp $
  *
  *	Based on: net/ipv4/ip_fragment.c
  *
@@ -79,11 +79,12 @@
 	int			len;
 	int			meat;
 	int			iif;
+	struct timeval		stamp;
+	unsigned int		csum;
 	__u8			last_in;	/* has first/last segment arrived? */
 #define COMPLETE		4
 #define FIRST_IN		2
 #define LAST_IN			1
-	__u8			nexthdr;
 	__u16			nhoffset;
 	struct frag_queue	**pprev;
 };
@@ -349,7 +350,7 @@
 
 
 static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, 
-			   struct frag_hdr *fhdr, u8 *nhptr)
+			   struct frag_hdr *fhdr, int nhoff)
 {
 	struct sk_buff *prev, *next;
 	int offset, end;
@@ -362,10 +363,14 @@
 			((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
 
 	if ((unsigned int)end >= 65536) {
-		icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off); 
-		goto err;
+ 		icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off - skb->nh.raw);
+ 		return;
 	}
 
+ 	if (skb->ip_summed == CHECKSUM_HW)
+ 		skb->csum = csum_sub(skb->csum,
+ 				     csum_partial(skb->nh.raw, (u8*)(fhdr+1)-skb->nh.raw, 0));
+
 	/* Is this the final fragment? */
 	if (!(fhdr->frag_off & __constant_htons(0x0001))) {
 		/* If we already have some bits beyond end
@@ -381,16 +386,12 @@
 		 * Required by the RFC.
 		 */
 		if (end & 0x7) {
-			/*
-			   It is not in specs, but I see no reasons
-			   to send an error in this case. --ANK
+			/* RFC2460 says always send parameter problem in
+			 * this case. -DaveM
 			 */
-			if (offset == 0) {
-				icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 
-						  &skb->nh.ipv6h->payload_len);
-				return;
-			} else
-				goto err;
+			icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 
+					  offsetof(struct ipv6hdr, payload_len));
+			return;
 		}
 		if (end > fq->len) {
 			/* Some bits beyond end -> corruption. */
@@ -404,8 +405,14 @@
 		goto err;
 
 	/* Point into the IP datagram 'data' part. */
-	skb_pull(skb, (u8 *) (fhdr + 1) - skb->data);
-	skb_trim(skb, end - offset);
+	if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data))
+		goto err;
+	if (end-offset < skb->len) {
+		if (pskb_trim(skb, end - offset))
+			goto err;
+		if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+			skb->ip_summed = CHECKSUM_NONE;
+	}
 
 	/* Find out which fragments are in front and at the back of us
 	 * in the chain of fragments so far.  We must know where to put
@@ -429,7 +436,10 @@
 			offset += i;
 			if (end <= offset)
 				goto err;
-			skb_pull(skb, i);
+			if (!pskb_pull(skb, i))
+				goto err;
+			if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+				skb->ip_summed = CHECKSUM_NONE;
 		}
 	}
 
@@ -443,9 +453,12 @@
 			/* Eat head of the next overlapped fragment
 			 * and leave the loop. The next ones cannot overlap.
 			 */
+			if (!pskb_pull(next, i))
+				goto err;
 			FRAG6_CB(next)->offset += i;	/* next fragment */
-			skb_pull(next, i);
 			fq->meat -= i;
+			if (next->ip_summed != CHECKSUM_UNNECESSARY)
+				next->ip_summed = CHECKSUM_NONE;
 			break;
 		} else {
 			struct sk_buff *free_it = next;
@@ -474,20 +487,18 @@
 	else
 		fq->fragments = skb;
 
-	fq->iif = skb->dev->ifindex;
+	if (skb->dev)
+		fq->iif = skb->dev->ifindex;
 	skb->dev = NULL;
+	fq->stamp = skb->stamp;
 	fq->meat += skb->len;
 	atomic_add(skb->truesize, &ip6_frag_mem);
 
-	/* First fragment.
-	   nexthdr and nhptr are get from the first fragment.
-	   Moreover, nexthdr is UNDEFINED for all the fragments but the
-	   first one.
-	   (fixed --ANK (980728))
+	/* The first fragment.
+	 * nhoffset is obtained from the first fragment, of course.
 	 */
 	if (offset == 0) {
-		fq->nexthdr = fhdr->nexthdr;
-		fq->nhoffset = nhptr - skb->nh.raw;
+		fq->nhoffset = nhoff;
 		fq->last_in |= FIRST_IN;
 	}
 	return;
@@ -505,21 +516,13 @@
  *	queue is eligible for reassembly i.e. it is not COMPLETE,
  *	the last and the first frames arrived and all the bits are here.
  */
-static u8 *ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
+static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
 			  struct net_device *dev)
 {
 	struct sk_buff *fp, *head = fq->fragments;
-	struct sk_buff *skb;
+	int    remove_fraghdr = 0;
 	int    payload_len;
-	int    unfrag_len;
-	int    copy;
-	u8     *nhptr;
-
-	/* 
-	 * we know the m_flag arrived and we have a queue,
-	 * starting from 0, without gaps.
-	 * this means we have all fragments.
-	 */
+	int    nhoff;
 
 	fq_kill(fq);
 
@@ -527,40 +530,86 @@
 	BUG_TRAP(FRAG6_CB(head)->offset == 0);
 
 	/* Unfragmented part is taken from the first segment. */
-	unfrag_len = head->h.raw - (u8 *) (head->nh.ipv6h + 1);
-	payload_len = unfrag_len + fq->len;
+	payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len;
+	nhoff = head->h.raw - head->nh.raw;
 
-	if (payload_len > 65535)
-		goto out_oversize;
+	if (payload_len > 65535) {
+		payload_len -= 8;
+		if (payload_len > 65535)
+			goto out_oversize;
+		remove_fraghdr = 1;
+	}
 
-	if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL)
+	/* Head of list must not be cloned. */
+	if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
 		goto out_oom;
 
-	copy = unfrag_len + sizeof(struct ipv6hdr);
-
-	skb->mac.raw = skb->data;
-	skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
-	skb->dev = dev;
-	skb->protocol = __constant_htons(ETH_P_IPV6);
-	skb->pkt_type = head->pkt_type;
-	FRAG6_CB(skb)->h = FRAG6_CB(head)->h;
-	skb->dst = dst_clone(head->dst);
-
-	memcpy(skb_put(skb, copy), head->nh.ipv6h, copy);
-	nhptr = skb->nh.raw + fq->nhoffset;
-	*nhptr = fq->nexthdr;
-
-	skb->h.raw = skb->tail;
-
-	skb->nh.ipv6h->payload_len = ntohs(payload_len);
-
-	*skb_in = skb;
+	/* If the first fragment is fragmented itself, we split
+	 * it to two chunks: the first with data and paged part
+	 * and the second, holding only fragments. */
+	if (skb_shinfo(head)->frag_list) {
+		struct sk_buff *clone;
+		int i, plen = 0;
+
+		if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
+			goto out_oom;
+		clone->next = head->next;
+		head->next = clone;
+		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
+		skb_shinfo(head)->frag_list = NULL;
+		for (i=0; i<skb_shinfo(head)->nr_frags; i++)
+			plen += skb_shinfo(head)->frags[i].size;
+		clone->len = clone->data_len = head->data_len - plen;
+		head->data_len -= clone->len;
+		head->len -= clone->len;
+		clone->csum = 0;
+		clone->ip_summed = head->ip_summed;
+		atomic_add(clone->truesize, &ip6_frag_mem);
+	}
+
+	/* Normally we do not remove frag header from datagram, but
+	 * we have to do this and to relocate header, when payload
+	 * is > 65535-8. */
+	if (remove_fraghdr) {
+		nhoff = fq->nhoffset;
+		head->nh.raw[nhoff] = head->h.raw[0];
+		memmove(head->head+8, head->head, (head->data-head->head)-8);
+		head->mac.raw += 8;
+		head->nh.raw += 8;
+	} else {
+		((struct frag_hdr*)head->h.raw)->frag_off = 0;
+	}
 
-	for (fp = fq->fragments; fp; fp=fp->next)
-		memcpy(skb_put(skb, fp->len), fp->data, fp->len);
+	skb_shinfo(head)->frag_list = head->next;
+	head->h.raw = head->data;
+	skb_push(head, head->data - head->nh.raw);
+	atomic_sub(head->truesize, &ip6_frag_mem);
+
+	for (fp=head->next; fp; fp = fp->next) {
+		head->data_len += fp->len;
+		head->len += fp->len;
+		if (head->ip_summed != fp->ip_summed)
+			head->ip_summed = CHECKSUM_NONE;
+		else if (head->ip_summed == CHECKSUM_HW)
+			head->csum = csum_add(head->csum, fp->csum);
+		head->truesize += fp->truesize;
+		atomic_sub(fp->truesize, &ip6_frag_mem);
+	}
+
+	head->next = NULL;
+	head->dev = dev;
+	head->stamp = fq->stamp;
+	head->nh.ipv6h->payload_len = ntohs(payload_len);
+
+	*skb_in = head;
+
+	/* Yes, and fold redundant checksum back. 8) */
+	if (head->ip_summed == CHECKSUM_HW)
+		head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
 
 	IP6_INC_STATS_BH(Ip6ReasmOKs);
-	return nhptr;
+	fq->fragments = NULL;
+	return nhoff;
 
 out_oversize:
 	if (net_ratelimit())
@@ -571,14 +620,14 @@
 		printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n");
 out_fail:
 	IP6_INC_STATS_BH(Ip6ReasmFails);
-	return NULL;
+	return -1;
 }
 
-u8* ipv6_reassembly(struct sk_buff **skbp, __u8 *nhptr)
+int ipv6_reassembly(struct sk_buff **skbp, int nhoff)
 {
 	struct sk_buff *skb = *skbp; 
-	struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw);
 	struct net_device *dev = skb->dev;
+	struct frag_hdr *fhdr;
 	struct frag_queue *fq;
 	struct ipv6hdr *hdr;
 
@@ -588,31 +637,34 @@
 
 	/* Jumbo payload inhibits frag. header */
 	if (hdr->payload_len==0) {
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
-		return NULL;
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
+		return -1;
 	}
-	if ((u8 *)(fhdr+1) > skb->tail) {
-		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
-		return NULL;
+	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) {
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
+		return -1;
 	}
 
+	hdr = skb->nh.ipv6h;
+	fhdr = (struct frag_hdr *)skb->h.raw;
+
 	if (!(fhdr->frag_off & __constant_htons(0xFFF9))) {
 		/* It is not a fragmented frame */
 		skb->h.raw += sizeof(struct frag_hdr);
 		IP6_INC_STATS_BH(Ip6ReasmOKs);
 
-		return &fhdr->nexthdr;
+		return (u8*)fhdr - skb->nh.raw;
 	}
 
 	if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh)
 		ip6_evictor();
 
 	if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr)) != NULL) {
-		u8 *ret = NULL;
+		int ret = -1;
 
 		spin_lock(&fq->lock);
 
-		ip6_frag_queue(fq, skb, fhdr, nhptr);
+		ip6_frag_queue(fq, skb, fhdr, nhoff);
 
 		if (fq->last_in == (FIRST_IN|LAST_IN) &&
 		    fq->meat == fq->len)
@@ -625,5 +677,5 @@
 
 	IP6_INC_STATS_BH(Ip6ReasmFails);
 	kfree_skb(skb);
-	return NULL;
+	return -1;
 }
diff -urN linux-2.4.4-pre2/net/ipv6/sit.c linux/net/ipv6/sit.c
--- linux-2.4.4-pre2/net/ipv6/sit.c	Tue Nov 28 21:53:45 2000
+++ linux/net/ipv6/sit.c	Mon Dec  3 22:34:51 2001
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: sit.c,v 1.47 2000/11/28 13:49:22 davem Exp $
+ *	$Id: sit.c,v 1.49 2001/03/19 20:31:17 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -229,7 +229,7 @@
 }
 
 
-void ipip6_err(struct sk_buff *skb, unsigned char *dp, int len)
+void ipip6_err(struct sk_buff *skb, u32 info)
 {
 #ifndef I_WISH_WORLD_WERE_PERFECT
 
@@ -237,14 +237,11 @@
    8 bytes of packet payload. It means, that precise relaying of
    ICMP in the real Internet is absolutely infeasible.
  */
-	struct iphdr *iph = (struct iphdr*)dp;
+	struct iphdr *iph = (struct iphdr*)skb->data;
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
 	struct ip_tunnel *t;
 
-	if (len < sizeof(struct iphdr))
-		return;
-
 	switch (type) {
 	default:
 	case ICMP_PARAMETERPROB:
@@ -382,20 +379,22 @@
 		IP6_ECN_set_ce(skb->nh.ipv6h);
 }
 
-int ipip6_rcv(struct sk_buff *skb, unsigned short len)
+int ipip6_rcv(struct sk_buff *skb)
 {
 	struct iphdr *iph;
 	struct ip_tunnel *tunnel;
 
+	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+		goto out;
+
 	iph = skb->nh.iph;
 
 	read_lock(&ipip6_lock);
 	if ((tunnel = ipip6_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) {
 		skb->mac.raw = skb->nh.raw;
-		skb->nh.raw = skb_pull(skb, skb->h.raw - skb->data);
+		skb->nh.raw = skb->data;
 		memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
 		skb->protocol = __constant_htons(ETH_P_IPV6);
-		skb->ip_summed = 0;
 		skb->pkt_type = PACKET_HOST;
 		tunnel->stat.rx_packets++;
 		tunnel->stat.rx_bytes += skb->len;
@@ -418,6 +417,7 @@
 	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
 	kfree_skb(skb);
 	read_unlock(&ipip6_lock);
+out:
 	return 0;
 }
 
@@ -842,7 +842,7 @@
 void sit_cleanup(void)
 {
 	inet_del_protocol(&sit_protocol);
-	unregister_netdevice(&ipip6_fb_tunnel_dev);
+	unregister_netdev(&ipip6_fb_tunnel_dev);
 }
 #endif
 
@@ -852,13 +852,7 @@
 
 	ipip6_fb_tunnel_dev.priv = (void*)&ipip6_fb_tunnel;
 	strcpy(ipip6_fb_tunnel_dev.name, ipip6_fb_tunnel.parms.name);
-#ifdef MODULE
 	register_netdev(&ipip6_fb_tunnel_dev);
-#else
-	rtnl_lock();
-	register_netdevice(&ipip6_fb_tunnel_dev);
-	rtnl_unlock();
-#endif
 	inet_add_protocol(&sit_protocol);
 	return 0;
 }
diff -urN linux-2.4.4-pre2/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- linux-2.4.4-pre2/net/ipv6/tcp_ipv6.c	Sun Mar 25 18:14:25 2001
+++ linux/net/ipv6/tcp_ipv6.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.128 2000/12/08 17:15:54 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.135 2001/04/06 18:41:36 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -635,6 +635,7 @@
 	}
 
 	ip6_dst_store(sk, dst, NULL);
+	sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
 
 	if (saddr == NULL) {
 		err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf);
@@ -678,25 +679,23 @@
 failure:
 	__sk_dst_reset(sk);
 	sk->dport = 0;
+	sk->route_caps = 0;
 	return err;
 }
 
-void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
-		struct inet6_skb_parm *opt,
-		int type, int code, unsigned char *header, __u32 info)
+void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+		int type, int code, int offset, __u32 info)
 {
+	struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
 	struct in6_addr *saddr = &hdr->saddr;
 	struct in6_addr *daddr = &hdr->daddr;
-	struct tcphdr *th = (struct tcphdr *)header;
+	struct tcphdr *th = (struct tcphdr *)(skb->data+offset);
 	struct ipv6_pinfo *np;
 	struct sock *sk;
 	int err;
 	struct tcp_opt *tp; 
 	__u32 seq;
 
-	if (header + 8 > skb->tail)
-		return;
-
 	sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source, skb->dev->ifindex);
 
 	if (sk == NULL) {
@@ -914,10 +913,15 @@
 			      struct sk_buff *skb)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	
-	th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 
-				    csum_partial((char *)th, th->doff<<2, 
-						 skb->csum));
+
+	if (skb->ip_summed == CHECKSUM_HW) {
+		th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,  0);
+		skb->csum = offsetof(struct tcphdr, check);
+	} else {
+		th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 
+					    csum_partial((char *)th, th->doff<<2, 
+							 skb->csum));
+	}
 }
 
 
@@ -1298,6 +1302,7 @@
 	MOD_INC_USE_COUNT;
 
 	ip6_dst_store(newsk, dst, NULL);
+	sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 
@@ -1371,22 +1376,20 @@
 static int tcp_v6_checksum_init(struct sk_buff *skb)
 {
 	if (skb->ip_summed == CHECKSUM_HW) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
+				  &skb->nh.ipv6h->daddr,skb->csum))
+			return 0;
+		NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n"));
+	}
+	if (skb->len <= 76) {
 		if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-				 &skb->nh.ipv6h->daddr,skb->csum)) {
-			NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n"));
+				 &skb->nh.ipv6h->daddr,skb_checksum(skb, 0, skb->len, 0)))
 			return -1;
-		}
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	} else {
-		if (skb->len <= 76) {
-			if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-					 &skb->nh.ipv6h->daddr,csum_partial((char *)skb->h.th, skb->len, 0)))
-				return -1;
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-		} else {
-			skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-						  &skb->nh.ipv6h->daddr,0);
-		}
+		skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
+					  &skb->nh.ipv6h->daddr,0);
 	}
 	return 0;
 }
@@ -1526,46 +1529,45 @@
 	return 0;
 }
 
-int tcp_v6_rcv(struct sk_buff *skb, unsigned long len)
+int tcp_v6_rcv(struct sk_buff *skb)
 {
 	struct tcphdr *th;	
 	struct sock *sk;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 	int ret;
 
-	th = skb->h.th;
-
 	if (skb->pkt_type != PACKET_HOST)
 		goto discard_it;
 
 	/*
-	 *	Pull up the IP header.
+	 *	Count it even if it's bad.
 	 */
+	TCP_INC_STATS_BH(TcpInSegs);
 
-	__skb_pull(skb, skb->h.raw - skb->data);
+	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+		goto discard_it;
 
-	/*
-	 *	Count it even if it's bad.
-	 */
+	th = skb->h.th;
 
-	TCP_INC_STATS_BH(TcpInSegs);
+	if (th->doff < sizeof(struct tcphdr)/4)
+		goto bad_packet;
+	if (!pskb_may_pull(skb, th->doff*4))
+		goto discard_it;
 
-	if (th->doff < sizeof(struct tcphdr)/4 ||
-	    (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+	if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
 	     tcp_v6_checksum_init(skb) < 0))
 		goto bad_packet;
 
+	th = skb->h.th;
 	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
 	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
-				    len - th->doff*4);
+				    skb->len - th->doff*4);
 	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 	TCP_SKB_CB(skb)->when = 0;
 	TCP_SKB_CB(skb)->flags = ip6_get_dsfield(skb->nh.ipv6h);
 	TCP_SKB_CB(skb)->sacked = 0;
-	skb->used = 0;
 
-	sk = __tcp_v6_lookup(saddr, th->source, daddr, ntohs(th->dest), tcp_v6_iif(skb));
+	sk = __tcp_v6_lookup(&skb->nh.ipv6h->saddr, th->source,
+			     &skb->nh.ipv6h->daddr, ntohs(th->dest), tcp_v6_iif(skb));
 
 	if (!sk)
 		goto no_tcp_socket;
@@ -1591,7 +1593,7 @@
 	return ret;
 
 no_tcp_socket:
-	if (len < (th->doff<<2) || tcp_checksum_complete(skb)) {
+	if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 bad_packet:
 		TCP_INC_STATS_BH(TcpInErrs);
 	} else {
@@ -1612,7 +1614,7 @@
 	goto discard_it;
 
 do_time_wait:
-	if (len < (th->doff<<2) || tcp_checksum_complete(skb)) {
+	if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 		TCP_INC_STATS_BH(TcpInErrs);
 		sock_put(sk);
 		goto discard_it;
@@ -1673,10 +1675,12 @@
 		if (dst->error) {
 			err = dst->error;
 			dst_release(dst);
+			sk->route_caps = 0;
 			return err;
 		}
 
 		ip6_dst_store(sk, dst, NULL);
+		sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
 	}
 
 	return 0;
@@ -1837,7 +1841,7 @@
 	/* Cleanup up the write buffer. */
   	tcp_writequeue_purge(sk);
 
-	/* Cleans up our, hopefuly empty, out_of_order_queue. */
+	/* Cleans up our, hopefully empty, out_of_order_queue. */
   	__skb_queue_purge(&tp->out_of_order_queue);
 
 	/* Clean prequeue, it must be empty really */
@@ -1847,6 +1851,10 @@
 	if(sk->prev != NULL)
 		tcp_put_port(sk);
 
+	/* If sendmsg cached page exists, toss it. */
+	if (tp->sndmsg_page != NULL)
+		__free_page(tp->sndmsg_page);
+
 	atomic_dec(&tcp_sockets_allocated);
 
 	return inet6_destroy_sock(sk);
@@ -1967,7 +1975,7 @@
 	off_t begin, pos = 0;
 	char tmpbuf[LINE_LEN+2];
 
-	if(offset < LINE_LEN+1)
+	if (offset < LINE_LEN+1)
 		len += sprintf(buffer, LINE_FMT,
 			       "  sl  "						/* 6 */
 			       "local_address                         "		/* 38 */
@@ -1997,7 +2005,7 @@
 			if (pos >= offset) {
 				get_tcp6_sock(sk, tmpbuf, num);
 				len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-				if (len >= length) {
+				if (pos >= offset + length) {
 					tcp_listen_unlock();
 					goto out_no_bh;
 				}
@@ -2016,7 +2024,7 @@
 							continue;
 						get_openreq6(sk, req, tmpbuf, num, uid);
 						len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-						if(len >= length) { 
+						if (pos >= offset + length) { 
 							read_unlock_bh(&tp->syn_wait_lock);
 							tcp_listen_unlock();
 							goto out_no_bh;
@@ -2048,7 +2056,7 @@
 				continue;
 			get_tcp6_sock(sk, tmpbuf, num);
 			len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2063,7 +2071,7 @@
 				continue;
 			get_timewait6_sock(tw, tmpbuf, num);
 			len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2078,7 +2086,7 @@
 	begin = len - (pos - offset);
 	*start = buffer + begin;
 	len -= begin;
-	if(len > length)
+	if (len > length)
 		len = length;
 	if (len < 0)
 		len = 0; 
diff -urN linux-2.4.4-pre2/net/ipv6/udp.c linux/net/ipv6/udp.c
--- linux-2.4.4-pre2/net/ipv6/udp.c	Mon Jan  1 11:01:58 2001
+++ linux/net/ipv6/udp.c	Mon Dec  3 22:34:51 2001
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.59 2000/11/28 13:38:38 davem Exp $
+ *	$Id: udp.c,v 1.62 2001/03/06 21:15:11 davem Exp $
  *
  *	Fixes:
  *	Hideaki YOSHIFUJI	:	sin6_scope_id support
@@ -391,13 +391,13 @@
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					      copied);
 	} else if (msg->msg_flags&MSG_TRUNC) {
-		if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum)))
+		if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
 			goto csum_copy_err;
 		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					      copied);
 	} else {
-		err = copy_and_csum_toiovec(msg->msg_iov, skb, sizeof(struct udphdr));
-		if (err)
+		err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);
+		if (err == -EINVAL)
 			goto csum_copy_err;
 	}
 	if (err)
@@ -461,22 +461,17 @@
 	goto out_free;
 }
 
-void udpv6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
-	       struct inet6_skb_parm *opt,
-	       int type, int code, unsigned char *buff, __u32 info)
+void udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+	       int type, int code, int offset, __u32 info)
 {
+	struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
 	struct net_device *dev = skb->dev;
 	struct in6_addr *saddr = &hdr->saddr;
 	struct in6_addr *daddr = &hdr->daddr;
+	struct udphdr *uh = (struct udphdr*)(skb->data+offset);
 	struct sock *sk;
-	struct udphdr *uh;
 	int err;
 
-	if (buff + sizeof(struct udphdr) > skb->tail)
-		return;
-
-	uh = (struct udphdr *) buff;
-
 	sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source, dev->ifindex);
    
 	if (sk == NULL)
@@ -577,8 +572,8 @@
 
 	buff = NULL;
 	sk2 = sk;
-	while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
-						  uh->source, daddr, dif))) {
+	while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, daddr,
+						  uh->source, saddr, dif))) {
 		if (!buff) {
 			buff = skb_clone(skb, GFP_ATOMIC);
 			if (!buff)
@@ -596,31 +591,29 @@
 	read_unlock(&udp_hash_lock);
 }
 
-int udpv6_rcv(struct sk_buff *skb, unsigned long len)
+int udpv6_rcv(struct sk_buff *skb)
 {
 	struct sock *sk;
   	struct udphdr *uh;
 	struct net_device *dev = skb->dev;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
-	u32 ulen;
+	struct in6_addr *saddr, *daddr;
+	u32 ulen = 0;
 
+	if (!pskb_may_pull(skb, sizeof(struct udphdr)))
+		goto short_packet;
+
+	saddr = &skb->nh.ipv6h->saddr;
+	daddr = &skb->nh.ipv6h->daddr;
 	uh = skb->h.uh;
-	__skb_pull(skb, skb->h.raw - skb->data);
 
 	ulen = ntohs(uh->len);
 
 	/* Check for jumbo payload */
-	if (ulen == 0 && skb->nh.ipv6h->payload_len == 0)
-		ulen = len;
+	if (ulen == 0)
+		ulen = skb->len;
 
-	if (ulen > len || len < sizeof(*uh)) {
-		if (net_ratelimit())
-			printk(KERN_DEBUG "UDP: short packet: %d/%ld\n", ulen, len);
-		UDP6_INC_STATS_BH(UdpInErrors);
-		kfree_skb(skb);
-		return(0);
-	}
+	if (ulen > skb->len || ulen < sizeof(*uh))
+		goto short_packet;
 
 	if (uh->check == 0) {
 		/* IPv6 draft-v2 section 8.1 says that we SHOULD log
@@ -631,17 +624,24 @@
 		goto discard;
 	}
 
-	skb_trim(skb, ulen);
+	if (ulen < skb->len) {
+		if (__pskb_trim(skb, ulen))
+			goto discard;
+		saddr = &skb->nh.ipv6h->saddr;
+		daddr = &skb->nh.ipv6h->daddr;
+		uh = skb->h.uh;
+	}
 
 	if (skb->ip_summed==CHECKSUM_HW) {
-		if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum))
-			goto discard;
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
-	} else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+		if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) {
+			NETDEBUG(printk(KERN_DEBUG "udp v6 hw csum failure.\n"));
+			skb->ip_summed = CHECKSUM_NONE;
+		}
+	}
+	if (skb->ip_summed != CHECKSUM_UNNECESSARY)
 		skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0);
 
-	len = ulen;
-
 	/* 
 	 *	Multicast receive code 
 	 */
@@ -656,12 +656,11 @@
 	 * check socket cache ... must talk to Alan about his plans
 	 * for sock caches... i'll skip this for now.
 	 */
-	
 	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest, dev->ifindex);
-	
+
 	if (sk == NULL) {
 		if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
-		    (unsigned short)csum_fold(csum_partial((char*)uh, len, skb->csum)))
+		    (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
 			goto discard;
 		UDP6_INC_STATS_BH(UdpNoPorts);
 
@@ -676,7 +675,11 @@
 	udpv6_queue_rcv_skb(sk, skb);
 	sock_put(sk);
 	return(0);
-	
+
+short_packet:	
+	if (net_ratelimit())
+		printk(KERN_DEBUG "UDP: short packet: %d/%u\n", ulen, skb->len);
+
 discard:
 	UDP6_INC_STATS_BH(UdpInErrors);
 	kfree_skb(skb);
diff -urN linux-2.4.4-pre2/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- linux-2.4.4-pre2/net/ipx/af_ipx.c	Mon Mar 26 15:43:01 2001
+++ linux/net/ipx/af_ipx.c	Mon Dec  3 22:34:51 2001
@@ -123,7 +123,7 @@
 static unsigned char ipxcfg_max_hops = 16;
 static char ipxcfg_auto_select_primary;
 static char ipxcfg_auto_create_interfaces;
-static int sysctl_ipx_pprop_broadcasting = 1;
+int sysctl_ipx_pprop_broadcasting = 1;
 
 /* Global Variables */
 static struct datalink_proto *p8022_datalink;
@@ -1542,7 +1542,7 @@
 	ipx_offset = intrfc->if_ipx_offset;
 	size = sizeof(struct ipxhdr) + len + ipx_offset;
 
-	skb = sock_alloc_send_skb(sk, size, 0, noblock, &err);
+	skb = sock_alloc_send_skb(sk, size, noblock, &err);
 	if (!skb)
 		goto out_put;
 
@@ -2531,6 +2531,7 @@
 	sendmsg:	ipx_sendmsg,
 	recvmsg:	ipx_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
diff -urN linux-2.4.4-pre2/net/ipx/af_spx.c linux/net/ipx/af_spx.c
--- linux-2.4.4-pre2/net/ipx/af_spx.c	Sun Aug  6 22:20:09 2000
+++ linux/net/ipx/af_spx.c	Mon Dec  3 22:34:51 2001
@@ -440,7 +440,7 @@
 
 		save_flags(flags);
 		cli();
-        	skb = sock_alloc_send_skb(sk, size, 1, 0, &err);
+        	skb = sock_alloc_send_skb(sk, size, 0, &err);
         	if(skb == NULL) {
 			restore_flags(flags);
                 	return (-ENOMEM);
@@ -742,7 +742,7 @@
         size 	= offset + sizeof(struct ipxspxhdr) + len;
 
 	cli();
-        skb  	= sock_alloc_send_skb(sk, size, 0, flags&MSG_DONTWAIT, &err);
+        skb  	= sock_alloc_send_skb(sk, size, flags&MSG_DONTWAIT, &err);
 	sti();
         if(skb == NULL)
                 return (err);
@@ -901,6 +901,7 @@
 	sendmsg:	spx_sendmsg,
 	recvmsg:	spx_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
diff -urN linux-2.4.4-pre2/net/ipx/sysctl_net_ipx.c linux/net/ipx/sysctl_net_ipx.c
--- linux-2.4.4-pre2/net/ipx/sysctl_net_ipx.c	Sun Mar 25 18:14:25 2001
+++ linux/net/ipx/sysctl_net_ipx.c	Mon Dec  3 22:34:51 2001
@@ -6,6 +6,7 @@
  * Added /proc/sys/net/ipx/ipx_pprop_broadcasting - acme March 4, 2001
  */
 
+#include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/sysctl.h>
 
diff -urN linux-2.4.4-pre2/net/irda/af_irda.c linux/net/irda/af_irda.c
--- linux-2.4.4-pre2/net/irda/af_irda.c	Fri Mar  2 11:12:12 2001
+++ linux/net/irda/af_irda.c	Mon Dec  3 22:34:51 2001
@@ -1257,7 +1257,7 @@
 		len = self->max_data_size;
 	}
 
-	skb = sock_alloc_send_skb(sk, len + self->max_header_size, 0, 
+	skb = sock_alloc_send_skb(sk, len + self->max_header_size, 
 				  msg->msg_flags & MSG_DONTWAIT, &err);
 	if (!skb)
 		return -ENOBUFS;
@@ -1500,7 +1500,7 @@
 		len = self->max_data_size;
 	}
 
-	skb = sock_alloc_send_skb(sk, len + self->max_header_size, 0, 
+	skb = sock_alloc_send_skb(sk, len + self->max_header_size, 
 				  msg->msg_flags & MSG_DONTWAIT, &err);
 	if (!skb)
 		return -ENOBUFS;
@@ -1563,7 +1563,7 @@
 		len = self->max_data_size;
 	}
 
-	skb = sock_alloc_send_skb(sk, len + self->max_header_size, 0, 
+	skb = sock_alloc_send_skb(sk, len + self->max_header_size, 
 				  msg->msg_flags & MSG_DONTWAIT, &err);
 	if (!skb)
 		return -ENOBUFS;
@@ -2256,6 +2256,7 @@
 	sendmsg:	irda_sendmsg,
 	recvmsg:	irda_recvmsg_stream,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 static struct proto_ops SOCKOPS_WRAPPED(irda_seqpacket_ops) = {
@@ -2276,6 +2277,7 @@
 	sendmsg:	irda_sendmsg,
 	recvmsg:	irda_recvmsg_dgram,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 static struct proto_ops SOCKOPS_WRAPPED(irda_dgram_ops) = {
@@ -2296,6 +2298,7 @@
 	sendmsg:	irda_sendmsg_dgram,
 	recvmsg:	irda_recvmsg_dgram,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #ifdef CONFIG_IRDA_ULTRA
@@ -2317,6 +2320,7 @@
 	sendmsg:	irda_sendmsg_ultra,
 	recvmsg:	irda_recvmsg_dgram,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 #endif /* CONFIG_IRDA_ULTRA */
 
diff -urN linux-2.4.4-pre2/net/lapb/lapb_iface.c linux/net/lapb/lapb_iface.c
--- linux-2.4.4-pre2/net/lapb/lapb_iface.c	Fri Feb 16 16:06:17 2001
+++ linux/net/lapb/lapb_iface.c	Mon Dec  3 22:34:51 2001
@@ -15,7 +15,6 @@
  *	2000-10-29	Henner Eisen	lapb_data_indication() return status.
  */
  
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
diff -urN linux-2.4.4-pre2/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- linux-2.4.4-pre2/net/netlink/af_netlink.c	Fri Feb  9 11:29:44 2001
+++ linux/net/netlink/af_netlink.c	Mon Dec  3 22:34:51 2001
@@ -962,6 +962,7 @@
 	sendmsg:	netlink_sendmsg,
 	recvmsg:	netlink_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 struct net_proto_family netlink_family_ops = {
diff -urN linux-2.4.4-pre2/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- linux-2.4.4-pre2/net/netrom/af_netrom.c	Fri Dec 29 14:35:47 2000
+++ linux/net/netrom/af_netrom.c	Mon Dec  3 22:34:51 2001
@@ -407,6 +407,9 @@
 	if (get_user(len, optlen))
 		return -EFAULT;
 
+	if (len < 0)
+		return -EINVAL;
+		
 	switch (optname) {
 		case NETROM_T1:
 			val = sk->protinfo.nr->t1 / HZ;
@@ -1010,7 +1013,7 @@
 	SOCK_DEBUG(sk, "NET/ROM: sendto: building packet.\n");
 	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
 
-	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
+	if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
 	skb_reserve(skb, size - len);
@@ -1224,10 +1227,9 @@
 	return(len);
 } 
 
-static struct net_proto_family nr_family_ops = 
-{
-	PF_NETROM,
-	nr_create
+static struct net_proto_family nr_family_ops = {
+	family:		PF_NETROM,
+	create:		nr_create,
 };
 
 static struct proto_ops SOCKOPS_WRAPPED(nr_proto_ops) = {
@@ -1248,22 +1250,29 @@
 	sendmsg:	nr_sendmsg,
 	recvmsg:	nr_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
 SOCKOPS_WRAP(nr_proto, PF_NETROM);
 
 static struct notifier_block nr_dev_notifier = {
-	nr_device_event,
-	0
+	notifier_call:	nr_device_event,
 };
 
 static struct net_device *dev_nr;
 
+static const char banner[] __initdata = KERN_INFO "G4KLX NET/ROM for Linux. Version 0.7 for AX25.037 Linux 2.4\n";
+
 static int __init nr_proto_init(void)
 {
 	int i;
 
+	if (nr_ndevs * sizeof(struct net_device) >= KMALLOC_MAXSIZE) {
+		printk(KERN_ERR "NET/ROM: nr_proto_init - nr_ndevs parameter to large\n");
+		return -1;
+	}
+
 	if ((dev_nr = kmalloc(nr_ndevs * sizeof(struct net_device), GFP_KERNEL)) == NULL) {
 		printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device structure\n");
 		return -1;
@@ -1279,7 +1288,7 @@
 
 	sock_register(&nr_family_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
-	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.7 for AX25.037 Linux 2.4\n");
+	printk(banner);
 
 	ax25_protocol_register(AX25_P_NETROM, nr_route_frame);
 	ax25_linkfail_register(nr_link_failed);
diff -urN linux-2.4.4-pre2/net/netrom/nr_out.c linux/net/netrom/nr_out.c
--- linux-2.4.4-pre2/net/netrom/nr_out.c	Fri Dec 29 14:44:46 2000
+++ linux/net/netrom/nr_out.c	Mon Dec  3 22:34:51 2001
@@ -56,7 +56,7 @@
 		frontlen = skb_headroom(skb);
 
 		while (skb->len > 0) {
-			if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, 0, &err)) == NULL)
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL)
 				return;
 
 			skb_reserve(skbn, frontlen);
diff -urN linux-2.4.4-pre2/net/netsyms.c linux/net/netsyms.c
--- linux-2.4.4-pre2/net/netsyms.c	Sun Mar 25 18:24:31 2001
+++ linux/net/netsyms.c	Mon Dec  3 22:34:51 2001
@@ -130,15 +130,27 @@
 EXPORT_SYMBOL(sock_no_sendmsg);
 EXPORT_SYMBOL(sock_no_recvmsg);
 EXPORT_SYMBOL(sock_no_mmap);
+EXPORT_SYMBOL(sock_no_sendpage);
 EXPORT_SYMBOL(sock_rfree);
 EXPORT_SYMBOL(sock_wfree);
 EXPORT_SYMBOL(sock_wmalloc);
 EXPORT_SYMBOL(sock_rmalloc);
+EXPORT_SYMBOL(skb_linearize);
+EXPORT_SYMBOL(skb_checksum);
+EXPORT_SYMBOL(skb_checksum_help);
 EXPORT_SYMBOL(skb_recv_datagram);
 EXPORT_SYMBOL(skb_free_datagram);
 EXPORT_SYMBOL(skb_copy_datagram);
 EXPORT_SYMBOL(skb_copy_datagram_iovec);
+EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec);
+EXPORT_SYMBOL(skb_copy_bits);
+EXPORT_SYMBOL(skb_copy_and_csum_bits);
 EXPORT_SYMBOL(skb_copy_expand);
+EXPORT_SYMBOL(___pskb_trim);
+EXPORT_SYMBOL(__pskb_pull_tail);
+EXPORT_SYMBOL(pskb_expand_head);
+EXPORT_SYMBOL(pskb_copy);
+EXPORT_SYMBOL(skb_realloc_headroom);
 EXPORT_SYMBOL(datagram_poll);
 EXPORT_SYMBOL(put_cmsg);
 EXPORT_SYMBOL(sock_kmalloc);
@@ -225,7 +237,6 @@
 EXPORT_SYMBOL(ip_route_output_key);
 EXPORT_SYMBOL(ip_route_input);
 EXPORT_SYMBOL(icmp_send);
-EXPORT_SYMBOL(icmp_reply);
 EXPORT_SYMBOL(ip_options_compile);
 EXPORT_SYMBOL(ip_options_undo);
 EXPORT_SYMBOL(arp_send);
@@ -295,7 +306,6 @@
 EXPORT_SYMBOL(ip_queue_xmit);
 EXPORT_SYMBOL(memcpy_fromiovecend);
 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
-EXPORT_SYMBOL(copy_and_csum_toiovec);
 EXPORT_SYMBOL(tcp_v4_lookup_listener);
 /* UDP/TCP exported functions for TCPv6 */
 EXPORT_SYMBOL(udp_ioctl);
@@ -362,6 +372,7 @@
 EXPORT_SYMBOL(sysctl_tcp_wmem);
 EXPORT_SYMBOL(sysctl_tcp_ecn);
 EXPORT_SYMBOL(tcp_cwnd_application_limited);
+EXPORT_SYMBOL(tcp_sendpage);
 
 EXPORT_SYMBOL(xrlim_allow);
 
diff -urN linux-2.4.4-pre2/net/packet/af_packet.c linux/net/packet/af_packet.c
--- linux-2.4.4-pre2/net/packet/af_packet.c	Fri Feb  9 11:29:44 2001
+++ linux/net/packet/af_packet.c	Mon Dec  3 22:34:51 2001
@@ -5,7 +5,7 @@
  *
  *		PACKET - implements raw packet sockets.
  *
- * Version:	$Id: af_packet.c,v 1.47 2000/12/08 17:15:54 davem Exp $
+ * Version:	$Id: af_packet.c,v 1.54 2001/03/03 01:20:11 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -149,7 +149,7 @@
  */
 
 /* List of all packet sockets. */
-static struct sock * packet_sklist = NULL;
+static struct sock * packet_sklist;
 static rwlock_t packet_sklist_lock = RW_LOCK_UNLOCKED;
 
 atomic_t packet_socks_nr;
@@ -404,6 +404,7 @@
 	struct sockaddr_ll *sll;
 	struct packet_opt *po;
 	u8 * skb_head = skb->data;
+	int skb_len = skb->len;
 #ifdef CONFIG_FILTER
 	unsigned snaplen;
 #endif
@@ -461,7 +462,7 @@
 
 		if (skb_head != skb->data) {
 			skb->data = skb_head;
-			skb->len = skb->tail - skb->data;
+			skb->len = skb_len;
 		}
 		kfree_skb(skb);
 		skb = nskb;
@@ -479,8 +480,8 @@
 		sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
 
 #ifdef CONFIG_FILTER
-	if (skb->len > snaplen)
-		__skb_trim(skb, snaplen);
+	if (pskb_trim(skb, snaplen))
+		goto drop_n_acct;
 #endif
 
 	skb_set_owner_r(skb, sk);
@@ -502,7 +503,7 @@
 #endif
 	if (skb_head != skb->data && skb_shared(skb)) {
 		skb->data = skb_head;
-		skb->len = skb->tail - skb->data;
+		skb->len = skb_len;
 	}
 drop:
 	kfree_skb(skb);
@@ -517,6 +518,7 @@
 	struct sockaddr_ll *sll;
 	struct tpacket_hdr *h;
 	u8 * skb_head = skb->data;
+	int skb_len = skb->len;
 	unsigned snaplen;
 	unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER;
 	unsigned short macoff, netoff;
@@ -534,6 +536,8 @@
 		else if (skb->pkt_type == PACKET_OUTGOING) {
 			/* Special case: outgoing packets have ll header at head */
 			skb_pull(skb, skb->nh.raw - skb->data);
+			if (skb->ip_summed == CHECKSUM_HW)
+				status |= TP_STATUS_CSUMNOTREADY;
 		}
 	}
 
@@ -580,6 +584,8 @@
 		if ((int)snaplen < 0)
 			snaplen = 0;
 	}
+	if (snaplen > skb->len-skb->data_len)
+		snaplen = skb->len-skb->data_len;
 
 	spin_lock(&sk->receive_queue.lock);
 	h = po->iovec[po->head];
@@ -623,7 +629,7 @@
 drop_n_restore:
 	if (skb_head != skb->data && skb_shared(skb)) {
 		skb->data = skb_head;
-		skb->len = skb->tail - skb->data;
+		skb->len = skb_len;
 	}
 drop:
         kfree_skb(skb);
@@ -682,7 +688,7 @@
 	if (len > dev->mtu+reserve)
 		goto out_unlock;
 
-	skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, 
+	skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 
 				msg->msg_flags & MSG_DONTWAIT, &err);
 	if (skb==NULL)
 		goto out_unlock;
@@ -1051,8 +1057,7 @@
 		msg->msg_flags|=MSG_TRUNC;
 	}
 
-	/* We can't use skb_copy_datagram here */
-	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	if (err)
 		goto out_free;
 
@@ -1318,6 +1323,9 @@
   	if (get_user(len,optlen))
   		return -EFAULT;
 
+	if (len < 0)
+		return -EINVAL;
+		
 	switch(optname)	{
 	case PACKET_STATISTICS:
 	{
@@ -1780,6 +1788,7 @@
 	sendmsg:	packet_sendmsg_spkt,
 	recvmsg:	packet_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 #endif
 
@@ -1801,17 +1810,16 @@
 	sendmsg:	packet_sendmsg,
 	recvmsg:	packet_recvmsg,
 	mmap:		packet_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 static struct net_proto_family packet_family_ops = {
-	PF_PACKET,
-	packet_create
+	family:		PF_PACKET,
+	create:		packet_create,
 };
 
-struct notifier_block packet_netdev_notifier={
-	packet_notifier,
-	NULL,
-	0
+static struct notifier_block packet_netdev_notifier = {
+	notifier_call:	packet_notifier,
 };
 
 #ifdef CONFIG_PROC_FS
@@ -1864,19 +1872,14 @@
 }
 #endif
 
-
-
 static void __exit packet_exit(void)
 {
-#ifdef CONFIG_PROC_FS
 	remove_proc_entry("net/packet", 0);
-#endif
 	unregister_netdevice_notifier(&packet_netdev_notifier);
 	sock_unregister(PF_PACKET);
 	return;
 }
 
-
 static int __init packet_init(void)
 {
 	sock_register(&packet_family_ops);
@@ -1886,7 +1889,6 @@
 #endif
 	return 0;
 }
-
 
 module_init(packet_init);
 module_exit(packet_exit);
diff -urN linux-2.4.4-pre2/net/rose/af_rose.c linux/net/rose/af_rose.c
--- linux-2.4.4-pre2/net/rose/af_rose.c	Fri Dec 29 14:35:47 2000
+++ linux/net/rose/af_rose.c	Mon Dec  3 22:34:51 2001
@@ -70,7 +70,7 @@
 int sysctl_rose_maximum_vcs             = ROSE_DEFAULT_MAXVC;
 int sysctl_rose_window_size             = ROSE_DEFAULT_WINDOW_SIZE;
 
-static struct sock *volatile rose_list = NULL;
+static struct sock *rose_list;
 
 static struct proto_ops rose_proto_ops;
 
@@ -471,7 +471,10 @@
 		
 	if (get_user(len, optlen))
 		return -EFAULT;
-	
+
+	if (len < 0)
+		return -EINVAL;
+			
 	switch (optname) {
 		case ROSE_DEFER:
 			val = sk->protinfo.rose->defer;
@@ -1056,7 +1059,7 @@
 	SOCK_DEBUG(sk, "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, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
+	if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
 	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN);
@@ -1118,7 +1121,7 @@
 		frontlen = skb_headroom(skb);
 
 		while (skb->len > 0) {
-			if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, 0, &err)) == NULL)
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, &err)) == NULL)
 				return err;
 
 			skbn->sk   = sk;
@@ -1414,6 +1417,7 @@
 	sendmsg:	rose_sendmsg,
 	recvmsg:	rose_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
diff -urN linux-2.4.4-pre2/net/socket.c linux/net/socket.c
--- linux-2.4.4-pre2/net/socket.c	Fri Nov 17 11:36:27 2000
+++ linux/net/socket.c	Mon Dec  3 22:34:51 2001
@@ -71,6 +71,7 @@
 #include <linux/poll.h>
 #include <linux/cache.h>
 #include <linux/module.h>
+#include <linux/highmem.h>
 
 #if defined(CONFIG_KMOD) && defined(CONFIG_NET)
 #include <linux/kmod.h>
@@ -104,6 +105,8 @@
 			  unsigned long count, loff_t *ppos);
 static ssize_t sock_writev(struct file *file, const struct iovec *vector,
 			  unsigned long count, loff_t *ppos);
+static ssize_t sock_writepage(struct file *file, struct page *page,
+			  int offset, size_t size, loff_t *ppos, int more);
 
 
 /*
@@ -122,7 +125,8 @@
 	release:	sock_close,
 	fasync:		sock_fasync,
 	readv:		sock_readv,
-	writev:		sock_writev
+	writev:		sock_writev,
+	writepage:	sock_writepage
 };
 
 /*
@@ -602,6 +606,24 @@
 	return sock_sendmsg(sock, &msg, size);
 }
 
+ssize_t sock_writepage(struct file *file, struct page *page,
+		       int offset, size_t size, loff_t *ppos, int more)
+{
+	struct socket *sock;
+	int flags;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	sock = socki_lookup(file->f_dentry->d_inode);
+
+	flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+	if (more)
+		flags |= MSG_MORE;
+
+	return sock->ops->sendpage(sock, page, offset, size, flags);
+}
+
 int sock_readv_writev(int type, struct inode * inode, struct file * file,
 		      const struct iovec * iov, long count, long size)
 {
@@ -1269,7 +1291,10 @@
 {
 	int err;
 	struct socket *sock;
-	
+
+	if (optlen < 0)
+		return -EINVAL;
+			
 	if ((sock = sockfd_lookup(fd, &err))!=NULL)
 	{
 		if (level == SOL_SOCKET)
diff -urN linux-2.4.4-pre2/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- linux-2.4.4-pre2/net/sunrpc/svcsock.c	Wed Mar 14 14:37:36 2001
+++ linux/net/sunrpc/svcsock.c	Mon Dec  3 22:34:51 2001
@@ -381,10 +381,17 @@
 		dprintk("svc: recvfrom returned error %d\n", -err);
 	}
 
+	/* Sorry. */
+	if (skb_is_nonlinear(skb)) {
+		if (skb_linearize(skb, GFP_ATOMIC) != 0) {
+			kfree_skb(skb);
+			svc_sock_received(svsk, 0);
+			return 0;
+		}
+	}
+
 	if (skb->ip_summed != CHECKSUM_UNNECESSARY) {
-		unsigned int csum = skb->csum;
-		csum = csum_partial(skb->h.raw, skb->len, csum);
-		if ((unsigned short)csum_fold(csum)) {
+		if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
 			skb_free_datagram(svsk->sk_sk, skb);
 			svc_sock_received(svsk, 0);
 			return 0;
@@ -395,7 +402,7 @@
 	svsk->sk_data = 1;
 
 	len  = skb->len - sizeof(struct udphdr);
-	data = (u32 *) (skb->h.raw + sizeof(struct udphdr));
+	data = (u32 *) (skb->data + sizeof(struct udphdr));
 
 	rqstp->rq_skbuff      = skb;
 	rqstp->rq_argbuf.base = data;
diff -urN linux-2.4.4-pre2/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- linux-2.4.4-pre2/net/sunrpc/xprt.c	Fri Feb  9 11:29:44 2001
+++ linux/net/sunrpc/xprt.c	Mon Dec  3 22:34:51 2001
@@ -591,7 +591,7 @@
 					   struct sk_buff *skb,
 					   int copied)
 {
-	__u8 *pkt_data = skb->h.raw + sizeof(struct udphdr);
+	int offset = sizeof(struct udphdr);
 	__u8 *cur_ptr = iov->iov_base;
 	__kernel_size_t cur_len = iov->iov_len;
 	unsigned int csum = skb->csum;
@@ -599,18 +599,18 @@
 	int slack = skb->len - copied - sizeof(struct udphdr);
 
 	if (need_csum)
-		csum = csum_partial(skb->h.raw, sizeof(struct udphdr), csum);
+		csum = csum_partial(skb->data, sizeof(struct udphdr), csum);
 	while (copied > 0) {
 		if (cur_len) {
 			int to_move = cur_len;
 			if (to_move > copied)
 				to_move = copied;
 			if (need_csum)
-				csum = csum_partial_copy_nocheck(pkt_data, cur_ptr,
-								 to_move, csum);
+				csum = skb_copy_and_csum_bits(skb, offset, cur_ptr,
+							      to_move, csum);
 			else
-				memcpy(cur_ptr, pkt_data, to_move);
-			pkt_data += to_move;
+				skb_copy_bits(skb, offset, cur_ptr, to_move);
+			offset += to_move;
 			copied -= to_move;
 			cur_ptr += to_move;
 			cur_len -= to_move;
@@ -623,7 +623,7 @@
 	}
 	if (need_csum) {
 		if (slack > 0)
-			csum = csum_partial(pkt_data, slack, csum);
+			csum = skb_checksum(skb, offset, slack, csum);
 		if ((unsigned short)csum_fold(csum))
 			return -1;
 	}
@@ -1116,6 +1116,7 @@
 	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
 	struct rpc_rqst	*req = task->tk_rqstp;
 
+	spin_lock_bh(&xprt_sock_lock);
 	spin_lock(&xprt_lock);
 	if (xprt->snd_task && xprt->snd_task != task) {
 		dprintk("RPC: %4d TCP write queue full (task %d)\n",
@@ -1131,6 +1132,7 @@
 		req->rq_bytes_sent = 0;
 	}
 	spin_unlock(&xprt_lock);
+	spin_unlock_bh(&xprt_sock_lock);
 	return xprt->snd_task == task;
 }
 
@@ -1143,10 +1145,12 @@
 	struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
 
 	if (xprt->snd_task && xprt->snd_task == task) {
+		spin_lock_bh(&xprt_sock_lock);
 		spin_lock(&xprt_lock);
 		xprt->snd_task = NULL;
 		rpc_wake_up_next(&xprt->sending);
 		spin_unlock(&xprt_lock);
+		spin_unlock_bh(&xprt_sock_lock);
 	}
 }
 
diff -urN linux-2.4.4-pre2/net/unix/af_unix.c linux/net/unix/af_unix.c
--- linux-2.4.4-pre2/net/unix/af_unix.c	Tue Mar  6 19:44:37 2001
+++ linux/net/unix/af_unix.c	Mon Dec  3 22:34:51 2001
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.111 2001/02/02 11:37:28 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.116 2001/03/03 01:20:11 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
@@ -1182,7 +1182,7 @@
 	if ((unsigned)len > sk->sndbuf - 32)
 		goto out;
 
-	skb = sock_alloc_send_skb(sk, len, 0, msg->msg_flags&MSG_DONTWAIT, &err);
+	skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err);
 	if (skb==NULL)
 		goto out;
 
@@ -1285,7 +1285,6 @@
 	struct sockaddr_un *sunaddr=msg->msg_name;
 	int err,size;
 	struct sk_buff *skb;
-	int limit=0;
 	int sent=0;
 
 	err = -EOPNOTSUPP;
@@ -1323,21 +1322,10 @@
 			size = (128 * 1024) / 2;
 			
 		/*
-		 *	Keep to page sized kmalloc()'s as various people
-		 *	have suggested. Big mallocs stress the vm too
-		 *	much.
-		 */
-
-		if (size > PAGE_SIZE-16)
-			limit = PAGE_SIZE-16; /* Fall back to a page if we can't grab a big buffer this instant */
-		else
-			limit = 0;	/* Otherwise just grab and wait */
-
-		/*
 		 *	Grab a buffer
 		 */
 		 
-		skb=sock_alloc_send_skb(sk,size,limit,msg->msg_flags&MSG_DONTWAIT, &err);
+		skb=sock_alloc_send_skb(sk,size,msg->msg_flags&MSG_DONTWAIT, &err);
 
 		if (skb==NULL)
 			goto out_err;
@@ -1821,6 +1809,7 @@
 	sendmsg:	unix_stream_sendmsg,
 	recvmsg:	unix_stream_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 struct proto_ops unix_dgram_ops = {
@@ -1841,6 +1830,7 @@
 	sendmsg:	unix_dgram_sendmsg,
 	recvmsg:	unix_dgram_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 struct net_proto_family unix_family_ops = {
diff -urN linux-2.4.4-pre2/net/wanrouter/Makefile linux/net/wanrouter/Makefile
--- linux-2.4.4-pre2/net/wanrouter/Makefile	Fri Dec 29 14:07:24 2000
+++ linux/net/wanrouter/Makefile	Mon Dec  3 22:34:51 2001
@@ -12,7 +12,7 @@
 export-objs := wanmain.o
 
 obj-y :=  wanproc.o wanmain.o
-obj-m := $(O_TARGET)
+obj-m := $(O_TARGET) af_wanpipe.o
 
 include $(TOPDIR)/Rules.make
 
diff -urN linux-2.4.4-pre2/net/wanrouter/af_wanpipe.c linux/net/wanrouter/af_wanpipe.c
--- linux-2.4.4-pre2/net/wanrouter/af_wanpipe.c	Wed Dec 31 16:00:00 1969
+++ linux/net/wanrouter/af_wanpipe.c	Mon Dec  3 22:34:51 2001
@@ -0,0 +1,2765 @@
+/*****************************************************************************
+* af_wanpipe.c	WANPIPE(tm) Secure Socket Layer.
+*
+* Author:	Nenad Corbic	<ncorbic@sangoma.com>
+*
+* Copyright:	(c) 2000 Sangoma Technologies Inc.
+*
+*		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.
+* ============================================================================
+* Due Credit:
+*               Wanpipe socket layer is based on Packet and 
+*               the X25 socket layers. The above sockets were 
+*               used for the specific use of Sangoma Technoloiges 
+*               API programs. 
+*               Packet socket Authors: Ross Biro, Fred N. van Kempen and 
+*                                      Alan Cox.
+*               X25 socket Author: Jonathan Naylor.
+* ============================================================================
+* Apr 25, 2000  Nenad Corbic     o Added the ability to send zero length packets.
+* Mar 13, 2000  Nenad Corbic	 o Added a tx buffer check via ioctl call.
+* Mar 06, 2000  Nenad Corbic     o Fixed the corrupt sock lcn problem.
+*                                  Server and client applicaton can run
+*                                  simultaneously without conflicts.
+* Feb 29, 2000  Nenad Corbic     o Added support for PVC protocols, such as
+*                                  CHDLC, Frame Relay and HDLC API.
+* Jan 17, 2000 	Nenad Corbic	 o Initial version, based on AF_PACKET socket.
+*			           X25API support only. 
+*
+******************************************************************************/
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/wireless.h>
+#include <linux/kmod.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/wanpipe.h>
+#include <linux/if_wanpipe.h>
+#include <linux/pkt_sched.h>
+#include <linux/tcp.h>
+#include <linux/if_wanpipe_common.h>
+#include <linux/sdla_x25.h>
+
+#ifdef CONFIG_INET
+#include <net/inet_common.h>
+#endif
+
+#define SLOW_BACKOFF 0.1*HZ
+#define FAST_BACKOFF 0.01*HZ
+
+//#define PRINT_DEBUG
+#ifdef PRINT_DEBUG
+	#define DBG_PRINTK(format, a...) printk(format, ## a)
+#else
+	#define DBG_PRINTK(format, a...)
+#endif      
+
+#if defined(LINUX_2_1)
+ #define dev_put(a)
+ #define __sock_put(a)
+ #define sock_hold(a)
+ #define DECLARE_WAITQUEUE(a,b) \
+		struct wait_queue a = { b, NULL }
+#endif
+		
+/* SECURE SOCKET IMPLEMENTATION 
+ * 
+ *   TRANSMIT:
+ *
+ *      When the user sends a packet via send() system call
+ *      the wanpipe_sendmsg() function is executed.  
+ *      
+ *      Each packet is enqueud into sk->write_queue transmit
+ *      queue. When the packet is enqueued, a delayed transmit
+ *      timer is triggerd which acts as a Bottom Half hander. 
+ *
+ *      wanpipe_delay_transmit() function (BH), dequeues packets
+ *      from the sk->write_queue transmit queue and sends it 
+ *      to the deriver via dev->hard_start_xmit(skb, dev) function.  
+ *      Note, this function is actual a function pointer of if_send()
+ *      routine in the wanpipe driver.
+ *
+ *      X25API GUARANTEED DELIVERY:
+ *
+ *         In order to provide 100% guaranteed packet delivery, 
+ *         an atomic 'packet_sent' counter is implemented.  Counter 
+ *         is incremented for each packet enqueued 
+ *         into sk->write_queue.  Counter is decremented each
+ *         time wanpipe_delayed_transmit() function successfuly 
+ *         passes the packet to the driver. Before each send(), a poll
+ *         routine checks the sock resources The maximum value of
+ *         packet sent counter is 1, thus if one packet is queued, the
+ *         application will block untill that packet is passed to the
+ *         driver.
+ *
+ *   RECEIVE:
+ *
+ *      Wanpipe device drivers call the socket bottom half
+ *      function, wanpipe_rcv() to queue the incoming packets
+ *      into an AF_WANPIPE socket queue.  Based on wanpipe_rcv()
+ *      return code, the driver knows whether the packet was
+ *      sucessfully queued.  If the socket queue is full, 
+ *      protocol flow control is used by the driver, if any, 
+ *      to slow down the traffic untill the sock queue is free.
+ *
+ *      Every time a packet arrives into a socket queue the 
+ *      socket wakes up processes which are waiting to receive
+ *      data.
+ *
+ *      If the socket queue is full, the driver sets a block
+ *      bit which signals the socket to kick the wanpipe driver
+ *      bottom half hander when the socket queue is partialy
+ *      empty. wanpipe_recvmsg() function performs this action.
+ * 
+ *      In case of x25api, packets will never be dropped, since
+ *      flow control is available. 
+ *      
+ *      In case of streaming protocols like CHDLC, packets will 
+ *      be dropped but the statistics will be generated. 
+ */
+
+
+/* The code below is used to test memory leaks. It prints out
+ * a message every time kmalloc and kfree system calls get executed.
+ * If the calls match there is no leak :)
+ */
+
+/***********FOR DEBUGGING PURPOSES*********************************************
+#define KMEM_SAFETYZONE 8
+
+static void * dbg_kmalloc(unsigned int size, int prio, int line) {
+	void * v = kmalloc(size,prio);
+	printk(KERN_INFO "line %d  kmalloc(%d,%d) = %p\n",line,size,prio,v);
+	return v;
+}
+static void dbg_kfree(void * v, int line) {
+	printk(KERN_INFO "line %d  kfree(%p)\n",line,v);
+	kfree(v);
+}
+
+#define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__)
+#define kfree(x) dbg_kfree(x,__LINE__)
+******************************************************************************/
+
+
+/* List of all wanpipe sockets. */
+struct sock * wanpipe_sklist = NULL;
+static rwlock_t wanpipe_sklist_lock = RW_LOCK_UNLOCKED;
+
+atomic_t wanpipe_socks_nr;
+static unsigned long wanpipe_tx_critical=0;
+
+#if 0
+/* Private wanpipe socket structures. */
+struct wanpipe_opt
+{
+	void   *mbox;		/* Mail box  */
+	void   *card; 		/* Card bouded to */
+	netdevice_t *dev;	/* Bounded device */
+	unsigned short lcn;	/* Binded LCN */
+	unsigned char  svc;	/* 0=pvc, 1=svc */
+	unsigned char  timer;   /* flag for delayed transmit*/	
+	struct timer_list tx_timer;
+	unsigned poll_cnt;
+	unsigned char force;	/* Used to force sock release */
+	atomic_t packet_sent;   
+};
+#endif
+
+static int sk_count=0;
+extern struct proto_ops wanpipe_ops;
+static unsigned long find_free_critical=0;
+
+static void wanpipe_unlink_driver (struct sock *);
+static void wanpipe_link_driver (netdevice_t *,struct sock *sk);
+static void wanpipe_wakeup_driver(struct sock *sk);
+static int execute_command(struct sock *, unsigned char, unsigned int);
+static int check_dev (netdevice_t *, sdla_t *);
+netdevice_t * wanpipe_find_free_dev (sdla_t *);
+static void wanpipe_unlink_card (struct sock *);
+static int wanpipe_link_card (struct sock *);
+static struct sock *wanpipe_make_new(struct sock *);
+static struct sock *wanpipe_alloc_socket(void);
+static inline int get_atomic_device (netdevice_t *);
+static int wanpipe_exec_cmd(struct sock *, int, unsigned int);
+static int get_ioctl_cmd (struct sock *, void *);
+static int set_ioctl_cmd (struct sock *, void *);
+static void release_device (netdevice_t *);
+static void wanpipe_kill_sock_timer (unsigned long data);
+static void wanpipe_kill_sock_irq (struct sock *);
+static void wanpipe_kill_sock_accept (struct sock *);
+static int wanpipe_do_bind(struct sock *, netdevice_t *, int);
+struct sock * get_newsk_from_skb (struct sk_buff *);
+static int wanpipe_debug (struct sock *, void *);
+static void wanpipe_delayed_transmit (unsigned long data);
+static void release_driver(struct sock *);
+static void start_cleanup_timer (struct sock *);
+static void check_write_queue(struct sock *);
+static int check_driver_busy (struct sock *);
+
+/*============================================================
+ * wanpipe_rcv
+ *
+ *	Wanpipe socket bottom half handler.  This function
+ *      is called by the WANPIPE device drivers to queue a
+ *      incomming packet into the socket receive queue. 
+ *      Once the packet is queued, all processes waiting to 
+ *      read are woken up.
+ *
+ *      During socket bind, this function is bounded into
+ *      WANPIPE driver private.
+ *===========================================================*/
+
+static int wanpipe_rcv(struct sk_buff *skb, netdevice_t *dev,  struct sock *sk)
+{
+	struct wan_sockaddr_ll *sll = (struct wan_sockaddr_ll*)skb->cb;
+	wanpipe_common_t *chan = dev->priv;
+	/*
+	 *	When we registered the protocol we saved the socket in the data
+	 *	field for just this event.
+	 */
+
+	skb->dev = dev;
+
+	sll->sll_family = AF_WANPIPE;
+	sll->sll_hatype = dev->type;
+	sll->sll_protocol = skb->protocol;
+	sll->sll_pkttype = skb->pkt_type;
+	sll->sll_ifindex = dev->ifindex;
+	sll->sll_halen = 0;
+
+	if (dev->hard_header_parse)
+		sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
+
+	/* 
+	 * WAN_PACKET_DATA : Data which should be passed up the receive queue.
+         * WAN_PACKET_ASYC : Asynchronous data like place call, which should
+         *                   be passed up the listening sock.
+         * WAN_PACKET_ERR  : Asynchronous data like clear call or restart 
+         *                   which should go into an error queue.
+         */
+	switch (skb->pkt_type){
+
+		case WAN_PACKET_DATA:
+			if (sock_queue_rcv_skb(sk,skb)<0){
+				return -ENOMEM;
+			}
+			break;
+		case WAN_PACKET_CMD:
+			sk->state = chan->state;
+			/* Bug fix: update Mar6. 
+                         * Do not set the sock lcn number here, since
+         		 * cmd is not guaranteed to be executed on the
+                         * board, thus Lcn could be wrong */
+			sk->data_ready(sk,skb->len);
+			kfree_skb(skb);
+			break;
+		case WAN_PACKET_ERR:
+			sk->state = chan->state;
+			if (sock_queue_err_skb(sk,skb)<0){
+				return -ENOMEM;
+			}
+			break;
+		default:
+			printk(KERN_INFO "wansock: BH Illegal Packet Type Dropping\n");
+			kfree_skb(skb); 
+			break;
+	}
+
+//??????????????????????
+//	if (sk->state == WANSOCK_DISCONNECTED){
+//		if (sk->zapped){
+//			//printk(KERN_INFO "wansock: Disconnected, killing early\n");
+//			wanpipe_unlink_driver(sk);
+//			sk->bound_dev_if = 0;
+//		}
+//	}
+
+	return 0;
+}
+
+/*============================================================
+ * wanpipe_listen_rcv
+ *
+ *	Wanpipe LISTEN socket bottom half handler.  This function
+ *      is called by the WANPIPE device drivers to queue an
+ *      incomming call into the socket listening queue. 
+ *      Once the packet is queued, the waiting accept() process 
+ *      is woken up.
+ *
+ *      During socket bind, this function is bounded into
+ *      WANPIPE driver private. 
+ * 
+ *      IMPORTANT NOTE:
+ *          The accept call() is waiting for an skb packet
+ *          which contains a pointer to a device structure.
+ *
+ *          When we do a bind to a device structre, we 
+ *          bind a newly created socket into "chan->sk".  Thus, 
+ *          when accept receives the skb packet, it will know 
+ *          from which dev it came form, and in turn it will know
+ *          the address of the new sock.
+ *
+ *  	NOTE: This function gets called from driver ISR.
+ *===========================================================*/
+
+static int wanpipe_listen_rcv (struct sk_buff *skb,  struct sock *sk)
+{
+
+	struct wan_sockaddr_ll *sll = (struct wan_sockaddr_ll*)skb->cb;
+	struct sock *newsk;
+	netdevice_t *dev; 
+	sdla_t *card;
+	mbox_cmd_t *mbox_ptr;
+	wanpipe_common_t *chan;
+
+	/* Find a free device, if none found, all svc's are busy 
+         */
+
+	card = (sdla_t*)sk->protinfo.af_wanpipe->card;
+	if (!card){
+		printk(KERN_INFO "wansock: LISTEN ERROR, No Card\n");
+		return -ENODEV;
+	}
+	
+	dev = wanpipe_find_free_dev(card);
+	if (!dev){
+		printk(KERN_INFO "wansock: LISTEN ERROR, No Free Device\n");
+		return -ENODEV;
+	}
+
+	chan=dev->priv;	
+	chan->state = WANSOCK_CONNECTING;
+
+	/* Allocate a new sock, which accept will bind
+         * and pass up to the user 
+	 */
+	if ((newsk = wanpipe_make_new(sk)) == NULL){
+		release_device(dev);
+		return -ENOMEM;
+	}
+
+
+	/* Initialize the new sock structure 
+	 */
+	newsk->bound_dev_if = dev->ifindex;
+	newsk->protinfo.af_wanpipe->card = sk->protinfo.af_wanpipe->card;
+
+	/* Insert the sock into the main wanpipe
+         * sock list.
+         */
+	atomic_inc(&wanpipe_socks_nr);
+
+	/* Allocate and fill in the new Mail Box. Then
+         * bind the mail box to the sock. It will be 
+         * used by the ioctl call to read call information
+         * and to execute commands. 
+         */	
+	if ((mbox_ptr = kmalloc(sizeof(mbox_cmd_t), GFP_ATOMIC)) == NULL) {
+		wanpipe_kill_sock_irq (newsk);
+		release_device(dev);		
+		return -ENOMEM;
+	}
+	memset(mbox_ptr, 0, sizeof(mbox_cmd_t));
+	memcpy(mbox_ptr,skb->data,skb->len);
+
+	/* Register the lcn on which incoming call came
+         * from. Thus, if we have to clear it, we know
+         * whic lcn to clear 
+	 */ 
+
+	newsk->protinfo.af_wanpipe->lcn = mbox_ptr->cmd.lcn;
+	newsk->protinfo.af_wanpipe->mbox = (void *)mbox_ptr;
+
+	DBG_PRINTK(KERN_INFO "NEWSOCK : Device %s, bind to lcn %i\n",
+			dev->name,mbox_ptr->cmd.lcn);
+
+	chan->lcn = mbox_ptr->cmd.lcn;
+	card->u.x.svc_to_dev_map[(chan->lcn%MAX_X25_LCN)] = dev;
+
+	newsk->zapped=0;
+	newsk->num = htons(X25_PROT);
+
+	if (wanpipe_do_bind(newsk,dev,newsk->num)){
+		wanpipe_kill_sock_irq (newsk);
+		release_device(dev);
+		return -EINVAL;
+	}
+	newsk->state = WANSOCK_CONNECTING;
+
+
+	/* Fill in the standard sock address info */
+
+	sll->sll_family = AF_WANPIPE;
+	sll->sll_hatype = dev->type;
+	sll->sll_protocol = skb->protocol;
+	sll->sll_pkttype = skb->pkt_type;
+	sll->sll_ifindex = dev->ifindex;
+	sll->sll_halen = 0;
+
+	skb->dev = dev;
+	sk->ack_backlog++;
+
+	/* We must do this manually, since the sock_queue_rcv_skb()
+	 * function sets the skb->dev to NULL.  However, we use
+	 * the dev field in the accept function.*/ 
+	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= 
+			(unsigned)sk->rcvbuf){
+
+         	wanpipe_unlink_driver(newsk);
+		wanpipe_kill_sock_irq (newsk);
+		--sk->ack_backlog;
+		return -ENOMEM;
+	}	
+
+	skb_set_owner_r(skb, sk);
+	skb_queue_tail(&sk->receive_queue, skb);
+	sk->data_ready(sk,skb->len);
+	
+	return 0;
+}
+
+
+
+/*============================================================
+ * wanpipe_make_new
+ *
+ *	Create a new sock, and allocate a wanpipe private
+ *      structure to it. Also, copy the important data
+ *      from the original sock to the new sock.
+ *
+ *      This function is used by wanpipe_listen_rcv() listen
+ *      bottom half handler.  A copy of the listening sock
+ *      is created using this function.
+ *
+ *===========================================================*/
+
+static struct sock *wanpipe_make_new(struct sock *osk)
+{
+	struct sock *sk;
+
+	if (osk->type != SOCK_RAW)
+		return NULL;
+
+	if ((sk = wanpipe_alloc_socket()) == NULL)
+		return NULL;
+
+	sk->type        = osk->type;
+	sk->socket      = osk->socket;
+	sk->priority    = osk->priority;
+	sk->protocol    = osk->protocol;
+	sk->num		= osk->num;
+	sk->rcvbuf      = osk->rcvbuf;
+	sk->sndbuf      = osk->sndbuf;
+	sk->debug       = osk->debug;
+	sk->state       = WANSOCK_CONNECTING;
+	sk->sleep       = osk->sleep;
+
+	return sk;
+}
+
+/*============================================================
+ * wanpipe_make_new
+ *
+ *	Allocate memory for the a new sock, and sock
+ *      private data.  
+ *	
+ *	Increment the module use count.
+ *       	
+ *      This function is used by wanpipe_create() and 
+ *      wanpipe_make_new() functions. 
+ *
+ *===========================================================*/
+
+static struct sock *wanpipe_alloc_socket(void)
+{
+	struct sock *sk;
+	struct wanpipe_opt *wan_opt;
+
+	if ((sk = sk_alloc(PF_WANPIPE, GFP_ATOMIC, 1)) == NULL)
+		return NULL;
+
+	if ((wan_opt = kmalloc(sizeof(struct wanpipe_opt), GFP_ATOMIC)) == NULL) {
+		sk_free(sk);
+		return NULL;
+	}
+	memset(wan_opt, 0x00, sizeof(struct wanpipe_opt));
+
+	sk->protinfo.af_wanpipe = wan_opt;
+	sk->protinfo.destruct_hook = wan_opt;
+
+	/* Use timer to send data to the driver. This will act
+         * as a BH handler for sendmsg functions */
+	sk->protinfo.af_wanpipe->tx_timer.data=(unsigned long)sk;
+	sk->protinfo.af_wanpipe->tx_timer.function=wanpipe_delayed_transmit;
+
+	MOD_INC_USE_COUNT;
+
+	sock_init_data(NULL, sk);
+	return sk;
+}
+
+
+/*============================================================
+ * wanpipe_sendmsg
+ *
+ *	This function implements a sendto() system call,
+ *      for AF_WANPIPE socket family. 
+ *      During socket bind() sk->bound_dev_if is initialized
+ *      to a correct network device. This number is used
+ *      to find a network device to which the packet should
+ *      be passed to.
+ *
+ *      Each packet is queued into sk->write_queue and 
+ *      delayed transmit bottom half handler is marked for 
+ *      execution.
+ *
+ *      A socket must be in WANSOCK_CONNECTED state before
+ *      a packet is queued into sk->write_queue.
+ *===========================================================*/
+
+static int wanpipe_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+			  struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct wan_sockaddr_ll *saddr=(struct wan_sockaddr_ll *)msg->msg_name;
+	struct sk_buff *skb;
+	netdevice_t *dev;
+	unsigned short proto;
+	unsigned char *addr;
+	int ifindex, err, reserve = 0;
+
+	
+	if (!sk->zapped)
+		return -ENETDOWN;
+
+	if (sk->state != WANSOCK_CONNECTED)
+		return -ENOTCONN;	
+
+	if (msg->msg_flags&~MSG_DONTWAIT) 
+		return(-EINVAL);
+
+	/* it was <=, now one can send
+         * zero length packets */
+	if (len < sizeof(x25api_hdr_t))
+		return -EINVAL;
+
+	if (saddr == NULL) {
+		ifindex	= sk->bound_dev_if;
+		proto	= sk->num;
+		addr	= NULL;
+
+	}else{
+		if (msg->msg_namelen < sizeof(struct wan_sockaddr_ll)){ 
+			return -EINVAL;
+		}
+
+		ifindex = sk->bound_dev_if;
+		proto	= saddr->sll_protocol;
+		addr	= saddr->sll_addr;
+	}
+
+	dev = dev_get_by_index(ifindex);
+	if (dev == NULL){
+		printk(KERN_INFO "wansock: Send failed, dev index: %i\n",ifindex);
+		return -ENXIO;
+	}
+	dev_put(dev);
+	
+	if (sock->type == SOCK_RAW)
+		reserve = dev->hard_header_len;
+
+	if (len > dev->mtu+reserve){
+  		return -EMSGSIZE;
+	}
+
+      #ifndef LINUX_2_4
+	dev_lock_list();
+      #endif
+      
+      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+	skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 
+				msg->msg_flags & MSG_DONTWAIT, &err);
+      #else
+	skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, 
+				msg->msg_flags & MSG_DONTWAIT, &err);
+      #endif
+
+	if (skb==NULL){
+		goto out_unlock;
+	}
+		
+	skb_reserve(skb, (dev->hard_header_len+15)&~15);
+	skb->nh.raw = skb->data;
+
+	/* Returns -EFAULT on error */
+	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+	if (err){
+		goto out_free;
+	}
+
+	if (dev->hard_header) {
+		int res;
+		err = -EINVAL;
+		res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len);
+		if (res<0){
+			goto out_free;
+		}
+	}
+
+	skb->protocol = proto;
+	skb->dev = dev;
+	skb->priority = sk->priority;
+	skb->pkt_type = WAN_PACKET_DATA;
+
+	err = -ENETDOWN;
+	if (!(dev->flags & IFF_UP))
+		goto out_free;
+
+      #ifndef LINUX_2_4
+	dev_unlock_list();
+      #endif
+	
+	if (atomic_read(&sk->wmem_alloc) + skb->truesize > (unsigned int)sk->sndbuf){
+		kfree_skb(skb);
+		return -ENOBUFS;
+	}
+
+	skb_queue_tail(&sk->write_queue,skb);
+	atomic_inc(&sk->protinfo.af_wanpipe->packet_sent);
+
+	if (!(test_and_set_bit(0,&sk->protinfo.af_wanpipe->timer))){
+		del_timer(&sk->protinfo.af_wanpipe->tx_timer);
+		sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+1;
+		add_timer(&sk->protinfo.af_wanpipe->tx_timer);
+	}	
+	
+	return(len);
+
+out_free:
+	kfree_skb(skb);
+out_unlock:
+#ifndef LINUX_2_4
+	dev_unlock_list();
+#endif
+	return err;
+}
+
+/*============================================================
+ * wanpipe_delayed_tarnsmit
+ *
+ *	Transmit bottom half handeler. It dequeues packets
+ *      from sk->write_queue and passes them to the 
+ *      driver.  If the driver is busy, the packet is 
+ *      re-enqueued.  
+ *
+ *      Packet Sent counter is decremented on successful
+ *      transmission. 
+ *===========================================================*/
+
+
+static void wanpipe_delayed_transmit (unsigned long data)
+{
+	struct sock *sk=(struct sock *)data;
+	struct sk_buff *skb;
+	netdevice_t *dev = sk->protinfo.af_wanpipe->dev;
+	sdla_t *card = (sdla_t*)sk->protinfo.af_wanpipe->card;
+
+	if (!card || !dev){
+		clear_bit (0,&sk->protinfo.af_wanpipe->timer);
+		DBG_PRINTK(KERN_INFO "wansock: Transmit delay, no dev or card\n");
+		return;
+	}
+	
+	if (sk->state != WANSOCK_CONNECTED || !sk->zapped){	
+		clear_bit (0,&sk->protinfo.af_wanpipe->timer);
+		DBG_PRINTK(KERN_INFO "wansock: Tx Timer, State not CONNECTED\n");
+		return;
+	}
+	
+	/* If driver is executing command, we must offload
+         * the board by not sending data. Otherwise a 
+         * pending command will never get a free buffer
+         * to execute */ 	
+	if (atomic_read(&card->u.x.command_busy)){
+		sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+SLOW_BACKOFF;
+		add_timer(&sk->protinfo.af_wanpipe->tx_timer);
+		DBG_PRINTK(KERN_INFO "wansock: Tx Timer, command bys BACKOFF\n");
+		return;
+	}
+
+	
+	if (test_and_set_bit(0,&wanpipe_tx_critical)){
+		printk(KERN_INFO "WanSock: Tx timer critical %s\n",dev->name);
+		sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+SLOW_BACKOFF;
+		add_timer(&sk->protinfo.af_wanpipe->tx_timer);
+		return;
+	}	
+	
+	/* Check for a packet in the fifo and send */
+	if ((skb=skb_dequeue(&sk->write_queue)) != NULL){
+
+		if (dev->hard_start_xmit(skb, dev) != 0){			
+
+			/* Driver failed to transmit, re-enqueue
+                         * the packet and retry again later */
+			skb_queue_head(&sk->write_queue,skb);
+			clear_bit(0,&wanpipe_tx_critical);
+			return;
+		}else{
+
+			/* Packet Sent successful. Check for more packets
+                         * if more packets, re-trigger the transmit routine 
+                         * other wise exit
+                         */
+			atomic_dec(&sk->protinfo.af_wanpipe->packet_sent);
+
+			if (skb_peek(&sk->write_queue) == NULL){
+				/* If there is nothing to send, kick
+				 * the poll routine, which will trigger
+				 * the application to send more data */
+				sk->data_ready(sk,0);
+				clear_bit (0,&sk->protinfo.af_wanpipe->timer);
+			}else{
+				/* Reschedule as fast as possible */
+				sk->protinfo.af_wanpipe->tx_timer.expires=jiffies+1;
+				add_timer(&sk->protinfo.af_wanpipe->tx_timer);
+			}
+		}
+	}
+	clear_bit(0,&wanpipe_tx_critical);
+}
+
+/*============================================================
+ * execute_command 
+ *
+ *	Execute x25api commands.  The atomic variable
+ *      chan->command is used to indicate to the driver that
+ *      command is pending for exection.  The acutal command
+ *      structure is placed into a sock mbox structure 
+ *      (sk->protinfo.af_wanpipe->mbox).
+ *
+ *      The sock private structure, mbox is
+ *      used as shared memory between sock and the driver.
+ *      Driver uses the sock mbox to execute the command
+ *      and return the result.  
+ *
+ *      For all command except PLACE CALL, the function
+ *      waits for the result.  PLACE CALL can be ether
+ *      blocking or nonblocking. The user sets this option
+ *      via ioctl call.
+ *===========================================================*/
+
+
+static int execute_command(struct sock *sk,  unsigned char cmd, unsigned int flags)
+{
+	netdevice_t *dev;
+	wanpipe_common_t *chan=NULL;
+	int err=0;
+	DECLARE_WAITQUEUE(wait, current);
+	
+	dev = dev_get_by_index(sk->bound_dev_if);
+	if (dev == NULL){
+		printk(KERN_INFO "wansock: Exec failed no dev %i\n",
+			sk->bound_dev_if);
+		return -ENODEV;
+	}
+	dev_put(dev);
+
+	if ((chan=dev->priv) == NULL){
+		printk(KERN_INFO "wansock: Exec cmd failed no priv area\n");
+		return -ENODEV;
+	}
+
+	if (atomic_read(&chan->command)){
+		printk(KERN_INFO "wansock: ERROR: Command already running %x, %s\n",
+			atomic_read(&chan->command),dev->name);
+		return -EINVAL;
+	}
+
+	if (!sk->protinfo.af_wanpipe->mbox){
+		printk(KERN_INFO "wansock: In execute without MBOX\n");
+		return -EINVAL;
+	}
+
+	((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.command=cmd;	
+	((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn = 
+					sk->protinfo.af_wanpipe->lcn;
+	((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.result=0x7F;
+
+
+	if (flags & O_NONBLOCK){
+		cmd |= 0x80;
+		atomic_set(&chan->command, cmd);
+	}else{
+		atomic_set(&chan->command, cmd);
+	}
+
+	add_wait_queue(sk->sleep,&wait);
+	current->state = TASK_INTERRUPTIBLE;
+	for (;;){
+		if (((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.result != 0x7F) {
+			err = 0;
+			break;
+		}
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(sk->sleep,&wait);
+	
+	return err;
+}
+
+/*============================================================
+ * wanpipe_destroy_timer 
+ *
+ *	Used by wanpipe_release, to delay release of
+ *      the socket.
+ *===========================================================*/
+
+static void wanpipe_destroy_timer(unsigned long data)
+{
+	struct sock *sk=(struct sock *)data;
+
+	if ((!atomic_read(&sk->wmem_alloc) && !atomic_read(&sk->rmem_alloc)) ||
+	    (++sk->protinfo.af_wanpipe->force == 5)) {
+
+		if (atomic_read(&sk->wmem_alloc) || atomic_read(&sk->rmem_alloc))
+			printk(KERN_INFO "wansock: Warning, Packet Discarded due to sock shutdown!\n");
+
+		if (sk->protinfo.af_wanpipe){
+			kfree(sk->protinfo.af_wanpipe);
+			sk->protinfo.af_wanpipe=NULL;
+		}
+		
+              #ifdef LINUX_2_4
+		if (atomic_read(&sk->refcnt) != 1){
+			atomic_set(&sk->refcnt,1);
+			DBG_PRINTK(KERN_INFO "wansock: Error, wrong reference count: %i ! :delay.\n",
+					atomic_read(&sk->refcnt));
+		}
+		sock_put(sk);
+              #else
+		sk_free(sk);
+              #endif
+		atomic_dec(&wanpipe_socks_nr);
+		MOD_DEC_USE_COUNT;
+		return;
+	}
+
+	sk->timer.expires=jiffies+5*HZ;
+	add_timer(&sk->timer);
+	printk(KERN_INFO "wansock: packet sk destroy delayed\n");
+}
+
+/*============================================================
+ * wanpipe_unlink_driver
+ *
+ * 	When the socket is released, this function is 
+ *      used to remove links that bind the sock and the
+ *      driver together.  
+ *===========================================================*/
+static void wanpipe_unlink_driver (struct sock *sk)
+{
+	netdevice_t *dev;
+	wanpipe_common_t *chan=NULL;
+
+	sk->zapped=0;
+	sk->state = WANSOCK_DISCONNECTED;
+	sk->protinfo.af_wanpipe->dev = NULL;
+
+	dev = dev_get_by_index(sk->bound_dev_if);
+	if (!dev){
+		printk(KERN_INFO "wansock: No dev on release\n");
+		return;
+	}			
+	dev_put(dev);
+
+	if ((chan = dev->priv) == NULL){
+		printk(KERN_INFO "wansock: No Priv Area on release\n");
+		return;
+	}
+
+	set_bit(0,&chan->common_critical);
+	chan->sk=NULL;
+	chan->func=NULL;
+	chan->mbox=NULL;
+	chan->tx_timer=NULL;
+	clear_bit(0,&chan->common_critical);
+	release_device(dev);
+	
+	return;
+}
+
+/*============================================================
+ * wanpipe_link_driver
+ *
+ * 	Upon successful bind(), sock is linked to a driver
+ *      by binding in the wanpipe_rcv() bottom half handler
+ *      to the driver function pointer, as well as sock and
+ *      sock mailbox addresses.  This way driver can pass
+ *      data up the socket.
+ *===========================================================*/
+
+static void wanpipe_link_driver (netdevice_t *dev, struct sock *sk)
+{
+	wanpipe_common_t *chan = dev->priv;
+	if (!chan)
+		return;
+	set_bit(0,&chan->common_critical);
+	chan->sk=sk;
+	chan->func=wanpipe_rcv;
+	chan->mbox=sk->protinfo.af_wanpipe->mbox;
+	chan->tx_timer = &sk->protinfo.af_wanpipe->tx_timer;
+	sk->protinfo.af_wanpipe->dev=dev;
+	sk->zapped = 1;
+	clear_bit(0,&chan->common_critical);
+}
+
+
+/*============================================================
+ * release_device
+ *
+ *   	During sock release, clear a critical bit, which 
+ *      marks the device a being taken.
+ *===========================================================*/
+
+
+static void release_device (netdevice_t *dev)
+{
+	wanpipe_common_t *chan=dev->priv;
+	clear_bit(0,(void*)&chan->rw_bind);
+}
+
+/*============================================================
+ * wanpipe_release
+ *
+ *	Close a PACKET socket. This is fairly simple. We 
+ *      immediately go to 'closed' state and remove our 
+ *      protocol entry in the device list.
+ *===========================================================*/
+
+#ifdef LINUX_2_4
+static int wanpipe_release(struct socket *sock)
+#else
+static int wanpipe_release(struct socket *sock, struct socket *peersock)
+#endif
+{
+	
+#ifndef LINUX_2_4
+	struct sk_buff	*skb;
+#endif
+	struct sock *sk = sock->sk;
+	struct sock **skp;
+	
+	if (!sk)
+		return 0;
+
+	check_write_queue(sk);
+
+	/* Kill the tx timer, if we don't kill it now, the timer
+         * will run after we kill the sock.  Timer code will 
+         * try to access the sock which has been killed and cause
+         * kernel panic */
+
+	del_timer(&sk->protinfo.af_wanpipe->tx_timer);
+
+	/*
+	 *	Unhook packet receive handler.
+	 */
+
+	if (sk->num == htons(X25_PROT) && sk->state != WANSOCK_DISCONNECTED && sk->zapped){
+		netdevice_t *dev = dev_get_by_index(sk->bound_dev_if);
+		wanpipe_common_t *chan;
+		if (dev){
+			chan=dev->priv;
+			atomic_set(&chan->disconnect,1);
+			DBG_PRINTK(KERN_INFO "wansock: Sending Clear Indication %i\n",
+					sk->state);
+			dev_put(dev);
+		}	
+	}
+
+	set_bit(1,&wanpipe_tx_critical);
+	write_lock(&wanpipe_sklist_lock);
+	for (skp = &wanpipe_sklist; *skp; skp = &(*skp)->next) {
+		if (*skp == sk) {
+			*skp = sk->next;
+			__sock_put(sk);
+			break;
+		}
+	}
+	write_unlock(&wanpipe_sklist_lock);
+	clear_bit(1,&wanpipe_tx_critical);
+
+
+	
+	release_driver(sk);
+
+	
+	/*
+	 *	Now the socket is dead. No more input will appear.
+	 */
+
+	sk->state_change(sk);	/* It is useless. Just for sanity. */
+
+	sock->sk = NULL;
+	sk->socket = NULL;
+	sk->dead = 1;
+
+	/* Purge queues */
+#ifdef LINUX_2_4
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+	skb_queue_purge(&sk->error_queue);
+#else	
+
+	while ((skb=skb_dequeue(&sk->receive_queue))!=NULL){
+		kfree_skb(skb);
+	}
+	while ((skb=skb_dequeue(&sk->error_queue))!=NULL){
+		kfree_skb(skb);
+	}
+	while ((skb=skb_dequeue(&sk->write_queue))!=NULL){
+		kfree_skb(skb);
+	}
+#endif	
+	if (atomic_read(&sk->rmem_alloc) || atomic_read(&sk->wmem_alloc)) {
+		del_timer(&sk->timer);
+		printk(KERN_INFO "wansock: Killing in Timer R %i , W %i\n",
+			atomic_read(&sk->rmem_alloc),atomic_read(&sk->wmem_alloc));
+		sk->timer.data=(unsigned long)sk;
+		sk->timer.expires=jiffies+HZ;
+		sk->timer.function=wanpipe_destroy_timer;
+		add_timer(&sk->timer);
+		return 0;
+	}
+
+	if (sk->protinfo.af_wanpipe){
+		kfree(sk->protinfo.af_wanpipe);
+		sk->protinfo.af_wanpipe=NULL;
+	}
+
+      #ifdef LINUX_2_4
+	if (atomic_read(&sk->refcnt) != 1){
+		DBG_PRINTK(KERN_INFO "wansock: Error, wrong reference count: %i !:release.\n",
+					atomic_read(&sk->refcnt));
+		atomic_set(&sk->refcnt,1);
+	}
+	sock_put(sk);
+      #else	
+	sk_free(sk);
+      #endif
+	atomic_dec(&wanpipe_socks_nr);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*============================================================
+ * check_write_queue
+ *
+ *  	During sock shutdown, if the sock state is 
+ *      WANSOCK_CONNECTED and there is transmit data 
+ *      pending. Wait until data is released 
+ *      before proceeding.
+ *===========================================================*/
+
+static void check_write_queue(struct sock *sk)
+{
+
+	if (sk->state != WANSOCK_CONNECTED)
+		return;
+
+	if (!atomic_read(&sk->wmem_alloc))
+		return;
+
+	printk(KERN_INFO "wansock: MAJOR ERROR, Data lost on sock release !!!\n");
+
+}
+
+/*============================================================
+ * release_driver
+ *
+ *	This function is called during sock shutdown, to 
+ *      release any resources and links that bind the sock
+ *      to the driver.  It also changes the state of the
+ *      sock to WANSOCK_DISCONNECTED
+ *===========================================================*/
+
+static void release_driver(struct sock *sk)
+{
+	struct sk_buff *skb=NULL;
+	struct sock *deadsk=NULL;
+
+	if (sk->state == WANSOCK_LISTEN || sk->state == WANSOCK_BIND_LISTEN){
+		while ((skb=skb_dequeue(&sk->receive_queue))!=NULL){
+			if ((deadsk = get_newsk_from_skb(skb))){
+				DBG_PRINTK (KERN_INFO "wansock: RELEASE: FOUND DEAD SOCK\n");
+				deadsk->dead=1;
+				start_cleanup_timer(deadsk);
+			}
+			kfree_skb(skb);
+		}
+		if (sk->zapped)
+			wanpipe_unlink_card(sk);
+	}else{
+		if (sk->zapped)
+			wanpipe_unlink_driver(sk);
+	}
+	sk->state = WANSOCK_DISCONNECTED;
+	sk->bound_dev_if = 0;
+	sk->zapped=0;
+
+	if (sk->protinfo.af_wanpipe){
+		if (sk->protinfo.af_wanpipe->mbox){
+			kfree(sk->protinfo.af_wanpipe->mbox);
+			sk->protinfo.af_wanpipe->mbox=NULL;
+		}
+	}
+}
+
+/*============================================================
+ *  start_cleanup_timer
+ *
+ *  	If new incoming call's are pending but the socket
+ *      is being released, start the timer which will 
+ *      envoke the kill routines for pending socks.
+ *===========================================================*/
+
+
+static void start_cleanup_timer (struct sock *sk)
+{
+	del_timer(&sk->timer);
+	sk->timer.data = (unsigned long)sk;
+	sk->timer.expires = jiffies + HZ;
+	sk->timer.function = wanpipe_kill_sock_timer;
+	add_timer(&sk->timer);
+}
+
+
+/*============================================================
+ *  wanpipe_kill_sock
+ *
+ *	This is a function which performs actual killing
+ *      of the sock.  It releases socket resources,
+ *      and unlinks the sock from the driver. 
+ *===========================================================*/
+
+static void wanpipe_kill_sock_timer (unsigned long data)
+{
+
+	struct sock *sk = (struct sock *)data;
+#ifndef LINUX_2_4
+	struct sk_buff *skb;
+#endif
+
+	struct sock **skp;
+
+	if (!sk)
+		return;
+
+	/* This functin can be called from interrupt. We must use
+	 * appropriate locks */
+	
+	if (test_bit(1,&wanpipe_tx_critical)){
+		sk->timer.expires=jiffies+10;
+		add_timer(&sk->timer);
+		return;
+	}
+	
+	write_lock(&wanpipe_sklist_lock);
+	for (skp = &wanpipe_sklist; *skp; skp = &(*skp)->next) {
+		if (*skp == sk) {
+			*skp = sk->next;
+			__sock_put(sk);
+			break;
+		}
+	}
+	write_unlock(&wanpipe_sklist_lock);
+
+
+	if (sk->num == htons(X25_PROT) && sk->state != WANSOCK_DISCONNECTED){
+		netdevice_t *dev = dev_get_by_index(sk->bound_dev_if);
+		wanpipe_common_t *chan;
+		if (dev){
+			chan=dev->priv;
+			atomic_set(&chan->disconnect,1);
+			dev_put(dev);
+		}	
+	}
+
+	release_driver(sk);
+
+	sk->socket = NULL;
+
+	/* Purge queues */
+#ifdef LINUX_2_4
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+	skb_queue_purge(&sk->error_queue);
+#else	
+	while ((skb=skb_dequeue(&sk->receive_queue)) != NULL){
+		kfree_skb(skb);
+	}
+	while ((skb=skb_dequeue(&sk->write_queue)) != NULL) {
+		kfree_skb(skb);
+	}
+	while ((skb=skb_dequeue(&sk->error_queue)) != NULL){
+		kfree_skb(skb);
+	}
+#endif
+	
+	if (atomic_read(&sk->rmem_alloc) || atomic_read(&sk->wmem_alloc)) {
+		del_timer(&sk->timer);
+		printk(KERN_INFO "wansock: Killing SOCK in Timer\n");
+		sk->timer.data=(unsigned long)sk;
+		sk->timer.expires=jiffies+HZ;
+		sk->timer.function=wanpipe_destroy_timer;
+		add_timer(&sk->timer);
+		return;
+	}
+
+	if (sk->protinfo.af_wanpipe){
+		kfree(sk->protinfo.af_wanpipe);
+		sk->protinfo.af_wanpipe=NULL;
+	}
+
+      #ifdef LINUX_2_4
+	if (atomic_read(&sk->refcnt) != 1){
+		atomic_set(&sk->refcnt,1);
+		DBG_PRINTK(KERN_INFO "wansock: Error, wrong reference count: %i ! :timer.\n",
+					atomic_read(&sk->refcnt));
+	}
+	sock_put(sk);
+      #else
+	sk_free(sk);
+      #endif
+	atomic_dec(&wanpipe_socks_nr);
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+static void wanpipe_kill_sock_accept (struct sock *sk)
+{
+
+	struct sock **skp;
+
+	if (!sk)
+		return;
+
+	/* This functin can be called from interrupt. We must use
+	 * appropriate locks */
+	
+	write_lock(&wanpipe_sklist_lock);
+	for (skp = &wanpipe_sklist; *skp; skp = &(*skp)->next) {
+		if (*skp == sk) {
+			*skp = sk->next;
+			__sock_put(sk);
+			break;
+		}
+	}
+	write_unlock(&wanpipe_sklist_lock);
+
+	sk->socket = NULL;
+
+
+	if (sk->protinfo.af_wanpipe){
+		kfree(sk->protinfo.af_wanpipe);
+		sk->protinfo.af_wanpipe=NULL;
+	}
+
+      #ifdef LINUX_2_4
+	if (atomic_read(&sk->refcnt) != 1){
+		atomic_set(&sk->refcnt,1);
+		DBG_PRINTK(KERN_INFO "wansock: Error, wrong reference count: %i ! :timer.\n",
+					atomic_read(&sk->refcnt));
+	}
+	sock_put(sk);
+      #else
+	sk_free(sk);
+      #endif
+	atomic_dec(&wanpipe_socks_nr);
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+
+static void wanpipe_kill_sock_irq (struct sock *sk)
+{
+
+	if (!sk)
+		return;
+
+	sk->socket = NULL;
+
+	if (sk->protinfo.af_wanpipe){
+		kfree(sk->protinfo.af_wanpipe);
+		sk->protinfo.af_wanpipe=NULL;
+	}
+
+      #ifdef LINUX_2_4
+	if (atomic_read(&sk->refcnt) != 1){
+		atomic_set(&sk->refcnt,1);
+		DBG_PRINTK(KERN_INFO "wansock: Error, wrong reference count: %i !:listen.\n",
+					atomic_read(&sk->refcnt));
+	}
+	sock_put(sk);
+      #else
+	sk_free(sk);
+      #endif
+	atomic_dec(&wanpipe_socks_nr);
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+
+/*============================================================
+ *  wanpipe_do_bind
+ *
+ * 	Bottom half of the binding system call.
+ *      Once the wanpipe_bind() function checks  the
+ *      legality of the call, this function binds the
+ *      sock to the driver.
+ *===========================================================*/
+
+static int wanpipe_do_bind(struct sock *sk, netdevice_t *dev, int protocol)
+{
+	wanpipe_common_t *chan=NULL;
+	int err=0;
+
+	if (sk->zapped){
+		err = -EALREADY;
+		goto bind_unlock_exit;
+	}
+
+	sk->num = protocol;
+
+	if (protocol == 0){
+		release_device(dev);
+		err = -EINVAL;
+		goto bind_unlock_exit;
+	}
+
+	if (dev) {
+		if (dev->flags&IFF_UP) {
+			chan=dev->priv;
+			sk->state = chan->state;
+
+			if (sk->num == htons(X25_PROT) && 
+			    sk->state != WANSOCK_DISCONNECTED && 
+			    sk->state != WANSOCK_CONNECTING){
+				DBG_PRINTK(KERN_INFO 
+					"wansock: Binding to Device not DISCONNECTED %i\n",
+						sk->state);
+				release_device(dev);
+				err = -EAGAIN;
+				goto bind_unlock_exit;
+			}
+
+			wanpipe_link_driver(dev,sk);
+			sk->bound_dev_if = dev->ifindex;
+
+			/* X25 Specific option */
+			if (sk->num == htons(X25_PROT))
+				sk->protinfo.af_wanpipe->svc = chan->svc;
+
+		} else {
+			sk->err = ENETDOWN;
+			sk->error_report(sk);
+			release_device(dev);
+			err = -EINVAL;
+		}
+	} else {
+		err = -ENODEV;
+	}
+bind_unlock_exit:
+	/* FIXME where is this lock */
+
+	return err;
+}
+
+/*============================================================
+ *  wanpipe_bind
+ *
+ *      BIND() System call, which is bound to the AF_WANPIPE
+ *      operations structure.  It checks for correct wanpipe
+ *      card name, and cross references interface names with
+ *      the card names.  Thus, interface name must belong to
+ *      the actual card.
+ *===========================================================*/
+
+
+static int wanpipe_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct wan_sockaddr_ll *sll = (struct wan_sockaddr_ll*)uaddr;
+	struct sock *sk=sock->sk;
+	netdevice_t *dev = NULL;
+	sdla_t *card=NULL;
+	char name[15];
+
+	/*
+	 *	Check legality
+	 */
+	 
+	if (addr_len < sizeof(struct wan_sockaddr_ll)){
+		printk(KERN_INFO "wansock: Address length error\n");
+		return -EINVAL;
+	}
+	if (sll->sll_family != AF_WANPIPE){
+		printk(KERN_INFO "wansock: Illegal family name specified.\n");
+		return -EINVAL;
+	}
+
+	card = wanpipe_find_card (sll->sll_card);
+	if (!card){
+		printk(KERN_INFO "wansock: Wanpipe card not found: %s\n",sll->sll_card);
+		return -ENODEV;
+	}else{
+		sk->protinfo.af_wanpipe->card = (void *)card;
+	}
+
+	if (!strcmp(sll->sll_device,"svc_listen")){
+
+		/* Bind a sock to a card structure for listening 
+		 */		
+		int err=0; 
+
+		/* This is x25 specific area if protocol doesn't
+                 * match, return error */
+		if (sll->sll_protocol != htons(X25_PROT))
+			return -EINVAL;
+
+		err= wanpipe_link_card (sk);
+		if (err < 0)
+			return err;
+
+		if (sll->sll_protocol)
+			sk->num = sll->sll_protocol;
+		sk->state = WANSOCK_BIND_LISTEN;
+		return 0;
+
+	}else if (!strcmp(sll->sll_device,"svc_connect")){ 
+
+		/* This is x25 specific area if protocol doesn't
+                 * match, return error */
+		if (sll->sll_protocol != htons(X25_PROT))
+			return -EINVAL;
+
+		/* Find a free device 
+		 */
+		dev = wanpipe_find_free_dev(card);
+		if (dev == NULL){
+			DBG_PRINTK(KERN_INFO "wansock: No free network devices for card %s\n",
+				card->devname);
+			return -EINVAL;
+		}
+	}else{
+		/* Bind a socket to a interface name 
+                 * This is used by PVC mostly
+                 */
+		strncpy(name,sll->sll_device,14);
+		name[14]=0;
+#ifdef LINUX_2_4
+		dev = dev_get_by_name(name);
+#else
+		dev = dev_get(name);
+#endif
+		if (dev == NULL){
+			printk(KERN_INFO "wansock: Failed to get Dev from name: %s,\n",
+					name);
+			return -ENODEV;
+		}
+
+		dev_put(dev);
+
+		if (check_dev(dev, card)){
+			printk(KERN_INFO "wansock: Device %s, doesn't belong to card %s\n",
+				dev->name, card->devname);
+			return -EINVAL;
+		}
+		if (get_atomic_device (dev))
+			return -EINVAL;
+	}
+
+	return wanpipe_do_bind(sk, dev, sll->sll_protocol ? : sk->num);
+}
+
+/*============================================================
+ * get_atomic_device
+ *	
+ *	Sets a bit atomically which indicates that 
+ *      the interface is taken. This avoids race conditions.
+ *===========================================================*/
+
+
+static inline int get_atomic_device (netdevice_t *dev)
+{
+	wanpipe_common_t *chan = dev->priv;
+	if (!test_and_set_bit(0,(void *)&chan->rw_bind)){
+		return 0;
+	}
+	return 1;
+}
+
+/*============================================================
+ * check_dev
+ *	
+ *  	Check that device name belongs to a particular card.
+ *===========================================================*/
+
+static int check_dev (netdevice_t *dev, sdla_t *card)
+{
+	netdevice_t* tmp_dev;
+
+	for (tmp_dev = card->wandev.dev; tmp_dev; tmp_dev=*((netdevice_t**)tmp_dev->priv)){
+		if (tmp_dev->ifindex == dev->ifindex){ 
+			return 0;	
+		}
+	}
+	return 1;
+}
+
+/*============================================================
+ *  wanpipe_find_free_dev
+ *	
+ *	Find a free network interface. If found set atomic
+ *      bit indicating that the interface is taken.
+ *      X25API Specific.
+ *===========================================================*/
+
+netdevice_t * wanpipe_find_free_dev (sdla_t *card)
+{
+	netdevice_t* dev;
+	volatile wanpipe_common_t *chan;
+
+	if (test_and_set_bit(0,&find_free_critical)){
+		printk(KERN_INFO "CRITICAL in Find Free\n");
+	}	
+
+	for (dev = card->wandev.dev; dev; dev=*((netdevice_t**)dev->priv)){
+		chan = dev->priv;
+		if (!chan) 
+			continue;
+		if (chan->usedby == API && chan->svc){
+			if (!get_atomic_device (dev)){
+				if (chan->state != WANSOCK_DISCONNECTED){
+					release_device(dev);
+				}else{
+					clear_bit(0,&find_free_critical);
+					return dev;
+				}
+			}
+		}
+	}
+	clear_bit(0,&find_free_critical);
+	return NULL;
+}
+
+/*============================================================
+ *  wanpipe_create
+ *	
+ * 	SOCKET() System call.  It allocates a sock structure
+ *      and adds the socket to the wanpipe_sk_list. 
+ *      Crates AF_WANPIPE socket.
+ *===========================================================*/
+
+static int wanpipe_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	
+	//FIXME: This checks for root user, SECURITY ?
+	//if (!capable(CAP_NET_RAW))
+	//	return -EPERM;
+
+	if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->state = SS_UNCONNECTED;
+
+	if ((sk = wanpipe_alloc_socket()) == NULL)
+		return -ENOBUFS;
+
+	sk->reuse = 1;
+	sock->ops = &wanpipe_ops;
+	sock_init_data(sock,sk);
+
+	sk->zapped=0;
+	sk->family = PF_WANPIPE;
+	sk->num = protocol;
+	sk->state = WANSOCK_DISCONNECTED;
+	sk->ack_backlog = 0;
+	sk->bound_dev_if=0;
+
+	atomic_inc(&wanpipe_socks_nr);
+	
+	/* We must disable interrupts because the ISR
+	 * can also change the list */
+	set_bit(1,&wanpipe_tx_critical);
+	write_lock(&wanpipe_sklist_lock);
+	sk->next = wanpipe_sklist;
+	wanpipe_sklist = sk;
+	sock_hold(sk);
+	write_unlock(&wanpipe_sklist_lock);
+	clear_bit(1,&wanpipe_tx_critical);
+
+	return(0);
+}
+
+
+/*============================================================
+ *  wanpipe_recvmsg
+ *	
+ *	Pull a packet from our receive queue and hand it 
+ *      to the user. If necessary we block.
+ *===========================================================*/
+
+static int wanpipe_recvmsg(struct socket *sock, struct msghdr *msg, int len,
+			  int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int copied, err=-ENOBUFS;
+
+
+	/*
+	 *	If the address length field is there to be filled in, we fill
+	 *	it in now.
+	 */
+
+	msg->msg_namelen = sizeof(struct wan_sockaddr_ll);
+
+	/*
+	 *	Call the generic datagram receiver. This handles all sorts
+	 *	of horrible races and re-entrancy so we can forget about it
+	 *	in the protocol layers.
+	 *
+	 *	Now it will return ENETDOWN, if device have just gone down,
+	 *	but then it will block.
+	 */
+
+	if (flags & MSG_OOB){	
+		skb=skb_dequeue(&sk->error_queue);
+	}else{
+		skb=skb_recv_datagram(sk,flags,1,&err);
+	}
+	/*
+	 *	An error occurred so return it. Because skb_recv_datagram() 
+	 *	handles the blocking we don't see and worry about blocking
+	 *	retries.
+	 */
+
+	if(skb==NULL)
+		goto out;
+
+	/*
+	 *	You lose any data beyond the buffer you gave. If it worries a
+	 *	user program they can ask the device for its MTU anyway.
+	 */
+
+	copied = skb->len;
+	if (copied > len)
+	{
+		copied=len;
+		msg->msg_flags|=MSG_TRUNC;
+	}
+
+	wanpipe_wakeup_driver(sk);
+
+	/* We can't use skb_copy_datagram here */
+	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
+	if (err)
+		goto out_free;
+	
+#ifdef LINUX_2_1
+	sk->stamp=skb->stamp;
+#else
+	sock_recv_timestamp(msg, sk, skb);
+#endif
+	
+	if (msg->msg_name)
+		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+
+	/*
+	 *	Free or return the buffer as appropriate. Again this
+	 *	hides all the races and re-entrancy issues from us.
+	 */
+	err = (flags&MSG_TRUNC) ? skb->len : copied;
+
+out_free:
+	skb_free_datagram(sk, skb);
+out:
+	return err;
+}
+
+
+/*============================================================
+ *  wanpipe_wakeup_driver
+ *	
+ * 	If socket receive buffer is full and driver cannot
+ *      pass data up the sock, it sets a packet_block flag.
+ *      This function check that flag and if sock receive 
+ *      queue has room it kicks the driver BH handler. 
+ *
+ * 	This way, driver doesn't have to poll the sock 
+ *      receive queue.
+ *===========================================================*/
+
+static void wanpipe_wakeup_driver(struct sock *sk)
+{
+	netdevice_t *dev=NULL;
+	wanpipe_common_t *chan=NULL;
+
+	dev = dev_get_by_index(sk->bound_dev_if);
+	if (!dev)
+		return;
+
+	dev_put(dev);
+
+	if ((chan = dev->priv) == NULL)
+		return;
+	
+	if (atomic_read(&chan->receive_block)){  
+		if (atomic_read(&sk->rmem_alloc) < ((unsigned)sk->rcvbuf*0.9) ){
+			printk(KERN_INFO "wansock: Queuing task for wanpipe\n");
+			atomic_set(&chan->receive_block,0);
+			wanpipe_queue_tq(&chan->wanpipe_task);
+			wanpipe_mark_bh();
+		}
+	}	
+}	
+
+/*============================================================
+ *  wanpipe_getname
+ *	
+ * 	I don't know what to do with this yet. 
+ *      User can use this function to get sock address
+ *      information. Not very useful for Sangoma's purposes.
+ *===========================================================*/
+
+
+static int wanpipe_getname(struct socket *sock, struct sockaddr *uaddr,
+			  int *uaddr_len, int peer)
+{
+	netdevice_t *dev;
+	struct sock *sk = sock->sk;
+	struct wan_sockaddr_ll *sll = (struct wan_sockaddr_ll*)uaddr;
+
+	sll->sll_family = AF_WANPIPE;
+	sll->sll_ifindex = sk->bound_dev_if;
+	sll->sll_protocol = sk->num;
+	dev = dev_get_by_index(sk->bound_dev_if);
+	if (dev) {
+		sll->sll_hatype = dev->type;
+		sll->sll_halen = dev->addr_len;
+		memcpy(sll->sll_addr, dev->dev_addr, dev->addr_len);
+	} else {
+		sll->sll_hatype = 0;	/* Bad: we have no ARPHRD_UNSPEC */
+		sll->sll_halen = 0;
+	}
+	*uaddr_len = sizeof(*sll);
+	
+	dev_put(dev);
+	
+	return 0;
+}
+
+/*============================================================
+ *  wanpipe_notifier
+ *	
+ *	If driver turns off network interface, this function
+ *      will be envoked. Currently I treate it as a 
+ *      call disconnect. More thought should go into this
+ *      function.
+ *
+ * FIXME: More thought should go into this function.
+ *
+ *===========================================================*/
+
+static int wanpipe_notifier(struct notifier_block *this, unsigned long msg, void *data)
+{
+	struct sock *sk;
+	netdevice_t *dev = (netdevice_t*)data;
+	struct wanpipe_opt *po;
+
+	for (sk = wanpipe_sklist; sk; sk = sk->next) {
+
+		if ((po = sk->protinfo.af_wanpipe)==NULL)
+			continue;
+		if (dev == NULL)
+			continue;
+		
+		switch (msg) {
+		case NETDEV_DOWN:
+		case NETDEV_UNREGISTER:
+			if (dev->ifindex == sk->bound_dev_if) {
+				printk(KERN_INFO "wansock: Device down %s\n",dev->name);
+				if (sk->zapped){
+					wanpipe_unlink_driver(sk);
+					sk->err = ENETDOWN;
+					sk->error_report(sk);
+				}
+
+				if (msg == NETDEV_UNREGISTER) {
+					printk(KERN_INFO "wansock: Unregistering Device: %s\n",
+						 	  dev->name);
+					wanpipe_unlink_driver(sk);
+					sk->bound_dev_if = 0;
+				}
+			}
+			break;
+		case NETDEV_UP:
+			if (dev->ifindex == sk->bound_dev_if && sk->num && !sk->zapped) {
+				printk(KERN_INFO "wansock: Registering Device: %s\n",
+						dev->name);
+				wanpipe_link_driver(dev,sk);
+			}
+			break;
+		}
+	}
+	return NOTIFY_DONE;
+}
+
+/*============================================================
+ *  wanpipe_ioctl
+ *	
+ * 	Execute a user commands, and set socket options.
+ *
+ * FIXME: More thought should go into this function.
+ *
+ *===========================================================*/
+
+static int wanpipe_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err;
+	int pid;
+
+	switch(cmd) 
+	{
+		case FIOSETOWN:
+		case SIOCSPGRP:
+			err = get_user(pid, (int *) arg);
+			if (err)
+				return err; 
+			if (current->pid != pid && current->pgrp != -pid && 
+			    !capable(CAP_NET_ADMIN))
+				return -EPERM;
+			sk->proc = pid;
+			return(0);
+		case FIOGETOWN:
+		case SIOCGPGRP:
+			return put_user(sk->proc, (int *)arg);
+		case SIOCGSTAMP:
+			if(sk->stamp.tv_sec==0)
+				return -ENOENT;
+			err = -EFAULT;
+			if (!copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+				err = 0;
+			return err;
+
+		case SIOC_WANPIPE_CHECK_TX:
+
+			return atomic_read(&sk->wmem_alloc);
+
+		case SIOC_WANPIPE_SOCK_STATE:
+
+			if (sk->state == WANSOCK_CONNECTED)
+				return 0;
+			
+			return 1;
+
+
+		case SIOC_WANPIPE_GET_CALL_DATA:
+
+			return get_ioctl_cmd (sk,(void*)arg);
+
+		case SIOC_WANPIPE_SET_CALL_DATA:
+
+			return set_ioctl_cmd (sk,(void*)arg);
+
+		case SIOC_WANPIPE_ACCEPT_CALL:
+		case SIOC_WANPIPE_CLEAR_CALL:
+		case SIOC_WANPIPE_RESET_CALL:
+
+			if ((err=set_ioctl_cmd(sk,(void*)arg)) < 0)
+				return err;
+
+			err=wanpipe_exec_cmd(sk,cmd,0);
+			get_ioctl_cmd(sk,(void*)arg);
+			return err;
+
+		case SIOC_WANPIPE_DEBUG:
+
+			return wanpipe_debug(sk,(void*)arg);
+	
+		case SIOC_WANPIPE_SET_NONBLOCK:
+
+			if (sk->state != WANSOCK_DISCONNECTED)
+				return -EINVAL;
+
+			sock->file->f_flags |= O_NONBLOCK;
+			return 0;
+	
+		case SIOCGIFFLAGS:
+#ifndef CONFIG_INET
+		case SIOCSIFFLAGS:
+#endif
+		case SIOCGIFCONF:
+		case SIOCGIFMETRIC:
+		case SIOCSIFMETRIC:
+		case SIOCGIFMEM:
+		case SIOCSIFMEM:
+		case SIOCGIFMTU:
+		case SIOCSIFMTU:
+		case SIOCSIFLINK:
+		case SIOCGIFHWADDR:
+		case SIOCSIFHWADDR:
+		case SIOCSIFMAP:
+		case SIOCGIFMAP:
+		case SIOCSIFSLAVE:
+		case SIOCGIFSLAVE:
+		case SIOCGIFINDEX:
+		case SIOCGIFNAME:
+		case SIOCGIFCOUNT:
+		case SIOCSIFHWBROADCAST:
+			return(dev_ioctl(cmd,(void *) arg));
+
+#ifdef CONFIG_INET
+		case SIOCADDRT:
+		case SIOCDELRT:
+		case SIOCDARP:
+		case SIOCGARP:
+		case SIOCSARP:
+		case SIOCDRARP:
+		case SIOCGRARP:
+		case SIOCSRARP:
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFBRDADDR:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+		case SIOCSIFFLAGS:
+		case SIOCADDDLCI:
+		case SIOCDELDLCI:
+			return inet_dgram_ops.ioctl(sock, cmd, arg);
+#endif
+
+		default:
+			if ((cmd >= SIOCDEVPRIVATE) &&
+			    (cmd <= (SIOCDEVPRIVATE + 15)))
+				return(dev_ioctl(cmd,(void *) arg));
+
+#ifdef CONFIG_NET_RADIO
+			if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
+				return(dev_ioctl(cmd,(void *) arg));
+#endif
+			return -EOPNOTSUPP;
+	}
+	/*NOTREACHED*/
+}
+
+/*============================================================
+ *  wanpipe_debug
+ *	
+ *	This function will pass up information about all
+ *      active sockets.
+ *
+ * FIXME: More thought should go into this function.
+ *
+ *===========================================================*/
+
+static int wanpipe_debug (struct sock *origsk, void *arg)
+{
+	struct sock *sk=NULL;
+	netdevice_t *dev=NULL;
+	wanpipe_common_t *chan=NULL;
+	int cnt=0, err=0;
+	wan_debug_t *dbg_data = (wan_debug_t *)arg;
+
+	for (sk = wanpipe_sklist; sk; sk = sk->next){
+
+		if (sk == origsk){
+			continue;
+		}
+
+		if ((err=put_user(1, &dbg_data->debug[cnt].free)))
+			return err;
+		if ((err=put_user(sk->state, &dbg_data->debug[cnt].sk_state)))
+			return err;
+		if ((err=put_user(sk->rcvbuf, &dbg_data->debug[cnt].rcvbuf)))
+			return err;
+		if ((err=put_user(atomic_read(&sk->rmem_alloc), &dbg_data->debug[cnt].rmem)))
+			return err;
+		if ((err=put_user(atomic_read(&sk->wmem_alloc), &dbg_data->debug[cnt].wmem)))
+			return err;
+		if ((err=put_user(sk->sndbuf, &dbg_data->debug[cnt].sndbuf)))
+			return err;
+		if ((err=put_user(sk_count, &dbg_data->debug[cnt].sk_count)))
+			return err;
+		if ((err=put_user(sk->protinfo.af_wanpipe->poll_cnt, 
+						&dbg_data->debug[cnt].poll_cnt)))
+			return err;
+		if ((err=put_user(sk->bound_dev_if, &dbg_data->debug[cnt].bound)))
+			return err;
+
+		if (sk->bound_dev_if){
+			dev = dev_get_by_index(sk->bound_dev_if);
+			if (!dev)	
+				continue;
+
+			chan=dev->priv;
+			dev_put(dev);
+	
+			if ((err=put_user(chan->state, &dbg_data->debug[cnt].d_state)))
+				return err;
+			if ((err=put_user(chan->svc, &dbg_data->debug[cnt].svc)))
+				return err;
+
+			if ((err=put_user(atomic_read(&chan->command), 
+						&dbg_data->debug[cnt].command)))
+				return err;
+
+
+			if (sk->protinfo.af_wanpipe){
+				sdla_t *card = (sdla_t*)sk->protinfo.af_wanpipe->card;			
+	
+				if (card){
+					if ((err=put_user(atomic_read(&card->u.x.command_busy), 
+								&dbg_data->debug[cnt].cmd_busy)))
+						return err;
+				}
+
+				if ((err=put_user(sk->protinfo.af_wanpipe->lcn, 
+								&dbg_data->debug[cnt].lcn)))
+					return err;
+				
+				if (sk->protinfo.af_wanpipe->mbox){
+					if ((err=put_user(1, &dbg_data->debug[cnt].mbox)))
+						return err;
+				}
+			}
+
+			if ((err=put_user(atomic_read(&chan->receive_block), 
+								&dbg_data->debug[cnt].rblock)))
+				return err;
+
+			if (copy_to_user(dbg_data->debug[cnt].name, dev->name, strlen(dev->name)))
+				return -EFAULT;
+		}
+	
+		if (++cnt == MAX_NUM_DEBUG)
+			break;
+	}
+	return 0;
+}
+
+/*============================================================
+ *  get_ioctl_cmd
+ *	
+ *	Pass up the contents of socket MBOX to the user.
+ *===========================================================*/
+
+static int get_ioctl_cmd (struct sock *sk, void *arg)
+{
+	x25api_t *usr_data = (x25api_t *)arg;
+	mbox_cmd_t *mbox_ptr;
+	int err;
+
+	if (usr_data == NULL)
+		return -EINVAL;
+
+	if (!sk->protinfo.af_wanpipe->mbox){
+		return -EINVAL;
+	}
+
+	mbox_ptr = (mbox_cmd_t *)sk->protinfo.af_wanpipe->mbox;
+
+	if ((err=put_user(mbox_ptr->cmd.qdm, &usr_data->hdr.qdm)))
+		return err;
+	if ((err=put_user(mbox_ptr->cmd.cause, &usr_data->hdr.cause)))
+		return err;
+	if ((err=put_user(mbox_ptr->cmd.diagn, &usr_data->hdr.diagn)))
+		return err;
+	if ((err=put_user(mbox_ptr->cmd.length, &usr_data->hdr.length)))
+		return err;
+	if ((err=put_user(mbox_ptr->cmd.result, &usr_data->hdr.result)))
+		return err;
+	if ((err=put_user(mbox_ptr->cmd.lcn, &usr_data->hdr.lcn)))
+		return err;	
+
+	if (mbox_ptr->cmd.length > 0){
+		if (mbox_ptr->cmd.length > X25_MAX_DATA)
+			return -EINVAL;
+
+		if (copy_to_user(usr_data->data, mbox_ptr->data, mbox_ptr->cmd.length)){
+			printk(KERN_INFO "wansock: Copy failed !!!\n");
+			return -EFAULT;
+		}
+	}
+	return 0;
+} 
+
+/*============================================================
+ *  set_ioctl_cmd
+ *	
+ *	Before command can be execute, socket MBOX must
+ *      be created, and initialized with user data.	
+ *===========================================================*/
+
+static int set_ioctl_cmd (struct sock *sk, void *arg)
+{
+	x25api_t *usr_data = (x25api_t *)arg;
+	mbox_cmd_t *mbox_ptr;
+	int err;
+
+	if (!sk->protinfo.af_wanpipe->mbox){
+		void *mbox_ptr;
+		netdevice_t *dev = dev_get_by_index(sk->bound_dev_if);
+		if (!dev)
+			return -ENODEV;
+
+		dev_put(dev);
+		
+		if ((mbox_ptr = kmalloc(sizeof(mbox_cmd_t), GFP_ATOMIC)) == NULL)
+			return -ENOMEM;
+
+		memset(mbox_ptr, 0, sizeof(mbox_cmd_t));
+		sk->protinfo.af_wanpipe->mbox = mbox_ptr;
+
+		wanpipe_link_driver(dev,sk);
+	}
+
+	mbox_ptr = (mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox;
+	memset(mbox_ptr, 0, sizeof(mbox_cmd_t));
+
+	if (usr_data == NULL){
+		return 0;
+	}
+	if ((err=get_user(mbox_ptr->cmd.qdm, &usr_data->hdr.qdm)))
+		return err;
+	if ((err=get_user(mbox_ptr->cmd.cause, &usr_data->hdr.cause)))
+		return err;
+	if ((err=get_user(mbox_ptr->cmd.diagn, &usr_data->hdr.diagn)))
+		return err;
+	if ((err=get_user(mbox_ptr->cmd.length, &usr_data->hdr.length)))
+		return err;
+	if ((err=get_user(mbox_ptr->cmd.result, &usr_data->hdr.result)))
+		return err;
+
+	if (mbox_ptr->cmd.length > 0){
+		if (mbox_ptr->cmd.length > X25_MAX_DATA)
+			return -EINVAL;
+
+		if (copy_from_user(mbox_ptr->data, usr_data->data, mbox_ptr->cmd.length)){
+			printk(KERN_INFO "Copy failed\n");
+			return -EFAULT;
+		}
+	}
+	return 0;
+}
+
+
+/*======================================================================
+ * wanpipe_poll
+ *
+ *	Datagram poll: Again totally generic. This also handles
+ *	sequenced packet sockets providing the socket receive queue
+ *	is only ever holding data ready to receive.
+ *
+ *	Note: when you _don't_ use this routine for this protocol,
+ *	and you use a different write policy from sock_writeable()
+ *	then please supply your own write_space callback.
+ *=====================================================================*/
+
+unsigned int wanpipe_poll(struct file * file, struct socket *sock, poll_table *wait)
+{
+	struct sock *sk = sock->sk;
+	unsigned int mask;
+
+	++sk->protinfo.af_wanpipe->poll_cnt;
+
+	poll_wait(file, sk->sleep, wait);
+	mask = 0;
+
+	/* exceptional events? */
+	if (sk->err || !skb_queue_empty(&sk->error_queue)){
+		mask |= POLLPRI;
+		return mask;
+	}
+	if (sk->shutdown & RCV_SHUTDOWN)
+		mask |= POLLHUP;
+
+	/* readable? */
+	if (!skb_queue_empty(&sk->receive_queue)){
+		mask |= POLLIN | POLLRDNORM;
+	}
+
+	/* connection hasn't started yet */
+	if (sk->state == WANSOCK_CONNECTING){
+		return mask;
+	}
+
+	if (sk->state == WANSOCK_DISCONNECTED){
+		mask = POLLPRI;
+		return mask;
+	}
+
+	/* This check blocks the user process if there is   
+	 * a packet already queued in the socket write queue.
+         * This option is only for X25API protocol, for other
+         * protocol like chdlc enable streaming mode, 
+         * where multiple packets can be pending in the socket 
+         * transmit queue */
+
+	if (sk->num == htons(X25_PROT)){
+		if (atomic_read(&sk->protinfo.af_wanpipe->packet_sent))
+			return mask;
+	}
+
+	/* writable? */
+	if (sock_writeable(sk)){
+		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+	}else{
+	      #ifdef LINUX_2_4
+		set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+	      #else
+ 		sk->socket->flags |= SO_NOSPACE;
+	      #endif
+	}
+		
+	return mask;
+}
+
+/*======================================================================
+ * wanpipe_listen
+ *
+ *	X25API Specific function. Set a socket into LISTENING  MODE.
+ *=====================================================================*/
+
+
+static int wanpipe_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+
+ 	/* This is x25 specific area if protocol doesn't
+         * match, return error */
+	if (sk->num != htons(X25_PROT))
+		return -EINVAL;
+
+	if (sk->state == WANSOCK_BIND_LISTEN) {
+
+		sk->max_ack_backlog = backlog;
+		sk->state           = WANSOCK_LISTEN;
+		return 0;
+	}else{
+		printk(KERN_INFO "wansock: Listening sock was not binded\n");
+	}
+
+	return -EINVAL;
+}
+
+/*======================================================================
+ * wanpipe_link_card
+ *
+ *	Connects the listening socket to the driver
+ *=====================================================================*/
+
+static int wanpipe_link_card (struct sock *sk)
+{
+	sdla_t *card;
+
+	card = (sdla_t*)sk->protinfo.af_wanpipe->card;
+	if (!card)
+		return -ENOMEM;
+
+	if ((card->sk != NULL) || (card->func != NULL)){
+		printk(KERN_INFO "wansock: Listening queue is already established\n");
+		return -EINVAL;
+	}
+
+	card->sk=sk;
+	card->func=wanpipe_listen_rcv;
+	sk->zapped=1;
+ 
+	return 0;
+}
+
+/*======================================================================
+ * wanpipe_listen
+ *
+ *	X25API Specific function. Disconnect listening socket from
+ *      the driver.
+ *=====================================================================*/
+
+static void wanpipe_unlink_card (struct sock *sk)
+{
+	sdla_t *card; 
+
+	card = (sdla_t*)sk->protinfo.af_wanpipe->card; 
+
+	if (card){
+		card->sk=NULL;
+		card->func=NULL;
+	}
+}
+
+/*======================================================================
+ * wanpipe_exec_cmd
+ *
+ *	Ioctl function calls this function to execute user command.
+ *      Connect() sytem call also calls this function to execute
+ *      place call.  This function blocks until command is executed.
+ *=====================================================================*/
+
+static int wanpipe_exec_cmd(struct sock *sk, int cmd, unsigned int flags)
+{
+	int err = -EINVAL;
+	mbox_cmd_t *mbox_ptr = (mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox;
+
+	if (!mbox_ptr){
+		printk(KERN_INFO "NO MBOX PTR !!!!!\n");
+		return -EINVAL;
+	}
+	
+	/* This is x25 specific area if protocol doesn't
+         * match, return error */
+	if (sk->num != htons(X25_PROT))
+		return -EINVAL;
+
+
+	switch (cmd){
+
+		case SIOC_WANPIPE_ACCEPT_CALL:
+
+			if (sk->state != WANSOCK_CONNECTING){
+				err = -EHOSTDOWN;
+				break;
+			}
+			
+			err = execute_command(sk,X25_ACCEPT_CALL,0);
+			if (err < 0)
+				break;
+
+			/* Update. Mar6 2000. 
+                         * Do not set the sock lcn number here, since
+                         * it is done in wanpipe_listen_rcv(). 
+                         */ 
+		 	if (sk->state == WANSOCK_CONNECTED){
+				sk->protinfo.af_wanpipe->lcn =
+				((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn;	
+				DBG_PRINTK(KERN_INFO "\nwansock: Accept OK %i\n",
+					sk->protinfo.af_wanpipe->lcn );
+				err = 0;
+
+			}else{
+				DBG_PRINTK (KERN_INFO "\nwansock: Accept Failed %i\n",
+					sk->protinfo.af_wanpipe->lcn);
+				sk->protinfo.af_wanpipe->lcn = 0;
+				err = -ECONNREFUSED;
+			}
+			break;
+
+		case SIOC_WANPIPE_CLEAR_CALL:
+
+			if (sk->state == WANSOCK_DISCONNECTED){
+				err = -EINVAL;
+				break;
+			}
+
+
+			/* Check if data buffers are pending for transmission,
+                         * if so, check wheter user wants to wait untill data
+                         * is transmitted, or clear a call and drop packets */
+                          
+			if (atomic_read(&sk->wmem_alloc) || check_driver_busy(sk)){
+			  	mbox_cmd_t *mbox = sk->protinfo.af_wanpipe->mbox;
+				if (mbox->cmd.qdm & 0x80){
+					mbox->cmd.result = 0x35;
+					err = -EAGAIN;	
+					break;
+				}
+			}
+
+			sk->state = WANSOCK_DISCONNECTING;
+
+			err = execute_command(sk,X25_CLEAR_CALL,0);
+			if (err < 0)
+				break;
+
+			err = -ECONNREFUSED;
+			if (sk->state == WANSOCK_DISCONNECTED){
+				DBG_PRINTK(KERN_INFO "\nwansock: CLEAR OK %i\n",
+					sk->protinfo.af_wanpipe->lcn);
+				sk->protinfo.af_wanpipe->lcn=0;
+				err = 0;
+			}
+			break;
+
+		case SIOC_WANPIPE_RESET_CALL:
+
+			if (sk->state != WANSOCK_CONNECTED){
+				err = -EINVAL;
+				break;
+			}
+
+
+			/* Check if data buffers are pending for transmission,
+                         * if so, check wheter user wants to wait untill data
+                         * is transmitted, or reset a call and drop packets */
+                          
+			if (atomic_read(&sk->wmem_alloc) || check_driver_busy(sk)){
+			  	mbox_cmd_t *mbox = sk->protinfo.af_wanpipe->mbox;
+				if (mbox->cmd.qdm & 0x80){
+					mbox->cmd.result = 0x35;
+					err = -EAGAIN;	
+					break;
+				}
+			}
+
+
+			err = execute_command(sk, X25_RESET,0);
+			if (err < 0)
+				break;
+
+			err = mbox_ptr->cmd.result;
+			break;
+
+
+		case X25_PLACE_CALL:
+
+			err=execute_command(sk,X25_PLACE_CALL,flags);
+			if (err < 0)
+				break;
+
+			if (sk->state == WANSOCK_CONNECTED){
+
+				sk->protinfo.af_wanpipe->lcn =
+				((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn;	
+
+				DBG_PRINTK(KERN_INFO "\nwansock: PLACE CALL OK %i\n",
+					sk->protinfo.af_wanpipe->lcn);
+				err = 0;
+
+			}else if (sk->state == WANSOCK_CONNECTING && (flags & O_NONBLOCK)){
+				sk->protinfo.af_wanpipe->lcn = 
+				((mbox_cmd_t*)sk->protinfo.af_wanpipe->mbox)->cmd.lcn;
+				DBG_PRINTK(KERN_INFO "\nwansock: Place Call OK: Waiting %i\n",
+					sk->protinfo.af_wanpipe->lcn);
+
+				err = 0;
+
+			}else{
+				DBG_PRINTK(KERN_INFO "\nwansock: Place call Failed\n");
+				err = -ECONNREFUSED;
+			}
+
+			break;
+
+		default: 
+			return -EINVAL;
+	}
+
+	return err;
+}
+
+static int check_driver_busy (struct sock *sk)
+{
+	netdevice_t *dev = dev_get_by_index(sk->bound_dev_if);
+	wanpipe_common_t *chan;
+
+	if (!dev)
+		return 0;
+
+	dev_put(dev);
+
+	if ((chan=dev->priv) == NULL)
+		return 0;
+
+	return atomic_read(&chan->driver_busy);
+}
+
+
+/*======================================================================
+ * wanpipe_accept
+ *
+ *	ACCEPT() System call.	X25API Specific function. 
+ *	For each incoming call, create a new socket and 
+ *      return it to the user.	
+ *=====================================================================*/
+
+static int wanpipe_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct sock *sk;
+	struct sock *newsk;
+	struct sk_buff *skb;
+	DECLARE_WAITQUEUE(wait, current);
+	int err=0;
+
+	if (newsock->sk != NULL){
+		wanpipe_kill_sock_accept(newsock->sk);	
+		newsock->sk=NULL;
+	}
+	
+	if ((sk = sock->sk) == NULL)
+		return -EINVAL;
+
+	if (sk->type != SOCK_RAW)
+		return -EOPNOTSUPP;
+
+	if (sk->state != WANSOCK_LISTEN)
+		return -EINVAL;
+
+	if (sk->num != htons(X25_PROT))
+		return -EINVAL;
+
+	add_wait_queue(sk->sleep,&wait);
+	current->state = TASK_INTERRUPTIBLE;
+	for (;;){
+		skb = skb_dequeue(&sk->receive_queue);
+		if (skb){
+			err=0;
+			break;
+		}
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(sk->sleep,&wait);
+	
+	if (err != 0)
+		return err;
+	
+	newsk = get_newsk_from_skb(skb);
+	if (!newsk){
+		return -EINVAL;
+	}
+
+	set_bit(1,&wanpipe_tx_critical);
+	write_lock(&wanpipe_sklist_lock);
+	newsk->next = wanpipe_sklist;
+	wanpipe_sklist = newsk;
+	sock_hold(sk);
+	write_unlock(&wanpipe_sklist_lock);
+	clear_bit(1,&wanpipe_tx_critical);
+
+	newsk->pair = NULL;
+	newsk->socket = newsock;
+	newsk->sleep = &newsock->wait;
+
+	/* Now attach up the new socket */
+	sk->ack_backlog--;
+	newsock->sk = newsk;
+	
+	kfree_skb(skb);
+
+	DBG_PRINTK(KERN_INFO "\nwansock: ACCEPT Got LCN %i\n",newsk->protinfo.af_wanpipe->lcn);
+	return 0;
+}
+
+/*======================================================================
+ *  get_newsk_from_skb
+ *
+ *	Accept() uses this function to get the address of the new
+ *      socket structure.
+ *=====================================================================*/
+
+struct sock * get_newsk_from_skb (struct sk_buff *skb)
+{
+	netdevice_t *dev = skb->dev;
+	wanpipe_common_t *chan;	
+
+	if (!dev){
+		return NULL;
+	}
+		
+	if ((chan = dev->priv) == NULL){
+		return NULL;
+	}
+		
+	if (!chan->sk){
+		return NULL;
+	}
+	return (struct sock *)chan->sk;
+}
+
+/*======================================================================
+ *  wanpipe_connect
+ *
+ *  	CONNECT() System Call. X25API specific function
+ * 	Check the state of the sock, and execute PLACE_CALL command.
+ *      Connect can ether block or return without waiting for connection, 
+ *      if specified by user.
+ *=====================================================================*/
+
+static int wanpipe_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct wan_sockaddr_ll *addr = (struct wan_sockaddr_ll*)uaddr;
+	netdevice_t *dev;
+	int err;
+
+	if (sk->num != htons(X25_PROT))
+		return -EINVAL;
+
+	if (sk->state == WANSOCK_CONNECTED)
+		return EISCONN;	/* No reconnect on a seqpacket socket */
+
+	if (sk->state != WAN_DISCONNECTED){
+		printk(KERN_INFO "wansock: Trying to connect on channel NON DISCONNECT\n");
+		return -ECONNREFUSED;
+	}
+
+	sk->state   = WANSOCK_DISCONNECTED;	
+	sock->state = SS_UNCONNECTED;
+
+	if (addr_len != sizeof(struct wan_sockaddr_ll))
+		return -EINVAL;
+
+	if (addr->sll_family != AF_WANPIPE)
+		return -EINVAL;
+
+	if ((dev = dev_get_by_index(sk->bound_dev_if)) == NULL)
+		return -ENETUNREACH;
+
+	dev_put(dev);
+	
+	if (!sk->zapped)		/* Must bind first - autobinding does not work */
+		return -EINVAL;
+
+	sock->state   = SS_CONNECTING;
+	sk->state     = WANSOCK_CONNECTING;
+
+	if (!sk->protinfo.af_wanpipe->mbox){
+		if (sk->protinfo.af_wanpipe->svc){
+			return -EINVAL;
+		}else{
+			int err;
+			if ((err=set_ioctl_cmd(sk,NULL)) < 0)
+				return err;
+		}
+	}
+
+	if ((err=wanpipe_exec_cmd(sk, X25_PLACE_CALL,flags)) != 0){
+		sock->state = SS_UNCONNECTED;
+		sk->state = WANSOCK_CONNECTED;
+		return err;
+	}
+
+	if (sk->state != WANSOCK_CONNECTED && (flags & O_NONBLOCK)){
+		return 0;
+	}
+
+	if (sk->state != WANSOCK_CONNECTED) {
+		sock->state = SS_UNCONNECTED;
+		return -ECONNREFUSED; 
+	}
+
+	sock->state = SS_CONNECTED;
+	return 0;
+}
+
+#ifdef LINUX_2_4
+struct proto_ops wanpipe_ops = {
+	family: 	PF_WANPIPE,
+
+	release: 	wanpipe_release,
+	bind: 		wanpipe_bind,
+	connect: 	wanpipe_connect,
+	socketpair: 	sock_no_socketpair,
+	accept: 	wanpipe_accept,
+	getname: 	wanpipe_getname, 
+	poll: 		wanpipe_poll,
+	ioctl: 		wanpipe_ioctl,
+	listen: 	wanpipe_listen, 
+	shutdown: 	sock_no_shutdown,
+	setsockopt: 	sock_no_setsockopt,
+	getsockopt: 	sock_no_getsockopt,
+	sendmsg: 	wanpipe_sendmsg,
+	recvmsg: 	wanpipe_recvmsg
+};
+#else
+struct proto_ops wanpipe_ops = {
+	PF_WANPIPE,
+
+	sock_no_dup,
+	wanpipe_release,
+	wanpipe_bind,
+	wanpipe_connect,
+	sock_no_socketpair,
+	wanpipe_accept,
+	wanpipe_getname, 
+	wanpipe_poll,
+	wanpipe_ioctl,
+	wanpipe_listen, 
+	sock_no_shutdown,
+	sock_no_setsockopt,
+	sock_no_getsockopt,
+	sock_no_fcntl,
+	wanpipe_sendmsg,
+	wanpipe_recvmsg
+};
+#endif
+
+
+static struct net_proto_family wanpipe_family_ops = {
+	PF_WANPIPE,
+	wanpipe_create
+};
+
+struct notifier_block wanpipe_netdev_notifier={
+	wanpipe_notifier,
+	NULL,
+	0
+};
+
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	printk(KERN_INFO "wansock: Cleaning up \n");
+	unregister_netdevice_notifier(&wanpipe_netdev_notifier);
+	sock_unregister(PF_WANPIPE);
+	return;
+}
+
+
+int init_module(void)
+{
+
+	printk(KERN_INFO "wansock: Registering Socket \n");
+	sock_register(&wanpipe_family_ops);
+	register_netdevice_notifier(&wanpipe_netdev_notifier);
+	return 0;
+}
+#endif
diff -urN linux-2.4.4-pre2/net/wanrouter/patchlevel linux/net/wanrouter/patchlevel
--- linux-2.4.4-pre2/net/wanrouter/patchlevel	Thu Jan  7 09:25:02 1999
+++ linux/net/wanrouter/patchlevel	Mon Dec  3 22:34:51 2001
@@ -1 +1 @@
-2.0.3
+2.2.1
diff -urN linux-2.4.4-pre2/net/wanrouter/wanmain.c linux/net/wanrouter/wanmain.c
--- linux-2.4.4-pre2/net/wanrouter/wanmain.c	Sun Mar 25 18:14:25 2001
+++ linux/net/wanrouter/wanmain.c	Mon Dec  3 22:34:51 2001
@@ -18,6 +18,14 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Nov 24, 2000  Nenad Corbic	Updated for 2.4.X kernels 
+* Nov 07, 2000  Nenad Corbic	Fixed the Mulit-Port PPP for kernels 2.2.16 and
+*  				greater.
+* Aug 2,  2000  Nenad Corbic	Block the Multi-Port PPP from running on
+*  			        kernels 2.2.16 or greater.  The SyncPPP 
+*  			        has changed.
+* Jul 13, 2000  Nenad Corbic	Added SyncPPP support
+* 				Added extra debugging in device_setup().
 * Oct 01, 1999  Gideon Hack     Update for s514 PCI card
 * Dec 27, 1996	Gene Kozin	Initial version (based on Sangoma's WANPIPE)
 * Jan 16, 1997	Gene Kozin	router_devlist made public
@@ -31,7 +39,6 @@
 * Dec 22, 1998  Arnaldo Melo    vmalloc/vfree used in device_setup to allocate
 *                               kernel memory and copy configuration data to
 *                               kernel space (for big firmwares)
-* May 19, 1999  Arnaldo Melo    __init in wanrouter_init
 * Jun 02, 1999  Gideon Hack	Updates for Linux 2.0.X and 2.2.X kernels.	
 *****************************************************************************/
 
@@ -41,17 +48,95 @@
 #include <linux/errno.h>	/* return codes */
 #include <linux/kernel.h>
 #include <linux/module.h>	/* support for loadable modules */
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/mm.h>		/* verify_area(), etc. */
 #include <linux/string.h>	/* inline mem*, str* functions */
-#include <linux/vmalloc.h>	/* vmalloc, vfree */
-#include <asm/segment.h>	/* kernel <-> user copy */
+
 #include <asm/byteorder.h>	/* htons(), etc. */
-#include <asm/uaccess.h>	/* copy_to/from_user */
 #include <linux/wanrouter.h>	/* WAN router API definitions */
-#include <linux/init.h>		/* __init et al. */
 
 
+#if defined(LINUX_2_4)
+ #include <linux/vmalloc.h>	/* vmalloc, vfree */
+ #include <asm/uaccess.h>        /* copy_to/from_user */
+ #include <linux/init.h>         /* __initfunc et al. */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+ #include <net/syncppp.h>
+#else
+ #include <../drivers/net/wan/syncppp.h>
+#endif
+
+#elif defined(LINUX_2_1) 
+ #define LINUX_2_1
+ #include <linux/vmalloc.h>	/* vmalloc, vfree */
+ #include <asm/uaccess.h>        /* copy_to/from_user */
+ #include <linux/init.h>         /* __initfunc et al. */
+ #include <../drivers/net/syncppp.h>
+
+#else
+ #include <asm/segment.h>	/* kernel <-> user copy */
+#endif
+
+#define KMEM_SAFETYZONE 8
+
+/***********FOR DEBUGGING PURPOSES*********************************************
+static void * dbg_kmalloc(unsigned int size, int prio, int line) {
+	int i = 0;
+	void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio);
+	char * c1 = v;	
+	c1 += sizeof(unsigned int);
+	*((unsigned int *)v) = size;
+
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D';
+		c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F';
+		c1 += 8;
+	}
+	c1 += size;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G';
+		c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L';
+		c1 += 8;
+	}
+	v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8;
+	printk(KERN_INFO "line %d  kmalloc(%d,%d) = %p\n",line,size,prio,v);
+	return v;
+}
+static void dbg_kfree(void * v, int line) {
+	unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8));
+	unsigned int size = *sp;
+	char * c1 = ((char *)v) - KMEM_SAFETYZONE*8;
+	int i = 0;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		if (   c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D'
+		    || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') {
+			printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v);
+			printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
+			                c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
+		}
+		c1 += 8;
+	}
+	c1 += size;
+	for (i = 0; i < KMEM_SAFETYZONE; i++) {
+		if (   c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G'
+		    || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L'
+		   ) {
+			printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v);
+			printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
+			                c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
+		}
+		c1 += 8;
+	}
+	printk(KERN_INFO "line %d  kfree(%p)\n",line,v);
+	v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8);
+	kfree(v);
+}
+
+#define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__)
+#define kfree(x) dbg_kfree(x,__LINE__)
+*****************************************************************************/
+
 
 /*
  * 	Defines and Macros 
@@ -91,14 +176,18 @@
  */
 
 static wan_device_t *find_device (char *name);
-static int delete_interface (wan_device_t *wandev, char *name, int force);
+static int delete_interface (wan_device_t *wandev, char *name);
+void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags);
+void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags);
+
+
 
 /*
  *	Global Data
  */
 
-static char fullname[]		= "WAN Router";
-static char copyright[]		= "(c) 1995-1999 Sangoma Technologies Inc.";
+static char fullname[]		= "Sangoma WANPIPE Router";
+static char copyright[]		= "(c) 1995-2000 Sangoma Technologies Inc.";
 static char modname[]		= ROUTER_NAME;	/* short module name */
 wan_device_t* router_devlist 	= NULL;	/* list of registered devices */
 static int devcnt 		= 0;
@@ -113,31 +202,41 @@
 #endif
 
 #ifndef MODULE
-int __init wanrouter_init(void)
+
+int wanrouter_init(void)
 {
 	int err;
-	extern int wanpipe_init(void),
-		   cyclomx_init(void);
+	extern int wanpipe_init(void);
+	extern int sdladrv_init(void);
 
 	printk(KERN_INFO "%s v%u.%u %s\n",
 		fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright);
+
 	err = wanrouter_proc_init();
-	if (err)
-		printk(KERN_ERR "%s: can't create entry in proc filesystem!\n",	modname);
+	if (err){
+		printk(KERN_INFO "%s: can't create entry in proc filesystem!\n", modname);
+	}
+
+        /*
+         *      Initialise compiled in boards
+         */
 
-	/*
-	 *	Initialise compiled in boards
-	 */		
-	 
 #ifdef CONFIG_VENDOR_SANGOMA
+	sdladrv_init();
 	wanpipe_init();
 #endif	
-#ifdef CONFIG_CYCLADES_SYNC
-	cyclomx_init();
-#endif
+ 
 	return err;
 }
 
+
+#ifdef LINUX_2_4
+static void __exit wanrouter_cleanup (void)
+{
+	wanrouter_proc_cleanup();
+}
+#endif
+
 #else
 
 /*
@@ -161,9 +260,13 @@
 
 	printk(KERN_INFO "%s v%u.%u %s\n",
 		fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright);
+	
 	err = wanrouter_proc_init();
-	if (err) printk(KERN_ERR
+	
+	if (err){ 
+		printk(KERN_INFO
 		"%s: can't create entry in proc filesystem!\n", modname);
+	}
 	return err;
 }
 
@@ -207,7 +310,7 @@
 	if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC) ||
 	    (wandev->name == NULL))
 		return -EINVAL;
-
+ 		
 	namelen = strlen(wandev->name);
 	if (!namelen || (namelen > WAN_DRVNAME_SZ))
 		return -EINVAL;
@@ -219,12 +322,13 @@
 	printk(KERN_INFO "%s: registering WAN device %s\n",
 		modname, wandev->name);
 #endif
+
 	/*
 	 *	Register /proc directory entry 
 	 */
 	err = wanrouter_proc_add(wandev);
 	if (err) {
-		printk(KERN_ERR
+		printk(KERN_INFO
 			"%s: can't create /proc/net/router/%s entry!\n",
 			modname, wandev->name);
 		return err;
@@ -274,17 +378,17 @@
 #ifdef WANDEBUG		
 	printk(KERN_INFO "%s: unregistering WAN device %s\n", modname, name);
 #endif
-	
+
 	if (wandev->state != WAN_UNCONFIGURED) {
-		while(wandev->dev)
-			delete_interface(wandev, wandev->dev->name, 1);
-		if (wandev->shutdown)	
-			wandev->shutdown(wandev);
+		device_shutdown(wandev);
 	}
-	if (prev)
+	
+	if (prev){
 		prev->next = wandev->next;
-	else
+	}else{
 		router_devlist = wandev->next;
+	}
+	
 	--devcnt;
 	wanrouter_proc_delete(wandev);
         MOD_DEC_USE_COUNT;
@@ -302,12 +406,12 @@
  */
 
 
-int wanrouter_encapsulate (struct sk_buff* skb, struct net_device* dev)
+int wanrouter_encapsulate (struct sk_buff *skb, netdevice_t *dev,
+	unsigned short type)
 {
 	int hdr_len = 0;
 
-	switch (skb->protocol)
-	{
+	switch (type) {
 	case ETH_P_IP:		/* IP datagram encapsulation */
 		hdr_len += 1;
 		skb_push(skb, 1);
@@ -321,18 +425,19 @@
 		skb->data[0] = 0;
 		skb->data[1] = NLPID_SNAP;
 		memcpy(&skb->data[2], oui_ether, sizeof(oui_ether));
-		*((unsigned short*)&skb->data[5]) = htons(skb->protocol);
+		*((unsigned short*)&skb->data[5]) = htons(type);
 		break;
 
 	default:		/* Unknown packet type */
 		printk(KERN_INFO
 			"%s: unsupported Ethertype 0x%04X on interface %s!\n",
-			modname, skb->protocol, dev->name);
+			modname, type, dev->name);
 		hdr_len = -EINVAL;
 	}
 	return hdr_len;
 }
 
+
 /*
  *	Decapsulate packet.
  *
@@ -344,21 +449,19 @@
  */
 
 
-unsigned short wanrouter_type_trans (struct sk_buff* skb, struct net_device* dev)
+unsigned short wanrouter_type_trans (struct sk_buff *skb, netdevice_t *dev)
 {
 	int cnt = skb->data[0] ? 0 : 1;	/* there may be a pad present */
 	unsigned short ethertype;
 
-	switch (skb->data[cnt])
-	{
+	switch (skb->data[cnt]) {
 	case NLPID_IP:		/* IP datagramm */
 		ethertype = htons(ETH_P_IP);
 		cnt += 1;
 		break;
 
         case NLPID_SNAP:	/* SNAP encapsulation */
-		if (memcmp(&skb->data[cnt + 1], oui_ether, sizeof(oui_ether)))
-		{
+		if (memcmp(&skb->data[cnt + 1], oui_ether, sizeof(oui_ether))){
           		printk(KERN_INFO
 				"%s: unsupported SNAP OUI %02X-%02X-%02X "
 				"on interface %s!\n", modname,
@@ -380,8 +483,8 @@
 	}
 	skb->protocol = ethertype;
 	skb->pkt_type = PACKET_HOST;	/*	Physically point to point */
-	skb->mac.raw  = skb->data;
 	skb_pull(skb, cnt);
+	skb->mac.raw  = skb->data;
 	return ethertype;
 }
 
@@ -399,9 +502,11 @@
 	struct proc_dir_entry *dent;
 	wan_device_t *wandev;
 
+      #if defined (LINUX_2_1) || defined (LINUX_2_4)
 	if (!capable(CAP_NET_ADMIN)){
 		return -EPERM;
 	}
+      #endif
 		
 	if ((cmd >> 8) != ROUTER_IOCTL)
 		return -EINVAL;
@@ -466,44 +571,105 @@
 	wandev_conf_t *conf;
 	int err = -EINVAL;
 
-	if (wandev->setup == NULL)	/* Nothing to do ? */
+	if (wandev->setup == NULL){	/* Nothing to do ? */
+		printk(KERN_INFO "%s: ERROR, No setup script: wandev->setup()\n",
+				wandev->name);
 		return 0;
+	}
+
+      #ifdef LINUX_2_0 
+	err = verify_area (VERIFY_READ, u_conf, sizeof(wandev_conf_t));
+	if(err){
+		return err;
+	}
+      #endif	
 
 	conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL);
-	if (conf == NULL)
+	if (conf == NULL){
+		printk(KERN_INFO "%s: ERROR, Failed to allocate kernel memory !\n",
+				wandev->name);
 		return -ENOBUFS;
-
+	}
+		
+      #if defined (LINUX_2_1) || defined (LINUX_2_4)		
 	if(copy_from_user(conf, u_conf, sizeof(wandev_conf_t))) {
+		printk(KERN_INFO "%s: Failed to copy user config data to kernel space!\n",
+				wandev->name);
 		kfree(conf);
 		return -EFAULT;
 	}
+      #else
+	memcpy_fromfs ((void *)conf, (void *)u_conf, sizeof(wandev_conf_t));
+      #endif
 	
-	if (conf->magic != ROUTER_MAGIC) {
+	if (conf->magic != ROUTER_MAGIC){
 		kfree(conf);
+		printk(KERN_INFO "%s: ERROR, Invalid MAGIC Number\n",
+				wandev->name);
 	        return -EINVAL; 
 	}
 
-	if (conf->data_size && conf->data) {
+	if (conf->data_size && conf->data){
 		if(conf->data_size > 128000 || conf->data_size < 0) {
 			kfree(conf);
+			printk(KERN_INFO 
+			    "%s: ERROR, Invalid firmware data size %i !\n",
+					wandev->name, conf->data_size);
 		        return -EINVAL;;
 		}
 
+#if defined (LINUX_2_1) || defined (LINUX_2_4)
 		data = vmalloc(conf->data_size);
 		if (data) {
 			if(!copy_from_user(data, conf->data, conf->data_size)){
 				conf->data=data;
 				err = wandev->setup(wandev,conf);
-			}
-			else 
+			}else{ 
+				printk(KERN_INFO 
+				     "%s: ERROR, Faild to copy from user data !\n",
+				       wandev->name);
 				err = -EFAULT;
-		}
-		else 
+			}
+		}else{ 
+			printk(KERN_INFO 
+			 	"%s: ERROR, Faild allocate kernel memory !\n",
+				wandev->name);
 			err = -ENOBUFS;
-
-		if (data)
+		}
+			
+		if (data){
 			vfree(data);
+		}
+#else
+                err = verify_area(VERIFY_READ, conf->data, conf->data_size);
+                if (!err) {
+                        data = kmalloc(conf->data_size, GFP_KERNEL);
+                        if (data) {
+                                memcpy_fromfs(data, (void*)conf->data,
+                                        conf->data_size);
+                                conf->data = data;
+                        }else{
+				printk(KERN_INFO 
+				    "%s: ERROR, Faild allocate kernel memory !\n",wandev->name);
+				err = -ENOMEM;
+			}
+                }else{
+			printk(KERN_INFO 
+			 	"%s: ERROR, Faild to copy from user data !\n",wandev->name);
+		}
 
+		if (!err){
+			err = wandev->setup(wandev, conf);
+		}
+		
+        	if (data){
+			kfree(data);
+		}
+#endif
+	}else{
+		printk(KERN_INFO 
+		    "%s: ERROR, No firmware found ! Firmware size = %i !\n",
+				wandev->name, conf->data_size);
 	}
 
 	kfree(conf);
@@ -516,27 +682,37 @@
  *	o call driver's shutdown() entry point
  */
  
-static int device_shutdown (wan_device_t* wandev)
+static int device_shutdown (wan_device_t *wandev)
 {
-	struct net_device* dev;
-
-	if (wandev->state == WAN_UNCONFIGURED)
+	netdevice_t *dev;
+	int err=0;
+		
+	if (wandev->state == WAN_UNCONFIGURED){
 		return 0;
+	}
+
+	printk(KERN_INFO "\n%s: Shutting Down!\n",wandev->name);
 		
-	for (dev = wandev->dev; dev;)
-	{
-		if (delete_interface(wandev, dev->name, 0))
-		{
-			struct net_device **slave = dev->priv;
-			dev = *slave;
+	for (dev = wandev->dev; dev;) {
+		if ((err=delete_interface(wandev, dev->name)) != 0){
+			return err;
 		}
+
+		/* The above function deallocates the current dev
+		 * structure. Therefore, we cannot use dev->priv
+		 * as the next element: wandev->dev points to the
+		 * next element */
+		dev = wandev->dev;
 	}
-	if (wandev->ndev)
+	
+	if (wandev->ndev){
 		return -EBUSY;	/* there are opened interfaces  */
-		
+	}	
+	
 	if (wandev->shutdown)
-		return wandev->shutdown(wandev);
-	return 0;
+		err=wandev->shutdown(wandev);
+	
+	return err;
 }
 
 /*
@@ -547,6 +723,13 @@
 {
 	wandev_stat_t stat;
 
+      #ifdef LINUX_2_0
+	int err;
+	err = verify_area(VERIFY_WRITE, u_stat, sizeof(wandev_stat_t));
+        if (err)
+                return err;
+      #endif
+
 	memset(&stat, 0, sizeof(stat));
 
 	/* Ask device driver to update device statistics */
@@ -557,8 +740,12 @@
 	stat.ndev  = wandev->ndev;
 	stat.state = wandev->state;
 
+      #if defined (LINUX_2_1) || defined (LINUX_2_4)
 	if(copy_to_user(u_stat, &stat, sizeof(stat)))
 		return -EFAULT;
+      #else
+        memcpy_tofs((void*)u_stat, (void*)&stat, sizeof(stat));
+      #endif
 
 	return 0;
 }
@@ -573,58 +760,149 @@
  *	o register network interface
  */
 
-static int device_new_if (wan_device_t* wandev, wanif_conf_t* u_conf)
+static int device_new_if (wan_device_t *wandev, wanif_conf_t *u_conf)
 {
 	wanif_conf_t conf;
-	struct net_device *dev;
+	netdevice_t *dev=NULL;
+      #ifdef CONFIG_WANPIPE_MULTPPP
+	struct ppp_device *pppdev=NULL;
+      #endif
 	int err;
 
 	if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL))
 		return -ENODEV;
-		
+	
+      #if defined (LINUX_2_1) || defined (LINUX_2_4)	
 	if(copy_from_user(&conf, u_conf, sizeof(wanif_conf_t)))
 		return -EFAULT;
+      #else
+        err = verify_area(VERIFY_READ, u_conf, sizeof(wanif_conf_t));
+        if (err)
+                return err;
+        memcpy_fromfs((void*)&conf, (void*)u_conf, sizeof(wanif_conf_t));
+      #endif
 		
 	if (conf.magic != ROUTER_MAGIC)
 		return -EINVAL;
+
+	err = -EPROTONOSUPPORT;
+
+	
+#ifdef CONFIG_WANPIPE_MULTPPP
+	if (conf.config_id == WANCONFIG_MPPP){
+
+		pppdev = kmalloc(sizeof(struct ppp_device), GFP_KERNEL);
+		if (pppdev == NULL){
+			return -ENOBUFS;
+		}
+		memset(pppdev, 0, sizeof(struct ppp_device));
+
+	      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16)
+		pppdev->dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL);
+		if (pppdev->dev == NULL){
+			return -ENOBUFS;
+		}
+		memset(pppdev->dev, 0, sizeof(netdevice_t));
+	      #endif
 		
-	dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
-	if (dev == NULL)
-		return -ENOBUFS;
+		err = wandev->new_if(wandev, (netdevice_t *)pppdev, &conf);
 		
-	memset(dev, 0, sizeof(struct net_device));
-	err = wandev->new_if(wandev, dev, &conf);
+		      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,16)
+			dev = pppdev->dev;
+		      #else
+			dev = &pppdev->dev;
+		      #endif
+			
+	}else{
+
+		dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL);
+		if (dev == NULL){
+			return -ENOBUFS;
+		}
+		memset(dev, 0, sizeof(netdevice_t));	
+		err = wandev->new_if(wandev, dev, &conf);
+	}
+
+#else
+	/* Sync PPP is disabled */
+	if (conf.config_id != WANCONFIG_MPPP){
+
+		dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL);
+		if (dev == NULL){
+			return -ENOBUFS;
+		}
+		memset(dev, 0, sizeof(netdevice_t));	
+		err = wandev->new_if(wandev, dev, &conf);
+	}else{
+		printk(KERN_INFO "%s: Wanpipe Mulit-Port PPP support has not been compiled in!\n",
+				wandev->name);
+		return err;
+	}
+#endif
+	
 	if (!err) {
 		/* Register network interface. This will invoke init()
 		 * function supplied by the driver.  If device registered
 		 * successfully, add it to the interface list.
 		 */
-		if (dev->name == NULL)
+
+		if (dev->name == NULL){
 			err = -EINVAL;
-			
-		else if (dev_get(dev->name))
+		}else if (dev_get(dev->name)){
 			err = -EEXIST;	/* name already exists */
-		else {
-#ifdef WANDEBUG		
+		}else{
+				
+			#ifdef WANDEBUG		
 			printk(KERN_INFO "%s: registering interface %s...\n",
 				modname, dev->name);
-#endif				
+			#endif				
+			
 			err = register_netdev(dev);
 			if (!err) {
-				struct net_device **slave = dev->priv;
+				netdevice_t *slave=NULL;
+				unsigned long smp_flags=0;
+				
+				lock_adapter_irq(&wandev->lock, &smp_flags);
+			
+				if (wandev->dev == NULL){
+					wandev->dev = dev;
+				}else{
+					for (slave=wandev->dev;
+					     *((netdevice_t**)slave->priv);
+					     slave=*((netdevice_t**)slave->priv));
 
-				cli();	/***** critical section start *****/
-				*slave = wandev->dev;
-				wandev->dev = dev;
+					*((netdevice_t**)slave->priv) = dev;
+				}
 				++wandev->ndev;
-				sti();	/****** critical section end ******/
+				
+				unlock_adapter_irq(&wandev->lock, &smp_flags);
 				return 0;	/* done !!! */
 			}
 		}
 		if (wandev->del_if)
 			wandev->del_if(wandev, dev);
 	}
-	kfree(dev);
+
+	/* This code has moved from del_if() function */
+	if (dev->priv){
+		kfree(dev->priv);
+		dev->priv=NULL;
+	}
+
+	
+      #ifdef CONFIG_WANPIPE_MULTPPP
+	if (conf.config_id == WANCONFIG_MPPP){
+		kfree(pppdev);
+	}else{
+		kfree(dev);
+	}
+      #else
+	/* Sync PPP is disabled */
+	if (conf.config_id != WANCONFIG_MPPP){
+		kfree(dev);
+	}
+      #endif
+	
 	return err;
 }
 
@@ -638,15 +916,43 @@
 static int device_del_if (wan_device_t *wandev, char *u_name)
 {
 	char name[WAN_IFNAME_SZ + 1];
+        int err = 0;
 
 	if (wandev->state == WAN_UNCONFIGURED)
 		return -ENODEV;
 	
+      #ifdef LINUX_2_0
+        err = verify_area(VERIFY_READ, u_name, WAN_IFNAME_SZ);
+        if (err)
+		return err;
+      #endif	
+
 	memset(name, 0, sizeof(name));
 
+      #if defined (LINUX_2_1) || defined (LINUX_2_4)
 	if(copy_from_user(name, u_name, WAN_IFNAME_SZ))
 		return -EFAULT;
-	return delete_interface(wandev, name, 0);
+      #else
+        memcpy_fromfs((void*)name, (void*)u_name, WAN_IFNAME_SZ);
+      #endif
+
+	err = delete_interface(wandev, name);
+	if (err)
+		return(err);
+
+	/* If last interface being deleted, shutdown card
+	 * This helps with administration at leaf nodes
+	 * (You can tell if the person at the other end of the phone 
+	 * has an interface configured) and avoids DoS vulnerabilities
+	 * in binary driver files - this fixes a problem with the current
+	 * Sangoma driver going into strange states when all the network
+	 * interfaces are deleted and the link irrecoverably disconnected.
+	 */ 
+
+        if (!wandev->ndev && wandev->shutdown){
+                err = wandev->shutdown(wandev);
+	}
+	return err;
 }
 
 
@@ -685,59 +991,102 @@
  *	sure that opened interfaces are not removed!
  */
 
-static int delete_interface (wan_device_t *wandev, char *name, int force)
+static int delete_interface (wan_device_t *wandev, char *name)
 {
-	struct net_device *dev, *prev;
+	netdevice_t *dev=NULL, *prev=NULL;
+	unsigned long smp_flags=0;
 
+	lock_adapter_irq(&wandev->lock, &smp_flags);
 	dev = wandev->dev;
 	prev = NULL;
 	while (dev && strcmp(name, dev->name)) {
-		struct net_device **slave = dev->priv;
-
+		netdevice_t **slave = dev->priv;
 		prev = dev;
 		dev = *slave;
 	}
-
-	if (dev == NULL)
+	unlock_adapter_irq(&wandev->lock, &smp_flags);
+	
+	if (dev == NULL){
 		return -ENODEV;	/* interface not found */
-
-	if (netif_running(dev)) {
-		if (force) {
-			printk(KERN_WARNING
-				"%s: deleting opened interface %s!\n",
-				modname, name);
-		}
-		else
-			return -EBUSY;	/* interface in use */
+	}
+		
+       #ifdef LINUX_2_4
+	if (netif_running(dev)){
+       #else
+	if (dev->start) {
+       #endif
+		return -EBUSY;	/* interface in use */
 	}
 
 	if (wandev->del_if)
 		wandev->del_if(wandev, dev);
 
-	cli();			/***** critical section start *****/
+	lock_adapter_irq(&wandev->lock, &smp_flags);
 	if (prev) {
-		struct net_device **prev_slave = prev->priv;
-		struct net_device **slave = dev->priv;
+		netdevice_t **prev_slave = prev->priv;
+		netdevice_t **slave = dev->priv;
 
 		*prev_slave = *slave;
 	} else {
-		struct net_device **slave = dev->priv;
-
+		netdevice_t **slave = dev->priv;
 		wandev->dev = *slave;
 	}
 	--wandev->ndev;
-	sti();			/****** critical section end ******/
+	unlock_adapter_irq(&wandev->lock, &smp_flags);
+	
+	printk(KERN_INFO "%s: unregistering '%s'\n", wandev->name, dev->name); 
+	
+	/* Due to new interface linking method using dev->priv,
+	 * this code has moved from del_if() function.*/
+	if (dev->priv){
+		kfree(dev->priv);
+		dev->priv=NULL;
+	}
 
-	printk("Unregistering '%s'\n", dev->name); 
 	unregister_netdev(dev);
+
+      #ifdef LINUX_2_4
+	kfree(dev);
+      #else
+	if (dev->name){
+		kfree(dev->name);
+	}
 	kfree(dev);
+      #endif
+
 	return 0;
 }
 
+void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
+{
+      #ifdef LINUX_2_0
+	save_flags(*smp_flags);
+	cli();
+      #else
+       	spin_lock_irqsave(lock, *smp_flags);
+      #endif
+}
+
+
+void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags)
+{
+      #ifdef LINUX_2_0
+	restore_flags(*smp_flags);
+      #else
+	spin_unlock_irqrestore(lock, *smp_flags);
+      #endif
+}
+
+
+
+#if defined (LINUX_2_1) || defined (LINUX_2_4)
 EXPORT_SYMBOL(register_wan_device);
 EXPORT_SYMBOL(unregister_wan_device);
 EXPORT_SYMBOL(wanrouter_encapsulate);
 EXPORT_SYMBOL(wanrouter_type_trans);
+EXPORT_SYMBOL(lock_adapter_irq);
+EXPORT_SYMBOL(unlock_adapter_irq);
+#endif
 
 /*
  *	End
diff -urN linux-2.4.4-pre2/net/wanrouter/wanproc.c linux/net/wanrouter/wanproc.c
--- linux-2.4.4-pre2/net/wanrouter/wanproc.c	Fri Feb  9 11:34:13 2001
+++ linux/net/wanrouter/wanproc.c	Mon Dec  3 22:34:51 2001
@@ -13,7 +13,6 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
-* Jan 20, 2001  Arnaldo C.Melo  Fix leak on error in router_proc_read, cleanups
 * Jun 02, 1999  Gideon Hack	Updates for Linux 2.2.X kernels.
 * Jun 29, 1997	Alan Cox	Merged with 1.0.3 vendor code
 * Jan 29, 1997	Gene Kozin	v1.0.1. Implemented /proc read routines
@@ -26,397 +25,1017 @@
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/kernel.h>
-#include <linux/slab.h>	/* kmalloc(), kfree() */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/mm.h>		/* verify_area(), etc. */
 #include <linux/string.h>	/* inline mem*, str* functions */
-#include <linux/init.h>		/* __init et al. */
-#include <asm/segment.h>	/* kernel <-> user copy */
 #include <asm/byteorder.h>	/* htons(), etc. */
-#include <asm/uaccess.h>	/* copy_to_user */
 #include <asm/io.h>
 #include <linux/wanrouter.h>	/* WAN router API definitions */
 
 
-/****** Defines and Macros **************************************************/
 
-#define PROC_STATS_FORMAT "%30s: %12lu\n"
+#if defined(LINUX_2_1) || defined(LINUX_2_4) 
+ #include <linux/init.h>	/* __initfunc et al. */
+ #include <asm/uaccess.h>       /* copy_to_user */
+ #define PROC_STATS_FORMAT "%30s: %12lu\n"
+#else
+ #define PROC_STATS_FORMAT "%30s: %12u\n"
+ #include <asm/segment.h>	/* kernel <-> user copy */
+#endif
+
+
+/****** Defines and Macros **************************************************/
 
 #ifndef	min
 #define min(a,b) (((a)<(b))?(a):(b))
 #endif
+#ifndef	max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
 
 #define	PROC_BUFSZ	4000	/* buffer size for printing proc info */
 
+#define PROT_DECODE(prot) ((prot == WANCONFIG_FR) ? " FR" :\
+			      (prot == WANCONFIG_X25) ? " X25" : \
+			         (prot == WANCONFIG_PPP) ? " PPP" : \
+				    (prot == WANCONFIG_CHDLC) ? " CHDLC": \
+				       (prot == WANCONFIG_MPPP) ? " MPPP" : \
+				           " Unknown" )
+	
+/****** Data Types **********************************************************/
+
+typedef struct wan_stat_entry
+{
+	struct wan_stat_entry *next;
+	char *description;		/* description string */
+	void *data;			/* -> data */
+	unsigned data_type;		/* data type */
+} wan_stat_entry_t;
+
 /****** Function Prototypes *************************************************/
 
 #ifdef CONFIG_PROC_FS
 
-/* Proc filesystem interface */
-static int router_proc_perms(struct inode *, int);
-static ssize_t router_proc_read(struct file* file, char* buf, size_t count, 					loff_t *ppos);
 
-/* Methods for preparing data for reading proc entries */
+#ifdef LINUX_2_4  /* Start of LINUX 2.4.X code */
 
-static int config_get_info(char* buf, char** start, off_t offs, int len);
-static int status_get_info(char* buf, char** start, off_t offs, int len);
-static int wandev_get_info(char* buf, char** start, off_t offs, int len);
 
-/* Miscellaneous */
+	/* Proc filesystem interface */
+	static int router_proc_perms(struct inode *, int);
+	static ssize_t router_proc_read(struct file* file, char* buf, size_t count, 					loff_t *ppos);
+
+	/* Methods for preparing data for reading proc entries */
+
+	static int config_get_info(char* buf, char** start, off_t offs, int len);
+	static int status_get_info(char* buf, char** start, off_t offs, int len);
+	static int wandev_get_info(char* buf, char** start, off_t offs, int len);
+
+	/* Miscellaneous */
+
+	/*
+	 *	Structures for interfacing with the /proc filesystem.
+	 *	Router creates its own directory /proc/net/router with the folowing
+	 *	entries:
+	 *	config		device configuration
+	 *	status		global device statistics
+	 *	<device>	entry for each WAN device
+	 */
+
+	/*
+	 *	Generic /proc/net/router/<file> file and inode operations 
+	 */
+	static struct file_operations router_fops =
+	{
+		read:		router_proc_read,
+	};
+
+	static struct inode_operations router_inode =
+	{
+		permission:	router_proc_perms,
+	};
+
+	/*
+	 *	/proc/net/router/<device> file operations
+	 */
+
+	static struct file_operations wandev_fops =
+	{
+		read:		router_proc_read,
+		ioctl:		wanrouter_ioctl,
+	};
+
+	/*
+	 *	/proc/net/router 
+	 */
+
+	static struct proc_dir_entry *proc_router;
+
+	/* Strings */
+	static char conf_hdr[] =
+		"Device name    | port |IRQ|DMA|  mem.addr  |mem.size|"
+		"option1|option2|option3|option4\n";
+		
+	static char stat_hdr[] =
+		"Device name    |protocol|station|interface|clocking|baud rate"
+		"| MTU |ndev|link state\n";
+
+
+	/*
+	 *	Interface functions
+	 */
+
+	/*
+	 *	Initialize router proc interface.
+	 */
+
+	int __init wanrouter_proc_init (void)
+	{
+		struct proc_dir_entry *p;
+		proc_router = proc_mkdir(ROUTER_NAME, proc_net);
+		if (!proc_router)
+			goto fail;
+
+		p = create_proc_entry("config",0,proc_router);
+		if (!p)
+			goto fail_config;
+		p->proc_fops = &router_fops;
+		p->proc_iops = &router_inode;
+		p->get_info = config_get_info;
+		p = create_proc_entry("status",0,proc_router);
+		if (!p)
+			goto fail_stat;
+		p->proc_fops = &router_fops;
+		p->proc_iops = &router_inode;
+		p->get_info = status_get_info;
+		return 0;
+	fail_stat:
+		remove_proc_entry("config", proc_router);
+	fail_config:
+		remove_proc_entry(ROUTER_NAME, proc_net);
+	fail:
+		return -ENOMEM;
+	}
 
-/*
- *	Structures for interfacing with the /proc filesystem.
- *	Router creates its own directory /proc/net/wanrouter with the folowing
- *	entries:
- *	config		device configuration
- *	status		global device statistics
- *	<device>	entry for each WAN device
- */
+	/*
+	 *	Clean up router proc interface.
+	 */
+
+	void wanrouter_proc_cleanup (void)
+	{
+		remove_proc_entry("config", proc_router);
+		remove_proc_entry("status", proc_router);
+		remove_proc_entry(ROUTER_NAME,proc_net);
+	}
 
-/*
- *	Generic /proc/net/wanrouter/<file> file and inode operations 
- */
-static struct file_operations router_fops =
-{
-	read:		router_proc_read,
-};
+	/*
+	 *	Add directory entry for WAN device.
+	 */
+
+	int wanrouter_proc_add (wan_device_t* wandev)
+	{
+		if (wandev->magic != ROUTER_MAGIC)
+			return -EINVAL;
+			
+		wandev->dent = create_proc_entry(wandev->name, 0, proc_router);
+		if (!wandev->dent)
+			return -ENOMEM;
+		wandev->dent->proc_fops	= &wandev_fops;
+		wandev->dent->proc_iops	= &router_inode;
+		wandev->dent->get_info	= wandev_get_info;
+		wandev->dent->data	= wandev;
+		return 0;
+	}
 
-static struct inode_operations router_inode =
-{
-	permission:	router_proc_perms,
-};
+	/*
+	 *	Delete directory entry for WAN device.
+	 */
+	 
+	int wanrouter_proc_delete(wan_device_t* wandev)
+	{
+		if (wandev->magic != ROUTER_MAGIC)
+			return -EINVAL;
+		remove_proc_entry(wandev->name, proc_router);
+		return 0;
+	}
 
-/*
- *	/proc/net/wanrouter/<device> file operations
- */
+	/****** Proc filesystem entry points ****************************************/
 
-static struct file_operations wandev_fops =
-{
-	read:		router_proc_read,
-	ioctl:		wanrouter_ioctl,
-};
+	/*
+	 *	Verify access rights.
+	 */
 
-/*
- *	/proc/net/wanrouter 
- */
+	static int router_proc_perms (struct inode* inode, int op)
+	{
+		return 0;
+	}
 
-static struct proc_dir_entry *proc_router;
+	/*
+	 *	Read router proc directory entry.
+	 *	This is universal routine for reading all entries in /proc/net/wanrouter
+	 *	directory.  Each directory entry contains a pointer to the 'method' for
+	 *	preparing data for that entry.
+	 *	o verify arguments
+	 *	o allocate kernel buffer
+	 *	o call get_info() to prepare data
+	 *	o copy data to user space
+	 *	o release kernel buffer
+	 *
+	 *	Return:	number of bytes copied to user space (0, if no data)
+	 *		<0	error
+	 */
+
+	static ssize_t router_proc_read(struct file* file, char* buf, size_t count,
+					loff_t *ppos)
+	{
+		struct inode *inode = file->f_dentry->d_inode;
+		struct proc_dir_entry* dent;
+		char* page;
+		int pos, offs, len;
+
+		if (count <= 0)
+			return 0;
+			
+		dent = inode->u.generic_ip;
+		if ((dent == NULL) || (dent->get_info == NULL))
+			return 0;
+			
+		page = kmalloc(PROC_BUFSZ, GFP_KERNEL);
+		if (page == NULL)
+			return -ENOBUFS;
+			
+		pos = dent->get_info(page, dent->data, 0, 0);
+		offs = file->f_pos;
+		if (offs < pos) {
+			len = min(pos - offs, count);
+			if(copy_to_user(buf, (page + offs), len))
+				return -EFAULT;
+			file->f_pos += len;
+		}
+		else
+			len = 0;
+		kfree(page);
+		return len;
+	}
 
-/* Strings */
-static char conf_hdr[] =
-	"Device name    | port |IRQ|DMA| mem.addr |mem.size|"
-	"option1|option2|option3|option4\n";
-	
-static char stat_hdr[] =
-	"Device name    |station|interface|clocking|baud rate| MTU |ndev"
-	"|link state\n";
+	/*
+	 *	Prepare data for reading 'Config' entry.
+	 *	Return length of data.
+	 */
+
+	static int config_get_info(char* buf, char** start, off_t offs, int len)
+	{
+		int cnt = sizeof(conf_hdr) - 1;
+		wan_device_t* wandev;
+		strcpy(buf, conf_hdr);
+		for (wandev = router_devlist;
+		     wandev && (cnt < (PROC_BUFSZ - 120));
+		     wandev = wandev->next) {
+			if (wandev->state) cnt += sprintf(&buf[cnt],
+				"%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n",
+				wandev->name,
+				wandev->ioport,
+				wandev->irq,
+				wandev->dma,
+				wandev->maddr,
+				wandev->msize,
+				wandev->hw_opt[0],
+				wandev->hw_opt[1],
+				wandev->hw_opt[2],
+				wandev->hw_opt[3]);
+		}
 
+		return cnt;
+	}
 
-/*
- *	Interface functions
- */
+	/*
+	 *	Prepare data for reading 'Status' entry.
+	 *	Return length of data.
+	 */
+
+	static int status_get_info(char* buf, char** start, off_t offs, int len)
+	{
+		int cnt = 0;
+		wan_device_t* wandev;
+
+		//cnt += sprintf(&buf[cnt], "\nSTATUS:\n\n");
+		strcpy(&buf[cnt], stat_hdr);
+		cnt += sizeof(stat_hdr) - 1;
+
+		for (wandev = router_devlist;
+		     wandev && (cnt < (PROC_BUFSZ - 80));
+		     wandev = wandev->next) {
+			if (!wandev->state) continue;
+			cnt += sprintf(&buf[cnt],
+				"%-15s|%-8s|%-7s|%-9s|%-8s|%9u|%5u|%3u |",
+				wandev->name,
+				PROT_DECODE(wandev->config_id),
+				wandev->config_id == WANCONFIG_FR ? 
+					(wandev->station ? " Node" : " CPE") :
+					(wandev->config_id == WANCONFIG_X25 ?
+					(wandev->station ? " DCE" : " DTE") :
+					(" N/A")),
+				wandev->interface ? " V.35" : " RS-232",
+				wandev->clocking ? "internal" : "external",
+				wandev->bps,
+				wandev->mtu,
+				wandev->ndev);
+
+			switch (wandev->state) {
+
+			case WAN_UNCONFIGURED:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "unconfigured");
+				break;
+
+			case WAN_DISCONNECTED:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "disconnected");
+				break;
+
+			case WAN_CONNECTING:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "connecting");
+				break;
+
+			case WAN_CONNECTED:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "connected");
+				break;
 
-/*
- *	Initialize router proc interface.
- */
+			default:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "invalid");
+				break;
+			}
+		}
+		return cnt;
+	}
 
-int __init wanrouter_proc_init (void)
-{
-	struct proc_dir_entry *p;
-	proc_router = proc_mkdir(ROUTER_NAME, proc_net);
-	if (!proc_router)
-		goto fail;
-
-	p = create_proc_entry("config",0,proc_router);
-	if (!p)
-		goto fail_config;
-	p->proc_fops = &router_fops;
-	p->proc_iops = &router_inode;
-	p->get_info = config_get_info;
-	p = create_proc_entry("status",0,proc_router);
-	if (!p)
-		goto fail_stat;
-	p->proc_fops = &router_fops;
-	p->proc_iops = &router_inode;
-	p->get_info = status_get_info;
-	return 0;
-fail_stat:
-	remove_proc_entry("config", proc_router);
-fail_config:
-	remove_proc_entry(ROUTER_NAME, proc_net);
-fail:
-	return -ENOMEM;
-}
+	/*
+	 *	Prepare data for reading <device> entry.
+	 *	Return length of data.
+	 *
+	 *	On entry, the 'start' argument will contain a pointer to WAN device
+	 *	data space.
+	 */
+
+	static int wandev_get_info(char* buf, char** start, off_t offs, int len)
+	{
+		wan_device_t* wandev = (void*)start;
+		int cnt = 0;
+		int rslt = 0;
+
+		if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC))
+			return 0;
+		if (!wandev->state)
+			return sprintf(&buf[cnt], "device is not configured!\n");
+
+		/* Update device statistics */
+		if (wandev->update) {
+
+			rslt = wandev->update(wandev);
+			if(rslt) {
+				switch (rslt) {
+				case -EAGAIN:
+					return sprintf(&buf[cnt], "Device is busy!\n");
+
+				default:
+					return sprintf(&buf[cnt],
+						"Device is not configured!\n");
+				}
+			}
+		}
 
-/*
- *	Clean up router proc interface.
- */
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total packets received", wandev->stats.rx_packets);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total packets transmitted", wandev->stats.tx_packets);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total bytes received", wandev->stats.rx_bytes);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total bytes transmitted", wandev->stats.tx_bytes);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"bad packets received", wandev->stats.rx_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"packet transmit problems", wandev->stats.tx_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"received frames dropped", wandev->stats.rx_dropped);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"transmit frames dropped", wandev->stats.tx_dropped);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"multicast packets received", wandev->stats.multicast);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"transmit collisions", wandev->stats.collisions);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receive length errors", wandev->stats.rx_length_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receiver overrun errors", wandev->stats.rx_over_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"CRC errors", wandev->stats.rx_crc_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"frame format errors (aborts)", wandev->stats.rx_frame_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receiver fifo overrun", wandev->stats.rx_fifo_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receiver missed packet", wandev->stats.rx_missed_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"aborted frames transmitted", wandev->stats.tx_aborted_errors);
+		return cnt;
+	}
 
-void wanrouter_proc_cleanup (void)
-{
-	remove_proc_entry("config", proc_router);
-	remove_proc_entry("status", proc_router);
-	remove_proc_entry(ROUTER_NAME,proc_net);
-}
 
-/*
- *	Add directory entry for WAN device.
- */
+#else /* ------------------- END OF LINUX 2.4.X VERSION -------------*/
 
-int wanrouter_proc_add (wan_device_t* wandev)
-{
-	if (wandev->magic != ROUTER_MAGIC)
-		return -EINVAL;
-		
-	wandev->dent = create_proc_entry(wandev->name, 0, proc_router);
-	if (!wandev->dent)
-		return -ENOMEM;
-	wandev->dent->proc_fops	= &wandev_fops;
-	wandev->dent->proc_iops	= &router_inode;
-	wandev->dent->get_info	= wandev_get_info;
-	wandev->dent->data	= wandev;
-	return 0;
-}
 
-/*
- *	Delete directory entry for WAN device.
- */
- 
-int wanrouter_proc_delete(wan_device_t* wandev)
-{
-	if (wandev->magic != ROUTER_MAGIC)
-		return -EINVAL;
-	remove_proc_entry(wandev->name, proc_router);
-	return 0;
-}
 
-/****** Proc filesystem entry points ****************************************/
+	/* Proc filesystem interface */
+	static int router_proc_perms(struct inode *, int);
+#ifdef LINUX_2_1
+	static ssize_t router_proc_read(struct file *file, char *buf, size_t count, 					loff_t *ppos);
+#else
+	static int router_proc_read(
+		struct inode* inode, struct file* file, char* buf, int count);
+	static int device_write(
+		struct inode* inode, struct file* file, const char* buf, int count);
+#endif
 
-/*
- *	Verify access rights.
- */
+	/* Methods for preparing data for reading proc entries */
+	static int config_get_info(char* buf, char** start, off_t offs, int len,
+		int dummy);
+	static int status_get_info(char* buf, char** start, off_t offs, int len,
+		int dummy);
+	static int wandev_get_info(char* buf, char** start, off_t offs, int len,
+		int dummy);
+
+	/* Miscellaneous */
+
+	/*
+	 *	Global Data
+	 */
+
+	/*
+	 *	Names of the proc directory entries 
+	 */
+
+	static char name_root[]	= ROUTER_NAME;
+	static char name_conf[]	= "config";
+	static char name_stat[]	= "status";
+
+	/*
+	 *	Structures for interfacing with the /proc filesystem.
+	 *	Router creates its own directory /proc/net/router with the folowing
+	 *	entries:
+	 *	config		device configuration
+	 *	status		global device statistics
+	 *	<device>	entry for each WAN device
+	 */
+
+	/*
+	 *	Generic /proc/net/router/<file> file and inode operations 
+	 */
+#ifdef LINUX_2_1
+	static struct file_operations router_fops =
+	{
+		NULL,			/* lseek   */
+		router_proc_read,	/* read	   */
+		NULL,			/* write   */
+		NULL,			/* readdir */
+		NULL,			/* select  */
+		NULL,			/* ioctl   */
+		NULL,			/* mmap	   */
+		NULL,			/* no special open code	   */
+		NULL,			/* flush */
+		NULL,			/* no special release code */
+		NULL			/* can't fsync */
+	};
+#else
+	static struct file_operations router_fops =
+	{
+		NULL,                   /* lseek   */
+		router_proc_read,       /* read    */
+		NULL,                   /* write   */
+		NULL,                   /* readdir */
+		NULL,                   /* select  */
+		NULL,                   /* ioctl   */
+		NULL,                   /* mmap    */
+		NULL,                   /* no special open code    */
+		NULL,                   /* no special release code */
+		NULL                    /* can't fsync */
+	};
+#endif
 
-static int router_proc_perms (struct inode* inode, int op)
-{
-	return 0;
-}
+	static struct inode_operations router_inode =
+	{
+		&router_fops,
+		NULL,			/* create */
+		NULL,			/* lookup */
+		NULL,			/* link */
+		NULL,			/* unlink */
+		NULL,			/* symlink */
+		NULL,			/* mkdir */
+		NULL,			/* rmdir */
+		NULL,			/* mknod */
+		NULL,			/* rename */
+		NULL,			/* follow link */
+		NULL,			/* readlink */
+		NULL,			/* readpage */
+		NULL,			/* writepage */
+		NULL,			/* bmap */
+		NULL,			/* truncate */
+		router_proc_perms
+	};
+
+	/*
+	 *	/proc/net/router/<device> file and inode operations
+	 */
+
+#ifdef LINUX_2_1
+	static struct file_operations wandev_fops =
+	{
+		NULL,			/* lseek   */
+		router_proc_read,	/* read	   */
+		NULL,			/* write   */
+		NULL,			/* readdir */
+		NULL,			/* select  */
+		wanrouter_ioctl,	/* ioctl   */
+		NULL,			/* mmap	   */
+		NULL,			/* no special open code	   */
+		NULL,			/* flush */
+		NULL,			/* no special release code */
+		NULL			/* can't fsync */
+	};
+#else
+	static struct file_operations wandev_fops =
+	{
+		NULL,                   /* lseek   */
+		router_proc_read,       /* read    */
+		device_write,           /* write   */
+		NULL,                   /* readdir */
+		NULL,                   /* select  */
+		wanrouter_ioctl,        /* ioctl   */
+		NULL,                   /* mmap    */
+		NULL,                   /* no special open code    */
+		NULL,                   /* no special release code */
+		NULL                    /* can't fsync */
+	};
+#endif
 
-/*
- *	Read router proc directory entry.
- *	This is universal routine for reading all entries in /proc/net/wanrouter
- *	directory.  Each directory entry contains a pointer to the 'method' for
- *	preparing data for that entry.
- *	o verify arguments
- *	o allocate kernel buffer
- *	o call get_info() to prepare data
- *	o copy data to user space
- *	o release kernel buffer
- *
- *	Return:	number of bytes copied to user space (0, if no data)
- *		<0	error
- */
+	static struct inode_operations wandev_inode =
+	{
+		&wandev_fops,
+		NULL,			/* create */
+		NULL,			/* lookup */
+		NULL,			/* link */
+		NULL,			/* unlink */
+		NULL,			/* symlink */
+		NULL,			/* mkdir */
+		NULL,			/* rmdir */
+		NULL,			/* mknod */
+		NULL,			/* rename */
+		NULL,			/* readlink */
+		NULL,			/* follow_link */
+		NULL,			/* readpage */
+		NULL,			/* writepage */
+		NULL,			/* bmap */
+		NULL,			/* truncate */
+		router_proc_perms
+	};
+
+	/*
+	 * Proc filesystem derectory entries.
+	 */
+
+	/*
+	 *	/proc/net/router 
+	 */
+	 
+	static struct proc_dir_entry proc_router =
+	{
+		0,			/* .low_ino */
+		sizeof(name_root) - 1,	/* .namelen */
+		name_root,		/* .name */
+		0555 | S_IFDIR,		/* .mode */
+		2,			/* .nlink */
+		0,			/* .uid */
+		0,			/* .gid */
+		0,			/* .size */
+		&proc_dir_inode_operations, /* .ops */
+		NULL,			/* .get_info */
+		NULL,			/* .fill_node */
+		NULL,			/* .next */
+		NULL,			/* .parent */
+		NULL,			/* .subdir */
+		NULL,			/* .data */
+	};
+
+	/*
+	 *	/proc/net/router/config 
+	 */
+	 
+	static struct proc_dir_entry proc_router_conf =
+	{
+		0,			/* .low_ino */
+		sizeof(name_conf) - 1,	/* .namelen */
+		name_conf,		/* .name */
+		0444 | S_IFREG,		/* .mode */
+		1,			/* .nlink */
+		0,			/* .uid */
+		0,			/* .gid */
+		0,			/* .size */
+		&router_inode,		/* .ops */
+		&config_get_info,	/* .get_info */
+		NULL,			/* .fill_node */
+		NULL,			/* .next */
+		NULL,			/* .parent */
+		NULL,			/* .subdir */
+		NULL,			/* .data */
+	};
+
+	/*
+	 *	/proc/net/router/status 
+	 */
+	 
+	static struct proc_dir_entry proc_router_stat =
+	{
+		0,			/* .low_ino */
+		sizeof(name_stat) - 1,	/* .namelen */
+		name_stat,		/* .name */
+		0444 | S_IFREG,		/* .mode */
+		1,			/* .nlink */
+		0,			/* .uid */
+		0,			/* .gid */
+		0,			/* .size */
+		&router_inode,		/* .ops */
+		status_get_info,	/* .get_info */
+		NULL,			/* .fill_node */
+		NULL,			/* .next */
+		NULL,			/* .parent */
+		NULL,			/* .subdir */
+		NULL,			/* .data */
+	};
+
+	/* Strings */
+	static char conf_hdr[] =
+		"Device name    | port |IRQ|DMA|  mem.addr  |mem.size|"
+		"option1|option2|option3|option4\n";
+		
+	static char stat_hdr[] =
+		"Device name    |protocol|station|interface|clocking|baud rate| MTU |ndev"
+		"|link state\n";
+
+
+	/*
+	 *	Interface functions
+	 */
+
+	/*
+	 *	Initialize router proc interface.
+	 */
+
+#ifdef LINUX_2_1
+	__initfunc(int wanrouter_proc_init (void))
+	{
+		int err = proc_register(proc_net, &proc_router);
+
+		if (!err) {
+			proc_register(&proc_router, &proc_router_conf);
+			proc_register(&proc_router, &proc_router_stat);
+		}
+		return err;
+	}
+#else
+	int wanrouter_proc_init (void)
+	{
+		int err = proc_register_dynamic(&proc_net, &proc_router);
+
+		if (!err) {
+			proc_register_dynamic(&proc_router, &proc_router_conf);
+			proc_register_dynamic(&proc_router, &proc_router_stat);
+		}
+		return err;
+	}
+#endif
 
-static ssize_t router_proc_read(struct file* file, char* buf, size_t count,
-				loff_t *ppos)
-{
-	struct inode *inode = file->f_dentry->d_inode;
-	struct proc_dir_entry* dent;
-	char* page;
-	int pos, offs, len;
+	/*
+	 *	Clean up router proc interface.
+	 */
+
+	void wanrouter_proc_cleanup (void)
+	{
+		proc_unregister(&proc_router, proc_router_conf.low_ino);
+		proc_unregister(&proc_router, proc_router_stat.low_ino);
+#ifdef LINUX_2_1
+		proc_unregister(proc_net, proc_router.low_ino);
+#else
+		proc_unregister(&proc_net, proc_router.low_ino);
+#endif
+	}
 
-	if (count <= 0)
-		return 0;
-		
-	dent = inode->u.generic_ip;
-	if ((dent == NULL) || (dent->get_info == NULL))
-		return 0;
+	/*
+	 *	Add directory entry for WAN device.
+	 */
+
+	int wanrouter_proc_add (wan_device_t* wandev)
+	{
+		if (wandev->magic != ROUTER_MAGIC)
+			return -EINVAL;
 		
-	page = kmalloc(PROC_BUFSZ, GFP_KERNEL);
-	if (page == NULL)
-		return -ENOBUFS;
-		
-	pos = dent->get_info(page, dent->data, 0, 0);
-	offs = file->f_pos;
-	if (offs < pos) {
-		len = min(pos - offs, count);
-		if(copy_to_user(buf, (page + offs), len)) {
-			len = -EFAULT;
-			goto out;
-		}
-		file->f_pos += len;
-	}
-	else
-		len = 0;
-out:	kfree(page);
-	return len;
-}
-
-/*
- *	Prepare data for reading 'Config' entry.
- *	Return length of data.
- */
+		memset(&wandev->dent, 0, sizeof(wandev->dent));
+		wandev->dent.namelen	= strlen(wandev->name);
+		wandev->dent.name	= wandev->name;
+		wandev->dent.mode	= 0444 | S_IFREG;
+		wandev->dent.nlink	= 1;
+		wandev->dent.ops	= &wandev_inode;
+		wandev->dent.get_info	= &wandev_get_info;
+		wandev->dent.data	= wandev;
+#ifdef LINUX_2_1
+		return proc_register(&proc_router, &wandev->dent);
+#else
+		return proc_register_dynamic(&proc_router, &wandev->dent);
+#endif
+	}
 
-static int config_get_info(char* buf, char** start, off_t offs, int len)
-{
-	int cnt = sizeof(conf_hdr) - 1;
-	wan_device_t* wandev;
-	strcpy(buf, conf_hdr);
-	for (wandev = router_devlist;
-	     wandev && (cnt < (PROC_BUFSZ - 120));
-	     wandev = wandev->next) {
-		if (wandev->state) cnt += sprintf(&buf[cnt],
-			"%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n",
-			wandev->name,
-			wandev->ioport,
-			wandev->irq,
-			wandev->dma,
-			wandev->maddr,
-			wandev->msize,
-			wandev->hw_opt[0],
-			wandev->hw_opt[1],
-			wandev->hw_opt[2],
-			wandev->hw_opt[3]);
+	/*
+	 *	Delete directory entry for WAN device.
+	 */
+	 
+	int wanrouter_proc_delete(wan_device_t* wandev)
+	{
+		if (wandev->magic != ROUTER_MAGIC)
+			return -EINVAL;
+		proc_unregister(&proc_router, wandev->dent.low_ino);
+		return 0;
 	}
 
-	return cnt;
-}
+	/****** Proc filesystem entry points ****************************************/
 
-/*
- *	Prepare data for reading 'Status' entry.
- *	Return length of data.
- */
+	/*
+	 *	Verify access rights.
+	 */
 
-static int status_get_info(char* buf, char** start, off_t offs, int len)
-{
-	int cnt = 0;
-	wan_device_t* wandev;
+	static int router_proc_perms (struct inode* inode, int op)
+	{
+		return 0;
+	}
 
-	cnt += sprintf(&buf[cnt], "\nSTATUS FOR PORT 0\n\n");
-	strcpy(&buf[cnt], stat_hdr);
-	cnt += sizeof(stat_hdr) - 1;
-
-	for (wandev = router_devlist;
-	     wandev && (cnt < (PROC_BUFSZ - 80));
-	     wandev = wandev->next) {
-		if (!wandev->state) continue;
-		cnt += sprintf(&buf[cnt],
-			"%-15s|%-7s|%-9s|%-8s|%9u|%5u|%3u |",
-			wandev->name,
-			wandev->station ? " DCE" : " DTE",
-			wandev->interface ? " V.35" : " RS-232",
-			wandev->clocking ? "internal" : "external",
-			wandev->bps,
-			wandev->mtu,
-			wandev->ndev);
-
-		switch (wandev->state) {
-
-		case WAN_UNCONFIGURED:
-			cnt += sprintf(&buf[cnt], "%-12s\n", "unconfigured");
-			break;
-
-		case WAN_DISCONNECTED:
-			cnt += sprintf(&buf[cnt], "%-12s\n", "disconnected");
-			break;
-
-		case WAN_CONNECTING:
-			cnt += sprintf(&buf[cnt], "%-12s\n", "connecting");
-			break;
-
-		case WAN_CONNECTED:
-			cnt += sprintf(&buf[cnt], "%-12s\n", "connected");
-			break;
-
-		default:
-			cnt += sprintf(&buf[cnt], "%-12s\n", "invalid");
-			break;
+	/*
+	 *	Read router proc directory entry.
+	 *	This is universal routine for reading all entries in /proc/net/wanrouter
+	 *	directory.  Each directory entry contains a pointer to the 'method' for
+	 *	preparing data for that entry.
+	 *	o verify arguments
+	 *	o allocate kernel buffer
+	 *	o call get_info() to prepare data
+	 *	o copy data to user space
+	 *	o release kernel buffer
+	 *
+	 *	Return:	number of bytes copied to user space (0, if no data)
+	 *		<0	error
+	 */
+#ifdef LINUX_2_1
+	static ssize_t router_proc_read(struct file* file, char* buf, size_t count,
+					loff_t *ppos)
+	{
+		struct inode *inode = file->f_dentry->d_inode;
+		struct proc_dir_entry* dent;
+		char* page;
+		int pos, offs, len;
+
+		if (count <= 0)
+			return 0;
+			
+		dent = inode->u.generic_ip;
+		if ((dent == NULL) || (dent->get_info == NULL))
+			return 0;
+			
+		page = kmalloc(PROC_BUFSZ, GFP_KERNEL);
+		if (page == NULL)
+			return -ENOBUFS;
+			
+		pos = dent->get_info(page, dent->data, 0, 0, 0);
+		offs = file->f_pos;
+		if (offs < pos) {
+			len = min(pos - offs, count);
+			if(copy_to_user(buf, (page + offs), len))
+				return -EFAULT;
+			file->f_pos += len;
 		}
+		else
+			len = 0;
+		kfree(page);
+		return len;
 	}
-	return cnt;
-}
 
-/*
- *	Prepare data for reading <device> entry.
- *	Return length of data.
- *
- *	On entry, the 'start' argument will contain a pointer to WAN device
- *	data space.
- */
+#else
+	static int router_proc_read(
+		struct inode* inode, struct file* file, char* buf, int count)
+	{
+		struct proc_dir_entry* dent;
+		char* page;
+		int err, pos, offs, len;
+
+		if (count <= 0)
+			return 0;
+		dent = inode->u.generic_ip;
+		if ((dent == NULL) || (dent->get_info == NULL))
+			return -ENODATA;
+		err = verify_area(VERIFY_WRITE, buf, count);
+		if (err) return err;
+
+		page = kmalloc(PROC_BUFSZ, GFP_KERNEL);
+		if (page == NULL)
+			return -ENOMEM;
+
+		pos = dent->get_info(page, dent->data, 0, 0, 0);
+		offs = file->f_pos;
+		if (offs < pos) {
+			len = min(pos - offs, count);
+			memcpy_tofs((void*)buf, (void*)(page + offs), len);
+			file->f_pos += len;
+		}
+		else len = 0;
+		kfree(page);
+		return len;
+	}
+#endif
 
-static int wandev_get_info(char* buf, char** start, off_t offs, int len)
-{
-	wan_device_t* wandev = (void*)start;
-	int cnt = 0;
-	int rslt = 0;
 
-	if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC))
-		return 0;
-	if (!wandev->state)
-		return sprintf(&buf[cnt], "device is not configured!\n");
+	/*
+	 *	Prepare data for reading 'Config' entry.
+	 *	Return length of data.
+	 */
+
+	static int config_get_info(char* buf, char** start, off_t offs, int len, 
+		int dummy)
+	{
+		int cnt = sizeof(conf_hdr) - 1;
+		wan_device_t* wandev;
+		strcpy(buf, conf_hdr);
+		for (wandev = router_devlist;
+		     wandev && (cnt < (PROC_BUFSZ - 120));
+		     wandev = wandev->next) {
+			if (wandev->state) cnt += sprintf(&buf[cnt],
+				"%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n",
+				wandev->name,
+				wandev->ioport,
+				wandev->irq,
+				wandev->dma,
+				wandev->maddr,
+				wandev->msize,
+				wandev->hw_opt[0],
+				wandev->hw_opt[1],
+				wandev->hw_opt[2],
+				wandev->hw_opt[3]);
+		}
 
-	/* Update device statistics */
-	if (wandev->update) {
+		return cnt;
+	}
 
-		rslt = wandev->update(wandev);
-		if(rslt) {
-			switch (rslt) {
-			case -EAGAIN:
-				return sprintf(&buf[cnt], "Device is busy!\n");
+	/*
+	 *	Prepare data for reading 'Status' entry.
+	 *	Return length of data.
+	 */
+
+	static int status_get_info(char* buf, char** start, off_t offs, int len, 
+				int dummy)
+	{
+		int cnt = 0;
+		wan_device_t* wandev;
+
+		//cnt += sprintf(&buf[cnt], "\nSTATUS:\n\n");
+		strcpy(&buf[cnt], stat_hdr);
+		cnt += sizeof(stat_hdr) - 1;
+
+		for (wandev = router_devlist;
+		     wandev && (cnt < (PROC_BUFSZ - 80));
+		     wandev = wandev->next) {
+			if (!wandev->state) continue;
+			cnt += sprintf(&buf[cnt],
+				"%-15s|%-8s|%-7s|%-9s|%-8s|%9u|%5u|%3u |",
+				wandev->name,
+				PROT_DECODE(wandev->config_id),
+				wandev->config_id == WANCONFIG_FR ? 
+					(wandev->station ? " Node" : " CPE") :
+					(wandev->config_id == WANCONFIG_X25 ?
+					(wandev->station ? " DCE" : " DTE") :
+					(" N/A")),
+				wandev->interface ? " V.35" : " RS-232",
+				wandev->clocking ? "internal" : "external",
+				wandev->bps,
+				wandev->mtu,
+				wandev->ndev);
+
+			switch (wandev->state) {
+
+			case WAN_UNCONFIGURED:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "unconfigured");
+				break;
+
+			case WAN_DISCONNECTED:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "disconnected");
+				break;
+
+			case WAN_CONNECTING:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "connecting");
+				break;
+
+			case WAN_CONNECTED:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "connected");
+				break;
+
+			case WAN_FT1_READY:
+				cnt += sprintf(&buf[cnt], "%-12s\n", "ft1 ready");
+				break;
 
 			default:
-				return sprintf(&buf[cnt],
-					"Device is not configured!\n");
+				cnt += sprintf(&buf[cnt], "%-12s\n", "invalid");
+				break;
 			}
 		}
+		return cnt;
 	}
 
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "total packets received", wandev->stats.rx_packets);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "total packets transmitted", wandev->stats.tx_packets);
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "total bytes received", wandev->stats.rx_bytes);
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "total bytes transmitted", wandev->stats.tx_bytes);
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "bad packets received", wandev->stats.rx_errors);
-       	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "packet transmit problems", wandev->stats.tx_errors);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "received frames dropped", wandev->stats.rx_dropped);
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "transmit frames dropped", wandev->stats.tx_dropped);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "multicast packets received", wandev->stats.multicast);
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "transmit collisions", wandev->stats.collisions);
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "receive length errors", wandev->stats.rx_length_errors);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-		"receiver overrun errors", wandev->stats.rx_over_errors);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-		"CRC errors", wandev->stats.rx_crc_errors);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-		"frame format errors (aborts)", wandev->stats.rx_frame_errors);
-        cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-                "receiver fifo overrun", wandev->stats.rx_fifo_errors);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-		"receiver missed packet", wandev->stats.rx_missed_errors);
-	cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
-		"aborted frames transmitted", wandev->stats.tx_aborted_errors);
-  	return cnt;
-}
+	/*
+	 *	Prepare data for reading <device> entry.
+	 *	Return length of data.
+	 *
+	 *	On entry, the 'start' argument will contain a pointer to WAN device
+	 *	data space.
+	 */
+
+	static int wandev_get_info(char* buf, char** start, off_t offs, int len, 
+				int dummy)
+	{
+		wan_device_t* wandev = (void*)start;
+		int cnt = 0;
+		int rslt = 0;
+
+		if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC))
+			return 0;
+		if (!wandev->state)
+			return sprintf(&buf[cnt], "Device is not configured!\n");
+
+		/* Update device statistics */
+		if (wandev->update) {
+
+			rslt = wandev->update(wandev);
+			if(rslt) {
+				switch (rslt) {
+				case -EAGAIN:
+					return sprintf(&buf[cnt], "Device is busy!\n");
+
+				default:
+					return sprintf(&buf[cnt],
+						"Device is not configured!\n");
+				}
+			}
+		}
+
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total packets received", wandev->stats.rx_packets);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total packets transmitted", wandev->stats.tx_packets);
+#ifdef LINUX_2_1
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total bytes received", wandev->stats.rx_bytes);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"total bytes transmitted", wandev->stats.tx_bytes);
+#endif
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"bad packets received", wandev->stats.rx_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"packet transmit problems", wandev->stats.tx_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"received frames dropped", wandev->stats.rx_dropped);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"transmit frames dropped", wandev->stats.tx_dropped);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"multicast packets received", wandev->stats.multicast);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"transmit collisions", wandev->stats.collisions);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receive length errors", wandev->stats.rx_length_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receiver overrun errors", wandev->stats.rx_over_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"CRC errors", wandev->stats.rx_crc_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"frame format errors (aborts)", wandev->stats.rx_frame_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receiver fifo overrun", wandev->stats.rx_fifo_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"receiver missed packet", wandev->stats.rx_missed_errors);
+		cnt += sprintf(&buf[cnt], PROC_STATS_FORMAT,
+			"aborted frames transmitted", wandev->stats.tx_aborted_errors);
+
+		return cnt;
+	}
+
+#endif /* End of ifdef LINUX_2_4 */
+
 
-/*
- *	End
- */
- 
 #else
 
 /*
  *	No /proc - output stubs
  */
- 
-int __init wanrouter_proc_init(void)
+
+__initfunc(int wanrouter_proc_init(void))
 {
 	return 0;
 }
@@ -436,6 +1055,33 @@
 	return 0;
 }
 
+#endif
+
+/*============================================================================
+ * Write WAN device ???.
+ * o Find WAN device associated with this node
+ */
+#ifdef LINUX_2_0
+static int device_write(
+        struct inode* inode, struct file* file, const char* buf, int count)
+{
+        int err = verify_area(VERIFY_READ, buf, count);
+        struct proc_dir_entry* dent;
+        wan_device_t* wandev;
+
+        if (err) return err;
+
+        dent = inode->u.generic_ip;
+        if ((dent == NULL) || (dent->data == NULL))
+                return -ENODATA;
+
+        wandev = dent->data;
+
+        printk(KERN_ERR "%s: writing %d bytes to %s...\n",
+                name_root, count, dent->name);
+        
+	return 0;
+}
 #endif
 
 /*
diff -urN linux-2.4.4-pre2/net/x25/af_x25.c linux/net/x25/af_x25.c
--- linux-2.4.4-pre2/net/x25/af_x25.c	Mon Jan 22 13:32:10 2001
+++ linux/net/x25/af_x25.c	Mon Dec  3 22:34:51 2001
@@ -409,6 +409,9 @@
 
 	len = min(len, sizeof(int));
 
+	if (len < 0)
+		return -EINVAL;
+		
 	if (put_user(len, optlen))
 		return -EFAULT;
 
@@ -912,7 +915,7 @@
 
 	size = len + X25_MAX_L2_LEN + X25_EXT_MIN_LEN;
 
-	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
+	if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
 	X25_SKB_CB(skb)->flags = msg->msg_flags;
 
@@ -1283,6 +1286,7 @@
 	sendmsg:	x25_sendmsg,
 	recvmsg:	x25_recvmsg,
 	mmap:		sock_no_mmap,
+	sendpage:	sock_no_sendpage,
 };
 
 #include <linux/smp_lock.h>
diff -urN linux-2.4.4-pre2/net/x25/x25_out.c linux/net/x25/x25_out.c
--- linux-2.4.4-pre2/net/x25/x25_out.c	Wed Jan 24 15:28:36 2001
+++ linux/net/x25/x25_out.c	Mon Dec  3 22:34:51 2001
@@ -79,7 +79,7 @@
 		frontlen = skb_headroom(skb);
 
 		while (skb->len > 0) {
-			if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, noblock, &err)) == NULL){
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, noblock, &err)) == NULL){
 				if(err == -EWOULDBLOCK && noblock){
 					kfree_skb(skb);
 					return sent;
diff -urN linux-2.4.4-pre2/scripts/ver_linux linux/scripts/ver_linux
--- linux-2.4.4-pre2/scripts/ver_linux	Fri Mar  2 11:12:12 2001
+++ linux/scripts/ver_linux	Mon Dec  3 22:34:51 2001
@@ -20,7 +20,9 @@
 ld -v 2>&1 | awk -F\) '{print $1}' | awk \
       '/BFD/{print "binutils              ",$NF}'
 
-mount --version | awk -F\- '{print "util-linux            ", $NF}'
+fdformat --version | awk -F\- '{print "util-linux            ", $NF}'
+
+mount --version | awk -F\- '{print "mount                 ", $NF}'
 
 insmod -V  2>&1 | awk 'NR==1 {print "modutils              ",$NF}'
 
